Screen Reader Compatibility Testing Concepts — practical accessibility testing techniques.
Screen Reader Compatibility Testing Concepts is an essential part of WCAG 2.1 AA compliance. This lesson covers the techniques, tools, and test patterns needed to verify accessible implementations in production React applications.
Testing screen reader compatibility testing concepts requires a systematic approach combining automated tools (axe-core, Lighthouse) with manual verification (keyboard testing, screen reader review).
1import { render, screen } from '@testing-library/react';
2import { axe, toHaveNoViolations } from 'jest-axe';
3
4expect.extend(toHaveNoViolations);
5
6describe('Screen Reader Compatibility Testing Concepts', () => {
7 it('meets WCAG 2.1 AA requirements', async () => {
8 const { container } = render(<YourComponent />);
9 const results = await axe(container, {
10 runOnly: { type: 'tag', values: ['wcag2a', 'wcag2aa'] },
11 });
12 expect(results).toHaveNoViolations();
13 });
14
15 it('has proper semantic structure', () => {
16 render(<YourComponent />);
17 // Verify landmarks, headings, and semantic elements
18 expect(screen.getByRole('main')).toBeInTheDocument();
19 expect(screen.getByRole('heading', { level: 1 })).toBeInTheDocument();
20 });
21});Key implementation patterns for screen reader compatibility testing concepts:
1// Accessible component pattern
2function AccessibleComponent({ label, error }: Props) {
3 const inputId = useId();
4 const errorId = useId();
5
6 return (
7 <div>
8 <label htmlFor={inputId}>{label}</label>
9 <input
10 id={inputId}
11 aria-describedby={error ? errorId : undefined}
12 aria-invalid={error ? 'true' : undefined}
13 />
14 {error && (
15 <span id={errorId} role="alert">
16 {error}
17 </span>
18 )}
19 </div>
20 );
21}End-to-end accessibility tests with Playwright:
1import { test, expect } from '@playwright/test';
2import AxeBuilder from '@axe-core/playwright';
3
4test('screen reader compatibility testing concepts meets WCAG AA', async ({ page }) => {
5 await page.goto('/your-page');
6 await page.waitForLoadState('networkidle');
7
8 const results = await new AxeBuilder({ page })
9 .withTags(['wcag2a', 'wcag2aa'])
10 .analyze();
11
12 const critical = results.violations.filter(
13 v => v.impact === 'critical' || v.impact === 'serious'
14 );
15
16 if (critical.length > 0) {
17 critical.forEach(v => console.error('[' + v.impact + '] ' + v.description));
18 }
19
20 expect(critical).toHaveLength(0);
21});Integrate screen reader compatibility testing concepts into CI:
1# .github/workflows/a11y.yml
2name: Accessibility Tests
3on: [push, pull_request]
4jobs:
5 a11y:
6 runs-on: ubuntu-latest
7 steps:
8 - uses: actions/checkout@v4
9 - uses: actions/setup-node@v4
10 with: { node-version: '20', cache: 'npm' }
11 - run: npm ci
12 - run: npm run test:a11y
13 - uses: actions/upload-artifact@v4
14 if: always()
15 with:
16 name: a11y-results
17 path: test-results/Most common screen reader compatibility testing concepts violations and their fixes:
1// Violation: Missing form label
2// BAD:
3<input type="email" placeholder="Email" />
4
5// GOOD:
6<label htmlFor="email">Email address</label>
7<input id="email" type="email" aria-required="true" />
8
9// Violation: Non-descriptive link text
10// BAD:
11<a href="/courses/1">Read more</a>
12
13// GOOD:
14<a href="/courses/typescript-mastery">
15 View TypeScript Mastery course
16</a>
17
18// Violation: Color as only differentiator
19// BAD:
20<span style={{ color: 'red' }}>Error</span>
21
22// GOOD:
23<span style={{ color: 'red' }} role="alert">
24 <svg aria-hidden="true"><path /></svg>
25 Error: Invalid email format
26</span>A working test implementation covering automated and manual accessibility testing patterns.
1// tests/a11y/screen-reader-testing.spec.ts
2import { test, expect } from '@playwright/test';
3import AxeBuilder from '@axe-core/playwright';
4
5test.describe('Screen Reader Compatibility Testing Concepts', () => {
6 test('no critical axe violations', async ({ page }) => {
7 await page.goto('/');
8 await page.waitForLoadState('networkidle');
9
10 const results = await new AxeBuilder({ page })
11 .withTags(['wcag2a', 'wcag2aa', 'wcag21aa'])
12 .analyze();
13
14 const critical = results.violations.filter(
15 v => v.impact === 'critical' || v.impact === 'serious'
16 );
17 expect(critical, 'No critical/serious a11y violations').toHaveLength(0);
18 });
19
20 test('keyboard navigation works', async ({ page }) => {
21 await page.goto('/');
22 // First Tab stop should be skip link
23 await page.keyboard.press('Tab');
24 const isSkipLink = await page.evaluate(() =>
25 document.activeElement?.textContent?.toLowerCase().includes('skip') ||
26 (document.activeElement as HTMLAnchorElement)?.href?.includes('#main')
27 );
28 expect(isSkipLink).toBe(true);
29 });
30
31 test('all images have alt text', async ({ page }) => {
32 await page.goto('/courses');
33 const missing = await page.evaluate(() =>
34 Array.from(document.querySelectorAll('img:not([aria-hidden])'))
35 .filter(img => !(img as HTMLImageElement).alt)
36 .map(img => (img as HTMLImageElement).src)
37 );
38 expect(missing, 'All images must have alt text').toHaveLength(0);
39 });
40
41 test('color contrast meets WCAG AA (4.5:1)', async ({ page }) => {
42 await page.goto('/');
43 const results = await new AxeBuilder({ page })
44 .withRules(['color-contrast'])
45 .analyze();
46 expect(results.violations).toHaveLength(0);
47 });
48});This test suite covers the four key screen reader compatibility testing concepts areas: automated axe-core scanning for structural violations, keyboard navigation verification, image alt text completeness, and color contrast checking. The axe-core approach catches 30-40% of WCAG issues automatically; the remaining violations require the manual keyboard testing and screen reader verification covered in subsequent lessons.
Continue with the next accessibility topic to build a comprehensive WCAG 2.1 AA test suite.
Your progress is saved in this browser, on this device only.