← 戻る

VRT自動修正ループで学んだフレームワーク移行時の視覚差異パターン


Next.js → Astro 移行でPlaywright VRTを使い、33テストケース(11ページ×3BP)の差分を自動修正するループを回した。得られた知見。

アーキテクチャ: スキル(司令塔)+ エージェント(修正者)

/vrt スキル (orchestrator)
├── pnpm test:vrt → JSON結果パース
├── 失敗ページをグルーピング
├── vrt-fixer エージェント(ページ単位で並列起動)
├── 再テスト → 収束判定
└── 結果レポート

エージェントにはページ名・差分情報(高さ差・ratio)を渡し、Astro側のコードだけを修正させる。

実証済み差異パターン(頻度順)

パターン症状原因修正
scroll animation属性セクション全体が白data-anim="fadeUp" → Playwrightでopacity:0のまま属性削除 or CSS注入でopacity:1強制
Tailwind preflight height:auto画像の高さが数px違うSVGのheight属性がpreflightで上書きインラインstyleで固定
next/image vs <img>コンテナサイズ差Image最適化ラッパーの有無CSS注入でvisibility:hidden
DOMPurify正規化リッチテキストの行間差Next.jsはDOMPurifyで空白除去、移行先は生HTMLnormalizeRichEditorHtml()で空白除去
alt属性の不一致CSS注入が片側のみ有効移行時にalt文字列を変えてしまうセレクターに両方のalt値を追加
whitespace-pre-lineテキスト行数が違う移行先でクラスを追加してしまった該当クラス削除
クライアントUIの欠落インタラクティブ要素がないReactコンポーネント未移植vanilla JS or island で再実装

VRT CSS注入が必須

両サーバーのスクリーンショットに共通CSSを注入して動的要素を安定化する。これがないとテストが不安定になる。

const HIDE_CSS = `
  [data-anim] { opacity: 1 !important; transform: none !important; }
  .swiper-wrapper, time { visibility: hidden !important; }
`;
await page.addStyleTag({ content: HIDE_CSS });

高さクリッピング戦略

next/imageのsubpixel差が累積して数px〜数十pxの高さ差になる。MAX_HEIGHT_DIFF_PX以内なら短い方にクリップして比較する。これにより寸法不一致エラーを回避しつつ、大きな構造差異は検出できる。

学び

  • 共通コンポーネントの差異を最初に潰す(Footer 3px差 → 全33テストに波及)
  • 環境変数の設定漏れは最大の偽陽性源(CMS APIキー未設定 → 15テスト失敗)
  • display:none vs visibility:hidden — 前者はレイアウトフローを変える。画像非表示はvisibility:hiddenが安全
  • VRT CSS注入リストの更新も正当な修正手段。コード修正と並行して使う