diff --git a/code/.storybook/vitest.config.ts b/code/.storybook/vitest.config.ts index 05205f3e762a..4b177f409281 100644 --- a/code/.storybook/vitest.config.ts +++ b/code/.storybook/vitest.config.ts @@ -34,20 +34,13 @@ export default mergeConfig( ], test: { name: 'storybook-ui', - include: [ - '../addons/**/*.{story,stories}.?(c|m)[jt]s?(x)', - // '../core/template/stories/**/*.{story,stories}.?(c|m)[jt]s?(x)', - '../core/src/manager/**/*.{story,stories}.?(c|m)[jt]s?(x)', - '../core/src/preview-api/**/*.{story,stories}.?(c|m)[jt]s?(x)', - '../core/src/components/{brand,components}/**/*.{story,stories}.?(c|m)[jt]s?(x)', - ], exclude: [ ...defaultExclude, '../node_modules/**', '**/__mockdata__/**', '../**/__mockdata__/**', - // expected to fail in Vitest because of fetching /iframe.html to cause ECONNREFUSED - '**/Zoom.stories.tsx', + '**/Zoom.stories.tsx', // expected to fail in Vitest because of fetching /iframe.html to cause ECONNREFUSED + '**/lib/blocks/src/**', // won't work because of https://github.com/storybookjs/storybook/issues/29783 ], // TODO: bring this back once portable stories support @storybook/core/preview-api hooks // @ts-expect-error this type does not exist but the property does! diff --git a/code/addons/test/package.json b/code/addons/test/package.json index 55babe10d98a..9be90b1c5199 100644 --- a/code/addons/test/package.json +++ b/code/addons/test/package.json @@ -103,6 +103,7 @@ "execa": "^8.0.1", "find-up": "^7.0.0", "formik": "^2.2.9", + "glob": "^10.0.0", "istanbul-lib-report": "^3.0.1", "pathe": "^1.1.2", "picocolors": "^1.1.0", diff --git a/code/addons/test/src/postinstall.ts b/code/addons/test/src/postinstall.ts index f6772f7691c1..3ea45bd01bf7 100644 --- a/code/addons/test/src/postinstall.ts +++ b/code/addons/test/src/postinstall.ts @@ -336,8 +336,6 @@ export default async function postInstall(options: PostinstallOptions) { // If there's an existing config, we create a workspace file so we can run Storybook tests alongside. const extension = extname(rootConfig); const browserWorkspaceFile = resolve(dirname(rootConfig), `vitest.workspace${extension}`); - // to be set in vitest config - const vitestSetupFilePath = relative(dirname(browserWorkspaceFile), vitestSetupFile); logger.line(1); logger.plain(`${step} Creating a Vitest project workspace file:`); @@ -355,6 +353,7 @@ export default async function postInstall(options: PostinstallOptions) { { extends: '${viteConfigFile ? relative(dirname(browserWorkspaceFile), viteConfigFile) : ''}', plugins: [ + // The plugin will run tests for the stories defined in your Storybook config // See options at: https://storybook.js.org/docs/writing-tests/vitest-plugin#storybooktest storybookTest({ configDir: '${options.configDir}' }),${vitestInfo.frameworkPluginDocs + vitestInfo.frameworkPluginCall} ], @@ -366,9 +365,7 @@ export default async function postInstall(options: PostinstallOptions) { name: 'chromium', provider: 'playwright', }, - // Make sure to adjust this pattern to match your stories files. - include: ['**/*.stories.?(m)[jt]s?(x)'], - setupFiles: ['${vitestSetupFilePath}'], + setupFiles: ['./.storybook/vitest.setup.ts'], }, }, ]); @@ -393,6 +390,7 @@ export default async function postInstall(options: PostinstallOptions) { // More info at: https://storybook.js.org/docs/writing-tests/vitest-plugin 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/vitest-plugin#storybooktest storybookTest({ configDir: '${options.configDir}' }),${vitestInfo.frameworkPluginDocs + vitestInfo.frameworkPluginCall} ], @@ -404,8 +402,6 @@ export default async function postInstall(options: PostinstallOptions) { name: 'chromium', provider: 'playwright', }, - // Make sure to adjust this pattern to match your stories files. - include: ['**/*.stories.?(m)[jt]s?(x)'], setupFiles: ['${vitestSetupFilePath}'], }, }); diff --git a/code/addons/test/src/vitest-plugin/index.ts b/code/addons/test/src/vitest-plugin/index.ts index b93eed96ddcf..177b2e6ca745 100644 --- a/code/addons/test/src/vitest-plugin/index.ts +++ b/code/addons/test/src/vitest-plugin/index.ts @@ -4,12 +4,16 @@ import type { Plugin } from 'vitest/config'; import { getInterpretedFile, loadAllPresets, + normalizeStories, validateConfigurationFiles, } from 'storybook/internal/common'; +import { StoryIndexGenerator } from 'storybook/internal/core-server'; import { readConfig, vitestTransform } from 'storybook/internal/csf-tools'; import { MainFileMissingError } from 'storybook/internal/server-errors'; -import type { StoriesEntry } from 'storybook/internal/types'; +import type { DocsOptions, StoriesEntry } from 'storybook/internal/types'; +// eslint-disable-next-line depend/ban-dependencies +import { escape } from 'glob'; import { join, resolve } from 'pathe'; import type { InternalOptions, UserOptions } from './types'; @@ -51,8 +55,6 @@ export const storybookTest = (options?: UserOptions): Plugin => { process.env.__STORYBOOK_URL__ = storybookUrl; process.env.__STORYBOOK_SCRIPT__ = finalOptions.storybookScript; - let stories: StoriesEntry[]; - if (!finalOptions.configDir) { finalOptions.configDir = resolve(join(process.cwd(), '.storybook')); } else { @@ -60,6 +62,8 @@ export const storybookTest = (options?: UserOptions): Plugin => { } let previewLevelTags: string[]; + let storiesGlobs: StoriesEntry[]; + let storiesFiles: string[]; return { name: 'vite-plugin-storybook-test', @@ -82,7 +86,27 @@ export const storybookTest = (options?: UserOptions): Plugin => { packageJson: {}, }); - stories = await presets.apply('stories', []); + const workingDir = process.cwd(); + const directories = { + configDir, + workingDir, + }; + storiesGlobs = await presets.apply('stories'); + const indexers = await presets.apply('experimental_indexers', []); + const docsOptions = await presets.apply('docs', {}); + const normalizedStories = normalizeStories(await storiesGlobs, directories); + + const generator = new StoryIndexGenerator(normalizedStories, { + ...directories, + indexers: indexers, + docs: docsOptions, + workingDir, + }); + + await generator.initialize(); + + storiesFiles = generator.storyFileNames(); + previewLevelTags = await extractTagsFromPreview(configDir); const framework = await presets.apply('framework', undefined); @@ -92,8 +116,19 @@ export const storybookTest = (options?: UserOptions): Plugin => { // const isRunningInBrowserMode = config.plugins.find((plugin: Plugin) => // plugin.name?.startsWith('vitest:browser') // ) + config.test ??= {}; + config.test.include ??= []; + config.test.include.push( + // Escape magic characters in paths because they shouldn't be treated as glob patterns + // Paths are resolved using `pathe` to convert Windows paths to POSIX paths first + ...storiesFiles.map((path) => escape(resolve(path))) + ); + + config.test.exclude ??= []; + config.test.exclude.push('**/*.mdx'); + config.test.env ??= {}; config.test.env = { ...config.test.env, @@ -163,13 +198,13 @@ export const storybookTest = (options?: UserOptions): Plugin => { return code; } - if (id.match(/(story|stories)\.[cm]?[jt]sx?$/)) { + if (storiesFiles.includes(id)) { return vitestTransform({ code, fileName: id, configDir: finalOptions.configDir, tagsFilter: finalOptions.tags, - stories, + stories: storiesGlobs, previewLevelTags, }); } diff --git a/code/core/src/core-server/index.ts b/code/core/src/core-server/index.ts index 4906ddd9b1f5..3163e70875ea 100644 --- a/code/core/src/core-server/index.ts +++ b/code/core/src/core-server/index.ts @@ -6,3 +6,4 @@ export * from './build-static'; export * from './build-dev'; export * from './withTelemetry'; export { default as build } from './standalone'; +export { StoryIndexGenerator } from './utils/StoryIndexGenerator'; diff --git a/code/yarn.lock b/code/yarn.lock index 2be3e8503969..97473ea1d726 100644 --- a/code/yarn.lock +++ b/code/yarn.lock @@ -6623,6 +6623,7 @@ __metadata: execa: "npm:^8.0.1" find-up: "npm:^7.0.0" formik: "npm:^2.2.9" + glob: "npm:^10.0.0" istanbul-lib-report: "npm:^3.0.1" pathe: "npm:^1.1.2" picocolors: "npm:^1.1.0" diff --git a/scripts/tasks/sandbox-parts.ts b/scripts/tasks/sandbox-parts.ts index 27b73c36de4e..920b31857a8b 100644 --- a/scripts/tasks/sandbox-parts.ts +++ b/scripts/tasks/sandbox-parts.ts @@ -498,14 +498,11 @@ export async function setupVitest(details: TemplateDetails, options: PassedOptio test: { name: "storybook", pool: "threads", - include: [ - "src/**/*.{story,stories}.?(c|m)[jt]s?(x)", - "template-stories/**/*.{story,stories}.?(c|m)[jt]s?(x)", - ], exclude: [ ...defaultExclude, // TODO: investigate TypeError: Cannot read properties of null (reading 'useContext') "**/*argtypes*", + ${template.expected.renderer === '@storybook/svelte' ? '"**/*.stories.svelte",' : ''} ], /** * TODO: Either fix or acknowledge limitation of: