Chromatic
ChromaticはStorybookに対するクラウドベースのツールチェーンです。
StorybookをCDN上に公開したり、UIのテスト、レビュー、ドキュメント化を行うようなワークスペースを提供します。
ChromaticはStorybook上で行うインタラクションテストの実施とビルド前の状態からビルド後の状態で見た目の変化を検知するビジュアルテストを実施します(比較対象となるビルド前の状態はこちらの方法で選ばれます)。
さらに、Chromaticは開発者だけではなくデザイナーやプロジェクトマネージャーを巻き込んでUIコンポーネントを組み立てるする場を提供してくれます。具体的には、StorybookをCDN上に公開してStorybookの共有を可能に、UIテストによって検知した差分をクラウド上でレビュー可能に、過去の変更やコンポーネントの一覧を可視化してくれます。
ChromaticのビルドはGitHub ActionsのようなCIを用いて行われることが多いです。UIテストで比較対象になるコミットは、Gitの履歴上でビルドされた一番近い位置ものが採用されます。
スナップショット
Chromaticにはスナップショットという概念があります。これはChromaticによってStoryをメタ画像として生成したものを指します。
スナップショットはあくまでメタ画像なので実際にブラウザがペインティングする見た目とは異なりますが、スナップショット間における見た目の一貫性は保たれます。そして、スナップショットは主にビジュアルテストを行う際に見た目の比較に使われます。
このようなスナップショットの生成数に応じてChromaticではプランや請求額が決まります(プランによってはCustom DomainやIPに対する特典もあります)。
スナップショットはビルドのたびに生成されます。スナップショットはStory数と生成するブラウザ数、ビューポイント数の掛け合わせた数だけ生成されます。ブラウザ数とビューポイント数はほとんど据え置きですが、Story数やビルド数は開発規模によって値が大きく変わります。
TurboSnap
バージョン8のリリースで状況が少し変わっているので注意してください。
TurboSnapはChromaticが生成するスナップショットを最適化する機能です。開発規模の肥大化によって増加するスナップショット数を抑えるのに役立つ便利な機能です。
TurboSnapを有効にするには以下の条件を満たす必要があります。
- Chromatic CLI v5.8以上
- Storybook v6.2以上
- Webpack
- Storyの設定が正しくされている
- CI経由で10回以上のビルドが成功し、最低でも一度受け入れられている
- Github Actionsの場合
push
による発火
TurboSnapを有効にすると、ビルドしたコミットと比較するコミットのファイル差分を元にWebpackのdependency-graphを用いて変更に影響のあるStoryのスナップショットだけを生成します。
ただし、比較対象のコミットで生成したビルドのレビューで受け入れられなかったStoryのスナップショットは引き続き生成されます。さらに、package.json
の変更のように全体に影響を及ぼす変更があればTurboSnapの最適化はスキップされ全てのStoryに対してスナップショットが生成されます。
有効化
TurboSnapはCLIでは--only-changed
を追加して有効にします。
chromatic --only-changed
特定のブランチでのみTurboSnapを有効にしたい場合はpicomatchを元に指定します。main
ブランチ以外で有効にする場合は以下のようにします。
chromatic --only-changed "!(main)"
main
ブランチでは最適化を行わずに、全てのStoryに対してスナップショットを生成することでTurboSnapによる漏れを拾えます。
GitHub Actionsでは以下のようにwith
でonlyChanged
を加えることで有効になります。
name: "Chromatic"
on: push
jobs:
chromatic-deployment:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Install dependencies
run: yarn
- name: Publish to Chromatic
uses: chromaui/action@v1
with:
onlyChanged: true
projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
TurboSnapが有効な状態でビルドすると、ワークスペースのTESTS
の右側にTurboSnapの文字とその左に青円が現れます。
青円が表示されているときはTurboSnapによる最適化が行われています。周辺をホバーすることでどれほどのスナップショットが生成され、削減されたのかを確認できます(画像では全てのStoryがスキップされています)。
package.json
を変更した場合はTurboSnapによる最適化が行われないので円の色が灰色になります。こちらもホバーすることで生成したスナップショット数となぜ行われなかったかが表示されます。
TurboSnapが有効であることはワークスペース以外では、ビルド時に出力されるメッセージからも確認できます。
Vite
TurboSnapを利用する場合はwebpackを用いる必要があると書きましたが、vite-plugin-turbosnapを導入することでViteを用いたプロジェクトでもTurboSnapを利用できます。
まず、プラグインをdevDependenciesに導入します。
npm i -D vite-plugin-turbosnap
そして、StorybookにおけるViteの設定としてviteFinal
のplugins
にvite-plugin-turbosnapからインポートしたものを追加することでTurboSnapが有効になります。
import { StorybookConfig } from '@storybook/react-vite';
import { ConfigEnv, loadConfigFromFile, mergeConfig } from 'vite';
import turbosnap from 'vite-plugin-turbosnap';
import path from 'path';
const configEnvServe: ConfigEnv = {
mode: 'development',
command: 'serve',
ssrBuild: false,
};
const configEnvBuild: ConfigEnv = {
mode: 'production',
command: 'build',
ssrBuild: false,
};
const config: StorybookConfig = {
// お好みのStorybookの設定
framework: {
name: '@storybook/react-vite',
options: {},
},
async viteFinal(config, { configType }) {
const isProduction = configType === 'PRODUCTION';
const { config: userConfig } =
(await loadConfigFromFile(
isProduction ? configEnvBuild : configEnvServe,
path.resolve(__dirname, '../vite.config.ts'),
)) ?? {};
return mergeConfig(config, {
...userConfig,
plugins: isProduction
? [turbosnap({ rootDir: config.root ?? process.cwd() })]
: [],
});
},
};
export default config;
Vite環境でTurboSnapを有効にするにはwebpackよりも厳しい条件が課されることに注意してください。
- Chromatic CLI v6.5.0以上
- Storybook v7以上
- Storybook v6.5以上かつ
@storybook/builder-vite
v0.1.22以上
TurboSnapの無効化
TurboSnapによる最適化がスキップされるケースとしてpackage.json
の変更を紹介しました。
その他にもいくつかの条件で最適化がスキップされます。
-
package.json
とpackage-lock.json
、pnpm-lock.yaml
などの依存ファイルの変更 -
.storybook/main.ts
のようなStorybookの設定ファイルの変更 -
.storybook/preview.ts
が利用するファイルの変更 - Storybookで参照する
static
フォルダの変更 - ビルドの再実行
- Chromatic内のレンダリングエンジンのアップグレード
さらに、変更があったときにスキップするファイルをexternals
オプションを使って指定できます。
src/theme.ts
とcss
配下のファイルを変えた時にTurboSnapをスキップさせる場合は以下のように指定します。
chromatic --only-changed --externals "src/theme.ts" --externals "*.css"
スナップショットの生成に関与しないファイルを指定する
TurboSnapによるスナップショットの生成はStoryに関連するファイルが1つでもあれば行われます。しかし、視覚的な変化を及ぼさないことが明確なファイルも存在します。
そのようなファイルがあればuntraced
フラグを用いて、スナップショットの生成に関与しないファイルとして指定できます。
chromatic --only-changed --untraced "backend/**"
視覚的な変化を及ぼすファイルをこれで指定してしまうと、生成すべきスナップショットを生成できない可能性があります。確実に影響がないファイルだけを指定するなど、利用には注意してください。
おわりに
Chromaticはコンポーネントベースの開発で大活躍をしてくれる便利なツールです。しかし、開発規模が大きくなると一度のビルドで大量のスナップショットが生成されるため、予想外の金額的なコストがかかります。
この記事で紹介したTurboSnapを利用することで、1度のビルドで生成するスナップショット数を最適化して安定します。採用してみてはいかがでしょうか。