10/4追記: 本記事の中で不具合のような動作があると書いていたのですが、issueで報告したところ、すぐに修正されました。次のバージョン1.39で修正版がリリースされるようです。
以前書いたCypressの記事で、アクセシビリティ情報を使うことで壊れにくくなるよ、と(今では当たり前のようにみんな言っていますが)いう記事を書きました。
この時は出力に使うべきロールが何か? というのがわかっていませんでした。
サーバーからとってきた動的な値を表示するテキストって、DOM上は単なるテキストなのでロールを持っていないのですよね。テスト上はここにロールがあって要素取得できるととても助かる。
次のどちらかな、と思っているのですが、どちらの方がスクリーンリーダーユーザーにとって自然なロール指定のかはちょっとわかってません。とりあえず前者にしています。
先日、「実際使えるロールとか、それに適したタグって全部でどのぐらいあるんだろうか?」というのが気になってPlaywrightのソースコードを眺めていたところ、それが定義されているファイルがplaywright-core/src/server/injected/roleUtils.tsであることがわかり、その中で <output>
タグがあるというのを知りました。<output>
タグにはデフォルトでstatus
というaria-roleがついていることがわかります。
多くのブラウザが aria-live
の領域としてこのタグを扱ってくれるため、何かしらのロジックが走って結果が変わった時のとかにうまく変更をユーザーに知らせてくれるらしいです。ReactとかVueで計算した結果を出す先をマークアップするには良さそうですね。
<output>
タグを使ってみる
MDNの説明を読むと、<output>
タグはfor
属性を使って、フォーム要素っぽく、HTMLとして<label>
と関連付けられることがわかりました。これを使うのがHTMLのセマンティクス的に良さそうですね。
viteでReactの雛形を作って、サンプルアプリを作ってみました。2つの数値のフォームがあり、それに数値を入れると合計値、積算した結果を表示します。
import { useCallback, useState, ChangeEvent } from 'react' |
Playwrightで書いてみたテストは次の通りです。<output>
タグについたラベルを使って取得できています。
import { test, expect } from '@playwright/test'; |
# getByRole(‘status’)は現状使えなそう→1.39から使えます!
ラベルの選択ができたし、getByRole()
ではどうかな、と試してみましたがうまくいきません。これは見つけられませんでした。
expect(await page.getByRole('status', { name: 'Sum' })).toBeVisible() |
名前とロールは違うものだし、仕方ない気もしないでもないけど<input>
タグの中にはラベルをnameで指定するのが普通の要素もあります。
{/* ボタンのテキスト */} |
Playwrightで実験してみると次のようになりました。
// 成功 |
<output>
はフォーム属性ではないせいか、ラベルをroleのnameとしては認識しないようです。checkboxはいけるんですけどね。
ARIA周りの仕様とかを完全に理解したわけではないので、これはissue報告すべきなのかどうなのか悩むところではありますが、現状 getByLabel()
で情報が取れることはわかったので、一旦これでよしとしようかと思います。
→報告したところ修正されました。1.39で治るようです。
まとめ
数年前に発表した資料でもわからん、と書いていた、喉に使えた骨だった悩みが解決しました。一方でPlaywrightの挙動があるべきものなのか悩ましいのですが、今後は積極的に使ってみようと思います。
自分がスクリーンリーダー等を使っていなかったりもあって、アクセシビリティに関しては自信をもって「これだ!」とまではまだ言えないので、識者のフィードバックがあるといいな、と思いつつ技術ブログにしています。