Playwright Testing for Accessibility with Screen Reader Simulation
Test ARIA live regions, focus management, keyboard navigation, and screen reader announcements using Playwright accessibility APIs and axe-core.
playwright-v1-49-matrix
Playwright Testing for Accessibility with Screen Reader Simulation
Test ARIA live regions, focus management, keyboard navigation, and screen reader announcements using Playwright accessibility APIs and axe-core. This guide provides step-by-step Playwright patterns to handle this scenario reliably.
Introduction
Robust automation requires handling domain-specific patterns effectively. This article covers playwright testing for accessibility with screen reader simulation with production-ready Playwright code.
Architecture Overview
graph TD
Trigger["Open Modal"] --> Focus["Focus Moves to Modal"]
Focus --> Trap["Focus Trapped Inside"]
Trap --> Close["Close Modal"]
Close --> Return["Focus Returns to Trigger"]Implementation Guide
import { injectAxe, checkA11y } from 'axe-playwright';
test('modal manages focus correctly for screen readers', async ({ page }) => {
await page.goto('/app');
await page.getByRole('button', { name: 'Open Settings' }).click();
// Focus should move to modal
const modal = page.getByRole('dialog', { name: 'Settings' });
await expect(modal).toBeVisible();
await expect(modal).toBeFocused();
// Check no accessibility violations in modal
await injectAxe(page);
await checkA11y(page, '[role="dialog"]');
// Close and verify focus returns to trigger
await page.keyboard.press('Escape');
await expect(page.getByRole('button', { name: 'Open Settings' })).toBeFocused();
});Run Your Tests
npx playwright test --grep "Playwright Testing for"
npx playwright test --uiReference Table
| ARIA Pattern | Test Method | Expected Behavior |
|---|---|---|
| Modal dialog | getByRole('dialog') | Focus trapped |
| Live region | aria-live | Announced change |
| Skip link | keyboard Tab | First focusable |
| Combobox | keyboard ArrowDown | Options announced |
Best Practices
getByRole() and getByLabel() for resilient locators that survive UI changes.await expect(locator).toBeVisible() instead of waitForTimeout()Common Mistakes
| Anti-Pattern | Solution |
| Hardcoded wait times | Use auto-waiting assertions |
| Shared mutable test data | Create unique data per test |
| Testing 3rd party UIs directly | Mock external services |
Frequently Asked Questions
How to test ARIA live region announcements?
Trigger the dynamic content change and use aria-live=polite/assertive to verify screen readers would announce it.
Can Playwright test keyboard focus order?
Yes, press Tab repeatedly and verify focus moves in the correct DOM order through interactive elements.
How to test skip navigation links?
Tab on page load and verify the first focusable element is the skip link that jumps to main content.
How to test combobox keyboard interactions?
Focus the combobox, press ArrowDown, and verify the dropdown opens and first option receives focus.
Can Playwright test ARIA expanded states?
Yes, use expect(button).toHaveAttribute('aria-expanded', 'true') to verify accordion and dropdown states.
Summary
Test ARIA live regions, focus management, keyboard navigation, and screen reader announcements using Playwright accessibility APIs and axe-core. Apply these patterns to build a reliable, maintainable automation suite for your specific use case.
Related Articles
About The Author
PlaywrightPad Editorial reports on Chromium engines, E2E test optimizations, and AI integration specifications.
Newsletter
Get weekly browser reports sent directly to your inbox.