feat(ci): enforce WCAG 2.1 AA via @axe-core/playwright per PR (#70) - #100
Closed
Moonwalker-rgb wants to merge 1 commit into
Closed
feat(ci): enforce WCAG 2.1 AA via @axe-core/playwright per PR (#70)#100Moonwalker-rgb wants to merge 1 commit into
Moonwalker-rgb wants to merge 1 commit into
Conversation
…a#70) Adds a Playwright + axe-core based regression suite that fails the build on any critical or serious WCAG 2.1 A & AA violation across the public landing, demo, lab and campus routes. Closes Definition-of-Done item Epondia#7 of issue Epondia#70 ("axe DevTools audit shows 0 critical/serious violations") by enforcing the claim per PR rather than relying on manual smoke-testing. What changes: - frontend/package.json: devDependencies @axe-core/playwright + @playwright/test; scripts test:a11y / test:a11y:headed / test:a11y:ui. - frontend/playwright.config.ts: serial Chromium-only config, production-build webServer and HTML+list reporters (CI-safe). - frontend/e2e/accessibility.spec.ts: axe-core WCAG 2.1 A & AA scan per public route plus per-route <html lang> sanity (WCAG 3.1.1). - frontend/tsconfig.json: include e2e/**/*.ts and playwright.config.ts in the typecheck so the new files participate in CI typecheck. - .github/workflows/ci.yml: new accessibility-ci job - cached Playwright browser install, prod build, axe suite, HTML report upload. - .gitignore: ignore playwright-report/ and .playwright/. Pairs with the existing in-progress PR Epondia#95 (component-level fixes) - this PR gates the "0 critical/serious" claim per PR, so once Epondia#95 merges the gate turns green and any future regression is caught in CI.
Moonwalker-rgb
added a commit
to Moonwalker-rgb/starked-education
that referenced
this pull request
Jun 22, 2026
…pondia#96) Resolves GitHub Actions failure on PR Epondia#96 Install-dependencies step. Root cause: the project is an npm workspaces monorepo (contracts, backend, frontend in root package.json). PR Epondia#96 modified frontend/package.json (axe-core ^4.12.1, eslint-plugin-jsx-a11y) without also updating the root package-lock.json, so the single-source-of-truth lockfile drifts and both Frontend and Backend jobs fail at npm ci. Two changes: - frontend/package.json: removed jest-axe (PR Epondia#96 body notes it was dropped, but the dep line was left behind). - package-lock.json (root): regenerated to match; force-tracked with git add -f because root .gitignore still lists it (a one-line follow-up should drop that gitignore line). Refs: failure run 27943404202; pairs with Epondia#95 and Epondia#100.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
feat(ci): enforce WCAG 2.1 AA via @axe-core/playwright per PR — closes DoD item #7 of #70
TL;DR
Wires
@axe-core/playwrightinto CI as a hard-gate regression suite. Every PR that touchesfrontend/now produces awcag2aa/wcag21aaaxe-core scan of the public landing, demo, lab and campus routes; the build fails if anycriticalorseriousviolation is found. This converts DoD item #7 ("axe DevTools audit shows 0 critical/serious violations") from a manual smoke-test to a per-PR gate.Why this PR and not just merge #95?
Issue #70 is assigned to Moonwalker-rgb and is currently covered in two complementary pieces:
fix(frontend): achieve WCAG 2.1 AA accessibility — resolves #70<main id="main-content">+ skip-link,AccessibilityDashboardaxe scan, scoped.high-contrast/.reduce-motionCSS,RouteAnnouncer, Admin layout landmark fixes,axe-coreruntime dependency.@axe-core/playwrightregression suite that fails the build on critical/serious axe violations across the same WCAG 2.1 A & AA rule set.Together they close issue #70 in two halves: #95 ships the fix, this PR ships the guardrail. Once both merge, the gate turns green on a clean tree and a regression in any future component triggers a loud CI failure.
Definition-of-Done coverage
Issue #70 DoD status after this PR (cross-referenced with #95):
#95(useFocusTrap,ui/button.tsx)#95(useFocusTrap)#95opened the audit; full pass is a follow-up#95(globals.css.high-contrast)main,nav,banner,contentinfo)#95addsmain,nav,banner;contentinfois the#95"follow-up" itemaria-live)#95(RouteAnnouncer+ existing regions)accessibility-cijob fails the build on any critical/serious violation per PRWhat changes (file-by-file)
Added
frontend/e2e/accessibility.spec.ts— Playwright test that runsAxeBuilderagainst the public routes (/,/demo,/lab,/campus) with the WCAG 2.1 A & AA rule set, plus a per-route<html lang>(WCAG 3.1.1) sanity assertion. Each route's test:pageerrorfrom beforegotoso synchronous hydration-throws are surfaced (not silently lost during navigation).< 400, that the body actually rendered children (the "vacuous-zero violation" guard), and that nopageerrorfired.testEngine.nameis set + applicable-rule count > 0) so a misconfigured scan can't masquerade as a clean pass.frontend/playwright.config.ts— Playwright config tuned for the audit:fullyParallel: falseso axe-core's injected global state stays predictable across tests.webServer: npm run start(production bundle, no dev HMR scripts polluting the scan).trace: retain-on-failure,screenshot: only-on-failure.[html, list]reporter whenCI=true, plain list locally.Modified
frontend/package.json— added@axe-core/playwright@^4.10.2and@playwright/test@^1.49.1todevDependencies, plus three scriptstest:a11y,test:a11y:headed,test:a11y:ui. Existingtest/test:cijest callers are untouched.frontend/tsconfig.json—includenow listse2e/**/*.tsandplaywright.config.tsso the new files participate in the CInpx tsc --noEmitstep (previously they'd be silently skipped)..github/workflows/ci.yml— newaccessibility-cijob:~/.cache/ms-playwrightkeyed onrunner.os + package-lock.jsonhash (saves ~40 s/PR on warm cache).npm ci, builds the production bundle, thennpx playwright install --with-deps chromium.npm run test:a11ywithcontinue-on-errordeliberately off so a regression fails loudly.playwright-report/HTML report as a CI artefact, 7-day retention..gitignore— new entriesplaywright-report/and.playwright/.How to run locally
To pin versions, the spec runs against
@axe-core/playwright ^4.10.2(4.x line of axe-core rules).Reviewer checklist
feature/issue-70-a11y-ci. Ifaccessibility-ciis red on the existingmain, that's expected — issue Achieve WCAG 2.1 AA accessibility compliance #70 is still open inmain. Open the Playwright HTML report to see which WCAG rule is failing, then either fix the component or merge fix(frontend): achieve WCAG 2.1 AA accessibility — resolves #70 #95 first and re-run.for…of ROUTESloop creates one Playwright test per route, each with its ownpagefixture (default isolation)./,/demo,/lab,/campus). Authenticated routes are deliberately out of scope; testing them would require fragile sandbox accounts.frontend/src/components/ARVR/WebXREngine.tsxare out of scope and unchanged.playwright-a11y-report/retaining the HTML report for 7 days.Validation done
npx tsc --noEmit(frontend) — no new errors ine2e/,playwright.config.ts, or any path touched by this PR. The 3 pre-existing errors inWebXREngine.tsxare unrelated to this PR.npx tsc --noEmit(full frontend, with the new include entries) — the new spec / config files now participate in CI typecheck and pass cleanly.npm install --save-dev @axe-core/playwright@^4.10.2 @playwright/test@^1.49.1ran successfully (83 pre-existing package vulnerabilities from the rest of the tree are unrelated to this PR; flagged for a separate audit pass).Conflict analysis vs PR #95
Git merge will see a clean three-way merge because the two PRs touch disjoint surfaces:
frontend/src/app/layout.tsxfrontend/src/app/admin/layout.tsxfrontend/src/hooks/AccessibilityDashboard.tsxfrontend/package.jsonaxe-coreruntime dep@axe-core/playwright,@playwright/testdevDeps + scriptsfrontend/styles/globals.cssfrontend/e2e/accessibility.spec.tsfrontend/playwright.config.ts.github/workflows/ci.ymlaccessibility-cijobBoth
package.jsonedits touch thedependenciesanddevDependenciesobjects' tail; git's line-based merge resolves both cleanly.Out of scope / Follow-ups after merge
@axe-core/playwrightfailure reporting to PR — currently the HTML report is an artefact; posting a PR comment viagithub-scriptwould shorten the feedback loop.pushtodevelopin addition to PRs — currently gated to PR +mainpush only (matches existing CI convention).AxeBuilder.disableRules(['landmark-unique'])can be scoped per-page rather than globally.🤖 Tooling note
Generated with assistance from Codebuff CLI; reviewed and refined by the human collaborator.