Skip to content

Addon-Vitest: Use Vitest's provide-API for injecting values#34518

Open
JReinhold wants to merge 5 commits intonextfrom
jeppe/addon-vitest-provide
Open

Addon-Vitest: Use Vitest's provide-API for injecting values#34518
JReinhold wants to merge 5 commits intonextfrom
jeppe/addon-vitest-provide

Conversation

@JReinhold
Copy link
Copy Markdown
Contributor

@JReinhold JReinhold commented Apr 10, 2026

Closes #

What I did

This PR changes the logic for injecting configuration into Vitest runs. Previously, we were setting process.env, but now we're using Vitest's provide+inject API, which was built for this exact purpose.
The structure is also more generic, allowing for arbitrary values to be injected now. Before, we hardcoded the configurations of the accessibility and coverage options, but now anything is injectable by overriding the run config. In stories, the injected values are available as globals['storybook/test-provided]. Addon MCP might use this soon, to customise run options and add additional reports based on that.
It's also built in a way for any users to do the same thing, although the use case isn't clear yet, so let's not document it just yet.

See:

Checklist for Contributors

Testing

The changes in this PR are covered in the following automated tests:

  • stories
  • unit tests
  • integration tests
  • end-to-end tests

Manual testing

Run component tests through both the Vitest CLI and the Testing Module UI and see that it behaves as it always does. In the UI, try out different configurations of a11y and coverage.

I've also tried this with Vitest 3.0.9 to ensure it works with the older Vitest versions we support (v3.0.0 doesn't support the --project flag that we rely on), using the following package.json and Vitest config:

package.json
    "@vitejs/plugin-react": "^5",
    "@vitest/browser": "~3.0.0",
    "@vitest/coverage-v8": "~3.0.0",
    ...,
    "vite": "^6",
    "vitest": "~3.0.0"
Vite config
/// <reference types="vitest/config" />
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";

// https://vite.dev/config/
import path from "node:path";
import { fileURLToPath } from "node:url";
import { storybookTest } from "@storybook/addon-vitest/vitest-plugin";
const dirname =
	typeof __dirname !== "undefined" ? __dirname : path.dirname(fileURLToPath(import.meta.url));

// More info at: https://storybook.js.org/docs/next/writing-tests/integrations/vitest-addon
export default defineConfig({
	plugins: [react()],
	resolve: {
		preserveSymlinks: true,
	},
	server: {
		fs: {
			allow: ["../../.."],
		},
	},
	test: {
		workspace: [
			{
				extends: true,
				plugins: [
					// The plugin will run tests for the stories defined in your Storybook config
					// See options at: https://storybook.js.org/docs/next/writing-tests/integrations/vitest-addon#storybooktest
					storybookTest({
						configDir: path.join(dirname, ".storybook"),
						tags: {
							include: ["vitest"],
						},
					}),
				],
				test: {
					name: "storybook",
					browser: {
						enabled: true,
						headless: true,
						provider: "playwright",
						instances: [
							{
								browser: "chromium",
							},
						],
					},
				},
			},
		],
	},
});

Documentation

  • Add or update documentation reflecting your changes
  • If you are deprecating/removing a feature, make sure to update
    MIGRATION.MD

Checklist for Maintainers

  • When this PR is ready for testing, make sure to add ci:normal, ci:merged or ci:daily GH label to it to run a specific set of sandboxes. The particular set of sandboxes can be found in code/lib/cli-storybook/src/sandbox-templates.ts

  • Make sure this PR contains one of the labels below:

    Available labels
    • bug: Internal changes that fixes incorrect behavior.
    • maintenance: User-facing maintenance tasks.
    • dependencies: Upgrading (sometimes downgrading) dependencies.
    • build: Internal-facing build tooling & test updates. Will not show up in release changelog.
    • cleanup: Minor cleanup style change. Will not show up in release changelog.
    • documentation: Documentation only changes. Will not show up in release changelog.
    • feature request: Introducing a new feature.
    • BREAKING CHANGE: Changes that break compatibility in some way with current major version.
    • other: Changes that don't fit in the above categories.

🦋 Canary release

This pull request has been released as version 0.0.0-pr-34518-sha-7e3dc40d. Try it out in a new sandbox by running npx storybook@0.0.0-pr-34518-sha-7e3dc40d sandbox or in an existing project with npx storybook@0.0.0-pr-34518-sha-7e3dc40d upgrade.

More information
Published version 0.0.0-pr-34518-sha-7e3dc40d
Triggered by @JReinhold
Repository storybookjs/storybook
Branch jeppe/addon-vitest-provide
Commit 7e3dc40d
Datetime Fri Apr 10 10:43:43 UTC 2026 (1775817823)
Workflow run 24239115461

