import { defineConfig, devices } from '@playwright/test'; // Hermetic e2e: Playwright builds the app in `mock` mode (the in-memory fake transport, so the // smoke needs no backend/gateway/Postgres) and serves the MINIFIED artifact via `vite preview`, // rather than the dev server. Running against the production-minified bundle is what the contour // ships, so the e2e now catches minification-only regressions — e.g. a reactive dependency the // minifier drops — which the unminified dev server silently hid. The build goes to dist-e2e/ so it // never clobbers the dist/ the bundle-size gate measures. export default defineConfig({ testDir: './e2e', fullyParallel: true, forbidOnly: !!process.env.CI, retries: process.env.CI ? 1 : 0, reporter: 'list', use: { baseURL: 'http://localhost:4173', trace: 'on-first-retry', }, webServer: { // `--base /` overrides the production relative base (`./`, which lets the gateway serve the SPA // under /app/ and /telegram/): served from the preview root, an absolute base keeps assets at // /assets/ so the SPA-fallback also boots a subpath like /telegram/. Base only prefixes asset // URLs, so the minified JS under test is identical to the contour's. // After the mock build, copy the real per-variant dawgs into the preview output (dist-e2e) so the // offline specs can play a real local vs_ai game; the files are e2e-only (never committed, never in // the production build). Source: E2E_DICT_DIR (CI: the fetched release; local: the sibling). Two // copies for the two loader tiers: e2edict/.dawg for the mock's session-gated fetchDict // (network tier) and dict/@.dawg for the native offline-first spec's bundled tier. command: 'pnpm exec vite build --mode mock --base / --outDir dist-e2e --emptyOutDir && node scripts/e2e-dict.mjs && DICT_DIR="${E2E_DICT_DIR:-../../scrabble-solver/dawg}" OUT_DIR=dist-e2e node scripts/bundle-dicts.mjs && pnpm exec vite preview --outDir dist-e2e --port 4173 --strictPort', url: 'http://localhost:4173', reuseExistingServer: !process.env.CI, timeout: 120_000, }, // Run the same hermetic specs in Chromium and WebKit (Safari's engine) so the UI is // exercised in both rendering/JS engines. Note: desktop WebKit on Linux does not // reproduce iOS Safari's text auto-inflation, so the `text-size-adjust` guard in // app.css is not regression-covered here — but engine-level CSS/JS differences are. // The mock e2e exercises the mobile-first app, so it runs in a portrait viewport by default — // the layout the gesture/zoom/history-drawer specs are written for. The wide landscape layout // (Game.svelte's two-column branch) has its own spec that overrides the viewport (landscape.spec.ts). projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'], viewport: { width: 390, height: 844 } } }, { name: 'webkit', use: { ...devices['Desktop Safari'], viewport: { width: 390, height: 844 } } }, ], });