Repository navigation
Conversation
Mirror the playwright.a11y.config pattern from GSA/sam-styles and GSA/ngx-uswds: an axe-core-backed Playwright spec scans every route declared in test-app/src/app/app.module.ts with the wcag2a/wcag2aa/ wcag21a/wcag21aa tags and diffs violation fingerprints against a committed baseline (currently empty). New violations fail CI; resolved violations must be removed from the baseline. Runs as npm run test:a11y and as a step in e2e.yml. Co-Authored-By: Erik Rozi <erik.rozi@cognition.ai>
|
I'll fix CI failures and address comments from users with write access that start with 'Devin'.
Original prompt from Devin Bot
|
…rom smoke config - UPDATE_A11Y_BASELINE=1 now only drops resolved fingerprints; new violations still fail, so the committed baseline can only shrink. - playwright.config.ts ignores e2e/accessibility/** so test:e2e no longer runs the route scan under its 30 s timeout; playwright.a11y.config.ts clears the ignore for the dedicated run. Co-Authored-By: Erik Rozi <erik.rozi@cognition.ai>
Task prompt
Description
Adds the WCAG 2.1 AA accessibility gate requested in upstream GSA#708, mirroring the
playwright.a11y.config.{mjs,ts}pattern already running in GSA/sam-styles (tests/accessibility/storybook-a11y.spec.mjs) and GSA/ngx-uswds (tests/accessibility/storybook-a11y.spec.ts):test-app/playwright.a11y.config.tsextends the existingtest-app/playwright.config.ts(sameng servewebServer), points attest-app/e2e/accessibility/, and runs serially with one worker and a 600 s timeout, exactly as the two reference configs do.test-app/e2e/accessibility/test-app-a11y.spec.tsuses@axe-core/playwrightwith thewcag2a/wcag2aa/wcag21a/wcag21aatags. This repo has no Storybook, so instead of walkingindex.jsonit reads the route table fromtest-app/src/app/app.module.tsand scans every declared route (the gallery-route convention from AGENTS.md). Violation fingerprints are<route> | <rule id> | <selector>with Angular'sng-tns-*/_ngcontent-*/_nghost-*noise stripped (taken from the ngx-uswds spec), diffed against the committed baseline. New violations fail unconditionally, including in update mode; resolved violations also fail until removed from the baseline withUPDATE_A11Y_BASELINE=1 npm run test:a11y, so the baseline can only shrink. The smoke config (test-app/playwright.config.ts) ignorese2e/accessibility/**sotest:e2edoes not re-run the scan under its 30 s budget. A sanity test proves axe reports a known contrast failure so a green run can never mean "axe did nothing".test-app/e2e/accessibility/wcag-2.1-aa-baseline.json— the committed baseline of current violations (empty; see headline number).npm run test:a11yat the root (delegates totest-app) and intest-app/package.json; the dashboard'sa11ycell is a declared boolean that its maintainers set "during config review" from the presence of aplaywright.a11y.config.*plus atest:a11yscript (dashboard/lib.mjsresolveA11y), so both are now present at the same names the other two repos use..github/workflows/e2e.ymlruns the gate right after the smoke test, asci.yamlin ngx-uswds andtest.ymlin sam-styles do.Dependency:
@axe-core/playwright4.13.0 (the exact pin ngx-uswds uses; sam-styles uses^4.13.0), which bringsaxe-core4.13.0.@playwright/teststays at the already-installed 1.63.0 (the same version ngx-uswds pins). The lockfile diff is limited to those two entries.Motivation and Context
Upstream issue: GSA#708 (sub-issue of the pipeline modernization epic GSA#562). The angular-upgrade-dashboard quality grid currently reports this library's accessibility gate as
not enforcedbecause only a Playwright smoke test exists.Type of Change (Select One and Apply Label)
bugfixlabelenhancementlabelbreakinglabelmaintenancelabelHeadline number
Distinct WCAG 2.1 AA violations in the committed baseline: 0 rule ids, 0 fingerprints. Routes scanned: 0 -> 3 (
/,/datepicker,/tabs). Per route axe reported:/13 rules passed / 0 violations / 0 incomplete;/datepicker19 / 0 / 0;/tabs21 / 0 / 0.Before: no axe run anywhere in the repo, no
test:a11yscript, no a11y config (dashboard:not enforced). After: three routes scanned on every PR against a committed baseline (dashboard pattern:WCAG 2.1 AA enforced).Evidence
test-app/playwright.a11y.config.ts:11-14(testDir: "./e2e/accessibility",timeout: 600_000,fullyParallel: false,workers: 1), same shape as sam-stylesplaywright.a11y.config.mjsand ngx-uswdsplaywright.a11y.config.ts.test-app/e2e/accessibility/test-app-a11y.spec.ts:17(WCAG_21_AA_TAGS).app.module.ts—test-app/e2e/accessibility/test-app-a11y.spec.ts:41-48; asserted non-empty at:93-96.test-app/e2e/accessibility/test-app-a11y.spec.ts:55-62; fingerprint format:64-72.test-app/e2e/accessibility/test-app-a11y.spec.ts:75-87.test-app/e2e/accessibility/test-app-a11y.spec.ts:104-108(app-root h1visible; every gallery route has one).test-app/e2e/accessibility/test-app-a11y.spec.ts:123-148; new-violation assertion runs before the update-mode write (:135-143), soUPDATE_A11Y_BASELINE=1can only drop resolved entries.test-app/playwright.config.ts:6(testIgnore: "**/accessibility/**"); the a11y config clears it attest-app/playwright.a11y.config.ts:12.npx playwright test --list: smoke = 7 tests in 3 files, a11y = 2 tests in 1 file.test-app/e2e/accessibility/wcag-2.1-aa-baseline.json:1([]).package.json:35(test:a11y→npm --prefix test-app run test:a11y),test-app/package.json:12(playwright test --config=playwright.a11y.config.ts).test-app/package.json:52,test-app/package-lock.json:44,1332-1344,9684-9693..github/workflows/e2e.yml:38-40("Enforce WCAG 2.1 AA on test-app routes").AGENTS.md:26,AGENTS.md:87,CONTRIBUTING.md:16-19,test-app/README.md:25-27.<input type="text" id="a11y-probe" />and a#aaa-on-white paragraph totest-app/src/app/home/home.component.html, rannpm run test:a11y, gotNew WCAG 2.1 A/AA violations ... ["/ | color-contrast | p", "/ | label | #a11y-probe"]and exit code 1; reverted the file. Repeated withUPDATE_A11Y_BASELINE=1 npm run test:a11y: still1 failedon/ | label | #a11y-probeand the baseline stayed[].How to Test
npm ci && npm ci --prefix test-app && (cd test-app && npx playwright install --with-deps chromium)npm run test:a11y<input>totest-app/src/app/home/home.component.htmland rerun — the gate must fail with/ | label | ...listed as a new violation.Expected result:
2 passed— the axe sanity test and the route scan, with zero new and zero resolved violations againsttest-app/e2e/accessibility/wcag-2.1-aa-baseline.json.Commands run and results
Screenshots (if appropriate)
N/A — no UI change; the gate runs headless in Playwright.
Decisions for the repository owners
The failing
DAST (medium/high gate)check is pre-existing onmaster, not caused by this PR. ZAP flagsVulnerable JS Library [10003]for moment.js 2.30.1 (CVE-2026-17495, GHSA-4p3w-j4w9-5jqw, published 2026-09-29, patched in 2.31.0) in the productionmain-*.js. Root cause:test-app/package-lock.jsonhas moment 2.31.0 (bumped in Bump the patch group across 1 directory with 14 updates GSA/sam-ui-elements#765), but library source under rootsrc/resolvesmomentfrom the rootnode_modules, and the rootpackage-lock.jsonstill pins 2.30.1;momentis not intest-app/dedupe-packages.ts, so the root copy is what gets bundled. Aworkflow_dispatchofsecurity.ymlon unmodifiedmasterfails with the same alert: https://github.com/COG-GTM/sam-ui-elements/actions/runs/39713948937. This PR adds no runtime dependency (git diff --stat origin/mastertouches notest-app/src,angular.json, or runtime deps). Fix in a separate PR: bump moment to 2.31.0 in the root lockfile (and/or addmomenttodedupe-packages.tsso the test-app copy wins).No violations to triage. The three current test-app routes produce zero WCAG 2.1 A/AA violations under axe-core 4.13.0, so the baseline is
[]and there is nothing to list by rule. The ratchet is therefore already at its floor: any future violation fails the gate outright.Scan surface is only what test-app renders. This repo has no Storybook; the gate covers the home page,
sam-datepicker-v2, andsam-tabs-next(the gallery routes from Fix inactive tab content rendering on top of active tab in sam-tabs-next GSA/sam-ui-elements#665/Fix sam-datepicker-v2 outside-click close and the tabindex/aria-hidden restore leak GSA/sam-ui-elements#666). The ~190 other library components are unscanned until they get a gallery route. Decide whether to (a) add gallery routes component by component as they are touched, which the spec picks up automatically, or (b) add a dedicated a11y gallery route that renders many components at once.Static states only. Each route is scanned once on load; the datepicker calendar popup, tab switches, and other interactive states are not scanned. ngx-uswds and sam-styles have the same limitation.
Rule set. The gate uses the four WCAG 2.0/2.1 A/AA axe tags only (
best-practicerules such asheading-orderandregionare excluded), matching both reference repos. Widening towcag22aaorbest-practicewould be a separate decision.Dashboard flag. angular-upgrade-dashboard's a11y cell is a hand-maintained boolean in
dashboard/lib.mjs(a11y: false // no axe/WCAG gate yetfor sam-ui-elements); once this lands upstream, that line needs flipping in that repo.Branch naming. The PR template asks for
gh-<number>-<slug>; this branch isdevin/1792635793-wcag-a11y-gate.Checklist
gh-<number>-<slug>)format:checkpasses (npm run format:check)lintpasses (npm run lint)buildpasses (cd test-app && npm run build)cd test-app && npm test)Link to Devin session: https://devin-gtm.devinenterprise.com/sessions/b254a19e762f4c6d88c91a92c3b940db
Open in Devin Desktop: https://devin-gtm.devinenterprise.com/desktop/session/b254a19e762f4c6d88c91a92c3b940db?variant=devin
Requested by: @erikrozi
Devin Review