Utilisation avec Puppeteer
Puppeteer est une bibliothèque Node.js qui pilote Chrome/Chromium, en mode headless ou complet, via le protocole DevTools : de l’automatisation et du scripting de navigateur, souvent utilisés pour les tests, mais pas un framework de test en soi. surea11y s’y intègre via le binding @surea11y/puppeteer.
$ npm install @surea11y/core @surea11y/puppeteer puppeteer
Puppeteer télécharge sa propre version de Chrome lors de npm install : contrairement à Playwright, il n’y a pas d’étape distincte d’installation du navigateur.
const puppeteer = require('puppeteer');const { A11yCoreBuilder } = require('@surea11y/puppeteer');const browser = await puppeteer.launch();const page = await browser.newPage();await page.goto('https://example.com/');const results = await new A11yCoreBuilder({ page }).include('#main') // optional -- call multiple times for multi-region scans.exclude('.cookie-banner') // optional.withWcag('2.2', 'AA') // the rules for WCAG 2.2's A and AA criteria.disableRules(['meta-refresh-no-exceptions']).analyze();console.log(results.checksResults.filter(r => r.outcome === 'fail'));await browser.close();
.withWcag(version, level) sélectionne par cible de conformité : les règles des critères de niveau A et AA de WCAG 2.2, selon le niveau de chaque critère dans cette version. .withBestPractices() ajoute les règles qui ne vérifient aucun critère WCAG, comme region ; seule, elle n’exécute que celles-ci.
.withPacks() enregistre un pack dans la page avant l’analyse, et .options({ profile }) choisit son profil. Un pack qui ne s’exécute pas est signalé comme une lacune d’analyse 'pack-skipped', si bien qu’un test qui vérifie getScanGaps(results) échoue au lieu de passer sans lui.
Composants web. Un composant qui garde ses ElementInternals dans un champ privé, et se donne un nom, ses états ou un rôle par ce biais, ne peut être relu par aucun script de la page. Par défaut, l’analyse pose donc une question sur ce composant (cantTell) au lieu de le juger, et un rôle donné uniquement ainsi n’est pas vérifié du tout. Si vous construisez ou testez une bibliothèque de composants, installez le hook d’internals facultatif du binding avant que les scripts de la page elle-même ne s’exécutent, et ces composants sont jugés comme n’importe quel autre contrôle :
const page = await browser.newPage();await A11yCoreBuilder.installInternalsHook(page); // before gotoawait page.goto('https://example.com/');
Appelez-le avant page.goto(). Il s’exécute via page.evaluateOnNewDocument() : un seul appel couvre les frames et les navigations de la page pendant toute sa durée de vie, dans Chrome et Firefox ; un nouvel onglet que la page ouvre elle-même ne le reçoit pas.
Le hook est destiné aux exécutions de test et d’audit : il garde ce qu’il enregistre là où n’importe quel script de la page peut le lire, c’est pourquoi il est facultatif. Sans lui, l’analyse le signale une fois lorsqu’elle a posé une question sur de tels composants. La section du README sur le hook d’internals donne les détails.
Gestionnaires de clic et régions live. Depuis core 1.14, il existe deux autres hooks facultatifs. La plupart des pages ajoutent leurs gestionnaires de clic depuis un script, où aucun script de la page ne peut les lister ensuite, si bien que l’analyse ne voit pas qu’un <div role="button"> doté d’un écouteur de clic ne fait rien au clavier ; avec le hook d’écouteurs, ce bouton échoue (CLICK_HANDLER_NO_KEY_LISTENER). Le hook de régions live enregistre ce que reçoivent les régions live de la page : l’analyse pose alors une question sur un message d’état ajouté avec son texte déjà dedans, que les lecteurs d’écran n’annoncent souvent pas, et announcements() renvoie ce que la page a mis à disposition pour être annoncé, afin qu’un test vérifie qu’une action a bien informé l’utilisateur. Installez-les avant que les scripts de la page elle-même ne s’exécutent, comme le hook d’internals :
const page = await browser.newPage();await A11yCoreBuilder.installListenerHook(page); // before gotoawait A11yCoreBuilder.installLiveRegionHook(page);await page.goto('https://example.com/settings');await page.click('#save');const said = await A11yCoreBuilder.announcements(page);// [{ text: 'Saved', politeness: 'polite', ..., selector: '#status' }]
Tous deux s’installent comme le hook d’internals, via page.evaluateOnNewDocument(), avant page.goto().
Comme le hook d’internals, tous deux sont destinés aux exécutions de test et d’audit : n’importe quel script de la page peut lire ce qu’ils enregistrent. announcements() est ce que la page a mis à disposition pour être annoncé, pas ce qu’un lecteur d’écran a dit, et vaut null sans le hook de régions live. Sans l’un ou l’autre hook, getScanNotes() signale quand des constats que ce hook aurait tranchés ont été jugés sans lui. Les sections du README sur le hook d’écouteurs et le hook de régions live donnent les détails.
Mode sombre, largeurs de téléphone et seuil de gravité. .conditions([{ colorScheme: 'dark' }, { width: 320 }]) analyse la page telle qu’elle est, puis à nouveau sous chaque condition, et renvoie un seul résultat : le résultat d’une règle est le pire de toutes les analyses, et chaque constat indique les conditions sous lesquelles il a été vu. Chaque condition est une analyse complète, et .frames(true) ne peut pas encore s’y combiner. Dans Firefox, les largeurs fonctionnent, mais un schéma de couleurs exige le protocole DevTools, que Puppeteer n’a que dans Chrome : .analyze() y est donc rejeté avant toute analyse. getGatingFailures(results, { failOnSeverity: 'serious' }) renvoie les règles en échec à cette gravité ou au-dessus, pour qu’un test bloque d’abord les problèmes graves et critiques pendant que les autres restent signalés ; formatFailures() accepte la même option.
Des questions qui penchent, avec des étapes. Depuis core 1.14, un constat cantTell peut pencher vers fail ou pass, en disant pourquoi, et porte howToCheck, les étapes qu’une personne suit pour y répondre. formatFailures() liste les échecs probables juste après les échecs, et affiche les étapes avec { howToCheck: true }. Pour qu’un test échoue aussi sur les échecs probables, ajoutez leaningFailures(results) de core au seuil.
A11yCoreBuilder a aussi .wcagLevel(), qui limite les critères rapportés à un niveau WCAG sans changer les règles exécutées ; .includeMode('or'), qui exécute les règles d’une étiquette plus les règles que vous nommez ; .plannedChecks() et .plannedCriteria(), qui listent ce qu’une analyse vérifiera avant qu’elle ne s’exécute, pour qu’une sélection qui n’exécute rien ne passe pas pour une analyse sans échec ; .logger(), qui envoie les avertissements de lacunes d’analyse à votre propre journal ; .fetchStyleSheets(false), qui empêche .analyze() de récupérer les feuilles de style d’autres origines que la page ne peut pas lire elle-même, pour les règles qui lisent le CSS ; et getScanNotes(results), qui indique comment la page a été analysée. Le README du binding les décrit chacun.
Voir aussi un exemple exécutable dans le dépôt du paquet.
Ensuite : choisir les règles à exécuter, intégrer l’analyse à la CI et consulter le schéma complet du résultat.