I'm running into an issue when integrating my stories into my unit tests using Jest & React Testing Library in a NextJS app. In Storybook, my NextJS components render without issue with the Storybook Addon Next Router working as expected. However, in my Jest test file, my test throws an error because useRouter() returns null. I believe I've setup all the addons correctly.
My setup:
- NextJS 12
- Jest & React Testing Library
- Storybook
- Storybook Addon Next Router (for using Next router inside Storybook)
- @storybook/testing-react (for integrating my stories, args & params with testing library)
The problem:
I've setup all the files according to the docs. The story renders fine inside Storybook and the useRouter() works thanks to the Storybook Next addon. However, the composeStories()
function from @storybook/testing-react seems to fail to properly initialize the Next router Provider from the first addon. My unit test fails with the following error:
Test suite failed to run
TypeError: Cannot read property 'locale' of null
And points to the following line inside my component:
// Events.tsx
const { locale = 'en' } = useRouter();
This is my test file:
// Events.test.tsx
import React from 'react';
import { render, screen } from '@testing-library/react';
import { composeStories } from '@storybook/testing-react';
import * as stories from './Events.stories'
const { WithSeasonsAndEvents } = composeStories(stories);
describe('Events Screen', ()=> {
render(<WithSeasonsAndEvents />);
it('renders input data', ()=> {
// set up test
});
});
And my stories file:
// Events.stories.tsx
import React from 'react';
import { ComponentStory, ComponentMeta } from '@storybook/react';
import Events from './Events';
export default {
title: 'Events/Events Screen',
component: Events
} as ComponentMeta<typeof Events>;
const Template: ComponentStory<typeof Events> = (args) => <Events {...args} />;
export const WithSeasonsAndEvents = Template.bind({});
WithSeasonsAndEvents.args = {
// many args here
};
This story renders fine in Storybook. useRouter() works as expected inside my story and I am able to use all of its properties including pathname
and locale
. However, for some reason the useRouter() function returns null when the composed story is being rendered by React Testing Library.
What I have tried:
- Verified installing @storybook/testing-react and set up my global configuration in jest setup file:
// jest-setup.ts
import '@testing-library/jest-dom/extend-expect';
import { setGlobalConfig } from '@storybook/testing-react';
// Storybook's preview file location
import * as globalStorybookConfig from './.storybook/preview';
setGlobalConfig(globalStorybookConfig);
- Verified that jest reads my setup file and finds my Storybook preview file
- Verified both .storybook/preview.js and .storybook/main.js match the usage guide:
preview.js
// .storybook/preview.js
import '../styles/tailwind.css';
import { RouterContext } from "next/dist/shared/lib/router-context"; // next 12 https://storybook.js.org/docs/react/writing-stories/parameters
export const parameters = {
actions: { argTypesRegex: "^on[A-Z].*" },
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/,
},
},
nextRouter: {
Provider: RouterContext.Provider,
locale: 'en',
},
}
main.js
// .storybook/main.js
module.exports = {
"stories": [
"../stories/**/*.stories.mdx",
"../stories/**/*.stories.@(js|jsx|ts|tsx)",
"../components/**/*.stories.@(js|jsx|ts|tsx|mdx)"
],
"addons": [
"@storybook/addon-links",
"@storybook/addon-essentials",
"storybook-addon-next-router",
],
"framework": "@storybook/react"
}
- Stories render fine in Storybook, all useRouter() features work, and when I console.log the useRouter() return value, I get the full suite of NextJS useRouter object properties:
// >> console.log(useRouter()); inside Storybook
{
route: "/",
pathname: "/",
query: {},
asPath: "/",
events: {},
isFallback: false,
locale: 'en'
}
- However, on the unit test, when logging the return value of
useRouter();
inside my component, it returnsnull
. Understandably, because it is null, the{ locale }
variable assignment is throwing an error in my unit test. - When logging the value of
useRouter
variable, both inside my Storybook preview and inside my unit test I get the following function:
// >> console.log(useRouter.toString())
function useRouter() {
return _react.default.useContext(_routerContext.RouterContext);
}
Does anyone have any idea on what can be going wrong? I'm fairly new to Storybook but I've tried looking through GitHuB issues and online and haven't been able to solve. No idea why useRouter()
returns null inside Jest if the composeStories()
fn should take care of resolving my Storybook params & args. Any insight would be appreciated.