Verwendung mit Selenium
Selenium WebDriver ist der langjährige Standard, um echte Browser aus jeder Sprache heraus zu steuern — das etablierteste browserübergreifende Automatisierungswerkzeug, häufig für End-to-End-Tests eingesetzt. surea11y wird über das Binding @surea11y/selenium eingebunden.
$ npm install @surea11y/core @surea11y/selenium selenium-webdriver
selenium-webdriver enthält den Selenium Manager, der beim ersten Erstellen eines Treibers den passenden chromedriver automatisch herunterlädt und verwaltet — kein separater Selenium-Server, keine manuelle Treiberinstallation (Chrome selbst muss bereits auf dem Rechner installiert sein).
const { Builder, Browser } = require('selenium-webdriver');const chrome = require('selenium-webdriver/chrome');const { A11yCoreBuilder } = require('@surea11y/selenium');const options = new chrome.Options().addArguments('--headless=new');const driver = await new Builder().forBrowser(Browser.CHROME).setChromeOptions(options).build();await driver.get('https://example.com/');const results = await new A11yCoreBuilder({ driver }).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 driver.quit();
.withWcag(version, level) wählt nach Konformitätsziel aus: die Regeln für die Erfolgskriterien der Stufen A und AA von WCAG 2.2, nach der Stufe, die jedes Kriterium in dieser Version hat. .withBestPractices() fügt die Regeln hinzu, die kein WCAG-Erfolgskriterium prüfen, etwa region; allein ausgeführt, laufen nur diese.
.withPacks() registriert vor dem Scan ein Pack in der Seite, und .options({ profile }) wählt sein Profil. Ein Pack, das nicht läuft, wird als Scan-Lücke 'pack-skipped' gemeldet, sodass ein Test, der getScanGaps(results) prüft, fehlschlägt, statt ohne das Pack zu bestehen.
Der Builder nimmt { driver } (einen Selenium-WebDriver) entgegen, wo Puppeteer/Playwright { page } erwarten — das ist der einzige Unterschied bei der Erstellung. Alles Weitere ist identisch.
Web Components. Eine Komponente, die ihre ElementInternals in einem privaten Feld hält und sich darüber einen Namen, ihre Zustände oder eine Rolle gibt, lässt sich von keinem Skript der Seite auslesen. Standardmäßig stellt der Scan zu einer solchen Komponente eine Frage (cantTell), statt sie zu beurteilen, und eine Rolle, die nur so gesetzt ist, wird gar nicht geprüft. Wenn Sie eine Komponentenbibliothek bauen oder testen, installieren Sie den optionalen Internals-Hook des Bindings, bevor die eigenen Skripte der Seite laufen, und diese Komponenten werden wie jedes andere Bedienelement beurteilt:
const options = new chrome.Options().enableBidi(); // the webSocketUrl: true capabilityconst driver = await new Builder().forBrowser(Browser.CHROME).setChromeOptions(options).build();await A11yCoreBuilder.installInternalsHook(driver); // 'bidi' (or 'cdp' without BiDi on Chrome)await driver.get('https://example.com/');
Rufen Sie ihn vor driver.get() auf. Klassisches WebDriver kann kein Skript vor denen der Seite ausführen, deshalb nutzt der Hook WebDriver BiDi, wenn die Session es hat (Chrome, Edge und Firefox, mit BiDi beim Bauen des Treibers aktiviert, wie oben), und sonst das DevTools-Protokoll in Chrome und Edge; er wird zu 'bidi' oder 'cdp' aufgelöst. In einer klassischen Session in Safari, oder in Firefox ohne BiDi, wird der Aufruf abgelehnt.
Der Hook ist für Test- und Audit-Läufe gedacht: Er legt das, was er aufzeichnet, dort ab, wo jedes Skript der Seite es lesen kann, deshalb ist er optional. Ohne ihn meldet der Scan einmal, wenn er zu solchen Komponenten Fragen gestellt hat. Der Abschnitt der README zum Internals-Hook enthält die Details.
Klick-Handler und Live-Regionen. Seit core 1.14 gibt es zwei weitere optionale Hooks. Die meisten Seiten fügen ihre Klick-Handler per Skript hinzu, wo kein Skript der Seite sie danach auflisten kann. Der Scan sieht daher nicht, dass ein <div role="button"> mit einem Klick-Listener per Tastatur nichts tut; mit dem Listener-Hook ergibt ein solcher Button fail (CLICK_HANDLER_NO_KEY_LISTENER). Der Live-Region-Hook zeichnet auf, was die Live-Regionen der Seite erhalten: Der Scan stellt dann eine Frage zu einer Statusmeldung, die schon mit ihrem Text eingefügt wurde und die Screenreader oft nicht ansagen, und announcements() gibt zurück, was die Seite zur Ansage bereitgestellt hat, damit ein Test prüfen kann, ob eine Aktion den Nutzenden etwas mitgeteilt hat. Installieren Sie sie, bevor die eigenen Skripte der Seite laufen, wie den Internals-Hook:
await A11yCoreBuilder.installListenerHook(driver); // before driver.get()await A11yCoreBuilder.installLiveRegionHook(driver);await driver.get('https://example.com/settings');await driver.findElement(By.css('#save')).click();const said = await A11yCoreBuilder.announcements(driver);// [{ text: 'Saved', politeness: 'polite', ..., selector: '#status' }]
Beide werden wie der Internals-Hook vor driver.get() installiert: über WebDriver BiDi, wenn die Session es hat, sonst über das DevTools-Protokoll in Chrome und Edge. Jeder liefert 'bidi' oder 'cdp'. By stammt aus selenium-webdriver.
Wie der Internals-Hook sind beide für Test- und Audit-Läufe gedacht: Jedes Skript der Seite kann lesen, was sie aufzeichnen. announcements() ist das, was die Seite zur Ansage bereitgestellt hat, nicht das, was ein Screenreader gesagt hat, und ohne den Live-Region-Hook null. Fehlt einer der beiden Hooks, meldet getScanNotes(), wenn Befunde, die er entschieden hätte, ohne ihn beurteilt wurden. Die Abschnitte der README zum Listener-Hook und zum Live-Region-Hook enthalten die Details.
Dunkler Modus, Smartphone-Breiten und eine Schwelle nach Schweregrad. .conditions([{ colorScheme: 'dark' }, { width: 320 }]) scannt die Seite so, wie sie ist, und dann erneut unter jeder Bedingung, und gibt ein einziges Ergebnis zurück: Das Ergebnis einer Regel ist ihr schlechtestes über alle Scans, und jeder Befund nennt die Bedingungen, unter denen er auftrat. Jede Bedingung ist ein vollständiger Scan, und .frames(true) lässt sich noch nicht damit kombinieren. In Chrome und Edge laufen beide über das DevTools-Protokoll; in anderen Browsern ändert eine Breite die Fenstergröße, und bei einem Farbschema lehnt .analyze() vor jedem Scan ab. getGatingFailures(results, { failOnSeverity: 'serious' }) gibt die fehlschlagenden Regeln ab diesem Schweregrad zurück, sodass ein Test zuerst bei schwerwiegenden und kritischen Problemen blockiert, während der Rest weiter gemeldet wird; formatFailures() nimmt dieselbe Option.
Fragen mit Tendenz und Prüfschritten. Seit core 1.14 kann ein cantTell-Befund zu fail oder pass tendieren, mit Begründung, und er enthält howToCheck: die Schritte, mit denen eine Person ihn beantwortet. formatFailures() listet die wahrscheinlich nicht erfüllten Fragen direkt nach den Fehlschlägen und gibt mit { howToCheck: true } die Schritte aus. Soll ein Test auch bei wahrscheinlichen Fehlschlägen scheitern, nehmen Sie leaningFailures(results) aus core in die Schwelle auf.
A11yCoreBuilder hat außerdem .wcagLevel(), das die gemeldeten Kriterien auf eine WCAG-Stufe begrenzt, ohne zu ändern, welche Regeln laufen; .includeMode('or'), das die Regeln eines Tags plus die Regeln ausführt, die Sie nennen; .plannedChecks() und .plannedCriteria(), die auflisten, was ein Scan prüfen wird, bevor er läuft, damit eine Auswahl, die nichts ausführt, nicht als Scan ohne Befunde durchgeht; .logger(), das Warnungen zu Scan-Lücken an Ihr eigenes Log schickt; .fetchStyleSheets(false), das .analyze() davon abhält, Stylesheets anderer Origins, die die Seite selbst nicht lesen kann, für die Regeln abzurufen, die das CSS lesen; und getScanNotes(results), das angibt, wie die Seite gescannt wurde. Die README des Bindings beschreibt jede davon.
Siehe auch ein lauffähiges Beispiel im Repository des Pakets.
Als Nächstes: festlegen, welche Regeln laufen, den Scan in die CI einbinden und das vollständige Ergebnisschema ansehen.