概要
判断できないことを、判断できないと伝えるアクセシビリティテスト。
Playwright、Cypress、Puppeteer、Selenium、WebdriverIO、Jest/Vitest、CIパイプライン向けの決定論的なアクセシビリティテスト。
@surea11y/coreは、自分のテストスイートやCIパイプライン、コマンドラインから実行するアクセシビリティテストエンジンです。
ほかと違うのは、自動テストでは決着がつかないケースの扱い方です。W3CのオープンなAccessibility Conformance Testing (ACT) Rules Formatを実装しており、自分自身の基準だけで判断するのではなく、ACTが公開しているテストコーパスで検証しています。報告するのは、見つかった問題、問題がなかったこと、そして珍しいことに、明示的な「判断できない」です。そのため結果は、安心させるためのものではなく、検証できるものになっています。
Sureが意味するのは、わかっていることについての確かさと、わからないことについての誠実さです。わからないことは、わからないと伝えます。
アクセシビリティルール145件 · W3CのACTルール60件をW3Cのテストケースで検証 · 参照ケース957件 · 決定論的な結果 · 不確実性を明示
単体の製品ではありません
surea11yは、1つのコマンドでインストールする1つのツールではなく、1つのエンジンを共有するパッケージ群です。今のテストのやり方に合うものを、それが実行するエンジンである@surea11y/coreと一緒にインストールしてください。手元にあるDOMに対してエンジンを実行する場合や、その上に独自のツールを作る場合は、コアだけをインストールしてください。
| やりたいこと | インストールするもの |
|---|---|
| Playwrightのテストにアクセシビリティチェックを追加する | @surea11y/playwright + @surea11y/core |
| Puppeteerのテストに追加する | @surea11y/puppeteer + @surea11y/core |
| Seleniumのテストに追加する | @surea11y/selenium + @surea11y/core |
| Cypressのテストに追加する | @surea11y/cypress + @surea11y/core |
| WebdriverIOのテストに追加する | @surea11y/webdriverio + @surea11y/core |
| Jest/Vitestのコンポーネントテストやユニットテストでアクセシビリティをアサーションする | @surea11y/test-matchers + @surea11y/core |
| ターミナルやCIパイプラインから、静的なHTMLとして、またはChromiumでページをスキャンする | @surea11y/cli |
| 上記のいずれかで、欧州の規格EN 301 549に照らして確認する | @surea11y/pack-en301549 + @surea11y/core |
| 独自のルール、プロファイル、規格(公開・社内を問わず)に照らして確認する | 独自のパック:パックを書くを参照 |
| 手元にあるDOMに対してエンジンを実行する | @surea11y/core |
| エンジンの上に独自のツール(CLI、拡張機能、ダッシュボード、レポート生成ツールなど)を作る | @surea11y/core |
バインディングごとのインストール方法と使い方ははじめにを参照してください。ツールを作る場合は、GitHub上のBUILDING_ON_CORE.md(英語)が、安定した契約、スキャンの実行、チェックの選び方、そして結果が示す以上のことを主張しない提示の仕方を説明しています。
テストスイートなしでまず試すなら、スタンドアロンのバンドルをページに読み込み、ブラウザーのコンソールからスキャンできます。await a11ycore.run()がページをスキャンし、a11ycore.table(result)が検出結果を一覧にし、a11ycore.highlight(result)がページ上で枠を付けて示し、await a11ycore.openReport(result)がHTMLレポートを開きます。ページの見出し、ランドマーク、名前、フォーカス順序を見るa11ycore.inspect('headings')などのほかのヘルパーは、GitHub上のINTEGRATION.md(英語)にあります。
結果の違い
どのルールも、4つの判定結果からなる固定の語彙で、1つの決定論的な判定を行います。ほかの広く使われているアクセシビリティテストツールのviolations/passes/incomplete/inapplicableという形ではありません。
- fail(
fail):DOMから証明できる違反です。客観的で規範的なケースだけに使います。 - pass(
pass):このルール固有の条件を満たしています。ページがアクセシブルだという主張ではありません。 - can't tell(
cantTell):人間が判断する必要があります。何があいまいだったのかは結果に書かれています。 - n/a(
notApplicable):ルールの前提条件が存在しません。
cantTellこそがこのプロジェクトの要です。判断できないことを黙って捨てるエンジンは、レポートが短くなる代わりに、カバーできているという誤った安心感を生みます。結果の全体像は結果とレポートを、個々の結果がどのようにWCAGの判定に集約されるか(対象とするWCAGのバージョン2.0/2.1/2.2がどう決まり、どう適用されるかを含む)は適合性を参照してください。
cantTellは、問いを投げたままにはしません。コアのルールが報告するcantTellにはすべて、答え方が付いています。示された要素から始める数ステップの手順と、適合する場合・不適合の場合にそれぞれ何が見えるかが、スキャンの言語で書かれています。ページに手がかりがある場合は、判定結果がさらにpassとfailのどちらに傾くか(見込み)も示し、読み取った事実、その理由、見込みが外れうる条件を添えます。見込みはW3CのACTテストケースに照らして数えられ、5件以上で数えられると、どれだけ当たったかを示します。それまでは数値を一切持たず、推定値を示すこともありません。判定結果はcantTellのままです。見込みは問いに順番を付けるもので、答えるのは引き続き人です。詳しくは出力スキーマの確認方法と見込みを、どのような場合に見込みが付き、どこまで信頼できるかはGitHub上のLEANS.md(英語)を参照してください。
HTMLレポートは、確認する人が取り組む順に問いを並べます。「不適合の可能性が高い: 確認してください」「適合の可能性が高い: 確認してください」「人による確認が必要」の3つに分けます。各カードには「確認方法」の手順があり、最初の2つのグループのカードには、なぜそちらに傾くのかも示されます。CIでは、JUnitとSARIFのレポートで、不適合に傾く検出結果があればビルドを失敗させることもできます(初期設定では無効)。ベースラインでページに既にあるものを受け入れておけば、ビルドを止めるのは新しいものだけです。詳しくはHTMLレポートとベースライン/許可リストを参照してください。小さな決済ページをスキャンしたレポートの例も開けます。
passが際どかった場合は、そのことも示します。しきい値に対して測定するルール(コントラスト、ターゲットのサイズ、テキストの間隔)は、最も際どい合格が限界に近かった場合にmarginを報告します。しきい値を満たしつつ、最も限界に近づいた要素です。最低4.5:1に対して4.52:1のテキストは合格しますが、マージンがそれを示すため、わずかな変更でfailに変わる前に修正できます。詳しくは出力スキーマを参照してください。
規格を指定すれば、エンジンがルールを選びます。{ wcag: { version: '2.2', level: 'AA' } }は、WCAG 2.2のAとAAの達成基準のルールを、各達成基準がそのバージョンで持つレベルに従って実行し、bestPractices: trueはWCAGが求めていないチェックを追加します。国の規格や組織独自の方針など、WCAGを超える規格はパックとして提供されます。パックのルール、プロファイル、チェックリスト項目はコアのものと並んで実行され、すべてのレポートに表示されます。詳しくはエンジンオプションとパックを参照してください。
規格ではなくチェックを指定しても、結果にはそれが関わるWCAGの達成基準が示されます。link-name-presentだけを実行するスキャンでも2.4.4と4.1.2が報告され(レベルを指定しない場合はAAAの2.4.9も)、スキャンがほかのチェックを除外した達成基準はpassではなくcantTell(HTMLレポートでは「一部のみ確認」)になります。wcagLevel: 'AA'はスキャンが報告する達成基準のレベルを決め、JUnit、SARIF、EARLの各レポートもそれに従います。そのため、AAのビルドがAAAの達成基準で失敗することはありません。
一部しか確認しないルールの結果をもって、WCAGの達成基準を合格とすることはありません。達成基準が合格になるのは、そのルールが達成基準の求めることをすべて確認する場合だけで、そこまで確認できている達成基準はまだありません。そのため、ある達成基準のルールがすべて合格した場合、その達成基準はcantTellになります。ルールが確認する部分は満たされており、残りは人が確認します。不適合が1件もないレポートも、見出しの下で同じことを伝えます。失敗したルールはないが、それはページが適合しているという主張ではない、ということです。こうした達成基準を合格と表示すれば、このエンジンが避けようとしている、カバーできているという誤った安心感を生むことになります。詳しくはWCAG適合性との対応を参照してください。
Webコンポーネントは、公開している情報で判定されます。ElementInternalsで名前、状態、ロールを与えるカスタム要素は、エンジンがそれを読み取れる場合はその内容で確認され、読み取れない場合は人の確認に回されます。テストや監査の実行向けには、オプトインのスクリプトinternalsHookScript()があり、コンポーネントが非公開で保持しているinternalsをスキャンが読み取れるようにします。これにより、そうした要素もcantTellではなく合格か不合格かで判定されます。ページ自身のスクリプトより前にインストールする方法は、GitHub上のINTEGRATION.mdを参照してください。
これまでどのルールも扱っていなかった3つの達成基準にも、ルールが届くようになりました。3.3.1「エラーの特定」(無効とマークされた入力欄で、エラーメッセージがない、隠れている、または関連付けられていないもの)、4.1.3「ステータスメッセージ」(支援技術が届かないライブリージョン)、1.4.5「文字画像」(画像として描かれているが、実際のテキストにできそうな文字)です。3つとも不適合とはせず、人の確認に回します。キーボードのルールはマークアップからイベントハンドラーを読み取ります。ページ自身のスクリプトより前にインストールするオプトインのスクリプトlistenerHookScript()を使えば、スクリプトから追加されたリスナーも判定します。タブ順序に含まれ、クリックのリスナーがあり、キー操作が届きうるどこにも(要素自身、その内部、祖先、要素が含まれるシャドウルート、ドキュメント、ウィンドウ)キーのリスナーがなく、キーのリスナーを追加しうるフォーカスのリスナーもないrole="button"は不適合になります。EnterキーとSpaceキーでは何も起きないためです。インストール方法と、ステータスメッセージ向けの対になるライブリージョンのフックについては、GitHub上のINTEGRATION.mdを参照してください。
ブラウザーでは、コントラストをページが描画されたとおりに測定します。対象は、独自の背景を持たないページのテキスト(ブラウザーが描くキャンバスの上で、明るい場合も暗い場合も)、::before、::after、リストマーカーによる生成テキスト、閉じた状態の<select>です。これらは以前はスキップされるか、人の確認に回されていました。輪郭線や影だけで描かれたテキストは、描かれる色がすべて比率を下回る場合に不合格になるようになりました。一方、コントロールの名前の代わりに置かれた1文字(「閉じる」という名前のボタンの「X」など)は、失敗とせず人の確認に回します。アイコンなのか文字なのかは、人が判断する必要があるためです。
ACTコーパスによる検証
エンジンには145件のルールがあります。そのうちW3C ACT Rulesに対応するルールがあるものは、W3Cがそれらのルールについて公開しているテストケース、つまりW3Cの実装一覧が評価に使うもの(60件のACTルールにわたる957件の例)に対して実行しています。ACTがpassedまたはinapplicableとしている例をエンジンがfailにしたものは1件もなく、このコーパスに対して誤検出はありません。これはこれらの例についての結果であり、あらゆるページについての保証ではありません。エンジンが判断できないケースではcantTellを返します。これはACTが自動テストの実装に認めているものです。
この結果は、エンジンが動く2つの環境の両方で成り立ちます。test-matchersとCLIが初期設定で使うjsdomと、ブラウザーのバインディングが使うChromiumです。Chromiumでは、各テストケースをスクリプトやリンクされたスタイルシートも含めて実際のページとして読み込みます。ACTがfailedとしている例のうち、jsdomでは22件、Chromiumでは18件を検出していません。その多くは、見出しがその下の内容を説明しているかどうかといった、判断を要するものです。ブラウザーで検出できる4件は、jsdomが実行しないページ自身のスクリプトやスタイルシートを必要とします。1件ずつの理由はACT_RULE_MAPPING.mdにまとめています。
2つのEARL実装レポートには、passやinapplicableの結果も含め、これらのケースすべての判定結果を記録しています。そのため、適用対象がなくて何も報告しなかったルールと、そもそも実装されていないルールを区別できます。jsdomで実行したSureA11yと、Chromiumで実行したSureA11y in a browserです。形式についてはEARLレポートを参照してください。
基本方針
- 決定論的な実行。同じ条件で描画された同じページを同じオプションでスキャンすれば、同じ判定になります。ただし、結果に含まれる所要時間、タイムスタンプ、環境の詳細は実行ごとに異なることがあります。実行中のアニメーションやトランジションはスキャンの間止められるため、進行中のフェードがルールの計測を変えることはありません。
- 意図して保守的に。コアのルールは、誤った警告を少なく抑えるため、
failを客観的で規範的な違反だけに使います。 - 標準へのトレーサビリティ。ルールは、適切な場合には、該当するWCAGの達成基準に紐付けています。
- 安定した機械可読の出力。ルールIDは言語によって変わりません。
- フレームワークに依存しない。jsdomでも、どのブラウザー自動化フレームワークの中でも実行できます。
- 拡張可能。カスタムルールの追加、ポリシーの登録、ルールID・タグ・WCAGバージョンによるスキャンの絞り込みができます。
- ローカライズされたレポート。人が読むメッセージは、機械可読のデータに影響を与えずに翻訳できます。現在は
en、fr、de、es、jaに対応しています。
考え方
客観的に判断できることは自動化する。できないことを自動化したふりは決してしない。
WCAGの要件には、完全な確信を持ってチェックできるものもあれば、人間の判断や文脈の知識、ユーザビリティの評価が必要なものもあります。1つのスコアやpass/failの判定は、その違いを押しつぶしてしまいます。surea11yはその違いを報告します。cantTellとnotApplicableが判定結果として存在するのはそのためで、この考え方がエンジンのすべてのルールを形づくっています。
surea11yが検出しないもの
自動化の限界をはっきりさせることも、同じ考え方の一部です。たとえばsurea11yは、次のことはしません。
- 代替テキストが意味のあるものかどうかは確認せず、存在するかどうかだけを確認します(
"image123.png"というalt属性も、客観的なチェックには合格します。ただし、ファイル名として、不適合に傾く見込みを付けて人の確認に回します)。 - 色のコントラストの選択が美的に適切かどうかは判断せず、該当するコントラスト比を満たしているかどうかだけを判断します。
- エラーメッセージが問題を実際に説明しているかどうかは判断しません。それは静的なスキャンからは見えないバリデーションのロジックに左右されるからです。
- キーボードのフォーカストラップや、400%に拡大したときに切れてしまうコンテンツは検出しません。どちらも、ある一瞬のDOMを読むだけでなく、実際のユーザー操作を時間をかけてシミュレートする必要があるからです。
実装済みのチェックの一部は、人が判断しなければならない場合にcantTellを報告します。キーボードトラップや拡大時のリフローなど、ほかの側面はエンジンがまったくチェックしないため、別途テストが必要です。それらの代わりになるcantTellはないので、cantTellのない結果は、それらが確認済みであることを意味しません。全一覧は既知の制限事項を参照してください。
プロジェクトについて
surea11yは、開発の過程もすべて公開している、独立したオープンソースのプロジェクトです。
@surea11y/coreとすべての連携パッケージはnpmで公開しています。issue、議論、変更はすべてGitHub上で公開の場で行っており、コントリビューションを歓迎します。コントリビューションのガイドラインはCONTRIBUTING.mdを参照してください。