import { defineConfig, devices } from "@playwright/test"; import { readFileSync, existsSync, mkdirSync } from "node:fs"; // Playwright does not load Next's .env.local; the E2E run needs the real Jev key and a stable APP_SECRET. if (existsSync(".env.local")) for (const line of readFileSync(".env.local", "utf8").split("\n")) { const m = /^([A-Z_]+)=(.*)$/.exec(line.trim()); if (m && !process.env[m[1]]) process.env[m[1]] = m[2]; } mkdirSync(".auth", { recursive: true }); export default defineConfig({ testDir: "./tests-e2e", fullyParallel: false, // one SQLite file, one app process workers: 1, retries: 0, timeout: 180_000, // real models are slow; say so with a timeout, not with retries reporter: [["list"]], use: { baseURL: "http://localhost:3000", trace: "retain-on-failure" }, projects: [ { name: "setup", testMatch: /auth\.setup\.ts/ }, // The expensive specs are chromium-only (each real run costs ~30s of model time) and reuse the // session the setup project wrote. { name: "chromium", use: { ...devices["Desktop Chrome"] }, dependencies: ["setup"], testIgnore: /session\.spec\.ts/ }, // Safari/WebKit only checks session survival: it is the one engine that refuses a Secure cookie on // http://localhost, which is exactly how the wizard "signed itself out" in a production build. { name: "webkit", use: { ...devices["Desktop Safari"] }, testMatch: /session\.spec\.ts/ }, // A real model run costs ~40s; the mobile project only needs the layout checks, not a second full run. { name: "mobile", use: { ...devices["Pixel 7"] }, dependencies: ["setup"], testMatch: /responsive\.spec\.ts/ }, ], webServer: { // Run the production server: the cookie flags and hydration behaviour differ from dev, and that // difference hid a sign-out bug from a dev-only suite. `npm run pree2e` builds before this boots. command: "npm run start", url: "http://localhost:3000/api/healthz", reuseExistingServer: false, timeout: 120_000, env: { DATA_DIR: ".playwright-data", APP_SECRET: process.env.APP_SECRET ?? "playwright-secret-value" } }, });