9
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Storybook】Chromaticで生成するスナップショット数を最適化するTurboSnap

Last updated at Posted at 2023-09-25

Chromatic

ChromaticはStorybookに対するクラウドベースのツールチェーンです。
StorybookをCDN上に公開したり、UIのテスト、レビュー、ドキュメント化を行うようなワークスペースを提供します。

image.png
引用: https://www.chromatic.com/docs/

ChromaticはStorybook上で行うインタラクションテストの実施とビルド前の状態からビルド後の状態で見た目の変化を検知するビジュアルテストを実施します(比較対象となるビルド前の状態はこちらの方法で選ばれます)。
さらに、Chromaticは開発者だけではなくデザイナーやプロジェクトマネージャーを巻き込んでUIコンポーネントを組み立てるする場を提供してくれます。具体的には、StorybookをCDN上に公開してStorybookの共有を可能に、UIテストによって検知した差分をクラウド上でレビュー可能に、過去の変更やコンポーネントの一覧を可視化してくれます。

ChromaticのビルドはGitHub ActionsのようなCIを用いて行われることが多いです。UIテストで比較対象になるコミットは、Gitの履歴上でビルドされた一番近い位置ものが採用されます。

スナップショット

Chromaticにはスナップショットという概念があります。これはChromaticによってStoryをメタ画像として生成したものを指します。

スクリーンショット 2023-09-22 16.04.04.png

スナップショットはあくまでメタ画像なので実際にブラウザがペインティングする見た目とは異なりますが、スナップショット間における見た目の一貫性は保たれます。そして、スナップショットは主にビジュアルテストを行う際に見た目の比較に使われます。

このようなスナップショットの生成数に応じてChromaticではプランや請求額が決まります(プランによってはCustom DomainやIPに対する特典もあります)。
スクリーンショット 2023-09-21 16.58.13.png
スナップショットはビルドのたびに生成されます。スナップショットは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では以下のようにwithonlyChangedを加えることで有効になります。

.github/workflows/chromatic.yml
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の文字とその左に青円が現れます。
スクリーンショット 2023-09-22 16.36.30.png
青円が表示されているときはTurboSnapによる最適化が行われています。周辺をホバーすることでどれほどのスナップショットが生成され、削減されたのかを確認できます(画像では全てのStoryがスキップされています)。
package.jsonを変更した場合はTurboSnapによる最適化が行われないので円の色が灰色になります。こちらもホバーすることで生成したスナップショット数となぜ行われなかったかが表示されます。
スクリーンショット 2023-09-22 16.44.45.png

TurboSnapが有効であることはワークスペース以外では、ビルド時に出力されるメッセージからも確認できます。

Vite

TurboSnapを利用する場合はwebpackを用いる必要があると書きましたが、vite-plugin-turbosnapを導入することでViteを用いたプロジェクトでもTurboSnapを利用できます。

まず、プラグインをdevDependenciesに導入します。

npm i -D vite-plugin-turbosnap

そして、StorybookにおけるViteの設定としてviteFinalpluginsに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.jsonpackage-lock.jsonpnpm-lock.yamlなどの依存ファイルの変更
  • .storybook/main.tsのようなStorybookの設定ファイルの変更
  • .storybook/preview.tsが利用するファイルの変更
  • Storybookで参照するstaticフォルダの変更
  • ビルドの再実行
  • Chromatic内のレンダリングエンジンのアップグレード

さらに、変更があったときにスキップするファイルをexternalsオプションを使って指定できます。
src/theme.tscss配下のファイルを変えた時にTurboSnapをスキップさせる場合は以下のように指定します。

chromatic --only-changed --externals "src/theme.ts" --externals "*.css"

スナップショットの生成に関与しないファイルを指定する

TurboSnapによるスナップショットの生成はStoryに関連するファイルが1つでもあれば行われます。しかし、視覚的な変化を及ぼさないことが明確なファイルも存在します。
そのようなファイルがあればuntracedフラグを用いて、スナップショットの生成に関与しないファイルとして指定できます。

chromatic --only-changed --untraced "backend/**"

視覚的な変化を及ぼすファイルをこれで指定してしまうと、生成すべきスナップショットを生成できない可能性があります。確実に影響がないファイルだけを指定するなど、利用には注意してください。

おわりに

Chromaticはコンポーネントベースの開発で大活躍をしてくれる便利なツールです。しかし、開発規模が大きくなると一度のビルドで大量のスナップショットが生成されるため、予想外の金額的なコストがかかります。
この記事で紹介したTurboSnapを利用することで、1度のビルドで生成するスナップショット数を最適化して安定します。採用してみてはいかがでしょうか。

9
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?