rwsdk 1.0.0-alpha.5 → 1.0.0-alpha.7

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.
Files changed (56) hide show
  1. package/dist/lib/e2e/browser.d.mts +10 -0
  2. package/dist/lib/e2e/browser.mjs +107 -0
  3. package/dist/lib/e2e/dev.d.mts +8 -0
  4. package/dist/lib/e2e/dev.mjs +232 -0
  5. package/dist/lib/e2e/environment.d.mts +14 -0
  6. package/dist/lib/e2e/environment.mjs +201 -0
  7. package/dist/lib/e2e/index.d.mts +7 -0
  8. package/dist/lib/e2e/index.mjs +7 -0
  9. package/dist/lib/e2e/release.d.mts +56 -0
  10. package/dist/lib/e2e/release.mjs +537 -0
  11. package/dist/lib/e2e/tarball.d.mts +14 -0
  12. package/dist/lib/e2e/tarball.mjs +189 -0
  13. package/dist/lib/e2e/testHarness.d.mts +98 -0
  14. package/dist/lib/e2e/testHarness.mjs +393 -0
  15. package/dist/lib/e2e/types.d.mts +31 -0
  16. package/dist/lib/e2e/types.mjs +1 -0
  17. package/dist/lib/smokeTests/browser.mjs +3 -94
  18. package/dist/lib/smokeTests/development.mjs +2 -223
  19. package/dist/lib/smokeTests/environment.d.mts +4 -11
  20. package/dist/lib/smokeTests/environment.mjs +10 -158
  21. package/dist/lib/smokeTests/release.d.mts +2 -49
  22. package/dist/lib/smokeTests/release.mjs +3 -503
  23. package/dist/runtime/lib/injectHtmlAtMarker.d.ts +11 -0
  24. package/dist/runtime/lib/injectHtmlAtMarker.js +90 -0
  25. package/dist/runtime/lib/realtime/worker.d.ts +1 -1
  26. package/dist/runtime/lib/router.js +32 -20
  27. package/dist/runtime/lib/router.test.js +506 -1
  28. package/dist/runtime/lib/rwContext.d.ts +22 -0
  29. package/dist/runtime/lib/rwContext.js +1 -0
  30. package/dist/runtime/render/assembleDocument.d.ts +6 -0
  31. package/dist/runtime/render/assembleDocument.js +22 -0
  32. package/dist/runtime/render/createThenableFromReadableStream.d.ts +1 -0
  33. package/dist/runtime/render/createThenableFromReadableStream.js +9 -0
  34. package/dist/runtime/render/normalizeActionResult.d.ts +1 -0
  35. package/dist/runtime/render/normalizeActionResult.js +43 -0
  36. package/dist/runtime/render/preloads.d.ts +2 -2
  37. package/dist/runtime/render/preloads.js +2 -3
  38. package/dist/runtime/render/{renderRscThenableToHtmlStream.d.ts → renderDocumentHtmlStream.d.ts} +3 -3
  39. package/dist/runtime/render/renderDocumentHtmlStream.js +39 -0
  40. package/dist/runtime/render/renderHtmlStream.d.ts +7 -0
  41. package/dist/runtime/render/renderHtmlStream.js +31 -0
  42. package/dist/runtime/render/renderToRscStream.d.ts +2 -3
  43. package/dist/runtime/render/renderToRscStream.js +2 -41
  44. package/dist/runtime/render/renderToStream.d.ts +2 -1
  45. package/dist/runtime/render/renderToStream.js +15 -8
  46. package/dist/runtime/render/stylesheets.d.ts +2 -2
  47. package/dist/runtime/render/stylesheets.js +2 -3
  48. package/dist/runtime/ssrBridge.d.ts +2 -1
  49. package/dist/runtime/ssrBridge.js +2 -1
  50. package/dist/runtime/worker.d.ts +1 -0
  51. package/dist/runtime/worker.js +11 -6
  52. package/dist/vite/configPlugin.mjs +2 -2
  53. package/package.json +8 -4
  54. package/dist/runtime/render/renderRscThenableToHtmlStream.js +0 -54
  55. package/dist/runtime/render/transformRscToHtmlStream.d.ts +0 -8
  56. package/dist/runtime/render/transformRscToHtmlStream.js +0 -19
