scottrippey / next-router-mock

Mock implementation of the Next.js Router
MIT License
400 stars 38 forks source link

How to mock next router with jest `experimental-vm-modules` #81

Open acc-nicholas opened 1 year ago

acc-nicholas commented 1 year ago

I've been trying a ton of different ways but nothing i try seems to work. Any ideas or suggestions would be greatly appreciated.

// Test file
import React from "react";
import { render, screen } from "@accolade-x/test-utils";
import { jest } from "@jest/globals";

jest.unstable_mockModule("next/router", () => require("next-router-mock"));

describe("BackButton", () => {
  it("should render an button tag", async () => {
    const { BackButton } = await import("./BackButton");
    render(<BackButton />);
    expect(await screen.findByRole("button")).toBeInTheDocument();
  });
});
// Component file
export const BackButton = ({
  onClick,
  ...rest
}: BackButtonProps) => {
  const router = useRouter();
  const clickHandler: MouseEventHandler<HTMLButtonElement> = (e) => {
    if (onClick) {
      onClick(e);
    }
    router.back();
  };
  return (
    <Button onClick={clickHandler} {...rest}>
      Back
    </Button>
  );
};
acc-nicholas commented 1 year ago

Normal mocks do not get hoisted with esm: https://jestjs.io/docs/manual-mocks#using-with-es-module-imports Prescribed method of mocking (that i can't get working): https://jestjs.io/docs/ecmascript-modules#module-mocking-in-esm

valleywood commented 1 year ago

Also struggling with this. Seems like useRouter isn't mocked since i updated my package to be an ESM module

NextRouter was not mounted. https://nextjs.org/docs/messages/next-router-not-mounted

   8 |
   9 | export const MyPage = () => {
> 10 |   const router = useRouter();
     |                  ^

Has anyone been able to mock next router when working with ESM modules?

ludwighogstrom commented 1 year ago

Running Jest via next/jest (typescript project).

Had problem with the next-router-mock and got the NextRouter was not mounted. message.

Moved out the jest.mock("next/router", () => require("next-router-mock")); from the actual test(s) file to a file called from the setupFilesAfterEnv prop in jest.config.js. See the "do this globally" example here https://github.com/scottrippey/next-router-mock#usage-with-jest

Seems to work!

In jest.config.js:

const nextJest = require("next/jest");

const createJestConfig = nextJest({
  // Provide the path to your Next.js app to load next.config.js and .env files in your test environment
  dir: "./",
});

// createJestConfig is exported this way to ensure that next/jest can load the Next.js config which is async
module.exports = createJestConfig({
  resetMocks: false,
  moduleDirectories: ["node_modules", "src"],
  setupFiles: ["jest-localstorage-mock", "<rootDir>/jest.env.js"],
  setupFilesAfterEnv: ["<rootDir>/jest.setup.js"],
  testEnvironment: "jest-environment-node",
});

In jest.setup.js (a bunch of other logic stripped from this file):

import { jest } from "@jest/globals";
...
jest.mock("next/router", () => require("next-router-mock"));