📚

[備忘録] vitest workspaceを利用してstorybook testを行ってハマった

2025/01/30に公開

構成

apps
├ storybook
├ web
...

モノレポの構成は次の2つの記事を参照
https://github.com/vercel/turborepo/tree/main/examples/basic
https://zenn.dev/inurun/articles/3e3af5f8a1a0c0#comment-16c09d18f8bba9

ハマりポイント 各パッケージ配下で実行したときとworkspace rootで実行したときでprocess.cwdが異なる

workspace rootでテストを実行すると

pnpm exec vitest run

process.cwdはプロジェクトルートになる

各パッケージ配下で実行すると

pnpm --filter storybook test

process.cwdapps/storybookとなる

これが原因で、各パッケージ配下で動いたのに、あらためてworkspace rootでvitest runすると動かなくなる。

storybookのvitest-pluginの設定

サンプルコードをみると次のように書かれてあるが、workspace rootから実行するとstorybookTest({ configDir: '.storybook' })のパス解決がうまくいかない。

/// <reference types="@vitest/browser/providers/playwright" />

import { defineProject } from 'vitest/config';
import { storybookTest } from '@storybook/experimental-addon-test/vitest-plugin';
import { storybookNextJsPlugin } from '@storybook/experimental-nextjs-vite/vite-plugin';

export default defineProject({
  plugins: [
    // See options at: https://storybook.js.org/docs/writing-tests/vitest-plugin#storybooktest
    storybookTest({ configDir: '.storybook' }),
    // More info at: https://github.com/storybookjs/vite-plugin-storybook-nextjs
    storybookNextJsPlugin(),
  ],
  test: {
    globals: false,
    browser: {
      enabled: true,
      headless: true,
      provider: 'playwright',
      // エラーが発生するためそれまでは、deprecatedだがinstancesではなくname指定を行う
      // エラー内容: Cannot define a nested project for a chromium browser. The project name "chromium" was already defined.
      // issue: https://github.com/storybookjs/storybook/issues/30363
      // instances: [{ browser: 'chromium', name: 'chromium' }],
      name: 'chromium',
    },
    setupFiles: ['.storybook/vitest.setup.tsx'],
  },
});

対策: vitest.configをローカル実行用とworkspace root実行用の2つに分ける

ローカル実行用

apps/storybook/vitest.config.local.mts
/// <reference types="@vitest/browser/providers/playwright" />

import { defineProject } from 'vitest/config';
import { storybookTest } from '@storybook/experimental-addon-test/vitest-plugin';
import { storybookNextJsPlugin } from '@storybook/experimental-nextjs-vite/vite-plugin';

export default defineProject({
  plugins: [
    // See options at: https://storybook.js.org/docs/writing-tests/vitest-plugin#storybooktest
    storybookTest({ configDir: '.storybook' }),
    // More info at: https://github.com/storybookjs/vite-plugin-storybook-nextjs
    storybookNextJsPlugin(),
  ],
  test: {
    globals: false,
    browser: {
      enabled: true,
      headless: true,
      provider: 'playwright',
      // エラーが発生するためそれまでは、deprecatedだがinstancesではなくname指定を行う
      // エラー内容: Cannot define a nested project for a chromium browser. The project name "chromium" was already defined.
      // issue: https://github.com/storybookjs/storybook/issues/30363
      // instances: [{ browser: 'chromium', name: 'chromium' }],
      name: 'chromium',
    },
    setupFiles: ['.storybook/vitest.setup.tsx'],
  },
  // エラー対策 https://zenn.dev/coji/articles/a8508bae1d8fa6
  optimizeDeps: { exclude: ['@mapbox/node-pre-gyp'] },
});

apps/storybook/package.json
{
  "name": "storybook",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "storybook dev -p 6006",
    "build": "storybook build",
    "lint": "eslint .",
    "test:coverage": "vitest --coverage  --config vitest.config.local.mts",
    "test": "vitest --config vitest.config.local.mts"
  }
}

workspace root実行用

vitest.config.mts
/// <reference types="@vitest/browser/providers/playwright" />

import { defineProject } from 'vitest/config';
import { storybookTest } from '@storybook/experimental-addon-test/vitest-plugin';
import { storybookNextJsPlugin } from '@storybook/experimental-nextjs-vite/vite-plugin';

export default defineProject({
  plugins: [
    // See options at: https://storybook.js.org/docs/writing-tests/vitest-plugin#storybooktest
    storybookTest({ configDir: 'apps/storybook/.storybook' }),
    // More info at: https://github.com/storybookjs/vite-plugin-storybook-nextjs
    storybookNextJsPlugin(),
  ],
  test: {
    globals: false,
    browser: {
      enabled: true,
      headless: true,
      provider: 'playwright',
      // エラーが発生するためそれまでは、deprecatedだがinstancesではなくname指定を行う
      // エラー内容: Cannot define a nested project for a chromium browser. The project name "chromium" was already defined.
      // issue: https://github.com/storybookjs/storybook/issues/30363
      // instances: [{ browser: 'chromium', name: 'chromium' }],
      name: 'chromium',
    },
    setupFiles: ['.storybook/vitest.setup.tsx'],
  },
});

vitest workspaceの設定はこちらを参照する

vitest.config.mts
import { defineConfig } from 'vitest/config';

export default defineConfig({
  test: {
    workspace: [
      './apps/**/vitest.config.mts', // ここでworkspaceを設定
      './packages/**/vitest.config.mts',
    ],
    include: ['apps/**/*.spec.{ts,tsx}', 'packages/**/*.spec.{ts,tsx}'],
    exclude: ['node_modules', 'dist', '.next', 'coverage', '.turbo'],
    coverage: {
      provider: 'v8', // または 'istanbul'
      reporter: ['text', 'html', 'lcov'], // 必要なカバレッジレポート形式
      all: true, // 全てのファイルを対象
      include: [
        '**/src/**/*.{ts,tsx}',
        '**/lib/**/*.{ts,tsx}',
        '**/stories/**/*.{ts,tsx}',
        '**/app/**/*.{ts,tsx}',
      ], // カバレッジ計測対象
      exclude: [
        '**/node_modules',
        '**/dist',
        'tests',
        '**/.next',
        '**/coverage',
        '**/.turbo',
      ], // 除外対象
    },
  },
});

workspace rootから実行するとpandacssのpostcssでエラーがでる

エラー

headless: trueの場合:

 FAIL   storybook (chromium)  ../xxx.stories.tsx [ xxx.stories.tsx ]
TypeError: Failed to fetch dynamically imported module: http://localhost:63315/xxx/apps/storybook/.storybook/vitest.setup.ts?import&browserv=xxx

headless: falseの場合:

[vite] Internal server error: [postcss] Cannot find config file `panda.config.{ts,js,mjs,mts}`. Did you forget to run `panda init`?

postcss.config.cjscwdオプションを渡す

対策

apps/storybook/postcss.config.cjs
module.exports = {
  plugins: {
    '@pandacss/dev/postcss': {
      // vitest workspaceのルートから起動するとき、cwdが異なることでpanda.config.tsを検知できないため、cwdを指定する
      cwd: __dirname,
    },
  },
};
TRAPE(トラピ)

Discussion