Files
collective-lists/apps/web/playwright.config.ts
Oier Bravo Urtasun a86b296ecd fix(e2e): pin Playwright baseURL to localhost regardless of PUBLIC_APP_URL
Root cause of the recurring "just test-all shows 27/16, playwright alone
shows 43/0" split:

  Justfile loads .env with dotenv-load, which sets PUBLIC_APP_URL to the
  LAN IP from the phone-preview config. playwright.config.ts was reading
  that env and using it as baseURL. The resulting LAN-IP OAuth round-trip
  (browser → GoTrue → Keycloak → browser) adds ~hundreds of ms of
  loopback routing per hop and pushes the $currentCollective hydration
  past Playwright's first synthetic keystroke, producing a silent early
  return in handleAdd (name typed but Enter never triggers a write).

  Running playwright directly skipped dotenv-load, baseURL fell back to
  localhost, and everything passed. Same code, same machine, different
  wrapper.

Fix
  apps/web/playwright.config.ts: baseURL hardcoded to 'http://localhost:5173'.
    The LAN IP stays in PUBLIC_APP_URL for on-device phone previews (step
    2/3 of the mobile-testing recipe in CLAUDE.md), but e2e always hits
    the loopback so the timing profile is deterministic.
  apps/web/tests/fixtures/login.ts: origin-check hardcoded to localhost
    to match.

Verification
  just test-all → exit 0, 228 tests green (34 pgTAP + 140 Vitest
    integration + 11 Vitest unit + 43 Playwright), 4 skipped (upstream
    Realtime presence + WebKit-only touch gestures).
  just test-e2e alone → 43 passed.
  pnpm exec playwright test alone → 43 passed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 00:48:10 +02:00

42 lines
1.2 KiB
TypeScript

import { defineConfig, devices } from '@playwright/test';
import path from 'path';
import { fileURLToPath } from 'url';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
export default defineConfig({
testDir: './tests/e2e',
globalSetup: './tests/fixtures/global-setup.ts',
fullyParallel: false, // sequential: sessions depend on shared Docker DB state
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 1 : 0,
workers: 1,
reporter: [['html', { open: 'never' }], ['list']],
use: {
// Playwright always talks to Vite over the loopback. The LAN IP
// variant in `.env` (PUBLIC_APP_URL) is for on-device phone previews
// and introduces extra routing latency that produces intermittent
// races in the OAuth → optimistic-write path; e2e needs to stay
// deterministic regardless of what the dev env has in PUBLIC_APP_URL.
baseURL: 'http://localhost:5173',
trace: 'on-first-retry',
screenshot: 'only-on-failure',
// Load anon storage state by default; individual tests override as needed
},
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] }
}
],
webServer: {
command: 'pnpm dev',
url: 'http://localhost:5173',
reuseExistingServer: true,
timeout: 120_000
}
});