The previous version navigated to /briefwechsel with no params, which renders the hero state — axe-core scanned the hero, not the new ThumbnailRow / ConversationThumbnail / DistributionBar. This commit seeds two persons + one document via the API in beforeAll, then drives the URL with ?senderId=X&receiverId=Y so each of the 36 test runs (3 viewports × 2 themes × 2 assertions) actually scans the intended DOM. Also asserts that conv-person-bar is visible first, so a regression that drops the page back to hero fails explicitly rather than silently passing an empty sweep. Refs #305 Fixes @saraholt blocker 1 from PR review Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
79 lines
2.7 KiB
TypeScript
79 lines
2.7 KiB
TypeScript
import AxeBuilder from '@axe-core/playwright';
|
|
import { test, expect } from '@playwright/test';
|
|
|
|
// Accessibility coverage specifically for the briefwechsel thumbnail-row
|
|
// layout. Seeds two persons + a bilateral document via the API 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 senderId: string;
|
|
let receiverId: string;
|
|
|
|
test.describe('Accessibility — /briefwechsel row layout', () => {
|
|
test.beforeAll(async ({ request }) => {
|
|
const timestamp = Date.now();
|
|
const senderRes = await request.post('/api/persons', {
|
|
data: { firstName: 'A11y', lastName: `Sender-${timestamp}` }
|
|
});
|
|
if (!senderRes.ok()) throw new Error(`Create sender failed: ${senderRes.status()}`);
|
|
senderId = (await senderRes.json()).id;
|
|
|
|
const receiverRes = await request.post('/api/persons', {
|
|
data: { firstName: 'A11y', lastName: `Receiver-${timestamp}` }
|
|
});
|
|
if (!receiverRes.ok()) throw new Error(`Create receiver failed: ${receiverRes.status()}`);
|
|
receiverId = (await receiverRes.json()).id;
|
|
|
|
const docRes = await request.post('/api/documents', {
|
|
multipart: {
|
|
title: 'A11y Test Brief',
|
|
documentDate: '1950-06-15',
|
|
senderId,
|
|
receiverIds: receiverId
|
|
}
|
|
});
|
|
if (!docRes.ok()) throw new Error(`Create document failed: ${docRes.status()}`);
|
|
});
|
|
|
|
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(senderId)}&receiverId=${encodeURIComponent(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([]);
|
|
});
|
|
}
|
|
}
|
|
});
|