storybook
storybook copied to clipboard
Supporting Vitest
Is your feature request related to a problem? Please describe
vitest does not work straightforwardly with Storybook in the way that jest
does. In my particular case, I run into this when attempting to use Interactions. The Interactions README makes explicit reference to using @storybook/jest for assertions (which itself relies on storybookjs/expect - a re-export of jest
's expect w/ node modules polyfilled). Usage of @storybook/jest
's implementation of expect
is also linted for by eslint-plugin-storybook/use-storybook-expect, which I would hope to liberate.
Note that while I haven't run into this yet, I expect further problems when trying to use @storybook/testing-react, though perhaps in that case using this utility inside vitest
tests will work more straightforwardly.
In general, I'm making a go of using vitest
in place of jest
in my projects. My hope is that I'll be able to reconcile this pursuit with using Storybook including the newer features like Interactions (which I think is amazing, by the way!).
Describe the solution you'd like
I would love for there to be a way to use Interactions and assertions without relying on jest
in any way. My assumption is that this would require a vitest
equivalent to @storybook/jest
. Subsequently, I'm hoping that @storybook/testing-react
will work well in the case of vitest
, in case it currently first-classes jest
.
Describe alternatives you've considered
I've considered moving my test suite back to jest
because I so enjoy using Storybook and the Interactions feature in particular. With that said, vitest
has been a huge improvement over jest
for my use cases, and so I'm not eager to make the move back to jest
.
Are you able to assist to bring the feature to reality?
Yes, I would happily work on this. I am attempting this locally for now and given some guidance or go-ahead from maintainers I think I'd be happy to take on the brunt of the work here.
@joshacheson Have you made any progress on this locally, and would you be able to share some of that?
Hi @joshacheson sorry I missed this! Would love to figure out what is required here. All of the interactions work runs in the browser, so it should work completely independently of Jest. However, I'm fuzzy on the details of how it will work in Vitest. @yannbf @ghengeveld @IanVS will probably have a lot to say here.
Hi @joshacheson. I'd like to hear more about your reasons not to use expect
from jest. As far as I know (things may have changed since I last checked), vitest uses chai, and is not really designed to run in a browser.
I believe other than expect, storybook uses jest.fn
for mocking. Perhaps vi.fn
is compatible enough to find a way to swap it out? Is vi.fn
completely compatible with the jest-mock APIs?
vitest has been a huge improvement over jest for my use cases
Can you explain a bit more about your use cases, and the types of improvements you've seen?
Hey @joshacheson I'm quite interested to hear about your experience! Have you actually run into any trouble yet?
@storybook/testing-library
is a wrapper on top of @testing-library/dom
which just does general queries in HTML Elements and it's not really connected to Jest.
@storybook/testing-react
doesn't really do any testing and is not connected to any test runner – it really just composes stories into a React component that you can use in your tests (or anywhere else like documentation websites etc).
About the use-expect
lint rule, you could definitely turn it off by overriding the lint configuration.
Hi folks. This is many replies , and sorry for being slow on my end to reply. I'll do my best to respond to everything.
Something useful to say to probably everyone - I circumvented any of my own concerns by having vitest
resolve @storybook/jest
to vitest
. This works great because both packages export { expect }
, and the API is roughly the same (even though the implementation details appear very different. This gives me a best of both worlds - I'm able to use @storybook/jest
when I'm actually using the browser/storybook runtime, and I'm getting vitest
's expect in my vitest
runtime.
@IanVS I don't use expect
from jest
because I want to run my interactions in my test runner (vitest
) which ships its own expect
. I'm not sure what the implications would be of using jest
's expect
long term in my tests when I'm running with vitest
, but didn't want to take any chances.
Perhaps vi.fn is compatible enough to find a way to swap it out? Is vi.fn completely compatible with the jest-mock APIs?
For what it's worth, this appears to be the case. In general vitest
supports almost the entire api of jest
.
Can you explain a bit more about your use cases, and the types of improvements you've seen?
Yes. And sorry that this might not be interesting. My use cases aren't very unique or specific, just that:
- I use
vite
for dev and build stuff. Usingvitest
allows me to more or less share my existing config, which is much nicer than having to have a separatejest
configuration. -
vitest
is way faster for me thanjest
was, especially in dev/watch mode.
@yannbf essentially ever since doing my resolution workaround, I've had no issues. So I'm good for now. It is possible that my workaround will not work long term if APIs diverge.
@joshacheson are you able to share an implementation of your work around?
@fazulk yep, here's a snippet that would belong in a vitest.config.js
or whatever file one is using to configure vitest
.
export default defineConfig({
...otherPartsOfConfig,
resolve: {
alias: {
/**
* Storybook (specifically the interactions addon) requires that we use their
* instrumented version of jest-expect. So our storybook does so. To make
* these interactions still work in vitest we have @storybook/jest aliased
* to resolve to vitest which, critically, exports { expect } as well.
*/
'@storybook/jest': 'vitest',
},
},
});
@joshacheson were you able to use vi.fn
in this workaround? The moment I try to use it the resulting import from vitest causes Storybook to fail in the Pre-bundling phase.
Error details:
> start-storybook -p 6006
info @storybook/react v6.4.19
info
info => Loading presets
info => Loading custom manager config
info => Loading custom manager config
info => Ignoring cached manager due to change in manager config
ℹ 「wdm」: wait until bundle finished:
Pre-bundling dependencies:
react
msw
@chakra-ui/react
(...and 67 more)
(this will be run only when your dependencies or config have changed)
✘ [ERROR] No matching export in "browser-external:fs" for import "existsSync"
node_modules/local-pkg/dist/shared.mjs:54:9:
54 │ import { existsSync, promises as fs3 } from "fs";
╵ ~~~~~~~~~~
✘ [ERROR] No matching export in "browser-external:fs" for import "promises"
node_modules/local-pkg/dist/shared.mjs:54:21:
54 │ import { existsSync, promises as fs3 } from "fs";
╵ ~~~~~~~~
✘ [ERROR] No matching export in "browser-external:node:fs" for import "promises"
node_modules/local-pkg/dist/shared.mjs:62:13:
62 │ import fs, { promises as fsPromises } from "node:fs";
╵ ~~~~~~~~
✘ [ERROR] No matching export in "browser-external:node:fs" for import "promises"
node_modules/local-pkg/dist/shared.mjs:235:14:
235 │ import fs2, { promises as fsPromises2 } from "node:fs";
╵ ~~~~~~~~
✘ [ERROR] No matching export in "browser-external:path" for import "join"
node_modules/local-pkg/index.mjs:1:9:
1 │ import { join, dirname } from 'path'
╵ ~~~~
✘ [ERROR] No matching export in "browser-external:path" for import "dirname"
node_modules/local-pkg/index.mjs:1:15:
1 │ import { join, dirname } from 'path'
╵ ~~~~~~~
✘ [ERROR] No matching export in "browser-external:fs" for import "promises"
node_modules/local-pkg/index.mjs:2:9:
2 │ import { promises as fs, existsSync } from 'fs'
╵ ~~~~~~~~
✘ [ERROR] No matching export in "browser-external:fs" for import "existsSync"
node_modules/local-pkg/index.mjs:2:25:
2 │ import { promises as fs, existsSync } from 'fs'
╵ ~~~~~~~~~~
✘ [ERROR] No matching export in "browser-external:module" for import "createRequire"
node_modules/local-pkg/index.mjs:3:9:
3 │ import { createRequire } from 'module'
╵ ~~~~~~~~~~~~~
✘ [ERROR] No matching export in "browser-external:fs" for import "promises"
node_modules/vitest/dist/chunk-runtime-chain.d117aaa7.js:6:13:
6 │ import fs, { promises } from 'fs';
╵ ~~~~~~~~
ERR! Error: Build failed with 10 errors:
Thank you @joshacheson for this issue and your exploration there! :)
My storybook repo setup uses @storybook/builder-vite
and vitest
for testing, and although I didn't use the play
function yet, I was wondering how would it work for me, and I had a similar idea of aliasing @storybook/jest
to vitest
, so I am happy to see it somewhat does the job for you.
I am definitely interested in helping with the development needed to support vitest
in storybook. I hope to play with it locally for now and see how far I can get with it, and what's involved :)
hame some problem with:
Vite-builder \ Interaction tests \ Vitest
When i do import { anything } from 'vitest'
in storybook i see:
Cannot read properties of undefined (reading 'includes')
i tried resolve alias and import like @storybook/jest
. same result
@cwe-spb can you describe what your tests and stories look like, and how you are attempting to mix storybook and vitest?
Same here. Added the alias and it seems to be working. But as soon as I import anything from @storybook/jest
aliased to vitest
I also get a Cannot read properties of undefined (reading 'includes')
.
Just trying to get the example from the docs working.
@n0rdlicht can you explain what you want to accomplish by using vitest this way? @storybook/jest
is really just two small packages, expect
and jest-mock
. Are you wanting to use vitest's assertions (chai) and it's mocking system?
vitest has no plans on supporting jest.requireActual that @storybook/addon-storyshots
uses here (file & line).
Could you please clarify plans on supporting vitest
?
- also related to ongoing #21156
Could you please clarify plans on supporting vitest?
Support could mean several different things. In what way do you want to use it with storybook, @o-alexandrov?
Could you please clarify plans on supporting vitest?
Support could mean several different things. In what way do you want to use it with storybook, @o-alexandrov?
In our case, I'd like @storybook/addon-storyshots
:
- not to use
jest.requireActual
, so it could be possible to runstoryshots
test file w/vitest
- or Storybook could make this file (mentioned in prior message) configurable
A nice-to-have (could be done separately later) is:
- remove
jest
-related deps fromdependencies
inpackage.json
, sovitest
users won't need to install a long list of unused dependencies-
jest
-related deps should be listed indevDependencies
w/ a note in the readme about the need to install them, if usingjest
-
EDIT: thank you very much for the work on the test-runner
you mentioned below
Thanks for sharing. I will note that addon-storyshots is unofficially deprecated and will not be receiving updates. The preferred way to generate snapshots is with the test runner: https://github.com/storybookjs/test-runner#image-snapshot-recipe. It also uses jest, but only as a simple test runner, and it's planned to investigate what it would take to move it to vitest.
I wanted to shime in, I use the storybook test runner with a vite storybook v7, runing my unit test with vitest, but I still need to keep a jest configuration to run the because of this and I would love to remove it and only keeping my vite/vitest config
Adding this alias worked for me, { find: '@storybook/jest', replacement: 'vitest' },
@fazulk yep, here's a snippet that would belong in a
vitest.config.js
or whatever file one is using to configurevitest
.export default defineConfig({ ...otherPartsOfConfig, resolve: { alias: { /** * Storybook (specifically the interactions addon) requires that we use their * instrumented version of jest-expect. So our storybook does so. To make * these interactions still work in vitest we have @storybook/jest aliased * to resolve to vitest which, critically, exports { expect } as well. */ '@storybook/jest': 'vitest', }, }, });
For anyone using the proposed alias workaround, keep in mind that @storybook/jest contains an instrumented expect
which makes the addon-interactions debugger work, and without it you will experience issues.
Thanks for sharing. I will note that addon-storyshots is unofficially deprecated and will not be receiving updates. The preferred way to generate snapshots is with the test runner: https://github.com/storybookjs/test-runner#image-snapshot-recipe. It also uses jest, but only as a simple test runner, and it's planned to investigate what it would take to move it to vitest.
Make it official, please. What's on the manual is misguiding some of us into sinking a lot of time to try to make things work.
Going back to the subject, please support vitest!
Trying to set up jest to work with typescript, storybook, importing modules, is a real headache of contradicting instructions disseminated throughout partially explained github issues, stackoverflows and medium blogs.
I mean, if test-runner is the way to go, it should just work with tests, without needing us to jumping through hoops :(
please support vitest!
In what manner would you like to use Storybook together with Vitest?
I'd like Storybook to be able to have Storybook use vitest instead of jest when it's installed on a vite project.
@squidjam thanks. Can you talk a little bit more about the reasons for that, i.e. what features / behavior you want to see which switching to vitest could provide?
If at all possible, I'd like that if vitest is detected, test-runner uses it instead of jest. From this one could have snapshot testing, interaction tests with Playwright, test coverage.
Setting jest up involves a lot of steps that are already included in vitest.
I haven't seen yet a way to integrate React, Typescript, Jest and Storybook in a way that testing for more than just smoke would not show errors when my components import another one from a monorepo for a component library.
The answer might be very easy to implement, but getting to it or figuring it out is not.
If Storybook's ideal is to have a friendly out of the box experience with minimal setting up for the basic cases, this could be an improvement.
I would join to @squidjam Storybook is sitting "on top" of a project. Ideally it will blend into its build process so anything configured in the bundle like aliases, plugins , transformers and etc wouldn't need to be reconfigured (same source code). I've created the kind of setup in the past with vite &vitest. But it would be nice if those external tools like testing frameworks and etc could have been configured. To fit the source code and not vice versa
What kind of configurations are y'all making to jest in storybook test runner? Jest doesn't process the source code at all, it just finds the test files and watches for changes. Then the test runner sends them off to playwright.
@IanVS Simplest example, aliases. react-refresh plugin...
Those won't have any impact on jest in the test runner though, and are not necessary to configure for the test runner. Like I said, jest does not process your code. Storybook does when it renders into a browser, and if you're using a vite-based storybook framework, we do extend from your normal Vite config.
Throwing a hat in the ring for "why vitest over jest"
- Native ESM support
- Native TS support
- Already using vite as the storybook build tool
- Multithreaded by default with totally separated and independently analyzed test files (no tests from file A can impact the results in file B)
- Loudly fails on unhandled promises (Jest quietly ignores them)
- Fully Jest API compliant
My team just finished migrating our entire FE codebase from Jest to Vitest and we saw a 50% reduction in unit test runtime. Our goal is to have a standardized testing library across the board. If someone is using the vite builder for storybook, it would be great if it came with bindings to use vitest instead of jest (if its detected in pkg.json), as @squidjam mentioned.