/chronik → /aktivitaeten; heading updated in all three locales. Component folder (lib/components/chronik/) stays unchanged — internal implementation detail, not user-facing. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
111 lines
3.5 KiB
TypeScript
111 lines
3.5 KiB
TypeScript
import AxeBuilder from '@axe-core/playwright';
|
|
import { test, expect } from '@playwright/test';
|
|
|
|
/**
|
|
* Automated accessibility checks using axe-core (wcag2a + wcag2aa).
|
|
* Authenticated pages use the stored admin session from playwright.config.ts.
|
|
* The login page test overrides to an unauthenticated context.
|
|
*/
|
|
|
|
const AUTHENTICATED_PAGES = [
|
|
{ name: 'home', path: '/' },
|
|
{ name: 'persons', path: '/persons' },
|
|
{ name: 'aktivitaeten', path: '/aktivitaeten' },
|
|
{ name: 'admin', path: '/admin' }
|
|
];
|
|
|
|
function buildAxe(page: Parameters<typeof AxeBuilder>[0]['page']) {
|
|
return new AxeBuilder({ page }).withTags(['wcag2a', 'wcag2aa']);
|
|
}
|
|
|
|
test.describe('Accessibility — authenticated pages', () => {
|
|
for (const { name, path } of AUTHENTICATED_PAGES) {
|
|
test(`${name} page has no critical wcag2a/wcag2aa violations`, async ({ page }) => {
|
|
await page.goto(path);
|
|
await page.waitForSelector('[data-hydrated]');
|
|
|
|
const results = await buildAxe(page).analyze();
|
|
|
|
if (results.violations.length > 0) {
|
|
const summary = results.violations
|
|
.map((v) => `[${v.impact}] ${v.id}: ${v.description} (${v.nodes.length} node(s))`)
|
|
.join('\n');
|
|
console.log(`\nAccessibility violations on ${name}:\n${summary}`);
|
|
}
|
|
|
|
expect(results.violations).toEqual([]);
|
|
});
|
|
}
|
|
});
|
|
|
|
test.describe('Accessibility — dark mode (system preference)', () => {
|
|
for (const { name, path } of AUTHENTICATED_PAGES) {
|
|
test(`${name} page has no wcag2a/wcag2aa violations in prefers-color-scheme: dark`, async ({
|
|
browser
|
|
}) => {
|
|
const context = await browser.newContext({
|
|
colorScheme: 'dark',
|
|
storageState: 'e2e/.auth/user.json'
|
|
});
|
|
const page = await context.newPage();
|
|
await page.goto(path);
|
|
await page.waitForSelector('[data-hydrated]');
|
|
|
|
const results = await buildAxe(page).analyze();
|
|
|
|
if (results.violations.length > 0) {
|
|
const summary = results.violations
|
|
.map((v) => `[${v.impact}] ${v.id}: ${v.description} (${v.nodes.length} node(s))`)
|
|
.join('\n');
|
|
console.log(`\nAccessibility violations on ${name} (dark/media):\n${summary}`);
|
|
}
|
|
|
|
await context.close();
|
|
expect(results.violations).toEqual([]);
|
|
});
|
|
}
|
|
});
|
|
|
|
test.describe('Accessibility — dark mode (manual toggle)', () => {
|
|
for (const { name, path } of AUTHENTICATED_PAGES) {
|
|
test(`${name} page has no wcag2a/wcag2aa violations with data-theme='dark'`, async ({
|
|
page
|
|
}) => {
|
|
await page.goto(path);
|
|
await page.waitForSelector('[data-hydrated]');
|
|
await page.evaluate(() => document.documentElement.setAttribute('data-theme', 'dark'));
|
|
|
|
const results = await buildAxe(page).analyze();
|
|
|
|
if (results.violations.length > 0) {
|
|
const summary = results.violations
|
|
.map((v) => `[${v.impact}] ${v.id}: ${v.description} (${v.nodes.length} node(s))`)
|
|
.join('\n');
|
|
console.log(`\nAccessibility violations on ${name} (dark/manual):\n${summary}`);
|
|
}
|
|
|
|
expect(results.violations).toEqual([]);
|
|
});
|
|
}
|
|
});
|
|
|
|
test.describe('Accessibility — login page', () => {
|
|
test.use({ storageState: { cookies: [], origins: [] } });
|
|
|
|
test('login page has no critical wcag2a/wcag2aa violations', async ({ page }) => {
|
|
await page.goto('/login');
|
|
await expect(page.getByLabel('Benutzername')).toBeVisible();
|
|
|
|
const results = await buildAxe(page).analyze();
|
|
|
|
if (results.violations.length > 0) {
|
|
const summary = results.violations
|
|
.map((v) => `[${v.impact}] ${v.id}: ${v.description} (${v.nodes.length} node(s))`)
|
|
.join('\n');
|
|
console.log(`\nAccessibility violations on login:\n${summary}`);
|
|
}
|
|
|
|
expect(results.violations).toEqual([]);
|
|
});
|
|
});
|