🌄

Visual Regression Testをサポートするreg-actionsをリリースした

2023/11/14に公開

Visual Regression Test(以下VRT)をやろうと思うと画像をどこに保存するかを検討する必要がでてくるケースがある。

(web アプリケーションのVRTを前提とすると)多くの場合、テキスト形式である*.snapとは異なり、画像取得時のOSfontブラウザのversionなどにより差分がでやすくなってしまう。そのため画像はCIなど環境を極力そろえた状態で取得し、S3などに上げVRT対象の画像を管理するケースがみられる。

今回はこのフロー・管理の簡略化を目指しactionsを作成・リリースした。

成果物

repositoryは以下。yamlに後述するstepを記述すれば使用できる。

https://github.com/reg-viz/reg-actions

セットアップ

最小の記述は以下となる。これで./images以下の画像に対してVRTを行ってくれる。VRTに必要な画像管理はactionsが受け持ち、PRごとにレポートをコメントする。

ci.yaml
name: CI

on: [push, pull_request]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: reg-viz/reg-actions@v2
        with:
          github-token: "${{ secrets.GITHUB_TOKEN }}"
          image-directory-path: "./images"

サンプル

簡単な動作は以下のrepositoryで確認ができる。
現状はPRJob Summaryの両方にコメントされるが、将来的にはdisableにできるフラグも用意しておくつもり。

https://github.com/bokuweb/reg-actions-example

以下のサンプルは最小限のセットアップにおいて画像に差分を発生させてみたサンプルだ。

PRコメント

コメントサンプル
comment

JobSummary

サマリーサンプル
summary

どのように動作するか

以下の手順で動作する。大前提として画像はすべてartifactとしてgithubにアップロードする。

  1. actionsに渡される情報から比較対象とするcommitを求め、そこからrunIdを取得する
  2. runIdから比較対象となるartifactを取得する
  3. 画像を比較し、結果をartifactとしてアップロードする
  4. 差分がある場合、指定ブランチにコメントのための差分画像をpushする(disable-branchfalseの場合)
  5. コメントする

1. actionsに渡される情報から比較対象とするcommitを求め、そこからrunIdを取得する

github actionsではprocess.env.GITHUB_EVENT_PATHに配置されたjsonを読むことでpull_requestの情報などを取得できる。

これをもとにbaseブランチをもとめ、比較対象とすべきcommitを算出する。

2. runIdから比較対象となるartifactを取得する

1commitが算出されたらrunIdを取得し、そこに紐付くartifactyamlに設定されたartifact-nameをもとに download する。

3. 画像比較を行い、結果をartifactとしてアップロードする

2で download した画像yamlに設定されたimage-directory-pathreg-viz/reg-clicompareを用いて比較し、差分画像とレポートを生成する。その後artifactとして upload する。

抜粋だが、upload は以下のように行える。

import * as artifact from '@actions/artifact';

const client = artifact.create();

await client.uploadArtifact(artifactName, files, workspace())

4. 差分がある場合、指定ブランチにコメントのための差分画像をpushする(disable-branchfalseの場合)

以前から PR のコメントに直接画像を貼り付けたいと考えていたが、その場合画像の保存先が問題となる。S3などをわざわざ用意したくない。というのがこのactionのモチベーションでもあるためだ。しかし、artifactzipであるため、その中に含まれる画像は直接コメントに貼り付けることはできない。

そのため、本actionではs0/git-publish-subdir-actionを参考にrepositoryのブランチを間借りしてしまう案を採用した。
つまり画像に差分が発生した場合はbranchoptionで指定したbranchに差分画像をpushし、そのURLをコメントに貼り付ける。これによりprivate repositoryでも使用が可能となる。

この機能は個人的には気に入っているものの、「勝手に push されたくない...」というケースもあるように思う。(なんなら自分で作ったものでなければ自分もそう思うかもしれない。)その場合disable-branchtrueにしておけばpushはされなくなる。

ただし、その場合画像がコメントに貼り付けられなくなるので、差分を確認するには自分でartifactを 手元にdownload したり、レポートを自分で upload する必要がでてくるかもしれない。(すでにそのような使い方をしているユーザもいるようだ。)

このようなケースのためにgh extensionを用意し、コマンド一発でreg-cliのレポートが開くような工夫をしてもいいのかな。と感じている。

5. コメントする

ここまでくれば、あとはコメントするだけだ。最近Job Summariesにもmarkdownを投稿できることを知ったので、こちらにもレポートが投稿されるようにした。
これも手軽に行える。

import { summary } from '@actions/core';

await summary.addRaw(`# Hoge`).write();

Tips

画像の取得について

storybookと合わせてVRTを行っている方も多い認識だが、その場合はreg-viz/storycapを使用すれば簡単にstoryごとの画像が取得できる。

また、test-runnerが入っている場合は以下のようにpostRenderで取得しても良い。

test-runner.js
const { resolve } = require('path');
const { writeFile } = require('fs').promises;

module.exports = {
  async postRender(page, context) {
    const image = await page.screenshot();
    await writeFile(resolve([YOUR_DIR], `${context.id}.png`), image);
  },
};

比較対象の制御について

デフォルトの挙動としては前述したようにPRのマージ先から自動的に算出するが、これを固定したり自分で割り出したい場合はtarget-hashoption を使用することで指定できる。また、以下のようにgithub scriptを使用して算出も可能だ。

ci.yaml
- uses: actions/github-script@v5
  id: target
  with:
    result-encoding: string
    script: |
      return (Math.random() > 0.5) ? "59b7802" : "34f93ed"
- uses: reg-viz/reg-actions@v2
  with:
    github-token: "${{ secrets.GITHUB_TOKEN }}"
    image-directory-path: "./images"
    target-hash: "${{ steps.target.outputs.result }}"

一つのPRで複数のVRTを行う

以下のようにartifact-nameをユニークに設定することで1つのPRで複数のVRTを行うことが可能となる。弊社もe2e用とinteraction用で分けてVRTを行っているケースがある。

  • e2e用
e2e.yaml
- uses: reg-viz/reg-actions@v2
  with:
    github-token: '${{ secrets.GITHUB_TOKEN }}'
    image-directory-path: 'e2e_images'
    artifact-name: 'e2e'
  • interaction用
interaction.yaml
- uses: reg-viz/reg-actions@v2
  with:
    github-token: '${{ secrets.GITHUB_TOKEN }}'
    image-directory-path: 'interaction_images'
    artifact-name: 'interaction'

TODO

まだいくつかタスクは積んでいてちょっとずつ作業はしていくつもりだ。主に以下のようなことを考えている。

  • reg-viz/reg-cliwasm
  • webpの対応
  • retention-daysoption の追加
  • gh extensionの用意

やはりNode.jsでこういったツールを書くのはセキュリティ面で担保するのが難しいな。とたびたび思う。今でこそPermissionsがあるが、だったらいっそのことwasm化してやったほうがいいのではないかと考えている。

また、画像サイズを小さくすることにはメリットがあるのでwasm化と合わせwebpにも対応できたら良いなと考えている。

現状の仕組みではbranchの画像が増えていってしまうのでなるべく早くretention-daysは用意するつもり。

まとめ

reg-actionsを紹介した。社内で使用しはじめているが、なかなか快適で、TODOにあるタスクを粛々と進めていきたい。

以上。

FRAIMテックブログ

Discussion