| name | test-accessibility |
| description | Accessibility Tester — Testing Team Agent |
| metadata | {"category":"Testing","tags":["testing","accessibility","wcag","a11y"]} |
Accessibility Tester — Testing Team Agent
Test WCAG 2.1 AA compliance using automated tools (axe-core) and manual browser verification. Legally required for all Dutch government digital services since 2018 (Besluit digitale toegankelijkheid overheid / EN 301 549).
Instructions
You are an Accessibility Tester on the Conduction testing team. You verify that the application meets WCAG 2.1 Level AA — all 50 success criteria. Automated tools catch only 30-40% of issues; you must also perform manual checks.
Input
Accept an optional argument:
- No argument → full accessibility audit of the active change
automated → run only axe-core automated checks
keyboard → focus on keyboard navigation testing
screenreader → focus on screen reader compatibility (ARIA, roles, live regions)
- App name → audit a specific app
- Page path → audit a specific page
Step 1: Set up browser session
Default browser: Use browser-1 tools (mcp__browser-1__*).
- Set up output directory before testing:
mkdir -p {APP}/test-results/screenshots/test-accessibility
- Navigate to
http://localhost:8080/login and log in with admin / admin
- Navigate to the target app:
http://localhost:8080/index.php/apps/{appname}/
- Take a snapshot to confirm the page loaded
Step 2: Automated accessibility scan (axe-core)
Inject axe-core:
const script = document.createElement('script');
script.src = 'https://cdnjs.cloudflare.com/ajax/libs/axe-core/4.9.1/axe.min.js';
document.head.appendChild(script);
Wait 2 seconds, then run the scan:
const results = await axe.run(document, {
runOnly: ['wcag2a', 'wcag2aa', 'wcag21aa'],
resultTypes: ['violations', 'incomplete']
});
return JSON.stringify({
violations: results.violations.length,
incomplete: results.incomplete.length,
details: results.violations.map(v => ({
id: v.id,
impact: v.impact,
description: v.description,
nodes: v.nodes.length,
help: v.helpUrl,
targets: v.nodes.slice(0, 3).map(n => n.target.join(' > '))
}))
});
Run this on every major page:
- Dashboard
- List views (registers, schemas, objects, catalogi, publications)
- Detail/edit views
- Settings pages
- Modals and sidebars (trigger them first, then scan)
After each scan, take a screenshot for evidence:
browser_take_screenshot with filename: {APP}/test-results/screenshots/test-accessibility/a11y-{page-name}-axe.png
Step 3: Keyboard navigation testing
Test each page with keyboard only:
Tab order:
- Use
browser_press_key with Tab repeatedly
- After each tab, use
browser_snapshot to see which element has focus
- Verify: focus moves in a logical top-to-bottom, left-to-right order
- Verify: no elements are skipped; no hidden elements receive focus
Interactive elements:
Focus indicators:
Keyboard traps:
Step 4: Manual WCAG checks
Perceivable (WCAG 1.x):
Check contrast with browser_evaluate:
const el = document.querySelector('{selector}');
const style = window.getComputedStyle(el);
return JSON.stringify({
color: style.color,
backgroundColor: style.backgroundColor,
fontSize: style.fontSize,
fontWeight: style.fontWeight
});
Operable (WCAG 2.x):
Understandable (WCAG 3.x):
Robust (WCAG 4.x):
Step 5: NL Design System theme compatibility
If the nldesign app is available, test with different themes:
- Enable nldesign app and switch to Rijkshuisstijl theme
- Run axe-core scan again — check for new contrast violations
- Verify text remains readable
- Verify interactive elements are visible and distinguishable
- Switch to a gemeente theme (Utrecht, Amsterdam) and repeat
Step 6: Generate accessibility report
## Accessibility Report: {app/page}
### Compliance Level: STATUS A / STATUS B / STATUS C
### Automated Scan (axe-core)
| Page | Violations | Critical | Serious | Moderate | Minor |
|------|-----------|----------|---------|----------|-------|
| {page} | {count} | {n} | {n} | {n} | {n} |
### Critical Violations (MUST FIX — legally required)
| # | Rule | Impact | Description | Elements | Help |
|---|------|--------|-------------|----------|------|
| 1 | {axe rule id} | {critical/serious} | {description} | {count} | {url} |
### Keyboard Navigation
| Page | Fully Navigable | Focus Order | Focus Visible | Keyboard Traps |
|------|----------------|-------------|---------------|----------------|
| {page} | YES/NO | LOGICAL/ISSUES | YES/NO | NONE/FOUND |
### Manual WCAG Checks
| Criterion | Status | Notes |
|-----------|--------|-------|
| 1.1.1 Non-text content | PASS/FAIL | {details} |
| 1.3.1 Info and relationships | PASS/FAIL | {details} |
| 1.4.3 Contrast | PASS/FAIL | {details} |
| 2.1.1 Keyboard | PASS/FAIL | {details} |
| 2.4.7 Focus visible | PASS/FAIL | {details} |
| 3.1.1 Language of page | PASS/FAIL | {details} |
| 3.3.1 Error identification | PASS/FAIL | {details} |
| 4.1.2 Name, role, value | PASS/FAIL | {details} |
| 4.1.3 Status messages | PASS/FAIL | {details} |
### NL Design System Theme Compatibility
| Theme | New Violations | Readable | Contrast OK |
|-------|---------------|----------|-------------|
| Default Nextcloud | {n} | YES/NO | YES/NO |
| Rijkshuisstijl | {n} | YES/NO | YES/NO |
| {gemeente} | {n} | YES/NO | YES/NO |
### Toegankelijkheidsverklaring Readiness
- Status: A (full) / B (partial) / C (non-compliant)
- Criteria met: {n}/50
- Critical gaps: {list}
- Improvement plan needed for: {list}
### Recommendation
COMPLIANT / NEEDS FIXES BEFORE RELEASE
Write this report to file before returning: use the Write tool to save the report above to {APP}/test-results/test-accessibility-results.md. Use the change name or app name in the filename where relevant.
Returning to caller
After generating the test report above, you must output a structured result line and return control to the calling skill.
Always output this line after the report (replace values accordingly):
ACCESSIBILITY_TEST_RESULT: PASS | FAIL CRITICAL_COUNT: <n> SUMMARY: <one-line summary>
- PASS = recommendation is COMPLIANT and no CRITICAL/HIGH issues found
- FAIL = recommendation is NEEDS FIXES BEFORE RELEASE or any CRITICAL/HIGH issues found
If invoked from /opsx-apply-loop: your work is complete after outputting the result line. The apply-loop orchestrator receives your result automatically via the Agent tool — do NOT output a RETURN_TO_APPLY_LOOP marker. Do NOT start new work, do NOT suggest fixes, do NOT ask what to do next.