はじめに
Seleniumはチラッと触ったことがあるが、Playwrightは未経験なので軽く触ってみます
今回は、WSL2上で動かしていますが、
依存パッケージのインストールが面倒だったのでwindowsネイティブでやった方が良いと思いました
Playwrightとは
Microsoftが開発した高速で安定したWeb自動テストフレームワーク
以下のような事が可能
- WebアプリのE2Eテスト
- スクレイピングやブラウザ操作の自動化
環境構築
npm init -y
# playwrightをインストール
npm install -D @playwright/test
# ブラウザをインストール
npx playwright install
WSL2のUbuntuでは、Playwrightが内部で使用するChromiumに必要なライブラリが不足しているため、そのままではブラウザが起動せずエラーになります
そのため、必要な依存パッケージを追加でインストールする必要があります(筆者の環境はUbuntu24.04です)
文字化けもするので、日本語フォントもインストールします
sudo apt-get update
sudo apt-get install -y \
libnss3 \
libnspr4 \
libatk1.0-0 \
libatk-bridge2.0-0 \
libcups2t64 \
libdrm2 \
libxkbcommon0 \
libxcomposite1 \
libxdamage1 \
libxfixes3 \
libxrandr2 \
libgbm1 \
libpango-1.0-0 \
libcairo2 \
libasound2t64
sudo apt-get install -y fonts-ipafont-gothic fonts-ipafont-mincho
ブラウザ操作の自動化
# QiitaのHPを開き、操作のテストコードをtests/example.spec.tsに出力
npx playwright codegen https://qiita.com/ --output=tests/example.spec.ts
Chromeが開きます
とりあえず開いたQiita HPの右上の検索ボックスで検索してみると、/tests/example.spec.tsに一連の操作がコード化されます
import { test, expect } from '@playwright/test';
test('test', async ({ page }) => {
// Qiita HPにアクセス
await page.goto('https://qiita.com/');
// 検索ボックスクリック
await page.getByRole('searchbox', { name: 'Search' }).click();
// 'playwright'と入力
await page.getByRole('searchbox', { name: 'Search' }).fill('playwright');
// Enter押下
await page.getByRole('searchbox', { name: 'Search' }).press('Enter');
});
生成されたテストコードを実行
npx playwright test --ui
スクショも追加しよう
スクショは手動でコードに追加してください
import { test, expect } from '@playwright/test';
test('test', async ({ page }) => {
// Qiita HPにアクセス
await page.goto('https://qiita.com/');
// 検索ボックスクリック
await page.getByRole('searchbox', { name: 'Search' }).click();
// 'playwright'と入力
await page.getByRole('searchbox', { name: 'Search' }).fill('playwright');
// Enter押下
await page.getByRole('searchbox', { name: 'Search' }).press('Enter');
// 以下を追加(ページが遷移した事を確認してからスクショ)
await expect(page).toHaveURL(/search/);
await page.screenshot({ path: 'tests/images/search-result.png' });
});
再実行すると、tests/images に検索結果のスクショ、search-result.pngが作成されます!
テスト
ちょっとしたテストを実装してみます
Qiitaで'playwright'と検索し、検索結果に'playwright'と含まれているかテストします
import { test, expect } from '@playwright/test';
test('test', async ({ page }) => {
// Qiita HPにアクセス
await page.goto('https://qiita.com/');
// 検索ボックスクリック
await page.getByRole('searchbox', { name: 'Search' }).click();
// 'playwright'と入力
await page.getByRole('searchbox', { name: 'Search' }).fill('playwright');
// Enter押下
await page.getByRole('searchbox', { name: 'Search' }).press('Enter');
await expect(page).toHaveURL(/search/);
// 以下を追加(検索結果の最初の記事タイトルリンクに'playwright'が含まれているか)
await expect(page.getByRole('link', { name: /playwright/i}).first()).toBeVisible();
await page.screenshot({ path: 'tests/images/search-result.png' });
});
テストコードが正常終了したらテスト成功です
ちょっと気になった点
await page.getByRole('searchbox', { name: 'Search' }).click();
Roleの'searchbox'とか、nameの'Search'とか、何を基準に命名されてるの?
AIに聞いた結果を整理してみた:
- Role は ARIA ロール
- name は アクセシブルネーム
'searchbox'は、DOMの<input type="search">から自動判定
ARIAロールは、HTML要素の種類で判定
'Search'は、DOMの「placeholder="Search"」から自動判定
以下の優先順序で判定される
- aria-labelledby
<label id="lbl">検索</label>
<input aria-labelledby="lbl">
⇒上記の場合、アクセシブルネームは「検索」
- aria-label
<input aria-label="検索ボックス">
⇒上記の場合、アクセシブルネームは「検索ボックス」
- label 要素
<label for="q">検索</label>
<input id="q">
⇒上記の場合、アクセシブルネームは「検索」
- placeholder
<input type="search" placeholder="Search">
⇒上記の場合、アクセシブルネームは「Search」
- 要素のテキスト
<button>ログイン</button>
⇒上記の場合、アクセシブルネームは「ログイン」
- alt(画像)
<img src="logo.png" alt="Qiita ロゴ">
⇒上記の場合、アクセシブルネームは「Qiita ロゴ」
最後に
導入部分と簡単なテストについて触れました
codegenで、ブラウザ操作を自動でコード化できるのは滅茶苦茶便利ですね
気が向いたらもう少し高度な使い方を実践してまとめます

