📚

Visual Regression Test をして差分をAIに食わせる

に公開

背景

CursorなりClaudeCodeでフロントエンド開発をしている際に、見た目は壊さずにロジックやパフォーマンスだけ調整したいシーンがあった。
PlaywrightやPlaywright MCPでブラウザの操作やE2Eテストはできるが、見た目が微妙に壊れている、という判断ができない。

HTML要素としては壊れていないが、段組みがズレている、文字が改行されてしまっている、レイアウトが崩れている、などが検知できない。
HTMLやCSSではなくレンダリングされた見た目、画面表示としての差分比較ということでVisual Regression Testのツールを利用できないだろうか?

※この記事で使っている開発AIツールはClaudeCode + Opus です。

結論

  • Playwright + reg-suit で差分画像を作ってAIに渡すことはできる
  • 差分画像だけでもそれっぽいことは言えるが、怪しい
  • ソースコードとコミットログがあれば精度は上がる
  • 実用的なユースケースでどこまで使えるかはまだまだ要検証

VRTの手段

2023年にVisual Regression Testingを始めるならどんな選択肢があるか

Storybook + Chromatic

StorybookのホスティングサービスであるChromaticを利用するとビルドごとのStoryのキャプチャが自動で残り、差分比較機能もある。
有料SaaSであり、local動作して差分画像が得られる手段ではないので除外。

reg-suit

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

画像の差分比較をするOSSのCLIツール。

プラグインが色々あり、手軽にCIに組み込めるらしい。
https://github.com/reg-viz/reg-suit?tab=readme-ov-file#plugins

CLIとして使う分には reg init して所定のフォルダに画像を配置。
reg compare すれば差分画像とレポートHTMLが作成される。

今回の検証作業用にコミットハッシュ2つを指定してキャプチャ取得と差分比較を行うスクリプトと、レポートHTMLを一括で見るための補助UIを作成して利用した。

reg-suit自体は画像比較ツールなので、アプリのキャプチャは別途用意する必要がある。
Playwrightでブラウザ操作してキャプチャするか、Storybookを利用しているならstorycapなどが使える。今回の作業ではPlaywrightを利用している。

やってみる

  • 適当なTODOアプリを生成する
  • コミットしながらUIを調整する
  • それぞれのバージョンでキャプチャをとり、差分画像を作成する
  • 差分画像とソースコードを段階的にClaudeCodeに渡して、何が変わったかを考えさせる

題材となるアプリの作成

適当にTwitter風アプリを生成。

UIを微調整

目視ではほとんどわからないが、縦方向の余白を少しだけ広げている。

差分画像作成

比較するバージョンでアプリを起動してPlaywrightでキャプチャをとり、 reg compare で差分画像とレポートを作成。

レポートから眺めてみる

ClaudeCodeに渡して解釈できるか試す

/clear で毎回コンテキストをリセットしながら次のように段階的に実験する。

  1. 上で作成したBefore, After, 差分の画像のみを渡す
  2. 画像に加えてソースコードの参照を指示する

画像のみを渡した場合

最初の指示:

UI改修のBefore/Afterの画像と差分画像から、どういう変更が加わったかを説明してください。
差分画像ではBefore と Afterでズレがあった場所が赤く表示されています。
ソースコードやコミットログは絶対に見てはいけません。
画像だけを見て判断しなければいけません。

まず、Before画像を説明してください。
次にAfter画像を説明してください。
次にDiff画像を説明してください。
最後に、どういう変更があったか推測してください。

vrt-tools/vrt-results/padding-test-20250703_091220/.reg/
├── actual
│   └── target-page-desktop.png   // 現在のUIの画像
├── diff
│   └── target-page-desktop.png   // 差分画像
└── expected
   └── target-page-desktop.png    // 期待するUIの画像

結果:

⏺ Before画像の説明

  TwitterのようなSNSのタイムライン画面です。6つの投稿が表示されており、各投稿
  には青いプロフィールアイコン、ユーザー名、投稿時間、投稿内容、そしてコメント
  数・リツイート数・いいね数・共有ボタンが配置されています。

  After画像の説明

  Before画像とほぼ同じレイアウトですが、全体的に各投稿の配置が微妙に変更されて
  います。投稿の内容や構成要素は同じですが、投稿間の間隔や各要素の位置がわずか
  に異なっています。

  Diff画像の説明

  差分画像では、各投稿の周囲全体が赤く表示されています。特に投稿の上下の境界線
  、テキスト部分、アイコンやボタンの位置などに赤い差分が現れており、UI要素の位
  置が全体的にずれていることを示しています。

  推測される変更内容

  投稿間のパディング(余白)やマージンが変更されたと推測されます。各投稿の高さ
  や、投稿内の要素間のスペーシングが調整され、より広い間隔でレイアウトされるよ
  うになったようです。これにより全体的に見やすさが向上した可能性があります。

投稿間のパディング(余白)やマージンが変更された

