// Playwright config — runs smoke tests against the already-running PedScribe // container (no dev server spin-up). Expects BASE_URL, which scripts/e2e.sh // supplies. const { defineConfig, devices } = require('@playwright/test'); // BASE_URL must be a loopback origin (127.0.0.1 / localhost), not a container // hostname. The app is a secure context in production and is written on that // assumption: AccountBoundary mints a session generation with // crypto.randomUUID() on every sign-in. Over plain http on a hostname that is // not loopback the browser provides no crypto.randomUUID at all, so that call // throws, the boot handler's catch swallows it, and every test lands on the // login screen no matter how valid its session is — which is exactly what the // whole browser suite was doing. // // Chrome's --unsafely-treat-insecure-origin-as-secure was tried first and does // not work here: Playwright rejects the --user-data-dir it has to be paired // with, and the flag alone leaves isSecureContext false. Loopback needs no // flags, so scripts/e2e.sh runs the browser on the host network and reaches the // app through its published port instead. module.exports = defineConfig({ testDir: './tests', timeout: 30_000, expect: { timeout: 5_000 }, fullyParallel: false, retries: 0, workers: 1, reporter: [['list']], use: { baseURL: process.env.BASE_URL || 'http://127.0.0.1:3553', // The app registers a service worker that answers every /api/ request with // its own fetch(). A request made inside a service worker never reaches // page.route, so mockAI could not intercept anything while one was running // and the tests called the real model. Blocking registration puts the // requests back in the page, where the mocks can see them. serviceWorkers: 'block', trace: 'retain-on-failure', screenshot: 'only-on-failure', actionTimeout: 5_000, navigationTimeout: 15_000, }, projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'] } }, // Mobile pass — catches layout regressions at ~375 px (iPhone SE) { name: 'mobile-chrome', use: { ...devices['Pixel 5'] } }, ], });