Spaces:
Runtime error
Runtime error
| import { spawn } from "node:child_process"; | |
| import { cpSync, existsSync, mkdirSync, readdirSync, renameSync } from "node:fs"; | |
| import { dirname, join } from "node:path"; | |
| import { pathToFileURL } from "node:url"; | |
| import { | |
| resolveRuntimePorts, | |
| sanitizeColorEnv, | |
| spawnWithForwardedSignals, | |
| withRuntimePortEnv, | |
| } from "../build/runtime-env.mjs"; | |
| import { bootstrapEnv } from "../build/bootstrap-env.mjs"; | |
| const mode = process.argv[2] === "start" ? "start" : "dev"; | |
| const cwd = process.cwd(); | |
| const appDir = join(cwd, "app"); | |
| const srcAppDir = join(cwd, "src", "app"); | |
| const appPage = join(appDir, "page.tsx"); | |
| const defaultBackupDir = join(cwd, "app.__qa_backup"); | |
| const backupDir = resolvePlaywrightAppBackupDir({ | |
| cwd, | |
| baseBackupExists: existsSync(defaultBackupDir), | |
| appDirExists: existsSync(appDir), | |
| }); | |
| const usingAlternativeBackupDir = backupDir !== defaultBackupDir; | |
| const buildScript = join(cwd, "scripts", "build", "build-next-isolated.mjs"); | |
| const standaloneServer = join(cwd, testDistDir(), "standalone", "server.js"); | |
| const rootStaticDir = join(cwd, testDistDir(), "static"); | |
| const rootPublicDir = join(cwd, "public"); | |
| const standaloneStaticDir = join(cwd, testDistDir(), "standalone", ".next", "static"); | |
| const standalonePublicDir = join(cwd, testDistDir(), "standalone", "public"); | |
| let appDirMoved = false; | |
| function testDistDir() { | |
| // Layer 1 moved the Next distDir default to .build/next; the Playwright | |
| // `start` runner must resolve the standalone server under the same dir. | |
| return process.env.NEXT_DIST_DIR || ".build/next"; | |
| } | |
| function resolvePlaywrightDataDir({ cwd, env, pid = process.pid }) { | |
| if (typeof env.DATA_DIR === "string" && env.DATA_DIR.trim().length > 0) { | |
| return env.DATA_DIR; | |
| } | |
| return join(cwd, ".tmp", "playwright-data", String(pid)); | |
| } | |
| export function resolvePlaywrightAppBackupDir({ | |
| cwd, | |
| baseBackupExists, | |
| appDirExists, | |
| pid = process.pid, | |
| now = Date.now(), | |
| }) { | |
| const baseBackupDir = join(cwd, "app.__qa_backup"); | |
| if (!baseBackupExists || !appDirExists) { | |
| return baseBackupDir; | |
| } | |
| return join(cwd, `app.__qa_backup.${pid}.${now}`); | |
| } | |
| function shouldMoveAppDir() { | |
| return existsSync(appDir) && !existsSync(appPage) && existsSync(srcAppDir); | |
| } | |
| export function directoryHasEntries(dirPath) { | |
| try { | |
| return readdirSync(dirPath).length > 0; | |
| } catch { | |
| return false; | |
| } | |
| } | |
| export function standaloneAssetsNeedSync({ | |
| standaloneServerPath, | |
| rootStaticDirPath, | |
| standaloneStaticDirPath, | |
| }) { | |
| return ( | |
| existsSync(standaloneServerPath) && | |
| existsSync(rootStaticDirPath) && | |
| !directoryHasEntries(standaloneStaticDirPath) | |
| ); | |
| } | |
| export function syncStandaloneRuntimeAssets({ | |
| standaloneServerPath, | |
| rootStaticDirPath, | |
| standaloneStaticDirPath, | |
| rootPublicDirPath, | |
| standalonePublicDirPath, | |
| log = console, | |
| }) { | |
| if (!existsSync(standaloneServerPath)) return false; | |
| let changed = false; | |
| if (existsSync(rootPublicDirPath) && !directoryHasEntries(standalonePublicDirPath)) { | |
| cpSync(rootPublicDirPath, standalonePublicDirPath, { | |
| recursive: true, | |
| force: true, | |
| }); | |
| changed = true; | |
| } | |
| if (existsSync(rootStaticDirPath) && !directoryHasEntries(standaloneStaticDirPath)) { | |
| mkdirSync(dirname(standaloneStaticDirPath), { | |
| recursive: true, | |
| }); | |
| cpSync(rootStaticDirPath, standaloneStaticDirPath, { | |
| recursive: true, | |
| force: true, | |
| }); | |
| changed = true; | |
| } | |
| if (changed) { | |
| log.log("[Playwright WebServer] Rehydrated standalone static/public assets"); | |
| } | |
| return changed; | |
| } | |
| function prepareAppDir() { | |
| if (!shouldMoveAppDir()) return; | |
| if (usingAlternativeBackupDir) { | |
| console.warn( | |
| "[Playwright WebServer] Existing app.__qa_backup detected; using a per-run backup dir instead." | |
| ); | |
| } | |
| renameSync(appDir, backupDir); | |
| appDirMoved = true; | |
| console.log("[Playwright WebServer] Temporarily moved app/ to app.__qa_backup"); | |
| } | |
| function restoreAppDir() { | |
| if (!appDirMoved) return; | |
| if (!existsSync(backupDir) || existsSync(appDir)) return; | |
| renameSync(backupDir, appDir); | |
| console.log("[Playwright WebServer] Restored app/ directory"); | |
| } | |
| const playwrightDataDir = resolvePlaywrightDataDir({ | |
| cwd, | |
| env: process.env, | |
| }); | |
| const bootstrapEnvVars = bootstrapEnv({ | |
| quiet: true, | |
| dataDirOverride: playwrightDataDir, | |
| }); | |
| const runtimePorts = resolveRuntimePorts(bootstrapEnvVars); | |
| const bootstrapMode = process.env.OMNIROUTE_E2E_BOOTSTRAP_MODE || "auth"; | |
| const playwrightPassword = | |
| process.env.OMNIROUTE_E2E_PASSWORD || process.env.INITIAL_PASSWORD || "omniroute-e2e-password"; | |
| const testServerEnv = { | |
| ...sanitizeColorEnv(bootstrapEnvVars), | |
| ...sanitizeColorEnv(process.env), | |
| NODE_ENV: mode === "start" ? "production" : "development", | |
| DATA_DIR: playwrightDataDir, | |
| NEXT_PUBLIC_OMNIROUTE_E2E_MODE: process.env.NEXT_PUBLIC_OMNIROUTE_E2E_MODE || "1", | |
| OMNIROUTE_DISABLE_BACKGROUND_SERVICES: | |
| process.env.OMNIROUTE_DISABLE_BACKGROUND_SERVICES || "true", | |
| OMNIROUTE_DISABLE_TOKEN_HEALTHCHECK: process.env.OMNIROUTE_DISABLE_TOKEN_HEALTHCHECK || "true", | |
| OMNIROUTE_DISABLE_LOCAL_HEALTHCHECK: process.env.OMNIROUTE_DISABLE_LOCAL_HEALTHCHECK || "true", | |
| OMNIROUTE_HIDE_HEALTHCHECK_LOGS: process.env.OMNIROUTE_HIDE_HEALTHCHECK_LOGS || "true", | |
| ...(bootstrapMode === "open" | |
| ? { | |
| INITIAL_PASSWORD: "", | |
| OMNIROUTE_E2E_PASSWORD: "", | |
| OMNIROUTE_API_KEY: "", | |
| } | |
| : { | |
| INITIAL_PASSWORD: playwrightPassword, | |
| OMNIROUTE_E2E_PASSWORD: playwrightPassword, | |
| }), | |
| ...(process.env.OMNIROUTE_USE_TURBOPACK | |
| ? { | |
| OMNIROUTE_USE_TURBOPACK: process.env.OMNIROUTE_USE_TURBOPACK, | |
| } | |
| : {}), | |
| }; | |
| export function shouldUseWebpackForPlaywrightDev({ mode, env }) { | |
| return mode === "dev" && env.OMNIROUTE_USE_TURBOPACK !== "1"; | |
| } | |
| function runChild(command, args, env) { | |
| return new Promise((resolve) => { | |
| const child = spawn(command, args, { | |
| stdio: "inherit", | |
| env, | |
| }); | |
| const forward = (signal) => { | |
| if (!child.killed) child.kill(signal); | |
| }; | |
| process.on("SIGINT", forward); | |
| process.on("SIGTERM", forward); | |
| child.on("exit", (code, signal) => { | |
| process.off("SIGINT", forward); | |
| process.off("SIGTERM", forward); | |
| resolve({ code: code ?? 1, signal: signal ?? null }); | |
| }); | |
| }); | |
| } | |
| async function runBuildForStart() { | |
| if (mode !== "start") return; | |
| if (process.env.OMNIROUTE_PLAYWRIGHT_SKIP_BUILD === "1") return; | |
| console.log("[Playwright WebServer] Building fresh standalone app for this run..."); | |
| const buildEnv = withRuntimePortEnv(testServerEnv, runtimePorts); | |
| const result = await runChild(process.execPath, [buildScript], buildEnv); | |
| if (result.signal) { | |
| process.kill(process.pid, result.signal); | |
| return; | |
| } | |
| if (result.code !== 0) { | |
| process.exit(result.code); | |
| } | |
| } | |
| export async function main() { | |
| process.on("exit", restoreAppDir); | |
| process.on("uncaughtException", (error) => { | |
| restoreAppDir(); | |
| throw error; | |
| }); | |
| prepareAppDir(); | |
| await runBuildForStart(); | |
| if (mode === "start") { | |
| if (existsSync(standaloneServer)) { | |
| syncStandaloneRuntimeAssets({ | |
| standaloneServerPath: standaloneServer, | |
| rootStaticDirPath: rootStaticDir, | |
| standaloneStaticDirPath: standaloneStaticDir, | |
| rootPublicDirPath: rootPublicDir, | |
| standalonePublicDirPath: standalonePublicDir, | |
| }); | |
| spawnWithForwardedSignals(process.execPath, [standaloneServer], { | |
| stdio: "inherit", | |
| env: { | |
| ...withRuntimePortEnv(testServerEnv, runtimePorts), | |
| PORT: String(runtimePorts.dashboardPort), | |
| HOSTNAME: process.env.HOSTNAME || "127.0.0.1", | |
| }, | |
| }); | |
| return; | |
| } | |
| const args = [ | |
| "./node_modules/next/dist/bin/next", | |
| "start", | |
| "--port", | |
| String(runtimePorts.dashboardPort), | |
| ]; | |
| spawnWithForwardedSignals(process.execPath, args, { | |
| stdio: "inherit", | |
| env: withRuntimePortEnv(testServerEnv, runtimePorts), | |
| }); | |
| return; | |
| } | |
| const args = [ | |
| "./node_modules/next/dist/bin/next", | |
| mode, | |
| "--port", | |
| String(runtimePorts.dashboardPort), | |
| ]; | |
| if (shouldUseWebpackForPlaywrightDev({ mode, env: testServerEnv })) { | |
| args.splice(2, 0, "--webpack"); | |
| } | |
| spawnWithForwardedSignals(process.execPath, args, { | |
| stdio: "inherit", | |
| env: withRuntimePortEnv(testServerEnv, runtimePorts), | |
| }); | |
| } | |
| if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) { | |
| await main(); | |
| } | |