画像上の変化としては正しい。
ソースコードを見ていないので具体的な変更内容には言及できない。

ちなみに「最初に各画像を説明してください」という指示を与えなかった場合、全く見当違いの回答だった。

画像とソースコードを渡した場合

最初の指示:

UI改修のBefore/Afterの画像と差分画像、ソースコードから、どういう変更が加わったかを説明してください。
差分画像ではBefore と Afterでズレがあった場所が赤く表示されています。
コミットログは絶対に見てはいけません。gitコマンドは使用禁止です。

まず、Before画像を説明してください。
次にAfter画像を説明してください。
次にDiff画像を説明してください。
最後に、どういう変更があったか推測してください。

vrt-tools/vrt-results/padding-test-20250703_091220/.reg/
├── actual
│   └── target-page-desktop.png   // 現在のUIの画像
├── diff
│   └── target-page-desktop.png   // 差分画像
└── expected
   └── target-page-desktop.png    // 期待するUIの画像

結果:

⏺ Before画像の説明

  ツイッター風のソーシャルメディアインターフェースです。画面上部に「ホーム」タ
  イトルがあり、その下に「いまどうしてる?」というプレースホルダーのツイート投
  稿エリアと青い「ツイートする」ボタンがあります。6つの日本語ツイートが表示さ
  れており、各ツイートには丸いアバター(イニシャル表示)、ユーザー名、投稿時間
  、ツイート本文、そしてリプライ・リツイート・いいね・共有のアクションボタンが
  配置されています。

  After画像の説明

  基本的なレイアウトとコンテンツは同じですが、各ツイートカードの縦方向の間隔が
  微妙に変更されています。特に、ユーザー名エリアとツイート本文の間、そしてツイ
  ート本文とアクションボタンの間のスペーシングに変化が見られます。

  Diff画像の説明

  差分画像では、各ツイートカードの全体に赤い枠線が表示されています。これは、ツ
  イートカード内の要素(ユーザー情報部分、本文、アクションボタン行)の垂直方向
  の位置が変更されたことを示しています。すべてのツイートで一貫して同じ箇所に差
  分が検出されています。

  推測される変更内容

⏺ Read(demo-app/src/components/Tweet.css)
  ⎿  Read 74 lines (ctrl+r to expand)

⏺ ソースコードの分析から、以下の変更が加えられたと推測されます:

  Tweet.cssの26行目と45行目のmargin-bottomの値が変更された

  具体的には:
  - .tweet-headerのmargin-bottom(26行目)
  - .tweet-textのmargin-bottom(45行目)

  これらのマージン値が調整されたことで、ツイートカード内の要素間の垂直方向のス
  ペーシングが変化し、差分画像で赤い枠線として検出されています。微細なパディン
  グ調整により、UI全体の縦方向のリズムと読みやすさが改善されたものと考えられま
  す。

画像上の変化としては正しい。
ソースコードに言及しているが、微妙に誤っている。

ちなみにコミットログの参照を許して確認させた結果は以下の通り。

⏺ 推測の答え合わせ

  私の推測は部分的に正しかったですが、変更箇所が異なっていました。

  実際の変更内容:
  - Timeline.cssの.tweet-composeのpadding値が変更された
  - 変更前:padding: 16px;(上下左右すべて16px)
  - 変更後:padding: 16px 16px 20px 16px;(下のみ20pxに増加)

  つまり、ツイート入力エリアの下余白が4px増加したことが実際の変更でした。

  私は個々のツイートカード内の要素間スペーシング(Tweet.css)の変更と推測しま
  したが、実際はツイート投稿エリア全体の下パディング(Timeline.css)の変更でし
  た。これにより、投稿エリアと最初のツイートの間隔が広がり、全体的なツイートの
  位置が下にずれたため、差分画像ですべてのツイートに赤い枠線が表示されたのです。

まとめ

VibeCodingでUIを作る際、Playwright (MCP)を使った動作確認は広く知られているが、実際に表示される見た目(画像)の確認はPlaywrightだけではやりきれない。
そこで reg-suitを組み合わせて、変更前後のキャプチャと差分画像を作成し、これをAIに渡すことで何が変わったか検知できるかを検証した。
結果は上記の通り、画像だけでは完璧とはいかないがある程度特定できた。

今回は簡単なUIかつ、直接的に padding を変更するという条件だったが、実際に検知したい用途だと「直接レイアウトをいじるつもりじゃなかったが、何かの拍子に狂った」というもの。例えばスクロールを修正したら横幅がずれて全体の表示が微妙に狂う、など。そういう実践的というか、直接的な変更でないケースにどこまで対応できるかは、まだ要検証。

精度と処理時間を考えると、VibeCoding時に頻繁に実行する手段としてはイマイチ。
プルリクなどある程度開発が完了したタイミングで実行するのはワークしそうだが、それは従来通りの話。

もっと良いツールや手法があればTwitterやZennでコメント頂けると幸いです。

Discussion