Skip to content

feat(vitest): add formatTitle and Storybook 10 viewport globals#330

Draft
kasperpeulen wants to merge 19 commits intomainfrom
kasper/vitest-snapshots-as-modes
Draft

feat(vitest): add formatTitle and Storybook 10 viewport globals#330
kasperpeulen wants to merge 19 commits intomainfrom
kasper/vitest-snapshots-as-modes

Conversation

@kasperpeulen
Copy link
Copy Markdown
Contributor

@kasperpeulen kasperpeulen commented May 2, 2026

Summary

  • Add an opt-in formatTitle callback for projects that want to customize the title generated from Vitest tests.
  • Keep the default title behavior unchanged: [projectName?, filePath, ...testPath].
  • Emit Storybook 10 viewport globals (globals.viewport as w{width}h{height}, matching the archive snapshot key) so archive stories open in the captured viewport.

Why

Vitest gives the plugin a title path array. For example:

["app/auth/sign-in/page.test.tsx", "shows the magic link error banner"]

Today writeTestResult turns that array into the Storybook title by stripping test file extensions and joining the parts with /:

app/auth/sign-in/page/shows the magic link error banner

That default is still correct and remains unchanged. The problem is how this appears in Chromatic: Chromatic commonly uses the final / segment as the visible leaf/display name in the UI. For the default title above, that means the row users scan is just:

shows the magic link error banner

The useful route/file context (app/auth/sign-in/page) is hidden in the parent path, which makes Vitest snapshots harder to scan in Chromatic.

formatTitle lets a project opt into a custom title string before writeTestResult writes the archive. Returning a single string also keeps the API simpler than exposing the internal title-path array. If a project wants Storybook grouping, it can still include / in the returned string.

API

chromaticPlugin({
  formatTitle({ filePath, testPath, projectName }) {
    const route = filePath
      .replace(/\/page\.test\.[tj]sx?$/, '')
      .replace(/\.test\.[tj]sx?$/, '')
      .replaceAll('/', '∕');

    return `${route}${testPath.join(' ')}`;
  },
});

This can turn the default path:

app/auth/sign-in/page/shows the magic link error banner

into one visible title:

app∕auth∕sign-in → shows the magic link error banner

Named snapshots remain separate stories under that title, for example:

app∕auth∕sign-in → shows the magic link error banner / Dark desktop

Viewports

Storybook 10 selects the active viewport through globals.viewport, together with parameters.viewport (available sizes / default). See Defining the viewport for a story.

For archive stories we emit the captured archive viewport key as the story global, e.g.:

{
  "globals": { "viewport": "w1280h720" }
}

That value matches the archive snapshot filename segment, so the renderer can fetch the matching rrweb snapshot. The archive still includes parameters.viewport.viewports and parameters.viewport.defaultViewport so the viewport option is defined for Storybook.

Test plan

  • yarn vitest run packages/shared/src/write-archive/stories-files.test.ts packages/shared/src/write-archive/index.test.ts packages/vitest/src/node/commands.test.ts
  • yarn lint
  • Patched @chromatic-com/vitest into a consumer app and confirmed Vitest browser tests + chromatic --vitest complete successfully (expect baseline diffs until snapshots are accepted).

Allow Vitest archives to map files to components, tests to stories, and named snapshots to Chromatic modes so multi-snapshot tests render as one logical test group.
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 2, 2026

⚠️ No Changeset found

Latest commit: e427f04

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@kasperpeulen kasperpeulen changed the title feat(vitest): support snapshots as modes feat(vitest): group snapshots by test title May 2, 2026
@kasperpeulen kasperpeulen marked this pull request as draft May 2, 2026 08:40
@kasperpeulen kasperpeulen changed the title feat(vitest): group snapshots by test title feat(vitest): allow custom title path formatting May 2, 2026
@codecov
Copy link
Copy Markdown

codecov Bot commented May 2, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 91.66%. Comparing base (946a9e6) to head (e427f04).

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #330      +/-   ##
==========================================
+ Coverage   91.48%   91.66%   +0.17%     
==========================================
  Files          22       22              
  Lines         611      624      +13     
  Branches      114      120       +6     
==========================================
+ Hits          559      572      +13     
  Misses         49       49              
  Partials        3        3              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@kasperpeulen kasperpeulen changed the title feat(vitest): allow custom title path formatting feat(vitest): format title paths and set viewport globals May 2, 2026
@kasperpeulen kasperpeulen changed the title feat(vitest): format title paths and set viewport globals feat(vitest): add formatTitle and viewport globals May 2, 2026
@kasperpeulen kasperpeulen requested a review from AriPerkkio May 2, 2026 16:23
….viewport

- Emit globals.viewport (width-height) alongside existing parameters.viewport
- Map globals string to w*h* archive snapshot names in preview fetch
- Guard defaultViewport fallback when parameters.viewport is absent
- Clarify formatTitle option JSDoc
…eview mapping

globals.viewport now matches defaultViewport and snapshot filenames. Preview
uses string globals as the archive key directly.
@kasperpeulen kasperpeulen force-pushed the kasper/vitest-snapshots-as-modes branch from 42b7490 to 5a9f5f5 Compare May 2, 2026 17:20
@kasperpeulen kasperpeulen changed the title feat(vitest): add formatTitle and viewport globals feat(vitest): add formatTitle and Storybook 10 viewport globals May 2, 2026
SB 10 reads options (not legacy viewports), so archives fell back to
MINIMAL_VIEWPORTS (e.g. 320x568) in the toolbar. Add viewport type for the
addon; keep defaultViewport for archive preview fetch fallback.
}
}

function getTitlePath(test: TestCase, formatTitle?: ResolvedOptions['formatTitle']): string[] {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I would actually propose function formatTitle(test: TestCase): string as public API. Internally its default value would be the current getNames. This would allow more complex configuration for user.

Copy link
Copy Markdown
Member

@AriPerkkio AriPerkkio May 4, 2026

Choose a reason for hiding this comment

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

@kasperpeulen how does the Storybook Chromatic integration set the titles? Does it use the CSF's component property? Should we have have something similar here?

import { test } from "vitest";
import { setComponent } from "@chromatic-com/vitest";
import Accordion from "../src/components/accordion";

setComponent("Accordion"); 

test("opens when...", ...)
test("closes when ...", ...)

This setComponent would internally just set task.meta.__chromatic_componentName, that we would then use in getNames and possible user defined formatTitles(test: testCase, componentName?: string): string.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Let's talk about this on wednesday!

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

As these Viewport fixes affect all test runners, could you move them to a separate PR? We'll need changesets to reference that PR in Playwright and Cypress release notes too.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Split to #339

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.

I think these viewport changes got Chromatic baselines changed, and made main fail. Some globals + viewports parts in URLs made stories of published Storybook not load.

We should revert these tests once all fixes are complete:

  • packages/cypress/tests/cypress/e2e/storybook.cy.ts
  • packages/playwright/tests/storybook.spec.ts

See #331

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants