@ -521,6 +521,18 @@ jobs:
|
||||
sleep 2
|
||||
done
|
||||
|
||||
# Two logins per run → reset first (see note above).
|
||||
- name: Reset login rate limit before a11y pack
|
||||
run: |
|
||||
echo "DELETE FROM rate_limits WHERE key LIKE 'login%';" | \
|
||||
pnpm --filter @dorfteich/api exec prisma db execute --stdin --url "$DATABASE_URL"
|
||||
|
||||
# WCAG-A/AA-Regressionsschutz (issue #171): axe-Scan der Kernscreens.
|
||||
- name: Run a11y pack
|
||||
run: |
|
||||
E2E_BASE_URL=http://localhost:5173 \
|
||||
pnpm --filter @dorfteich/web exec playwright test e2e/a11y.spec.ts
|
||||
|
||||
- name: Run setup wizard pack
|
||||
run: |
|
||||
E2E_BASE_URL=http://localhost:5175 E2E_SETUP=1 \
|
||||
|
||||
62
apps/web/e2e/a11y.spec.ts
Normal file
62
apps/web/e2e/a11y.spec.ts
Normal file
@ -0,0 +1,62 @@
|
||||
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();
|
||||
});
|
||||
@ -42,6 +42,7 @@
|
||||
"zod": "^4.4.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@axe-core/playwright": "^4.12.1",
|
||||
"@playwright/test": "^1.61.1",
|
||||
"@types/d3-force": "^3.0.10",
|
||||
"@types/react": "^19.0.0",
|
||||
|
||||
19
pnpm-lock.yaml
generated
19
pnpm-lock.yaml
generated
@ -336,6 +336,9 @@ importers:
|
||||
specifier: ^4.4.3
|
||||
version: 4.4.3
|
||||
devDependencies:
|
||||
'@axe-core/playwright':
|
||||
specifier: ^4.12.1
|
||||
version: 4.12.1(playwright-core@1.61.1)
|
||||
'@playwright/test':
|
||||
specifier: ^1.61.1
|
||||
version: 1.61.1
|
||||
@ -630,6 +633,11 @@ packages:
|
||||
'@asamuzakjp/css-color@3.2.0':
|
||||
resolution: {integrity: sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==}
|
||||
|
||||
'@axe-core/playwright@4.12.1':
|
||||
resolution: {integrity: sha512-rMd7xriptqKpP+w5265i4Hdkv2X5kbu6uiBi/B2I7uf3hieRBM3qDCfaKPtxfiYb2mKXfF+yLODJwIx+Jv1GDw==}
|
||||
peerDependencies:
|
||||
playwright-core: '>= 1.0.0'
|
||||
|
||||
'@babel/code-frame@7.29.7':
|
||||
resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
@ -3508,6 +3516,10 @@ packages:
|
||||
resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
axe-core@4.12.1:
|
||||
resolution: {integrity: sha512-s7iGf5GaVMxEG0ENN9x+xTr7GFZCb1ZP/1uATUpCEK2X78nDB3RwbtFCo9pGAf9ru+VwoQ464DkaLEeRM08wJA==}
|
||||
engines: {node: '>=4'}
|
||||
|
||||
b4a@1.8.1:
|
||||
resolution: {integrity: sha512-aiqre1Nr0B/6DgE2N5vwTc+2/oQZ4Wh1t4NznYY4E00y8LCt6NqdRv81so00oo27D8MVKTpUa/MwUUtBLXCoDw==}
|
||||
peerDependencies:
|
||||
@ -7022,6 +7034,11 @@ snapshots:
|
||||
'@csstools/css-tokenizer': 3.0.4
|
||||
lru-cache: 10.4.3
|
||||
|
||||
'@axe-core/playwright@4.12.1(playwright-core@1.61.1)':
|
||||
dependencies:
|
||||
axe-core: 4.12.1
|
||||
playwright-core: 1.61.1
|
||||
|
||||
'@babel/code-frame@7.29.7':
|
||||
dependencies:
|
||||
'@babel/helper-validator-identifier': 7.29.7
|
||||
@ -9855,6 +9872,8 @@ snapshots:
|
||||
dependencies:
|
||||
possible-typed-array-names: 1.1.0
|
||||
|
||||
axe-core@4.12.1: {}
|
||||
|
||||
b4a@1.8.1: {}
|
||||
|
||||
babel-plugin-polyfill-corejs2@0.4.17(@babel/core@7.29.7):
|
||||
|
||||
Loading…
Reference in New Issue
Block a user