Some checks failed
Veza CI / Notify on failure (push) Blocked by required conditions
Veza CI / Rust (Stream Server) (push) Successful in 3m47s
Security Scan / Secret Scanning (gitleaks) (push) Successful in 50s
Veza CI / Backend (Go) (push) Successful in 5m25s
Veza CI / Frontend (Web) (push) Has been cancelled
E2E Playwright / e2e (full) (push) Has been cancelled
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>
185 lines
7.5 KiB
TypeScript
185 lines
7.5 KiB
TypeScript
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;
|
||
// 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
|
||
? 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,
|
||
// 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),
|
||
// 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,
|
||
},
|
||
],
|
||
});
|