Playwright 1.14の新機能
公式のリリースノートでは以下の2機能が追加されたよって書いてありますが、どういうときに便利なの?ってのがイマイチわかりづらいので、実際のユースケースを紹介しつつ説明してみます。
- Strict modeの導入
- Locator API
Strict mode
ひとことでいうと、querySelectorをするときに、ヒットする要素が1個しか無いことをしっかりと確認して次にすすむ、というモードです。
おもちゃのような例でまずは説明すると、
<button>送信</button>
<button>キャンセル</button>
これに対して、従来は page.click('button')
で送信ボタンが押されていました。上から順にみて最初の1個を返していたからです。
1.14では、従来の挙動に変更はありませんが、 page.click('button, strict=True)
とすると、strict mode violationの例外が発生するようになります。
page.click("text=送信", strict=True)
のように、ヒットする要素が1個しかない場合には、例外は発生しません。
これが、どういうときに役に立つかと言うと、実際に自動テストを実装する際には「思っていた要素と違うものを触ってしまっていた!」なんてことが稀によく起きます。 page.query_selector("span.loading")
でLoadingのくるくるを選択していると思っていたら、どこか別の所のLoading文字列を選択してしまっていて、デバッグしてみたらようやくそれがわかった、みたいなケース。
Webサービスが大きく複雑になればなるほど、自動テストにおいて「思った通りのDOM要素を間違いなく選択できているか」は重要になってきます。それを実装時に気付けるようにするための便利なしくみが導入された、ということです。
Locator API
DOM要素を表現するクラスとして、従来はElementHandleがありました。1.14からはElementHandleと似たような概念としてLocatorが登場します。
ElementHandleは具体的なDOM要素を内部的に保持するのに対し、LocatorはDOM要素を特定するためのクエリのみを保持します。
Locator APIにはいろいろメリットはありますが、auto-waitingとの相性が向上することが期待できます。
page.click('.load_more')
この書き方だと、
- clickを実行した瞬間にDOM要素として存在しない場合 →当該DOM要素が現れactionableになるまで待つ
- clickを実行した瞬間にDOM要素として存在するがactionableではない場合 →actionableになるまで待つ
のように、タイムアウト(デフォルト30秒)するまでPlaywrightは結構しぶとく待ってくれます。
いっぽうで、ちょっと複雑なページを相手に、以下のようにElementHandleを使うことにしたとします。
metadata_section = page.query_selector('.metadata')
metadata_section.query_selector('.load_more').click
こうしてしまうと、
- .metadataや.load_moreのいずれかのDOM要素が存在しない場合 →待ってくれない。ぬるぽ発生
- .metadataや.load_moreのいずれもDOM要素は存在するが、いずれかがactionableでない場合 →actionableになるまで待つ
のように、auto-waitingが限定的になってしまいます。困ります。
そこでLocatorです。
metadata_section = page.locator('.metadata')
metadata_section.locator('.load_more').click
これは内部的に page.click('.metadata .loadmore')
と同じことをやります。よってauto-waitingがばっちり効きます。便利ですね!
Locator APIはデフォルトでstrictです
LocatorはElementHandleの遅延評価みたいなものなので、思った通りの要素をつかんでいないまま次に進まれると結構デバッグが大変になりそうです。そうならないよう、Locator APIによるDOM要素選択はデフォルトでstrictです。2つ以上のDOM要素がヒットするようなクエリは例外になります。
リストの2番めをクリック、みたいにやるときは、同じくPlaywritht 1.14で導入された nth() を使って○○でヒットする2番め!って明示すればいいです。
list_section.locator('.list_item >> nth=1').click
Discussion