Does Cypress support accessibility testing?
A website, tool, or technology is considered accessible if designed to be effectively usable by differently-abled people. Your application should be accessible using tools like (OS X) VoiceOver, NVDA screen reader, ChromeVox, etc. Testing your website for the above acceptance criteria is the core of accessibility testing.
Table of contents
- Why is accessibility testing important?
- Accessibility testing guidelines
- How to use Cypress for accessibility testing
- Printing a11y violations in human-readable form
- How to customize axe-core
- Customize using configAxe object
- Useful hack
- Conclusion
Why is accessibility testing important?
Given that one in four adult Americans has a disability, a website designed for accessibility ensures that everyone, regardless of disabilities, can fully experience and enjoy web-based services, content, and digital products.
In many countries, there are legal requirements regarding product accessibility. Ensuring your product is accessible can help prevent potential lawsuits in the future.
Most government entities and well-established companies, especially financial institutions, require an accessible product that meets their minimum accessibility criteria before engaging in business with you.
Apart from the above, an accessible product has common advantages like:
- Wider Audience Reach
- More Website Traffic
- Improved usability
Accessibility testing guidelines
Web Content Accessibility Guidelines (WCAG) are the accessibility guidelines published by the Web Accessibility Initiative of the World Wide Web. Most countries and institute derive their Accessibility guidelines and requirements from WCAG. The US has the ADA (Americans With Disabilities Act) defined as its accessibility guidelines, and Canada has ACA (Accessible Canada Act) defined as its accessibility guidelines.
a11y (short for accessibility) is a community-driven project encouraging and inspiring people and companies to prioritize accessibility in their products. It assists companies in auditing their products to ensure universal accessibility.
WCAG categorizes websites into 3 basic categories based on the level of accessibility.
- WCAG Level A — Basic accessibility
- WCAG Level AA — Strong accessibility
- WCAG Level AAA — Excellent accessibility
How to use Cypress for accessibility testing
While achieving your desired accessibility goals, some manual testing is inevitable. However, tools like Cypress can significantly simplify common accessibility checks with minimal effort.
The beauty lies in seamlessly integrating these checks into your existing automation framework, ensuring that accessibility testing becomes an automated part of your software development life cycle (SDLC).
The two most common tools Cypress uses for automation accessibility testing are as follows.
- axe-core with cypress-axe
- Lighthouse, which uses axe-core as the core library internally
We will use axe-core and cypress-axe for this article.
Axe-Core
Axe-core is a core library that is used for finding accessibility violations on any HTML-based application.
Accessibility testing tools like Lighthouse use axe-core under the hood.
Axe-core verifies your application against 70+ accessibility rules, which aligns with WCAG 2.0, 2.1, and 2.2 standards. Apart from these rules, it also checks for several industry best practices for accessibility.
Using axe-core, you can detect, on average, 57% of WCAG issues automatically. Furthermore, axe-core identifies elements as ‘incomplete’ when it cannot provide a definitive assessment, necessitating manual review.
Cypress-axe
Cypress-axe is used to inject axe-core library and dependencies into your page under tests in a Cypress test and use it to check the DOM for accessibility violations.
Setup
Language used: TypeScript
Test website: automateNow’s automation practice website
Setting up the project:
If needed, set up a new cypress project.
Install Typescript:
yarn add typescript --dev
Install axe-core:
yarn add axe-core
Install cypress-axe:
yarn add cypress-axe
Include the commands
For Cypress v10 and above, update cypress/support/e2e.js
file to include the cypress-axe commands by adding:
import 'cypress-axe'
Code language: JavaScript (javascript)
For TypeScript, add cypress-axe types to your Cypress’ tsconfig.json
file:
{
"compilerOptions": {
"baseUrl": "./",
"target": "es5",
"lib": ["esnext", "dom"],
"types": ["cypress", "node","cypress-axe"]
},
"include": ["."]
}
Code language: JSON / JSON with Comments (json)
Code snippet
Below is an example of calling axe-core in its simplest form.
describe('To verify accessibility for practice-automation Website', () => {
it('To verify accessibility for practice-automation Website Landing Page', () => {
cy.visit("https://practice-automation.com");
cy.injectAxe();
cy.checkA11y();
});
});
Code language: PHP (php)
Output
AssertionError: 3 accessibility violations were detected: expected 3 to equal 0
Code language: HTTP (http)
The test failed, stating AssertionError: 3 accessibility violations were detected. However, this error message is not very helpful for debugging and addressing the violations. To enhance clarity, let’s print the accessibility violations in a nice, human-readable format.
Printing a11y violations in human-readable form
Define a function to print logs function. This function will enable the violation to be printed in a nice tabular format with predefined columns.
function printHumanReadbleAccessibilityLog(violations: string | any[]) {
cy.task(
'log',
`${violations.length} accessibility violation${
violations.length === 1 ? '' : 's'
} ${violations.length === 1 ? 'was' : 'were'} detected`
);
// pluck specific keys to keep the table readable
const violationData = violations.map(
({ id,impact, description, nodes, help }) => ({
id, // the id of the rule this also helps us in configuring custom action for a rule.
impact, // criticality of the violation
description, // description of the violation
nodes: nodes.length, //how many times the violation occured
help, // help on how to fix the violation
})
);
// calling the table funtion to print these logs
cy.task('table', violationData);
}
Code language: JavaScript (javascript)
Add table and log task in cypress config
export default (on: (arg0: string, arg1: any) => void, config: any) => {
on('task', {
log(message: any) {
console.log(message);
return null;
},
table(message: any) {
console.table(message);
return null;
},
});
};
Code language: JavaScript (javascript)
Update your script as below
describe('To verify accessibility for practice-automation Website', () => {
it('To verify accessibility for practice-automation Website Landing Page', () => {
cy.visit('https://practice-automation.com');
cy.injectAxe();
cy.checkA11y(null, undefined, printHumanReadbleAccessibilityLog);
});
});
Code language: PHP (php)
Output
To verify accessibility for practice-automation Website
3 accessibility violations were detected
┌─────────┬───────────────────────────────────────┬────────────┬───────────────────────────────────────────────────────────────────────────────────────────────────────────────────┬───────┬──────────────────────────────────────────────────────────────┐
│ (index) │ id │ impact │ description │ nodes │ help │
├─────────┼───────────────────────────────────────┼────────────┼───────────────────────────────────────────────────────────────────────────────────────────────────────────────────┼───────┼──────────────────────────────────────────────────────────────┤
│ 0 │ 'color-contrast' │ 'serious' │ 'Ensures the contrast between foreground and background colors meets WCAG 2 AA minimum contrast ratio thresholds' │ 24 │ 'Elements must meet minimum color contrast ratio thresholds' │
│ 1 │ 'landmark-complementary-is-top-level' │ 'moderate' │ 'Ensures the complementary landmark or aside is at top level' │ 1 │ 'Aside should not be contained in another landmark' │
│ 2 │ 'region' │ 'moderate' │ 'Ensures all page content is contained by landmarks' │ 2 │ 'All page content should be contained by landmarks' │
└─────────┴───────────────────────────────────────┴────────────┴───────────────────────────────────────────────────────────────────────────────────────────────────────────────────┴───────┴──────────────────────────────────────────────────────────────┘
1) To verify accessibility for practice-automation Website Landing Page
0 passing (8s)
1 failing
1) To verify accessibility for practice-automation Website
To verify accessibility for practice-automation Website Landing Page:
Code language: JavaScript (javascript)
How to customize axe-core
There are two common ways to customize axe-core:
- Adding configureAxe to your test
- Passing rules and conditions to the checkA11y function
// configure rules usong configureAxe
cy.configureAxe({
rules: [
{
id: 'region',
enabled: false,
},
],
})
// passing rules and conditions to the checkA11y funtion
cy.checkA11y(null, {
includedImpacts: ['minor', 'moderate', 'serious', 'critical'],
});
Code language: JavaScript (javascript)
Let’s say we want our test to fail and detect only serious and critical accessibility violations.
Code snippet 1
describe('To verify accessibility for practice-automation Website', () => {
it('To verify accessibility for practice-automation Website Landing Page', () => {
cy.visit('https://practice-automation.com');
cy.injectAxe();
// to include only serious and critical violation as part of accessibility checks
cy.checkA11y(
null,
{ includedImpacts: ['serious', 'critical'] },
printHumanReadbleAccessibilityLog
);
});
});
Code language: PHP (php)
Output
As shown below, only serious and critical violations are checked and logged.
The two moderate violations we had before are being ignored.
To verify accessibility for practice-automation Website
1 accessibility violation was detected
┌─────────┬───────────┬───────────────────────────────────────────────────────────────────────────────────────────────────────────────────┬───────┬──────────────────────────────────────────────────────────────┐
│ (index) │ impact │ description │ nodes │ help │
├─────────┼───────────┼───────────────────────────────────────────────────────────────────────────────────────────────────────────────────┼───────┼──────────────────────────────────────────────────────────────┤
│ 0 │ 'serious' │ 'Ensures the contrast between foreground and background colors meets WCAG 2 AA minimum contrast ratio thresholds' │ 24 │ 'Elements must meet minimum color contrast ratio thresholds' │
└─────────┴───────────┴───────────────────────────────────────────────────────────────────────────────────────────────────────────────────┴───────┴──────────────────────────────────────────────────────────────┘
1) To verify accessibility for practice-automation Website Landing Page
0 passing (7s)
1 failing
1) To verify accessibility for practice-automation Website
To verify accessibility for practice-automation Website Landing Page:
AssertionError: 1 accessibility violation was detected: expected 1 to equal 0
Code language: JavaScript (javascript)
Code snippet 2
Pass the test even on accessibility violations:
// passing true means the test passes even when accessiblity violations are found
cy.checkA11y(null, null, null, true)
Code language: JavaScript (javascript)
Code snippet 3
Pass the test even on accessibility violations:
cy.checkA11y(null, {
retries: 3,
interval: 100
})
Code language: CSS (css)
Customize using configureAxe object
The configureAxe object enables more minute configuration of the check and rules for axe-code execution.
Below is a sample of the configureAxe object. We will see examples of a few essential and commonly used configurations.
cy.configureAxe({
branding: { //mixed(optional) Used to set the branding of the helpUrls
brand: String, // string(optional) sets the brand string - default "axe
application: String // string(optional) sets the application string - default "axeAPI
},
reporter: 'option', //Used to set the output format that the axe.run function will pass to the callback function. Can pass a reporter name or a custom reporter function
checks: [Object], //Used to add checks to the list of checks used by rules, or to override the properties of existing checks. The checks attribute is an array of check objects
rules: [Object], //Used to add rules to the existing set of rules, or to override the properties of existing rules
locale: Object, // A locale object to apply (at runtime) to all rules and checks, in the same shape as /locales/*.json.
axeVersion: String, //Set the compatible version of a custom rule with the current axe version
disableOtherRules: Boolean, //Disables all rules not included in the rules property.
noHtml: Boolean , //Disables the HTML output of nodes from rules.
})
Code language: JavaScript (javascript)
ConfigureAxe code 1
Suppose we encounter an expected violation, but we choose to ignore it either because it’s a false positive or because it isn’t mandatory for the desired level of accessibility certification.
In our first example, we got 3 violations and region -Ensures all page content is contained by landmarks violation does not apply to a specific test, and we wish to ignore this.
describe('To verify accessibility for practice-automation Website', () => {
it('To verify accessibility for practice-automation Website Landing Page', () => {
cy.visit('https://practice-automation.com');
cy.injectAxe();
cy.configureAxe({
rules: [
// The rule is a false alarm, so it is disabled for all test
{
id: 'region',
enabled: false,
},
],
});
cy.checkA11y(null, undefined, printHumanReadbleAccessibilityLog);
});
});
Code language: PHP (php)
Output
As you can see region rule is no longer present in the violation checks:
To verify accessibility for practice-automation Website
2 accessibility violations were detected
┌─────────┬───────────────────────────────────────┬────────────┬───────────────────────────────────────────────────────────────────────────────────────────────────────────────────┬───────┬──────────────────────────────────────────────────────────────┐
│ (index) │ id │ impact │ description │ nodes │ help │
├─────────┼───────────────────────────────────────┼────────────┼───────────────────────────────────────────────────────────────────────────────────────────────────────────────────┼───────┼──────────────────────────────────────────────────────────────┤
│ 0 │ 'color-contrast' │ 'serious' │ 'Ensures the contrast between foreground and background colors meets WCAG 2 AA minimum contrast ratio thresholds' │ 24 │ 'Elements must meet minimum color contrast ratio thresholds' │
│ 1 │ 'landmark-complementary-is-top-level' │ 'moderate' │ 'Ensures the complementary landmark or aside is at top level' │ 1 │ 'Aside should not be contained in another landmark' │
└─────────┴───────────────────────────────────────┴────────────┴───────────────────────────────────────────────────────────────────────────────────────────────────────────────────┴───────┴──────────────────────────────────────────────────────────────┘
1) To verify accessibility for practice-automation Website Landing Page
0 passing (8s)
1 failing
1) To verify accessibility for practice-automation Website
To verify accessibility for practice-automation Website Landing Page:
Code language: JavaScript (javascript)
Small hack
Add the following code to the before-all hook to ignore the rule for all tests in the spec.
cy.visit('https://practice-automation.com');
cy.injectAxe();
cy.configureAxe({
rules: [
// The rule is a false alarm, so it is disabled for all test
{
id: 'region',
enabled: false,
},
],
});
Code language: PHP (php)
Check accessibility for a particular element
Using axe-core, we can also check accessibility for a specific element. In the example below, we check the accessibility of the start button and not the whole page.
it('To verify accessibility for start button', () => {
cy.visit('https://practice-automation.com/javascript-delays/');
cy.injectAxe();
cy.configureAxe({
rules: [
// The rule is a false alarm, so it is disabled for all test
{
id: 'region',
enabled: false,
},
],
});
// check accessibilty of only the below element
cy.get('#start').checkA11y(
null,
undefined,
printHumanReadbleAccessibilityLog
);
});
Code language: PHP (php)
Output
To verify accessibility for practice-automation Website
3 accessibility violations were detected
┌─────────┬───────────────────────────────────────┬────────────┬───────────────────────────────────────────────────────────────────────────────────────────────────────────────────┬───────┬──────────────────────────────────────────────────────────────┐
│ (index) │ id │ impact │ description │ nodes │ help │
├─────────┼───────────────────────────────────────┼────────────┼───────────────────────────────────────────────────────────────────────────────────────────────────────────────────┼───────┼──────────────────────────────────────────────────────────────┤
│ 0 │ 'color-contrast' │ 'serious' │ 'Ensures the contrast between foreground and background colors meets WCAG 2 AA minimum contrast ratio thresholds' │ 5 │ 'Elements must meet minimum color contrast ratio thresholds' │
│ 1 │ 'label' │ 'critical' │ 'Ensures every form element has a label' │ 1 │ 'Form elements must have labels' │
│ 2 │ 'landmark-complementary-is-top-level' │ 'moderate' │ 'Ensures the complementary landmark or aside is at top level' │ 1 │ 'Aside should not be contained in another landmark' │
└─────────┴───────────────────────────────────────┴────────────┴───────────────────────────────────────────────────────────────────────────────────────────────────────────────────┴───────┴──────────────────────────────────────────────────────────────┘
1) To verify accessibility for start button
0 passing (7s)
1 failing
1) To verify accessibility for practice-automation Website
To verify accessibility for start button:
AssertionError: 3 accessibility violations were detected: expected 3 to equal 0
Code language: JavaScript (javascript)
Quick tip
Encourage your team to use axe accessibility linter vs. code extension to find accessibility bugs faster.
Final thoughts on Cypress accessibility testing
The article provides an overview of accessibility testing and demonstrates how we can automate aspects of it using Cypress. We’ve covered using axe-core/cypress-axe and how to leverage various configurations to make our tests more robust and productive.
Related articles
Follow our blog
Be the first to know when we publish new content.
How to test accessibility using Cypress
- Accessibility Testing Using Cypress - April 8, 2024