From ada93bc3f4ebac73ff0a6d90c0f0d0d1a47aed5b Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Mon, 9 Feb 2026 14:59:26 +0100 Subject: [PATCH 1/2] Addon-Vitest: Improve config file detection in monorepos --- code/addons/vitest/src/node/vitest-manager.ts | 41 ++++++++++++++----- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/code/addons/vitest/src/node/vitest-manager.ts b/code/addons/vitest/src/node/vitest-manager.ts index 8cf3e0f15e7e..1a9686789113 100644 --- a/code/addons/vitest/src/node/vitest-manager.ts +++ b/code/addons/vitest/src/node/vitest-manager.ts @@ -1,4 +1,4 @@ -import { existsSync } from 'node:fs'; +import { existsSync, readFileSync } from 'node:fs'; import type { CoverageOptions, @@ -13,7 +13,7 @@ import { Tag } from 'storybook/internal/core-server'; import type { StoryId, StoryIndex, StoryIndexEntry } from 'storybook/internal/types'; import * as find from 'empathic/find'; -import path, { dirname, join, normalize } from 'pathe'; +import path, { dirname, join, normalize, resolve } from 'pathe'; // eslint-disable-next-line depend/ban-dependencies import slash from 'slash'; @@ -75,19 +75,40 @@ export class VitestManager { : { enabled: false } ) as CoverageOptions; - const vitestWorkspaceConfig = find.any( - [ - ...VITEST_WORKSPACE_FILE_EXTENSION.map((ext) => `vitest.workspace.${ext}`), - ...VITEST_CONFIG_FILE_EXTENSIONS.map((ext) => `vitest.config.${ext}`), - ], - { last: getProjectRoot() } - ); + // In monorepos, the Storybook configDir (e.g. packages/web-app/.storybook) identifies + // the sub-package. We start the Vitest config search from its parent (the package root) + // and traverse upward to the project root, so configs in both sub-packages and the + // monorepo root are found. Without this, find.any defaults to process.cwd() which may + // be the monorepo root and would miss sub-package configs entirely. + const configDir = this.testManager.storybookOptions.configDir; + const packageRoot = configDir ? dirname(resolve(configDir)) : undefined; + + const configFiles = [ + ...VITEST_WORKSPACE_FILE_EXTENSION.map((ext) => `vitest.workspace.${ext}`), + ...VITEST_CONFIG_FILE_EXTENSIONS.flatMap((ext) => [ + `vitest.config.${ext}`, + `vite.config.${ext}`, + ]), + ]; + + let vitestWorkspaceConfig: string | undefined; + + for (const file of configFiles) { + const maybe = find.any([file], { cwd: packageRoot, last: getProjectRoot() }); + if (maybe && existsSync(maybe)) { + const content = readFileSync(maybe, 'utf8'); + if (content.includes('storybookTest')) { + vitestWorkspaceConfig = maybe; + break; + } + } + } const projectName = 'storybook:' + process.env.STORYBOOK_CONFIG_DIR; try { this.vitest = await createVitest('test', { - root: vitestWorkspaceConfig ? dirname(vitestWorkspaceConfig) : process.cwd(), + root: vitestWorkspaceConfig ? dirname(vitestWorkspaceConfig) : packageRoot || process.cwd(), watch: true, passWithNoTests: false, project: [projectName], From b09081a7c8c82a54d27d962f72b71cfbd0a9cb0f Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Thu, 12 Feb 2026 10:02:53 +0100 Subject: [PATCH 2/2] Harden lookup of Vitest config --- code/addons/vitest/src/node/vitest-manager.ts | 32 +++++++++++++------ 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/code/addons/vitest/src/node/vitest-manager.ts b/code/addons/vitest/src/node/vitest-manager.ts index 1a9686789113..f787556ed4b4 100644 --- a/code/addons/vitest/src/node/vitest-manager.ts +++ b/code/addons/vitest/src/node/vitest-manager.ts @@ -13,6 +13,7 @@ import { Tag } from 'storybook/internal/core-server'; import type { StoryId, StoryIndex, StoryIndexEntry } from 'storybook/internal/types'; import * as find from 'empathic/find'; +import * as walk from 'empathic/walk'; import path, { dirname, join, normalize, resolve } from 'pathe'; // eslint-disable-next-line depend/ban-dependencies import slash from 'slash'; @@ -91,24 +92,37 @@ export class VitestManager { ]), ]; - let vitestWorkspaceConfig: string | undefined; + const potentialConfigFileLocations = walk.up(packageRoot || process.cwd(), { + last: getProjectRoot(), + }); - for (const file of configFiles) { - const maybe = find.any([file], { cwd: packageRoot, last: getProjectRoot() }); - if (maybe && existsSync(maybe)) { - const content = readFileSync(maybe, 'utf8'); - if (content.includes('storybookTest')) { - vitestWorkspaceConfig = maybe; - break; + let vitestWorkspaceConfig: string | undefined; + let firstVitestConfig: string | undefined; + + for (const location of potentialConfigFileLocations) { + for (const file of configFiles) { + const maybe = find.any([file], { cwd: location, last: getProjectRoot() }); + if (maybe && existsSync(maybe)) { + firstVitestConfig ??= maybe; + const content = readFileSync(maybe, 'utf8'); + if (content.includes('storybookTest') || content.includes('@storybook/addon-vitest')) { + vitestWorkspaceConfig = dirname(maybe); + break; + } } } + if (vitestWorkspaceConfig) { + break; + } } const projectName = 'storybook:' + process.env.STORYBOOK_CONFIG_DIR; + const vitestConfigFallbackLocation = firstVitestConfig || packageRoot || process.cwd(); + try { this.vitest = await createVitest('test', { - root: vitestWorkspaceConfig ? dirname(vitestWorkspaceConfig) : packageRoot || process.cwd(), + root: vitestWorkspaceConfig ?? vitestConfigFallbackLocation, watch: true, passWithNoTests: false, project: [projectName],