diff --git a/code/.storybook/main.ts b/code/.storybook/main.ts index 9870741de29e..48109681b6b0 100644 --- a/code/.storybook/main.ts +++ b/code/.storybook/main.ts @@ -160,6 +160,12 @@ const config: StorybookConfig = { // disable sourcemaps in CI to not run out of memory sourcemap: process.env.CI !== 'true', }, + server: { + watch: { + // Something odd happens with tsconfig and nx which causes Storybook to keep reloading, so we ignore them + ignored: ['**/.nx/cache/**', '**/tsconfig.json'], + }, + }, } satisfies typeof viteConfig); }, // logLevel: 'debug', diff --git a/code/addons/test/src/node/vitest-manager.ts b/code/addons/test/src/node/vitest-manager.ts index 4145acf18a3f..5e23088dfcbb 100644 --- a/code/addons/test/src/node/vitest-manager.ts +++ b/code/addons/test/src/node/vitest-manager.ts @@ -14,6 +14,7 @@ import type { TestingModuleRunRequestPayload } from 'storybook/internal/core-eve import type { DocsIndexEntry, StoryIndex, StoryIndexEntry } from '@storybook/types'; +import { findUp } from 'find-up'; import path, { dirname, join, normalize } from 'pathe'; import slash from 'slash'; @@ -23,6 +24,9 @@ import type { StorybookCoverageReporterOptions } from './coverage-reporter'; import { StorybookReporter } from './reporter'; import type { TestManager } from './test-manager'; +const VITEST_CONFIG_FILE_EXTENSIONS = ['mts', 'mjs', 'cts', 'cjs', 'ts', 'tsx', 'js', 'jsx']; +const VITEST_WORKSPACE_FILE_EXTENSION = ['ts', 'js', 'json']; + type TagsFilter = { include: string[]; exclude: string[]; @@ -68,7 +72,13 @@ export class VitestManager { : { enabled: false } ) as CoverageOptions; + const vitestWorkspaceConfig = await findUp([ + ...VITEST_WORKSPACE_FILE_EXTENSION.map((ext) => `vitest.workspace.${ext}`), + ...VITEST_CONFIG_FILE_EXTENSIONS.map((ext) => `vitest.config.${ext}`), + ]); + this.vitest = await createVitest('test', { + root: vitestWorkspaceConfig ? dirname(vitestWorkspaceConfig) : process.cwd(), watch: true, passWithNoTests: false, // TODO: diff --git a/code/addons/test/src/postinstall.ts b/code/addons/test/src/postinstall.ts index bb51dfa3ff1e..cac46c5fa619 100644 --- a/code/addons/test/src/postinstall.ts +++ b/code/addons/test/src/postinstall.ts @@ -417,6 +417,12 @@ export default async function postInstall(options: PostinstallOptions) { dedent` import { defineWorkspace } from 'vitest/config'; import { storybookTest } from '@storybook/experimental-addon-test/vitest-plugin'; + import path from 'node:path'; + import { fileURLToPath } from 'node:url'; + + const dirname = typeof __dirname !== 'undefined' + ? __dirname + : path.dirname(fileURLToPath(import.meta.url)); // More info at: https://storybook.js.org/docs/writing-tests/test-addon export default defineWorkspace([ @@ -426,7 +432,7 @@ export default async function postInstall(options: PostinstallOptions) { plugins: [ // The plugin will run tests for the stories defined in your Storybook config // See options at: https://storybook.js.org/docs/writing-tests/test-addon#storybooktest - storybookTest({ configDir: '${options.configDir}' }), + storybookTest({ configDir: path.join(dirname, '${options.configDir}') }) ], test: { name: 'storybook', @@ -457,13 +463,19 @@ export default async function postInstall(options: PostinstallOptions) { dedent` import { defineConfig } from 'vitest/config'; import { storybookTest } from '@storybook/experimental-addon-test/vitest-plugin'; + import path from 'node:path'; + import { fileURLToPath } from 'node:url'; + + const dirname = typeof __dirname !== 'undefined' + ? __dirname + : path.dirname(fileURLToPath(import.meta.url)); // More info at: https://storybook.js.org/docs/writing-tests/test-addon export default defineConfig({ plugins: [ // The plugin will run tests for the stories defined in your Storybook config // See options at: https://storybook.js.org/docs/writing-tests/test-addon#storybooktest - storybookTest({ configDir: '${options.configDir}' }), + storybookTest({ configDir: path.join(dirname, '${options.configDir}') }) ], test: { name: 'storybook', diff --git a/docs/_snippets/vitest-plugin-vitest-config.md b/docs/_snippets/vitest-plugin-vitest-config.md index 382fe191b76e..e61f9e9f5c9b 100644 --- a/docs/_snippets/vitest-plugin-vitest-config.md +++ b/docs/_snippets/vitest-plugin-vitest-config.md @@ -1,6 +1,11 @@ ```ts filename="vitest.config.ts" renderer="react" import { defineConfig, mergeConfig } from 'vitest/config'; import { storybookTest } from '@storybook/experimental-addon-test/vitest-plugin'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const dirname = + typeof __dirname !== 'undefined' ? __dirname : path.dirname(fileURLToPath(import.meta.url)); import viteConfig from './vite.config'; @@ -10,7 +15,7 @@ export default mergeConfig( plugins: [ storybookTest({ // The location of your Storybook config, main.js|ts - configDir: './.storybook', + configDir: path.join(dirname, '.storybook'), // This should match your package.json script to run Storybook // The --ci flag will skip prompts and not open a browser storybookScript: 'yarn storybook --ci', @@ -35,16 +40,21 @@ export default mergeConfig( import { defineConfig, mergeConfig } from 'vitest/config'; import { storybookTest } from '@storybook/experimental-addon-test/vitest-plugin'; import { storybookVuePlugin } from '@storybook/vue3-vite/vite-plugin'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; import viteConfig from './vite.config'; +const dirname = + typeof __dirname !== 'undefined' ? __dirname : path.dirname(fileURLToPath(import.meta.url)); + export default mergeConfig( viteConfig, defineConfig({ plugins: [ storybookTest({ // The location of your Storybook config, main.js|ts - configDir: './.storybook', + configDir: path.join(dirname, '.storybook'), // This should match your package.json script to run Storybook // The --ci flag will skip prompts and not open a browser storybookScript: 'yarn storybook --ci', @@ -69,6 +79,11 @@ export default mergeConfig( ```ts filename="vitest.config.ts" renderer="svelte" import { defineConfig, mergeConfig } from 'vitest/config'; import { storybookTest } from '@storybook/experimental-addon-test/vitest-plugin'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const dirname = + typeof __dirname !== 'undefined' ? __dirname : path.dirname(fileURLToPath(import.meta.url)); import viteConfig from './vite.config'; @@ -78,7 +93,7 @@ export default mergeConfig( plugins: [ storybookTest({ // The location of your Storybook config, main.js|ts - configDir: './.storybook', + configDir: path.join(dirname, '.storybook'), // This should match your package.json script to run Storybook // The --ci flag will skip prompts and not open a browser storybookScript: 'yarn storybook --ci', diff --git a/docs/_snippets/vitest-plugin-vitest-workspace.md b/docs/_snippets/vitest-plugin-vitest-workspace.md index 99e3e4371c15..9363f21df907 100644 --- a/docs/_snippets/vitest-plugin-vitest-workspace.md +++ b/docs/_snippets/vitest-plugin-vitest-workspace.md @@ -1,6 +1,11 @@ ```ts filename="vitest.workspace.ts" renderer="react" import { defineWorkspace } from 'vitest/config'; import { storybookTest } from '@storybook/experimental-addon-test/vitest-plugin'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const dirname = + typeof __dirname !== 'undefined' ? __dirname : path.dirname(fileURLToPath(import.meta.url)); export default defineWorkspace([ // This is the path to your existing Vitest config file @@ -11,7 +16,7 @@ export default defineWorkspace([ plugins: [ storybookTest({ // The location of your Storybook config, main.js|ts - configDir: './.storybook', + configDir: path.join(dirname, '.storybook'), // This should match your package.json script to run Storybook // The --ci flag will skip prompts and not open a browser storybookScript: 'yarn storybook --ci', @@ -36,9 +41,14 @@ export default defineWorkspace([ ```ts filename="vitest.config.ts" renderer="vue" import { defineConfig, mergeConfig } from 'vitest/config'; import { storybookTest } from '@storybook/experimental-addon-test/vitest-plugin'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; import viteConfig from './vite.config'; +const dirname = + typeof __dirname !== 'undefined' ? __dirname : path.dirname(fileURLToPath(import.meta.url)); + export default defineWorkspace([ // This is the path to your existing Vitest config file './vitest.config.ts', @@ -48,7 +58,7 @@ export default defineWorkspace([ plugins: [ storybookTest({ // The location of your Storybook config, main.js|ts - configDir: './.storybook', + configDir: path.join(dirname, '.storybook'), // This should match your package.json script to run Storybook // The --ci flag will skip prompts and not open a browser storybookScript: 'yarn storybook --ci', @@ -73,9 +83,14 @@ export default defineWorkspace([ ```ts filename="vitest.config.ts" renderer="svelte" import { defineConfig, mergeConfig } from 'vitest/config'; import { storybookTest } from '@storybook/experimental-addon-test/vitest-plugin'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; import viteConfig from './vite.config'; +const dirname = + typeof __dirname !== 'undefined' ? __dirname : path.dirname(fileURLToPath(import.meta.url)); + export default defineWorkspace([ // This is the path to your existing Vitest config file './vitest.config.ts', @@ -85,7 +100,7 @@ export default defineWorkspace([ plugins: [ storybookTest({ // The location of your Storybook config, main.js|ts - configDir: './.storybook', + configDir: path.join(dirname, '.storybook'), // This should match your package.json script to run Storybook // The --ci flag will skip prompts and not open a browser storybookScript: 'yarn storybook --ci', diff --git a/scripts/tasks/sandbox-parts.ts b/scripts/tasks/sandbox-parts.ts index b95bbffb16dd..56ab7176a63c 100644 --- a/scripts/tasks/sandbox-parts.ts +++ b/scripts/tasks/sandbox-parts.ts @@ -451,12 +451,20 @@ export async function setupVitest(details: TemplateDetails, options: PassedOptio dedent` import { defineWorkspace, defaultExclude } from "vitest/config"; import { storybookTest } from "@storybook/experimental-addon-test/vitest-plugin"; + import path from 'node:path'; + import { fileURLToPath } from 'node:url'; + + import viteConfig from './vite.config'; + + const dirname = + typeof __dirname !== 'undefined' ? __dirname : path.dirname(fileURLToPath(import.meta.url)); export default defineWorkspace([ { ${!isNextjs ? `extends: "${viteConfigPath}",` : ''} plugins: [ storybookTest({ + configDir: path.join(dirname, '.storybook'), storybookScript: "yarn storybook --ci", tags: { include: ["vitest"],