この記事はファンタアドベントカレンダー2023の2日目の記事です!
社内で今までVRT(Visual Regression Test)の採用経験がなかったため、VRT環境を一度試してみよう!という話になりました。この際、以下の2点を踏まえてGitHubだけで完結するVRT環境を構築しました。
- S3などのストレージが必要ない手軽なインフラ環境で試したい
- 既存の財産・知見であるStorybookを活用したい
本記事では、その環境構築手順と気づきを共有します。
なお本記事で掲載した一部コードを含む全体をレポジトリで公開しています。
Cybozu Frontend Advent Calendar 2023の12日目にkorosuke613さんも近い内容の記事を公開しています!
GitHub Actions のキャッシュを使った VRT のすゝめ
スナップショットをどこに置くかの選択肢およびメリデメ等、本記事でカバーしていない内容も記載されています。
ぜひ参考にしてください!
想定読者
- VRTに興味はあるが、試したことがない方
- 普段からStorybookを利用して開発をしている方
技術スタック
本記事で使用する主な技術スタックは以下の通りです。
- Storybook v7
- storycap v4.2.0: Storybookのスナップショットの生成
- reg-suit v0.12.2: 画像比較および差分レポートの生成
- GitHub Actions
- GitHub Pages
VRTのフロー
VRTはGitHub Actionsを利用して実行されます。実行フローを下図に示します。
1. 比較用画像群の生成
storycapを利用して参照スナップショット(develop/mainブランチ)とテスト対象スナップショット(featureブランチ)の画像群を生成します。
生成した画像群はArtifactsに保存され、VRT実行時に利用します。
2. VRT実行および結果レポート生成
reg-suitを利用してVRTを実行し、その結果をGitHub Pagesに公開します。また、PRコメントに結果サマリーとレポートのリンクを作成します。
3. レポートの閲覧
PRコメントに記載されたリンクからレポートを確認できます。
ワークフローの実装
以下では前項で紹介したフローの実装例を抜粋して解説します。
全体はサンプルレポジトリで公開していますので、よければ参考にしてください。
1. 比較用画像群の生成
参照スナップショットとテスト対象スナップショットの生成処理は独立しているため、並列で実行しています。
これらスナップショットは画像生成処理を実行するブランチが異なるだけで処理自体は同一となるため、以下では参照スナップショットを説明します。
画像生成処理は対象が増えるに従い実行時間が長くなるため、並列実行できるようにしています。そのため画像生成処理に利用するStorybookのbuild処理を別ジョブに切り出し、事前実行させています。
Storybookの事前buildジョブ
PR修正の度にスナップショットを生成するのはもったいないので、生成成功時にcacheさせています。
cacheが存在しない場合、Storybookのbuildを行います。
check_expected_screenshots:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: volta-cli/action@v4
- uses: actions/checkout@v3
with:
ref: main
fetch-depth: 0
- name: restore cached expected screenshots
id: screenshots_cache
uses: ./.github/actions/cache-screenshots
with:
lookup-only: 'true'
- name: restore cached node_modules
if: ${{ steps.screenshots_cache.outputs.cache-hit != 'true' }}
uses: ./.github/actions/cache-node-modules
- name: cache storybook build result
if: ${{ steps.screenshots_cache.outputs.cache-hit != 'true' }}
id: storybook_result_cache
uses: ./.github/actions/cache-storybook-build-result
- name: storybook build
if: ${{ steps.screenshots_cache.outputs.cache-hit != 'true' && steps.storybook_result_cache.outputs.cache-hit != 'true' }}
run: yarn storybook:build
env:
NEXT_IS_TEST: 'true'
スナップショット生成ジョブ
前述したように並列で実行します。各子ジョブで生成した画像群をArtifactsにアップロードして画像群を集約させます。
take_expected_screenshots:
needs: check_expected_screenshots
runs-on: ubuntu-latest
timeout-minutes: 30
strategy:
matrix:
shard: [1/4, 2/4, 3/4, 4/4] # 任意の並列数
steps:
- uses: volta-cli/action@v4
- uses: actions/checkout@v3
with:
ref: main
fetch-depth: 0
- name: restore cached expected screenshots
id: screenshots_cache
uses: ./.github/actions/cache-screenshots
with:
lookup-only: 'false'
- name: restore cached node_modules
if: ${{ steps.screenshots_cache.outputs.cache-hit != 'true' }}
uses: ./.github/actions/cache-node-modules
- name: cache storybook build result
if: ${{ steps.screenshots_cache.outputs.cache-hit != 'true' }}
id: storybook_result_cache
uses: ./.github/actions/cache-storybook-build-result
- name: prepare for screenshots
if: ${{ steps.screenshots_cache.outputs.cache-hit != 'true' }}
uses: ./.github/actions/prepare-screenshots
- name: take screenshots
if: ${{ steps.screenshots_cache.outputs.cache-hit != 'true' }}
run: yarn storybook:screenshot --shard=${{ matrix.shard }}
- name: upload screenshots
uses: actions/upload-artifact@v3
with:
name: expected-screenshots
path: __screenshots__
retention-days: 1
2. VRT実行および結果レポート生成
参照スナップショットとテスト対象スナップショットの生成ジョブが成功した場合、VRTを実行します。
実行後に予め設定したGitHub Pagesの公開元ブランチgh-pages
ブランチにレポートをpushします。
GitHub Pagesの事前設定内容
予め公開元ブランチを用意した上、Settings > Pages
にて以下のような設定をしてください。
また、Settings > Actions > General
にてワークフローの権限をread/writeに変更してください。
run_reg_suit:
needs:
- take_expected_screenshots
- take_actual_screenshots
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: volta-cli/action@v4
- uses: actions/checkout@v3
with:
fetch-depth: 0
# cacheから参照スナップショットを取得
- name: restore cached expected screenshots
id: screenshots_cache
uses: ./.github/actions/cache-screenshots
with:
lookup-only: 'false'
# cacheにない場合はArtifactsから参照スナップショットを取得
- name: download expected screenshots
if: ${{ steps.screenshots_cache.outputs.cache-hit != 'true' }}
uses: actions/download-artifact@v3
with:
name: expected-screenshots
path: __screenshots__
# 取得した参照スナップショットを参照用画像群に移動
- name: set reg-suit expected
if: ${{ steps.screenshots_cache.outputs.cache-hit != 'true' }}
run: |
rm -rf .reg/expected/
mkdir -p .reg/expected
mv -f __screenshots__/* .reg/expected/
ls -l .reg/
# Artifactsからテスト対象スナップショットを取得
- name: download actual screenshots
uses: actions/download-artifact@v3
with:
name: actual-screenshots
path: __screenshots__
- name: restore cached node_modules
if: ${{ steps.screenshots_cache.outputs.cache-hit != 'true' }}
uses: ./.github/actions/cache-node-modules
- name: workaround for detached HEAD
run: |
git checkout ${GITHUB_HEAD_REF#refs/heads/} || git checkout -b ${GITHUB_HEAD_REF#refs/heads/} && git pull
# VRT実行
- name: run reg-suit
run: yarn test:vrt
# GitHub Page公開元ブランチへレポートをpush
- name: deploy report to github page
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: .reg/
destination_dir: ${{ github.head_ref }} # PR毎にディレクトリを分けることで上書きを防ぐ
- name: find comment
uses: peter-evans/find-comment@v2
id: fc
with:
issue-number: ${{ github.event.pull_request.number }}
comment-author: 'github-actions[bot]'
body-includes: reg-suit report
- name: upsert comment
uses: peter-evans/create-or-update-comment@v3
with:
comment-id: ${{ steps.fc.outputs.comment-id }}
issue-number: ${{ github.event.pull_request.number }}
body: |
reg-suit report
https://tacrew.github.io/vrt-with-github-sample/${{github.head_ref}}
edit-mode: replace
おわりに
本記事ではGitHubのみを利用してVRTを実行し、その結果を閲覧できる環境の構築方法を紹介しました。
VRTをちょっと試してみたいけどS3等を用意するのが手間...と感じていた方への朗報となれば幸いです!
冒頭にも追記しましたが、Cybozu Frontend Advent Calendar 2023の12日目にkorosuke613さんも近い内容の記事を公開しています!
GitHub Actions のキャッシュを使った VRT のすゝめ
スナップショットをどこに置くかの選択肢およびメリデメ等、本記事でカバーしていない内容も記載されています。
ぜひ参考にしてください!
明日はOSS活動など社内外で活躍しているエンジニア、yoshi2no55さんがNext.jsのMiddlewareを利用したIP制限のかけ方について記事を書いてくれます!乞うご期待
おまけ
PRを閉じた後のVRT実行結果レポートをGitHub Pagesから削除するためのワークフローも紹介します。必要に応じて使用してください。
レポート削除ワークフロー
name: clean-vrt-report-branch
on:
pull_request:
types: [closed]
defaults:
run:
shell: bash
jobs:
delete-dir:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
ref: gh-pages
fetch-depth: 0
- name: delete closed pr branch directory
run: |
if [[ "${{ github.head_ref }}" != "" ]]; then
rm -rf "./${{ github.head_ref }}"
fi
- name: check if there were changes
id: git_diff
run: |
git diff --exit-code || echo "changed=true" >> $GITHUB_OUTPUT
- name: Commit and push
if: steps.git_diff.outputs.changed == 'true'
run: |
git config --local user.email "action@github.com"
git config --local user.name "GitHub Action"
git add -A
git commit -m "delete ${{ github.head_ref }} directory"
git push