Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Addon Test: Refactor test addon to include stories automatically #29367

Merged
merged 12 commits into from
Dec 4, 2024
9 changes: 2 additions & 7 deletions code/.storybook/vitest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,20 +34,15 @@ 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)',
],
ghengeveld marked this conversation as resolved.
Show resolved Hide resolved
exclude: [
...defaultExclude,
'../node_modules/**',
'**/__mockdata__/**',
'../**/__mockdata__/**',
// expected to fail in Vitest because of fetching /iframe.html to cause ECONNREFUSED
'**/Zoom.stories.tsx',
// @yannbf what's wrong with these stories, that they do not work
'**/lib/blocks/src/**',
ghengeveld marked this conversation as resolved.
Show resolved Hide resolved
ghengeveld marked this conversation as resolved.
Show resolved Hide resolved
],
// 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!
Expand Down
10 changes: 3 additions & 7 deletions code/addons/test/src/postinstall.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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:`);
Expand All @@ -355,6 +353,7 @@ export default async function postInstall(options: PostinstallOptions) {
{
extends: '${viteConfigFile ? relative(dirname(browserWorkspaceFile), viteConfigFile) : ''}',
plugins: [
// The plugin will run tests in the stories defined in your Storybook config
ghengeveld marked this conversation as resolved.
Show resolved Hide resolved
// See options at: https://storybook.js.org/docs/writing-tests/vitest-plugin#storybooktest
storybookTest({ configDir: '${options.configDir}' }),${vitestInfo.frameworkPluginDocs + vitestInfo.frameworkPluginCall}
],
Expand All @@ -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'],
},
},
]);
Expand All @@ -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 in the stories defined in your Storybook config
ghengeveld marked this conversation as resolved.
Show resolved Hide resolved
// See options at: https://storybook.js.org/docs/writing-tests/vitest-plugin#storybooktest
storybookTest({ configDir: '${options.configDir}' }),${vitestInfo.frameworkPluginDocs + vitestInfo.frameworkPluginCall}
],
Expand All @@ -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}'],
},
});
Expand Down
74 changes: 69 additions & 5 deletions code/addons/test/src/vitest-plugin/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ 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';

import { join, resolve } from 'pathe';

Expand All @@ -20,6 +22,39 @@ const defaultOptions: UserOptions = {
storybookUrl: 'http://localhost:6006',
};

async function extractStorybookData(finalOptions: InternalOptions) {
const configDir = finalOptions.configDir;
try {
await validateConfigurationFiles(configDir);
} catch (err) {
throw new MainFileMissingError({
location: configDir,
source: 'vitest',
});
}
const previewLevelTags = await extractTagsFromPreview(configDir);

const presets = await loadAllPresets({
configDir,
corePresets: [],
overridePresets: [],
packageJson: {},
});
const stories = await presets.apply('stories', []);

const normalizedStories = normalizeStories(stories, {
configDir: finalOptions.configDir,
workingDir: process.cwd(),
});

const storiesGlobs = normalizedStories.map((entry) => `${entry.directory}/${entry.files}`);
// To discuss: Do we want to filter out mdx files?
// The vitest plugin ignores mdx files, but perhaps it might still give side effects based on user's config
// However if we do filter out mdx, how do we do it without affecting things like ./*.stories.@(js|jsx|ts|mdx|tsx)?
// .filter((entry) => !entry.includes('mdx'));
return { previewLevelTags, stories, storiesGlobs };
}

const extractTagsFromPreview = async (configDir: string) => {
const previewConfigPath = getInterpretedFile(join(resolve(configDir), 'preview'));

Expand Down Expand Up @@ -51,15 +86,14 @@ 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 {
finalOptions.configDir = resolve(process.cwd(), finalOptions.configDir);
}

let previewLevelTags: string[];
let storiesGlobs: StoriesEntry[];

return {
name: 'vite-plugin-storybook-test',
Expand All @@ -82,7 +116,29 @@ export const storybookTest = (options?: UserOptions): Plugin => {
packageJson: {},
});

stories = await presets.apply('stories', []);
const workingDir = process.cwd();
const directories = {
configDir,
workingDir,
};
ghengeveld marked this conversation as resolved.
Show resolved Hide resolved
storiesGlobs = await presets.apply('stories');
const indexers = await presets.apply('experimental_indexers', []);
const docsOptions = await presets.apply<DocsOptions>('docs', {});
const normalizedStories = normalizeStories(await storiesGlobs, directories);

const generator = new StoryIndexGenerator(normalizedStories, {
...directories,
indexers: indexers,
docs: docsOptions,
workingDir,
});

await generator.initialize();

const storyFiles = generator.storyFileNames();

// console.log({ storyFiles });

previewLevelTags = await extractTagsFromPreview(configDir);

const framework = await presets.apply('framework', undefined);
Expand All @@ -94,6 +150,14 @@ export const storybookTest = (options?: UserOptions): Plugin => {
// )
config.test ??= {};

config.test.include ??= [];
config.test.include.push(...storyFiles);

config.test.exclude ??= [];
config.test.exclude.push('**/*.mdx');

// console.log(config.test);

config.test.env ??= {};
config.test.env = {
...config.test.env,
Expand Down Expand Up @@ -169,7 +233,7 @@ export const storybookTest = (options?: UserOptions): Plugin => {
fileName: id,
configDir: finalOptions.configDir,
tagsFilter: finalOptions.tags,
stories,
stories: storiesGlobs,
previewLevelTags,
});
}
Expand Down
1 change: 1 addition & 0 deletions code/core/src/core-server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Exposing internal utilities like StoryIndexGenerator could make it harder to refactor internals in the future. Consider creating a dedicated public API for story file discovery instead.

Loading