Playwright + reg-actionsを使ってGitHub Actions上でビジュアルリグレッションテストをする
解決したい問題
CSSの変更やライブラリのアップデートなどが原因で、ブログのデザインが崩れてしまうことがある。
実際の画面を確認するのは手間がかかるので、自動化したい。
解決策の1つとして、ビジュアルリグレッションテストがある。
ビジュアルリグレッションテスト(VRT)とは、変更前後のスクリーンショットの差分を確認するテストである。
ライブラリのアップデートやCSSを変更する際はPull Requestを作成する運用としているため、GitHub Actionsを使ったVRTを実施することで、意図した変更かを確認できる。
確認したバージョン
- pnpm:v8
- @playwright/test:v1.40.1
- reg-actions:v2
Playwrightでスクリーンショットを撮る
Playwrightとは
PlaywrightはE2Eテストツールの1つで、ヘッドレスブラウザーを内包している。
スクリーンショットを撮るために使うのは本来の使い方ではないが、ブラウザーのセットアップは手間がかかるので、設定ファイルを書くだけで実行できるPlaywrightを使う。
Playwrightのセットアップ
以下のコマンドでPlaywrightをインストールする。
pnpm install -D @playwright/test
プロジェクトのルートにPlaywrightの設定ファイルを作成する。
設定ファイルの内容はPlaywright | Installで案内されているインストールコマンドを実行したときに生成される内容を参考にする。
唯一の差分としては、動かすブラウザーはGoogle Chromeに限定しているところである。playwright.config.tsimport { defineConfig, devices } from '@playwright/test'; export default defineConfig({ testDir: './tests', fullyParallel: true, forbidOnly: !!process.env.CI, retries: process.env.CI ? 2 : 0, workers: process.env.CI ? 1 : undefined, use: { trace: 'on-first-retry', }, reporter: 'html', projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'] }, }, ], });
GitHub Actions上でブログのローカルサーバーを起動する必要があるため、起動用の設定を追加する。
Astroでブログを作成している場合はhttp://localhost:4321でローカルサーバーが起動するため、以下のように設定する。playwright.config.tsexport default defineConfig({ // ... use: { + baseURL: 'http://localhost:4321', // ローカルサーバーの URL trace: 'on-first-retry', }, reporter: 'html', projects: [ // ... ], + webServer: { + command: 'npx astro dev', // ローカルサーバーを起動するコマンド + url: 'http://localhost:4321', // ローカルサーバーの URL + reuseExistingServer: !process.env.CI, + stdout: 'pipe', // デバッグログを出力する }, });
テストケースの追加
設定ファイルでtestDir
に「tests」を指定したので、「tests」ディレクトリにファイルを作成する。
今回はブログのトップページのスクリーンショットを保存するスクリプトを追加することにした。
Playwrightではpage.screenshot()
で現在アクセスしているページのキャプチャができる。
import { test, expect } from '@playwright/test';
test('home', async ({ page }) => {
// トップページにアクセスする
await page.goto('/');
// スクリーンショットを撮る
await page.screenshot({ path: `screenshots/home.png`, fullPage: true });
// おまけ:タイトルをチェックする
await expect(page).toHaveTitle(/ひよこまめ/);
});
ローカルでの実行
設定ファイルとテストケースができたので、npx playwright test
コマンドでPlaywrightを実行する。
以下の2点が確認できれば成功である。
- ローカルサーバーが起動して、テストがパスする
- 「screenshots」ディレクトリにスクリーンショットが生成されている
npx playwright test
[WebServer]
> blog@0.0.1 start /Users/chick-p/repos/github.com/...
> astro dev
[WebServer] 🚀 astro v3.5.7 started in 717ms
┃ Local http://localhost:4321/
┃ Network use --host to expose
Running 1 test using 1 worker
[WebServer] Skipping algolia index
1 passed (8.6s)
To open last HTML report run:
npx playwright show-report
reg-actionsでVRTを実行する
reg-actionsとは
PlaywrightでもtoMatchSnapshot
というAPIを使えば、スクリーンショットの差分を確認できるが、今回のケースでは以下の問題があるため使用しない。
- 変更前の画像(正解画像)をリポジトリで管理する必要がある。
差分が出るたびに正解画像を更新する必要があるため、管理が大変である。 - VRTでは変更前後のスクリーンショットを同じ環境でキャプチャする必要がある。
GitHub Actions上で撮ったスクリーンショットとローカルの執筆環境を比較しても、フォントが違うなどの差分が生まれてしまう。
この解決策として一般的にDockerが利用されることが多いが、こちらも準備が大変である。
そこで、reg-actionsというGitHub Actionsアクションを使うことにした。
reg-actionsとは、reg-cliを使ってGitHub Actions上でVRTを実行するためのアクションである。
reg-actionsでは、Playwrightでキャプチャした画像をArtifactsに保存する。
差分確認ではArtifactsに保存された比較対象の画像をコミットツリーを辿って特定するので、正解画像をリポジトリで管理する必要がない。
もちろん、変更前後のスクリーンショットはどちらもGitHub Actionsのランナーでキャプチャするので、環境の差分が発生しない。
reg-actionsが内部的に使っているreg-cliやreg-suitを使う方法もあるが、画像を保存するS3などのストレージの準備や管理がいる。
またレポートを確認するにはCloudFrontやCloudFront Functionsのセットアップも必要になる。 reg-actionsはGitHubのみで完結するため、これらの準備が不要になることもメリットである。
テストワークフローの追加
まずPlaywrightを動かすために必要なワークフローを追加する。
name: Visual Regression Test
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
permissions:
contents: write # Need to upload artifacts
jobs:
test:
timeout-minutes: 5
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v2
with:
version: 8
- uses: actions/setup-node@v4
with:
node-version-file: .node-version
- name: Install dependencies
run: pnpm install
- name: Cache playwright binaries
uses: actions/cache@v3
id: playwright-cache
with:
path: ~/.cache/ms-playwright
key: ${{ runner.os }}-playwright-${{ hashFiles('**/pnpm-lock.yaml') }}
- name: Install Playwright Browsers
if: steps.playwright-cache.outputs.cache-hit != 'true'
run: pnpm exec playwright install --with-deps chromium
- name: Run Playwright tests
run: pnpm exec playwright test
- uses: actions/upload-artifact@v3
if: always()
with:
name: playwright-report
path: playwright-report/
retention-days: 30
reg-actionsのセットアップ
次にreg-actionsをワークフローに追加する。
reg-actionsでは、Pull RequestとActionsのジョブサマリーに結果をコメントするため、GitHubのトークンにその権限を付与する。
permissions:
contents: write # upload artifacts
+ actions: write # Need to comment to job summary
+ pull-requests: write # Need to comment to PR
jobs:
test:
steps:
# ...
- uses: actions/upload-artifact@v3
if: always()
with:
name: playwright-report
path: playwright-report/
retention-days: 30
+ - uses: reg-viz/reg-actions@v2
+ with:
+ github-token: "${{ secrets.GITHUB_TOKEN }}"
+ image-directory-path: "./screenshots"
日本語フォントのインストール
GitHub ActionsのLinuxランナーでスクリーンショットを撮ると、日本語が豆腐化してしまう。
そのためワークフロー上で日本語フォントをインストールしておく。
- name: Install dependencies
run: pnpm install
+ - name: Cache fonts
+ uses: actions/cache@v3
+ id: fonts-cache
+ with:
+ path: ~/.fonts
+ key: ${{ runner.OS }}-fonts
+ - name: Install Japanese font
+ if: steps.fonts-cache.outputs.cache-hit != 'true'
+ run: sudo apt install fonts-noto-cjk
- name: Cache playwright binaries
動作確認
以上で設定が完了したので、Pull Requestを作成してワークフローを実行する。
初回実行にあたるPull Requestでは正解の画像が存在しないため、新規画像として扱われる。
差分を確認するには、初回実行のPull Requestをマージした後に、Pull Requestを作成する必要がある。
2回目以降のPull Requestでは、正解の画像と比較して差分がある場合、Pull Requestのコメントで差分が通知される。
コメントの「Report」を展開すると、実際のスクリーンショットで差分を確認できる。
スクリーンショットの例では、SNSのアイコンを鳥からXに変更したため差分が発生している。
なお、検索ボックスは外部リソースなので、スクリーンショットを撮ったタイミングに依存して検索ボックスに差分が発生している。
注意点
- reg-actionsでは、Pull Requestの変更元にあたるベースブランチのスクリーンショットを、変更前の画像として扱う。
そのため、あらかじめベースブランチでのスクリーンショットをArtifactsに保存しておく必要がある。
たとえば、main
ブランチからブランチを作成してPull Requestを作成する場合は、main
でのVRTが終わるのを待ってから、Pull RequestでのVRTを実行する。 - reg-actionsが作成するArtifactsには、有効期限がある。
そのため、親ブランチのArtifactsの有効期限が切れる前に、ワークフローを実行しておく必要がある。
削除されてしまった場合は、新規画像として扱われてしまう。 - スクリーンショットに差分が発生しても、reg-actionsのワークフローは失敗しない。
そのため、発生した差分が想定したものかを自分で確認する必要がある。