To request a new release of this pull request, mention the @storybookjs/core team.

core team members can create a new canary release here or locally with gh workflow run --repo storybookjs/storybook publish.yml --field pr=34518

Summary by CodeRabbit

Release Notes

  • Refactor

    • Improved test configuration system with more flexible runtime configuration handling for test execution.
    • Enhanced state management for test reports, enabling better tracking and persistence of test results across runs.
  • Tests

    • Expanded test coverage for test run configuration overrides and multi-report result handling scenarios.

JReinhold and others added 3 commits April 10, 2026 11:06
… configuration into the browser environment. allow any run config overrides. store all reports on currentRun, not just a11yReports
Co-authored-by: Copilot <copilot@github.com>
@JReinhold JReinhold self-assigned this Apr 22, 2026
@JReinhold JReinhold requested review from AriPerkkio and yannbf April 22, 2026 09:20
@JReinhold JReinhold added maintenance User-facing maintenance tasks ci:normal addon: vitest labels Apr 22, 2026
@storybook-app-bot
Copy link
Copy Markdown

Package Benchmarks

Commit: 1925ba5, ran on 22 April 2026 at 09:45:35 UTC

The following packages have significant changes to their size or dependencies:

@storybook/cli

Before After Difference
Dependency count 184 184 0
Self size 782 KB 782 KB 0 B
Dependency size 68.18 MB 68.20 MB 🚨 +22 KB 🚨
Bundle Size Analyzer Link Link

@storybook/codemod

Before After Difference
Dependency count 177 177 0
Self size 32 KB 32 KB 🚨 +36 B 🚨
Dependency size 66.70 MB 66.73 MB 🚨 +22 KB 🚨
Bundle Size Analyzer Link Link

@JReinhold JReinhold changed the title Addon-Vitest: Allow arbitrary run config and reports Addon-Vitest: Use Vitest's provide-API for injecting values Apr 22, 2026
@JReinhold JReinhold marked this pull request as ready for review April 22, 2026 12:06
@JReinhold JReinhold requested a review from Copilot April 22, 2026 12:06
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 22, 2026

📝 Walkthrough

Walkthrough

This change refactors the Vitest addon's configuration delivery mechanism by introducing Vitest's provide/inject pattern to replace the previous getInitialGlobals approach. It expands runtime state to track multiple report types beyond a11y, introduces new typed provide keys for type-safe configuration sharing, and broadens the config parameter typing to accept arbitrary key-value pairs while maintaining backward compatibility.

Changes

Cohort / File(s) Summary
Type and Constant Definitions
code/addons/vitest/src/types.ts, code/addons/vitest/src/constants.ts
Introduced RunConfig type (Record<string, unknown>), added STORYBOOK_TEST_PROVIDE_KEY and STORYBOOK_CORE_GHOST_STORIES_PROVIDE_KEY constants, replaced A11yReport alias with A11yRunReport derived from Report['result'], updated CurrentRun to include reports field alongside a11yReports, and relaxed TriggerTestRunRequestPayload.config typing.
Test Manager Implementation
code/addons/vitest/src/node/test-manager.ts, code/addons/vitest/src/node/test-manager.test.ts
Updated runTestsWithState parameter type to RunConfig, enhanced report flushing to store all reports by story ID in a new reportsByStoryId map, extended test suite with assertions for vitest.provide calls and config override handling, and added mocking for the new vitest.provide API.
Vitest Manager Integration
code/addons/vitest/src/node/vitest-manager.ts
Added private helpers getCurrentRunConfig() and provideRunConfig() to read config from state and pass it to Vitest via provide(), refactored runTests() to derive runConfig from currentRun.config and call provideRunConfig() before resetting test patterns.
Plugin and Runtime Configuration Delivery
code/addons/vitest/src/vitest-plugin/index.ts, code/addons/vitest/src/vitest-plugin/test-utils.ts, code/addons/vitest/src/vitest-provided-context.d.ts
Removed getInitialGlobals parsing logic from plugin initialization, replaced with Vite provide entry for ghost stories enablement. Refactored test-utils to use inject() to retrieve provided config instead of reading getInitialGlobals, constructing initialGlobals object synchronously with injected values. Added TypeScript declaration file augmenting Vitest's ProvidedContext with Storybook-specific keys.

Sequence Diagram

