Accessibility testing (a11y) with Scout
Scout includes built-in support for automated accessibility (a11y) checks in UI tests via page.checkA11y.
Use automated a11y checks to augment, not replace, manual accessibility reviews and assistive technology testing.
Behind the scenes, page.checkA11y runs an axe-core scan of the current page state. It analyzes the DOM for accessibility violations and returns { violations: string[] } you can assert on. Configuration is unified with Cypress and FTR.
We recommend adding page.checkA11y to:
- Key happy-path flows (for example, landing pages, dashboards, wizards)
- Important interaction states (for example, flyouts, modals, menus, toasts)
- Pages with frequent UI changes where regressions are likely
Avoid running a11y checks on every interaction. Pick a few high-value checkpoints per test or suite to keep runs fast and reduce flakiness.
Run page.checkA11y() once the page is fully loaded and the UI has settled:
import { expect } from '@kbn/scout/ui';
import { tags } from '@kbn/scout';
import { test } from '../fixtures';
test(
'Discover has no basic accessibility violations',
{ tag: tags.deploymentAgnostic },
async ({ browserAuth, pageObjects, page }) => {
await browserAuth.loginAsViewer();
await pageObjects.discover.goto();
const { violations } = await page.checkA11y();
expect(violations).toHaveLength(0);
}
);
Prefer running checkA11y with include set to the root element you are testing. This keeps the scan isolated and reduces runtime.
import { expect } from '@kbn/scout/ui';
import { tags } from '@kbn/scout';
import { test } from '../fixtures';
test(
'Modal dialog is accessible',
{ tag: tags.deploymentAgnostic },
async ({ browserAuth, page }) => {
await browserAuth.loginAsViewer();
await page.gotoApp('my-plugin');
await page.testSubj.click('open-modal');
const modal = page.locator('[role="dialog"]');
await expect(modal).toBeVisible();
const { violations } = await page.checkA11y({
include: ['[role="dialog"]'],
});
expect(violations).toHaveLength(0);
}
);
import { expect } from '@kbn/scout/ui';
import { tags } from '@kbn/scout';
import { test } from '../fixtures';
test(
'should load Discover',
{ tag: tags.deploymentAgnostic },
async ({ browserAuth, pageObjects, page }) => {
await browserAuth.loginAsViewer();
await pageObjects.discover.goto();
await test.step('no basic accessibility violations', async () => {
const { violations } = await page.checkA11y();
expect(violations).toHaveLength(0);
});
// continue with the flow...
}
);
If a failure isn’t caused by your change and there isn’t a quick fix, don’t disable the entire a11y check. Prefer excluding the specific problematic element so the suite still provides coverage:
import { expect } from '@kbn/scout/ui';
import { tags } from '@kbn/scout';
import { test } from '../fixtures';
test(
'Form components are accessible',
{ tag: tags.deploymentAgnostic },
async ({ browserAuth, page }) => {
await browserAuth.loginAsViewer();
await page.gotoApp('my-plugin');
const { violations } = await page.checkA11y({
exclude: ['[data-test-subj="problematic-element"]'],
});
expect(violations).toHaveLength(0);
}
);
When page.checkA11y detects violations, violations is a list of formatted strings (one per violation) that includes the rule id, impact, help URL, page URL, and node details. This keeps failure output readable in CI logs.
Violations appear in the Scout HTML report.
- Use
includeto target specific regions - Wait for page readiness before scanning
- Focus on critical paths and key interaction states
- Assert
violations.length === 0 - Combine with manual testing