storybook + storycap + reg-suit で VRT(Visual Regression Testing) していたのだけど、storycap が長らくメンテナンスされておらず、storybook アップデートの障壁になってしまっていた。なので @storybook/test-runner + storycap-testrun へ移行を行った。移行時に色々やったことのメモ。
npm script
storycap では --serverCmd オプションでサーバーの起動を待っていた。
storycap http://127.0.0.1:6006 --serverCmd 'npx http-server -p 6006 storybook-static' --serverTimeout 60000 --captureTimeout 60000
storycap-testrun ではオプションが無くなったので、npm script をサーバ起動とスクリーンショットで分けた。
"storybook:serve": "http-server storybook-static --port 6006 --silent", "storycap:run": "npx wait-on http://127.0.0.1:6006 && test-storybook --url http://127.0.0.1:6006 --index-json --testTimeout 60000 --maxWorkers=3", "storycap": "run-p --race storybook:serve storycap:run",
test-runner.ts の作成
@storybook/test-runner 用の設定。以下のようにした。
import { getStoryContext, type TestRunnerConfig } from '@storybook/test-runner'; import { screenshot } from '@storycap-testrun/node'; const config: TestRunnerConfig = { async preVisit(page, context) { // ストーリーの viewport パラメーターをそのまま Playwright に反映 const storyContext = await getStoryContext(page, context); const viewport = storyContext.parameters?.screenshot?.viewport; if (viewport) { await page.setViewportSize(viewport); } }, async postVisit(page, context) { await screenshot(page, context, { flakiness: { metrics: { enabled: true, retries: 1000, }, retake: { enabled: true, interval: 500, retries: 10, }, }, }); }, }; export default config;
- storycap 用の viewport パラメータがそのまま使えるように、preVisit で渡すようにした。
- postVisit でやってるのは、まだ描画中だったらスクリーンショット取得を待つための設定。これがめちゃめちゃ便利。 storycap のころは waitFor を使って要素がでてくるのを待つなどしていたが、不要になった。
preview.tsx の修正
storycap 時代では、以下のように、スクリーンショット取得時だけ動く decorator を作って動的な要素を固定していた。
import { isScreenshot } from 'storycap'; const FixDateDecorator: Decorator = (Story) => { if (!isScreenshot()) return Story(); MockDate.set('2025-05-01T00:00:00Z'); return Story(); };
しかし、isScreenshot 関数が使えなくなったので自前で定義した。ヘッドレスブラウザか否かで判定してるので雑だけど運用上はこれで OK。
export const isScreenshot = () => { if (typeof window === 'undefined') return false; // Playwright(test-runner)実行時は navigator.webdriver が自動的に true になる。 return navigator.webdriver === true; };
CI
手元では速いけど、CI 環境で動かすと実行速度が異常に遅い現象が発生した。並列実行数が高くて遅くなってそうなので、test-storybook コマンドで --maxWorkers=3 オプションを指定し、3並列にしたら比較的マシになった。
移行後の所感
メリットとしては、スクリーンショット取得が圧倒的に安定した。waitFor で要素の出現を待つ、のようなことが組み込みのリトライの仕組みでいい感じできるようになって、圧倒的にストレスが減った。 デメリットとしては、実行速度は storycap より若干遅くなった。が、誤差の範囲レベルなのでメリットのほうがかなり大きく、移行して最高!という気持ち。