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 b4949927736b..0ac20b91f9d4 100644 --- a/code/addons/test/src/postinstall.ts +++ b/code/addons/test/src/postinstall.ts @@ -419,6 +419,12 @@ export default async function postInstall(options: PostinstallOptions) { dedent` import { defineWorkspace } from 'vitest/config'; import { storybookTest } from '@storybook/experimental-addon-test/vitest-plugin';${vitestInfo.frameworkPluginImport} + 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([ @@ -428,7 +434,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}' }),${vitestInfo.frameworkPluginDocs + vitestInfo.frameworkPluginCall} + storybookTest({ configDir: path.join(dirname, '${options.configDir}') }),${vitestInfo.frameworkPluginDocs + vitestInfo.frameworkPluginCall} ], test: { name: 'storybook', @@ -459,13 +465,19 @@ export default async function postInstall(options: PostinstallOptions) { dedent` import { defineConfig } from 'vitest/config'; import { storybookTest } from '@storybook/experimental-addon-test/vitest-plugin';${vitestInfo.frameworkPluginImport} + 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}' }),${vitestInfo.frameworkPluginDocs + vitestInfo.frameworkPluginCall} + storybookTest({ configDir: path.join(dirname, '${options.configDir}') }),${vitestInfo.frameworkPluginDocs + vitestInfo.frameworkPluginCall} ], test: { name: 'storybook', diff --git a/docs/_snippets/vitest-plugin-vitest-config.md b/docs/_snippets/vitest-plugin-vitest-config.md index 6ce11fcef545..683bb0884315 100644 --- a/docs/_snippets/vitest-plugin-vitest-config.md +++ b/docs/_snippets/vitest-plugin-vitest-config.md @@ -1,9 +1,14 @@ ```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'; // 👇 If you're using Next.js, apply this framework plugin as well // import { storybookNextJsPlugin } from '@storybook/experimental-nextjs-vite/vite-plugin'; +const dirname = + typeof __dirname !== 'undefined' ? __dirname : path.dirname(fileURLToPath(import.meta.url)); + import viteConfig from './vite.config'; export default mergeConfig( @@ -12,7 +17,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', @@ -38,16 +43,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', @@ -72,9 +82,14 @@ 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'; // 👇 If you're using Sveltekit, apply this framework plugin as well // import { storybookSveltekitPlugin } from '@storybook/sveltekit/vite-plugin'; +const dirname = + typeof __dirname !== 'undefined' ? __dirname : path.dirname(fileURLToPath(import.meta.url)); + import viteConfig from './vite.config'; export default mergeConfig( @@ -83,7 +98,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 bce2c89ce97a..a1d33075fab7 100644 --- a/docs/_snippets/vitest-plugin-vitest-workspace.md +++ b/docs/_snippets/vitest-plugin-vitest-workspace.md @@ -1,9 +1,15 @@ ```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'; + // 👇 If you're using Next.js, apply this framework plugin as well // import { storybookNextJsPlugin } from '@storybook/experimental-nextjs-vite/vite-plugin'; +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', @@ -13,7 +19,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', @@ -40,9 +46,14 @@ export default defineWorkspace([ 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 defineWorkspace([ // This is the path to your existing Vitest config file './vitest.config.ts', @@ -52,7 +63,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', @@ -78,11 +89,17 @@ 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'; + // 👇 If you're using Sveltekit, apply this framework plugin as well // import { storybookSveltekitPlugin } from '@storybook/sveltekit/vite-plugin'; 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', @@ -92,7 +109,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 271fbba9dcd8..5d11bd7380ed 100644 --- a/scripts/tasks/sandbox-parts.ts +++ b/scripts/tasks/sandbox-parts.ts @@ -480,13 +480,22 @@ 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'; + ${frameworkPluginImport} + 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"],