Addon-Vitest: Use Vitest's provide-API for injecting values#34518
Addon-Vitest: Use Vitest's provide-API for injecting values#34518
Conversation
… 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>
Package BenchmarksCommit: The following packages have significant changes to their size or dependencies:
|
| 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 |
📝 WalkthroughWalkthroughThis change refactors the Vitest addon's configuration delivery mechanism by introducing Vitest's Changes
Sequence DiagramsequenceDiagram
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)
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~22 minutes Possibly related PRs
✨ Finishing Touches📝 Generate docstrings
Comment |
There was a problem hiding this comment.
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
📒 Files selected for processing (8)
code/addons/vitest/src/constants.tscode/addons/vitest/src/node/test-manager.test.tscode/addons/vitest/src/node/test-manager.tscode/addons/vitest/src/node/vitest-manager.tscode/addons/vitest/src/types.tscode/addons/vitest/src/vitest-plugin/index.tscode/addons/vitest/src/vitest-plugin/test-utils.tscode/addons/vitest/src/vitest-provided-context.d.ts
| 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; | ||
| } | ||
| } |
There was a problem hiding this comment.
🧩 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:
- 1: https://stackoverflow.com/questions/44110641/typescript-a-computed-property-name-in-a-type-literal-must-directly-refer-to-a-b
- 2: [isolatedDeclarations] Add a syntactic form of computed property name which is always emitted as a computed property name microsoft/TypeScript#58800
- 3: Primitive literals in computed property names microsoft/TypeScript#4653
- 4: https://isms.hashnode.dev/ts1169-a-computed-property-name-in-an-interface-must-refer-to-an-expression-whose-type-is-a-literal-type-or-a-unique-symbol-type
- 5: Improve error messages for computed class property names microsoft/TypeScript#42619
- 6: https://stackoverflow.com/questions/64180516/what-is-the-square-bracketed-field-in-the-interface-in-typescript
- 7: https://stackoverflow.com/questions/69531648/why-doesnt-interface-work-when-defining-a-type-that-uses-enum-as-property-key
- 8: https://www.typescriptlang.org/docs/handbook/declaration-merging.html
- 9: https://www.typescriptlang.org/docs/handbook/declaration-merging
🏁 Script executed:
fd -t f "constants.ts" --search-path code/addons/vitestRepository: storybookjs/storybook
Length of output: 100
🏁 Script executed:
cat -n code/addons/vitest/src/constants.tsRepository: storybookjs/storybook
Length of output: 3675
🏁 Script executed:
cd code/addons/vitest && find . -name "tsconfig*.json" -o -name ".eslintrc*" | head -5Repository: storybookjs/storybook
Length of output: 80
🏁 Script executed:
cat code/addons/vitest/tsconfig.jsonRepository: 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>&1Repository: storybookjs/storybook
Length of output: 473
🏁 Script executed:
cat -n code/addons/vitest/src/vitest-provided-context.d.tsRepository: 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.
There was a problem hiding this comment.
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 buildinitialGlobals. - Add generic
reportspersistence tocurrentRunwhile retaininga11yReportsfor 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.
| @@ -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; | |||
There was a problem hiding this comment.
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.
| @@ -377,6 +389,8 @@ export class VitestManager { | |||
| await this.vitestRestartPromise; | |||
| } | |||
|
|
|||
| this.provideRunConfig(); | |||
|
|
|||
| this.resetGlobalTestNamePattern(); | |||
|
|
|||
| await this.cancelCurrentRun(); | |||
There was a problem hiding this comment.
provideRunConfig() is only called from runTests(). The watch-mode path (runAffectedTestsAfterChange → runTestsWithState → runTestSpecifications) 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.
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:
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
--projectflag that we rely on), using the followingpackage.jsonand Vitest config:package.json
Vite config
Documentation
MIGRATION.MD
Checklist for Maintainers
When this PR is ready for testing, make sure to add
ci:normal,ci:mergedorci:dailyGH label to it to run a specific set of sandboxes. The particular set of sandboxes can be found incode/lib/cli-storybook/src/sandbox-templates.tsMake 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 runningnpx storybook@0.0.0-pr-34518-sha-7e3dc40d sandboxor in an existing project withnpx storybook@0.0.0-pr-34518-sha-7e3dc40d upgrade.More information
0.0.0-pr-34518-sha-7e3dc40djeppe/addon-vitest-provide7e3dc40d1775817823)To request a new release of this pull request, mention the
@storybookjs/coreteam.core team members can create a new canary release here or locally with
gh workflow run --repo storybookjs/storybook publish.yml --field pr=34518Summary by CodeRabbit
Release Notes
Refactor
Tests