I have upgraded next.js from version 6 to 7, and running tests with jest now exits with the following:
TypeError: require.resolveWeak is not a function
Steps to reproduce the behavior, please provide code snippets or a repository:
yarn test
I expect testing dynamic import as would work as for version 6
https://i.imgur.com/i3aw7Eg.png
Sounds like you have to transpile them yourself in the test environment using the babel plugin.
Hi! I can take a look at his đź‘€
@HeroProtagonist actually I have mocked next/dynamic
and added babel-plugin-transform-dynamic-import
.
I'm using "next": "^7.0.2-canary.7",
and have no issue with the with-jest
example.
My .babelrc
is just
{
"presets": ["next/babel"]
}
Could you try the latest version ?
@revskill10 I hope you have tried my fork because even if I use 7.0.2-canary.7 it doesn't work.
@HeroProtagonist did you get a chance to have a look?
I'm having the same issue and would like to get some support here, as it seems to me that the changes to dynamic imports in Next.js@7 are not compatible with the jest env. And no instructions given, nor there's an example/integration in the Next.js repo itself
The issue is reproducible in the repo @giovannigiordano provided.
hi @giovannigiordano can you elaborate a bit more about how you mocked the dynamic/import ? I can't figure it out yet :(
babelrc
{
"presets": ["next/babel"],
"plugins": [
["styled-components", { "ssr": true, "displayName": true, "preprocess": false }]
],
"env": {
"test": {
"plugins": ["transform-dynamic-import"]
}
}
}
test
jest.mock('next/dynamic', () => () => 'DynamicDrawer');
file
import dynamic from 'next/dynamic';
const DynamicDrawer = dynamic({
loader: () => import('./SimpleDrawer/index'),
Loading: () => null,
});
@Lidofernandez I've followed your same flow.
@giovannigiordano the problem I'm trying to test my dynamic component with jest and snapshot. It should render the dynamic component onClick but it nothing happens:
import React from 'react';
import { mount } from 'enzyme';
import navigationList from '../../../../data/navigation';
import Navigaton from '../../../../../src/components/Navigation';
describe('Navigaton', () => {
let navigation;
let navigationToHtml;
beforeEach(() => {
navigation = mount(<Navigaton
tabs={navigationList}
/>);
navigationToHtml = navigation.getDOMNode();
});
afterEach(() => {
navigation = () => {};
navigationToHtml = '';
});
it('should render', () => {
// this test is passing because its without the drawer
expect(navigationToHtml).toMatchSnapshot();
});
it('should render the drawer on click', () => {
// this test is failing because nothing is being generated on click
navigation.find('i.mdc-top-app-bar__navigation-icon').simulate('click');
expect(navigationToHtml).toMatchSnapshot();
});
});
@Lidofernandez if you mock the dynamic import substituting the implementation with an empty function, the module will return undefined so it won't show in the snapshot
indeed sorry, here there is my solution:
```
jest.mock('next/dynamic', () => () => {
const SimpleDrawer =
// eslint-disable-next-line global-require
require('../../../../../src/components/Navigation/SimpleDrawer').default;
return props => (
);
});```
FWIW, here's the fix we've implemented inside jest.setup.js
on my team. The advantage is that it's set up globally for all our tests.
// Manually mock next/dynamic as the next.js (7.0.2) babel plugin will compile to Webpack
// lazy imports (require.resolveWeak) who're conflicting with the Node module system.
jest.mock('next/dynamic', () => () => {
const DynamicComponent = () => null;
DynamicComponent.displayName = 'LoadableComponent';
DynamicComponent.preload = jest.fn();
return DynamicComponent;
});
The behavior should be exactly the same as before as the dynamic weren't ever processed (even inside snapshots if you have some)
heya, we've recently published a package that also mocks next/dynamic
, but actually lets you render the resulting dynamic components: https://github.com/FormidableLabs/jest-next-dynamic
The idea is:
next/dynamic
, but wrap it and capture the Loadable component's preload
function.preload
initializers to a queue.preloadAll
function that will flush the queue.From there, you just need to call beforeAll(preloadAll)
and it will be as if the dynamic components were all already available – meaning they actually render, appear in snapshots instead of "loading…", etc. This is exactly how you'd do it with react-loadable
's built-in Loadable.preloadAll
method.
Similar to the approach above, by mocking the next/dynamic
module and flushing a queue of every dynamic component, you don't have to track down and export every single one in order to get at its preload
method. :)
Hope that helps someone!
(* Since the default Jest environment simulates a DOM environment, even if you get a handle on Loadable's built-in preloadAll
method, it won't actually work due to this line which skips enqueuing the preload initializers – since in a Jest DOM environment, window
will be defined.)
Oh and as far as advising the Next.js team on a potential built-in solution for this:
NODE_ENV
is set to test
– maybe the custom import
Babel plugin is another thing to switch out there? Currently the import
plugin pretty much depends on webpack, but it's rare to use webpack for tests.react-loadable
implementation, which enqueues preload
initializers even on the client (no typeof window
check).preloadAll
as an export of next/dynamic
. Right now it's possible to call preload
on individual dynamic()
results, but preloadAll
isn't exposed as an API anywhere (unless you reach into dist
folders to find loadable.js
).this is how i hacked it,
const TodosList =
process.env.NODE_ENV === "test"
? require("../components/TodosList").default
: dynamic({
loader: () => import("../components/TodosList"),
loading: () => <p>loading ...</p>,
ssr: true
});
Here is my custom solution using Jest node modules mocking:
/* ./__mocks__/next/dynamic.js */
import React from 'react';
const mockReact = React;
let componentName = 'DynamicComponent';
export const __setComponentName = data => {
componentName = data;
};
const DynamicComponent = () => ({ children, ...rest }) =>
mockReact.createElement(componentName, rest, children);
export default DynamicComponent;
Then in your test:
import React from 'react';
import TestRenderer from 'react-test-renderer';
import MyComponent from '..';
describe('<MyComponent />', () => {
require('next/dynamic').__setComponentName('NameOfTheDynamicComponentForTheSnapshot');
it('should work as usual', () => {
const component = TestRenderer.create(<MyComponent />);
expect(component.toJSON()).toMatchSnapshot();
});
});
No dependency or babel plugin needed.
It will render the snapshot as if you had called jest.mock('../DynamicComponent', () => 'DynamicComponent');
on a regular component and lets you cover that component in another test.
I wonder if this is solved in canary with the import to node transform, cc @lfades
Just wanted to give a quick link to a CodeSandbox repro:
https://codesandbox.io/s/4j3lw8lj20
If you fork this and open a new terminal and run yarn jest
you can see the dynamic import fail. Maybe there is a config step missing here?
Just wanted to give a quick link to a CodeSandbox repro:
https://codesandbox.io/s/4j3lw8lj20
If you fork this and open a new terminal and run
yarn jest
you can see the dynamic import fail. Maybe there is a config step missing here?
@jhoffmcd I kept tinkering with it, took an approach of having separate tsconfig from this repo: https://github.com/deptno/next.js-typescript-starter-kit.
Forked/revised sandbox (https://codesandbox.io/s/949zpj8yw) seems to be working. I don't know if this is the right/best approach but... it works?
Hi, Is there any update regarding this problem? Using jest-next-dynamic is a bit hacky and it does not work fine with shallow render in test.
this is how i hacked it,
const TodosList = process.env.NODE_ENV === "test" ? require("../components/TodosList").default : dynamic({ loader: () => import("../components/TodosList"), loading: () => <p>loading ...</p>, ssr: true });
Changing the code to pass the tests seems not a good idea. But anyway it worked!
Here's a working sandbox: https://codesandbox.io/s/nextdynamictestingissue-siuvx using a fork from @jhoffmcd sandbox.
The only change I did was to .babelrc
:
{
"presets": [
"next/babel",
"@zeit/next-typescript/babel"
],
"env": {
"test": {
"plugins": [
"babel-plugin-dynamic-import-node"
]
}
}
}
Using [email protected]
, it also works in the latest canary
. Please let me know if it's working.
@lfades I'm having the same issue as originally posted but can't seem to fix it by adding jest-next-dynamic, babel-plugin-dynamic-import-node and using the .babelrc setup you suggested.
I upgraded to 8.1.0 without any difference, still getting the original TypeError: require.resolveWeak is not a function
error. Any ideas on how I can continue to debug this?
@axelinternet I did a change to .babelrc
, can you test it again ? 🙏
This used to work for me without any change to the code or .babelrc
, but [email protected]
breaks my tests again. (see https://github.com/zeit/next.js/issues/7872).
{
"presets": ["next/babel"],
"env": {
"test": {
"plugins": ["babel-plugin-dynamic-import-node"]
}
}
}
makes them pass again but seems unfortunate as it means that tests and code don't use the same config anymore. And using babel-plugin-dynamic-import-node
in all environments seems to disable the dynamic loading altogether (i.e. everything works but no chunks are loaded).
@damusnet Can you see test it with the .babelrc
in this codesandbox 🙏
it looks like this:
{
"presets": [
"next/babel"
],
"env": {
"test": {
"presets": [
[
"next/babel",
{
"transform-runtime": {
"corejs": false
}
}
]
],
"plugins": [
"babel-plugin-dynamic-import-node"
],
}
}
}
also got the same error in next 9.0.
I use typescript and added "babel-plugin-dynamic-import-node",
to the plugins in test
-env.
my babelrc looks like this:
{
plugins: ["babel-plugin-styled-components"],
env: {
development: {
presets: ["next/babel", "@zeit/next-typescript/babel", "@babel/preset-typescript"],
plugins: ["ts-optchain"],
},
production: {
presets: ["next/babel", "@zeit/next-typescript/babel", "@babel/preset-typescript"],
plugins: ["ts-optchain"],
},
test: {
presets: [
[
"next/babel",
{
"preset-env": {
modules: "commonjs",
},
},
],
"@zeit/next-typescript/babel",
"@babel/preset-typescript",
],
plugins: ["babel-plugin-dynamic-import-node", "ts-optchain"],
},
},
}
EDIT: using https://github.com/FormidableLabs/jest-next-dynamic solved the issue for me
I have met this error too and try above solutions except using jest-next-dynamic.
I still can't solve the problem.
Now, I manage to evade this error by use jest's options --coverage.
When I test with this option it will not get the error.
This was fixed recently in Next.js 8.1 or 9, I cannot recall exactly.
Hi @Timer, I'm not sure this issue should be closed. I'm still facing this issue (same error as in the first post). I've made a minimal reproducible version here: https://github.com/ellsclytn/next-with-jest
A clone, install all deps, and yarn test
/npm run test
will show it. This particular version has all dependencies updated to their latest at the time of writing.
@Timer, could you please provide a working example of this, since I am using [email protected]
and I still get TypeError: require.resolveWeak is not a function
edit: I found the answer on stack overflow
edit: I found the answer on stack overflow
I'm not sure this is really a solution. The jest-next-dynamic lib is working for me but I feel this is something that should be working out of the box for Next (and the team appears to think so too).
I feel this is something that should be working out of the box for Next (and the team appears to think so too).
Sure, it would be great to have them provide a working example for us.
Since the release 9.1.2, I have this error in all of my test js with a dynamic component I have this error:
TypeError: LoadableComponent.preload is not a function.
Did you have this problem ?
same issue here and the dynamic imports returns this
Are there any news on this issue? After upgrading to next js 10 i start to get again TypeError: require.resolveWeak is not a function
@Emiliano-Bucci Feel free to open a new issue with a reproduction that we can take a look at 🙏
@lfades Done, thanks! -> https://github.com/vercel/next.js/issues/18855
Most helpful comment
heya, we've recently published a package that also mocks
next/dynamic
, but actually lets you render the resulting dynamic components: https://github.com/FormidableLabs/jest-next-dynamicThe idea is:
next/dynamic
, but wrap it and capture the Loadable component'spreload
function.preload
initializers to a queue.preloadAll
function that will flush the queue.From there, you just need to call
beforeAll(preloadAll)
and it will be as if the dynamic components were all already available – meaning they actually render, appear in snapshots instead of "loading…", etc. This is exactly how you'd do it withreact-loadable
's built-inLoadable.preloadAll
method.Similar to the approach above, by mocking the
next/dynamic
module and flushing a queue of every dynamic component, you don't have to track down and export every single one in order to get at itspreload
method. :)Hope that helps someone!
(* Since the default Jest environment simulates a DOM environment, even if you get a handle on Loadable's built-in
preloadAll
method, it won't actually work due to this line which skips enqueuing the preload initializers – since in a Jest DOM environment,window
will be defined.)