Salt la conținut
Pandus

Testarea accesibilității cu Playwright: verificări WCAG în suita de teste

Actualizat 14 octombrie 2026 · Lectură de 8 min

Playwright controlează un browser real, iar axe-core este motorul de reguli open-source din spatele majorității verificatoarelor automate de accesibilitate. Împreună transformă verificările WCAG în teste obișnuite: o pagină care primește un buton fără nume sau un text cu contrast slab oprește build-ul înainte să ajungă la clienți. Acest ghid prezintă exemple funcționale cu @axe-core/playwright, opțiunile pentru componente React și Storybook, sfaturi pentru CI și limitele pe care e bine să le cunoașteți.

Exemplele urmează ghidul oficial Playwright pentru testarea accesibilității și README-ul @axe-core/playwright. Folosim același motor în propriul nostru scanner.

Instalare

npm install --save-dev @playwright/test @axe-core/playwright

Versiunea pachetului urmează versiunea axe-core pe care o include. README-ul precizează că nu respectă versionarea semantică, așa că fixați versiunea și actualizați-o în mod deliberat: o nouă versiune axe poate adăuga reguli care găsesc probleme noi.

Un prim test 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 limitează rularea la regulile etichetate cu aceste versiuni și niveluri WCAG. Fără el, axe rulează și regulile sale de bune practici, care sunt utile, dar nu sunt cerințe WCAG.

Testați stările pe care oamenii le văd efectiv

O pagină înseamnă mai mult decât prima ei randare. Deschideți mai întâi meniul, sertarul coșului sau dialogul de cookie-uri, apoi scanați doar acea parte cu include:

test('mobile menu is accessible when open', async ({ page }) => {
    await page.goto('/');
    await page.getByRole('button', { name: 'Meniu' }).click();

    const results = await new AxeBuilder({ page })
        .include('#navigation-menu')
        .withTags(WCAG_AA)
        .analyze();

    expect(results.violations).toEqual([]);
});

Candidați buni într-un magazin: pagina de produs cu opțiuni selectate, coșul cu produse, checkout-ul cu erorile de validare afișate, formularul de autentificare în cont. Folosirea getByRole în teste este în sine un mic test de accesibilitate: dacă Playwright nu găsește butonul după rol și nume, probabil nici un utilizator de cititor de ecran nu îl găsește.

Probleme cunoscute fără a dezactiva testele

Proiectele reale au probleme pe care nu le puteți repara azi, de exemplu într-un widget terț. README-ul oferă trei instrumente:

const results = await new AxeBuilder({ page })
    .exclude('#third-party-chat')      // sare peste un element
    .disableRules(['color-contrast'])  // sare peste o regulă (rar)
    .withTags(WCAG_AA)
    .analyze();

Un model mai bun din ghidul Playwright este un snapshot de amprente: înregistrați ce reguli pică pe ce elemente, iar testul pică doar când apare ceva nou.

const fingerprints = (results) =>
    results.violations.map((v) => ({
        rule: v.id,
        targets: v.nodes.map((n) => n.target),
    }));

expect(fingerprints(results)).toMatchSnapshot();

Două observații din README: include și exclude primesc un singur selector per apel (înlănțuiți-le pentru mai mulți), iar apelarea din nou a withTags, withRules, disableRules sau options suprascrie apelul anterior.

Opriți mai întâi ce contează

Dacă restanța este mare, începeți cu problemele cele mai grave. Fiecare încălcare are un impact de minor, moderate, serious sau critical:

const blocking = results.violations.filter((v) =>
    ['serious', 'critical'].includes(v.impact),
);
expect(blocking).toEqual([]);

Atașați rezultatul complet la raport, ca oricine repară problema să vadă fiecare element:

// în interiorul test('…', async ({ page }, testInfo) => { … })
await testInfo.attach('accessibility-scan-results', {
    body: JSON.stringify(results, null, 2),
    contentType: 'application/json',
});

Ghidul Playwright arată și cum să încapsulați AxeBuilder într-un test fixture, astfel încât fiecare test primește aceleași etichete și excluderi dintr-un singur loc.

Componente React: jest-axe și vitest-axe

Pentru testele de componente care se randează în JSDOM, jest-axe și vitest-axe adaugă un matcher 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();
});

Sunt rapide și prind devreme etichetele și numele lipsă. Două limite din README-urile lor: jest-axe dezactivează verificările de contrast al culorilor, pentru că nu funcționează în JSDOM, iar vitest-axe nu funcționează cu mediul happy-dom. Contrastul, macheta, zoom-ul și focusul au nevoie de un browser real, așa că păstrați testele Playwright pentru pagini întregi.

