Puppeteerとaxe-coreで複数ページのアクセシビリティ検証を実現する

Mathias Bynensさんのツイートで「Puppeteer v2.0.0」がリリースされたことを知ったことがきっかけとなり、Chrome or Chromiumを操作するAPIを提供するPuppeteerを触ってみました。ツイートにもある通りpage.emulateMediaType(type)APIが追加され、ダークモード表示をエミュレートすることができるようになっていました。例えば、page.emulateMediaFeatures([{ name: 'prefers-color-scheme', value: 'dark' }]);としpage.screenshot()でスクリーンショットを取得すると、以下のような画像が得られます。
当ブログをダークモードで閲覧した画面の様子

他にもPuppeteerで何かできないかな?と考えた時、以前から存在を知っていたアクセシビリティ検証を行うことができるライブラリである「axe-core」を試してみようと思い、早速動かし方を調査しました。初めは「axe-puppeteer」を使おうとしたのですが、今日時点でもPuppeteer v1.1.0依存なことから他の手段を検討していたところ、「axe-core 3.2で検証結果を日本語化する方法 | アクセシビリティBlog | ミツエーリンクス」の記事を見つけました。Puppeteerとaxe-coreを読み込めば利用できるコードになっており、見事アクセシビリティの検証ができました。

CSVの出力

検証結果はconsole.log()でターミナルに表示させたのですが、これをCSVに出力することを考えました。これは難しくはなく、コマンド > ファイルとして標準出力をファイルに保存するだけでした。その際、検証結果をカンマ区切りにデータに整形するために「axe-reports」を使用しました。AxeReports.createCsvReportRow(results);の1行だけでCSVのデータに変換が行えました。

ただ、axeのChrome拡張と同じ検証結果を得るためにaxe.run()を実行する際にオプションを{ resultTypes: ['violations', 'incomplete', 'inapplicable'] }として目視でレビューが必要な項目なども出力されるようにしたのですが、violationsに該当するものしかCSVに出力されない問題に遭遇しました。axe-reportsのコードを確認するとresults.violationsしか出力しないコードになっていたため、リポジトリをフォークしresults.incompleteresults.inapplicableも出力されるように改造しました。改造したaxe-reportsは、hideki-a/axe-reportsのoutput-all-result-typesブランチに格納しています。

複数ページの検証

1ページずつChromeの拡張で検証するのは手間がかかるなと感じていたことから、複数ページをまとめて検証できないか検討しました。普段JavaScriptを書いている経験から、URLをまとめて与えforEach()か何かでループすれば良いと考え早速コードに落とし込みました。「async-foreach」パッケージを読み込み複数ページがまとめて検証できるようになったのですが、Promiseを使用するとasync-foreachを使わずとも良いことを知り、最終的には以下のようなコードとなりました。

const sitemap = fs.readFileSync('sitemap.xml', { encoding: "utf-8" });
const sitemapJSON = parser.parse(sitemap);
const urls = sitemapJSON.urlset.url;

for (let i = 0, nUrls = urls.length; i < nUrls; i += 1) {
    const url = urls[i]['loc'];
    promises.push(browser.newPage().then(async page => {
        // ページ読み込み
        await page.goto(`${url}`);

        // axeを注入して実行する
    }));
}

await Promise.all(promises);

コードを実行したところ、以下のキャプチャのように複数ページの検証結果が書き込まれたCSVが得られました。(少々分かりにくいキャプチャですが恥ずかしいのでご容赦下さい。)
開発したaxe-runnerを実行して得られたCSVのキャプチャ

さらなる発展

現在のコードではpage.emulate()にパソコンを想定したビューポートの情報を与えているのですが、ここにpuppeteer.devices['iPhone 8']を与えることで、iPhone 8で閲覧する状態をエミュレートすることができます。パソコンとスマートフォンのビューポートでアクセシビリティ検証を行うことができるのは便利ではないかと考えます。また冒頭に話題にしたダークモードのアクセシビリティ検証も自動化することもできるでしょう。

まとめ

サンプルコードはhideki-a/axe-runnerに格納しています。sitemap.xmlにURLを列挙し、npm startで検証が実行できます。

さらに使いやすいように検討を続け、ウェブサイトのアクセシビリティ向上に役立てたいと考えています。

今回の開発でアクセシビリティBlogの記事は大変参考になりました。ありがとうございました。