'Testing React Component with React Router V6

I understand that React Testing Library has an example of testing with react router, but I couldn't get it to work (I think because I am using react router V6).

Basically, I need router testing because I have details component that uses useParams() to get part of the url. I can't render the component without it.

This was my attempt to make it work (yes the page also needs apollo, although it doesn't really need redux).

const AllTheProviders = ({children, initialRoutes}) => {
    return (
        <ApolloProvider client={client}>
            <Provider store={store}>
                <MemoryRouter>
                    {children}
                </MemoryRouter>
            </Provider>
        </ApolloProvider>
    );
}
const customRender = (ui, options) => render(ui, {wrapper: AllTheProviders, ...options})


beforeEach(() => {
    window.history.pushState({}, 'Test page',"/details/url-param-the-component-needs")
    customRender(<App/>);
});

No surprise, but this didn't work. I assume window.history.pushState() doesn't work for react router V6. I tried using useNavigate(), but that doesn't work outside of a component.

If anybody has any ideas on how I could make this work. I would greatly appreciate it.



Solution 1:[1]

The MemoryRouter still takes an array of initialEntries.

MemoryRouter

declare function MemoryRouter(
  props: MemoryRouterProps
): React.ReactElement;

interface MemoryRouterProps {
  basename?: string;
  children?: React.ReactNode;
  initialEntries?: InitialEntry[];
  initialIndex?: number;
}

I would remove the MemoryRouter from the customRender and just wrap the component under test locally and pass in the specific initial route entries for the test.

const AllTheProviders = ({ children }) => {
  return (
    <ApolloProvider client={client}>
      <Provider store={store}>
        {children}
      </Provider>
    </ApolloProvider>
  );
};
const customRender = (ui, options) => 
  render(ui, { wrapper: AllTheProviders, ...options });

...

const { ....queries.... } = customRender(
  <MemoryRouter
    initialEntries={["Test page", "/details/url-param-the-component-needs"]}
  >
    <ComponentUnderTest />
  </MemoryRouter>
);

An additional thought, the useParams hook may also need a Route with a path prop specifying the match params the component needs, so your test could potentially look like the following:

const { ....queries.... } = customRender(
  <MemoryRouter
    initialEntries={["Test page", "/details/url-param-the-component-needs"]}
  >
    <Routes>
      <Route path="/details/:theParam" element={<ComponentUnderTest />} />
    </Routes>
  </MemoryRouter>
);

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 buzatto