test: vitest unit + playwright e2e suites; fix a11y, contrast, no-JS reveal
- vitest: dictionary parity, slug-map bijection, content-schema validation, JSON-LD (23) - playwright: both locales, language-switch mapping (incl. no-JS), CV downloads, form happy-path + honeypot, axe a11y on 5 templates x 2 themes (23) - fix: raise ink-faint + light accent to meet WCAG AA 4.5:1 (axe-verified) - fix: gate reveal animations behind html.js so content is visible without JS (progressive enhancement - was hidden at opacity 0); scan reduced-motion path - fix: validate contact form before the anti-bot time-trap (no false success) - chore: ESLint node globals, typed diagram props, resvg fontFiles, prettier pass Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
import AxeBuilder from '@axe-core/playwright';
|
||||
|
||||
/*
|
||||
Accessibility gate (TECH_SPEC §9.4): zero critical/serious violations on the main
|
||||
templates, both locales and both themes. wcag2a/2aa + best-practice rules.
|
||||
*/
|
||||
const pages = [
|
||||
{ name: 'home EN', url: '/' },
|
||||
{ name: 'home NO', url: '/no/' },
|
||||
{ name: 'case study', url: '/projects/inboxintel/' },
|
||||
{ name: 'contact', url: '/contact/' },
|
||||
{ name: 'experience', url: '/experience/' },
|
||||
];
|
||||
|
||||
for (const p of pages) {
|
||||
for (const theme of ['dark', 'light'] as const) {
|
||||
test(`${p.name} (${theme}) has no serious a11y violations`, async ({ page }) => {
|
||||
// Scan the reduced-motion rendering: it's the accessible path and shows all
|
||||
// reveal content immediately (entrance animations are otherwise mid-flight).
|
||||
await page.emulateMedia({ colorScheme: theme, reducedMotion: 'reduce' });
|
||||
await page.addInitScript((t) => {
|
||||
try {
|
||||
localStorage.setItem('theme', t);
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
}, theme);
|
||||
await page.goto(p.url);
|
||||
const results = await new AxeBuilder({ page })
|
||||
.withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa'])
|
||||
.analyze();
|
||||
const serious = results.violations.filter(
|
||||
(v) => v.impact === 'serious' || v.impact === 'critical',
|
||||
);
|
||||
expect(serious, JSON.stringify(serious.map((v) => ({ id: v.id, nodes: v.nodes.length })))).toEqual(
|
||||
[],
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user