sequenceDiagram
    participant TestManager as Test Manager
    participant VitestMgr as Vitest Manager
    participant Vitest as Vitest
    participant PluginInit as Plugin (init)
    participant TestUtils as Test Utils (story)

    TestManager->>TestManager: Store config in<br/>currentRun.config
    
    VitestMgr->>TestManager: getCurrentRunConfig()
    TestManager-->>VitestMgr: config (RunConfig)
    
    VitestMgr->>Vitest: provide(TEST_KEY, config)
    Vitest->>PluginInit: register provide context
    
    PluginInit->>Vitest: config provide +<br/>ghost stories provide
    
    TestUtils->>Vitest: inject(TEST_KEY)
    Vitest-->>TestUtils: config (RunConfig)
    
    TestUtils->>TestUtils: Build initialGlobals with<br/>injected config + ghost stories
    TestUtils->>TestUtils: composeStory(initialGlobals)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@code/addons/vitest/src/vitest-provided-context.d.ts`:
- Around line 1-13: The interface augmentation for ProvidedContext uses computed
property names with the exported constants STORYBOOK_TEST_PROVIDE_KEY and
STORYBOOK_CORE_GHOST_STORIES_PROVIDE_KEY (in vitest-provided-context.d.ts),
which is invalid TypeScript; replace those computed keys with the actual string
literal keys (e.g. the literal values those constants represent such as
'storybook/test-provided' and the ghost-stories key) or change the shape to an
index signature (e.g. add a specific string-keyed property or a Record<string,
unknown> entry) inside the declare module 'vitest' block so ProvidedContext does
not use variable-based computed property names. Ensure you reference the
ProvidedContext interface and the two constant names when making the change so
the augmentation matches the runtime keys.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2335ebf3-fd25-49ef-8818-ec34bf9eb483

📥 Commits

Reviewing files that changed from the base of the PR and between ff98f86 and 1925ba5.

📒 Files selected for processing (8)
  • code/addons/vitest/src/constants.ts
  • code/addons/vitest/src/node/test-manager.test.ts
  • code/addons/vitest/src/node/test-manager.ts
  • code/addons/vitest/src/node/vitest-manager.ts
  • code/addons/vitest/src/types.ts
  • code/addons/vitest/src/vitest-plugin/index.ts
  • code/addons/vitest/src/vitest-plugin/test-utils.ts
  • code/addons/vitest/src/vitest-provided-context.d.ts

Comment on lines +1 to +13
import 'vitest';

import type {
STORYBOOK_CORE_GHOST_STORIES_PROVIDE_KEY,
STORYBOOK_TEST_PROVIDE_KEY,
} from './constants.ts';

declare module 'vitest' {
interface ProvidedContext {
[STORYBOOK_TEST_PROVIDE_KEY]: Record<string, unknown>;
[STORYBOOK_CORE_GHOST_STORIES_PROVIDE_KEY]: boolean;
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

Does TypeScript support computed property names with const string literals in interface augmentation?

💡 Result:

No, TypeScript does not support computed property names using const string literals (even with 'as const') in interface augmentation. In interfaces (including augmentations via declaration merging), computed property names like [key] where key is a const variable require the expression to have a literal type or unique symbol type at compile time. A const string literal like const key = "foo" as const; produces a literal type "foo", but the syntax [key] in an interface declaration is restricted—it must be a direct built-in symbol or primitive literal directly written (like ["foo"]), not a reference to a variable, even const. Using a variable reference triggers TS1169: "A computed property name in an interface must refer to an expression whose type is a literal type or a 'unique symbol' type." This restriction applies identically in module augmentation contexts since augmentations use the same interface merging rules. Workarounds include direct string literals (e.g., ["foo"]: type), mapped types in type aliases (e.g., { [K in keyof T]: type }), or index signatures, but not variable references in interface computed properties.

Citations:


🏁 Script executed:

fd -t f "constants.ts" --search-path code/addons/vitest

Repository: storybookjs/storybook

Length of output: 100


🏁 Script executed:

cat -n code/addons/vitest/src/constants.ts

Repository: storybookjs/storybook

Length of output: 3675


🏁 Script executed:

cd code/addons/vitest && find . -name "tsconfig*.json" -o -name ".eslintrc*" | head -5

Repository: storybookjs/storybook

Length of output: 80


🏁 Script executed:

cat code/addons/vitest/tsconfig.json

Repository: storybookjs/storybook

Length of output: 248


🏁 Script executed:

grep -r "STORYBOOK_TEST_PROVIDE_KEY\|STORYBOOK_CORE_GHOST_STORIES_PROVIDE_KEY" code/addons/vitest/src --include="*.d.ts" --include="*.ts"

Repository: storybookjs/storybook

Length of output: 2084


🏁 Script executed:

cd code/addons/vitest && npx tsc --noEmit 2>&1 | grep -A 5 "vitest-provided-context"

Repository: storybookjs/storybook

Length of output: 47


🏁 Script executed:

cd code/addons/vitest && npx tsc --noEmit 2>&1

Repository: storybookjs/storybook

Length of output: 473


🏁 Script executed:

cat -n code/addons/vitest/src/vitest-provided-context.d.ts

Repository: storybookjs/storybook

Length of output: 465


Fix computed property names in module augmentation—this pattern is not valid TypeScript.

TypeScript does not support computed property names using variable references in interface declarations (TS1169), including in module augmentation. Additionally, lines 3-6 incorrectly import these as types when they are value exports. Either use direct string literals for the keys (e.g., ['storybook/test-provided']) or restructure using index signatures as a workaround.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@code/addons/vitest/src/vitest-provided-context.d.ts` around lines 1 - 13, The
interface augmentation for ProvidedContext uses computed property names with the
exported constants STORYBOOK_TEST_PROVIDE_KEY and
STORYBOOK_CORE_GHOST_STORIES_PROVIDE_KEY (in vitest-provided-context.d.ts),
which is invalid TypeScript; replace those computed keys with the actual string
literal keys (e.g. the literal values those constants represent such as
'storybook/test-provided' and the ghost-stories key) or change the shape to an
index signature (e.g. add a specific string-keyed property or a Record<string,
unknown> entry) inside the declare module 'vitest' block so ProvidedContext does
not use variable-based computed property names. Ensure you reference the
ProvidedContext interface and the two constant names when making the change so
the augmentation matches the runtime keys.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR updates @storybook/addon-vitest to inject Storybook run configuration into Vitest via Vitest’s provide/inject API instead of relying on process.env, and expands run reporting to support arbitrary report types (while keeping legacy a11y reports).

