Ctrl K
ComunidadGuías…

playwright.config.ts

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
/// <reference types="node" />
import { defineConfig, devices } from "@playwright/test";
import { resolve } from "node:path";
import { capturePerformanceEnvironment, performanceSampleCount } from "./scripts/browser-performance";

// 这个根层配置经常被编辑器作为独立文件打开;文件级 Node types 避免 TS Server
// 还没关联 tsconfig.playwright.json 时误报 process/node 内置类型缺失。
const env = process.env;
const performanceMode = env.RENEWLET_E2E_PERFORMANCE === "1";
const profilingMode = env.RENEWLET_E2E_PROFILE === "1";
const previousDist = env.RENEWLET_E2E_PREVIOUS_DIST;
if (previousDist && performanceMode) throw new Error("Deployment upgrade journeys must run separately from performance samples");
if (profilingMode && !performanceMode) throw new Error("RENEWLET_E2E_PROFILE requires the isolated performance fixture");
// 根包由 Playwright 按 CommonJS 加载;指纹根目录跟随配置文件,不依赖调用者 cwd。
const performanceEnvironment = performanceMode ? capturePerformanceEnvironment(__dirname) : undefined;
const productionClientBuild = profilingMode
  ? "pnpm --filter @renewlet/client exec vite build --sourcemap"
  : performanceMode ? "pnpm exec tsx scripts/build-performance-client.ts" : "pnpm --filter @renewlet/client build";

// Playwright 会为 reporter 设置 FORCE_COLOR;继承 NO_COLOR 会让 Node 在每个 webServer 子进程重复打印冲突告警。
delete env.NO_COLOR;
delete env.no_color;

// 本地 E2E 依赖 127.0.0.1 上的 Go server 和 Vite。继承用户代理配置时,
// localhost 请求可能被转发到外部代理,导致 healthcheck 或 API 等待随机失败。
for (const key of ["NO_PROXY", "no_proxy"]) {
  const values = new Set(
    (env[key] ?? "")
      .split(",")
      .map((value) => value.trim())
      .filter(Boolean),
  );
  values.add("127.0.0.1");
  values.add("localhost");
  values.add("::1");
  env[key] = Array.from(values).join(",");
}

const proxyEnv = {
  NO_PROXY: env.NO_PROXY ?? "",
  no_proxy: env.no_proxy ?? "",
};

// 避开 Linux 默认临时端口段 32768–60999,避免服务监听前被出站连接占用;仍拒绝复用已有服务。
const e2eServerPort = 19090;
const e2eClientPort = 15173;
const e2eServerURL = `http://127.0.0.1:${e2eServerPort}`;
const e2eClientURL = `http://127.0.0.1:${e2eClientPort}`;
const adminStorageState = "e2e/.auth/admin.json";
const browserExecutablePath = env.RENEWLET_E2E_BROWSER_EXECUTABLE?.trim();
// 本地可能遇到 Playwright CDN/TLS 被代理拦截;只在显式传入时用系统浏览器,CI 仍使用 hermetic 浏览器。
const localBrowserFallback = browserExecutablePath ? { launchOptions: { executablePath: browserExecutablePath } } : {};

