veza/tests/e2e/playwright.config.ts

186 lines
7.5 KiB
TypeScript
Raw Normal View History

import { defineConfig, devices } from '@playwright/test';
/**
* Configuration Playwright pour la suite E2E complète de VEZA.
*
* Usage :
* npm run e2e Chromium seul, parallèle (dev rapide)
* npm run e2e:all Tous les navigateurs (pré-commit)
* npm run e2e:critical Tests @critical uniquement
* PLAYWRIGHT_WORKERS=1 npm run e2e Séquentiel (debug rate-limit)
*
* Variables d'environnement :
* PLAYWRIGHT_BASE_URL URL du frontend (défaut: http://localhost:5173)
* PORT Port du dev server Vite (défaut: 5173)
* PLAYWRIGHT_WORKERS Nombre de workers (défaut: 3)
* PLAYWRIGHT_ALL "1" pour tous les navigateurs (défaut: chromium seul)
* CI Active les retries, reporters lourds, tous les browsers
*/
const isCI = !!process.env.CI;
perf(ci): cut frontend unit + e2e wall time ~5-10× (vitest threads + chromium-only + browser cache) CI runtime audit: - vitest: ~6min on 12-core R720 — `maxThreads: 2` AND `fileParallelism: false` made the 285-file suite essentially file-serial. - playwright e2e: ~1h30 — `workers: 2` in CI on a 12-core box, PLUS `allBrowsers = isCI` lit up 5 projects (chromium + firefox + webkit + mobile-chrome + mobile-safari) even though the workflow only runs `playwright install --with-deps chromium`. Firefox/webkit projects were silently failing/skipping for ~150 test slots each. - playwright install: ~150MB chromium download on every cold run, not cached. Three knobs flipped: (1) apps/web/vitest.config.ts - `fileParallelism: false` → `true` - `maxThreads: 2` → `6` Local bench: 344s → 130s (≈2.7× speedup). On a fresh CI box with cold setup the gain is wider since the setup overhead amortises across 6 workers instead of 2. (2) tests/e2e/playwright.config.ts - `allBrowsers = isCI || PLAYWRIGHT_ALL=1` → `PLAYWRIGHT_ALL=1` only. CI defaults to chromium-only; nightly cron can opt back into the full matrix by setting PLAYWRIGHT_ALL=1. - `workers: 2` (CI) → `6`. R720 has 12 cores; 6 leaves headroom for backend/postgres/redis containers. (3) .github/workflows/e2e.yml - Cache `~/.cache/ms-playwright` keyed on the resolved Playwright version. Cache hit → run `playwright install-deps` (apt-get only, ~5s). Cache miss → full install (~30-60s, first run after a Playwright bump). Combined ETA on the e2e workflow: ~10-15min vs ~1h30. The 5× project reduction is the dominant gain; workers and cache are smaller multipliers on top. If a fileParallelism-related regression shows up (cross-file global state, MSW mock leakage), the fix is test isolation — the previous caps were a workaround, not a root cause. SKIP_TESTS=1 — config-only, vitest already verified locally (285/285 file pass, 3469/3470 tests pass). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 14:04:52 +00:00
// allBrowsers controls whether the full chromium+firefox+webkit+mobile
// matrix runs. Defaults to chromium-only because:
// 1. The CI workflow only `npx playwright install --with-deps chromium`,
// so firefox/webkit/mobile binaries are never present — the projects
// were silently failing or skipping. Aligning the config with the
// actual install removes wasted retries on missing browsers.
// 2. Multi-browser is a coverage feature for nightly cron, not a
// per-push gate. Cron can opt in via PLAYWRIGHT_ALL=1.
// 3. 5× the project count was the dominant CI runtime factor — running
// chromium-only cuts e2e from ~1h30 to ~10-20min on its own.
const allBrowsers = process.env.PLAYWRIGHT_ALL === '1';
// Workers calibrated to the R720 (12 cores). 6 in CI leaves headroom
// for the backend Go process + postgres + redis containers running in
// the same job. Local stays at 4 (smaller dev machines + ERR_INSUFFICIENT_RESOURCES
// risk on Linux when chromium spawns ~workers×subprocesses).
const workerCount = process.env.PLAYWRIGHT_WORKERS
? parseInt(process.env.PLAYWRIGHT_WORKERS, 10)
: isCI
perf(ci): cut frontend unit + e2e wall time ~5-10× (vitest threads + chromium-only + browser cache) CI runtime audit: - vitest: ~6min on 12-core R720 — `maxThreads: 2` AND `fileParallelism: false` made the 285-file suite essentially file-serial. - playwright e2e: ~1h30 — `workers: 2` in CI on a 12-core box, PLUS `allBrowsers = isCI` lit up 5 projects (chromium + firefox + webkit + mobile-chrome + mobile-safari) even though the workflow only runs `playwright install --with-deps chromium`. Firefox/webkit projects were silently failing/skipping for ~150 test slots each. - playwright install: ~150MB chromium download on every cold run, not cached. Three knobs flipped: (1) apps/web/vitest.config.ts - `fileParallelism: false` → `true` - `maxThreads: 2` → `6` Local bench: 344s → 130s (≈2.7× speedup). On a fresh CI box with cold setup the gain is wider since the setup overhead amortises across 6 workers instead of 2. (2) tests/e2e/playwright.config.ts - `allBrowsers = isCI || PLAYWRIGHT_ALL=1` → `PLAYWRIGHT_ALL=1` only. CI defaults to chromium-only; nightly cron can opt back into the full matrix by setting PLAYWRIGHT_ALL=1. - `workers: 2` (CI) → `6`. R720 has 12 cores; 6 leaves headroom for backend/postgres/redis containers. (3) .github/workflows/e2e.yml - Cache `~/.cache/ms-playwright` keyed on the resolved Playwright version. Cache hit → run `playwright install-deps` (apt-get only, ~5s). Cache miss → full install (~30-60s, first run after a Playwright bump). Combined ETA on the e2e workflow: ~10-15min vs ~1h30. The 5× project reduction is the dominant gain; workers and cache are smaller multipliers on top. If a fileParallelism-related regression shows up (cross-file global state, MSW mock leakage), the fix is test isolation — the previous caps were a workaround, not a root cause. SKIP_TESTS=1 — config-only, vitest already verified locally (285/285 file pass, 3469/3470 tests pass). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 14:04:52 +00:00
? 6
: 4;
export default defineConfig({
testDir: '.',
testMatch: '**/*.spec.ts',
testIgnore: ['**/node_modules/**'],
/* ── Parallélisme ──────────────────────────────────────────────── */
fullyParallel: true,
workers: workerCount,
/* ── Timeouts ──────────────────────────────────────────────────── */
timeout: 30_000, // 30s par défaut (était 60s)
expect: { timeout: 10_000 }, // 10s pour les assertions — React SPA needs time to hydrate
/* ── CI ────────────────────────────────────────────────────────── */
forbidOnly: isCI,
retries: isCI ? 2 : 0,
/* ── Reporters ─────────────────────────────────────────────────── */
reporter: isCI
? [
['list'],
['json', { outputFile: './test-results/results.json' }],
['html', { outputFolder: './playwright-report', open: 'never' }],
['github'],
]
: [
['list'],
['json', { outputFile: './test-results/results.json' }],
],
/* ── Global setup/teardown ─────────────────────────────────────── */
globalSetup: process.env.PLAYWRIGHT_SKIP_GLOBAL_SETUP ? undefined : './global-setup.ts',
globalTeardown: './global-teardown.ts',
/* ── Options partagées ─────────────────────────────────────────── */
use: {
baseURL: process.env.PLAYWRIGHT_BASE_URL || `http://127.0.0.1:${process.env.PORT || '5173'}`,
/* Traces/screenshots/vidéo — désactivés en local pour la perf */
trace: isCI ? 'on-first-retry' : 'off',
screenshot: isCI ? 'only-on-failure' : 'off',
video: isCI ? 'retain-on-failure' : 'off',
actionTimeout: 8_000,
navigationTimeout: 12_000,
locale: 'en-US',
/* Réutiliser le contexte navigateur entre tests du même fichier */
launchOptions: {
args: [
'--disable-gpu', // Pas besoin de GPU pour les tests
'--disable-dev-shm-usage', // Évite les crashes mémoire partagée
'--no-sandbox', // Plus léger
],
},
},
projects: [
/* ── Desktop Chrome — TOUJOURS actif ─────────────────────────── */
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
/* ── Firefox — seulement en CI ou avec PLAYWRIGHT_ALL=1 ──────── */
...(allBrowsers
? [
{
name: 'firefox',
use: { ...devices['Desktop Firefox'] },
},
]
: []),
/* ── Safari — seulement en CI ou avec PLAYWRIGHT_ALL=1 ───────── */
...(allBrowsers
? [
{
name: 'webkit',
use: { ...devices['Desktop Safari'] },
},
]
: []),
/* ── Mobile Chrome — tests @mobile uniquement ────────────────── */
...(allBrowsers
? [
{
name: 'mobile-chrome',
use: { ...devices['Pixel 5'] },
grep: /@mobile/,
},
]
: []),
/* ── Mobile Safari — tests @mobile uniquement ────────────────── */
...(allBrowsers
? [
{
name: 'mobile-safari',
use: { ...devices['iPhone 12'] },
grep: /@mobile/,
},
]
: []),
],
/* ── Auto-start backend + frontend ────────────────────────────── */
webServer: [
{
command: 'go run cmd/api/main.go',
cwd: '../../veza-backend-api',
port: 18080,
fix(ci): unblock CI red — gofmt + e2e webserver reuse + orders.hyperswitch_payment_id (Day 4) Three pre-existing infra issues surfaced by the Day 1→Day 3 push wave. Each is independent — bundled here because the goal is "ci.yml + e2e.yml green" before the v1.0.9 tag, and they're all small. (1) gofmt — ci.yml golangci-lint v2 step Five files were unformatted on main. Pre-existing (untouched by my Item G work, but the formatter caught them now): - internal/api/router.go - internal/core/marketplace/reconcile_hyperswitch_test.go - internal/models/user.go - internal/monitoring/ledger_metrics.go - internal/monitoring/ledger_metrics_test.go Pure whitespace via `gofmt -w` — no behavior change. (2) e2e silent-fail — playwright webServer port collision The e2e workflow pre-starts the backend in step 9 ("Build + start backend API") so it can fail-fast on a non-ok health check. But playwright.config.ts had `reuseExistingServer: !process.env.CI` on the backend webServer entry — meaning in CI Playwright tried to spawn a SECOND backend on port 18080. The spawn collided with EADDRINUSE and Playwright silently exited before printing any test output. The artifact upload then warned "No files were found" because tests/e2e/playwright-report/ never got written, and the job ended in `Failure` for an unrelated reason (the artifact upload step's GHESNotSupportedError). Fix: backend `reuseExistingServer: true` always — workflow + dev both pre-start backend on 18080. Vite stays `!CI` because the workflow doesn't pre-start it. Comment in playwright.config.ts documents the symptom so the next person debugging gets the pointer immediately. (3) orders.hyperswitch_payment_id missing in fresh DBs — migration 080 skip-branch + 099 ordering drift Migration 080 (`add_payment_fields`) wraps its ALTERs in "skip if orders doesn't exist". At authoring time orders existed earlier in the migration sequence; that ordering has since shifted (orders is now created at 099_z_create_orders.sql, AFTER 080). Result: in any freshly-migrated DB (CI, fresh dev, future restore drills) migration 080 takes the skip branch and the columns are never added — even though the Order model and the marketplace code rely on them. Symptom: every CI run logs pq: column "hyperswitch_payment_id" does not exist from the periodic ledger_metrics worker. Order checkout would also fail to persist payment_id at write time, breaking reconciliation. Fix: append-only migration 987 with idempotent `ADD COLUMN IF NOT EXISTS` + a partial index on the reconciliation hot path. Production envs that did pick up 080 in the original order are no-ops; fresh envs converge to the same end state. Rollback in migrations/rollback/. Verified locally: $ cd veza-backend-api && go build ./... && VEZA_SKIP_INTEGRATION=1 \ go test -short -count=1 ./internal/... (all green) SKIP_TESTS=1: backend-only Go + Playwright config + SQL. Frontend unit tests irrelevant to this commit. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 10:03:55 +00:00
// Backend is pre-started by the e2e workflow (Build + start backend
// API step) so it can fail-fast on a non-ok health check. Playwright
// must reuse it instead of trying to spawn a second instance on the
// same port — the spawn collides on EADDRINUSE and the entire test
// run silently exits before printing any test output (visible
// symptom: "No files were found with the provided path:
// tests/e2e/playwright-report/" in the artifact upload step).
// In dev, `make dev` (or `make dev-backend-api`) is the canonical
// pre-start path, so reuse is also correct there.
reuseExistingServer: true,
timeout: 30_000,
env: {
APP_ENV: 'test',
DISABLE_RATE_LIMIT_FOR_TESTS: 'true',
ACCOUNT_LOCKOUT_EXEMPT_EMAILS: 'user@veza.music,artist@veza.music,admin@veza.music,mod@veza.music,new@veza.music',
RATE_LIMIT_LIMIT: '10000',
RATE_LIMIT_WINDOW: '60',
},
},
{
command: 'npm run dev -- --host 127.0.0.1',
cwd: '../../apps/web',
port: parseInt(process.env.PORT || '5173', 10),
fix(ci): unblock CI red — gofmt + e2e webserver reuse + orders.hyperswitch_payment_id (Day 4) Three pre-existing infra issues surfaced by the Day 1→Day 3 push wave. Each is independent — bundled here because the goal is "ci.yml + e2e.yml green" before the v1.0.9 tag, and they're all small. (1) gofmt — ci.yml golangci-lint v2 step Five files were unformatted on main. Pre-existing (untouched by my Item G work, but the formatter caught them now): - internal/api/router.go - internal/core/marketplace/reconcile_hyperswitch_test.go - internal/models/user.go - internal/monitoring/ledger_metrics.go - internal/monitoring/ledger_metrics_test.go Pure whitespace via `gofmt -w` — no behavior change. (2) e2e silent-fail — playwright webServer port collision The e2e workflow pre-starts the backend in step 9 ("Build + start backend API") so it can fail-fast on a non-ok health check. But playwright.config.ts had `reuseExistingServer: !process.env.CI` on the backend webServer entry — meaning in CI Playwright tried to spawn a SECOND backend on port 18080. The spawn collided with EADDRINUSE and Playwright silently exited before printing any test output. The artifact upload then warned "No files were found" because tests/e2e/playwright-report/ never got written, and the job ended in `Failure` for an unrelated reason (the artifact upload step's GHESNotSupportedError). Fix: backend `reuseExistingServer: true` always — workflow + dev both pre-start backend on 18080. Vite stays `!CI` because the workflow doesn't pre-start it. Comment in playwright.config.ts documents the symptom so the next person debugging gets the pointer immediately. (3) orders.hyperswitch_payment_id missing in fresh DBs — migration 080 skip-branch + 099 ordering drift Migration 080 (`add_payment_fields`) wraps its ALTERs in "skip if orders doesn't exist". At authoring time orders existed earlier in the migration sequence; that ordering has since shifted (orders is now created at 099_z_create_orders.sql, AFTER 080). Result: in any freshly-migrated DB (CI, fresh dev, future restore drills) migration 080 takes the skip branch and the columns are never added — even though the Order model and the marketplace code rely on them. Symptom: every CI run logs pq: column "hyperswitch_payment_id" does not exist from the periodic ledger_metrics worker. Order checkout would also fail to persist payment_id at write time, breaking reconciliation. Fix: append-only migration 987 with idempotent `ADD COLUMN IF NOT EXISTS` + a partial index on the reconciliation hot path. Production envs that did pick up 080 in the original order are no-ops; fresh envs converge to the same end state. Rollback in migrations/rollback/. Verified locally: $ cd veza-backend-api && go build ./... && VEZA_SKIP_INTEGRATION=1 \ go test -short -count=1 ./internal/... (all green) SKIP_TESTS=1: backend-only Go + Playwright config + SQL. Frontend unit tests irrelevant to this commit. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 10:03:55 +00:00
// Vite is NOT pre-started by the e2e workflow, so Playwright spawns
// it in CI. In dev `make dev` already runs vite — reuse to avoid
// double-spawn on a known port.
reuseExistingServer: !process.env.CI,
timeout: 30_000,
},
],
});