Skip to content

Commit a834932

Browse files
Merge pull request #31715 from storybookjs/valentin/support-vitest-3-2
Addon Vitest: Support init in Vitest >= 3.2 (cherry picked from commit 1cce824)
1 parent 198c329 commit a834932

File tree

14 files changed

+1124
-459
lines changed

14 files changed

+1124
-459
lines changed

code/addons/docs/src/preset.ts

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -212,11 +212,6 @@ export const resolvedReact = async (existing: any) => ({
212212
mdx: existing?.mdx ?? dirname(require.resolve('@mdx-js/react')),
213213
});
214214

215-
const optimizeViteDeps = [
216-
'@mdx-js/react',
217-
'@storybook/addon-docs > acorn-jsx',
218-
'@storybook/addon-docs',
219-
'markdown-to-jsx',
220-
];
215+
const optimizeViteDeps = ['@mdx-js/react', '@storybook/addon-docs', 'markdown-to-jsx'];
221216

222217
export { webpackX as webpack, docsX as docs, optimizeViteDeps };

code/addons/vitest/package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,8 @@
100100
"@types/micromatch": "^4.0.0",
101101
"@types/node": "^22.0.0",
102102
"@types/semver": "^7",
103-
"@vitest/browser": "^3.1.1",
104-
"@vitest/runner": "^3.1.1",
103+
"@vitest/browser": "^3.2.0",
104+
"@vitest/runner": "^3.2.0",
105105
"boxen": "^8.0.1",
106106
"es-toolkit": "^1.36.0",
107107
"execa": "^8.0.1",
@@ -119,7 +119,7 @@
119119
"tree-kill": "^1.2.2",
120120
"ts-dedent": "^2.2.0",
121121
"typescript": "^5.8.3",
122-
"vitest": "^3.1.1"
122+
"vitest": "^3.2.0"
123123
},
124124
"peerDependencies": {
125125
"@vitest/browser": "^3.0.0",

code/addons/vitest/src/postinstall.ts

Lines changed: 96 additions & 84 deletions
Large diffs are not rendered by default.

code/addons/vitest/src/updateVitestFile.test.ts

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,135 @@ describe('updateConfigFile', () => {
158158
expect(updated).toBe(false);
159159
});
160160

161+
it('adds projects property to test config', async () => {
162+
const source = babel.babelParse(
163+
await loadTemplate('vitest.config.3.2.template.ts', {
164+
CONFIG_DIR: '.storybook',
165+
BROWSER_CONFIG: "{ provider: 'playwright' }",
166+
SETUP_FILE: '../.storybook/vitest.setup.ts',
167+
})
168+
);
169+
const target = babel.babelParse(`
170+
/// <reference types="vitest/config" />
171+
import { defineConfig } from 'vite'
172+
import react from '@vitejs/plugin-react'
173+
174+
// https://vite.dev/config/
175+
export default defineConfig({
176+
plugins: [react()],
177+
test: {
178+
globals: true,
179+
},
180+
})
181+
`);
182+
183+
const updated = updateConfigFile(source, target);
184+
expect(updated).toBe(true);
185+
186+
const { code } = babel.generate(target);
187+
expect(code).toMatchInlineSnapshot(`
188+
"/// <reference types="vitest/config" />
189+
import { defineConfig } from 'vite';
190+
import react from '@vitejs/plugin-react';
191+
192+
// https://vite.dev/config/
193+
import path from 'node:path';
194+
import { fileURLToPath } from 'node:url';
195+
import { storybookTest } from '@storybook/addon-vitest/vitest-plugin';
196+
const dirname = typeof __dirname !== 'undefined' ? __dirname : path.dirname(fileURLToPath(import.meta.url));
197+
198+
// More info at: https://storybook.js.org/docs/next/writing-tests/integrations/vitest-addon
199+
export default defineConfig({
200+
plugins: [react()],
201+
test: {
202+
globals: true,
203+
projects: [{
204+
extends: true,
205+
plugins: [
206+
// The plugin will run tests for the stories defined in your Storybook config
207+
// See options at: https://storybook.js.org/docs/next/writing-tests/integrations/vitest-addon#storybooktest
208+
storybookTest({
209+
configDir: path.join(dirname, '.storybook')
210+
})],
211+
test: {
212+
name: 'storybook',
213+
browser: {
214+
provider: 'playwright'
215+
},
216+
setupFiles: ['../.storybook/vitest.setup.ts']
217+
}
218+
}]
219+
}
220+
});"
221+
`);
222+
});
223+
224+
it('edits projects property of test config', async () => {
225+
const source = babel.babelParse(
226+
await loadTemplate('vitest.config.3.2.template.ts', {
227+
CONFIG_DIR: '.storybook',
228+
BROWSER_CONFIG: "{ provider: 'playwright' }",
229+
SETUP_FILE: '../.storybook/vitest.setup.ts',
230+
})
231+
);
232+
const target = babel.babelParse(`
233+
/// <reference types="vitest/config" />
234+
import { defineConfig } from 'vite'
235+
import react from '@vitejs/plugin-react'
236+
237+
// https://vite.dev/config/
238+
export default defineConfig({
239+
plugins: [react()],
240+
test: {
241+
globals: true,
242+
projects: ['packages/*', {some: 'config'}]
243+
}
244+
})
245+
`);
246+
247+
const updated = updateConfigFile(source, target);
248+
expect(updated).toBe(true);
249+
250+
const { code } = babel.generate(target);
251+
expect(code).toMatchInlineSnapshot(`
252+
"/// <reference types="vitest/config" />
253+
import { defineConfig } from 'vite';
254+
import react from '@vitejs/plugin-react';
255+
256+
// https://vite.dev/config/
257+
import path from 'node:path';
258+
import { fileURLToPath } from 'node:url';
259+
import { storybookTest } from '@storybook/addon-vitest/vitest-plugin';
260+
const dirname = typeof __dirname !== 'undefined' ? __dirname : path.dirname(fileURLToPath(import.meta.url));
261+
262+
// More info at: https://storybook.js.org/docs/next/writing-tests/integrations/vitest-addon
263+
export default defineConfig({
264+
plugins: [react()],
265+
test: {
266+
globals: true,
267+
projects: ['packages/*', {
268+
some: 'config'
269+
}, {
270+
extends: true,
271+
plugins: [
272+
// The plugin will run tests for the stories defined in your Storybook config
273+
// See options at: https://storybook.js.org/docs/next/writing-tests/integrations/vitest-addon#storybooktest
274+
storybookTest({
275+
configDir: path.join(dirname, '.storybook')
276+
})],
277+
test: {
278+
name: 'storybook',
279+
browser: {
280+
provider: 'playwright'
281+
},
282+
setupFiles: ['../.storybook/vitest.setup.ts']
283+
}
284+
}]
285+
}
286+
});"
287+
`);
288+
});
289+
161290
it('adds workspace property to test config', async () => {
162291
const source = babel.babelParse(
163292
await loadTemplate('vitest.config.template.ts', {

code/addons/vitest/src/updateVitestFile.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,31 @@ const mergeProperties = (
5050
}
5151
};
5252

53+
/**
54+
* Merges a source Vitest configuration AST into a target configuration AST.
55+
*
56+
* This function intelligently combines configuration elements from a source file (typically a
57+
* template) into an existing target configuration file, avoiding duplicates and preserving the
58+
* structure of both files.
59+
*
60+
* The function performs the following operations:
61+
*
62+
* 1. **Import Merging**: Adds new import statements from source that don't exist in target (determined
63+
* by local specifier name). Imports are inserted after existing imports.
64+
* 2. **Variable Declaration Merging**: Copies variable declarations from source to target if they
65+
* don't already exist (determined by variable name). Variables are inserted after imports.
66+
* 3. **Configuration Object Merging**: Merges the configuration object properties from source into
67+
* target's default export. Supports both direct object exports and function-wrapped exports
68+
* (e.g., `defineConfig({})`). The merging is recursive:
69+
*
70+
* - Nested objects are merged deeply
71+
* - Arrays are concatenated (shallow merge)
72+
* - Primitive values are overwritten
73+
*
74+
* @param source - The source Babel AST (template configuration to merge from)
75+
* @param target - The target Babel AST (existing configuration to merge into)
76+
* @returns {boolean} - True if the target was modified, false otherwise
77+
*/
5378
export const updateConfigFile = (source: BabelFile['ast'], target: BabelFile['ast']) => {
5479
let updated = false;
5580
for (const sourceNode of source.program.body) {

code/addons/vitest/src/vitest-plugin/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import sirv from 'sirv';
2929
import { dedent } from 'ts-dedent';
3030

3131
// ! Relative import to prebundle it without needing to depend on the Vite builder
32+
import { INCLUDE_CANDIDATES } from '../../../../builders/builder-vite/src/constants';
3233
import { withoutVitePlugins } from '../../../../builders/builder-vite/src/utils/without-vite-plugins';
3334
import type { InternalOptions, UserOptions } from './types';
3435

@@ -151,6 +152,7 @@ export const storybookTest = async (options?: UserOptions): Promise<Plugin[]> =>
151152
staticDirs,
152153
previewLevelTags,
153154
core,
155+
extraOptimizeDeps,
154156
] = await Promise.all([
155157
getStoryGlobsAndFiles(presets, directories),
156158
presets.apply('framework', undefined),
@@ -159,6 +161,7 @@ export const storybookTest = async (options?: UserOptions): Promise<Plugin[]> =>
159161
presets.apply('staticDirs', []),
160162
extractTagsFromPreview(finalOptions.configDir),
161163
presets.apply('core'),
164+
presets.apply('optimizeViteDeps', []),
162165
]);
163166

164167
const pluginsToIgnore = [
@@ -326,6 +329,8 @@ export const storybookTest = async (options?: UserOptions): Promise<Plugin[]> =>
326329

327330
optimizeDeps: {
328331
include: [
332+
...extraOptimizeDeps,
333+
...INCLUDE_CANDIDATES,
329334
'@storybook/addon-vitest/internal/setup-file',
330335
'@storybook/addon-vitest/internal/global-setup',
331336
'@storybook/addon-vitest/internal/test-utils',
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import path from 'node:path';
2+
import { fileURLToPath } from 'node:url';
3+
4+
import { defineConfig } from 'vitest/config';
5+
6+
import { storybookTest } from '@storybook/addon-vitest/vitest-plugin';
7+
8+
const dirname =
9+
typeof __dirname !== 'undefined' ? __dirname : path.dirname(fileURLToPath(import.meta.url));
10+
11+
// More info at: https://storybook.js.org/docs/next/writing-tests/integrations/vitest-addon
12+
export default defineConfig({
13+
test: {
14+
projects: [
15+
{
16+
extends: true,
17+
plugins: [
18+
// The plugin will run tests for the stories defined in your Storybook config
19+
// See options at: https://storybook.js.org/docs/next/writing-tests/integrations/vitest-addon#storybooktest
20+
storybookTest({ configDir: path.join(dirname, 'CONFIG_DIR') }),
21+
],
22+
test: {
23+
name: 'storybook',
24+
browser: BROWSER_CONFIG,
25+
setupFiles: ['SETUP_FILE'],
26+
},
27+
},
28+
],
29+
},
30+
});

0 commit comments

Comments
 (0)