Files
sencho/playwright.config.ts
Anso 3f2b5348be fix(e2e): stop a failed two-factor spec from cascading the whole suite (#2009)
The E2E suite runs serially against one shared admin account, so a spec that
leaves server-side state dirty breaks every spec scheduled after it. The
two-factor spec was the worst offender: it enrols, consumes backup codes, then
disables, and its cleanup only ran when the block had captured both a secret
and two unused backup codes, with every error swallowed. A test failing
mid-chain destroys exactly that state, so MFA stayed enrolled and each later
login stopped at the challenge screen, paying the full readiness timeout three
times per test with retries until the job hit its ceiling.

Playwright also retries a serial block as a whole in a new worker process, so
the retry could not re-enrol either: the enrol endpoint answers 409 for an
already-enrolled account and the group failed again.

Reset the enrolment from beforeAll and afterAll through the existing emergency
CLI, which is the only path that does not require possession of a second
factor. The block is now self-healing on a retry and cannot leak state.

Two supporting changes:

- loginAs now recognises the challenge screen and a login that never left the
  sign-in form, and names the cause instead of timing out. A cascade costs
  seconds per test and says what to do about it.
- The suite bounds itself in CI, so a cascade fails the test step while the job
  is still alive to upload the report. A job cancelled by its own timeout runs
  no further steps, which is why the previous runs produced no evidence at all.
  The job ceiling is raised above that bound as a backstop.
2026-09-26 13:13:56 -04:00

65 lines
2.5 KiB
TypeScript

import { defineConfig, devices } from '@playwright/test';
/**
* Playwright E2E config for Sencho.
*
* Before running: ensure both dev servers are up:
* cd backend && npm run dev &
* cd frontend && npm run dev &
*
* Or use the webServer config below (which starts them automatically).
*/
export default defineConfig({
testDir: './e2e',
// Creates the test admin once so no spec depends on file order.
globalSetup: './e2e/global-setup.ts',
// Don't stop on first failure - show all results
maxFailures: 0,
// Bound the whole run in CI so a cascade fails the step instead of running
// until the job's timeout kills it. A job cancelled by its own timeout runs
// no further steps, so nothing would be uploaded and the next occurrence
// would have no evidence; failing here leaves the job alive long enough to
// collect the report. Locally the run is left unbounded, since a slow
// machine is not a broken build. The job's `timeout-minutes` sits above
// this value on purpose as a backstop for a wedged browser process.
globalTimeout: process.env.CI ? 15 * 60 * 1000 : 0,
// How long to wait for a single test
timeout: 30_000,
// How long to wait for an expect() assertion
expect: { timeout: 5_000 },
// Run tests serially - Sencho is a single-user app and tests share DB state
workers: 1,
// Retry once in CI: the deploy/health-gate specs drive real Docker container
// runs, so image-pull and gate-window timing varies on shared runners. One
// retry absorbs a transient blip without tripling the cost of a real failure;
// a test reported as flaky still needs a fix. trace: 'on-first-retry'
// captures the retried run.
retries: process.env.CI ? 1 : 0,
reporter: [['list'], ['html', { outputFolder: 'e2e/report', open: 'never' }]],
use: {
baseURL: 'http://localhost:5173',
// Persist auth state between tests in the same file
storageState: undefined,
trace: 'on-first-retry',
screenshot: 'only-on-failure',
},
projects: [
{
// Default project (what CI runs via --project=chromium). Skips the manual
// screenshot capture spec.
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
testIgnore: ['**/screenshots.spec.ts'],
},
{
// Manual-only project for capturing docs/images/. Run explicitly:
// npx playwright test --project=screenshots
name: 'screenshots',
use: { ...devices['Desktop Chrome'] },
testMatch: ['**/screenshots.spec.ts'],
},
],
});