Files
Portfolio/e2e/a11y.e2e.ts
Antonio Ledebuhr 34367181d2 integration: add Playwright, axe and Lighthouse CI gates
Lock SEO, accessibility and crawlability with headless browser checks so regressions fail before a merge instead of after publication.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-25 18:28:24 +02:00

49 lines
1.6 KiB
TypeScript

import AxeBuilder from '@axe-core/playwright';
import { expect, test } from '@playwright/test';
import { pagePath } from './helpers';
const AXE_PATHS = [
pagePath('home', 'de'),
pagePath('home', 'en'),
pagePath('projects', 'de'),
pagePath('projects', 'en'),
pagePath('servicesAi', 'de'),
pagePath('servicesAi', 'en'),
pagePath('contact', 'de'),
pagePath('contact', 'en'),
pagePath('imprint', 'de'),
pagePath('imprint', 'en'),
'/missing-route',
'/en/missing-route',
];
test.describe('accessibility', () => {
test('revealed content stays visible under reduced motion', async ({ page }) => {
await page.emulateMedia({ reducedMotion: 'reduce' });
await page.goto(pagePath('home', 'de'));
const pending = page.locator('.reveal-pending');
await expect(pending).toHaveCount(0);
await expect(page.locator('app-systems-map')).toBeVisible();
await expect(page.locator('app-case-card').first()).toBeVisible();
});
for (const path of AXE_PATHS) {
test(`axe has no serious or critical violations on ${path}`, async ({ page }) => {
await page.goto(path);
const results = await new AxeBuilder({ page }).analyze();
const blocking = results.violations.filter(
(violation) => violation.impact === 'serious' || violation.impact === 'critical',
);
const details = blocking
.map((violation) => {
const nodes = violation.nodes.map((node) => node.target.join(' ')).join(', ');
return `${violation.id} (${violation.impact}): ${nodes}`;
})
.join('\n');
expect(blocking, details).toEqual([]);
});
}
});