import { defineConfig, devices } from '@playwright/test'; export default defineConfig({ testDir: './tests/e2e', // Kept true so a file's tests are independent units, but with one worker it // no longer interleaves them. See `workers` below. fullyParallel: true, // One worker, because the suite shares a single database. // // This was the lever #241 deliberately held back, to see first whether better // failure messages and a wider assertion timeout were enough. They were not. // Measured on this branch, same commit, same machine: parallel runs failed 3 // of 155 twice over, and the three were not the same three — password-reset // and admin-inventory-filters in one run, admin-email-settings, // admin-inventory-filters and resend-verification in the next. Run serially, // all 16 of those same tests passed; the full suite then passed 155 of 155 // twice in a row. // // So the failures were contention, not defects: specs asserting over tables // that other specs were concurrently writing to. The cost is about 3 minutes // — roughly 1 minute parallel against 3.9 and 4.0 serially — and a red run // that means something is worth three minutes. // // The cheaper fix, if that time is ever needed back, is giving each worker // its own database rather than raising this number and reopening #241. workers: 1, retries: process.env.CI ? 1 : 0, reporter: [['list']], use: { baseURL: 'http://localhost:5173', trace: 'on-first-retry', // The app turns off antd's transitions under this preference. Animated // popups never settle long enough for Playwright's stability check when // the machine is loaded, which showed up as clicks timing out on a button // that was plainly visible and enabled. reducedMotion: 'reduce' }, // Playwright's default is 5s, and nothing overrode it. That is generous on an // idle laptop and tight on this runner: a full CI pass takes around 21.5 // minutes on a single machine that also builds, migrates and runs three other // suites, and the #239 failure reported exactly `Timeout: 5000ms`. // // Costs nothing on a green run. This bounds how long a *failing* assertion // waits before giving up, not how long a passing one takes — a locator that // resolves in 200ms still resolves in 200ms. expect: { timeout: 10000 }, webServer: { command: 'npm run dev', url: 'http://localhost:5173', // Forwarded so the dev server instruments its modules when the run is a // coverage run. Without this the tests still pass and every page reports no // coverage at all, which publishes a 0% that reads as "the tests stopped // covering things" rather than "collection was never switched on". env: { COVERAGE: process.env.COVERAGE ?? '' }, // Reusing a server that was started without COVERAGE would silently collect // nothing, so a coverage run always starts its own. reuseExistingServer: !process.env.CI && process.env.COVERAGE !== 'true', timeout: 30000 }, projects: [{ name: 'chromium', use: { ...devices['Desktop Chrome'] } }] });