[キャッチアップ] Playwright

スクラップについて
- Playwright をざっくり理解して現場で活用できるかの検討材料とするのが狙い
- ドキュメント読みながら手を動かしたりサンプルコード読んだりする
- Cypress の経験があるため、主にそれとの比較の観点で読む

① トップページ
Playwright enables reliable end-to-end testing for modern web apps.
Puppeteer みたいなブラウザ自動化APIでなく、あくまで E2E テストのフレームワークの模様。

Any browser • Any platform • One API
Cross-browser. Playwright supports all modern rendering engines including Chromium, WebKit, and Firefox.
Chrome に限らず WebKit, FireFox も同じ API で使用できるらしい。どういう仕組なんだろ。でも今回のスコープだと Chrome で動けばOKなのであんまり考えずにいく。
Cross-platform. Test on Windows, Linux, and macOS, locally or on CI, headless or headed.
どこでも動かせるらしい。それはありがたい。想定用途としては、Mac (M1) 上と、CI (Linux) の二箇所で動けばいいかな。
Cross-language. Use the Playwright API in TypeScript, JavaScript, Python, .NET, Java.
あ、JS(TS) じゃなくても良いんだ。ほかの言語のドライバがあるのかな。今回は TypeScript を使うものとする。
しらべるとクライアントとして Ruby が使えたりもする。本来は Node で、ファーストサポートに Python とか、サードパーティで任意の言語が使えるって感じかな。
Test Mobile Web. Native mobile emulation of Google Chrome for Android and Mobile Safari. The same rendering engine works on your Desktop and in the Cloud.
モバイルデバイスのエミュレーションも可能。これもすごそうだけど今回はPR用途だけで考える。

Resilient • No flaky tests
Auto-wait. Playwright waits for elements to be actionable prior to performing actions. It also has a rich set of introspection events. The combination of the two eliminates the need for artificial timeouts - the primary cause of flaky tests.
ajax 周りに対する自動待機か。このへんの仕組みは結構ツールによって差がありそう。Cypress だと内部的に自動リトライを何度も繰り返すって感じだった気がする
Web-first assertions. Playwright assertions are created specifically for the dynamic web. Checks are automatically retried until the necessary conditions are met.
アサートに関してはまさに自動リトライ機構を持ってるようね。
Tracing. Configure test retry strategy, capture execution trace, videos, screenshots to eliminate flakes.
失敗したテストに対するフォローアップ。このへんは Cypress がたいだい同じような仕組みを持ってるので理解してる。

No trade-offs • No limits
Browsers run web content belonging to different origins in different processes. Playwright is aligned with the modern browsers architecture and runs tests out-of-process. This makes Playwright free of the typical in-process test runner limitations.
んー、ちょっと自信ないけど、テストランナーのプロセスと、ヘッドレスブラウザが動作しているプロセスが分離されていて、オリジンの数だけプロセスが作られますよって話かな。これは Cypress がテストランナーと対象サイトを同一プロセスで動かしているアーキテクチャと逆を行ってるな。
Multiple everything. Test scenarios that span multiple tabs, multiple origins and multiple users. Create scenarios with different contexts for different users and run them against your server, all in one test.
複数タブ、複数オリジン、複数セッションをサポート。これ Cypress だと苦手だから良いな。
Trusted events. Hover elements, interact with dynamic controls, produce trusted events. Playwright uses real browser input pipeline indistinguishable from the real user.
要素のホバー出来るのありがたい。Cypress 出来なかった気がする。
Test frames, pierce Shadow DOM. Playwright selectors pierce shadow DOM and allow entering frames seamlessly.
これが何を言ってるのかちょっと理解できてないけど、Cypress でも DOM の更新によって更新前の参照をした状態でのアサートに失敗するとかあった気がする。Cypress の悪口ばっかだなさっきから。

Full isolation • Fast execution
Browser contexts. Playwright creates a browser context for each test. Browser context is equivalent to a brand new browser profile. This delivers full test isolation with zero overhead. Creating a new browser context only takes a handful of milliseconds.
テストケースごとに新規プロファイルでのブラウザコンテキストを利用するため完全の独立している。パフォーマンスは気になるけど爆速らしい。
Log in once. Save the authentication state of the context and reuse it in all the tests. This bypasses repetitive log-in operations in each test, yet delivers full isolation of independent tests.
うん?これは直前のと矛盾しないのかな。コンテキストを共有するわけじゃなくて認証情報を保持して共有するってことかな。ログイン用のシナリオと、ログイン後のシナリオがある場合、実行順に依存関係が生じそうな気がするけどどうだろ。書いてみて調べればいいか。

