Accessibility Testing in Playwright

Anandhi K
6 min readDec 29, 2023

This article is about Accessibility Testing and how to automate it using Playwright with Typescript.

What is Accessibility Testing?

Accessibility testing is a type of software testing process that verifies whether the website or web application is usable by every person who may or may not have disabilities.

It makes sure that the user can access the website content without any physical interactions like without using the computer keyboard or a mouse and touchpad!

Accessibility is also abbreviated as A11Y. Accessibility’s standard rules are derived by WCAG(Web Content Accessibility Guidelines Web Content Accessibility Guidelines (WCAG) 2.1 (w3.org))

About WCAG :

Web Content Accessibility Guidelines (WCAG) 2.1 defines how to make Web content more accessible to people with disabilities. Accessibility involves a wide range of disabilities, including visual, auditory, physical, speech, cognitive, language, learning, and neurological disabilities.

WCAG 2.1 is developed through the W3C process in cooperation with individuals and organizations around the world, with a goal of providing a shared standard for Web content accessibility that meets the needs of individuals, organizations, and governments internationally.

About axe-core manual testing

Automated accessibility tests can detect some common accessibility problems such as missing or invalid properties. But many accessibility problems can only be discovered through manual testing. It is better to use a combination of automated testing, manual accessibility assessments, and inclusive user testing. For manual assessments, Accessibility Insights for Web, a free and open source dev tool that walks through assessing a website for WCAG 2.1 AA coverage.

Accessibility Testing using Playwright :

We can automate Accessibility testing using Playwright.

A few examples of problems this can catch include:

· Text that would be hard to read for users with vision impairments due to poor color contrast with the background behind it

· UI controls and form elements without labels that a screen reader could identify

· Interactive elements with duplicate IDs which can confuse assistive technologies

To automate using Playwright we need to install Playwright and axe-core/playwright node packages.

To install required packages,

npm install @playwright/test
npm install @axe-core/playwright
"dependencies": {
"@axe-core/playwright": "^4.7.3",
"playwright": "^1.35.1"
}

Here we will create test scripts on how,

1. To scan an entire page

2. To scan a specific part of a page

3. To scan for WCAG violations

4. To exclude individual elements with known issues

5. To disable individual scan rule

1. To scan an entire page :

To scan an entire page create an instance of AxeBuilder and invode its analyze() method.

test('Detect Accessibility issues in entire page...', async ({ page }) => {
await page.goto('https://demo.opencart.com/')
const scanResutls = await new AxeBuilder({ page }).analyze()
//Console log the violations
let violation = scanResutls.violations;
violation.forEach(function (entry) {
console.log(
"Print the Violations:",
entry.impact + " " + entry.description
);
});
let count = violation.length;
console.log("List of Violations:", count);
expect(count).toEqual(5);
})

The output would be,

2. To scan a specific part of a page :

AxeBuilder class has many options to configure. For example, we can use AxeBuilder.include() to constrain an accessibility scan to only run against one specific part of a page.

AxeBuilder.analyze() will scan the page in its current state when we call it. To scan parts of a page that are revealed based on UI interactions, should use Locators to interact with the page before invoking analyze().

test('Configuring axe to scan a specific part of a page', async ({ page }) => {
await page.goto('https://demo.opencart.com/');
await page.getByRole('link', { name: 'Components' }).hover();
page.getByRole('link', { name: 'Monitors (2)' }).waitFor()
await page.getByRole('link', { name: 'Monitors (2)' }).click()
// await page.locator('ul.list-unstyled').waitFor();
const accessibilityScanResults = await new AxeBuilder({ page })
.include('.product-thumb')
.analyze();

let violations = await accessibilityScanResults.violations;
console.log("Total violations..." + violations.length)
violations.forEach(function (entry) {
console.log(
"Print the Violations:",
entry.impact + " " + entry.description
);
});
expect(violations.length).toEqual(1);
})

The output would be,

3. To scan for WCAG violations :