@@ -0,0 +1,31 @@
1
+ export type PackageManager = "pnpm" | "npm" | "yarn" | "yarn-classic";
2
+ /**
3
+ * Options for smoke tests
4
+ */
5
+ export interface SmokeTestOptions {
6
+ projectDir?: string;
7
+ artifactDir?: string;
8
+ headless?: boolean;
9
+ keep?: boolean;
10
+ sync?: boolean;
11
+ bail?: boolean;
12
+ skipDev?: boolean;
13
+ skipRelease?: boolean;
14
+ skipClient?: boolean;
15
+ packageManager?: PackageManager;
16
+ realtime?: boolean;
17
+ skipHmr?: boolean;
18
+ skipStyleTests?: boolean;
19
+ }
20
+ /**
21
+ * Resources created during a test run that need to be cleaned up
22
+ */
23
+ export interface TestResources {
24
+ tempDirCleanup?: () => Promise<void>;
25
+ workerName?: string;
26
+ targetDir?: string;
27
+ originalCwd: string;
28
+ workerCreatedDuringTest: boolean;
29
+ stopDev?: () => Promise<void>;
30
+ resourceUniqueKey: string;
31
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -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
- // Get browser path if not provided
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
- console.log("Finding Chrome executable...");
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
- console.log("🚀 Starting development server...");
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 tmp from "tmp-promise";
2
- import { SmokeTestOptions, TestResources, PackageManager } from "./types.mjs";
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 };
@@ -1,164 +1,16 @@
1
- import { join } from "path";
2
- import { pathExists, copy } from "fs-extra";
3
- import * as fs from "fs/promises";
4
- import tmp from "tmp-promise";
5
- import ignore from "ignore";
6
- import { relative, basename, resolve } from "path";
7
- import { uniqueNamesGenerator, adjectives, animals, } from "unique-names-generator";
8
- import { $ } from "../../lib/$.mjs";
9
- import { log } from "./constants.mjs";
10
- import { debugSync } from "../../scripts/debug-sync.mjs";
1
+ import { setupTestEnvironment as setupE2ETestEnvironment, copyProjectToTempDir, } from "../../lib/e2e/environment.mjs";
11
2
  import { createSmokeTestComponents } from "./codeUpdates.mjs";
12
- import { createHash } from "crypto";
3
+ import { log } from "./constants.mjs";
13
4
  /**
14
- * Sets up the test environment, preparing any resources needed for testing
5
+ * Sets up the test environment for smoke tests, preparing any resources needed for testing
15
6
  */
