Files
familienarchiv/frontend/e2e/briefwechsel-a11y.spec.ts
Marcel 00fa767419 refactor(e2e): extract seedBilateralPair fixture + afterAll cleanup
Lifts the three-API-call seeding (create sender, create receiver,
create document) out of briefwechsel-a11y.spec.ts and into a
dedicated fixtures module. The spec now calls seedBilateralPair()
in beforeAll and cleanupBilateralPair() in afterAll so the test
DB doesn't accrue seeded rows across reruns.

Two caveats captured in the helper docstring: the backend has no
person-delete endpoint (only the document is purged), and the
timestamped last names make leftover persons collision-free.

Refs #305
Fixes @saraholt follow-up 1 + 2 from PR round-2 review

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-23 21:01:17 +02:00

66 lines
2.1 KiB
TypeScript

import AxeBuilder from '@axe-core/playwright';
import { test, expect } from '@playwright/test';
import {
seedBilateralPair,
cleanupBilateralPair,
type BilateralPair
} from './fixtures/bilateral-correspondence';
// Accessibility coverage for the briefwechsel thumbnail-row layout. Seeds
// two persons + a bilateral document via the shared fixture so the page
// reaches the results state (not the hero), then runs axe-core
// (wcag2a + wcag2aa) across three viewports and two color schemes.
const VIEWPORTS = [
{ name: 'mobile', width: 375, height: 812 },
{ name: 'tablet', width: 768, height: 1024 },
{ name: 'desktop', width: 1280, height: 800 }
] as const;
const THEMES = ['light', 'dark'] as const;
let pair: BilateralPair;
test.describe('Accessibility — /briefwechsel row layout', () => {
test.beforeAll(async ({ request }) => {
pair = await seedBilateralPair(request, 'A11y');
});
test.afterAll(async ({ request }) => {
await cleanupBilateralPair(request, pair);
});
for (const vp of VIEWPORTS) {
for (const theme of THEMES) {
test(`${vp.name} / ${theme} has no wcag2a/wcag2aa violations`, async ({ page }) => {
await page.setViewportSize({ width: vp.width, height: vp.height });
await page.emulateMedia({ colorScheme: theme });
await page.goto(
`/briefwechsel?senderId=${encodeURIComponent(pair.senderId)}&receiverId=${encodeURIComponent(pair.receiverId)}`
);
await page.waitForSelector('[data-hydrated]');
// Assert we actually reached the row layout, not the hero — otherwise
// the axe sweep silently scans the wrong DOM.
await expect(page.getByTestId('conv-person-bar')).toBeVisible();
const results = await new AxeBuilder({ page })
.withTags(['wcag2a', 'wcag2aa'])
.include('main')
.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 briefwechsel (${vp.name}/${theme}):\n${summary}`
);
}
expect(results.violations).toEqual([]);
});
}
}
});