Automating accessibility checks using Playwright

We were tasked with introducing accessibility testing into one of our projects, and our first goal was to get a quick, high-level overview of the current accessibility state of the application. We needed a fast, efficient way to identify existing issues before diving deeper.
Since we already had an automation suite built with Playwright and Typescript — we thought, why not integrate accessibility checks right into our existing setup?
Approach:
- We started by selecting the axe-core library, known for its simplicity and seamless integration with Playwright.
- We integrated accessibility checks into the existing smoke test suite as it covered most critical user flows. This allowed us to get maximum visibility into key accessibility issues with minimal extra effort.
- We introduced a configuration to turn accessibility testing ON or OFF, as testing accessibility was not the primary goal of the smoke suite.
Let's walk through how we implemented this solution, step by step.
01. Installing axe-core and axe-html-reporter
First we needed to install the axe-core library in our existing automation solution.
npm install @axe-core/playwright
npm install axe-html-reporter
02. Import axe-core and axe-html-reporter packages
import { AxeBuilder } from '@axe-core/playwright';
import { createHtmlReport } from 'axe-html-reporter';
03. Implement accessibilityChecker.ts to avoid code duplication
Since our goal was to integrate accessibility checks into the existing smoke suite, we wanted to avoid duplicating accessibility logic across individual test scripts.
Instead, we focused on creating a centralized, reusable solution that could be easily plugged into relevant tests.
In this code, we’re checking for a few key WCAG rules, but there’s a lot more you can customize based on your accessibility needs. (Playwright documentation:👉 https://playwright.dev/docs/accessibility-testing)
//accessibilityChecker.ts
import { TestInfo, expect } from '@playwright/test';
import { AxeBuilder } from '@axe-core/playwright';
import { createHtmlReport } from 'axe-html-reporter';
import * as fs from 'fs';
import * as path from 'path';
export async function runAccessibilityCheck(page, testInfo, description): Promise<void> {
// Get axeBuilder from the fixture
const axeResults = await new AxeBuilder({ page })
.withTags(['wcag22aa', 'wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa'])
.analyze();
// Generate HTML report if violations exist
if (axeResults.violations.length > 0) {
const reportDir = 'test-results/axe-core-reports'
const reportPath = path.join(reportDir, `${description}-accessibility-report.html`);
createHtmlReport({
results: axeResults,
options: {
outputDir: reportDir,
reportFileName: `${description}-accessibility-report.html`,
},
});
// Ensure the file is created before attaching
if (fs.existsSync(reportPath)) {
await testInfo.attach(`${description}-accessibility-report`, {
path: reportPath,
contentType: 'text/html',
});
} else {
console.error(`Failed to generate accessibility report at: ${reportPath}`);
}
expect.soft(axeResults.violations,`Accessibility violations found in ${description} page`).toEqual([]);
}
}
04. Configuration to ON/OFF accessibility checks
To control the accessibility checks dynamically, you can create a configuration in your .env
file.
//.env
# Configurations
ENABLE_ACCESSIBILITY_CHECKS=true
05. Integrate accessibility checks into smoke test suite
After each page loaded in our smoke test suite, we called the runAccessibilityCheck function to ensure that accessibility violations were detected immediately.
Additionally, we ensured that the page was correctly loaded by using assertions before performing accessibility testing, which helped ensure more accurate and reliable results.
Note : I have used the Page Object Model in my test suite, so you’ll only see method calls like loginPage.loadLoginPage()
or homePage.selectProduct()
in the test script.
//smoke.spec.ts
import { test } from '../../pages/fixtures';
import { runAccessibilityCheck } from '../../utils/accessibilityChecker';
const ENABLE_ACCESSIBILITY_CHECKS = process.env.ENABLE_ACCESSIBILITY_CHECKS === 'true';
test.describe('Smoke Test Suite', () => {
test('Check out flow verification', async ({ page, loginPage, homePage, productDetailsPage }, testInfo) => {
await loginPage.loadLoginPage();
if (ENABLE_ACCESSIBILITY_CHECKS) {
await runAccessibilityCheck(page, testInfo, 'login-page');
}
await loginPage.Login(process.env.CUSTOMER_01_USERNAME, process.env.CUSTOMER_01_PASSWORD);
await homePage.clickHome();
if (ENABLE_ACCESSIBILITY_CHECKS) {
await runAccessibilityCheck(page, testInfo, 'home-page');
}
await homePage.selectProduct();
if (ENABLE_ACCESSIBILITY_CHECKS) {
await runAccessibilityCheck(page, testInfo, 'product-details-page');
}
await productDetailsPage.addItemToCart();
// Smoke Test code to be enhanced here
});
});
After executing the test script, an accessibility report for each page will be generated and attached to your Playwright HTML report.
This accessibility reports provides a detailed view of any accessibility violation detected during the test.
In this article, I've shared our practical approach to integrating accessibility testing with Playwright and TypeScript. By embedding accessibility checks directly into our existing smoke test suite, we achieved three significant outcomes:
- Early detection of issues:
Integrating accessibility checks directly into our smoke test suite allowed us to identify common accessibility issues early in the development process. Additionally, we were able to identify the pages with the most issues. This provided us with clear entry points to focus our manual accessibility testing efforts on those specific areas.
- Reduced loading time of application state:
With the complexity of the application, there were certain application states that took considerable time to recreate during testing. These states were already covered in our existing smoke test suite. So integrating accessibility checks into smoke test suite allowed us to validate accessibility without reloading or re-navigating through complex flows. This approach saved time and reduced redundancy.
- Revalidated after implementing the fixes:
Once fixes were applied to the reported accessibility issues, we re-executed our smoke test suite with accessibility checks enabled to verify that the issues were successfully resolved.
This implementation demonstrates that accessibility testing doesn't need to be a separate, resource-intensive process when introducing it into a project for the first time, especially for identifying common accessibility failures. With minimal code changes and thoughtful integration, we've made accessibility validation a natural part of our testing workflow.
Furthermore, by integrating accessibility checks into our functional testing, we can ensure that our application becomes progressively more inclusive with each development cycle.
For a complete implementation example including source code and configuration details, visit my GitHub repository: https://github.com/Thara90/playwright-integrations