rwsdk 1.0.0-alpha.1-test.20250911154541 → 1.0.0-alpha.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/e2e/browser.d.mts +10 -0
- package/dist/lib/e2e/browser.mjs +124 -0
- package/dist/lib/e2e/dev.d.mts +8 -0
- package/dist/lib/e2e/dev.mjs +232 -0
- package/dist/lib/e2e/environment.d.mts +14 -0
- package/dist/lib/e2e/environment.mjs +223 -0
- package/dist/lib/e2e/index.d.mts +7 -0
- package/dist/lib/e2e/index.mjs +7 -0
- package/dist/lib/e2e/release.d.mts +56 -0
- package/dist/lib/e2e/release.mjs +559 -0
- package/dist/lib/e2e/tarball.d.mts +13 -0
- package/dist/lib/e2e/tarball.mjs +99 -0
- package/dist/lib/e2e/testHarness.d.mts +123 -0
- package/dist/lib/e2e/testHarness.mjs +507 -0
- package/dist/lib/e2e/types.d.mts +32 -0
- package/dist/lib/getShortName.mjs +6 -1
- package/dist/lib/getShortName.test.d.mts +1 -0
- package/dist/lib/getShortName.test.mjs +25 -0
- package/dist/lib/hasPkgScript.d.mts +4 -1
- package/dist/lib/hasPkgScript.mjs +9 -6
- package/dist/lib/hasPkgScript.test.d.mts +1 -0
- package/dist/lib/hasPkgScript.test.mjs +33 -0
- package/dist/lib/jsonUtils.mjs +3 -0
- package/dist/lib/jsonUtils.test.d.mts +1 -0
- package/dist/lib/jsonUtils.test.mjs +90 -0
- package/dist/lib/normalizeModulePath.d.mts +5 -0
- package/dist/lib/normalizeModulePath.mjs +1 -1
- package/dist/lib/normalizeModulePath.test.d.mts +1 -0
- package/dist/lib/{normalizeModulePath.test.js → normalizeModulePath.test.mjs} +20 -1
- package/dist/lib/smokeTests/browser.mjs +3 -94
- package/dist/lib/smokeTests/development.mjs +2 -223
- package/dist/lib/smokeTests/environment.d.mts +4 -11
- package/dist/lib/smokeTests/environment.mjs +10 -158
- package/dist/lib/smokeTests/release.d.mts +2 -49
- package/dist/lib/smokeTests/release.mjs +3 -503
- package/dist/runtime/lib/injectHtmlAtMarker.d.ts +11 -0
- package/dist/runtime/lib/injectHtmlAtMarker.js +90 -0
- package/dist/runtime/lib/memoizeOnId.test.d.ts +1 -0
- package/dist/runtime/lib/memoizeOnId.test.js +49 -0
- package/dist/runtime/lib/realtime/protocol.test.d.ts +1 -0
- package/dist/runtime/lib/realtime/protocol.test.js +107 -0
- package/dist/runtime/lib/realtime/shared.test.d.ts +1 -0
- package/dist/runtime/lib/realtime/shared.test.js +18 -0
- package/dist/runtime/lib/realtime/validateUpgradeRequest.test.d.ts +1 -0
- package/dist/runtime/lib/realtime/validateUpgradeRequest.test.js +66 -0
- package/dist/runtime/lib/realtime/worker.d.ts +1 -1
- package/dist/runtime/lib/router.js +40 -22
- package/dist/runtime/lib/router.test.js +591 -2
- package/dist/runtime/lib/rwContext.d.ts +22 -0
- package/dist/runtime/lib/rwContext.js +1 -0
- package/dist/runtime/lib/turnstile/verifyTurnstileToken.d.ts +2 -1
- package/dist/runtime/lib/turnstile/verifyTurnstileToken.js +6 -6
- package/dist/runtime/lib/turnstile/verifyTurnstileToken.test.d.ts +1 -0
- package/dist/runtime/lib/turnstile/verifyTurnstileToken.test.js +49 -0
- package/dist/runtime/register/worker.d.ts +1 -1
- package/dist/runtime/register/worker.js +26 -21
- package/dist/runtime/render/assembleDocument.d.ts +6 -0
- package/dist/runtime/render/assembleDocument.js +22 -0
- package/dist/runtime/render/createThenableFromReadableStream.d.ts +1 -0
- package/dist/runtime/render/createThenableFromReadableStream.js +9 -0
- package/dist/runtime/render/normalizeActionResult.d.ts +1 -0
- package/dist/runtime/render/normalizeActionResult.js +43 -0
- package/dist/runtime/render/preloads.d.ts +2 -2
- package/dist/runtime/render/preloads.js +2 -3
- package/dist/runtime/render/{renderRscThenableToHtmlStream.d.ts → renderDocumentHtmlStream.d.ts} +3 -3
- package/dist/runtime/render/renderDocumentHtmlStream.js +39 -0
- package/dist/runtime/render/renderHtmlStream.d.ts +7 -0
- package/dist/runtime/render/renderHtmlStream.js +31 -0
- package/dist/runtime/render/renderToRscStream.d.ts +2 -3
- package/dist/runtime/render/renderToRscStream.js +2 -41
- package/dist/runtime/render/renderToStream.d.ts +2 -1
- package/dist/runtime/render/renderToStream.js +15 -8
- package/dist/runtime/render/stylesheets.d.ts +2 -2
- package/dist/runtime/render/stylesheets.js +2 -3
- package/dist/runtime/ssrBridge.d.ts +2 -1
- package/dist/runtime/ssrBridge.js +2 -1
- package/dist/runtime/worker.d.ts +1 -0
- package/dist/runtime/worker.js +11 -6
- package/dist/scripts/debug-sync.mjs +102 -133
- package/dist/vite/buildApp.d.mts +2 -1
- package/dist/vite/buildApp.mjs +9 -5
- package/dist/vite/checkIsUsingPrisma.d.mts +4 -0
- package/dist/vite/checkIsUsingPrisma.mjs +2 -2
- package/dist/vite/checkIsUsingPrisma.test.d.mts +1 -0
- package/dist/vite/checkIsUsingPrisma.test.mjs +30 -0
- package/dist/vite/configPlugin.mjs +35 -14
- package/dist/vite/createDirectiveLookupPlugin.d.mts +9 -0
- package/dist/vite/createDirectiveLookupPlugin.mjs +33 -29
- package/dist/vite/createDirectiveLookupPlugin.test.d.mts +1 -0
- package/dist/vite/createDirectiveLookupPlugin.test.mjs +40 -0
- package/dist/vite/directiveModulesDevPlugin.d.mts +4 -1
- package/dist/vite/directiveModulesDevPlugin.mjs +5 -4
- package/dist/vite/directiveModulesDevPlugin.test.d.mts +1 -0
- package/dist/vite/directiveModulesDevPlugin.test.mjs +59 -0
- package/dist/vite/directivesPlugin.d.mts +1 -0
- package/dist/vite/directivesPlugin.mjs +1 -1
- package/dist/vite/directivesPlugin.test.d.mts +1 -0
- package/dist/vite/directivesPlugin.test.mjs +24 -0
- package/dist/vite/ensureAliasArray.test.d.mts +1 -0
- package/dist/vite/ensureAliasArray.test.mjs +71 -0
- package/dist/vite/findSpecifiers.mjs +2 -1
- package/dist/vite/findSpecifiers.test.d.mts +1 -0
- package/dist/vite/findSpecifiers.test.mjs +202 -0
- package/dist/vite/findSsrSpecifiers.test.d.mts +1 -0
- package/dist/vite/findSsrSpecifiers.test.mjs +99 -0
- package/dist/vite/hasDirective.d.mts +6 -3
- package/dist/vite/hasDirective.mjs +43 -27
- package/dist/vite/hasDirective.test.d.mts +1 -0
- package/dist/vite/hasDirective.test.mjs +107 -0
- package/dist/vite/isJsFile.test.d.mts +1 -0
- package/dist/vite/isJsFile.test.mjs +38 -0
- package/dist/vite/{reactConditionsResolverPlugin.d.mts → knownDepsResolverPlugin.d.mts} +2 -2
- package/dist/vite/{reactConditionsResolverPlugin.mjs → knownDepsResolverPlugin.mjs} +28 -23
- package/dist/vite/linkerPlugin.d.mts +8 -0
- package/dist/vite/linkerPlugin.mjs +30 -22
- package/dist/vite/linkerPlugin.test.d.mts +1 -0
- package/dist/vite/linkerPlugin.test.mjs +41 -0
- package/dist/vite/miniflareHMRPlugin.d.mts +5 -0
- package/dist/vite/miniflareHMRPlugin.mjs +2 -2
- package/dist/vite/miniflareHMRPlugin.test.d.mts +1 -0
- package/dist/vite/miniflareHMRPlugin.test.mjs +42 -0
- package/dist/vite/redwoodPlugin.d.mts +7 -0
- package/dist/vite/redwoodPlugin.mjs +10 -5
- package/dist/vite/redwoodPlugin.test.d.mts +1 -0
- package/dist/vite/redwoodPlugin.test.mjs +34 -0
- package/dist/vite/runDirectivesScan.d.mts +21 -1
- package/dist/vite/runDirectivesScan.mjs +67 -52
- package/dist/vite/runDirectivesScan.test.d.mts +1 -0
- package/dist/vite/runDirectivesScan.test.mjs +73 -0
- package/dist/vite/ssrBridgePlugin.mjs +8 -1
- package/dist/vite/transformClientComponents.mjs +4 -3
- package/dist/vite/transformClientComponents.test.mjs +116 -58
- package/package.json +8 -4
- package/dist/runtime/render/renderRscThenableToHtmlStream.js +0 -54
- package/dist/runtime/render/transformRscToHtmlStream.d.ts +0 -8
- package/dist/runtime/render/transformRscToHtmlStream.js +0 -19
- /package/dist/lib/{normalizeModulePath.test.d.ts → e2e/types.mjs} +0 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { extractLastJson, extractAllJson, parseJson } from "./jsonUtils.mjs";
|
|
3
|
+
describe("jsonUtils", () => {
|
|
4
|
+
describe("extractLastJson", () => {
|
|
5
|
+
it("should extract the last JSON object from a string", () => {
|
|
6
|
+
const output = 'some text {"a":1} some other text {"b":2, "c": {"d": 3}} end';
|
|
7
|
+
expect(extractLastJson(output)).toEqual({ b: 2, c: { d: 3 } });
|
|
8
|
+
});
|
|
9
|
+
it("should extract the last JSON array from a string", () => {
|
|
10
|
+
const output = "start [1, 2] middle [3, 4, [5]] end";
|
|
11
|
+
expect(extractLastJson(output)).toEqual([3, 4, [5]]);
|
|
12
|
+
});
|
|
13
|
+
it("should return the object if the string is just JSON", () => {
|
|
14
|
+
const output = '{"a":1}';
|
|
15
|
+
expect(extractLastJson(output)).toEqual({ a: 1 });
|
|
16
|
+
});
|
|
17
|
+
it("should handle nested structures correctly", () => {
|
|
18
|
+
const output = '{"a":{"b":{"c":"d"}}}';
|
|
19
|
+
expect(extractLastJson(output)).toEqual({ a: { b: { c: "d" } } });
|
|
20
|
+
});
|
|
21
|
+
it("should return null if no valid JSON is found", () => {
|
|
22
|
+
const output = "this is just some text without json";
|
|
23
|
+
expect(extractLastJson(output)).toBeNull();
|
|
24
|
+
});
|
|
25
|
+
it("should return null for malformed JSON", () => {
|
|
26
|
+
const output = '{"a":1, "b":}';
|
|
27
|
+
expect(extractLastJson(output)).toBeNull();
|
|
28
|
+
});
|
|
29
|
+
it("should handle undefined and empty string input", () => {
|
|
30
|
+
expect(extractLastJson(undefined)).toBeNull();
|
|
31
|
+
expect(extractLastJson("")).toBeNull();
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
describe("extractAllJson", () => {
|
|
35
|
+
it("should extract all JSON objects from a string", () => {
|
|
36
|
+
const output = '{"a":1} some text {"b":2} and then {"c":3, "d": [4]}';
|
|
37
|
+
expect(extractAllJson(output)).toEqual([
|
|
38
|
+
{ a: 1 },
|
|
39
|
+
{ b: 2 },
|
|
40
|
+
{ c: 3, d: [4] },
|
|
41
|
+
]);
|
|
42
|
+
});
|
|
43
|
+
it("should extract all JSON arrays from a string", () => {
|
|
44
|
+
const output = "[1,2] then [3,4]";
|
|
45
|
+
expect(extractAllJson(output)).toEqual([
|
|
46
|
+
[1, 2],
|
|
47
|
+
[3, 4],
|
|
48
|
+
]);
|
|
49
|
+
});
|
|
50
|
+
it("should handle a mix of objects and arrays", () => {
|
|
51
|
+
const output = '{"a":1} [2,3] {"b":4}';
|
|
52
|
+
expect(extractAllJson(output)).toEqual([{ a: 1 }, [2, 3], { b: 4 }]);
|
|
53
|
+
});
|
|
54
|
+
it("should return an empty array if no JSON is found", () => {
|
|
55
|
+
const output = "no json here";
|
|
56
|
+
expect(extractAllJson(output)).toEqual([]);
|
|
57
|
+
});
|
|
58
|
+
it("should ignore malformed JSON", () => {
|
|
59
|
+
const output = '{"a":1} {"b":2,} [3,4]';
|
|
60
|
+
expect(extractAllJson(output)).toEqual([{ a: 1 }, [3, 4]]);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
describe("parseJson", () => {
|
|
64
|
+
it("should parse the last JSON object by default", () => {
|
|
65
|
+
const output = '{"a":1} {"b":2}';
|
|
66
|
+
expect(parseJson(output, {})).toEqual({ b: 2 });
|
|
67
|
+
});
|
|
68
|
+
it("should return the default value if no JSON is found", () => {
|
|
69
|
+
const output = "no json";
|
|
70
|
+
expect(parseJson(output, { default: true })).toEqual({ default: true });
|
|
71
|
+
});
|
|
72
|
+
it("should find an object with a uuid property when requested", () => {
|
|
73
|
+
const output = '{"a":1} {"uuid":"123-abc", "data": "yes"} {"c":3}';
|
|
74
|
+
expect(parseJson(output, {}, true)).toEqual({
|
|
75
|
+
uuid: "123-abc",
|
|
76
|
+
data: "yes",
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
it("should return the last object if findUuid is true but no object with uuid is found", () => {
|
|
80
|
+
const output = '{"a":1} {"b":2}';
|
|
81
|
+
expect(parseJson(output, {}, true)).toEqual({ b: 2 });
|
|
82
|
+
});
|
|
83
|
+
it("should return the default value if findUuid is true and no JSON is found", () => {
|
|
84
|
+
const output = "no json";
|
|
85
|
+
expect(parseJson(output, { default: true }, true)).toEqual({
|
|
86
|
+
default: true,
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
});
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Find the number of common ancestor segments between two absolute paths.
|
|
3
|
+
* Returns the count of shared directory segments from the root.
|
|
4
|
+
*/
|
|
5
|
+
export declare function findCommonAncestorDepth(path1: string, path2: string): number;
|
|
1
6
|
/**
|
|
2
7
|
* Normalize a module path to a consistent form.
|
|
3
8
|
* Returns slash-prefixed paths for files within project root,
|
|
@@ -4,7 +4,7 @@ import { normalizePath as normalizePathSeparators } from "vite";
|
|
|
4
4
|
* Find the number of common ancestor segments between two absolute paths.
|
|
5
5
|
* Returns the count of shared directory segments from the root.
|
|
6
6
|
*/
|
|
7
|
-
function findCommonAncestorDepth(path1, path2) {
|
|
7
|
+
export function findCommonAncestorDepth(path1, path2) {
|
|
8
8
|
const segments1 = path1.split("/").filter(Boolean);
|
|
9
9
|
const segments2 = path2.split("/").filter(Boolean);
|
|
10
10
|
let commonLength = 0;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { normalizeModulePath } from "./normalizeModulePath.mjs";
|
|
2
|
+
import { normalizeModulePath, findCommonAncestorDepth, } from "./normalizeModulePath.mjs";
|
|
3
|
+
describe("findCommonAncestorDepth", () => {
|
|
4
|
+
it("should return the correct depth for common paths", () => {
|
|
5
|
+
expect(findCommonAncestorDepth("/a/b/c", "/a/b/d")).toBe(2);
|
|
6
|
+
});
|
|
7
|
+
it("should return 0 for completely different paths", () => {
|
|
8
|
+
expect(findCommonAncestorDepth("/a/b/c", "/d/e/f")).toBe(0);
|
|
9
|
+
});
|
|
10
|
+
it("should handle identical paths", () => {
|
|
11
|
+
expect(findCommonAncestorDepth("/a/b/c", "/a/b/c")).toBe(3);
|
|
12
|
+
});
|
|
13
|
+
it("should handle paths of different lengths", () => {
|
|
14
|
+
expect(findCommonAncestorDepth("/a/b/c", "/a/b/c/d/e")).toBe(3);
|
|
15
|
+
});
|
|
16
|
+
it("should handle the root path", () => {
|
|
17
|
+
expect(findCommonAncestorDepth("/", "/a/b")).toBe(0);
|
|
18
|
+
expect(findCommonAncestorDepth("/a", "/")).toBe(0);
|
|
19
|
+
expect(findCommonAncestorDepth("/", "/")).toBe(0);
|
|
20
|
+
});
|
|
21
|
+
});
|
|
3
22
|
describe("normalizeModulePath", () => {
|
|
4
23
|
describe("1. Project-local paths", () => {
|
|
5
24
|
it("Relative file", () => {
|
|
@@ -1,10 +1,5 @@
|
|
|
1
|
-
import * as os from "os";
|
|
2
1
|
import { join } from "path";
|
|
3
|
-
import { pathExists } from "fs-extra";
|
|
4
2
|
import { log } from "./constants.mjs";
|
|
5
|
-
import { mkdirp } from "fs-extra";
|
|
6
|
-
import { install, resolveBuildId, computeExecutablePath, detectBrowserPlatform, Browser as PuppeteerBrowser, } from "@puppeteer/browsers";
|
|
7
|
-
import puppeteer from "puppeteer-core";
|
|
8
3
|
import { takeScreenshot } from "./artifacts.mjs";
|
|
9
4
|
import { RETRIES } from "./constants.mjs";
|
|
10
5
|
import { $ } from "../$.mjs";
|
|
@@ -14,6 +9,7 @@ import { updateTestStatus } from "./state.mjs";
|
|
|
14
9
|
import * as fs from "fs/promises";
|
|
15
10
|
import { template as urlStylesTemplate } from "./templates/smokeTestUrlStyles.css.template";
|
|
16
11
|
import { template as clientStylesTemplate } from "./templates/smokeTestClientStyles.module.css.template";
|
|
12
|
+
import { launchBrowser as launchE2EBrowser, getBrowserPath as getE2EBrowserPath, } from "../../lib/e2e/browser.mjs";
|
|
17
13
|
export async function checkUrlStyles(page, expectedColor) {
|
|
18
14
|
const selector = '[data-testid="smoke-test-url-styles"]';
|
|
19
15
|
log(`Checking for element with selector: ${selector}`);
|
|
@@ -86,100 +82,13 @@ export async function checkClientModuleStyles(page, expectedColor) {
|
|
|
86
82
|
* Launch a browser instance
|
|
87
83
|
*/
|
|
88
84
|
export async function launchBrowser(browserPath, headless = true) {
|
|
89
|
-
|
|
90
|
-
if (!browserPath) {
|
|
91
|
-
log("Getting browser executable path");
|
|
92
|
-
browserPath = await getBrowserPath();
|
|
93
|
-
}
|
|
94
|
-
console.log(`🚀 Launching browser from ${browserPath} (headless: ${headless})`);
|
|
95
|
-
log("Starting browser with puppeteer");
|
|
96
|
-
return await puppeteer.launch({
|
|
97
|
-
executablePath: browserPath,
|
|
98
|
-
headless,
|
|
99
|
-
args: ["--no-sandbox", "--disable-setuid-sandbox"],
|
|
100
|
-
});
|
|
85
|
+
return launchE2EBrowser(browserPath, headless);
|
|
101
86
|
}
|
|
102
87
|
/**
|
|
103
88
|
* Get the browser executable path
|
|
104
89
|
*/
|
|
105
90
|
export async function getBrowserPath(testOptions) {
|
|
106
|
-
|
|
107
|
-
// First try using environment variable if set
|
|
108
|
-
if (process.env.CHROME_PATH) {
|
|
109
|
-
console.log(`Using Chrome from environment variable: ${process.env.CHROME_PATH}`);
|
|
110
|
-
return process.env.CHROME_PATH;
|
|
111
|
-
}
|
|
112
|
-
// Detect platform
|
|
113
|
-
log("Detecting platform");
|
|
114
|
-
const platform = detectBrowserPlatform();
|
|
115
|
-
if (!platform) {
|
|
116
|
-
log("ERROR: Failed to detect browser platform");
|
|
117
|
-
throw new Error("Failed to detect browser platform");
|
|
118
|
-
}
|
|
119
|
-
log("Detected platform: %s", platform);
|
|
120
|
-
// Define a consistent cache directory path in system temp folder
|
|
121
|
-
const rwCacheDir = join(os.tmpdir(), "redwoodjs-smoke-test-cache");
|
|
122
|
-
await mkdirp(rwCacheDir);
|
|
123
|
-
log("Using cache directory: %s", rwCacheDir);
|
|
124
|
-
// Determine browser type based on headless option
|
|
125
|
-
const browser = testOptions?.headless === false
|
|
126
|
-
? PuppeteerBrowser.CHROME
|
|
127
|
-
: PuppeteerBrowser.CHROMEHEADLESSSHELL;
|
|
128
|
-
log(`Using browser type: ${browser}`);
|
|
129
|
-
// Resolve the buildId for the stable Chrome version - do this once
|
|
130
|
-
log("Resolving Chrome buildId for stable channel");
|
|
131
|
-
const buildId = await resolveBuildId(browser, platform, "stable");
|
|
132
|
-
log("Resolved buildId: %s", buildId);
|
|
133
|
-
// Create installation options - use them consistently
|
|
134
|
-
const installOptions = {
|
|
135
|
-
browser,
|
|
136
|
-
platform,
|
|
137
|
-
cacheDir: rwCacheDir,
|
|
138
|
-
buildId,
|
|
139
|
-
unpack: true,
|
|
140
|
-
};
|
|
141
|
-
try {
|
|
142
|
-
// Try to compute the path first (this will check if it's installed)
|
|
143
|
-
log("Attempting to find existing Chrome installation");
|
|
144
|
-
const path = computeExecutablePath(installOptions);
|
|
145
|
-
if (await pathExists(path)) {
|
|
146
|
-
console.log(`Found existing Chrome at: ${path}`);
|
|
147
|
-
return path;
|
|
148
|
-
}
|
|
149
|
-
else {
|
|
150
|
-
throw new Error("Chrome not found at: " + path);
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
catch (error) {
|
|
154
|
-
// If path computation fails, install Chrome
|
|
155
|
-
console.log("No Chrome installation found. Installing Chrome...");
|
|
156
|
-
// Add better error handling for the install step
|
|
157
|
-
try {
|
|
158
|
-
console.log("Downloading Chrome (this may take a few minutes)...");
|
|
159
|
-
await install(installOptions);
|
|
160
|
-
console.log("✅ Chrome installation completed successfully");
|
|
161
|
-
// Now compute the path for the installed browser
|
|
162
|
-
const path = computeExecutablePath(installOptions);
|
|
163
|
-
console.log(`Installed and using Chrome at: ${path}`);
|
|
164
|
-
return path;
|
|
165
|
-
}
|
|
166
|
-
catch (installError) {
|
|
167
|
-
// Provide more detailed error about the browser download failure
|
|
168
|
-
log("ERROR: Failed to download/install Chrome: %O", installError);
|
|
169
|
-
console.error(`❌ Failed to download/install Chrome browser.`);
|
|
170
|
-
console.error(`This is likely a network issue or the browser download URL is unavailable.`);
|
|
171
|
-
console.error(`Error details: ${installError instanceof Error ? installError.message : String(installError)}`);
|
|
172
|
-
// For debug builds, show the full error stack if available
|
|
173
|
-
if (installError instanceof Error && installError.stack) {
|
|
174
|
-
log("Error stack: %s", installError.stack);
|
|
175
|
-
}
|
|
176
|
-
console.log("\nPossible solutions:");
|
|
177
|
-
console.log("1. Check your internet connection");
|
|
178
|
-
console.log("2. Set CHROME_PATH environment variable to an existing Chrome installation");
|
|
179
|
-
console.log("3. Install Chrome manually and run the tests again");
|
|
180
|
-
throw new Error(`Failed to install Chrome browser: ${installError instanceof Error ? installError.message : String(installError)}`);
|
|
181
|
-
}
|
|
182
|
-
}
|
|
91
|
+
return getE2EBrowserPath(testOptions);
|
|
183
92
|
}
|
|
184
93
|
/**
|
|
185
94
|
* Check a URL by performing smoke tests and realtime upgrade
|
|
@@ -1,233 +1,12 @@
|
|
|
1
|
-
import { setTimeout } from "node:timers/promises";
|
|
2
1
|
import { log, RETRIES } from "./constants.mjs";
|
|
3
|
-
import { $ } from "../$.mjs";
|
|
4
2
|
import { checkUrl, checkServerUp, launchBrowser } from "./browser.mjs";
|
|
5
|
-
import { fail } from "./utils.mjs";
|
|
6
3
|
import { state } from "./state.mjs";
|
|
4
|
+
import { runDevServer as runE2EDevServer } from "../../lib/e2e/dev.mjs";
|
|
7
5
|
/**
|
|
8
6
|
* Run the local development server and return the URL
|
|
9
7
|
*/
|
|
10
8
|
export async function runDevServer(cwd) {
|
|
11
|
-
|
|
12
|
-
// Function to stop the dev server - defined early so we can use it in error handling
|
|
13
|
-
let devProcess = null;
|
|
14
|
-
let isErrorExpected = false;
|
|
15
|
-
const stopDev = async () => {
|
|
16
|
-
isErrorExpected = true;
|
|
17
|
-
if (!devProcess) {
|
|
18
|
-
log("No dev process to stop");
|
|
19
|
-
return;
|
|
20
|
-
}
|
|
21
|
-
console.log("Stopping development server...");
|
|
22
|
-
try {
|
|
23
|
-
// Send a regular termination signal first
|
|
24
|
-
devProcess.kill();
|
|
25
|
-
// Wait for the process to terminate with a timeout
|
|
26
|
-
const terminationTimeout = 5000; // 5 seconds timeout
|
|
27
|
-
const terminationPromise = Promise.race([
|
|
28
|
-
// Wait for natural process termination
|
|
29
|
-
(async () => {
|
|
30
|
-
try {
|
|
31
|
-
await devProcess;
|
|
32
|
-
log("Dev server process was terminated normally");
|
|
33
|
-
return true;
|
|
34
|
-
}
|
|
35
|
-
catch (e) {
|
|
36
|
-
// Expected error when the process is killed
|
|
37
|
-
log("Dev server process was terminated");
|
|
38
|
-
return true;
|
|
39
|
-
}
|
|
40
|
-
})(),
|
|
41
|
-
// Or timeout
|
|
42
|
-
(async () => {
|
|
43
|
-
await setTimeout(terminationTimeout);
|
|
44
|
-
return false;
|
|
45
|
-
})(),
|
|
46
|
-
]);
|
|
47
|
-
// Check if process terminated within timeout
|
|
48
|
-
const terminated = await terminationPromise;
|
|
49
|
-
// If not terminated within timeout, force kill
|
|
50
|
-
if (!terminated) {
|
|
51
|
-
log("Dev server process did not terminate within timeout, force killing with SIGKILL");
|
|
52
|
-
console.log("⚠️ Development server not responding after 5 seconds timeout, force killing...");
|
|
53
|
-
// Try to kill with SIGKILL if the process still has a pid
|
|
54
|
-
if (devProcess.pid) {
|
|
55
|
-
try {
|
|
56
|
-
// Use process.kill with SIGKILL for a stronger termination
|
|
57
|
-
process.kill(devProcess.pid, "SIGKILL");
|
|
58
|
-
log("Sent SIGKILL to process %d", devProcess.pid);
|
|
59
|
-
}
|
|
60
|
-
catch (killError) {
|
|
61
|
-
log("Error sending SIGKILL to process: %O", killError);
|
|
62
|
-
// Non-fatal, as the process might already be gone
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
catch (e) {
|
|
68
|
-
// Process might already have exited
|
|
69
|
-
log("Could not kill dev server process: %O", e);
|
|
70
|
-
}
|
|
71
|
-
console.log("Development server stopped");
|
|
72
|
-
};
|
|
73
|
-
try {
|
|
74
|
-
// Check if we're in CI mode
|
|
75
|
-
const inCIMode = process.env.CI === "true" || process.env.CI === "1";
|
|
76
|
-
// Start dev server with stdout pipe to capture URL
|
|
77
|
-
// Create environment variables object
|
|
78
|
-
const env = { ...process.env };
|
|
79
|
-
// Disable colors when running in CI mode to make URL parsing more reliable
|
|
80
|
-
if (inCIMode) {
|
|
81
|
-
log("Running in CI mode, disabling colors for dev server output");
|
|
82
|
-
env.NO_COLOR = "1";
|
|
83
|
-
env.FORCE_COLOR = "0";
|
|
84
|
-
}
|
|
85
|
-
// Map package manager names to actual commands
|
|
86
|
-
const getPackageManagerCommand = (pm) => {
|
|
87
|
-
switch (pm) {
|
|
88
|
-
case "yarn-classic":
|
|
89
|
-
return "yarn";
|
|
90
|
-
default:
|
|
91
|
-
return pm;
|
|
92
|
-
}
|
|
93
|
-
};
|
|
94
|
-
const pm = getPackageManagerCommand(state.options.packageManager || "npm");
|
|
95
|
-
// Use the provided cwd if available
|
|
96
|
-
devProcess = $({
|
|
97
|
-
all: true,
|
|
98
|
-
detached: false, // Keep attached so we can access streams
|
|
99
|
-
cleanup: false, // Don't auto-kill on exit
|
|
100
|
-
cwd: cwd || process.cwd(), // Use provided directory or current directory
|
|
101
|
-
env, // Pass the updated environment variables
|
|
102
|
-
stdio: 'pipe', // Ensure streams are piped
|
|
103
|
-
}) `${pm} run dev`;
|
|
104
|
-
devProcess.catch((error) => {
|
|
105
|
-
if (!isErrorExpected) {
|
|
106
|
-
// Use fail() directly here to properly handle errors from the dev process
|
|
107
|
-
fail(error, 1, "Development Server Process");
|
|
108
|
-
}
|
|
109
|
-
});
|
|
110
|
-
log("Development server process spawned in directory: %s", cwd || process.cwd());
|
|
111
|
-
// Store chunks to parse the URL
|
|
112
|
-
let url = "";
|
|
113
|
-
let allOutput = "";
|
|
114
|
-
// Listen for all output to get the URL
|
|
115
|
-
const handleOutput = (data, source) => {
|
|
116
|
-
const output = data.toString();
|
|
117
|
-
console.log(output);
|
|
118
|
-
allOutput += output; // Accumulate all output
|
|
119
|
-
log("Received output from %s: %s", source, output.replace(/\n/g, "\\n"));
|
|
120
|
-
if (!url) {
|
|
121
|
-
// Multiple patterns to catch different package manager outputs
|
|
122
|
-
const patterns = [
|
|
123
|
-
// Standard Vite output: "Local: http://localhost:5173/"
|
|
124
|
-
/Local:\s*(?:\u001b\[\d+m)?(https?:\/\/localhost:\d+)/i,
|
|
125
|
-
// Alternative Vite output: "➜ Local: http://localhost:5173/"
|
|
126
|
-
/[➜→]\s*Local:\s*(?:\u001b\[\d+m)?(https?:\/\/localhost:\d+)/i,
|
|
127
|
-
// Unicode-safe arrow pattern
|
|
128
|
-
/[\u27A1\u2192\u279C]\s*Local:\s*(?:\u001b\[\d+m)?(https?:\/\/localhost:\d+)/i,
|
|
129
|
-
// Direct URL pattern: "http://localhost:5173"
|
|
130
|
-
/(https?:\/\/localhost:\d+)/i,
|
|
131
|
-
// Port-only pattern: "localhost:5173"
|
|
132
|
-
/localhost:(\d+)/i,
|
|
133
|
-
// Server ready messages
|
|
134
|
-
/server.*ready.*localhost:(\d+)/i,
|
|
135
|
-
/dev server.*localhost:(\d+)/i,
|
|
136
|
-
];
|
|
137
|
-
for (const pattern of patterns) {
|
|
138
|
-
const match = output.match(pattern);
|
|
139
|
-
log("Testing pattern %s against output: %s", pattern.source, output.replace(/\n/g, "\\n"));
|
|
140
|
-
if (match) {
|
|
141
|
-
log("Pattern matched: %s, groups: %o", pattern.source, match);
|
|
142
|
-
if (match[1] && match[1].startsWith("http")) {
|
|
143
|
-
url = match[1];
|
|
144
|
-
log("Found development server URL with pattern %s: %s", pattern.source, url);
|
|
145
|
-
break;
|
|
146
|
-
}
|
|
147
|
-
else if (match[1] && /^\d+$/.test(match[1])) {
|
|
148
|
-
url = `http://localhost:${match[1]}`;
|
|
149
|
-
log("Found development server URL with port pattern %s: %s", pattern.source, url);
|
|
150
|
-
break;
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
// Log potential matches for debugging
|
|
155
|
-
if (!url &&
|
|
156
|
-
(output.includes("localhost") ||
|
|
157
|
-
output.includes("Local") ||
|
|
158
|
-
output.includes("server"))) {
|
|
159
|
-
log("Potential URL pattern found but not matched: %s", output.trim());
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
};
|
|
163
|
-
// Listen to all possible output streams
|
|
164
|
-
log("Setting up stream listeners. Available streams: all=%s, stdout=%s, stderr=%s", !!devProcess.all, !!devProcess.stdout, !!devProcess.stderr);
|
|
165
|
-
devProcess.all?.on("data", (data) => handleOutput(data, "all"));
|
|
166
|
-
devProcess.stdout?.on("data", (data) => handleOutput(data, "stdout"));
|
|
167
|
-
devProcess.stderr?.on("data", (data) => handleOutput(data, "stderr"));
|
|
168
|
-
// Also try listening to the raw process output
|
|
169
|
-
if (devProcess.child) {
|
|
170
|
-
log("Setting up child process stream listeners");
|
|
171
|
-
devProcess.child.stdout?.on("data", (data) => handleOutput(data, "child.stdout"));
|
|
172
|
-
devProcess.child.stderr?.on("data", (data) => handleOutput(data, "child.stderr"));
|
|
173
|
-
}
|
|
174
|
-
// Wait for URL with timeout
|
|
175
|
-
const waitForUrl = async () => {
|
|
176
|
-
const start = Date.now();
|
|
177
|
-
const timeout = 60000; // 60 seconds
|
|
178
|
-
while (Date.now() - start < timeout) {
|
|
179
|
-
if (url) {
|
|
180
|
-
return url;
|
|
181
|
-
}
|
|
182
|
-
// Fallback: check accumulated output if stream listeners aren't working
|
|
183
|
-
if (!url && allOutput) {
|
|
184
|
-
log("Checking accumulated output for URL patterns: %s", allOutput.replace(/\n/g, "\\n"));
|
|
185
|
-
const patterns = [
|
|
186
|
-
/Local:\s*(?:\u001b\[\d+m)?(https?:\/\/localhost:\d+)/i,
|
|
187
|
-
/[➜→]\s*Local:\s*(?:\u001b\[\d+m)?(https?:\/\/localhost:\d+)/i,
|
|
188
|
-
/[\u27A1\u2192\u279C]\s*Local:\s*(?:\u001b\[\d+m)?(https?:\/\/localhost:\d+)/i,
|
|
189
|
-
/(https?:\/\/localhost:\d+)/i,
|
|
190
|
-
/localhost:(\d+)/i,
|
|
191
|
-
];
|
|
192
|
-
for (const pattern of patterns) {
|
|
193
|
-
const match = allOutput.match(pattern);
|
|
194
|
-
if (match) {
|
|
195
|
-
if (match[1] && match[1].startsWith("http")) {
|
|
196
|
-
url = match[1];
|
|
197
|
-
log("Found URL in accumulated output with pattern %s: %s", pattern.source, url);
|
|
198
|
-
return url;
|
|
199
|
-
}
|
|
200
|
-
else if (match[1] && /^\d+$/.test(match[1])) {
|
|
201
|
-
url = `http://localhost:${match[1]}`;
|
|
202
|
-
log("Found URL in accumulated output with port pattern %s: %s", pattern.source, url);
|
|
203
|
-
return url;
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
// Check if the process is still running
|
|
209
|
-
if (devProcess.exitCode !== null) {
|
|
210
|
-
log("ERROR: Development server process exited with code %d. Final output: %s", devProcess.exitCode, allOutput);
|
|
211
|
-
throw new Error(`Development server process exited with code ${devProcess.exitCode}`);
|
|
212
|
-
}
|
|
213
|
-
await setTimeout(500); // Check every 500ms
|
|
214
|
-
}
|
|
215
|
-
log("ERROR: Timed out waiting for dev server URL. Final accumulated output: %s", allOutput);
|
|
216
|
-
throw new Error("Timed out waiting for dev server URL");
|
|
217
|
-
};
|
|
218
|
-
// Wait for the URL
|
|
219
|
-
const serverUrl = await waitForUrl();
|
|
220
|
-
console.log(`✅ Development server started at ${serverUrl}`);
|
|
221
|
-
return { url: serverUrl, stopDev };
|
|
222
|
-
}
|
|
223
|
-
catch (error) {
|
|
224
|
-
// Make sure to try to stop the server on error
|
|
225
|
-
log("Error during dev server startup: %O", error);
|
|
226
|
-
await stopDev().catch((e) => {
|
|
227
|
-
log("Failed to stop dev server during error handling: %O", e);
|
|
228
|
-
});
|
|
229
|
-
throw error;
|
|
230
|
-
}
|
|
9
|
+
return runE2EDevServer(state.options.packageManager, cwd);
|
|
231
10
|
}
|
|
232
11
|
/**
|
|
233
12
|
* Runs tests against the development server
|
|
@@ -1,14 +1,7 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { SmokeTestOptions, TestResources
|
|
1
|
+
import { copyProjectToTempDir } from "../../lib/e2e/environment.mjs";
|
|
2
|
+
import { SmokeTestOptions, TestResources } from "../../lib/e2e/types.mjs";
|
|
3
3
|
/**
|
|
4
|
-
* Sets up the test environment, preparing any resources needed for testing
|
|
4
|
+
* Sets up the test environment for smoke tests, preparing any resources needed for testing
|
|
5
5
|
*/
|
|
6
6
|
export declare function setupTestEnvironment(options?: SmokeTestOptions): Promise<TestResources>;
|
|
7
|
-
|
|
8
|
-
* Copy project to a temporary directory with a unique name
|
|
9
|
-
*/
|
|
10
|
-
export declare function copyProjectToTempDir(projectDir: string, sync: boolean | undefined, resourceUniqueKey: string, packageManager?: PackageManager): Promise<{
|
|
11
|
-
tempDir: tmp.DirectoryResult;
|
|
12
|
-
targetDir: string;
|
|
13
|
-
workerName: string;
|
|
14
|
-
}>;
|
|
7
|
+
export { copyProjectToTempDir };
|