VRT(storycap, reg-suit, Next.js)を安定させるためのTips
はじめに
社のフロントエンド開発で、品質担保施策の一環としてVRTを実施しています。
単純にコンポーネントをマウントした結果だけではなく、interaction test や msw-storybook-addon を合わせて活用し、pageやmodal単位の振る舞いの変化の結果の担保として、integration test の役割も一部担わせているため、これを安定化させ、オオカミ少年にしないことが重要です。
そのために、ドキュメントされていない点で改善を試行錯誤した部分があるのでまとめておきます。
改善事項
storycapは厳密には play function に対応していないことを意識する
storycapは、正式なサポートとして、play関数のPromiseが解消されたことを確認してから撮影しているわけではありません。storybookにこれが導入される前から、撮影の安定化のために導入されていたMetricsWatcher
これによって、16msごとにdomとstyleの変化を計測しつづけ、これが変化し続ける = レンダリングが終わっていない。と判断し撮影をしないだけです。
なので、play関数の中で長時間 deplay
やsetTimeout
するような挙動を記述していても、これの解決を待たずに撮影されてしまうこともあり、不安定な撮影タイミングにつながります。
この制約を意識して、play関数の内容を調整したり、どうしても複雑な挙動を担保したいものは通常のintegration testを記述すること。
または、以下のように waitFor
を活用して期待するUI要素の出現を待たせることで解決しました。
import { screen } from '@storybook/testing-library';
parameters: {
screenshot: {
waitFor: async () => {
await screen.findByText('このテキストが出てる状態で撮影する');
},
},
}
CSSアニメーションのキャンセル
storycap
のdefaultでdisableCssAnimation
が効いているはずなのですが、アニメーションの途中のタイミングで撮影されてしまうことが多発していました。
そのため、撮影前のタイミングでstorybookのiframe内に確実にstyleが付くように処理を加えることで解決しました。
export const decorators = [
// ..., other decorators
(Story) => {
// https://github.com/reg-viz/storycap/issues/464
if (isScreenshot()) {
function addStyle(styleString) {
const style = document.createElement('style');
style.textContent = styleString;
document.head.append(style);
}
addStyle(`
*,
*::before,
*::after {
transition: none !important;
animation: none !important;
}
input {
caret-color: transparent !important;
}
`);
}
return <Story />;
},
];
Infinite なアニメーションを使用しない
上の項目に関連すると思いますが、css/js問わずアニメーション期限を無限に設定したものが存在すると撮影が開始されずにtimeoutすることがあり、skip
をしても該当コンポーネントでタイムアウトする挙動が残ります。
そのため、storybook上ではアニメーション意図的に切れるように設定するか、storybook上から削除することで解決しました。
Infinite なmsw のリクエストを設定しない
Issueに関連して、delay('infinite')
なqueryがひとつでも存在すると、同一のqueryを使ったstoryが存在し、かつ、撮影タイミングがdelay('infinite')
なものよりも後であった時、全てこれとと同じ状態で撮影されてしまう挙動がありました。
そのため、infinite
なstoryを削除することで解決しました。
iframeを使用しない
iframeの内部の表示についてvisualを担保する必要はないので、skip
し通常のtestを書くことで解決しました。
Next/Imageの挙動安定化
特に、Intersection Observerを使用して、srcを実際のimgタグに渡す/渡さないを制御する形でlazy loadingを実現していた next/legacy/image
の方で顕著でしたが
Next/Imageは内部でdefaultに lazy loading など読み込みを最小化するような処理がされています。
これによって、画像が空の状態で撮影されることが多発していました。
このため、このようなworkaroundを使用するなどして、storybookの起動中はoptimizeが効かないようにすることで解決しました。
画像差分検知の安定化
一部の画像において、全く同じ画像URLかつ、storycap
のdefaultの機能で画像読み込み終了まで撮影をしないように設定されているはずだが、画像の内部で差分が発生してしまうことが起きました。
画像の表示が担保されていればよく、特定の画像でテストする必要はなかったため、mock用のdata uriを用意し、これを各所で使用するように変更することで収めることができました。
mainImage: {
- imageUrl: 'https://storage.googleapis.com/hoge/fuga.jpg',
+ imageUrl: 'data:image/png;base64,iVBORw0KGg ... ',
おわりに
諸々調整したことによって、現状安定した状態でVRTを運用できています。
設定する側としては一定労力が掛かりますが、開発時の体験としてはstorybook駆動にUI開発をしていればそれ自体がテストになってしまうため、コスパ高く開発できるものと考えており、引き続き運用してみようと考えています。
(Next.jsの major version 更新のたびにstorybookが壊れて、それもそれで大変ですなぁ)
Web3スタートアップ「Gaudiy(ガウディ)」所属エンジニアの個人発信をまとめたPublicationです。公式Tech Blog:techblog.gaudiy.com/
Discussion