React
Test di accessibilità con axe: cosa trova e cosa no
axe controlla un componente React dentro il test: trova ruoli mancanti ed etichette assenti, ma non giudica contrasto, ordine di tabulazione o lettura.
In sintesi
- axe esegue regole sul DOM renderizzato e restituisce violazioni con un livello di impatto.
- In jsdom la regola del contrasto non gira: nessun CSS, nessun layout, nessun pixel.
- Il test boccia solo impact critical; le violazioni minori vanno in backlog, non in CI.
axe trova in un test le violazioni strutturali del DOM: ruoli mancanti, etichette assenti, gerarchia di titoli rotta. Non trova contrasto, ordine di tabulazione o senso di lettura.
La regola di base
axe-core, pubblicato da Deque con licenza MPL-2.0, esegue un set di regole sul DOM già renderizzato. Ogni violazione porta un impact: critical, serious, moderate o minor.
Il test che vale come modello
import { render } from "@testing-library/react";
import { axe, toHaveNoViolations } from "jest-axe";
expect.extend(toHaveNoViolations);
test("il dialog non ha violazioni critiche", async () => {
const { container } = render();
const { violations } = await axe(container);
expect(violations.filter((v) => v.impact === "critical")).toHaveLength(0);
});
Il test renderizza, esegue axe e boccia solo critical. Filtrare per impatto tiene la CI verde: un test che fallisce per un aria-* deprecato viene spento dopo una settimana.
Cosa controlla davvero
- Ruoli ed etichette: un input senza label o un pulsante senza nome accessibile viene segnalato.
- Struttura: heading saltati, landmark duplicati, liste con figli non validi.
- ARIA: attributi sconosciuti,
aria-*che richiede un ruolo assente. - Contenuti: link con testo vuoto, immagini senza
alt, iframe senza titolo. - Duplicati: due
iduguali nella stessa pagina.
Installazione
npm i -D vitest jsdom @testing-library/react jest-axe
npx vitest run --environment jsdom
jest-axe è MIT e axe-core è MPL-2.0: entrambi girano in locale, senza servizi esterni e senza chiamate di rete.
Record vivi nel catalogo
Tre record su cui provare il primo test: dialog, navigation-menu e dropdown-menu. Per esplorare per categoria parti da 111.985 componenti.
La limitazione
In jsdom non c'è CSS: la regola color-contrast non può girare e axe la salta in silenzio. Il test automatico copre una parte dei criteri WCAG, non tutti: contrasto, focus visibile e lettura con screen reader restano manuali.
Dove continuare
La checklist manuale è in componenti React accessibili; il caso del modale è in dialog accessibile; il modello di test è in Testing Library: cinque test; il confronto misurato fra librerie è in librerie React 2026.
Domande frequenti
axe sostituisce un test manuale?
No. Copre una parte dei criteri WCAG e lascia fuori contrasto, focus e lettura: il test manuale resta obbligatorio.
Perché il contrasto non viene controllato?
Perché jsdom non applica CSS. Senza colori calcolati e layout la regola non ha nulla da misurare, quindi viene saltata.
Meglio jest-axe o vitest-axe?
Sono lo stesso motore. jest-axe funziona con Vitest dopo expect.extend, quindi non serve un pacchetto diverso.
Quante violazioni deve avere un componente per passare?
Zero critical. Le violazioni serious e moderate si correggono, ma non bloccano la pipeline.
axe-core è gratis per uso commerciale?
Sì. axe-core è MPL-2.0 e jest-axe è MIT: uso commerciale permesso, con obbligo di pubblicare le modifiche al motore.