はじめに
Playwrightの公式を読み漁っていた際にAccessibility testing
の項目を見つけまして、実際にプロジェクトに導入してみたのと、調べたことをアウトプットします。
私はアクセシビリティ浅瀬にも到達できていない砂場ザッザッ勢です
@axe-core/playwright
Playwightの公式では@axe-core/playwright
を推奨しています。
Axe Accessibility Testing ToolsをPlaywrightテストの一部として使用できるライブラリとのことです。
便利ですね。
Axe、axe-coreとは
Axe、axe-coreとは何なのかGoogle先生に聞いてみました。
HTML向けのアクセシビリティテストツール。 プラガブルな作りで、Lighthouseのアクセシビリティテストをする時にも使われている。 Axeのコアとなる機能はaxe-coreとしてOSS化されている。
なるほど、初めて知りました。
axe-coreとしてOSS化されていて、それをplaywrightで使用できるようにしたものが@axe-core/playwright
というわけですね。
Lighthouseのアクセシビリティテストに使われているのは非常に信頼性が高いと思いました。
Axeについてもう少し深掘りします
そもそもAxeでチェックできるアクセシビリティのルールって?
こちらのREADMEにまとまっていました。
Axeのチェックがパスすればアクセシビリティは問題ないの?
そんなことはないです。
デジタル庁のウェブアクセシビリティ導入ガイドラインにも記載がある通り、ツールで機械的に確認できるものには限りがあります。
ツールで機械的に確認できるのは達成基準の 2割から 3割程度にとどまります。また、ツールによっては開発から時間も経過し、最新のウェブテクノロジーを採用して実装されたページのテストが困難な場合もあります。このため、試験は必ず人が目視とスクリーンリーダーで行う必要があります。
20ページ目参照
なので、明確に基準A、AAを満たしています!と言えるのかというとそうではないという理解をしています。
ただ、プロジェクトを開発する上でaxe-coreで担保できる部分まではテストで担保する
という落とし所はありなのかなとも思います。
この辺りはすごく難しい話だなと物凄く感じました...
じゃあAxeでチェックできないルールを把握しておく必要があるよね
把握しておく必要がありますよね。
調べてみましたがなかなかヒットせず、WCAGのガイドラインから差分を抽出する元気もなかった為(言い訳です)ChatGPT大先生に質問しました。
Q.
axe-core/playwrightでチェックしきれないアクセシビリティのルールを教えてください。
A.
Axe-coreやPlaywrightなどの自動化ツールは多くのアクセシビリティルールをチェックしますが、一部のルールは自動化ツールでは完全に検出できない場合があります。
- コンテンツの意味論的な適切さ:自動化ツールはHTML構造を分析しますが、コンテンツの意味や文脈に関する詳細なチェックは難しいです。
- ユーザー体験に関する規則:例えば特定の色のコントラストが低い場合に警告を出すルールなど、デザインの身的側面や実際の利用状況に関連する規則は自動化ツールでは限界があります。
- 一部の動的なコンテンツ:ユーザーの操作や状況に応じて変化するコンテンツは、自動化ツールが確実にチェックするのが難しいことがあります。
- 複雑なウェブアプリケーション内のアクセシビリティ:動的なコンポーネントや複雑なUIパターンは、完全に自動化されたテストでは全てのアクセシビリティ問題を捉えきれないことがあります。
まあ、そうだよねと。
コンテンツの意味論的な適切さ、UXなどセマンティック含めたアクセシビリティの適切さはデザインレベルまで落ちるので、あくまで実装レベルの品質担保という点
では@axe-core/playwright
で十分だという結論になりました。
実装
さて、ここまでつらつらと調べたことを書いてきましたが、ここからは導入方法とテストコードについて記載していきます。
公式に書いていると言えばそうですが、念の為です(?)
@axe-core/playwrightの導入
npm i -D @axe-core/playwright
テストコード
import AxeBuilder from '@axe-core/playwright';
test('Run HomePage E2E Test', async ({ page }) => {
await test.step('Test accessibility', async () => {
const accessibilityScanResults = await new AxeBuilder({ page })
.withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa',])
.analyze();
await expect(accessibilityScanResults.violations).toEqual([]);
});
});
これだけで前述したアクセシビリティのルールをチェックしてくれます。
expect(accessibilityScanResults.violations).toEqual([]);
の部分でルールに違反しているかを確認しています。
解析後に返ってくるデータのaccessibilityScanResults
の中身をlog出力してデータ構造を見てみます。
valueは削除していますが、データ構造は以下です。
{
testEngine: { name: },
testRunner: { name: },
testEnvironment: {
userAgent:
windowWidth:
windowHeight:
orientationAngle:
orientationType:
},
timestamp: ,
url: ,
toolOptions: { runOnly: { type: }, reporter: },
inapplicable: [
{
id: 'area-alt',
impact: null,
tags: [Array],
description: 'Ensures <area> elements of image maps have alternate text',
help: 'Active <area> elements must have alternate text',
helpUrl: 'https://dequeuniversity.com/rules/axe/4.8/area-alt?application=playwright',
nodes: []
},
...
],
passes: [
{
id: 'aria-deprecated-role',
impact: null,
tags: [Array],
description: 'Ensures elements do not use deprecated roles',
help: 'Deprecated ARIA roles must not be used',
helpUrl: 'https://dequeuniversity.com/rules/axe/4.8/aria-deprecated-role?application=playwright',
nodes: [Array]
}
...
],
incomplete: [],
⭐️violations: []
}
ルール違反が存在した場合、⭐️の部分に違反内容が格納され、await expect(accessibilityScanResults.violations).toEqual([])
のテストコードに引っかかるという流れなります。
では実際にエラーを出してみましょう。
Error: expect(received).toEqual(expected) // deep equality
- Expected - 1
+ Received + 82
- Array []
+ Array [
+ Object {
+ "description": "Ensures each HTML document contains a non-empty <title> element",
+ "help": "Documents must have <title> element to aid in navigation",
+ "helpUrl": "https://dequeuniversity.com/rules/axe/4.8/document-title?application=playwright",
+ "id": "document-title",
+ "impact": "serious",
+ "nodes": Array [
+ Object {
+ "all": Array [],
+ "any": Array [
+ Object {
+ "data": null,
+ "id": "doc-has-title",
+ "impact": "serious",
+ "message": "Document does not have a non-empty <title> element",
+ "relatedNodes": Array [],
+ },
+ ],
+ "failureSummary": "Fix any of the following:
+ Document does not have a non-empty <title> element",
+ "html": "<html><head></head><body></body></html>",
+ "impact": "serious",
+ "none": Array [],
+ "target": Array [
+ "html",
+ ],
+ },
+ ],
+ "tags": Array [
+ "cat.text-alternatives",
+ "wcag2a",
+ "wcag242",
+ "TTv5",
+ "TT12.a",
+ "EN-301-549",
+ "EN-9.2.4.2",
+ "ACT",
+ ],
+ },
+ Object {
+ "description": "Ensures every HTML document has a lang attribute",
+ "help": "<html> element must have a lang attribute",
+ "helpUrl": "https://dequeuniversity.com/rules/axe/4.8/html-has-lang?application=playwright",
+ "id": "html-has-lang",
+ "impact": "serious",
+ "nodes": Array [
+ Object {
+ "all": Array [],
+ "any": Array [
+ Object {
+ "data": Object {
+ "messageKey": "noLang",
+ },
+ "id": "has-lang",
+ "impact": "serious",
+ "message": "The <html> element does not have a lang attribute",
+ "relatedNodes": Array [],
+ },
+ ],
+ "failureSummary": "Fix any of the following:
+ The <html> element does not have a lang attribute",
+ "html": "<html><head></head><body></body></html>",
+ "impact": "serious",
+ "none": Array [],
+ "target": Array [
+ "html",
+ ],
+ },
+ ],
+ "tags": Array [
+ "cat.language",
+ "wcag2a",
+ "wcag311",
+ "TTv5",
+ "TT11.a",
+ "EN-301-549",
+ "EN-9.3.1.1",
+ "ACT",
+ ],
+ },
+ ]
というような感じでエラー内容が配列に格納され空配列
と空ではない配列
の比較となりテストに失敗します。
簡単ですね。
終わりに
Playwrightで楽にアクセシビリティのチェックができることが分かりました。
@axe-core/playwrightを導入することによって、CIで自動化することもできますし、導入する価値は十分にあると考えています。
ただ、過信はしすぎず実装レベルの品質担保
という線引きは必ず必要になってきそうです。
以上です。