WebdriverIOで使う
WebdriverIOは、WebDriverプロトコルの上に構築されたNode.jsのテスト自動化フレームワークで、E2Eテストとコンポーネントテストの両方に対応しています。surea11yは@surea11y/webdriverioバインディングで連携します。
$ npm install @surea11y/core @surea11y/webdriverio webdriverio
WebdriverIO v9は、初めてChromeを起動するときにローカルのchromedriverを自動で管理します。インストールしたばかりの状態で、そのまま実際のヘッドレスChromeを起動できます。
const { remote } = require('webdriverio');const { A11yCoreBuilder } = require('@surea11y/webdriverio');const browser = await remote({capabilities: {browserName: 'chrome','wdio:enforceWebDriverClassic': true, // recommended -- see below'goog:chromeOptions': { args: ['--headless=new'] }}});await browser.url('https://example.com/');const results = await new A11yCoreBuilder({ browser }).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.deleteSession();
.withWcag(version, level)は適合性の目標で選択します。WCAG 2.2のレベルAとAAの達成基準のルールを、各達成基準がそのバージョンで持つレベルに従って実行します。.withBestPractices()は、WCAGの達成基準を検査しないregionなどのルールを追加します。単独で使うと、それらのルールだけを実行します。
.withPacks()はスキャンの前にページへパックを登録し、.options({ profile })でそのプロファイルを選びます。実行されなかったパックは'pack-skipped'のスキャンギャップとして報告されるため、getScanGaps(results)を検証するテストは、パックなしで合格せずに失敗します。
この用途では、BiDiではなく従来のWebDriverを強制してください。WebdriverIO v9はデフォルトでBiDiプロトコルを使います。多数のbrowser.execute()呼び出しを発行する長時間のセッション(スキャンがまさにそれです)は、テストでBiDi上では大きく性能が落ち、1回のスキャンの所要時間が約2秒から数分にまで伸びました。上の例のように'wdio:enforceWebDriverClassic': trueで従来のWebDriverを強制すると、1回のスキャンあたり約150msで安定しました。
Webコンポーネント。ElementInternalsをプライベートフィールドに保持し、それを通じて名前や状態、ロールを自分に設定するコンポーネントは、ページのどのスクリプトからも読み取れません。そのため既定では、スキャンはそのようなコンポーネントを判定せずに確認を求め(cantTell)、その方法だけで設定されたロールはまったくチェックされません。コンポーネントライブラリーを作ったりテストしたりしているなら、ページ自身のスクリプトが実行される前に、バインディングのオプトインのinternalsフックをインストールしてください。そうすれば、それらのコンポーネントもほかのコントロールと同じように判定されます。
await A11yCoreBuilder.installInternalsHook(browser); // 'bidi' or 'cdp'await browser.url('https://example.com/');
browser.url()の前に呼び出してください。WDIOのテストランナーではbeforeフックで呼び出します。BiDiセッションではbrowser.addInitScript()を、そうでなければChromeとEdgeでchromedriver経由のDevToolsプロトコルを使います。上で推奨している従来のセッションもこれでカバーされます。FirefoxまたはSafariでの従来のセッションでは、呼び出しは拒否されます。
このフックはテストや監査の実行のためのものです。記録した内容をページのどのスクリプトからも読める場所に保持するため、オプトインになっています。フックがない場合、スキャンはそのようなコンポーネントについて確認を求めたときに、一度だけそのことを知らせます。詳しくはREADMEのinternalsフックの節を参照してください。
クリックハンドラーとライブリージョン。core 1.14から、オプトインのフックがさらに2つあります。多くのページはクリックハンドラーをスクリプトから追加しますが、後からそれを一覧にできるページのスクリプトはありません。そのため、スキャンはクリックリスナーを持つ<div role="button">がキーボードでは何もしないことを見抜けません。リスナーフックがあれば、そのようなボタンは失敗します(CLICK_HANDLER_NO_KEY_LISTENER)。ライブリージョンフックは、ページのライブリージョンに何が与えられたかを記録します。スキャンはテキストが入った状態で追加された、スクリーンリーダーが読み上げないことの多いステータスメッセージについて確認を求め、announcements()はページが読み上げ用に提供した内容を返します。これで、操作の結果がユーザーに伝えられたかをテストで確かめられます。internalsフックと同じように、ページ自身のスクリプトが実行される前にインストールしてください。
await A11yCoreBuilder.installListenerHook(browser); // before browser.url()await A11yCoreBuilder.installLiveRegionHook(browser);await browser.url('https://example.com/settings');await $('#save').click();const said = await A11yCoreBuilder.announcements(browser);// [{ text: 'Saved', politeness: 'polite', ..., selector: '#status' }]
どちらもinternalsフックと同じセッションで、browser.url()の前に(WDIOのテストランナーではbeforeフックで)インストールし、'bidi'または'cdp'を返します。
internalsフックと同じく、どちらもテストや監査の実行のためのものです。記録した内容はページのどのスクリプトからも読めます。announcements()はページが読み上げ用に提供した内容で、スクリーンリーダーが実際に読み上げた内容ではありません。ライブリージョンフックがなければnullです。どちらかのフックがない場合、そのフックなら判断できた検出結果がフックなしで判定されたときにgetScanNotes()がそのことを示します。詳しくはREADMEのリスナーフックとライブリージョンフックの節を参照してください。
ダークモード、スマートフォンの幅、重大度によるゲート。.conditions([{ colorScheme: 'dark' }, { width: 320 }])は、ページをそのままの状態でスキャンしたあと、条件ごとにもう一度スキャンし、1つの結果を返します。ルールの判定結果はすべてのスキャンのうち最も悪いものになり、各検出結果にはそれが見つかった条件が示されます。条件ごとに完全なスキャンを行います。また、.frames(true)とはまだ組み合わせられません。ChromeとEdgeでは、クラシックとBiDiのどちらのセッションでもDevToolsプロトコルで設定します。ほかのブラウザーでは幅はウィンドウのサイズを変え、カラースキームを指定すると.analyze()がスキャンの前に拒否されます。getGatingFailures(results, { failOnSeverity: 'serious' })は、その重大度以上で失敗したルールを返します。これにより、ほかの問題は報告し続けながら、まず重大な問題と致命的な問題でテストを止められます。formatFailures()も同じオプションを受け取ります。
傾きと確認手順のある問い。core 1.14から、cantTellの検出結果は理由とともにfailまたはpassのどちらかに傾くことがあり、人が答えるための手順であるhowToCheckを持ちます。formatFailures()は失敗の直後に不適合の可能性が高い問いを並べ、{ howToCheck: true }で手順も出力します。不適合の可能性が高いものでもテストを失敗させるには、coreのleaningFailures(results)をゲートに加えてください。
A11yCoreBuilderにはほかにも、実行するルールを変えずに報告する達成基準をWCAGのレベルまでに絞る.wcagLevel()、タグのルールに名前を挙げたルールを加えて実行する.includeMode('or')、スキャンが何をチェックするかを実行前に一覧にし、何も実行しない選択が問題のないスキャンに見えないようにする.plannedChecks()と.plannedCriteria()、スキャンギャップの警告を自分のログに送る.logger()、CSSを読むルールのために、ページ自身が読めない別オリジンのスタイルシートを.analyze()が取得しないようにする.fetchStyleSheets(false)、ページがどのようにスキャンされたかを示すgetScanNotes(results)があります。それぞれの説明はバインディングのREADMEにあります。
パッケージのリポジトリにある実行可能なサンプルも参照してください。
次のステップ:実行するルールを選ぶ、スキャンをCIに組み込む、結果のスキーマ全体を確認する。