msw: "Cannot find module 'msw/node'" in Jest JSDOM environment
Prerequisites
- I confirm my issue is not in the opened issues
- I confirm the Frequently Asked Questions didn’t contain the answer to my issue
Environment check
- I’m using the latest
msw
version - I’m using Node.js version 18 or higher
Node.js version
v18.18.2
Reproduction repository
https://github.com/textbook/msw2-mre
Reproduction steps
npm ci && npm test
Current behavior
Error message
Cannot find module ‘msw/node’ from ‘path/to/file’
$ npm test
> msw2@0.1.0 test
> jest
FAIL ./index.test.js
● Test suite failed to run
Cannot find module 'msw/node' from 'index.test.js'
1 | const { http, HttpResponse } = require("msw");
> 2 | const { setupServer } = require("msw/node");
| ^
3 |
4 | const server = setupServer();
5 |
at Resolver._throwModNotFoundError (node_modules/jest-resolve/build/resolver.js:427:11)
at Object.require (index.test.js:2:25)
Test Suites: 1 failed, 1 total
Tests: 0 total
Snapshots: 0 total
Time: 0.961 s
Ran all test suites.
Error: Process completed with exit code 1.
Expected behavior
The test should pass, as it does if you switch to the Node environment by updating jest.config.js
as follows:
diff --git a/jest.config.js b/jest.config.js
index f343a5c..4e07134 100644
--- a/jest.config.js
+++ b/jest.config.js
@@ -13,5 +13,5 @@ module.exports = {
ReadableStream,
TextEncoder,
},
- testEnvironment: "jsdom",
+ testEnvironment: "node",
};
Outcome:
$ npm test
> msw2@0.1.0 test
> jest
PASS ./index.test.js
✓ works (10 ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 0.203 s
Ran all test suites.
About this issue
- Original URL
- State: closed
- Created 8 months ago
- Reactions: 34
- Comments: 24 (6 by maintainers)
Commits related to this issue
- Start to use msw to mock server requests. So far have had to stop jsdom declaring that tests are running in a browser: https://github.com/mswjs/msw/issues/1786 Fiddle with whatwg-fetch polyfill: h... — committed to jg210/spring-experiments by jg210 5 months ago
- Add dark mode As a beer league hockey player, Given it's light outside, When I open the Wolfpack app, Then I see a light-themed app, So that it's easier on my eyes. As a beer league hockey player, G... — committed to pachun/too-many-men by pachun 4 months ago
- Add dark mode As a beer league hockey player, Given it's light outside, When I open the Wolfpack app, Then I see a light-themed app, So that it's easier on my eyes. As a beer league hockey player, G... — committed to pachun/too-many-men by pachun 4 months ago
Sorry to drag this out more, but this is just not accurate @kettanaito. I understand the technical details of your explanation and the differences between
jest
,jsdom
,vitest
, etc. perfectly, so don’t mistake this for lack of understanding. There’s no need for reiteration of your position, rather there’s a need for listening and understanding your users’ (valid) choices and circumstances.Jest and jsdom are not “OLD” or outdated. As @joshkel mentioned, they provide a far more accurate testing environment for browser code. They are actively maintained and updated. They support the bleeding edge of browser changes and many wide-sweeping libraries and tools.
I’ve already spent far too much time trying to work around this issue including your suggestion, using other DOM implementations that fall short (such as
happy-dom
), polyfills, resolvers, etc. None of these are fully acceptable and have side effects that impact other correct code outside ofmsw
. Please note that just because I was able to get my tests to run and pass, that does not mean that the overall outcome is acceptable.As such, this is a total blocker for us. We will not be sacrificing the accuracy of our tests in relation to the production environment they’ll be running in for one library. Somehow all the thousands of other libraries we use haven’t had this issue.
This could be fixed for your users with some action on your part and you’re choosing not to. That said, I understand if this is the direction you’re taking the library. I’m not asking for anything and I expect nothing in return. I simply wanted to share feedback with you about the entrenched position you’ve taken that is very clearly not cut and dry like you’ve painted it.
You spend your time making this awesome tool and I appreciate that, it’s just that I’ll be unable to continue using it going forward. Thanks for all of your hard work and maybe I’ll still be able to use it on some other non-browser project because I really do like it a lot ❤️
Why does this error happen?
Because JSDOM forces the
browser
export condition. In other words, JSDOM says “if a third-party package exports abrowser
field, use that.” That’s the default and a rather dangerous default at that. Why? Because JSDOM still runs in Node.js. More to that, JSDOM cannot have 100% browser compatibility by design, so forcing thebrowser
export condition will subject your tests to failures more than necessary when working with packages that ship different code for different environments, like MSW does.The
msw/node
import must never happen in the browser code. Thus, MSW has the following export condition to ensure that on the bundler level:https://github.com/mswjs/msw/blob/1d1fbca6a39b0d89c7e18bc50cab73ddac7ea5f0/package.json#L23-L24
The same is true for the
msw/browser
import in Node.js. This is the right setup to achieve publishing to multiple environments at once. The problem is not the package but how your test environment is configured. Export conditions is a relatively new feature is Node.js, and some tools are either lacking behind, don’t understand it entirely, or, like JSDOM, assume dangerous defaults that put the users in confusing situations.How to solve this?
Opt out of the
browser
export condition by adding this to yourjest.config.js
:This will force JSDOM to use the
node
(ordefault
) export condition, which is the correct behavior.If you encounter other import-related issued after this change, they aren’t related and have to be addressed separately. Adding this recommendation to the migration guide as well so everyone could follow.
JSDOM is broken export conditions-wise, I believe I explained it at length. It’s not about making MSW happy, it’s about resolving third-party modules using the
node
export while running in Node.js. This is how module resolution is supposed to work, and by using old tools you are not getting the modern JavaScript. If you don’t believe me, try Vitest. It works with the correct module resolution out of the box.@joshkel, your custom resolver implementation looks interesting. I will leave it up to the people to decide which approach works best for them, but may I please stress this one last and final time: You need all this because you are using an OLD testing framework. Whether you decide to ignore the
browser
export condition in Node, which is what you should be doing, to begin with, or introduce a customresolver
for Jest—nothing of this has anything to do with MSW. Hope we get this straight in the discussions to come. Thanks.Thanks for the reply, @kettanaito (and for your patience).
If I’m understanding correctly, it’s not a matter of Jest being old or not supporting modern module resolution. (Jest fully supports modern modules and module resolution, as far as I know - somewhat held back by issues in Node.js itself.) Instead, it’s a legitimate trade-off:
Jest and jsdom seem to default to the first approach. jsdom won’t use implementations if they’re not W3C-spec-compliant, and Jest uses the VM API (I think) to exclude Node.js globals (so code under test won’t mistakenly reference them), and Jest decides to emulate the browser as closely as possible when using jsdom, including honoring the
"browser"
export conditions of Node modules. (And, since Jest’s jsdom environment doesn’t expose Node.js globals, some packages need to have their"browser"
export conditions honored, so they’ll reference browser globals rather than Node.)Vitest takes the second approach (e.g., it exposes Buffer because “TODO a lot of dependencies use it”) - but that means I can have production browser-side code that mistakenly references Buffer and passes tests and fails at runtime. MSW 2 picks the second approach (for reasons that you explain in depth), and I’m certain those are the correct tradeoffs for MSW - but now I have test failures because
new FormData(form)
no longer works. (This is not a complaint and not your problem.)It wouldn’t surprise me if Jest’s tradeoffs are worse in general. (They’ve certainly caused a lot of complexity.) But it doesn’t seem quite fair to say that picking a worse set of tradeoffs is broken. (I’m interested in switching to Vitest, but parts of the ecosystem still aren’t there for my project.)
I’m happy to accept MSW’s decision as to appropriate tradeoffs when using it. (That’s what I meant by “making MSW happy”; I apologize if I contributed to any confusion.)
We are unfortunately seeing the same issue when upgrading to v2. This is blocking us from moving to the new version. It is a Nextjs project and works without issue in v1
Here’s an improved version of my custom Jest
resolver
that replacescustomExportConditions
just for MSW, while leaving Jest’sbrowser
export for other code that expects it. This allows tests of code that’s intended to run in the browser to continue to use the browser versions of other packages, while following MSW’s expectation and recommendation of using MSW’s Node code within tests.If this works for others, it may be worth adding as an option in the docs.
For me, this works only partially. As it breaks other imports.
This is what resolved it for me from the examples repository. I don’t pretend to know what this does, but it works.
https://github.com/mswjs/examples/blob/main/examples/with-jest/jest.config.ts#L20
Disabling Jest’s
customExportConditions
everywhere seemed somewhat drastic to me; I was hoping to change just the interaction between Jest and MSW, instead of changing everything within Jest to make MSW happy. (And, in a brief test, I ran into problems with other packages not working correctly when I clearedcustomExportConditions
.)Instead, I’m using a custom Jest resolver to modify MSW’s imports.
Please note: This is a hack. It’s deliberately doing something not intended by the MSW project. Subsequent MSW updates may break it. If you do this and it causes problems, please do not report any resulting issues to this project.
To do this: In the Jest config, define your resolver:
And create it with the following contents:
I have the same issue using
react
,vite
andvitest
.System: NodeJS : v18.18.2 npm : 9.8.1 OS : macOS 13.5.2
@joshkel thanks so much for this! Saved me a ton of time. I made one small tweak to this:
Namely, I left the conditions intact aside from filtering out browser. I’m still early in my testing, but this seems to be working for me and preventing another issue I was running into when I overwrote conditions to be empty.
Still having the issue with vite, vitest and react…
React Vite Vitest & MSW v2 still doesnt work 😵
I have same problem on React 18, Vite 5.1.0 , Vitest 1.2.2 and MSW 2.0
See no resolution for that on Vite and Vitest config.
I have checked example in Vitest from @kettanaito, but I cannot see the solution there…
Like @mkalvas said, there should be separated solution in documentation below Jest solution, as Vitest is huge communitt as well.
https://mswjs.io/docs/migrations/1.x-to-2.x/#cannot-find-module-mswnode-jsdom
Waiting for solution guide for Vitest 🧪
I got a similar error but running ESLINT
I’m not sure why this “node: null” is needed 👇 https://github.com/mswjs/msw/blob/a54138a62a7e89bd5f768d369e6b5bc4c46686ae/package.json#L16-L17
but removing it fixes my issue above
Please refer to the usage examples with Vitest that feature both ESM and CJS tests, all functional with MSW v2.0:
I’ve also just migrated the entire internal test suite of MSW to Vitest yesterday without any issues. You don’t need to configure JSDOM in any way for Vitest+MSW to work.
I have a similar situation
Inside the test environment (using
vitest
), I get the same error, because with./browser
.If I follow the instruction to remove the conditional
node
, or put an empty string in the array, I end up breaking other tests that need the conditionalnode
Does anyone have any suggestions?
Confirmed, now 🟢 passes in both
testEnvironment
s - thanks!