Tester l'accessibilité JavaScript : Outils et méthodes 2026
En Bref : L'essentiel à retenir
- Un test automatisé vérifie la présence d'un attribut, jamais sa pertinence : la majeure partie de la conformité RGAA reste un travail humain.
- Le bon usage des tests automatisés est la non-régression : ils empêchent un défaut déjà corrigé de revenir, ils ne prouvent pas la conformité.
- Branchez-les au plus près du code (test unitaire de composant) plutôt qu'en fin de chaîne : un défaut détecté sur un composant est corrigé partout à la fois.
- Un échec de test signale une anomalie à instruire, pas une non-conformité établie.
Les applications JavaScript modernes (React, Vue, Angular) présentent des défis spécifiques en matière d'accessibilité : contenu dynamique, gestion du focus, composants interactifs complexes. Heureusement, l'écosystème de tests s'est considérablement enrichi pour répondre à ces enjeux.
Les limites des tests automatisés
Avant de plonger dans les outils, une mise en garde. Un automate vérifie la présence d'un élément, jamais sa pertinence. Il sait dire qu'une image porte un attribut alt ; il ne sait pas dire que cet alt décrit correctement l'image. Or une part importante des critères RGAA porte précisément sur ce jugement, ce que détaille notre page de couverture des critères. Les tests excellent en revanche pour identifier :
- Les images sans attribut
alt - Les contrastes insuffisants
- Les formulaires sans labels
- Les structures de titres incorrectes
- Les liens vides
Mais ils ne peuvent pas évaluer :
- La pertinence d'un texte alternatif
- La cohérence de l'ordre de lecture
- L'expérience réelle avec un lecteur d'écran
- La compréhensibilité du contenu
axe-core : la référence du marché
Développé par Deque, axe-core est le moteur de test d'accessibilité le plus utilisé. Il alimente de nombreux outils (Lighthouse, axe DevTools, Pa11y) et s'intègre dans tous les environnements JavaScript.
Installation
npm install axe-core --save-dev
Utilisation basique
import axe from 'axe-core';
// analyser toute la page
axe.run(document).then(results => {
if (results.violations.length) {
console.error('Problèmes d\'accessibilité détectés :', results.violations);
}
});
Configuration avancée
axe.run(document, {
runOnly: {
type: 'tag',
values: ['wcag2a', 'wcag2aa', 'wcag21aa']
},
rules: {
'color-contrast': { enabled: true },
'region': { enabled: false } // désactiver une règle spécifique
}
}).then(results => {
// traiter les résultats
});
Intégration avec les frameworks de test
Jest + React Testing Library
import { render } from '@testing-library/react';
import { axe, toHaveNoViolations } from 'jest-axe';
import MonComposant from './MonComposant';
expect.extend(toHaveNoViolations);
describe('MonComposant', () => {
it('ne doit pas avoir de violations d\'accessibilité', async () => {
const { container } = render(<MonComposant />);
const results = await axe(container);
expect(results).toHaveNoViolations();
});
});
Cypress avec cypress-axe
// cypress/support/commands.js
import 'cypress-axe';
// Dans vos tests
describe('Page d\'accueil', () => {
it('doit être accessible', () => {
cy.visit('/');
cy.injectAxe();
cy.checkA11y();
});
it('doit être accessible après interaction', () => {
cy.visit('/');
cy.injectAxe();
cy.get('[data-cy=menu-toggle]').click();
cy.checkA11y('.menu-ouvert');
});
});
Playwright
import { test, expect } from '@playwright/test';
import AxeBuilder from '@axe-core/playwright';
test('accessibilité de la page', async ({ page }) => {
await page.goto('/');
const results = await new AxeBuilder({ page })
.withTags(['wcag2a', 'wcag2aa'])
.analyze();
expect(results.violations).toEqual([]);
});
Tester le contenu dynamique
Les applications SPA nécessitent des tests spécifiques pour le contenu chargé dynamiquement.
Attendre le rendu
// Avec Cypress
cy.visit('/dashboard');
cy.get('[data-cy=data-loaded]').should('be.visible');
cy.injectAxe();
cy.checkA11y();
Tester les états interactifs
// Tester un modal
it('le modal doit être accessible', async () => {
render(<App />);
// état initial
const { container: closedContainer } = render(<App />);
expect(await axe(closedContainer)).toHaveNoViolations();
// Ouvrir le modal
fireEvent.click(screen.getByText('Ouvrir'));
// Tester avec le modal ouvert
expect(await axe(document.body)).toHaveNoViolations();
});
Astuce
Consultez notre article sur l'accessibilité des SPA React et Vue pour des conseils spécifiques sur la gestion du focus.
Intégration CI/CD
L'objectif ultime est d'intégrer les tests d'accessibilité dans votre pipeline d'intégration continue.
GitHub Actions
name: Tests d'accessibilité
on: [push, pull_request]
jobs:
a11y:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '18'
- run: npm ci
- run: npm run build
- run: npm run test:a11y
Configuration package.json
{
"scripts": {
"test:a11y": "jest --testPathPattern=a11y",
"test:a11y:ci": "cypress run --spec 'cypress/e2e/accessibility/**'"
}
}
Outils complémentaires
Lighthouse CI
npm install -g @lhci/cli
lhci autorun --collect.url=http://localhost:3000 \
--assert.assertions.categories:accessibility=error
Pa11y
const pa11y = require('pa11y');
async function testPages() {
const urls = ['/', '/contact', '/produits'];
for (const url of urls) {
const results = await pa11y(`http://localhost:3000${url}`);
console.log(`${url}: ${results.issues.length} problèmes`);
}
}
ESLint pour JSX
npm install eslint-plugin-jsx-a11y --save-dev
// .eslintrc.js
module.exports = {
plugins: ['jsx-a11y'],
extends: ['plugin:jsx-a11y/recommended']
};
Note
Le plugin eslint-plugin-jsx-a11y détecte les problèmes d'accessibilité directement dans votre éditeur, avant même l'exécution du code.
Bonnes pratiques
1. Tester à chaque étape
- Développement : ESLint + tests unitaires
- Pull Request : Tests Cypress/Playwright en CI
- Staging : Audit complet avec RGAA Checker
- Production : Monitoring continu
2. Ne pas désactiver les tests qui échouent
// MAUVAISE PRATIQUE
it.skip('accessibilité du formulaire', async () => { ... });
// BONNE PRATIQUE : corriger le problème ou documenter l'exception
it('accessibilité du formulaire', async () => {
const results = await axe(container, {
rules: {
// Exception documentée : le composant tiers X n'est pas conforme
'specific-rule': { enabled: false }
}
});
expect(results).toHaveNoViolations();
});
3. Catégoriser les violations
const results = await axe(document);
const critical = results.violations.filter(v => v.impact === 'critical');
const serious = results.violations.filter(v => v.impact === 'serious');
// Échouer uniquement sur les violations critiques en CI
expect(critical).toHaveLength(0);
// Logger les autres pour correction ultérieure
if (serious.length) {
console.warn('Violations sérieuses à corriger :', serious);
}
Conclusion
Les tests automatisés d'accessibilité JavaScript sont un filet de sécurité indispensable, mais ils ne remplacent pas les tests manuels. Combinez axe-core dans vos tests unitaires, Cypress ou Playwright pour les tests E2E, et complétez par des audits réguliers avec des outils comme RGAA Checker.
Pour une couverture complète, consultez notre guide des tests utilisateurs et notre comparatif des lecteurs d'écran.
Questions fréquentes
Les tests automatisés suffisent-ils pour la conformité RGAA ?
Non. Un automate vérifie la présence d'un élément (une alternative textuelle existe, une étiquette est associée) mais pas sa pertinence (cette alternative décrit-elle correctement l'image). Une part importante des 106 critères repose sur ce jugement, qui reste humain. Les tests automatisés servent à défricher et à empêcher les régressions, pas à établir une conformité.
Où placer les tests d'accessibilité dans un projet JavaScript ?
Au plus près du code, dans les tests unitaires de composants. Un défaut détecté sur un composant du design system est corrigé une fois et disparaît partout où ce composant est utilisé. Les tests de bout en bout viennent en complément pour valider les parcours assemblés, mais ils détectent plus tard et coûtent plus cher à maintenir.
Que faire quand un test d'accessibilité échoue ?
Traiter le résultat comme une anomalie à instruire, pas comme une non-conformité prouvée. Certains signalements sont des faux positifs liés au contexte, d'autres révèlent un défaut réel. La vérification manuelle du critère concerné tranche, et c'est elle qui doit décider si le test doit être corrigé ou si c'est le code.
Guides RGAA associés
Pour aller plus loin sur les sujets abordés dans cet article, consultez nos fiches techniques :
Chaque image porteuse d'information doit avoir une alternative textuelle pertinente via l'attribut alt. Les images décoratives doivent avoir un attribut alt vide.
Chaque champ de formulaire doit avoir une étiquette (label) qui lui est liée explicitement.
Le contraste entre la couleur du texte et la couleur de son arrière-plan doit être suffisamment élevé (4.5:1 pour le texte normal, 3:1 pour le grand texte).
Vidéos RGAA sur ce thème
Pour voir ces sujets en action, regardez les épisodes vidéo correspondants :
Articles similaires
Votre site est-il conforme ?
Ne prenez pas de risques avec l'accessibilité. Lancez un audit complet de votre site en quelques minutes et obtenez un rapport détaillé des corrections à apporter.