Changes:

  • Replace env-based config passing with Vitest provide (manager) + inject (test runtime) to build initialGlobals.
  • Add generic reports persistence to currentRun while retaining a11yReports for backwards compatibility.
  • Introduce provide keys in constants and wire ghost-stories enablement through Vitest config provide.

Reviewed changes

Copilot reviewed 7 out of 8 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
code/addons/vitest/src/vitest-provided-context.d.ts Adds Vitest ProvidedContext augmentation for Storybook-provided keys.
code/addons/vitest/src/vitest-plugin/test-utils.ts Switches globals initialization to read injected values via inject() and sets initialGlobals accordingly.
code/addons/vitest/src/vitest-plugin/index.ts Removes browser command-based globals and uses Vitest config provide for ghost-stories.
code/addons/vitest/src/types.ts Introduces RunConfig, adds reports, and updates run-state typing.
code/addons/vitest/src/node/vitest-manager.ts Calls vitest.provide() with the current run config before running tests.
code/addons/vitest/src/node/test-manager.ts Stops writing run config into process.env, and persists generic reports alongside a11y reports.
code/addons/vitest/src/node/test-manager.test.ts Adds assertions for vitest.provide() and for persisting generic reports.
code/addons/vitest/src/constants.ts Adds provide keys and extends initial state with reports; broadens trigger payload config typing.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 33 to 52
@@ -45,7 +46,9 @@ export type CurrentRun = {
warning: number;
error: number;
};
a11yReports: Record<StoryId, A11yReport[]>;
// Backwards compatibility for consumers that still read the legacy a11y-only shape.
a11yReports: Record<StoryId, A11yRunReport[]>;
reports: Record<StoryId, Report[]>;
totalTestCount: number | undefined;
Copy link

Copilot AI Apr 22, 2026

Choose a reason for hiding this comment

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

A11yRunReport changing from the previous any-based shape to Report['result'] makes currentRun.a11yReports effectively unknown[], which is a TypeScript-breaking change for existing consumers that relied on the permissive typing. If this field is meant to remain backwards-compatible, consider keeping a11yReports as any[] (or Report<any>['result'][]) and using the new reports field for stricter/typed access.

Copilot uses AI. Check for mistakes.
Comment on lines 369 to 396
@@ -377,6 +389,8 @@ export class VitestManager {
await this.vitestRestartPromise;
}

this.provideRunConfig();

this.resetGlobalTestNamePattern();

await this.cancelCurrentRun();
Copy link

Copilot AI Apr 22, 2026

Choose a reason for hiding this comment

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

provideRunConfig() is only called from runTests(). The watch-mode path (runAffectedTestsAfterChangerunTestsWithStaterunTestSpecifications) no longer updates the injected config now that process.env.VITEST_STORYBOOK_CONFIG was removed, so watch reruns may use stale provided values. Consider calling provideRunConfig() (or vitest.provide(...)) in the watch rerun flow as well, right before invoking runTestSpecifications.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Member

@AriPerkkio AriPerkkio left a comment

Choose a reason for hiding this comment

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

Looks correct to me.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

addon: vitest ci:normal maintenance User-facing maintenance tasks

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants