test(e2e): axe-core WCAG A/AA checks on every kind of page

Colour contrast is left out until the dim tokens are retuned (owner looks first).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Lucas WintherandClaude Opus 5.5 committed 2026-11-12 11:12:17 +01:00
1 parent 5491230807
commit cf53e4280a
5 files changed
+34 -1

No files matched your search

+5
View File
@@ -18,6 +18,7 @@
"react-dom": "^19",
},
"devDependencies": {
"@axe-core/playwright": "^4.14.0",
"@playwright/test": "^1.65",
"@types/bun": "^1.4.4",
"@types/react": "^19.3.0",
@@ -61,6 +62,8 @@
"@astrojs/telemetry": ["@astrojs/telemetry@3.4.2", "", { "dependencies": { "ci-info": "^4.4.0", "dset": "^3.1.4", "is-docker": "^4.0.0", "package-manager-detector": "^1.9.0" } }, "sha512-2JOMRUxJrBSfiVDfeKLv1wBmvSIx1Y6u2YEO8awZ9qSq3u/rNAiUyFdOYi8dHv91kHrmYEQX7pRDw0J4xyj3UA=="],
"@axe-core/playwright": ["@axe-core/playwright@4.14.0", "", { "dependencies": { "axe-core": "~4.14.0" }, "peerDependencies": { "playwright-core": ">= 1.0.0" } }, "sha512-SGUhG5PIHd2eMdpgBF2PyQLOhX+qyDR2PT4cmoS4Qk2ak+jX/rrSvst9l0PGv5WTHolKpUMvVmoHpr4cZgf1RQ=="],
"@babel/helper-string-parser": ["@babel/helper-string-parser@7.29.13", "", {}, "sha512-fWegr+WsEsiMVW4KUNdIIzzMTZSzNiqhy0//6+2MRi2d5NFNsL/AYwBQW8eBnRbTBVRnDVzjaJEUqKaCWjMMdQ=="],
"@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="],
@@ -303,6 +306,8 @@
"astro": ["astro@7.4.7", "", { "dependencies": { "@astrojs/compiler-rs": "^0.5.2", "@astrojs/internal-helpers": "0.13.1", "@astrojs/markdown-satteri": "0.4.7", "@astrojs/telemetry": "3.4.2", "@capsizecss/unpack": "^4.0.1", "@clack/prompts": "^1.8.1", "@oslojs/encoding": "^1.1.0", "am-i-vibing": "^0.5.0", "aria-query": "^5.3.2", "axobject-query": "^4.1.0", "ci-info": "^4.4.0", "clsx": "^2.1.1", "common-ancestor-path": "^2.0.0", "cookie": "^2.0.1", "devalue": "^6.0.3", "diff": "^9.0.0", "dset": "^3.1.4", "es-module-lexer": "^3.0.4", "flattie": "^1.1.1", "fontace": "~0.4.1", "get-tsconfig": "5.0.0-beta.6", "github-slugger": "^2.0.0", "html-escaper": "3.0.3", "http-cache-semantics": "^4.3.0", "jsonc-parser": "^3.3.1", "magic-string": "^1.4.5", "magicast": "^0.5.5", "mrmime": "^2.0.1", "neotraverse": "^1.0.1", "obug": "^3.0.0", "p-limit": "^7.3.3", "p-queue": "^10.0.0", "package-manager-detector": "^1.9.0", "piccolore": "^0.1.3", "picomatch": "^4.0.7", "rolldown": "^1.2.16", "shiki": "^4.5.0", "smol-toml": "^1.9.1", "svgo": "^4.1.0", "tinyclip": "^1.0.3", "tinyexec": "^1.3.1", "tinyglobby": "^0.2.17", "tinyps": "^0.5.1", "ultrahtml": "^1.7.0", "unifont": "^1.0.3", "unstorage": "^1.17.6", "verkit": "^0.5.0", "vite": "^8.3.5", "vitefu": "^1.1.3", "xxhash-wasm": "^1.1.0", "yargs-parser": "^22.0.0", "zod": "^4.6.7" }, "optionalDependencies": { "sharp": "^0.35.4" }, "peerDependencies": { "@astrojs/markdown-remark": "^7.3.0" }, "optionalPeers": ["@astrojs/markdown-remark"], "bin": { "astro": "./bin/astro.mjs" } }, "sha512-fH54IOaCkZY1paEjygKn8CPfuJ3w8ns7PourcPML5AouJkQuTBqBQ2RbbIbBhutZbg2qn5se6Yl9DYybzqjC0Q=="],
"axe-core": ["axe-core@4.14.0", "", {}, "sha512-9WTZxEjsZ7b13TH8JPmbV2z8CHbl80/2hm3XPEG4JgNdQLK81IBRXmSxHfMAOkSqQeRxT/0dwNDz2GOm3zzpcQ=="],
"axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="],
"bail": ["bail@2.0.2", "", {}, "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw=="],
+6
View File
@@ -432,3 +432,9 @@ build test compares the sitemap with the files Astro wrote, so a missing one fai
empty state (the spec says no page without courses); linking strip titles to course URLs.
**Consequences:** `site.intro` is still validated but not shown on `/work/`. The old Now / Latest writing / CV strip
(`components/work/NowStrip.astro`) is gone. Tests that counted fixture projects or featured panels were updated.
## 2026-11-12 — axe-core in the e2e suite
The owner allowed `@axe-core/playwright` (dev dependency only). `e2e/a11y.spec.ts` runs the WCAG 2.0/2.1 A and AA rules
on the start page, `/work/`, `/work/learning/`, a project page, `/off-the-clock/`, `/simple/` and `/everything/`.
`color-contrast` is disabled for now: passing it means retuning dim text tokens in every space, a visual change the
owner reviews first (`docs/TODO.md`).
+5 -1
View File
@@ -68,7 +68,11 @@ Tests
- [ ] Missing automated tests: sheet history, keys and focus; filter toggling on `/everything/`; dead-anchor and live-region regressions; a date-independent issue test; the CLI `build` path; the CSS filter contract and pre-paint script.
- [ ] Gemini allow `run_shell_command(bun test)` may not match argument forms (cosmetic).
- [ ] Plan 009 asked for axe checks of `/work/` and `/work/learning/` in `e2e/a11y.spec.ts`, but the repo has no axe dependency (adding one needs the owner's yes). The learning page was added to the existing alt-text and reduced-motion checks instead.
- [ ] Colour contrast (axe `color-contrast`, disabled in `e2e/a11y.spec.ts`): small dim text is below 4.5:1 in all three
spaces. Work `--dim #625c54` on the panels (2.6–2.8:1; mono labels, facts `dt`, list sub-lines, history dates,
drawn-showcase captions); off the clock `--mute #7d6a5c` (4.1–4.3:1) and sage `#6f7f45` (3.5:1), cream text on the
amber typeset card (1.6:1); `/everything/` `#786a62` on `#1b1517` (3.5:1). Retune the tokens, show the owner, then
remove `disableRules(['color-contrast'])`.
## Recurring
- [ ] Maintain `AGENTS.md`:
+17
View File
@@ -1,4 +1,6 @@
import { expect, test, type Page } from '@playwright/test';
import AxeBuilder from '@axe-core/playwright';
import { P } from './helpers.ts';
// Accessibility and motion checks on the built fixture site (see playwright.config.ts).
@@ -79,3 +81,18 @@ test('every image has alt text', async ({ page }) => {
expect(missing, path).toEqual([]);
}
});
// axe-core (WCAG 2.x A and AA rules) on every kind of page. Spec 009 asked for /work/ and /work/learning/;
// the others ride along so the whole site keeps the same bar. `color-contrast` is left out until the dim text
// tokens of all three spaces are retuned (a visual change the owner looks at first; see docs/TODO.md).
test.describe('axe', () => {
const pages = ['/', '/work/', '/work/learning/', `/work/${P.byHand2}/`, '/off-the-clock/', '/simple/', '/everything/'];
for (const path of pages) {
test(`no WCAG A/AA violations on ${path}`, async ({ page }) => {
await page.goto(path);
const { violations } = await new AxeBuilder({ page }).withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa']).disableRules(['color-contrast']).analyze();
const summary = violations.map((v) => `${v.id} (${v.impact}): ${v.nodes.length}× ${v.nodes.slice(0, 3).map((n) => n.target.join(' ')).join(' | ')}`);
expect(summary).toEqual([]);
});
}
});
+1
View File
@@ -25,6 +25,7 @@
"react-dom": "^19"
},
"devDependencies": {
"@axe-core/playwright": "^4.14.0",
"@playwright/test": "^1.65",
"@types/bun": "^1.4.4",
"@types/react": "^19.3.0",