Playwrightで使う

Playwrightは、Microsoftが開発したクロスブラウザーのE2Eテストフレームワークで、Node.jsから実際のChromium、Firefox、WebKitのエンジンを操作します。surea11yは@surea11y/playwrightバインディングで連携します。

$ npm install @surea11y/core @surea11y/playwright playwright
$ npx playwright install chromium

2つ目のコマンドが重要です。どのテストも実際のブラウザーを起動しますが、npm installだけではブラウザーは取得されません。

const { chromium } = require('playwright');
const { A11yCoreBuilder } = require('@surea11y/playwright');
 
const browser = await chromium.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)は適合性の目標で選択します。WCAG 2.2のレベルAとAAの達成基準のルールを、各達成基準がそのバージョンで持つレベルに従って実行します。.withBestPractices()は、WCAGの達成基準を検査しないregionなどのルールを追加します。単独で使うと、それらのルールだけを実行します。

.withPacks()はスキャンの前にページへパックを登録し、.options({ profile })でそのプロファイルを選びます。実行されなかったパックは'pack-skipped'のスキャンギャップとして報告されるため、getScanGaps(results)を検証するテストは、パックなしで合格せずに失敗します。

Chromiumだけでなく、Playwrightの3つのエンジンすべてで動作します。実際のFirefoxとWebKitでの実行で検証済みです。

Webコンポーネント。ElementInternalsをプライベートフィールドに保持し、それを通じて名前や状態、ロールを自分に設定するコンポーネントは、ページのどのスクリプトからも読み取れません。そのため既定では、スキャンはそのようなコンポーネントを判定せずに確認を求め(cantTell)、その方法だけで設定されたロールはまったくチェックされません。コンポーネントライブラリーを作ったりテストしたりしているなら、ページ自身のスクリプトが実行される前に、バインディングのオプトインのinternalsフックをインストールしてください。そうすれば、それらのコンポーネントもほかのコントロールと同じように判定されます。

const context = await browser.newContext();
await A11yCoreBuilder.installInternalsHook(context); // before any page navigates
const page = await context.newPage();
await page.goto('https://example.com/');

page.goto()の前に、ページかそのブラウザーコンテキストに対して呼び出してください。addInitScript()を通じて実行されるので、1回の呼び出しでページの、あるいはコンテキストが開くすべてのページの、すべてのフレームとナビゲーションがカバーされます。Chromium、Firefox、WebKitで動作します。@playwright/testでは、contextフィクスチャーに対するbeforeEachで呼び出します。

このフックはテストや監査の実行のためのものです。記録した内容をページのどのスクリプトからも読める場所に保持するため、オプトインになっています。フックがない場合、スキャンはそのようなコンポーネントについて確認を求めたときに、一度だけそのことを知らせます。詳しくはREADMEのinternalsフックの節を参照してください。

クリックハンドラーとライブリージョン。core 1.14から、オプトインのフックがさらに2つあります。多くのページはクリックハンドラーをスクリプトから追加しますが、後からそれを一覧にできるページのスクリプトはありません。そのため、スキャンはクリックリスナーを持つ<div role="button">がキーボードでは何もしないことを見抜けません。リスナーフックがあれば、そのようなボタンは失敗します(CLICK_HANDLER_NO_KEY_LISTENER)。ライブリージョンフックは、ページのライブリージョンに何が与えられたかを記録します。スキャンはテキストが入った状態で追加された、スクリーンリーダーが読み上げないことの多いステータスメッセージについて確認を求め、announcements()はページが読み上げ用に提供した内容を返します。これで、操作の結果がユーザーに伝えられたかをテストで確かめられます。internalsフックと同じように、ページ自身のスクリプトが実行される前にインストールしてください。

const context = await browser.newContext();
await A11yCoreBuilder.installListenerHook(context); // before any page navigates
await A11yCoreBuilder.installLiveRegionHook(context);
const page = await context.newPage();
await page.goto('https://example.com/settings');
await page.getByRole('button', { name: 'Save' }).click();
 
const said = await A11yCoreBuilder.announcements(page);
// [{ text: 'Saved', politeness: 'polite', ..., selector: '#status' }]

どちらもinternalsフックと同じように、ページまたはそのブラウザーコンテキストに、ナビゲーションの前にインストールします。@playwright/testでは、contextフィクスチャーのbeforeEachでインストールしてください。

internalsフックと同じく、どちらもテストや監査の実行のためのものです。記録した内容はページのどのスクリプトからも読めます。announcements()はページが読み上げ用に提供した内容で、スクリーンリーダーが実際に読み上げた内容ではありません。ライブリージョンフックがなければnullです。どちらかのフックがない場合、そのフックなら判断できた検出結果がフックなしで判定されたときにgetScanNotes()がそのことを示します。詳しくはREADMEのリスナーフックとライブリージョンフックの節を参照してください。

ダークモード、スマートフォンの幅、重大度によるゲート。.conditions([{ colorScheme: 'dark' }, { width: 320 }])は、ページをそのままの状態でスキャンしたあと、条件ごとにもう一度スキャンし、1つの結果を返します。ルールの判定結果はすべてのスキャンのうち最も悪いものになり、各検出結果にはそれが見つかった条件が示されます。条件ごとに完全なスキャンを行います。また、.frames(true)とはまだ組み合わせられません。Chromium、Firefox、WebKitで動作します。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に組み込む、結果のスキーマ全体を確認する。