まだ試してはいないけど、どうやら context.storageState();
みたいな仕組みでブラウザコンテキストをまるごと保存して、それを他のシナリオで再読み込みできるみたい。ので認証情報に限らず、クッキーまるごととかできるっぽい。

Powerful Tooling
Codegen. Generate tests by recording your actions. Save them into any language.
あー、ブラウザ手動操作してテストコードに起こすやつね。これはファーストテイクとしてはかなりアリ。流石にそのまま使うことはできないと思ってるけど。
Playwright inspector. Inspect page, generate selectors, step through the test execution, see click points, explore execution logs.
GUI のデバッグツールがあるらしい。そんなに使う機会あるかな?
Trace Viewer. Capture all the information to investigate the test failure. Playwright trace contains test execution screencast, live DOM snapshots, action explorer, test source, and many more.
失敗したテストのフォローアップ用のツールがある。DOM スナップショット良さそう。状況再現できるってことだよね。

② Installation
手元で某サービスをテストするコードを書きながら理解を深めていきたいので、インストール作業から始めてく。
yarn create playwright
TS 使う。GitHub Actions なし。各種ブラウザインストール。
簡易的なプロジェクトが作られる。
こんなサンプルコードが作られてる。
import { test, expect } from '@playwright/test';
test('has title', async ({ page }) => {
await page.goto('https://playwright.dev/');
// Expect a title "to contain" a substring.
await expect(page).toHaveTitle(/Playwright/);
});
test('get started link', async ({ page }) => {
await page.goto('https://playwright.dev/');
// Click the get started link.
await page.getByRole('link', { name: 'Get started' }).click();
// Expects page to have a heading with the name of Installation.
await expect(page.getByRole('heading', { name: 'Installation' })).toBeVisible();
});
とりあえず実行する
$ npx playwright test
Running 6 tests using 6 workers
6 passed (7.5s)
To open last HTML report run:
npx playwright show-report
どうやら3ブラウザ x 2シナリオでサンプルテスト6種が実行されたらしい。
ブラウザについては設定ファイルのこのへんのことっぽいが、今回は Chrome 1個あればいいかな。
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
{
name: 'firefox',
use: { ...devices['Desktop Firefox'] },
},
{
name: 'webkit',
use: { ...devices['Desktop Safari'] },
},
],
UI モードで起動する。
npx playwright test --ui
想像してたより遥かにカッコいいし多機能だ。この感じは一見すると Cypress だけど、プロセスはしっかり別れてるんだね。
これブラウザが中で動いてるんじゃないんだ。ブラウザは別プロセスで動いていて、その結果の DOM スナップショットをツール内で描画してるだけだな?実際にブラウザが自動で動いてる様は確認できないのかな。

明日以降、具体的なテストシナリオを書きながらツールに慣れていくよ。

③ Wrighting tests
Introduction
Playwright は要素がインタラクション可能であるかを自動でチェック・待機してくれる。
First test
テスト対象ページにアクセスし、ログイン画面の見出しをアサートする
import { test, expect } from "@playwright/test";
const LOGIN_URL = "https://hogehoge/login";
test("ログイン画面を表示できる", async ({ page }) => {
await page.goto(LOGIN_URL);
await expect(page.getByText("Log In")).toBeVisible();
});
-
goto
はテストの基本となるページ遷移メソッド -
getByText
はロケータの一種で、要素の取得に使用する -
expect
は Jest みたいなテストアサーション

ロケータはやっぱりユーザーが視認する情報と、role みたいなアクセシビリティ属性を使用するのが推奨されてるみたい。code generator
使って自動生成した場合もそれが優先されるから、それをもとに手直しでOK

アサーションで意識しなきゃならないのは、自動リトライするかどうかだな。
await expect(locator).toBeAttached()
みたいな要素やページに基づくアサートは自動リトライ対象。
expect(value).toBeTruthy()
みたいな単体テストで見かけるようなヤツは自動リトライしない。
These assertions allow to test any conditions, but do not auto-retry. Most of the time, web pages show information asynchronously, and using non-retrying assertions can lead to a flaky test.
ってことなので推奨されて無さそう。それはそう。

ログインできるぐらいのシナリオを作ってみる。
test("ログインできる", async ({ page }) => {
await page.goto(LOGIN_URL);
await page.getByPlaceholder("Enter group ID").fill(TENANT_NAME);
await page.getByRole("button", { name: "Next" }).click();
await expect(page.getByText(TENANT_NAME)).toBeVisible();
await page
.getByPlaceholder("Enter email address or account name")
.fill(USER_NAME);
await page.getByPlaceholder("Enter password").fill(PASSWORD);
await page.getByRole("button", { name: "Log In" }).click();
await expect(page.getByText("HogeHoge")).toBeVisible();
});
-
click
fill
は要素に対するアクション - ほかにも
check
hover
focus
setInputFiles
selectOption
あたりは使いそう

