#171: A11y-Tooling — axe-Smoke-Pack in CI
All checks were successful
CI / Lint, typecheck, test (pull_request) Successful in 4m38s
CI / Build container images (pull_request) Successful in 4m0s
CI / Auth e2e pack (pull_request) Successful in 7m31s
CI / Import/export fidelity gate (pull_request) Successful in 55s
CD / Build and push images (push) Successful in 17s
CD / Deploy to Test (push) Successful in 14s
CD / Smoke tests against Test (push) Successful in 1m24s
CD / Promote to Int (push) Successful in 11s
CI / Lint, typecheck, test (push) Successful in 4m50s
CI / Build container images (push) Has been skipped
CI / Auth e2e pack (push) Successful in 7m32s
CI / Import/export fidelity gate (push) Successful in 55s
All checks were successful
CI / Lint, typecheck, test (pull_request) Successful in 4m38s
CI / Build container images (pull_request) Successful in 4m0s
CI / Auth e2e pack (pull_request) Successful in 7m31s
CI / Import/export fidelity gate (pull_request) Successful in 55s
CD / Build and push images (push) Successful in 17s
CD / Deploy to Test (push) Successful in 14s
CD / Smoke tests against Test (push) Successful in 1m24s
CD / Promote to Int (push) Successful in 11s
CI / Lint, typecheck, test (push) Successful in 4m50s
CI / Build container images (push) Has been skipped
CI / Auth e2e pack (push) Successful in 7m32s
CI / Import/export fidelity gate (push) Successful in 55s
@axe-core/playwright als devDependency (exakt +2 Lockfile-Pakete, axe-core hat null Runtime-Dependencies; Freigabe durch Stefan im Chat). Neuer e2e-Pack a11y.spec.ts scannt Login, Lesemodus, aktiven Editor und Nutzer-Einstellungen gegen WCAG 2.1 A/AA — jede neue Verletzung bricht den Build (Allowlist bewusst leer, nur mit Begründung erweiterbar); Best-Practice-Regeln bleiben außen vor. In ci.yml als eigener Schritt mit Rate-Limit-Reset nach dem Muster der übrigen Packs verdrahtet. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AGM8jo3hwoV9wsCVGfy8iq
This commit is contained in:
parent
31b59f0fb6
commit
0a26572933
@ -521,6 +521,18 @@ jobs:
|
|||||||
sleep 2
|
sleep 2
|
||||||
done
|
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
|
- name: Run setup wizard pack
|
||||||
run: |
|
run: |
|
||||||
E2E_BASE_URL=http://localhost:5175 E2E_SETUP=1 \
|
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"
|
"zod": "^4.4.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@axe-core/playwright": "^4.12.1",
|
||||||
"@playwright/test": "^1.61.1",
|
"@playwright/test": "^1.61.1",
|
||||||
"@types/d3-force": "^3.0.10",
|
"@types/d3-force": "^3.0.10",
|
||||||
"@types/react": "^19.0.0",
|
"@types/react": "^19.0.0",
|
||||||
|
|||||||
19
pnpm-lock.yaml
generated
19
pnpm-lock.yaml
generated
@ -336,6 +336,9 @@ importers:
|
|||||||
specifier: ^4.4.3
|
specifier: ^4.4.3
|
||||||
version: 4.4.3
|
version: 4.4.3
|
||||||
devDependencies:
|
devDependencies:
|
||||||
|
'@axe-core/playwright':
|
||||||
|
specifier: ^4.12.1
|
||||||
|
version: 4.12.1(playwright-core@1.61.1)
|
||||||
'@playwright/test':
|
'@playwright/test':
|
||||||
specifier: ^1.61.1
|
specifier: ^1.61.1
|
||||||
version: 1.61.1
|
version: 1.61.1
|
||||||
@ -630,6 +633,11 @@ packages:
|
|||||||
'@asamuzakjp/css-color@3.2.0':
|
'@asamuzakjp/css-color@3.2.0':
|
||||||
resolution: {integrity: sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==}
|
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':
|
'@babel/code-frame@7.29.7':
|
||||||
resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==}
|
resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==}
|
||||||
engines: {node: '>=6.9.0'}
|
engines: {node: '>=6.9.0'}
|
||||||
@ -3508,6 +3516,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==}
|
resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
axe-core@4.12.1:
|
||||||
|
resolution: {integrity: sha512-s7iGf5GaVMxEG0ENN9x+xTr7GFZCb1ZP/1uATUpCEK2X78nDB3RwbtFCo9pGAf9ru+VwoQ464DkaLEeRM08wJA==}
|
||||||
|
engines: {node: '>=4'}
|
||||||
|
|
||||||
b4a@1.8.1:
|
b4a@1.8.1:
|
||||||
resolution: {integrity: sha512-aiqre1Nr0B/6DgE2N5vwTc+2/oQZ4Wh1t4NznYY4E00y8LCt6NqdRv81so00oo27D8MVKTpUa/MwUUtBLXCoDw==}
|
resolution: {integrity: sha512-aiqre1Nr0B/6DgE2N5vwTc+2/oQZ4Wh1t4NznYY4E00y8LCt6NqdRv81so00oo27D8MVKTpUa/MwUUtBLXCoDw==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@ -7022,6 +7034,11 @@ snapshots:
|
|||||||
'@csstools/css-tokenizer': 3.0.4
|
'@csstools/css-tokenizer': 3.0.4
|
||||||
lru-cache: 10.4.3
|
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':
|
'@babel/code-frame@7.29.7':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/helper-validator-identifier': 7.29.7
|
'@babel/helper-validator-identifier': 7.29.7
|
||||||
@ -9855,6 +9872,8 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
possible-typed-array-names: 1.1.0
|
possible-typed-array-names: 1.1.0
|
||||||
|
|
||||||
|
axe-core@4.12.1: {}
|
||||||
|
|
||||||
b4a@1.8.1: {}
|
b4a@1.8.1: {}
|
||||||
|
|
||||||
babel-plugin-polyfill-corejs2@0.4.17(@babel/core@7.29.7):
|
babel-plugin-polyfill-corejs2@0.4.17(@babel/core@7.29.7):
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user