Madogiwa Blog

主に技術系の学習メモに使っていきます。

Ruby on Rails: Capybaraでスクリーンショットを取得してreg-cliで画像比較する簡単なVRT的なのを作るMEMO

Ruby on Railsのsystem specで画面ショットを取りつつmasterとの画像比較してVRT的なことできないかなーと思っていたのですが、Capybaraのpage.save_screenshotreg-cliを使うと実現できそうだったのでメモ📝

ちなみにPlaywrightでは以下の通りtoHaveScreenshotを使うことで、今回試したような簡単にVRTを実施できます。

madogiwa0124.hatenablog.com

※playwright-ruby-clinetを直接使えば出来るかなと思ったけどtoHaveScreenshotには現時点では対応していなさそうだった📝

前提事項

  • rails (7.1.3.2)
  • capybara (3.40.0)
  • capybara-playwright-driver (0.5.1)
    • 試してないですがplaywrightじゃなくても出来ると思います

実現したいこと

Ruby on Railsのsystem specで画面ショットを取りつつmasterとの差分比較して一定の差異があったら落としたいので、以下のような感じでspec/screenshotsのmasterとcompareに、それぞれmasterのものとトピックで取得した画像ファイルを配置し同一パスのファイルを比較して一定の差異があればエラーにします。 ※masterは比較用に初回実行時だけ作成しリポジトリで管理するようにします。(トピックが正になる場合に更新してpushします。)

spec/screenshots
├── compare
│   └── foo
│       └── bar
│           └── baz.png
└── master
    └── foo
        └── bar
            └── baz.png

System Specで任意のタイミングで画面ショットを取得し差分比較用のフォルダに配置する

画面ショットを取得すること自体は、page.save_screenshot(path: Rails.root.join("spec/screenshots/compare/foo/bar/baz")を実行すればいいのですが、差分比較用のフォルダを毎回してするのは面倒なので以下のようなHeplerを用意していい感じのフォルダに配置できるようにしてみました。

module VrtScreenshotHelper
  def vrt_screenshot(page, path:)
    return if ENV["GET_SCREENSHOTS_FOR_VRT"] != "true"
    base_path = Rails.root.join("spec/screenshots/compare")
    page.save_screenshot(base_path.join(path))
  end
end

こうすると以下のように取得できます。

    it "sample example" do
      visit example_path
      expect(page).to have_content "sample title"
      expect(page).to have_content "sample description"
      vrt_screenshot(page, path: "foo/bar/baz.png")
    end

初回実行時にはmasterの画面ショットを作成する必要があるので、以下のように変更してmaster側に画面ショットを取得します。

module VrtScreenshotHelper
  def vrt_screenshot(page, path:)
    return if ENV["GET_SCREENSHOTS_FOR_VRT"] != "true"
-   base_path = Rails.root.join("spec/screenshots/compare")
+   base_path = Rails.root.join("spec/screenshots/master")
    page.save_screenshot(base_path.join(path))
  end
end

取得した画面ショットを比較して一定差分があった場合に落とす

取得した画面ショットを比較して一定差分があった場合に落とすのは以下のreg-cliを使うと簡単に実現することが出来ました。

github.com

以下のように比較するパスと差分を表す画像の出力先を指定し、レポート出力先-R、エラーにする閾値-Tを指定することで先ほど保存したスクリーンショットをmasterを比較し、一定の差分があったらレポート付きでエラーにすることが出来ます。

$ reg-cli spec/screenshots/compare spec/screenshots/master spec/screenshots/diff -R spec/screenshots/diff/report.html -T 0.01

上記はspec/screenshots/comparespec/screenshots/masterを比較し、差分を示す画像をspec/screenshots/diffに配置、1%でも差分があればエラーとし、レポートをspec/screenshots/diff/report.htmlに吐き出すような指定になります。

実際に実行して失敗すると以下のような出力が表示され差分を検知できます。

$ reg-cli spec/screenshots/compare spec/screenshots/master spec/screenshots/diff -R spec/screenshots/diff/report.html -T 0.01 -U

✘ change  spec/screenshots/compare/foo/bar/baz.png

✘ 1 file(s) changed.

差分が発生した場合にはレポートで具体的な差分をブラウザで確認できます。

https://github.com/reg-viz/reg-cli?tab=readme-ov-file#html-report

CircleCIやGitHub Actionでartifactsとして保存するようにすると差分比較がよりしやすくなって良さそうです。

おわりに

reg-cliあまり使ってことなかったのですが、お手軽に画像比較・レポート作成まで出来て非常に便利ですね ✨