Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 45 additions & 10 deletions code/addons/vitest/src/node/vitest-manager.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { existsSync } from 'node:fs';
import { existsSync, readFileSync } from 'node:fs';

import type {
CoverageOptions,
Expand All @@ -13,7 +13,8 @@ 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 * as walk from 'empathic/walk';
import path, { dirname, join, normalize, resolve } from 'pathe';
// eslint-disable-next-line depend/ban-dependencies
import slash from 'slash';

Expand Down Expand Up @@ -75,19 +76,53 @@ 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}`,
]),
];

const potentialConfigFileLocations = walk.up(packageRoot || process.cwd(), {
last: getProjectRoot(),
});

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) : process.cwd(),
root: vitestWorkspaceConfig ?? vitestConfigFallbackLocation,
watch: true,
passWithNoTests: false,
project: [projectName],
Expand Down