Playwright + reg-actions を使って GitHub Actions 上でビジュアルリグレッションテストをする

Posts

解決したい問題

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 を使う。

Cross-browser end-to-end testing for modern web apps

Playwright のセットアップ

  1. 以下のコマンドで Playwright をインストールする。

    pnpm install -D @playwright/test
    
  2. プロジェクトのルートに Playwright の設定ファイルを作成する。
    設定ファイルの内容は Playwright | Install で案内されているインストールコマンドを実行したときに生成される内容を参考にする。
    唯一の差分としては、動かすブラウザーは Google Chrome に限定しているところである。

    playwright.config.ts
    import { 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'] },
        },
      ],
    });
    
  3. GitHub Actions 上でブログのローカルサーバーを起動する必要があるため、起動用の設定を追加する。
    Astro でブログを作成している場合は http://localhost:4321 でローカルサーバーが起動するため、以下のように設定する。

    playwright.config.ts
    export 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() で現在アクセスしているページのキャプチャができる。

tests/home.spec.ts
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 のみで完結するため、これらの準備が不要になることもメリットである。

A visual regression test tool for github actions :octocat:. - GitHub - reg-viz/reg-actions: A visual regression test tool for github actions :octocat:.
GitHub

テストワークフローの追加

まず Playwright を動かすために必要なワークフローを追加する。

.github/workflows/vrt.yml
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 のトークンにその権限を付与する。

.github/workflows/vrt.yml
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 ランナーでスクリーンショットを撮ると、日本語が豆腐化してしまう。
そのためワークフロー上で日本語フォントをインストールしておく。

.github/workflows/vrt.yml

    - 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 をマージした後に、Pull Request を作成する必要がある。
2 回目以降の Pull Request では、正解の画像と比較して差分がある場合、Pull Request のコメントで差分が通知される。

2回目以降の Pull Request に、「change detected」が表示されている

コメントの「Report」を展開すると、実際のスクリーンショットで差分を確認できる。

画像の差分が表示されている

スクリーンショットの例では、SNS のアイコンを鳥から X に変更したため差分が発生している。
なお、検索ボックスは外部リソースなので、スクリーンショットを撮ったタイミングに依存して検索ボックスに差分が発生している。

注意点

  • reg-actions では、Pull Request の変更元にあたるベースブランチのスクリーンショットを、変更前の画像として扱う。
    そのため、あらかじめベースブランチでのスクリーンショットを Artifacts に保存しておく必要がある。
    たとえば、main ブランチからブランチを作成して Pull Request を作成する場合は、main での VRT が終わるのを待ってから、Pull Request での VRT を実行する。
  • reg-actions が作成する Artifacts には、有効期限がある。
    そのため、親ブランチの Artifacts の有効期限が切れる前に、ワークフローを実行しておく必要がある。
    削除されてしまった場合は、新規画像として扱われてしまう。
  • スクリーンショットに差分が発生しても、reg-actions のワークフローは失敗しない。
    そのため、発生した差分が想定したものかを自分で確認する必要がある。