import { createHash } from 'node:crypto'; import path from 'node:path'; import { defineConfig, devices } from '@playwright/test'; /** * End-to-end tests live in `e2e//*.spec.ts` and run against a production build served by * `vite preview`. `E2E_DEV=1` runs them against the Vite dev server instead (no rebuild). * * Several agents run e2e suites at the same time from sibling worktrees (`-`), so each * worktree gets its own port. Otherwise one worktree could silently reuse another worktree's server * and test the wrong code. `E2E_PORT` overrides the port. */ function worktreePort(): number { if (process.env.E2E_PORT) return Number(process.env.E2E_PORT); const folder = path.basename(process.cwd()); const areas = [ 'editor', 'sync', 'databases', 'search', 'plugins', 'desktop', 'importers', 'ci', 'docs', ]; const area = areas.findIndex((name) => folder.endsWith(`-${name}`)); if (area >= 0) return 4200 + (area + 1) * 10; if (folder === 'Tessera' || folder === 'tessera') return 4173; const hash = createHash('sha256').update(process.cwd()).digest().readUInt16BE(0); return 4300 + (hash % 500); } const PORT = worktreePort(); const useDevServer = process.env.E2E_DEV === '1'; const baseURL = `http://localhost:${PORT}`; const viewport = { width: 1440, height: 900 }; export default defineConfig({ testDir: 'e2e', testMatch: '**/*.spec.ts', outputDir: 'test-results', fullyParallel: true, forbidOnly: !!process.env.CI, retries: process.env.CI ? 2 : 0, workers: process.env.CI ? 2 : undefined, reporter: process.env.CI ? [['list'], ['html', { open: 'never', outputFolder: 'playwright-report' }]] : [['list']], timeout: 45_000, expect: { timeout: 7_500 }, use: { baseURL, viewport, colorScheme: 'light', locale: 'en-US', timezoneId: 'UTC', trace: 'retain-on-failure', // Requests a service worker answers bypass `page.route`, so specs run without it; the offline // spec turns it on (e2e/architect/offline.spec.ts). serviceWorkers: 'block', screenshot: 'only-on-failure', video: 'off', }, projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'], viewport } }, // Firefox runs the same steps two to three times slower on a busy machine (a plugin install // flow measured 60 s against Chromium's 25 s), so its budget is doubled. Assertions don't change. { name: 'firefox', timeout: 90_000, use: { ...devices['Desktop Firefox'], viewport }, }, ], webServer: { command: useDevServer ? `pnpm --filter @tessera/web exec vite --port ${PORT} --strictPort` : `pnpm --filter @tessera/web build && pnpm --filter @tessera/web exec vite preview --port ${PORT} --strictPort`, url: baseURL, reuseExistingServer: !process.env.CI, timeout: 300_000, stdout: 'ignore', stderr: 'pipe', }, });