130 lines
4.8 KiB
Markdown
130 lines
4.8 KiB
Markdown
---
|
|
name: e2e-a11y-testing
|
|
description: >-
|
|
Automate end-to-end accessibility (WCAG 2.2 AA) and visual regression testing using Playwright
|
|
and @axe-core/playwright. Enforces automated gates for keyboard focus trapping, ARIA roles,
|
|
and cross-viewport screenshot diffing.
|
|
---
|
|
|
|
# E2E Accessibility & Visual Regression Testing
|
|
|
|
Ce skill standardise les tests de bout en bout (E2E) pour garantir qu'aucune régression visuelle ou d'accessibilité (WCAG 2.2 AA) ne soit livrée en production. Il couple **Playwright** et le moteur d'analyse **Axe-core**.
|
|
|
|
---
|
|
|
|
## 🎯 Déclencheurs d'Activation
|
|
|
|
Activez ce skill dès que :
|
|
1. Vous créez ou modifiez des composants interactifs critiques (modales, menus déroulants, formulaires d'inscription, flux d'achat).
|
|
2. Vous devez vérifier qu'une modale capture bien le focus clavier (*focus trap*) et se ferme avec la touche `Échap`.
|
|
3. Vous mettez en place des tests d'accessibilité automatisés dans un pipeline CI/CD (GitHub Actions).
|
|
4. Vous validez le comportement responsive sur les 3 résolutions de référence (Mobile 375px, Tablette 768px, Desktop 1440px).
|
|
|
|
---
|
|
|
|
## 🧪 1. Test d'Accessibilité Automatisé avec `@axe-core/playwright`
|
|
|
|
Ce test injecte Axe-core directement dans la page rendue par Playwright pour détecter 100% des violations automatisables (contrastes, attributs ARIA orphelins, étiquettes de formulaires manquantes) :
|
|
|
|
```typescript
|
|
// tests/a11y.spec.ts
|
|
import { test, expect } from '@playwright/test';
|
|
import AxeBuilder from '@axe-core/playwright';
|
|
|
|
test.describe('Audit d\'accessibilité globale WCAG 2.2 AA', () => {
|
|
test('La page d\'accueil ne doit présenter aucune violation critique ou sérieuse', async ({ page }) => {
|
|
await page.goto('/');
|
|
|
|
// Attendre que la page et les polices soient stabilisées
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
const accessibilityScanResults = await new AxeBuilder({ page })
|
|
.withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa', 'wcag22aa'])
|
|
.analyze();
|
|
|
|
expect(accessibilityScanResults.violations).toEqual([]);
|
|
});
|
|
});
|
|
```
|
|
|
|
---
|
|
|
|
## ⌨️ 2. Test du Piège de Focus Clavier (*Focus Trap*) dans une Modale
|
|
|
|
Ce test vérifie que le focus reste enfermé dans la boîte de dialogue et revient sur le bouton d'origine à la fermeture :
|
|
|
|
```typescript
|
|
// tests/modal-keyboard.spec.ts
|
|
import { test, expect } from '@playwright/test';
|
|
|
|
test('La modale respecte le flux clavier standard', async ({ page }) => {
|
|
await page.goto('/dashboard');
|
|
|
|
const triggerButton = page.getByRole('button', { name: 'Nouvelle tâche' });
|
|
await triggerButton.click();
|
|
|
|
const dialog = page.getByRole('dialog');
|
|
await expect(dialog).toBeVisible();
|
|
|
|
// Le premier champ interactif de la modale doit avoir le focus
|
|
const firstInput = dialog.getByRole('textbox', { name: 'Titre' });
|
|
await expect(firstInput).toBeFocused();
|
|
|
|
// Navigation avec la touche Tab
|
|
await page.keyboard.press('Tab');
|
|
const closeButton = dialog.getByRole('button', { name: 'Fermer' });
|
|
await expect(closeButton).toBeFocused();
|
|
|
|
// Boucle : Tab depuis le dernier élément doit revenir au premier
|
|
await page.keyboard.press('Tab');
|
|
await expect(firstInput).toBeFocused();
|
|
|
|
// Fermeture par la touche Échap (Escape)
|
|
await page.keyboard.press('Escape');
|
|
await expect(dialog).toBeHidden();
|
|
|
|
// Le focus doit être restitué au bouton déclencheur initial
|
|
await expect(triggerButton).toBeFocused();
|
|
});
|
|
```
|
|
|
|
---
|
|
|
|
## 📸 3. Test de Régression Visuelle Multi-Viewport
|
|
|
|
Pour garantir que les ajustements CSS ne cassent pas le rendu visuel :
|
|
|
|
```typescript
|
|
// tests/visual-regression.spec.ts
|
|
import { test, expect } from '@playwright/test';
|
|
|
|
const viewports = [
|
|
{ name: 'mobile', width: 375, height: 812 },
|
|
{ name: 'tablet', width: 768, height: 1024 },
|
|
{ name: 'desktop', width: 1440, height: 900 },
|
|
];
|
|
|
|
for (const vp of viewports) {
|
|
test(`Capture de référence responsive - ${vp.name}`, async ({ page }) => {
|
|
await page.setViewportSize({ width: vp.width, height: vp.height });
|
|
await page.goto('/');
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
await expect(page).toHaveScreenshot(`homepage-${vp.name}.png`, {
|
|
maxDiffPixelRatio: 0.02, // Seuil de tolérance anti-bruit de 2%
|
|
animations: 'disabled', // Neutralise les animations pour éviter les faux positifs
|
|
});
|
|
});
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 📋 Checklist de Conformité Tests E2E & A11y
|
|
|
|
- [ ] La suite de tests `@axe-core/playwright` s'exécute sans violation sur toutes les pages publiques.
|
|
- [ ] Les flux au clavier (`Tab`, `Shift+Tab`, `Escape`, `Enter`) sont couverts par des assertions explicites.
|
|
- [ ] Les éléments cliquables possèdent un nom accessible vérifiable via `page.getByRole()`.
|
|
- [ ] Les captures de régression visuelle couvrent à la fois les thèmes clair et sombre.
|
|
- [ ] Les tests échouent immédiatement en CI si un contraste de couleur passe sous le seuil 4.5:1.
|