④ Running tests
テスト実行の色んなコマンドの紹介。
ブラウザ実際に立ち上げて走らせるコマンドあった。細かいデバッグせずにざっと1種のテストだけ走らせるってときはこれで良さそう。
$ npx playwright test --headed
デバッグモードでステップ実行するのも面白い
$ npx playwright test --debug
テスト結果のレポートをHTMLで生成できる
$ npx playwright show-report

⑤ Test generator
$ npx playwright codegen demo.playwright.dev/todomvc
みたいなコマンド実行するとブラウザが起動して対象ページにアクセスして、そのブラウザ上での操作がリアルタイムでコード化される。
使ってみたけど、記録用に使用されるブラウザが日本語で、実行時に使用されるほうが英語だったから再現できなかった。言語の指定はサッとできるんだろうけど。

英語にするのはこのオプション付けるだけだった。 --lang=en
同様にタイムゾーンや位置情報も変更可能。

しかもセッション情報の保存あるいは読み込みまでできるぞ…。
$ npx playwright codegen github.com/microsoft/playwright --save-storage=auth.json
$ npx playwright codegen --load-storage=auth.json github.com/microsoft/playwright

⑥ Trace viewer
トレースは実行ステップごとのDOMのスナップショットを閲覧してデバッグできる仕組み。
デフォルトだとテスト失敗時にリトライしてその時にのみトレースを記録する。めっちゃコスパ良さそう。
import { defineConfig } from '@playwright/test';
export default defineConfig({
retries: process.env.CI ? 2 : 0, // set to 2 when running on CI
// ...
use: {
trace: 'on-first-retry', // record traces on first retry of each test
},
});
trace
オプションで明示的にトレース作成もできる。
$ npx playwright test --trace on
トレースはレポートから確認可能。
npx playwright show-report

⑦ UI Mode
これまでもちょいちょい使ってた IDE みたいなやつ。トレース確認もできるし、ステップ実行もできるし、Playwright の機能を詰め込みまくった感がある。
- テスト実行
- ウォッチモード
- ファイルエクスプローラー
- トレース確認
- VRT
- ソースコード表示
- などなど

⑧ CI GitHub Actions
GitHub Actions に限らず任意の CI 内でテストを走らせられるよという話。それはそう。
Azure にテストレポートをアップロードするとかニッチな案内出てきたなと思ったけど、Playwright も GitHub も Azure もマイクロソフトか…。

⑨ Getting started - VS Code
vscode 拡張の話。vscode もMicrosoftだもんなぁ。
CLI なしで vscode 拡張だけでも完結できるらしい。
基本的にはここに書いてあることの通り。使っていって慣れるしか無いな。

公式ドキュメントの基本的なページはこのぐらい。
リファレンス系は困ったら読めば良いとして、しばらく色んなテスト書いてみようかな。

セッションの状態を保存する
ログイン後のセッションを保存して再利用する。
state.json
にその時点のクッキーとかローカルストレージとか全部をシリアライズしたやつが書き込まれる。
await page.context().storageState({ path: "state.json" });
これを別のテストで読み込むと再現できる。
test("ログインした状態でページを開ける", async () => {
const browser = await chromium.launch();
const state = JSON.parse(require("fs").readFileSync("state.json", "utf8"));
const context = await browser.newContext({ storageState: state });
const page = await context.newPage();
await page.goto(URL);
});
コンテキストの作り方が若干分かりづらかったので整理する
- browser: 起動するブラウザのインスタンス
- context: セッションとなるブラウザコンテキスト
- page: ブラウザのタブやウィンドウ
browser > context > page の階層関係で、それぞれ子を複数持てる感じかな。

画像をアップロードする
setInputFiles
が使える。
ファイルパスはカレントディレクトリからの相対パスとなるので、画像ファイルを用意しておく。
const fileInput = await page.$("input[type=file].media-file-input");
await fileInput?.setInputFiles("fixtures/sample.jpg");
ほんとは getByRole
とかアクセシビリティ属性で取得できればよかったんだけど、そういうのがなかったので $
を使ってCSSセレクタで取り出してる。

Basic 認証を突破する
Basic 認証情報を入力する手段はないっぽいので、素直にURLに埋め込む。
await page.goto('http://username:password@example.com');