16
7
  export async function setupTestEnvironment(options = {}) {
17
- log("Setting up test environment with options: %O", options);
18
- // Generate a resource unique key for this test run right at the start
19
- const uniqueNameSuffix = uniqueNamesGenerator({
20
- dictionaries: [adjectives, animals],
21
- separator: "-",
22
- length: 2,
23
- style: "lowerCase",
24
- });
25
- // Create a short unique hash based on the timestamp
26
- const hash = createHash("md5")
27
- .update(Date.now().toString())
28
- .digest("hex")
29
- .substring(0, 8);
30
- // Create a resource unique key even if we're not copying a project
31
- const resourceUniqueKey = `${uniqueNameSuffix}-${hash}`;
32
- const resources = {
33
- tempDirCleanup: undefined,
34
- workerName: undefined,
35
- originalCwd: process.cwd(),
36
- targetDir: undefined,
37
- workerCreatedDuringTest: false,
38
- stopDev: undefined,
39
- resourceUniqueKey, // Set at initialization
40
- };
41
- log("Current working directory: %s", resources.originalCwd);
42
- try {
43
- // If a project dir is specified, copy it to a temp dir with a unique name
44
- if (options.projectDir) {
45
- log("Project directory specified: %s", options.projectDir);
46
- const { tempDir, targetDir, workerName } = await copyProjectToTempDir(options.projectDir, options.sync !== false, // default to true if undefined
47
- resourceUniqueKey, // Pass in the existing resourceUniqueKey
48
- options.packageManager);
49
- // Store cleanup function
50
- resources.tempDirCleanup = tempDir.cleanup;
51
- resources.workerName = workerName;
52
- resources.targetDir = targetDir;
53
- log("Target directory: %s", targetDir);
54
- // Create the smoke test components in the user's project
55
- log("Creating smoke test components");
56
- await createSmokeTestComponents(targetDir, options.skipClient);
57
- }
58
- else {
59
- log("No project directory specified, using current directory");
60
- // When no project dir is specified, we'll use the current directory
61
- resources.targetDir = resources.originalCwd;
62
- }
63
- return resources;
64
- }
65
- catch (error) {
66
- log("Error during test environment setup: %O", error);
67
- throw error;
68
- }
69
- }
70
- /**
71
- * Copy project to a temporary directory with a unique name
72
- */
73
- export async function copyProjectToTempDir(projectDir, sync = true, resourceUniqueKey, packageManager) {
74
- log("Creating temporary directory for project");
75
- // Create a temporary directory
76
- const tempDir = await tmp.dir({ unsafeCleanup: true });
77
- // Create unique project directory name
78
- const originalDirName = basename(projectDir);
79
- const workerName = `${originalDirName}-smoke-test-${resourceUniqueKey}`;
80
- const targetDir = resolve(tempDir.path, workerName);
81
- console.log(`Copying project from ${projectDir} to ${targetDir}`);
82
- // Read project's .gitignore if it exists
83
- let ig = ignore();
84
- const gitignorePath = join(projectDir, ".gitignore");
85
- if (await pathExists(gitignorePath)) {
86
- log("Found .gitignore file at %s", gitignorePath);
87
- const gitignoreContent = await fs.readFile(gitignorePath, "utf-8");
88
- ig = ig.add(gitignoreContent);
89
- }
90
- else {
91
- log("No .gitignore found, using default ignore patterns");
92
- // Add default ignores if no .gitignore exists
93
- ig = ig.add([
94
- "node_modules",
95
- ".git",
96
- "dist",
97
- "build",
98
- ".DS_Store",
99
- "coverage",
100
- ".cache",
101
- ".wrangler",
102
- ".env",
103
- ].join("\n"));
104
- }
105
- // Copy the project directory, respecting .gitignore
106
- log("Starting copy process with ignored patterns");
107
- await copy(projectDir, targetDir, {
108
- filter: (src) => {
109
- // Get path relative to project directory
110
- const relativePath = relative(projectDir, src);
111
- if (!relativePath)
112
- return true; // Include the root directory
113
- // Check against ignore patterns
114
- const result = !ig.ignores(relativePath);
115
- return result;
116
- },
117
- });
118
- log("Project copy completed successfully");
119
- // For yarn, create .yarnrc.yml to disable PnP and use node_modules
120
- if (packageManager === "yarn" || packageManager === "yarn-classic") {
121
- const yarnrcPath = join(targetDir, ".yarnrc.yml");
122
- await fs.writeFile(yarnrcPath, "nodeLinker: node-modules\n");
123
- log("Created .yarnrc.yml to disable PnP for yarn");
124
- }
125
- // Install dependencies in the target directory
126
- await installDependencies(targetDir, packageManager);
127
- // Sync SDK to the temp dir if requested
128
- if (sync) {
129
- console.log(`🔄 Syncing SDK to ${targetDir} after installing dependencies...`);
130
- await debugSync({ targetDir });
131
- }
132
- return { tempDir, targetDir, workerName };
133
- }
134
- /**
135
- * Install project dependencies using pnpm
136
- */
137
- async function installDependencies(targetDir, packageManager = "pnpm") {
138
- console.log(`📦 Installing project dependencies in ${targetDir} using ${packageManager}...`);
139
- try {
140
- const installCommand = {
141
- pnpm: ["pnpm", "install"],
142
- npm: ["npm", "install"],
143
- yarn: ["yarn", "install", "--immutable"],
144
- "yarn-classic": ["yarn", "install", "--immutable"],
145
- }[packageManager];
146
- // Run install command in the target directory
147
- log(`Running ${installCommand.join(" ")}`);
148
- const [command, ...args] = installCommand;
149
- const result = await $(command, args, {
150
- cwd: targetDir,
151
- stdio: "pipe", // Capture output
152
- });
153
- console.log("✅ Dependencies installed successfully");
154
- // Log installation details at debug level
155
- if (result.stdout) {
156
- log(`${packageManager} install output: %s`, result.stdout);
157
- }
158
- }
159
- catch (error) {
160
- log("ERROR: Failed to install dependencies: %O", error);
161
- console.error(`❌ Failed to install dependencies: ${error instanceof Error ? error.message : String(error)}`);
162
- throw new Error(`Failed to install project dependencies. Please ensure the project can be installed with ${packageManager}.`);
8
+ const resources = await setupE2ETestEnvironment(options);
9
+ if (resources.targetDir) {
10
+ // Create the smoke test components in the user's project
11
+ log("Creating smoke test components");
12
+ await createSmokeTestComponents(resources.targetDir, options.skipClient);
163
13
  }
14
+ return resources;
164
15
  }
16
+ export { copyProjectToTempDir };
@@ -1,35 +1,6 @@
1
1
  import { TestResources } from "./types.mjs";
2
- interface ExpectOptions {
3
- expect: string | RegExp;
4
- send?: string;
5
- }
6
- interface ExpectResult {
7
- stdout: string;
8
- stderr: string;
9
- code: number | null;
10
- }
11
- interface D1Database {
12
- name: string;
13
- uuid: string;
14
- [key: string]: any;
15
- }
16
- /**
17
- * A mini expect-like utility for handling interactive CLI prompts and verifying output
18
- * @param command The command to execute
19
- * @param expectations Array of {expect, send} objects for interactive responses and verification
20
- * @param options Additional options for command execution including working directory and environment
21
- * @returns Promise that resolves when the command completes
22
- */
23
- export declare function $expect(command: string, expectations: Array<ExpectOptions>, options?: {
24
- reject?: boolean;
25
- env?: NodeJS.ProcessEnv;
26
- cwd?: string;
27
- }): Promise<ExpectResult>;
28
- /**
29
- * Ensures Cloudflare account ID is set in environment
30
- * First checks wrangler cache, then environment variables, and finally guides the user
31
- */
32
- export declare function ensureCloudflareAccountId(cwd?: string, projectDir?: string): Promise<void>;
2
+ import { deleteWorker, deleteD1Database, isRelatedToTest, $expect, listD1Databases } from "../../lib/e2e/release.mjs";
3
+ export { deleteWorker, deleteD1Database, isRelatedToTest, $expect, listD1Databases, };
33
4
  /**
34
5
  * Run the release command to deploy to Cloudflare
35
6
  */
@@ -41,21 +12,3 @@ export declare function runRelease(cwd: string, projectDir: string, resourceUniq
41
12
  * Runs tests against the production deployment
42
13
  */
43
14
  export declare function runReleaseTest(artifactDir: string, resources: TestResources, browserPath?: string, headless?: boolean, bail?: boolean, skipClient?: boolean, projectDir?: string, realtime?: boolean, skipHmr?: boolean, skipStyleTests?: boolean): Promise<void>;
44
- /**
45
- * Check if a resource name includes a specific resource unique key
46
- * This is used to identify resources created during our tests
47
- */
48
- export declare function isRelatedToTest(resourceName: string, resourceUniqueKey: string): boolean;
49
- /**
50
- * Delete the worker using wrangler
51
- */
52
- export declare function deleteWorker(name: string, cwd: string, resourceUniqueKey: string): Promise<void>;
53
- /**
54
- * List D1 databases using wrangler
55
- */
56
- export declare function listD1Databases(cwd?: string): Promise<Array<D1Database>>;
57
- /**
58
- * Delete a D1 database using wrangler
59
- */
60
- export declare function deleteD1Database(name: string, cwd: string, resourceUniqueKey: string): Promise<void>;
61
- export {};