Web アクセシビリティの検証ツール「acot」

同僚の @wadackel とウェブアクセシビリティの検証ツール acot (アコット)を作っていて、まだまだ途中だけど、Webアクセシビリティ Advent Calendar 2020の18日目になってしまったのでご紹介。

acot - Accessibility Testing Framework https://github.com/acot-a11y/acot

すでにWeb アクセシビリティの検証ツール acot を作ってる - wadackel.meで詳しく紹介されているので、私の記事では補足的にacotを作るモチベーションや経緯、アイデア、どんなルールが書けるかなどを紹介する。

モチベーションとか経緯とか

以前に @wadackel と開発していたWINTICKETというサービスでは、ウェブアクセシビリティに関する開発ポリシーとコードレビュー、そしてeslint-plugin-jsx-a11yによって一定の品質が保たれていた。

ウェブアクセシビリティ - WINTICKET

@storybook/addon-a11y(axe)も途中から導入したが、あまり見られないような状態になっていたと思う。

静的なHTMLやDOMの解析で発見できる問題は主に文法上の間違いやマシンリーダビリティの問題で、WINTICKETではeslint-plugin-jsx-a11yをstrictで導入しエラーがあるとCIで落ちるようになっていたため、検証範囲が重複していたこともあり、axe で発見できるエラーはそれほど多くなかった。

よって、@storybook/addon-a11yで検知される問題は、Storybookの構成上どうしようもないケース(ラベルのない状態のinput="radio"など)やコントラスト比の問題がほとんどで、エラーがあっても解消しずらいモノが多く割れ窓となってしまい、エラーを検知するモチベーションが高まらなかったなと反省している。

アクセシビリティのテストのカバレッジを広げるにはaxeに限らず静的なHTMLやDOMの解析ではなく、よりユーザーに近いところでの解析をする必要があると考えていた。

初期のアイデア

初期のアイデアは意図しないセマンティクスの変更を検知できないかと検討していた。例えば、Webkitはlist-style: noneであるulolのロールを一致するARIAロールなしとする。理由は次のツイートから始まるスレッドを見てほしい。

他にもdisplay: blocktableのロールがtableではなくなる、などがある。現状のアクセシビリティのテストツールではアクセシビリティオブジェクトモデル(AOM)の検証ができないため、適切なマークアップをしていても意図したAOMが提供できない可能性がある。

現在、Google ChromeではAOMへのアクセス手段をいくつか提供しておりそれらを用いてテストができないかというのが初期のアイデアであった。初期のアイデアをコードにすると次の形のようになる。

const AXNode = await window.getComputedAccessibilityNode(el);
if (AXNode.role === 'button') {
  console.log("OK!")
} else {
  console.log("NG")
}
ComputedAccessibilityNode は Full Introspection of an Accessibility Tree - ComputedAccessibleNode を参考のこと。

こんな雑なアイデアを整ったテストフレームワークにしてくれた@wadackelには足を向けて寝られようか 🙏

ちなみにPuppeteerでは、getComputedAccessibleNodeを利用しなくてもAPIに accessibility.snapshot() が用意されている。ただしブラウザのアクセシビリティツリーをそのまま利用できず、一部がフィルタリングされるためテスト用途だとgetComputedAccessibleNodeを利用したほうが正確にテストができる。

どんなルールが実装できるか

初期はAOMを検証するというアイデアからスタートしたが、計算されたロールとHTMLが持つ暗黙のロールが異なっていたとしてもユーザーがそれを望んでいる場合もあって一概にエラーと検知することに大きな意味がないかもしれない、と考えた。

またPuppeteerを利用するとGoogle Chromeでしかテストできず各ブラウザの癖みたいなのがテストできないことに気がついた(当たり前)。よって当初考えていたAOMをテストするルールは実装していない。今後実装する可能性はある。

キーボード操作のE2Eテスト

それよりもPuppeteerを利用するのであれば、実際のDOMの操作も可能となる点のほうが大きかった。例えば、WCAG 2.1 2.4.3 フォーカス順序の試験では、モーダルダイアログを開く要素をクリックしフォーカスがモーダルダイアログ内に移動している、などをテストする。

このように特にキーボード操作の検証はE2E的な検証が必要になることが多く自動化のメリットが大きいと考え、現在ではAOMよりもE2E的なテストを増やす方向でルールも拡充していこうと考えている。またこれが既存のテストツールとの一番の差別化でもある。

WAI-ARIAをフックにする

E2E的なテストではこのモーダルダイアログを開く要素、というのを特定する必要がある。ただ通常のHTMLにはモーダルダイアログを開く、というセマンティクスは無い。

こういう場合はテスト用の属性を作成する方法があるが、閉じたプロジェクト内のE2Eテストであればオレオレ属性でも良いが、テストツールの推奨ルールとして実装するのであればなるべく標準的なセマンティクスを利用できたほうが良い。

そこでWAI-ARIAを利用する。例えばダイアログを開く、であれば aria-haspopup="dialog" というセマンティクスがあるし、もう少し抽象的にはaria-controlsaria-expandedで、制御する要素と動作が特定できるのでセマンティクスの通りに振る舞うかというテストができる。

WAI-ARIAはその名の通りリッチインターネットアプリケーションの語彙なので、UIのテストのためのセマンティクスとして利用できる。奇しくも同時期に Puppetariaがリリースされ、AOMをテストのセマンティクスとして利用しやすくなった。acotでも早速利用している。

acotでテストすると、WAI-ARIAの語彙を用いるようになり、それが支援技術に対しても伝わるという良い世界が来る。テストの動作と相容れないWAI-ARIAは利用できないことにもつながるので、正しいセマンティクスを利用する制約としても機能するだろう。


その他にもChrome Devtools Protcolを利用して、要素に追加されているイベントを取得することもできる。フォーカス可能ではない要素(たとえば div など)において、クリックイベントが追加されている場合、同時にキーボードイベントも追加する必要がある、という要件を正確にテストすることが可能となる。

このようにブラウザのAPI、または実際にブラウザを動かすことでより多くの範囲をテストすることができる。

今後

私のacotにおける役割は良いルールセットを作りメンテナンスしドキュメントを書くことなので、まずはどんどんとpresetのルールセットを増やしていきたい。

こういうルールが欲しい、いまやってるテストを自動化したいなどのアイデアをIssueとして上げてもらえると泣いて喜ぶので何卒よろしくお願いしたい。

https://github.com/acot-a11y/acot/issues