Skip to content

Conversation

@yannbf
Copy link
Member

@yannbf yannbf commented Oct 27, 2025

Closes #32804, Closes #32806

What I did

  1. Only add /// <reference types="vitest/config" /> to vite.config files,
    not vitest.config files, because vitest.config files already have the
    vitest/config types available by default.

  2. Modified the updateConfigFile function in updateVitestFile.ts to:

  • Extract the coverage property before creating workspace/projects items
  • Filter it out from the test config that goes into the array
  • Preserve it at the top-level test object where it belongs

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

This section is mandatory for all contributions. If you believe no manual test is necessary, please state so explicitly. Thanks!

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-32844-sha-03847137. Try it out in a new sandbox by running npx [email protected] sandbox or in an existing project with npx [email protected] upgrade.

More information
Published version 0.0.0-pr-32844-sha-03847137
Triggered by @yannbf
Repository storybookjs/storybook
Branch yann/vitest-addon-setup-fixes
Commit 03847137
Datetime Mon Oct 27 12:35:09 UTC 2025 (1761568509)
Workflow run 18841123309

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=32844

Summary by CodeRabbit

  • Bug Fixes

    • Smarter insertion of Vitest type references to avoid duplicates and only add them where appropriate.
    • Improved handling of merged/configured Vitest setups so coverage settings stay at the top level and are preserved across workspace/project configurations.
  • Tests

    • Expanded test coverage for complex Vitest config merge scenarios and multi-project/workspace setups.

Copilot AI and others added 2 commits October 27, 2025 07:25
Only add `/// <reference types="vitest/config" />` to vite.config files,
not vitest.config files, because vitest.config files already have the
vitest/config types available by default.

