Testing React Component with React Router V6
Asked Answered
S

2

16

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.

Sapsucker answered 31/12, 2021 at 21:35 Comment(0)
W
9

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>
);
Weston answered 2/1, 2022 at 8:21 Comment(0)
E
6

Hope I can help someone.

This is my scenario:

  • Component that receives a parameter using useParams.
  • Testing library
  • React Router 6
  • Typescript

Component:

type UrlParamsType = {
  id: string;
};
const Code: FC = () => {
  const navigate = useNavigate();
  const { id } = useParams<keyof UrlParamsType>() as UrlParamsType;
return (<label> {id} </label>);};

I do several stuff with that id, so I needed it to be testable with testing-library.

import { createMemoryHistory } from 'history';
import { Router, Routes, Route } from 'react-router-dom';
it('should show something', () => {
    const history = createMemoryHistory();
    const route = '/something/sms/9393939393';
    history.push(route);
    render(
      <Router location={history.location} navigator={history}>
        <Routes>
          <Route path="/something/sms/:id" element={<Code/>} />
        </Routes>
      </Router>
    );
    expect(screen.getByText(/9393/i));
  });

In case this doesn't work for you, or history package is not available:

render(<MemoryRouter initialEntries={['/something/sms/9393939393']}>
  <Routes>
     <Route path="/something/sms/:id" element={<Code/>} />
  </Routes>
</MemoryRouter>);
Embowed answered 2/6, 2022 at 19:47 Comment(1)
I was stuck for some hours until I got your answer. I finally got my problem to get to work. BTW, from my side +1 first time upvoting in old question but still helpful :) Thanks a lot!!Secretarygeneral

© 2022 - 2024 — McMap. All rights reserved.