Files
anki/playwright.config.ts
Fernando Lins a8ba664e99 fix(ci): fix flaky e2e editor tests caused by premature webServer readiness (#5116)
## Linked issue (required)

Fixes #5108

## Summary / motivation (required)

`/favicon.ico` responds as soon as mediasrv's HTTP thread starts, before
the profile's collection finishes loading, `setupMediaServer()` runs
synchronously in `AnkiQt.__init__`, while `setupProfile()` is deferred
via a `QTimer`. Playwright's `webServer.url` readiness check polled
`/favicon.ico`, so the first e2e test to run in a given CI run could
race the async profile load and time out waiting for `.note-editor`, an
intermittent false negative, not a real editor bug.

This adds `GET /_anki/readyz`, which only returns 200 once `aqt.mw.col`
is open (503 otherwise — Playwright's webServer keeps polling on
non-2xx/3xx responses), and points `playwright.config.ts`'s
`webServer.url` at it instead of `/favicon.ico`.

## Steps to reproduce (required, use N/A if not applicable)

1. Run `just test-e2e` repeatedly on a loaded machine (or add artificial
delay to profile loading).
2. Occasionally, the first test in `context-switching.spec.ts` times out
at `fixtures.ts:37` (`waitForSelector(".note-editor")`) because
Playwright started the test run before the collection had finished
opening.
3. See CI failure:
https://github.com/ankitects/anki/actions/runs/28748635969/job/85243844164

## How to test (required)

- Ran `just test-e2e` locally and confirmed the suite passes.
- Verified `/_anki/readyz` returns 503 immediately after mediasrv starts
and 200 once the profile/collection finishes loading, by curling it
during `qt/tests/launch_anki_for_e2e.py` startup.
- No new test was added — this is a fix to test *infrastructure* (the
readiness gate), not to editor behavior; existing e2e specs are the
regression coverage.

## Before / after behavior (optional)

Before: e2e run could fail intermittently on the first test with a
`.note-editor` timeout, unrelated to the change under test.
After: `webServer` only accepts traffic once the collection is open, so
no test races the profile-load window.

## Risk / compatibility / migration (optional)

Low risk. `/_anki/readyz` is a new, isolated, read-only route used only
by the e2e harness; no existing behavior changes.
2026-07-07 10:20:43 +03:00

41 lines
1.4 KiB
TypeScript

// Copyright: Ankitects Pty Ltd and contributors
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
import { defineConfig } from "@playwright/test";
const MEDIASRV_PORT = process.env.ANKI_API_PORT ?? "40000";
const PYENV_PYTHON = process.platform === "win32"
? "out\\pyenv\\Scripts\\python.exe"
: "out/pyenv/bin/python";
export default defineConfig({
testDir: "./ts/tests/e2e",
outputDir: "./out/e2e-report/",
fullyParallel: false,
workers: 1,
forbidOnly: !!process.env.CI,
retries: 0,
reporter: process.env.CI
? [["github"], ["html", { open: "never", outputFolder: "out/e2e-report" }]]
: "list",
use: {
baseURL: `http://127.0.0.1:${MEDIASRV_PORT}`,
trace: "retain-on-failure",
screenshot: "only-on-failure",
},
webServer: {
command: `${PYENV_PYTHON} qt/tests/launch_anki_for_e2e.py`,
// /favicon.ico responds as soon as the HTTP server thread starts,
// before the profile's collection has finished loading. /_anki/readyz
// only returns 200 once the collection is open, so tests never race
// against the async profile-load that follows server startup.
url: `http://127.0.0.1:${MEDIASRV_PORT}/_anki/readyz`,
timeout: 60_000,
reuseExistingServer: process.env.ANKI_E2E_REUSE_SERVER === "1",
stdout: "pipe",
stderr: "pipe",
env: { ANKI_API_PORT: MEDIASRV_PORT },
},
});