storybook icon indicating copy to clipboard operation
storybook copied to clipboard

Supporting Vitest

Open joshacheson opened this issue 3 years ago • 12 comments

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 avatar Jan 24 '22 19:01 joshacheson

@joshacheson Have you made any progress on this locally, and would you be able to share some of that?

lukasluecke avatar Mar 01 '22 14:03 lukasluecke

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.

shilman avatar Mar 01 '22 15:03 shilman

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?

IanVS avatar Mar 01 '22 15:03 IanVS

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.

yannbf avatar Mar 02 '22 01:03 yannbf

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. Using vitest allows me to more or less share my existing config, which is much nicer than having to have a separate jest configuration.
  • vitest is way faster for me than jest 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 avatar Mar 10 '22 21:03 joshacheson

@joshacheson are you able to share an implementation of your work around?

fazulk avatar Mar 11 '22 01:03 fazulk

@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 avatar Mar 11 '22 16:03 joshacheson

@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:

jrudoljo avatar Mar 15 '22 20:03 jrudoljo

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 :)

szympajka avatar Apr 13 '22 08:04 szympajka

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 avatar Aug 23 '22 08:08 cwe-spb

@cwe-spb can you describe what your tests and stories look like, and how you are attempting to mix storybook and vitest?

IanVS avatar Aug 23 '22 14:08 IanVS

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 avatar Sep 21 '22 10:09 n0rdlicht

@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?

IanVS avatar Sep 22 '22 00:09 IanVS

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

o-alexandrov avatar Mar 14 '23 07:03 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?

IanVS avatar Mar 14 '23 11:03 IanVS

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:


A nice-to-have (could be done separately later) is:

  • remove jest-related deps from dependencies in package.json, so vitest users won't need to install a long list of unused dependencies
    • jest-related deps should be listed in devDependencies w/ a note in the readme about the need to install them, if using jest

EDIT: thank you very much for the work on the test-runner you mentioned below

o-alexandrov avatar Mar 14 '23 12:03 o-alexandrov

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.

IanVS avatar Mar 14 '23 12:03 IanVS

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

beaussan avatar Mar 14 '23 13:03 beaussan

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 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',
        },
    },
});

usman1947 avatar Apr 12 '23 02:04 usman1947

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.

yannbf avatar Apr 21 '23 21:04 yannbf

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 :(

squidjam avatar May 23 '23 07:05 squidjam

please support vitest!

In what manner would you like to use Storybook together with Vitest?

IanVS avatar May 23 '23 11:05 IanVS

I'd like Storybook to be able to have Storybook use vitest instead of jest when it's installed on a vite project.

squidjam avatar May 23 '23 12:05 squidjam

@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?

IanVS avatar May 23 '23 13:05 IanVS

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.

squidjam avatar May 23 '23 14:05 squidjam

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

yarinsa avatar May 26 '23 10:05 yarinsa

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 avatar May 26 '23 11:05 IanVS

@IanVS Simplest example, aliases. react-refresh plugin...

yarinsa avatar May 26 '23 14:05 yarinsa

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.

IanVS avatar May 26 '23 14:05 IanVS

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.

MWhite-22 avatar May 30 '23 12:05 MWhite-22