// 端口必须保持拆分:后端只给 PocketBase/Go API,浏览器页面只从前端的 Vite 入口进入。
// 如果把 baseURL 指到后端端口,headed 调试会看到 PocketBase UI 而不是 Renewlet 前端。
export default defineConfig({
  testDir: "./e2e",
  fullyParallel: false,
  workers: 1,
  // 性能样本不能自动重试到变绿;原始失败与不足十次的分组必须保留。
  retries: performanceMode ? 0 : env.CI ? 1 : 0,
  metadata: performanceEnvironment ? { performance: performanceEnvironment } : {},
  // CPU/commit 诊断不能生成可被比较器接受的生产耗时报告。
  reporter: performanceMode && !profilingMode ? [["list"], ["./e2e/support/performance-reporter.ts"]] : [
    ["list"], ["html", { open: "never" }],
    ["./e2e/support/server-diagnostics-reporter.ts", { outputFile: resolve(__dirname, "test-results/server-diagnostics.json") }],
  ],
  timeout: 90_000,
  expect: {
    timeout: 10_000,
  },
  use: {
    baseURL: e2eClientURL,
    locale: "zh-CN",
    timezoneId: "Asia/Shanghai",
    screenshot: performanceMode ? "off" : "only-on-failure",
    trace: performanceMode ? "off" : "retain-on-failure",
    video: performanceMode ? "off" : "retain-on-failure",
    ...localBrowserFallback,
  },
  webServer: [
    {
      // 每次清空 e2e 数据目录,让 setup project 拥有唯一的初始化状态;不复用旧 server
      // 可以避免 storage state 与 PocketBase SQLite 数据跨测试轮次串味。
      command: `rm -rf ./pb_data_e2e && go run ./cmd/renewlet serve --http=127.0.0.1:${e2eServerPort} --dir=./pb_data_e2e`,
      cwd: "./apps/docker-server",
      env: {
        ...proxyEnv,
        SETUP_ENABLED: "true",
        GOMEMLIMIT: "128MiB",
      },
      url: `${e2eServerURL}/api/app/health`,
      reuseExistingServer: false,
      timeout: 120_000,
      // Go 与 Vite 都可能在 stdout 输出诊断;两条流都交给 reporter,不能仅依赖页面 console 事件。
      stdout: "pipe",
      stderr: "pipe",
    },
    {
      // 性能模式重建生产产物;普通 E2E 仍重建 optimizer,不复用开发机残留缓存。
      command: performanceMode || previousDist
        ? `${productionClientBuild} && pnpm --dir apps/web exec vite preview --host 127.0.0.1 --port ${e2eClientPort} --strictPort`
        : `pnpm --dir apps/web exec vite --force --host 127.0.0.1 --port ${e2eClientPort} --strictPort`,
      env: {
        ...proxyEnv,
        VITE_DEV_PROXY_TARGET: e2eServerURL,
        ...(performanceMode || previousDist ? { VITE_RENEWLET_RUNTIME: "docker" } : {}),
      },
      url: e2eClientURL,
      reuseExistingServer: false,
      timeout: performanceMode || previousDist ? 300_000 : 120_000,
      stdout: "pipe",
      stderr: "pipe",
    },
  ],
  projects: [
    {
      name: "setup",
      testMatch: /.*\.setup\.ts/,
      testIgnore: ["**/cloudflare-check/**", "**/performance.setup.ts"],
      use: { ...devices["Desktop Chrome"] },
    },
    ...(performanceMode ? [{
      name: "performance-probe",
      testMatch: ["**/performance-probe.spec.ts", ...(profilingMode ? [] : ["**/performance-vitals-probe.spec.ts"])],
      use: { ...devices["Desktop Chrome"] },
    }, {
      name: "performance-seed",
      dependencies: ["setup", "performance-probe"],
      testMatch: "**/performance.setup.ts",
      use: { ...devices["Desktop Chrome"] },
    }] : []),
    // setup 通过真实 Renewlet UI 生成登录态;业务项目只消费 storage state,
    // 这样每个 spec 独立验证旅程,又不重复穿过首次安装流程。
    {
      name: performanceMode ? "performance-desktop" : "desktop",
      dependencies: [performanceMode ? "performance-seed" : "setup"],
      repeatEach: performanceMode && !profilingMode ? performanceSampleCount : 1,
      testMatch: previousDist ? ["**/version-upgrade.spec.ts"] : performanceMode ? ["**/performance.spec.ts", ...(profilingMode ? [] : ["**/performance-vitals.spec.ts"])] : [
        "**/calendar-feed-management.spec.ts",
        "**/subscriptions.spec.ts",
        "**/subscription-renewal.spec.ts",
        "**/subscription-renewal-members.spec.ts",
        "**/settings.spec.ts",
        "**/lunar-calendar.spec.ts",
        "**/statistics.spec.ts",
        "**/release-smoke.spec.ts",
        "**/route-progress.spec.ts",
        "**/report-exchange-rates.spec.ts",
      ],
      use: {
        ...devices["Desktop Chrome"],
        storageState: adminStorageState,
      },
    },
    {
      name: performanceMode ? "performance-mobile" : "mobile",
      dependencies: [performanceMode ? "performance-seed" : "setup"],
      repeatEach: performanceMode && !profilingMode ? performanceSampleCount : 1,
      testMatch: previousDist ? ["**/version-upgrade.spec.ts"] : performanceMode ? ["**/performance.spec.ts", ...(profilingMode ? [] : ["**/performance-vitals.spec.ts"])] : ["**/mobile-*.spec.ts", "**/lunar-calendar.spec.ts", "**/route-progress.spec.ts", "**/report-exchange-rates.spec.ts"],
      use: {
        ...devices["Pixel 5"],
        storageState: adminStorageState,
      },
    },
  ],
});