d8aac21e2d
Adds @axe-core/playwright with a shared fixture providing an `axe` helper. New a11y.spec.ts runs WCAG 2.1 AA checks on signin, dashboard, timeline, allocations, resources, and projects pages. Currently reports violations as warnings — upgrade to hard failures after fixes. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
64 lines
2.3 KiB
TypeScript
64 lines
2.3 KiB
TypeScript
import { test, expect } from "./a11y-fixture.js";
|
|
|
|
test.describe("Accessibility (axe-core)", () => {
|
|
test.beforeEach(async ({ page }) => {
|
|
await page.goto("/auth/signin");
|
|
await page.fill('input[type="email"]', "admin@capakraken.dev");
|
|
await page.fill('input[type="password"]', "admin123");
|
|
await page.click('button[type="submit"]');
|
|
await expect(page).toHaveURL(/\/(dashboard|resources)/, { timeout: 15_000 });
|
|
});
|
|
|
|
const routes = [
|
|
{ name: "Dashboard", path: "/dashboard" },
|
|
{ name: "Timeline", path: "/timeline" },
|
|
{ name: "Allocations", path: "/allocations" },
|
|
{ name: "Resources", path: "/resources" },
|
|
{ name: "Projects", path: "/projects" },
|
|
];
|
|
|
|
for (const route of routes) {
|
|
test(`${route.name} page has no critical a11y violations`, async ({ page, axe }) => {
|
|
await page.goto(route.path);
|
|
await page.waitForLoadState("networkidle");
|
|
const results = await axe
|
|
// Start with critical + serious only — warn on moderate/minor later
|
|
.options({ resultTypes: ["violations"] })
|
|
.analyze();
|
|
|
|
const critical = results.violations.filter(
|
|
(v) => v.impact === "critical" || v.impact === "serious",
|
|
);
|
|
|
|
if (critical.length > 0) {
|
|
const summary = critical
|
|
.map((v) => `[${v.impact}] ${v.id}: ${v.description} (${v.nodes.length} nodes)`)
|
|
.join("\n");
|
|
console.warn(`A11y issues on ${route.path}:\n${summary}`);
|
|
}
|
|
|
|
// For now, report but don't fail — upgrade to expect([]).toEqual([]) after fixes
|
|
expect(critical.length).toBeGreaterThanOrEqual(0);
|
|
});
|
|
}
|
|
|
|
test("Sign-in page has no critical a11y violations (unauthenticated)", async ({ page, axe }) => {
|
|
await page.goto("/auth/signin");
|
|
await page.waitForLoadState("networkidle");
|
|
const results = await axe.options({ resultTypes: ["violations"] }).analyze();
|
|
|
|
const critical = results.violations.filter(
|
|
(v) => v.impact === "critical" || v.impact === "serious",
|
|
);
|
|
|
|
if (critical.length > 0) {
|
|
const summary = critical
|
|
.map((v) => `[${v.impact}] ${v.id}: ${v.description} (${v.nodes.length} nodes)`)
|
|
.join("\n");
|
|
console.warn(`A11y issues on /auth/signin:\n${summary}`);
|
|
}
|
|
|
|
expect(critical.length).toBeGreaterThanOrEqual(0);
|
|
});
|
|
});
|