Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to unit test Next.js dynamic components?

The Next.js dynamic() HOC components aren't really straightforward to tests. I have 2 issues right now;

  • First jest is failing to compile dynamic imports properly (require.resolveWeak is not a function - seems to be added by next babel plugin)
  • Second I can't get good coverage of the modules logic; looks like it's simply not run when trying to render a dynamic component.
like image 848
Simon Boudrias Avatar asked Oct 24 '17 09:10

Simon Boudrias


4 Answers

Let's assume we have a component like this (using a dynamic import):

import dynamic from 'next/dynamic';

const ReactSelectNoSSR = dynamic(() => import('../components/select'), {
    loading: () => <Input />,
    ssr: false
});

export default () => (
    <>
        <Header />
        <ReactSelectNoSSR />
        <Footer />
    </>
);

The dynamic import support offered by Next.js does not expose a way to preload the dynamically imported components in Jestโ€™s environment. However, thanks to jest-next-dynamic, we can render the full component tree instead of the loading placeholder.

You'll need to add babel-plugin-dynamic-import-node to your .babelrc like so.

{
  "plugins": ["babel-plugin-dynamic-import-node"]
}

Then, you can use preloadAll() to render the component instead of the loading placeholder.

import preloadAll from 'jest-next-dynamic';
import ReactSelect from './select';

beforeAll(async () => {
    await preloadAll();
});

๐Ÿ“ Source

like image 95
leerob Avatar answered Oct 20 '22 15:10

leerob


You can add the following to your Jest setup ex: setupTests.ts

jest.mock('next/dynamic', () => () => {
    const DynamicComponent = () => null;
    DynamicComponent.displayName = 'LoadableComponent';
    DynamicComponent.preload = jest.fn();
    return DynamicComponent;
});
like image 38
Smakosh Avatar answered Oct 20 '22 16:10

Smakosh


Although a hacky solution, what I did was to simply mock next/dynamic by extracting the import path and returning that import:

jest.mock('next/dynamic', () => ({
  __esModule: true,
  default: (...props) => {
    const matchedPath = /(.)*(\'(.*)\')(.)*/.exec(props[0].toString());
    if (matchedPath) return require(matchedPath[3]);
    else return () => <></>;
  },
}));
like image 2
Usman Mani Avatar answered Oct 20 '22 17:10

Usman Mani


The following will load the required component. You can also use similar approach to load all components before hand.

jest.mock('next/dynamic', () => ({
  __esModule: true,
  default: (...props) => {
    const dynamicModule = jest.requireActual('next/dynamic');
    const dynamicActualComp = dynamicModule.default;
    const RequiredComponent = dynamicActualComp(props[0]);
    RequiredComponent.preload
      ? RequiredComponent.preload()
      : RequiredComponent.render.preload();
    return RequiredComponent;
  },
}));
like image 2
Farrukh Taqveem Haider Avatar answered Oct 20 '22 15:10

Farrukh Taqveem Haider