Co-authored-by: yannbf <[email protected]>
When transforming vitest configs with coverage settings, the addon now correctly:
- Extracts the coverage property from the existing test config
- Keeps it at the top-level test object (where it's global)
- Moves other test properties to workspace/projects array items
- Adds test cases to verify the fix works for both workspace and projects modes

Co-authored-by: yannbf <[email protected]>
@yannbf yannbf added this to the 10 non-blocking milestone Oct 27, 2025
@yannbf yannbf added bug ci:merged Run the CI jobs that normally run when merged. addon: vitest labels Oct 27, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 27, 2025

📝 Walkthrough

Walkthrough

Adds conditional insertion of triple-slash Vitest type references (only to vite.config when absent), and refactors mergeConfig/defineConfig handling to extract test/coverage objects, relocating coverage to top-level when workspace/projects exist and injecting sanitized test configs into projects.

Changes

Cohort / File(s) Summary
Reference insertion logic
code/addons/vitest/src/postinstall.ts
Introduces shouldAddReference and only prepends /// <reference types="vitest/config" /> when the target is a vite.config file and no existing reference exists; avoids adding the reference to vitest.config files.
MergeConfig / test extraction & coverage relocation
code/addons/vitest/src/updateVitestFile.ts
Simplifies mergeConfig applicability, collects config objects from mergeConfig args (defineConfig calls and plain objects), selects a target config object, extracts an existing test property and any coverage child, removes coverage from per-project configs, prepends coverage to top-level test when workspace/projects are present, and updates merging to use the chosen target config object's properties.
Tests for workspace/projects and coverage behavior
code/addons/vitest/src/updateVitestFile.test.ts
Adds/expands tests covering mergeConfig without defineConfig calls, ensures top-level coverage preservation and sanitized project test insertion across template variants (including defineConfig and Vitest 3.2+ patterns), and updates expected diffs/snapshots with new imports and workspace/projects wiring.

Sequence Diagram(s)

sequenceDiagram
    participant Postinstall as postinstall.ts
    participant Updater as updateVitestFile.ts
    participant FS as File System

    Note over Postinstall: On postinstall/update file
    Postinstall->>FS: Read config file
    alt file is vite.config AND no existing reference
        Postinstall->>FS: Prepend triple-slash reference
    else
        Postinstall->>FS: Leave formatted content unchanged
    end

    Note over Updater: When merging template into existing config
    Updater->>FS: Parse existing config AST
    Updater->>Updater: Collect config objects from mergeConfig args\n(defineConfig object args + plain objects)
    Updater->>Updater: Select target config object (prefer one with `test`)
    alt target has `test`
        Updater->>Updater: Extract `coverage` from `test` (if present)
        Updater->>Updater: Remove `coverage` from project test configs
        Updater->>Updater: Prepend extracted `coverage` to top-level `test`
        Updater->>Updater: Move `test` into workspace/projects entries
    else
        Updater->>Updater: No relocation needed
    end
    Updater->>FS: Write updated config file
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Pay extra attention to AST manipulation in updateVitestFile.ts (correct selection of target config object, safe removal/insertion of properties, ordering).
  • Validate new/updated test snapshots in updateVitestFile.test.ts for fidelity to intended behavior.
  • Confirm conditional logic in postinstall.ts covers all config filename/contents combinations.

Possibly related PRs

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch yann/vitest-addon-setup-fixes

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7ad957f and 0384713.

📒 Files selected for processing (2)
  • code/addons/vitest/src/updateVitestFile.test.ts (2 hunks)
  • code/addons/vitest/src/updateVitestFile.ts (5 hunks)
🧰 Additional context used
📓 Path-based instructions (7)
code/**/*.{test,spec}.{ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

code/**/*.{test,spec}.{ts,tsx}: Place all test files under the code/ directory
Name test files as *.test.ts, *.test.tsx, *.spec.ts, or *.spec.tsx

Files:

  • code/addons/vitest/src/updateVitestFile.test.ts
**/*.test.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/spy-mocking.mdc)

**/*.test.{ts,tsx,js,jsx}: Use vi.mock() with the spy: true option for all package and file mocks in Vitest tests
Place all mocks at the top of the test file before any test cases
Use vi.mocked() to type and access mocked functions
Implement mock behaviors in beforeEach blocks
Mock all required dependencies that the test subject uses
Mock implementations should be placed in beforeEach blocks
Each mock implementation should return a Promise for async functions
Mock implementations should match the expected return type of the original function
Use vi.mocked() to access and implement mock behaviors
Mock all required properties and methods that the test subject uses
Avoid direct function mocking without vi.mocked()
Avoid mock implementations outside of beforeEach blocks
Avoid mocking without the spy: true option
Avoid inline mock implementations within test cases
Avoid mocking only a subset of required dependencies
Mock at the highest level of abstraction needed
Keep mock implementations simple and focused
Use type-safe mocking with vi.mocked()
Document complex mock behaviors
Group related mocks together

Files:

  • code/addons/vitest/src/updateVitestFile.test.ts
**/*.{js,jsx,json,html,ts,tsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{js,jsx,json,html,ts,tsx,mjs}: Run Prettier formatting on changed files before committing
Run ESLint on changed files and fix all errors/warnings before committing (use yarn lint:js:cmd <file>)

Files:

  • code/addons/vitest/src/updateVitestFile.test.ts
  • code/addons/vitest/src/updateVitestFile.ts
**/*.@(test|spec).{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.@(test|spec).{ts,tsx,js,jsx}: Unit tests should import and execute the functions under test rather than only asserting on syntax patterns
Mock external dependencies in tests using vi.mock() (e.g., filesystem, loggers)

Files:

  • code/addons/vitest/src/updateVitestFile.test.ts
**/*.{ts,tsx,js,jsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Export functions from modules when they need to be unit-tested

Files:

  • code/addons/vitest/src/updateVitestFile.test.ts
  • code/addons/vitest/src/updateVitestFile.ts
code/**/*.{ts,tsx,js,jsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

In application code, use Storybook loggers instead of console.* (client code: storybook/internal/client-logger; server code: storybook/internal/node-logger)

Files:

  • code/addons/vitest/src/updateVitestFile.test.ts
  • code/addons/vitest/src/updateVitestFile.ts
{code/**,scripts/**}/**/*.{ts,tsx,js,jsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Do not use console.log, console.warn, or console.error directly unless in isolated files where importing loggers would significantly increase bundle size

Files:

  • code/addons/vitest/src/updateVitestFile.test.ts
  • code/addons/vitest/src/updateVitestFile.ts
🧠 Learnings (2)
📓 Common learnings
Learnt from: CR
PR: storybookjs/storybook#0
File: .cursorrules:0-0
Timestamp: 2025-09-17T08:11:04.287Z
Learning: Applies to code/vitest.workspace.ts : Keep and use the Vitest workspace configuration at code/vitest.workspace.ts
📚 Learning: 2025-09-17T08:11:04.287Z
Learnt from: CR
PR: storybookjs/storybook#0
File: .cursorrules:0-0
Timestamp: 2025-09-17T08:11:04.287Z
Learning: Applies to code/vitest.workspace.ts : Keep and use the Vitest workspace configuration at code/vitest.workspace.ts

Applied to files:

  • code/addons/vitest/src/updateVitestFile.test.ts
  • code/addons/vitest/src/updateVitestFile.ts
🧬 Code graph analysis (1)
code/addons/vitest/src/updateVitestFile.test.ts (1)
code/addons/vitest/src/updateVitestFile.ts (2)
  • loadTemplate (9-16)
  • updateConfigFile (79-321)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: merged
  • GitHub Check: Core Unit Tests, windows-latest
🔇 Additional comments (7)
code/addons/vitest/src/updateVitestFile.test.ts (3)

614-695: LGTM! Excellent coverage for plain object mergeConfig pattern.

This test validates the new capability to handle mergeConfig with plain object expressions (not wrapped in defineConfig). The expected output correctly shows that defineConfig is imported when needed, and the existing test configuration is properly migrated into a workspace array.


857-954: LGTM! Critical test for coverage extraction with workspace.

This test validates the core fix from issue #32804: ensuring coverage remains at the top-level test object while other test properties are moved into the workspace array. The expected output at lines 916-927 confirms that coverage is preserved at the top level (appearing before workspace), while per-workspace settings (name, environment, include) are correctly moved into the first workspace entry.


956-1053: LGTM! Comprehensive coverage for projects pattern (Vitest 3.2+).

This test validates the same coverage extraction logic for Vitest 3.2+ which uses projects instead of workspace. The expected output at lines 1015-1026 correctly shows coverage remaining at the top level while other test properties are moved into the projects array, mirroring the workspace behavior.

code/addons/vitest/src/updateVitestFile.ts (4)

126-126: LGTM! Enables support for plain object expressions in mergeConfig.

This change unconditionally enables handling of mergeConfig patterns, which is necessary to support plain object expressions (not just defineConfig wrapped objects) in mergeConfig arguments. This aligns with the new collection logic at lines 190-206.


190-220: LGTM! Robust collection and selection of config objects.

This segment implements a generalized approach to handling mergeConfig arguments:

  1. Collects both defineConfig({ ... }) wrapped objects and plain object expressions { ... } (lines 195-206)
  2. Prefers a config object that already contains a test property, falling back to the first available object (lines 208-216)
  3. Returns early if no suitable config object is found (lines 218-220)

The logic correctly handles the common patterns while gracefully skipping unsupported argument types (e.g., Identifier references to imported configs).


248-299: LGTM! Correct implementation of coverage extraction and top-level preservation.

This segment implements the core fix from issue #32804, ensuring coverage remains a top-level global setting:

  1. Lines 248-254: Extracts the coverage property from the existing test config
  2. Lines 257-264: Creates a new test config WITHOUT coverage for the workspace/projects entry
  3. Lines 267-288: Wraps the sanitized test config in a project object and adds it to the workspace/projects array
  4. Lines 291-293: Removes the original top-level test property (now redundant as it's in the array)
  5. Lines 296-299: If coverage existed, prepends it to the template's top-level test object (before workspace/projects)

The logic correctly ensures that coverage (a global setting) remains at the top level while per-workspace/project settings are moved into the array.


302-314: LGTM! Consistent merge operations with appropriate fallbacks.

All merge operations correctly target targetConfigObject.properties (the config object selected at lines 208-216), ensuring consistency regardless of whether:

  • Special workspace/projects handling occurs (line 302)
  • Template structure is unexpected (lines 305, 309)
  • No existing test config exists (line 313)

The fallback branches ensure the function remains robust when encountering edge cases while still making best-effort updates.


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

@nx-cloud
Copy link

nx-cloud bot commented Oct 27, 2025

View your CI Pipeline Execution ↗ for commit 0384713

Command Status Duration Result
nx run-many -t build --parallel=3 ✅ Succeeded 47s View ↗

☁️ Nx Cloud last updated this comment at 2025-10-27 12:51:04 UTC

Copy link
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: 0

🧹 Nitpick comments (4)
code/addons/vitest/src/postinstall.ts (2)

273-279: Use Storybook logger instead of console.error.

Replace console.error with logger.error to follow code logging guidelines for code/*.

-      console.error('Failed to install @storybook/nextjs-vite. Please install it manually');
+      logger.error('Failed to install @storybook/nextjs-vite. Please install it manually');
-      console.error('Failed to install Playwright. Please install it manually');
+      logger.error('Failed to install Playwright. Please install it manually');

Also applies to: 324-331


470-479: Optional: preserve shebang if present.

If a config file starts with a shebang, prepend the triple‑slash after it to avoid breaking executability.

-      await writeFile(
-        rootConfig,
-        shouldAddReference
-          ? '/// <reference types="vitest/config" />\n' + formattedContent
-          : formattedContent
-      );
+      const withRef = '/// <reference types="vitest/config" />\n' + formattedContent;
+      const contentToWrite = shouldAddReference
+        ? formattedContent.startsWith('#!')
+          ? formattedContent.replace(/^(#!.*\n)/, `$1/// <reference types="vitest/config" />\n`)
+          : withRef
+        : formattedContent;
+      await writeFile(rootConfig, contentToWrite);
code/addons/vitest/src/updateVitestFile.ts (1)

252-304: Good: coverage hoisted to top‑level test, projects/workspace sanitized.

This addresses the incorrect relocation of coverage into workspace/projects.

Two improvements:

  • Guard against duplicate coverage at the template’s top‑level test if it already exists.
  • Consider applying the same hoisting for defineConfig/object exports for consistency across patterns.

Minimal duplication guard diff:

-                if (coverageProp && templateTestProp.value.type === 'ObjectExpression') {
-                  templateTestProp.value.properties.unshift(coverageProp);
-                }
+                if (coverageProp && templateTestProp.value.type === 'ObjectExpression') {
+                  const hasCoverageAlready = templateTestProp.value.properties.some(
+                    (p) =>
+                      p.type === 'ObjectProperty' &&
+                      p.key.type === 'Identifier' &&
+                      p.key.name === 'coverage'
+                  );
+                  if (!hasCoverageAlready) {
+                    // Prefer cloning to avoid cross-AST node reuse
+                    const node = (t as any).cloneNode
+                      ? (t as any).cloneNode(coverageProp, /* deep */ true)
+                      : coverageProp;
+                    templateTestProp.value.properties.unshift(node);
+                  }
+                }

If helpful, I can draft a small helper (hoistCoverageFromTest(testObj: t.ObjectExpression, into: t.ObjectExpression)) and reuse it for defineConfig/object branches.

code/addons/vitest/src/updateVitestFile.test.ts (1)

10-16: Align vi.mock usage with repo test guidelines.

Use spy: true for mocks to keep type‑safe spying per guidelines.

-vi.mock('storybook/internal/node-logger', () => ({
+vi.mock('storybook/internal/node-logger', { spy: true }, () => ({
   logger: {
     info: vi.fn(),
     warn: vi.fn(),
     error: vi.fn(),
   },
 }));
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d075427 and 7ad957f.

📒 Files selected for processing (3)
  • code/addons/vitest/src/postinstall.ts (1 hunks)
  • code/addons/vitest/src/updateVitestFile.test.ts (1 hunks)
  • code/addons/vitest/src/updateVitestFile.ts (3 hunks)
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{js,jsx,json,html,ts,tsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{js,jsx,json,html,ts,tsx,mjs}: Run Prettier formatting on changed files before committing
Run ESLint on changed files and fix all errors/warnings before committing (use yarn lint:js:cmd <file>)

Files:

  • code/addons/vitest/src/postinstall.ts
  • code/addons/vitest/src/updateVitestFile.ts
  • code/addons/vitest/src/updateVitestFile.test.ts
**/*.{ts,tsx,js,jsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Export functions from modules when they need to be unit-tested

Files:

  • code/addons/vitest/src/postinstall.ts
  • code/addons/vitest/src/updateVitestFile.ts
  • code/addons/vitest/src/updateVitestFile.test.ts
code/**/*.{ts,tsx,js,jsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

In application code, use Storybook loggers instead of console.* (client code: storybook/internal/client-logger; server code: storybook/internal/node-logger)

Files:

  • code/addons/vitest/src/postinstall.ts
  • code/addons/vitest/src/updateVitestFile.ts
  • code/addons/vitest/src/updateVitestFile.test.ts
{code/**,scripts/**}/**/*.{ts,tsx,js,jsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Do not use console.log, console.warn, or console.error directly unless in isolated files where importing loggers would significantly increase bundle size

Files:

  • code/addons/vitest/src/postinstall.ts
  • code/addons/vitest/src/updateVitestFile.ts
  • code/addons/vitest/src/updateVitestFile.test.ts
code/**/*.{test,spec}.{ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

code/**/*.{test,spec}.{ts,tsx}: Place all test files under the code/ directory
Name test files as *.test.ts, *.test.tsx, *.spec.ts, or *.spec.tsx

Files:

  • code/addons/vitest/src/updateVitestFile.test.ts
**/*.test.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/spy-mocking.mdc)

**/*.test.{ts,tsx,js,jsx}: Use vi.mock() with the spy: true option for all package and file mocks in Vitest tests
Place all mocks at the top of the test file before any test cases
Use vi.mocked() to type and access mocked functions
Implement mock behaviors in beforeEach blocks
Mock all required dependencies that the test subject uses
Mock implementations should be placed in beforeEach blocks
Each mock implementation should return a Promise for async functions
Mock implementations should match the expected return type of the original function
Use vi.mocked() to access and implement mock behaviors
Mock all required properties and methods that the test subject uses
Avoid direct function mocking without vi.mocked()
Avoid mock implementations outside of beforeEach blocks
Avoid mocking without the spy: true option
Avoid inline mock implementations within test cases
Avoid mocking only a subset of required dependencies
Mock at the highest level of abstraction needed
Keep mock implementations simple and focused
Use type-safe mocking with vi.mocked()
Document complex mock behaviors
Group related mocks together

Files:

  • code/addons/vitest/src/updateVitestFile.test.ts
**/*.@(test|spec).{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.@(test|spec).{ts,tsx,js,jsx}: Unit tests should import and execute the functions under test rather than only asserting on syntax patterns
Mock external dependencies in tests using vi.mock() (e.g., filesystem, loggers)

Files:

  • code/addons/vitest/src/updateVitestFile.test.ts
🧠 Learnings (2)
📓 Common learnings
Learnt from: CR
PR: storybookjs/storybook#0
File: .cursorrules:0-0
Timestamp: 2025-09-17T08:11:04.287Z
Learning: Applies to code/vitest.workspace.ts : Keep and use the Vitest workspace configuration at code/vitest.workspace.ts
📚 Learning: 2025-09-17T08:11:04.287Z
Learnt from: CR
PR: storybookjs/storybook#0
File: .cursorrules:0-0
Timestamp: 2025-09-17T08:11:04.287Z
Learning: Applies to code/vitest.workspace.ts : Keep and use the Vitest workspace configuration at code/vitest.workspace.ts

Applied to files:

  • code/addons/vitest/src/updateVitestFile.test.ts
🧬 Code graph analysis (1)
code/addons/vitest/src/updateVitestFile.test.ts (1)
code/addons/vitest/src/updateVitestFile.ts (2)
  • loadTemplate (9-16)
  • updateConfigFile (79-325)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: merged
  • GitHub Check: Core Unit Tests, windows-latest
🔇 Additional comments (3)
code/addons/vitest/src/postinstall.ts (1)

471-479: Correctly scope triple‑slash directive to vite.config files.

This guards against adding it to vitest.config.* and avoids duplicates. Matches #32806.

code/addons/vitest/src/updateVitestFile.test.ts (2)

775-867: Tests validate coverage hoist with workspace.

Solid snapshot reflecting coverage remaining top‑level and workspace receiving sanitized test.


869-961: Tests validate coverage hoist with projects.

Confirms correct behavior for Vitest 3.2+ projects array.

@yannbf yannbf self-assigned this Oct 27, 2025
@yannbf yannbf merged commit a0e584f into next Oct 27, 2025
72 checks passed
@yannbf yannbf deleted the yann/vitest-addon-setup-fixes branch October 27, 2025 13:56
@github-actions github-actions bot mentioned this pull request Oct 27, 2025
6 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

addon: vitest bug ci:merged Run the CI jobs that normally run when merged.

Projects

None yet

3 participants