101 lines
3.7 KiB
JavaScript
101 lines
3.7 KiB
JavaScript
|
|
// Playwright E2E / visual-regression / a11y harness for BillTracker.
|
||
|
|
// See docs/QA_PLAN.md §4.4 and the B-UI / B14 / B15 batches.
|
||
|
|
//
|
||
|
|
// Setup (one-time): npm install && npx playwright install chromium
|
||
|
|
// Run: npm run test:e2e (headless)
|
||
|
|
// npm run test:e2e:ui (watch/debug)
|
||
|
|
// npm run test:e2e:update (re-baseline screenshots)
|
||
|
|
//
|
||
|
|
// SAFETY: the two `webServer` entries boot the app against a SCRATCH DB
|
||
|
|
// (e2e/setup/prepare-db.js seeds it) on DEDICATED ports (5199 UI / 3099 API), so
|
||
|
|
// E2E never touches your real db/bills.db and never reuses a dev server running
|
||
|
|
// on 5173/3000. `reuseExistingServer` is false for the same reason.
|
||
|
|
const { defineConfig, devices } = require('@playwright/test');
|
||
|
|
const { scratchDbPath, API_PORT, UI_PORT } = require('./e2e/constants');
|
||
|
|
|
||
|
|
const baseURL = `http://localhost:${UI_PORT}`;
|
||
|
|
|
||
|
|
module.exports = defineConfig({
|
||
|
|
testDir: './e2e',
|
||
|
|
fullyParallel: true,
|
||
|
|
forbidOnly: !!process.env.CI,
|
||
|
|
retries: process.env.CI ? 2 : 0,
|
||
|
|
workers: process.env.CI ? 1 : undefined,
|
||
|
|
reporter: [['list'], ['html', { open: 'never' }]],
|
||
|
|
|
||
|
|
use: {
|
||
|
|
baseURL,
|
||
|
|
trace: 'on-first-retry',
|
||
|
|
screenshot: 'only-on-failure',
|
||
|
|
video: 'retain-on-failure',
|
||
|
|
},
|
||
|
|
|
||
|
|
// Visual-regression tolerance. Screenshots are OS/font-sensitive, so baselines
|
||
|
|
// are committed per-platform (Playwright suffixes them with the platform).
|
||
|
|
expect: {
|
||
|
|
toHaveScreenshot: { maxDiffPixelRatio: 0.02, animations: 'disabled' },
|
||
|
|
},
|
||
|
|
|
||
|
|
projects: [
|
||
|
|
// Logs in once and writes STORAGE_STATE; browser projects depend on it.
|
||
|
|
{ name: 'setup', testMatch: /auth\.setup\.js/ },
|
||
|
|
|
||
|
|
{
|
||
|
|
name: 'chromium-desktop',
|
||
|
|
use: { ...devices['Desktop Chrome'] },
|
||
|
|
dependencies: ['setup'],
|
||
|
|
testIgnore: [/auth\.setup\.js/, /api\.probe\.spec\.js/, /a11y\.authed\.spec\.js/],
|
||
|
|
},
|
||
|
|
{
|
||
|
|
name: 'chromium-mobile',
|
||
|
|
use: { ...devices['Pixel 5'] },
|
||
|
|
dependencies: ['setup'],
|
||
|
|
testIgnore: [/auth\.setup\.js/, /api\.probe\.spec\.js/, /a11y\.authed\.spec\.js/],
|
||
|
|
},
|
||
|
|
// Find-mode diagnostics (adversarial API probe + authenticated a11y scan).
|
||
|
|
// Own project so they don't run concurrently with / pollute the UI specs, and
|
||
|
|
// so open findings here don't turn the default suite red. Run on demand:
|
||
|
|
// `npm run test:e2e:probe`. Fold a spec into the default projects once its
|
||
|
|
// findings are fixed (it becomes a passing regression guard).
|
||
|
|
{
|
||
|
|
name: 'probe',
|
||
|
|
testMatch: /(api\.probe|a11y\.authed)\.spec\.js/,
|
||
|
|
use: { ...devices['Desktop Chrome'] },
|
||
|
|
dependencies: ['setup'],
|
||
|
|
},
|
||
|
|
// Cross-browser (WebAuthn/Select behavior differs) — enable when ready:
|
||
|
|
// { name: 'firefox', use: { ...devices['Desktop Firefox'] }, dependencies: ['setup'], testIgnore: /auth\.setup\.js/ },
|
||
|
|
// { name: 'webkit', use: { ...devices['Desktop Safari'] }, dependencies: ['setup'], testIgnore: /auth\.setup\.js/ },
|
||
|
|
],
|
||
|
|
|
||
|
|
// Two servers: the API (node) and the Vite UI that proxies /api to it. Both
|
||
|
|
// run against the scratch DB on dedicated ports. Playwright waits for each
|
||
|
|
// `url` to respond before starting tests.
|
||
|
|
webServer: [
|
||
|
|
{
|
||
|
|
command: 'node server.js',
|
||
|
|
url: `http://localhost:${API_PORT}/api/version`,
|
||
|
|
reuseExistingServer: false,
|
||
|
|
timeout: 120_000,
|
||
|
|
stdout: 'ignore',
|
||
|
|
stderr: 'pipe',
|
||
|
|
env: {
|
||
|
|
DB_PATH: scratchDbPath(),
|
||
|
|
PORT: String(API_PORT),
|
||
|
|
BIND_HOST: '127.0.0.1',
|
||
|
|
},
|
||
|
|
},
|
||
|
|
{
|
||
|
|
command: `npm run dev:ui -- --port ${UI_PORT} --strictPort`,
|
||
|
|
url: baseURL,
|
||
|
|
reuseExistingServer: false,
|
||
|
|
timeout: 120_000,
|
||
|
|
stdout: 'ignore',
|
||
|
|
stderr: 'pipe',
|
||
|
|
env: {
|
||
|
|
API_PORT: String(API_PORT),
|
||
|
|
},
|
||
|
|
},
|
||
|
|
],
|
||
|
|
});
|