Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Error: invariant expected app router to be mounted why this happened when using react testing library

when i use react-testing-library, it says that error: invariant expected app router to be mounted , it has no such problem when running in the development environment.

the testing logics is here

import { render, screen } from "@testing-library/react"
import userEvent from "@testing-library/user-event"
import NavBar from "@/components/NavBar";

describe("<NavBar>", () => {
    it ("the login pop out displayed after click the login/sign up button", async () => {
        render(<NavBar />);

        const loginButton = screen.getByRole("link", {
            name: "LOGIN/SIGN UP"
        });
        const loginCard = screen.getByTestId("loginCard");

        await userEvent.click(loginButton);
        expect(loginButton).toBeCalled();
        expect(loginCard).toBeVisible();
    })
});

the components is here: Navbar:

"use client"

import React, { Dispatch, SetStateAction } from "react";
import { useRouter } from "next/navigation";

interface NavBarProps {
  setVisibleLogin?: Dispatch<SetStateAction<boolean>>;
}

const NavBar = ({ setVisibleLogin }: NavBarProps) => {
  const router = useRouter();

  const handleLoginBtn = () => {
    setVisibleLogin && setVisibleLogin(true);
  };

  const handleHome = () => {
    router.push("/");
  };

  return (
    <div className="bg-slate-50 flex justify-center">
      <div className="fix w-912px top-0 navbar w-require">
        <div className="navbar-start">
          <a
            className="btn btn-ghost normal-case text-xl text-sky-500"
            onClick={handleHome}
          >
            Alimama MealGPT
          </a>
        </div>
        <div className="navbar-center hidden lg:flex"></div>
        {
          // for distinguish between the page for login and others
          setVisibleLogin && (
          <div className="navbar-end">
            <a className="btn" onClick={handleLoginBtn}>
              Login/Sign Up
            </a>
          </div>
          )
        }
      </div>
    </div>
  );
};

export default NavBar;

LoginCard:

"use client"

import React, { Dispatch, SetStateAction } from "react";
import InputBox from "./InputBox";
import Image from "next/image";
import { useRouter } from "next/navigation";

import GoogleLoginCard from "@/assets/[email protected]"

interface LoginCardProps {
  visibleLogin: boolean;
  setVisibleLogin: Dispatch<SetStateAction<boolean>>;
}

const LoginCard = ({visibleLogin, setVisibleLogin}: LoginCardProps) => {
  const router = useRouter();

  const handleCancel = () => {
    setVisibleLogin(false);
  }

  const handleSignUp = () => {
    router.push("/SignUp");
  }

  return (
    <div 
    className="fixed left-1/2 top-1/2 -translate-x-2/4 -translate-y-2/4 z-50" 
    style={{ visibility: visibleLogin ? "visible" : "hidden" }}>
      <div className="card w-96 bg-neutral text-neutral-content bg-slate-200">
        <div className="flex flex-row-reverse">
          <div className="flex flex-row-reverse" style={
            {
              position: "relative",
              top: "0.5rem",
              right: "0.5rem"
            }
          }>
            <button className="btn btn-outline w-2 h-2" onClick={handleCancel}>X</button>
          </div>
          <a className="relative right-10 top-3 btn btn-ghost normal-case text-xl text-sky-500">Alimama MealGPT</a>
        </div>
        <div className="card-body items-center text-center">
          <h2 className="card-title">Login</h2>
          <InputBox
            title="Email/ User ID"
            textHolder="Please Enter Your Email or ID Here"
            otherLeftOption={false}
            otherRightOption={false}
          />
          <InputBox
            title="Password"
            textHolder="Please Enter Your Password Here"
            otherLeftOption={true}
            otherRightOption={true}
            optionLeftText="Forget Password?"
            optionRightText="Any Other Helps?"
          />
          <div className="card-actions justify-end">
            <button className="btn btn-primary w-27">Log In</button>
            <button className="btn btn-primary w-25" onClick={handleSignUp}>Sign Up</button>
            <button className="btn btn-outline" onClick={handleCancel}>Cancel</button>
          </div>
          <div>
            <Image src={GoogleLoginCard}
              height={200}
              width={200}
              alt="google login button"
            />
          </div>
        </div>
      </div>
    </div>
  );
};

export default LoginCard;

I saw some other similar question have such solution like add the html head and body tag on layout.tsx, i put head and body tag in layout.tsx already.

Does anyone know how to fix this problem?

I expect the solusion for fix the problem.

like image 961
Howard Cui Avatar asked Sep 13 '25 15:09

Howard Cui


1 Answers

For me it worked to mock useRouter in the Jest test like this:

imports ...

// Mock useRouter:
jest.mock("next/navigation", () => ({
  useRouter() {
    return {
      prefetch: () => null
    };
  }
}));

describe("Test something", () => { ...

The idea was taken from here (I just changed "next/router" to "next/navigation").

like image 60
rangfu Avatar answered Sep 15 '25 14:09

rangfu