Unit test for Storybook add-on is failing due to NextJS useRouter() returning null

I've encountered an issue while trying to integrate my stories into unit tests using Jest and React Testing Library in a NextJS application. When viewing the components in Storybook, everything works smoothly with the Storybook Addon Next Router. However, during the Jest testing process, I face an error because useRouter() returns null. I believe I have correctly configured all the necessary addons.

Here is my setup:

  • NextJS 12
  • Jest and React Testing Library
  • Storybook
  • Storybook Addon Next Router (used for Next router in Storybook)
  • @storybook/testing-react (to integrate stories, args, and params with testing library)

The problem at hand:

I followed the documentation to set up all the files properly. The story renders fine in Storybook and useRouter() functions as expected thanks to the Storybook Next addon. However, when the composeStories() function from @storybook/testing-react is called to render the composed story in React Testing Library, it fails to initialize the Next router Provider from the initial addon. This results in a failure of my unit test with the following error:

Test suite failed to run
TypeError: Cannot read property 'locale' of null

The error points to the following line within 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
  });
});

As well as 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
};

In Storybook, the story renders perfectly fine with useRouter() functionality working as expected. However, the useRouter() function unexpectedly returns null when the composed story is rendered through React Testing Library.

What I have attempted so far:

  • Confirmed installation of @storybook/testing-react and proper global configuration setup in jest setup file:
// jest-setup.ts
import '@testing-library/jest-dom/extend-expect';
import { setGlobalConfig } from '@storybook/testing-react';

// Location of Storybook's preview file
import * as globalStorybookConfig from './.storybook/preview';

setGlobalConfig(globalStorybookConfig);
  • Ensured that jest recognizes my setup file and locates my Storybook preview file
  • Verified that both .storybook/preview.js and .storybook/main.js align with the usage guide:

preview.js

// .storybook/preview.js
import '../styles/tailwind.css';
import { RouterContext } from "next/dist/shared/lib/router-context"; 

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 display without issues in Storybook and all features of useRouter() work as intended. When logging the return value of useRouter(), the complete NextJS useRouter object properties are visible.
// >> console.log(useRouter()); inside Storybook
{
    route: "/",
    pathname: "/",
    query: {},
    asPath: "/",
    events: {},
    isFallback: false,
    locale: 'en'
}
  • However, during the unit test, upon logging the return value of useRouter(); within my component, it shows null. Consequently, since it is null, the assignment of { locale } results in an error within the unit test.
  • When inspecting the value of the useRouter variable, both in my Storybook preview and within the unit test, the following function is displayed:
// >> console.log(useRouter.toString())
function useRouter() {
    return _react.default.useContext(_routerContext.RouterContext);
}

If anyone has insights on what might be causing this issue, I would greatly appreciate any suggestions. While I am still new to Storybook, I have tried researching GitHub issues and online resources without success. It remains unclear why useRouter() returns null within Jest despite the expectation that composeStories() should handle the resolution of Storybook arguments and parameters. Any help or insights would be welcomed.

Answer №1

Dealing with a similar problem, I managed to resolve it by exporting AsNextRouter as a decorator in the file preview.js

// preview.js

import { AsNextRouter } from 'storybook-addon-next-router/dist/decorators';

export const decorators = [AsNextRouter];

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

In order to activate the VScode Tailwind CSS Intellisense plugin, I have discovered that I need to insert a space before

