All checks were successful
CI / Lint, typecheck, test (pull_request) Successful in 4m41s
CI / Build container images (pull_request) Successful in 1m9s
CI / Auth e2e pack (pull_request) Successful in 7m32s
CI / Import/export fidelity gate (pull_request) Successful in 55s
CD / Build and push images (push) Successful in 16s
CD / Deploy to Test (push) Successful in 14s
CD / Smoke tests against Test (push) Successful in 1m41s
CD / Promote to Int (push) Successful in 10s
CI / Lint, typecheck, test (push) Successful in 4m46s
CI / Build container images (push) Has been skipped
CI / Auth e2e pack (push) Successful in 7m18s
CI / Import/export fidelity gate (push) Successful in 55s
Die Zeilen-Aktionen der Personenverwaltung sind jetzt IconButtons (lucide): MailCheck (Bestätigung erneut senden, nur bei Ausstehend), ShieldPlus/ShieldMinus (Zum Admin machen / Admin entfernen), UserX/UserCheck (Deaktivieren/Aktivieren), Trash2 (Löschen) — in dieser Reihenfolge. Das zweistufige Löschen bleibt: die Bestätigung ist weiterhin ein roter Text-Button und erhält beim Umschalten den Fokus (kein Fokusverlust, ADR 0017). Lokalisierte Namen kommen unverändert aus users.json via IconButton (aria-label+title), Icons aria-hidden. Der Admin-Bereich ist neu im a11y-CI-Pack (axe WCAG A/AA auf /admin). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
74 lines
2.8 KiB
TypeScript
74 lines
2.8 KiB
TypeScript
import AxeBuilder from '@axe-core/playwright';
|
||
import { expect, test, type Page } from '@playwright/test';
|
||
|
||
import { contextForUser } from './helpers';
|
||
|
||
/**
|
||
* A11y-Smoke-Pack (issue #171): axe-core-Scan der Kern-Oberflächen gegen
|
||
* WCAG 2.1 A/AA. Regressionsschutz für das Audit vom 21.07.2026 (Bericht im
|
||
* Workspace, Befunde A11Y-001…024) — die Screens hier waren nach den Fixes
|
||
* der Issues #162–#170 verletzungsfrei; jede neue Verletzung bricht den
|
||
* Build. Best-Practice-Regeln (axe-Tag best-practice) prüfen wir hier
|
||
* bewusst NICHT, nur normative WCAG-Kriterien.
|
||
*/
|
||
|
||
const BASE = process.env.E2E_BASE_URL ?? 'http://localhost:5173';
|
||
const TAGS = ['wcag2a', 'wcag21a', 'wcag2aa', 'wcag21aa'];
|
||
|
||
/** Bewusst tolerierte Regel-IDs — nur mit Begründung ergänzen. */
|
||
const ALLOWED_RULES: string[] = [];
|
||
|
||
async function expectClean(page: Page, label: string): Promise<void> {
|
||
const results = await new AxeBuilder({ page }).withTags(TAGS).analyze();
|
||
const violations = results.violations.filter((v) => !ALLOWED_RULES.includes(v.id));
|
||
expect(
|
||
violations.map((v) => ({
|
||
rule: v.id,
|
||
impact: v.impact,
|
||
help: v.help,
|
||
nodes: v.nodes.slice(0, 5).map((n) => n.target),
|
||
})),
|
||
`axe-Verletzungen auf ${label}`,
|
||
).toEqual([]);
|
||
}
|
||
|
||
test('login page passes the axe WCAG A/AA scan', async ({ page }) => {
|
||
await page.goto('/login');
|
||
await page.waitForLoadState('networkidle');
|
||
await expectClean(page, '/login');
|
||
});
|
||
|
||
test('reading and editing a page passes the axe WCAG A/AA scan', async ({ browser }) => {
|
||
const context = await contextForUser(browser, BASE, 'fixture-user');
|
||
const page = await context.newPage();
|
||
await page.goto('/p/content-fixtures/every-element');
|
||
await page.waitForLoadState('networkidle');
|
||
await expectClean(page, 'Lesemodus every-element');
|
||
|
||
await page.locator('.editor-page__mode-toggle').click();
|
||
await page.locator('.ProseMirror[contenteditable="true"]').waitFor({ timeout: 10_000 });
|
||
await page.waitForTimeout(500);
|
||
await expectClean(page, 'Editor every-element');
|
||
await context.close();
|
||
});
|
||
|
||
test('user settings pass the axe WCAG A/AA scan', async ({ browser }) => {
|
||
const context = await contextForUser(browser, BASE, 'fixture-user');
|
||
const page = await context.newPage();
|
||
await page.goto('/settings');
|
||
await page.waitForLoadState('networkidle');
|
||
await expectClean(page, '/settings');
|
||
await context.close();
|
||
});
|
||
|
||
test('admin area passes the axe WCAG A/AA scan', async ({ browser }) => {
|
||
const context = await contextForUser(browser, BASE, 'fixture-admin');
|
||
const page = await context.newPage();
|
||
await page.goto('/admin');
|
||
await page.waitForLoadState('networkidle');
|
||
// Personenliste sichtbar, inkl. der Icon-Aktionen (issue #175).
|
||
await page.locator('.user-manager__table .user-row').first().waitFor();
|
||
await expectClean(page, '/admin');
|
||
await context.close();
|
||
});
|