/// Resources / Accessibility
Automated Section 508 testing with Selenium: audit every page your tests already visit
Section 508 audits usually happen once, late, by a specialist, on a sample of pages. Your Selenium suite already visits every page that matters, on every build. The trick is to run an accessibility engine on each of those pages as a side effect of the functional test, so the compliance evidence is produced continuously and offline.
The hook
Cucumber gives you an @AfterStep hook with the WebDriver in scope. After each step, if the URL changed since the last audit in this scenario, inject axe-core and run it with the Section 508 and WCAG tag sets. That is the whole design; the rest is reporting.
@AfterStep
public void auditPage(Scenario scenario) {
if (!cfg.a11yAudit()) return;
String url = driver.getCurrentUrl();
if (url.equals(lastAudited)) return; // once per scenario + URL
lastAudited = url;
String axe = readResource("/axe/axe.min.js"); // bundled, no CDN
((JavascriptExecutor) driver).executeScript(axe);
String json = (String) ((JavascriptExecutor) driver).executeAsyncScript(
"var done = arguments[arguments.length - 1];" +
"axe.run(document, { runOnly: { type: 'tag', values: ['section508','wcag2a','wcag2aa'] } })" +
" .then(r => done(JSON.stringify(r)));");
A11yResult r = A11yResult.parse(json).atLeast(cfg.a11yImpact()); // e.g. serious + critical
scenario.attach(r.toHtml(), "text/html", "Section 508: " + url);
A11yReport.record(scenario, url, r); // feeds target/a11y/section508.html
if (cfg.a11yFail() && r.hasViolations()) throw new AssertionError(r.summary());
}
Three decisions that matter
- Bundle the engine. Loading axe from a CDN fails on air-gapped and proxy-restricted networks, which is where most 508 work happens. Ship
axe.min.jsin the test resources and inject it from disk. - Report by default, gate on request. The first run on a real application finds hundreds of issues. If the hook fails scenarios from day one the suite goes red and the audit gets switched off. Attach results and write the chapter; add
a11y.audit.fail=trueonce the backlog is under control. - Filter by impact. axe grades every finding minor to critical. Start at serious and up; that is what an auditor will write up first.
The compliance chapter
Attaching results to each scenario is good for developers. Auditors want one document. At the end of the run, write target/a11y/section508.html: a page per URL audited, the rule sets applied, the violations with the offending HTML and the axe help link, and a summary table. Keep the JSON beside it (section508.json) so a later run can be diffed. Archive both in CI.
Configuration
# healing.properties (or HEALING_* environment variables)
a11y.audit=true
a11y.audit.impact=serious # any | minor | moderate | serious | critical
a11y.audit.fail=false # true = a violation fails the scenario
What the automation does not do
axe finds about half of WCAG issues reliably: missing alt text, contrast, labels, landmarks, heading order, focusable regions. It cannot judge whether alt text is meaningful or whether a custom widget is usable by keyboard. Treat the continuous audit as the thing that keeps the easy half from ever regressing, and the manual audit as the thing you now have time for.
This is exactly what L3git generates when you tick Section 508 audit of every page at startup: the hook, the bundled axe, the impact filter, the per-scenario attachment and the standalone chapter, all offline. The page about it is here; setup is guide 11.
Questions
Is axe-core good enough for a Section 508 claim?
It is the engine most government and enterprise accessibility teams already use, and it maps rules to the Section 508 and WCAG 2.0 A/AA criteria. It covers the automatable criteria; a conformance statement still needs manual checks for the rest.
Does this slow the suite down?
About 0.5 to 1.5 seconds per audited page, once per scenario and URL. On a four-shard matrix run it is rarely noticeable.
Can it audit pages behind login or on an intranet?
Yes. It runs inside the same browser session as the test, so anything the test can reach, the audit can reach. Nothing is sent anywhere.