import { defineConfig } from '@playwright/test'; import * as os from 'node:os'; import * as path from 'node:path'; // Smoke-level e2e (WP-19): one happy path, one degraded path, against the REAL // backend — proving the FE+BE seam, not replacing component/unit tests. const baseURL = process.env['E2E_BASE_URL'] ?? 'http://localhost:4200'; // WP-74: give the backend a THROWAWAY SQLite file per `npm run e2e` invocation // instead of its default `bigregister.db`, so two separate runs never see each // other's leftover Applications/Documents/Briefs (`Program.cs:44-46` already reads // `ConnectionStrings__AppDb` from `IConfiguration` — zero backend change; the same // trick `TestWebApplicationFactory.cs` uses per xUnit test class). `Program.cs:93` // self-migrates a fresh file on startup, so a brand-new path is immediately usable. // // UNIQUE filename per invocation (pid + timestamp), not a single fixed name: a fixed // name that gets deleted WHILE the backend still has it open only stays safe if // Microsoft.Data.Sqlite's connection pool never needs to open a fresh native handle // by path after the delete — under `fullyParallel: true` (several workers hitting // the backend concurrently) a pool miss is a real, if intermittent, risk, and it // would silently recreate an empty, unmigrated file mid-run (every query after that // would 500 on "no such table"). A unique path sidesteps the whole question: nothing // ever deletes the file THIS run is actively using. The tradeoff is litter in the OS // temp dir across many runs, since Playwright has no webServer teardown hook to // delete it once the run ends — `globalSetup` (below) sweeps prior runs' files at // the start of each new run instead. That's safe regardless of ordering: it only // ever touches OTHER runs' paths, whose processes have already exited. const dbPath = path.join(os.tmpdir(), `big-register-e2e-${process.pid}-${Date.now()}.db`); // Shared with `e2e/global-setup.ts`, which runs in this SAME node process (it's // loaded and invoked by the Playwright runner right after `webServer` comes up, not // spawned separately) — so this assignment is visible there without a second import. process.env['E2E_DB_PATH'] = dbPath; export default defineConfig({ testDir: './e2e', timeout: 30_000, fullyParallel: true, retries: process.env['CI'] ? 1 : 0, reporter: process.env['CI'] ? 'github' : 'list', // Sweeps `big-register-e2e-*.db*` files left behind by EARLIER invocations (never // this run's own `dbPath` — see its comment above). Runs after `webServer` is // already up (Playwright always starts webServer before globalSetup — there's no // config knob to reverse that), which is exactly why it must never touch the // current run's own file. globalSetup: './e2e/global-setup.ts', use: { baseURL, trace: 'on-first-retry', }, projects: [{ name: 'chromium', use: { browserName: 'chromium' } }], // Playwright owns both servers' lifecycle — start, wait-for-ready, tear down — in the // one `npm run e2e` process, so `npm run e2e` is self-contained locally AND in CI. // Do NOT background them as separate CI steps: a process started with `&` in one // Actions `run:` step is killed when that step's shell exits, so a later `wait-on` // step hangs forever on servers that are already gone (the 2-hour e2e hang). // `reuseExistingServer` locally lets you run against an already-running app (incl. the // docker stack on 4200/5000); CI always starts fresh. Backend gets a longer timeout — // `dotnet run` cold-restores+builds before it listens. webServer: [ { command: 'dotnet run --project backend/src/BigRegister.Api --urls http://localhost:5000', url: 'http://localhost:5000/swagger', // WP-74 caveat, read before "fixing" this to `false`: this stays `!CI` (reuse // locally) on purpose, matching the FE server below and the docker-stack note // above it — turning reuse off would make `npm run e2e` hard-fail with // "port already in use" for anyone who already has the docker stack (or a // plain `dotnet run`) up on :5000, a real local-workflow regression for a POC // convenience feature. The tradeoff this buys: `env` below (the throwaway DB) // ONLY applies when Playwright itself spawns the process — reusing an // already-running backend silently falls back to THAT process's own DB // (typically the shared dev `bigregister.db`), so the isolation this WP adds // is real in CI (`reuseExistingServer` is always `false` there) and in the // common local case of "nothing was already running on :5000", but not if you // deliberately point e2e at an already-running shared backend — that was // already shared state before this WP and still is. reuseExistingServer: !process.env['CI'], timeout: 180_000, env: { ConnectionStrings__AppDb: `Data Source=${dbPath}` }, }, { command: 'npm start', url: baseURL, reuseExistingServer: !process.env['CI'], timeout: 120_000, }, ], });