#!/usr/bin/env node /** * 端到端测试启动器:先起 vite preview,再跑 Playwright,结束后确保关闭预览服务。 * 不使用 Playwright webServer,避免 vite preview 在部分环境下退出挂起。 * 用法:pnpm test:e2e(会先执行 build) * * A1 账号生命周期用例(e2e/a1-identity.spec.ts): * - 默认 A1_E2E=1 才执行,否则整文件 skip;本脚本默认不设该变量,因此 CI / pnpm test:e2e 不受影响; * - 它需要**真实 API + web dev**(非 preview): * 1) API 跑在 http://127.0.0.1:3000/api/v1(真实 PG / Redis); * 2) web 跑在 http://127.0.0.1:5173(`pnpm --filter @lingxi/web dev`,需关闭 demo 会话); * 3) 执行:A1_E2E=1 pnpm exec playwright test e2e/a1-identity.spec.ts --reporter=list * - 默认 baseURL 走 A1_E2E_BASE_URL(缺省 5173),API 走 A1_E2E_API_BASE_URL(缺省 3000); * 若改到 4173(preview)执行,需要 API 的 WEB_ORIGIN 同时包含 4173,否则浏览器请求被 CORS 拦截; * - 截图落盘到 design/a1-ui-v1/shots/impl/e2e/。 */ import { spawn, spawnSync } from 'node:child_process'; import { setTimeout as delay } from 'node:timers/promises'; const baseUrl = 'http://127.0.0.1:4173'; const pnpm = process.env.npm_execpath ? [process.execPath, process.env.npm_execpath] : ['pnpm']; function runPnpm(args, options = {}) { return spawnSync(pnpm[0], [...pnpm.slice(1), ...args], { env: process.env, stdio: 'inherit', ...options, }); } const server = spawn(pnpm[0], [...pnpm.slice(1), '--filter', '@lingxi/web', 'preview'], { env: process.env, stdio: 'ignore', detached: true, }); async function waitForServer(url, timeoutMs = 60_000) { const startedAt = Date.now(); while (Date.now() - startedAt < timeoutMs) { try { const response = await fetch(url); if (response.ok) return; } catch { // 预览服务尚未就绪,继续重试。 } await delay(500); } throw new Error(`预览服务在 ${timeoutMs}ms 内未就绪:${url}`); } let exitCode = 1; try { await waitForServer(`${baseUrl}/login`); const result = runPnpm(['exec', 'playwright', 'test']); exitCode = result.status ?? 1; } finally { try { process.kill(-server.pid, 'SIGTERM'); } catch { // 进程可能已退出。 } await delay(1_000); try { process.kill(-server.pid, 'SIGKILL'); } catch { // 进程可能已退出。 } } process.exit(exitCode);