Repository navigation
Expand file tree
/
Copy pathvitest-root.test.ts
More file actions
145 lines (121 loc) · 5.47 KB
/
Copy pathvitest-root.test.ts
File metadata and controls
145 lines (121 loc) · 5.47 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
import { fileURLToPath } from 'node:url';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { validateConfigurationFiles } from 'storybook/internal/common';
import { StoryIndexGenerator, experimental_loadStorybook } from 'storybook/internal/core-server';
import { isTelemetryModuleEnabled } from 'storybook/internal/telemetry';
import { storybookTest } from './index.ts';
const REPO_ROOT = '/repo';
const PACKAGE_ROOT = '/repo/apps/storybook';
const CONFIG_DIR = '/repo/apps/storybook/.storybook';
vi.mock('storybook/internal/common', { spy: true });
vi.mock('storybook/internal/core-server', { spy: true });
vi.mock('storybook/internal/telemetry', { spy: true });
const presetApply = vi.fn();
beforeEach(() => {
vi.stubEnv('VITEST', 'true');
presetApply.mockImplementation(async (key: string, fallback?: unknown) => {
switch (key) {
case 'stories':
return ['../stories/**/*.stories.tsx'];
case 'framework':
return { name: '@storybook/react-vite' };
// Mirrors a project without its own `viteFinal`: the common config is returned untouched,
// so the root the plugin proposes is the root it ends up returning.
case 'viteFinal':
return fallback;
case 'core':
return { disableTelemetry: true };
default:
return fallback;
}
});
vi.mocked(experimental_loadStorybook).mockResolvedValue({
presets: { apply: presetApply },
} as unknown as Awaited<ReturnType<typeof experimental_loadStorybook>>);
vi.mocked(StoryIndexGenerator.findMatchingFilesForSpecifiers).mockResolvedValue([]);
vi.mocked(validateConfigurationFiles).mockResolvedValue(undefined as never);
vi.mocked(isTelemetryModuleEnabled).mockReturnValue(false);
});
afterEach(() => {
vi.unstubAllEnvs();
});
/** Runs the plugin's `config` hook the way Vitest does, and returns the config it contributes. */
async function getPluginConfig(invokingRoot: string) {
const plugins = await storybookTest({ configDir: CONFIG_DIR });
const plugin = plugins.find((p) => p.name === 'vite-plugin-storybook-test')!;
const configHook = plugin.config!;
const handler = typeof configHook === 'function' ? configHook : configHook.handler;
const config = await handler.call(
{
meta: { rollupVersion: '4.0.0', viteVersion: '7.0.0' },
debug: () => {},
info: () => {},
warn: () => {},
error: (message: unknown): never => {
throw new Error(String(message));
},
},
{ root: invokingRoot },
{ command: 'serve', mode: 'development' }
);
if (!config || !config.test) {
throw new Error('The plugin config hook returned no test config');
}
return { root: config.root, test: config.test, plugin };
}
describe('story test patterns', () => {
// The plugin sets the project root itself, so story globs have to be written relative to that
// root rather than to whichever root Vitest happened to be invoked with. When a Vitest config
// lives above the package — a monorepo root — the two differ, and globs built against the
// invoking root resolve outside the project and match no story files at all, silently.
it('resolves story globs against the root it returns, not the invoking root', async () => {
const config = await getPluginConfig(REPO_ROOT);
expect(config.root).toBe(PACKAGE_ROOT);
expect(config.test.include).toEqual(['stories/**/*.stories.tsx']);
});
it('resolves story globs the same way when the invoking root already matches', async () => {
const config = await getPluginConfig(PACKAGE_ROOT);
expect(config.root).toBe(PACKAGE_ROOT);
expect(config.test.include).toEqual(['stories/**/*.stories.tsx']);
});
});
describe('preview html', () => {
it('sets the framework options global ahead of the preview head', async () => {
const applyPreset = presetApply.getMockImplementation()!;
presetApply.mockImplementation(async (key: string, fallback?: unknown) => {
switch (key) {
case 'frameworkOptions':
return { strictMode: true };
case 'previewHead':
return '<meta name="preview-head" />';
default:
return applyPreset(key, fallback);
}
});
const plugins = await storybookTest({ configDir: CONFIG_DIR });
const plugin = plugins.find((p) => p.name === 'vite-plugin-storybook-test')!;
const transformIndexHtml = plugin.transformIndexHtml as (html: string) => Promise<string>;
expect(await transformIndexHtml('<html><head></head><body></body></html>')).toBe(
'<html><head><script>window.FRAMEWORK_OPTIONS = {"strictMode":true};</script><meta name="preview-head" /></head><body></body></html>'
);
});
});
describe('internal setup files', () => {
it('registers internal setup files', async () => {
const { test, plugin } = await getPluginConfig(REPO_ROOT);
const project = {
config: { browser: { enabled: true }, setupFiles: ['/repo/my-own-setup.ts'] },
};
await (plugin.configureVitest as (context: unknown) => Promise<void>)({
vitest: { config: { coverage: { exclude: [] }, reporters: [] } },
project,
});
const resolve = (specifier: string) => fileURLToPath(import.meta.resolve(specifier));
expect([...project.config.setupFiles, ...(test.setupFiles as string[])]).toEqual([
'/repo/my-own-setup.ts',
resolve('@storybook/addon-vitest/internal/setup-file.browser.4'),
'@storybook/addon-vitest/internal/setup-file',
'@storybook/addon-vitest/internal/setup-file-with-project-annotations',
]);
});
});