Files
2026-05-27 23:29:53 +01:00

36 lines
2.5 KiB
Markdown

## Architecture
`attachAxe` is a thin, deterministic wrapper around `@axe-core/playwright`'s `AxeBuilder`. The factory takes a `Page` and per-attach defaults, returns an `AxeHandle` whose two methods (`assertNoViolations`, `getViolations`) build a fresh `AxeBuilder` on each call so callers can scope per-assertion (e.g. exclude a known-noisy third-party widget for one check without leaking the override).
### Adapter choice
`@axe-core/playwright` is the canonical Playwright adapter and ships the axe-core engine as a CDN-free script that is `evaluate()`-injected into the page. It matches the rest of the testing-e2e stack (Playwright primitives, no Node-only deps). Alternatives (`axe-playwright`, hand-rolled `page.addScriptTag`) were rejected: the former is community-maintained with weaker semver discipline, the latter duplicates work and forces us to track axe-core releases manually.
### Defaults
- **Rule set:** WCAG 2.1 AA (`['wcag2a','wcag2aa','wcag21a','wcag21aa']`). 2.2 tags are MAY-include via `runOnly` override, not default, until baseline runs prove no churn.
- **Impact gate:** `serious` + `critical` fail by default. `strict: true` widens to all four (`minor`, `moderate`, `serious`, `critical`).
- **Scope:** entire page unless `include`/`exclude` provided.
### Reporter
Violations are written to `playwright-report/a11y/<spec-name>.json` keyed by rule id, with `impact`, `nodes[].target`, `nodes[].html`, `helpUrl`. The path is fixed so the release-readiness aggregator (owned by `enterprise-testbed-verification`) can glob a single directory.
### Error model
`A11yError extends TestingE2eError` carrying `{ code, cause?, context: { violations, ruleSet, url } }`. Codes:
- `TESTING_E2E.A11Y.VIOLATIONS_FOUND` — assertion failed.
- `TESTING_E2E.A11Y.AXE_LOAD_FAILED` — axe-core script injection failed (CSP, navigation race).
- `TESTING_E2E.A11Y.INVALID_RULESET` — caller passed an unknown WCAG tag.
### Baseline-update workflow
There is no baseline file. Either the page passes the rule set or it fails — drift is impossible by construction. To temporarily ignore a rule, `disableRules: ['rule-id']` with a code-comment justification and an issue link is the only mechanism.
### Cross-links
- Release-readiness gate: `../enterprise-testbed-verification/proposal.md`.
- Audit driver: `/mnt/s/web/genesis/docs/superpowers/specs/2026-04-25-test-suite-audit.md` §2 row 1 and §7 row "Inspect browser console".
- Project rules: `/mnt/s/web/genesis/CLAUDE.md` "Web App & Page Verification".