Тестування доступності з Playwright: перевірки WCAG у вашому наборі тестів
Оновлено 14 жовтня 2026 · 7 хв читання
Playwright керує справжнім браузером, а axe-core — це відкритий рушій правил, на якому працює більшість автоматичних перевірок доступності. Разом вони перетворюють перевірки WCAG на звичайні тести: сторінка, на якій з'явилася кнопка без назви або текст із низьким контрастом, ламає збірку ще до того, як дістанеться покупців. У цьому посібнику — робочі приклади з @axe-core/playwright, варіанти для компонентів React і Storybook, поради для CI та обмеження, про які варто знати.
Приклади відповідають офіційному посібнику Playwright з тестування доступності і README @axe-core/playwright. Той самий рушій ми використовуємо у власному сканері.
Встановлення
npm install --save-dev @playwright/test @axe-core/playwright
Версія пакета відповідає версії axe-core, яку він постачає. У його README зазначено, що він не дотримується семантичного версіонування, тож зафіксуйте версію й оновлюйте її свідомо: новий реліз axe може додати правила, які знайдуть нові проблеми.
Перший тест WCAG
import { test, expect } from '@playwright/test';
import AxeBuilder from '@axe-core/playwright';
const WCAG_AA = ['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa', 'wcag22aa'];
test('home page has no detectable WCAG A/AA violations', async ({ page }) => {
await page.goto('/');
const results = await new AxeBuilder({ page }).withTags(WCAG_AA).analyze();
expect(results.violations).toEqual([]);
});
withTags обмежує запуск правилами, позначеними цими версіями й рівнями WCAG. Без нього axe запускає ще й правила найкращих практик, які корисні, але не є вимогами WCAG.
Тестуйте стани, які люди справді бачать
Сторінка — це більше, ніж її перший рендер. Спершу відкрийте меню, висувний кошик або діалог cookie, а потім скануйте лише цю частину через include:
test('mobile menu is accessible when open', async ({ page }) => {
await page.goto('/');
await page.getByRole('button', { name: 'Menu' }).click();
const results = await new AxeBuilder({ page })
.include('#navigation-menu')
.withTags(WCAG_AA)
.analyze();
expect(results.violations).toEqual([]);
});
Добрі кандидати в магазині: сторінка товару з вибраними опціями, кошик із товарами, оформлення замовлення з показаними помилками валідації, форма входу в обліковий запис. Використання getByRole у тестах — це вже маленький тест доступності: якщо Playwright не може знайти кнопку за роллю й назвою, то користувач програми зчитування з екрана, імовірно, теж не знайде.
Відомі проблеми без вимикання тестів
У реальних проєктах є проблеми, які не виправити сьогодні, наприклад усередині стороннього віджета. README пропонує три інструменти:
const results = await new AxeBuilder({ page })
.exclude('#third-party-chat') // skip an element
.disableRules(['color-contrast']) // skip a rule (use rarely)
.withTags(WCAG_AA)
.analyze();
Кращий шаблон із посібника Playwright — знімок відбитків: ви записуєте, які правила на яких елементах не проходять, і тест падає, лише коли з'являється щось нове.
const fingerprints = (results) =>
results.violations.map((v) => ({
rule: v.id,
targets: v.nodes.map((n) => n.target),
}));
expect(fingerprints(results)).toMatchSnapshot();
Дві примітки з README: include і exclude приймають один селектор за виклик (для кількох застосовуйте ланцюжок), а повторний виклик withTags, withRules, disableRules або options перезаписує попередній.
Падайте спершу на найважливішому
Якщо беклог великий, почніть із найсерйозніших проблем. Кожне порушення має impact: minor, moderate, serious або critical:
const blocking = results.violations.filter((v) =>
['serious', 'critical'].includes(v.impact),
);
expect(blocking).toEqual([]);
Додайте повний результат до звіту, щоб той, хто виправляє, бачив кожен елемент:
// inside test('…', async ({ page }, testInfo) => { … })
await testInfo.attach('accessibility-scan-results', {
body: JSON.stringify(results, null, 2),
contentType: 'application/json',
});
Посібник Playwright також показує, як обгорнути AxeBuilder у фікстуру тестів, щоб кожен тест отримував однакові теги й винятки з одного місця.
Компоненти React: jest-axe і vitest-axe
Для тестів компонентів, що рендеряться в JSDOM, jest-axe і vitest-axe додають матчер toHaveNoViolations:
import { render } from '@testing-library/react';
import { axe } from 'vitest-axe';
import 'vitest-axe/extend-expect';
it('renders an accessible form', async () => {
const { container } = render(<SignupForm />);
expect(await axe(container)).toHaveNoViolations();
});
Вони швидкі й рано ловлять відсутні мітки та назви. Два обмеження з їхніх README: jest-axe вимикає перевірки контрастності кольорів, бо вони не працюють у JSDOM, а vitest-axe не працює із середовищем happy-dom. Контраст, верстка, масштабування і фокус потребують справжнього браузера, тож тести Playwright залишайте для повних сторінок.
Storybook
Додаток доступності Storybook запускає axe-core для кожної історії:
npx storybook add @storybook/addon-a11y
Задайте parameters.a11y.test значення 'error' для історій, що мають лишатися чистими: порушення тоді провалюють тест в інтерфейсі Storybook і в CI. 'todo' показує їх лише як попередження, а 'off' пропускає автоматичну перевірку.
Поради для CI
- Запускайте в тому самому конвеєрі, що й інші тести. Посібник Playwright з CI використовує
npm ci,npx playwright install --with-depsіnpx playwright testта завантажує текуplaywright-report/як артефакт. - Тестуйте зібрану сторінку, схожу на продакшн, а не сервер розробки з налагоджувальними накладками.
- Оберіть короткий список ключових URL — головна, категорія, товар, кошик, оформлення замовлення, вхід, сторінка з контентом — замість обходу всього на кожен коміт.
- Тримайте версію axe зафіксованою й оновлюйте її в окремому pull request.
- Не ховайте збої через
disableRulesнадовго; віддавайте перевагуexcludeдля одного елемента або шаблону зі знімком.
Чого автоматичні тести не скажуть
У README axe-core сказано, що автоматично можна знайти в середньому 57 % проблем WCAG, а посібник Playwright так само чітко зазначає, що автоматичне тестування не здатне виявити всі типи порушень WCAG. Порожній масив violations не означає, що сторінка відповідає WCAG 2.2 AA. Вам усе одно потрібні проходження з клавіатури, перевірка масштабування й перекомпонування та тест з програмою зчитування з екрана. Цю частину описує наш посібник із тестування WCAG, а чек-лист WCAG 2.2 позначає, які пункти потребують людини.
Де Pandus
Тести в CI захищають код, який ви змінюєте. Вони не бачать, що відбувається на живому сайті потім: оновлення теми, новий застосунок чи плагін, маркетинговий банер, опис товару, вставлений від постачальника. Pandus перевіряє продакшн-сайт: усі сторінки щотижня, тим самим рушієм axe-core у справжньому браузері, і дає звіт, який може прочитати власник магазину.
- API запускає перевірки й повертає результати у форматі JSON, а вебхуки повідомляють, коли перевірку завершено чи з'явилися нові проблеми, — це корисно після розгортання.
- Розширення для браузера перевіряє сторінку такою, якою її показує ваш браузер, зокрема тестові сервери й сторінки за логіном.
- Безкоштовний WCAG-чекер перевіряє одну публічну сторінку без облікового запису.
Pandus — це автоматична перевірка, а не повний аудит і не юридична консультація. Порівняння інших інструментів — у матеріалі інструменти тестування доступності.
Часті запитання
Як тестувати доступність з Playwright?
Встановіть @axe-core/playwright, відкрийте сторінку в тесті Playwright, виконайте new AxeBuilder({ page }).analyze() і перевірте, що масив violations порожній. Обмежте запуск правилами WCAG через withTags, наприклад wcag2a, wcag2aa, wcag21a, wcag21aa і wcag22aa.
Чи означає успішний тест axe, що сторінка відповідає WCAG?
Ні. Автоматичні правила знаходять лише частину проблем WCAG. Deque, розробник axe-core, оцінює це приблизно в 57 % проблем у середньому. Порядок переходу клавіатурою, змістовний альтернативний текст, порядок фокуса і зрозумілі повідомлення про помилки все одно потребують людини.
Чи можна використати jest-axe або vitest-axe?
Так, для компонентів, відрендерених у JSDOM. Вони швидкі, але jest-axe вимикає перевірку контрастності кольорів, бо вона не працює в JSDOM. Для повних сторінок у справжньому браузері, де можна перевірити контраст, верстку і фокус, використовуйте Playwright.
Як тестувати доступність компонентів Storybook?
Додайте @storybook/addon-a11y. Він запускає axe-core для кожної історії; якщо parameters.a11y.test має значення error, порушення провалюють тест в інтерфейсі Storybook і в CI.
Чи має збірка падати на кожному порушенні?
Почніть із падіння на серйозних і критичних проблемах та збережіть знімок відомих, щоб нові проблеми ламали збірку, а старі виправлялися крок за кроком. Посильте правило, коли беклог закінчиться.
Джерела
- Playwright, Accessibility testing: https://playwright.dev/docs/accessibility-testing
- Playwright, Setting up CI: https://playwright.dev/docs/ci-intro
- Deque, @axe-core/playwright README: https://github.com/dequelabs/axe-core-npm/blob/develop/packages/playwright/README.md
- Deque, axe-core README: https://github.com/dequelabs/axe-core
- jest-axe README: https://github.com/NickColley/jest-axe
- vitest-axe README: https://github.com/chaance/vitest-axe
- Storybook, Accessibility tests: https://storybook.js.org/docs/writing-tests/accessibility-testing