1
import { render, screen, waitFor } from '@testing-library/react';
import React from "react";
import { createMemoryRouter, RouterProvider, Outlet, createBrowserRouter } from 'react-router-dom';

const Layout = () => (
  <div>
    <header>Header</header>
    <main>
      <Outlet />
    </main>
    <footer>Footer</footer>
  </div>
);


const routes = [
  {
    path: '/',
    element: <Layout />,
    children: [
      { path: '/', element: <>home</> },
      { path: 'about', element: <>about</> },
    ],
  },
];
const router = createBrowserRouter(routes);

function App() {
  return <RouterProvider router={router} />;
}



describe("sample code ", () => {
  it("test home", async () => {
    const router1 = createMemoryRouter(routes, { initialEntries: ['/about'] })
    render(
      <RouterProvider router={router1} />
    );

    expect(screen.getByText(/header/i)).toBeInTheDocument();
    await waitFor(() => expect(screen.getByText(/about/i)).toBeInTheDocument());
    expect(screen.getByText(/footer/i)).toBeInTheDocument();
  })
})

I'm currently testing routes and nested routes using initial entries in React Testing Library (RTL). However, I'm encountering an issue where the Outlet component isn't functioning as expected in my tests, even though it works perfectly in the actual application.

4
  • 1
    You are rendering a data BrowserRouter (via App) within a "legacy" MemoryRouter. You can't, and shouldn't, render routers within routers. What exactly are you trying to unit test there? Please edit to clarify that you are trying to accomplish and include a complete minimal reproducible example of the relevant code. Commented Dec 16, 2024 at 16:11
  • Thank you for pointing that out. I apologize for the confusion in my earlier message. I've updated the code. Commented Dec 16, 2024 at 21:53
  • Is routes from createMemoryRouter(routes, { initialEntries: ['/about'] }) in the testing code the same routes from the App component code? Or is this actually all just your unit test code? It's still unclear what exactly you are wanting to unit test here. On the surface it looks like you are trying to unit test the 3rd-party React-Router code, that it correctly matches a URL path and renders the appropriate routed component UI. Commented Dec 16, 2024 at 22:08
  • Thank you for your response! To clarify, I’m not testing React Router itself but my own routing setup. Specifically, I’m using createMemoryRouter with initialEntries set to /about, and I expect it to render the Layout component with Header, Footer, and the about content in the Outlet. While Header and Footer render as expected, the Outlet isn’t rendering the about content in my test, even though it works in the actual app. I’d appreciate any guidance on why this might be happening or how to fix it in my test setup. Commented Dec 16, 2024 at 22:50

1 Answer 1

1

I was initially able to reproduce the testing issue you described. The same code you have here works perfectly well in React-Router-DOM 6, so something non-trivial, but very subtle, changed in the major version update from v6 to v7.

I checked:

It was in the migration guide where they discuss in better detail the deprecations and changes to be made from v6 to v7. Specifically the "Update DOM-specific imports" section at the end that describes where RouterProvider is exported from in the repo.

👉 Update DOM-specific imports

RouterProvider and HydratedRouter come from a deep import because they depend on "react-dom":

-import { RouterProvider } from "react-router-dom";
+import { RouterProvider } from "react-router/dom";

Note you should use a top-level import for non-DOM contexts, such as Jest tests:

-import { RouterProvider } from "react-router-dom";
+import { RouterProvider } from "react-router";

Congratulations, you're now on v7!

In your unit test code, import the router components from react-router instead of react-router-dom.

import { render, screen } from "@testing-library/react";
import "@testing-library/jest-dom";
import React from "react";
import {
  createMemoryRouter,
  RouterProvider,
  Outlet,
  createBrowserRouter,
} from "react-router"; // <-- Use main React-Router package!

const Layout = () => (
  <div>
    <header>Header</header>
    <main>
      <Outlet />
    </main>
    <footer>Footer</footer>
  </div>
);

const routes = [
  {
    path: "/",
    element: <Layout />,
    children: [
      { path: "/", element: <>home</> },
      { path: "about", element: <div>about</div> },
    ],
  },
];
const router = createBrowserRouter(routes);

function App() {
  return <RouterProvider router={router} />;
}

describe("sample code ", () => {
  it("test home", async () => {
    const router1 = createMemoryRouter(routes, { initialEntries: ["/about"] });
    render(<RouterProvider router={router1} />);

    expect(screen.getByText(/header/i)).toBeInTheDocument();
    expect(screen.getByText(/about/i)).toBeInTheDocument();
    expect(screen.getByText(/footer/i)).toBeInTheDocument();
  });
});

Even though @remix/react-router re-exports all of react-router-dom, this doesn't include the RouterProvider which is located in another repo package. Using react-router-dom alone doesn't get you the correct imports.

imports from react-router-dom imports from react-router
imports from react-router-dom imports from react-router
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.