Accessibility Testing with Cypress
Accessibility testing with Cypress is very simple and ensures that everyone, including those with disabilities or impairments can fully access and use the site or app. It is not only a legal requirement, but also a moral and ethical one, to ensure that digital products are inclusive and usable by all.
Axe: Accessibility testing
From the Dequeue family of products, we are using aXe for accessibility testing. Using their Node.js plugin, axe-core
, we also utilise cypress-axe
for integration into the Cypress testing framework.
Scaffolded Example
When accessibility tests are scaffolded into your project you will see the following example:
import 'cypress-axe';
import { terminalLogAxe } from '../support/e2e';
describe('@accessibility-tests', () => {
beforeEach(() => {
cy.visit('/');
cy.injectAxe();
});
it('Has no detectable a11y violations on load (with custom parameters)', () => {
// Test the page at initial load (with context and options)
cy.checkA11y(
null,
{
runOnly: {
type: 'tag',
values: ['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa'],
},
},
terminalLogAxe
);
});
it('Has no detectable a11y violations on load (filtering to only include critical impact violations)', () => {
// Test on initial load, only report and assert for critical impact items
cy.checkA11y(
null,
{
includedImpacts: ['critical'],
},
terminalLogAxe
);
});
// Basic usage after interacting with the page
it('Has no a11y violations after button click', () => {
// Interact with the page, then check for a11y issues
cy.contains('a', "What's next?").click();
cy.checkA11y(null, undefined, terminalLogAxe);
});
it('Only logs a11y violations while allowing the test to pass', () => {
// Do not fail the test when there are accessibility failures
cy.checkA11y(null, undefined, terminalLogAxe, true);
});
it('Has no a11y violations after asynchronous load', () => {
// Retry the check if there are initial failures
cy.checkA11y(
null,
{
retries: 3,
interval: 100,
},
terminalLogAxe
);
});
});
Using cypress-axe
we first inject axe-core
into the webpage within our beforeEach hook, this ensures that each test in the spec file has been set up to run tests using the axe testing engine.
beforeEach(() => {
cy.visit('/');
cy.injectAxe();
});
Using the cy.checkA11y()
function (see docs for usage) we then test the webpage against various accessibility rules.
it('Has no detectable a11y violations on load (with custom parameters)', () => {
// Test the page at initial load (with context and options)
cy.checkA11y(
null,
{
runOnly: {
type: 'tag',
values: ['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa'],
},
},
terminalLogAxe
);
});
Passing the terminalLogAxe
function as a callback to cy.checkA11y()
then creates and prints a readable table of accessibility violations to the console. (The terminalLogAxe
function has been added to your app-name/cypress/support/e2e.ts file)
it('Has no detectable a11y violations on load (with custom parameters)', () => {
// Test the page at initial load (with context and options)
cy.checkA11y(
null,
{
runOnly: {
type: 'tag',
values: ['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa'],
},
},
terminalLogAxe
);
});
Running your accessibility tests
We recommend tagging your Cypress accessibility tests so that you can target these specific tests if required. In the below examples we have tagged the tests with @accessibility which enables us to target them through grep as seen in the 'Run specific tests' example
- Grouped
- Individual
//multiple tests grouped in a describe block
describe('Example test group @accessibility', () => {
it('accessibility test 1', async ({ page }) => {
//test code
});
it('accessibility test 2', async ({ page }) => {
//test code
});
}
it('Example individual test @accessibility', () => {
//Accessibility test with axe
});
Viewing your test results
Further to the explanation given in the 'Testing with Cypress' page, accessibility test results can also be found in the console output post execution.
1) Has no detectable a11y violations on load (with custom parameters)
3 accessibility violations were detected
┌─────────┬──────────────────┬───────────┬───────────────────────────────────────────────────────────────────────────────────────────────────────────────────┬───────┐
│ (index) │ id │ impact │ description │ nodes │
├─────────┼──────────────────┼───────────┼───────────────────────────────────────────────────────────────────────────────────────────────────────────────────┼───────┤
│ 0 │ 'color-contrast' │ 'serious' │ 'Ensures the contrast between foreground and background colors meets WCAG 2 AA minimum contrast ratio thresholds' │ 1 │
│ 1 │ 'html-has-lang' │ 'serious' │ 'Ensures every HTML document has a lang attribute' │ 1 │
│ 2 │ 'svg-img-alt' │ 'serious' │ 'Ensures <svg> elements with an img, graphics-document or graphics-symbol role have an accessible text' │ 3 │
└─────────┴──────────────────┴───────────┴───────────────────────────────────────────────────────────────────────────────────────────────────────────────────┴───────┘