Files
redefined-designs/frontend/playwright.config.ts
bermudalambandClaude Opus 5 6489486de3
Linting / lint (pull_request) Successful in 2m37s
SonarQube Analysis / sonarqube (pull_request) Failing after 1m43s
test(e2e): run the suite on one worker so a red run means something (#241)
The lever the plan deliberately held back, applied now that there is evidence it is needed. Better failure messages and a wider assertion timeout were not enough.

Measured on this branch, same commit, same machine. Two parallel runs each failed 3 of 155, and not the same three: password-reset and admin-inventory-filters in one, admin-email-settings, admin-inventory-filters and resend-verification in the next. All 16 tests from those three specs then passed when run serially, and the full suite passed 155 of 155 twice in a row. Failures that move between runs of identical code are contention, not defects — specs asserting over tables other specs are concurrently writing to.

The cost is about three minutes, roughly one minute parallel against 3.9 and 4.0 serially. A red run that means something is worth three minutes; the previous state was a suite whose result nobody could act on, which is what #245 had to skip a real test to work around.

If that time is ever needed back, the cheaper fix is giving each worker its own database rather than raising this number and reopening #241.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-31 20:13:37 -05:00

64 lines
3.1 KiB
TypeScript
Executable File

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'] } }]
});