Storybook

Addonul de accesibilitate Storybook rulează axe-core pe fiecare story:

npx storybook add @storybook/addon-a11y

Setați parameters.a11y.test la 'error' pentru story-urile care trebuie să rămână curate: încălcările fac atunci testul să pice în interfața Storybook și în CI. 'todo' le afișează doar ca avertismente, iar 'off' sare peste verificarea automată.

Sfaturi pentru CI

  • Rulați în același pipeline cu celelalte teste. Ghidul Playwright pentru CI folosește npm ci, npx playwright install --with-deps și npx playwright test și încarcă folderul playwright-report/ ca artefact.
  • Testați o pagină construită, apropiată de producție, nu un server de dezvoltare cu suprapuneri de depanare.
  • Alegeți o listă scurtă de URL-uri cheie — prima pagină, o categorie, un produs, coșul, checkout-ul, autentificarea, o pagină de conținut — în loc să parcurgeți totul la fiecare commit.
  • Păstrați versiunea axe fixată și actualizați-o într-un pull request separat.
  • Nu ascundeți eșecurile cu disableRules pe termen lung; preferați exclude pentru un singur element sau modelul cu snapshot.

Ce nu vă pot spune testele automate

README-ul axe-core spune că puteți găsi automat în medie 57 % din problemele WCAG, iar ghidul Playwright este la fel de clar că testarea automată nu poate detecta toate tipurile de încălcări WCAG. O listă violations goală nu înseamnă că o pagină îndeplinește WCAG 2.2 AA. Aveți în continuare nevoie de o parcurgere cu tastatura, de o verificare a zoom-ului și a readaptării conținutului și de un test cu cititorul de ecran. Ghidul nostru de testare WCAG descrie această parte, iar checklistul WCAG 2.2 marchează punctele care au nevoie de o persoană.

Unde se încadrează Pandus

Testele din CI protejează codul pe care îl modificați. Ele nu văd ce se întâmplă ulterior pe site-ul live: o actualizare de temă, o aplicație sau un plugin nou, un banner de marketing, o descriere de produs copiată de la un furnizor. Pandus verifică site-ul de producție: fiecare pagină în fiecare săptămână, cu același motor axe-core într-un browser real, și un raport pe care un proprietar de magazin îl poate citi.

  • API-ul pornește verificări și returnează rezultatele ca JSON, cu webhook-uri când o verificare se termină sau apar probleme noi — util după un deployment.
  • Extensia de browser verifică o pagină așa cum o afișează browserul dumneavoastră, inclusiv staging și pagini din spatele unei autentificări.
  • WCAG checkerul gratuit verifică o pagină publică fără cont.

Pandus este o verificare automată, nu un audit complet, și nu constituie consultanță juridică. O comparație a altor instrumente găsiți în instrumente de testare a accesibilității.

Întrebări frecvente

Cum testez accesibilitatea cu Playwright?

Instalați @axe-core/playwright, deschideți pagina într-un test Playwright, rulați new AxeBuilder({ page }).analyze() și verificați că lista violations este goală. Limitați rularea la regulile WCAG cu withTags, de exemplu wcag2a, wcag2aa, wcag21a, wcag21aa și wcag22aa.

Un test axe trecut înseamnă că pagina îndeplinește WCAG?

Nu. Regulile automate găsesc doar o parte din problemele WCAG. Deque, producătorul axe-core, estimează în medie aproximativ 57 % din probleme. Parcurgerea cu tastatura, textul alternativ cu sens, ordinea focusului și erorile ușor de înțeles au nevoie în continuare de o persoană.

Pot folosi jest-axe sau vitest-axe în schimb?

Da, pentru componente randate în JSDOM. Sunt rapide, dar jest-axe dezactivează verificarea contrastului culorilor, pentru că aceasta nu funcționează în JSDOM. Folosiți Playwright pentru pagini întregi într-un browser real, unde contrastul, macheta și focusul pot fi verificate.

Cum testez accesibilitatea componentelor Storybook?

Adăugați @storybook/addon-a11y. Acesta rulează axe-core pe fiecare story; cu parameters.a11y.test setat la error, încălcările fac testul să pice în interfața Storybook și în CI.

Trebuie ca build-ul să pice la fiecare încălcare?

Începeți prin a face build-ul să pice la problemele serious și critical și păstrați un snapshot al celor cunoscute, astfel încât problemele noi să oprească build-ul, iar cele vechi să fie reparate pas cu pas. Înăspriți regula după ce restanța a dispărut.

Surse

  • 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