By default, axe checks against a wide variety of accessibility rules. Some of these rules correspond to specific success criteria from the Web Content Accessibility Guidelines (WCAG), and others are “best practice” rules that are not specifically required by any WCAG criterion.

We can constrain an accessibility scan to only run those rules which are “tagged” as corresponding to specific WCAG success criteria by using AxeBuilder.withTags().

For example, Accessibility Insights for Web’s Automated Checks only include axe rules that test for violations of WCAG A and AA success criteria; to match that behavior. We are going to scan for specific violations like [‘wcag2aa’, ‘wcag2a’]

Note : Automated testing cannot detect all types of WCAG violations.

test('Should  have any automatically detectable WCAG A or AA violations', async ({ page }) => {
await page.goto('https://demo.opencart.com/');
const accessibilityScanResults = await new AxeBuilder({ page })
// .withTags(['wcag2aa', 'wcag2a', 'wcag21a'])
.withTags(['wcag2aa', 'wcag2a'])
.analyze();
let violations = await accessibilityScanResults.violations;
console.log("Total violations..." + violations.length)
violations.forEach(function (entry) {
console.log(
"Print the Violations:",
entry.impact + " " + entry.description
)
})
//.withTags(['wcag2aa'])
expect(violations.length).toEqual(3);
});

The output would be,

4. To exclude individual elements with known issues :

To suppress known violations, we have exclude method. Use AxeBuilder.exclude() to exclude them from being scanned until those issues are fixed.

This is usually the simplest option, but it has some important downsides:

· exclude() will exclude the specified elements and all of their descendants. Avoid using it with components that contain many children.

· exclude() will prevent all rules from running against the specified elements, not just the rules corresponding to known issues.

 await page.goto('https://demo.opencart.com/');
// .list-inline-item:nth-child(1) > a - This elmeent got a violation
//Ensures links have discernible text.
//To void this from scanning, exclude this element.
const accessibilityScanResults = await new AxeBuilder({ page })
.exclude('.list-inline-item:nth-child(1) > a')
.analyze();

let violations = accessibilityScanResults.violations;
violations.forEach(function (entry) {
console.log(
"Print the Violations:",
entry.impact + " " + entry.description
)
})
console.log("Total violations..." + violations.length)

The output would be,

5. To disable individual scan rule :

If application contains many different pre-existing violations of a specific rule, you can use AxeBuilder.disableRules() to temporarily disable individual rules until you’re able to fix the issues.

You can find the rule IDs to pass to disableRules() in the id property of the violations you want to suppress.

In the above test results there is a violation ‘Ensures buttons have discernible text’ whose Rule ID is ‘button-name’. To void scanning this rule, it has to be included as parameter in disableRule method.

The mentioned url has list of Rules defined — axe-core/doc/rule-descriptions.md at master · dequelabs/axe-core (github.com).

 test('To disable individual scan rule', async ({
page,
}) => {
await page.goto('https://demo.opencart.com/');
//To void scanning an element which has voilation
//Description - Ensures buttons have discernible text
//RuleID - button-name
const accessibilityScanResults = await new AxeBuilder({ page })
// .disableRules('Ensures buttons have discernible text')
.disableRules('button-name')
.analyze();
let violations = accessibilityScanResults.violations;

violations.forEach(function (entry) {
console.log(
"Print the Violations:",
entry.impact + " " + entry.description
)
})
console.log("Total violations..." + violations.length)

})

The below violations list does not have disabled rule.

References :

Accessibility testing | Playwright

Web Content Accessibility Guidelines (WCAG) 2.1 (w3.org)

axe-core/doc/rule-descriptions.md at master · dequelabs/axe-core (github.com)

Conclusion:

Using axe-core/playwright library we are able to do Accessibility Testing. And from this library we have seen some of the options to perform Accessibility testing from Playwright.

--

--

Anandhi K

DevOps Test Automation Consultant, Trainer and Blogger in Cypress, Selenium, Cucumber, Playwright & CI/CD Tools.