I recently followed the installation guide for using Tailwind with Next.js Here is a snippet from my tailwind.config.js file: /** @type {import('tailwindcss').Config} */ module.exports = { content: [ "./app/**/*.{js,ts,jsx,tsx}", ...

Getting this error in React Recoil - "Type 'any[]' is not compatible with type 'never[]' parameter"?

Currently, I am working through the React Recoil Todo List tutorial, however, I am encountering some type errors while following along and I am not sure how to resolve them effectively. Below is the code snippet: export const todoListAtom = atom({ key: ...

Troubleshooting Routing Issues in a Next.js Website Tutorial

After going through the next.js tutorial at https://github.com/zeit/next-learn-demo.git, I encountered an issue starting from stage 3 "dynamic routing". Despite the tutorial indicating that dynamic routing should be working from stages 3 to 8, it wasn&apos ...

Can anyone guide me on implementing getServerSideProps in a TypeScript NextPage component?

I've come across a page that I'd like to replicate, with the code sourced from https://github.com/dabit3/nextjs-lit-token-gating/blob/main/pages/protected.js: import Cookies from 'cookies' import LitJsSdk from 'lit-js-sdk' ex ...

Exploring the functionality of window.matchmedia in React while incorporating Typescript

Recently, I have been working on implementing a dark mode toggle switch in React Typescript. In the past, I successfully built one using plain JavaScript along with useState and window.matchmedia('(prefers-color-scheme dark)').matches. However, w ...

Troubleshooting: NextJS Typescript getInitialProps returning null value

I am currently working with NextJS 'latest' and TypeScript to extract the token from the URL, but I am encountering an issue where it returns undefined. To achieve this, I am utilizing the getInitialProps method. The URL in question looks like th ...

Guide to customizing the layout preview specifically for certain components in Storybook, without affecting all components

Currently working on my storybook project and facing an issue. I'm aiming to have the layout centered in the preview section. I attempted export const parameters = { layout: 'centered', }; in the .storybook/preview.js file However, this c ...

Dates comparison causing Firestore security rules issue

After running the query shown below, I encountered a permission-denied message with an error in the "Monitor rules" tab. const timeNow = useMemo(() => Timestamp.now(), []); const query = query( postRef, where("tags", "array-contai ...

Refreshing a Next JS page with updated data from MongoDB database without needing a manual page reload

I've been working on a project where I interact with a MongoDB database from my NextJS site to display data. The goal is to implement CRUD functionality, allowing users to add new content and delete existing one directly from the site. However, there& ...

Utilize useSWR to trigger a new fetch upon page initialization even when the key is stored in the cache within Next.js

Currently, I am utilizing the useSWR hook to retrieve data from my api endpoints. It is my understanding that it uses the api routes as the key for caching the data. The issue at hand is that I have implemented an api route on 2 separate pages (referred t ...

Unable to get basic Framer Motion animation to function in my NextJS project

I recently attempted to incorporate a waving hand animation into the hero component of my nextjs project [v13, pages directory]. However, instead of seeing the desired animated effect, all I'm getting is a static emoji. Here's a link to the anima ...

Having trouble with retrieving API on nextjs 13 (application directory)?

I am encountering an issue while trying to retrieve data from a headless CMS (Strapi). The error message "Error: _hooks_useFetch__WEBPACK_IMPORTED_MODULE_2___default(...) is not a function" keeps popping up in Data.js. Can anyone assist me in understanding ...

Steps to facilitate all CORS API requests in Next.js

I need to set up CORS for my app to allow all sources and destinations. For deploying my Next.js app on Vercel, I am referring to this guide. Here is the content of my next.config.js file. /** @type {import('next').NextConfig} */ const nextConf ...

How can getStaticPaths be used to define routes?

Imagine you have these 3 route paths stored in an array: const routes = ["route1", "route2", "route3"]; To set up these paths, you would do the following: export async function getStaticPaths() { const routes = ["route1", "route2", "route3"]; const ...

The Importance of Implementing Content Security Policy (CSP) in NextJS

Currently in the process of creating a Content Security Policy (CSP) for a Next.js production application, I have come across reliable documentation on how to implement a CSP with the framework. However, there are a few concerns that need to be addressed p ...

What is the best way to handle query string parameters when routing in Next.js?

One of the challenges I am facing is related to a URL structure like this: bar?id=foo When I navigate to this URL using router.push('bar?id=foo'), everything works perfectly. However, if I directly access the route in the browser, the query st ...

Maintaining the image's aspect ratio using the Next Image component

Currently, I am using Next's Image component to display a logo. Although the image itself is 1843x550 px, I have specified the width and height properties to adjust it to fit within a space of 83x24 px. However, due to a slightly different aspect rati ...

Exploring NextJS: Implementing Routing for Multilingual Support

I am currently working on developing a multilanguage website using Next JS. For the translation functionality, I have integrated the next-translate package into my project. When it comes to generating posts, I have opted for static generation as my appro ...

Combining the power of next.js, styled-components, and react-helmet for seamless website development

Having trouble using both react-helmet and styled-component in conjunction with next.js. The next.js examples demonstrate utilizing each package in separate repositories. Does anyone know how to resolve this issue? ...

Whenever signing in with Next Auth, the response consistently exhibits the values of "ok" being false and "status" being 302, even

I am currently using Next Auth with credentials to handle sign-ins. Below is the React sign-in function, which can be found at this link. signIn('credentials', { redirect: false, email: email, password: password, ...