Project
Write guard internals
Write guard: Playwright behaviours it relies on #
Verified on @playwright/test 1.62.1. Re-check each on a Playwright upgrade; every one has broken
a design once.
- Route handlers run newest first. A spec's own
page.routeregistered after the guard's would see a request before the guard. The fixture wraps every handler a spec registers (context and page) so the guard decides first, and keeps a predicate catch-all route as a second layer. - Playwright builds the test's context through the public
browser.newContext(_contextFactory). The lock onnewContextlets exactly one call through while the built-incontextfixture is being created (builtinWindow), and every guarded test is forced to create that context so the window always closes. - Teardown reads
context.request. A throwing getter breaks Playwright's own cleanup, sopage.request/context.requestare Proxies that refuse only the send methods. testInfo.attach(name, { path })copies the file. The report lists the copies; the gate matches the originals under the run directory to them by sha256.- A CLI file filter does not apply to dependency or teardown projects, and
PLAYWRIGHT_JSON_OUTPUT_NAMEresolves against the config's directory. Fixture projects are scoped byQA_PILOT_FEATURE; every output path passed to a run is absolute.
What the guard cannot see, by design (Node's own node:http, WebSocket frames, popups opened
outside the guarded context and more), is listed once, in the header of
qa-pilot/templates/write-guard.fixture.ts. Change it there, not here.
Where a trace may go #
effectiveEvidenceUpload in qa-pilot/scripts/lib/profile.mjs is the only place that decides,
and the loader writes its answer into the normalized profile: local under tracker: none and
on production, else the environment's explicit value, else reference. Four consumers must
agree with it: publish-payload.mjs (only tracker produces an attach), validate-report.mjs
rule 6 (pins reference and local traces by sha256), bug-report.mjs (only tracker attaches,
and no profile means reference), and the CI template, which uploads the run directory only when
GitHub confirms the repository is private. A new consumer of traces checks the same value; a new
evidence value is added to all five.
The approval ledger has two writers #
case-status.mjs --transitions (at publish) keeps an entry when an approved, unchanged spec passes
again and deletes every other; it never adds one. case-status.mjs --record-approvals (at review)
adds the hash of each pass QA approved and removes an approved failure. Both read the ledger
through one loader (lib/json-file.mjs): a missing file starts a new ledger, and a file that
does not parse, such as one mid-merge, is refused, because a ledger printed from an empty read
is written back and erases every earlier approval. bug-report.mjs reads bugs.json the same
way.
The hash (lib/spec-hash.mjs) covers the spec, the local files it imports and the case's steps,
expected outcomes, preconditions and fixture, and is taken when the run starts
(spec-shas.json). The run and CI compute it with that one function, and the publish reads the
run's spec-shas.json, so they cannot disagree about whether a test changed.