How I could test location with MemoryRouter on React Router DOM v6?
Asked Answered
C

2

8

Using React Router DOM (v6) with React Testing Library, I would like to know if there's a way to deal with "mocked" history from createMemoryHistoryor an alternative like it.

Based on the example of Testing Library, I could mock the history on the Router component but in v6 this component doesn't accept the history prop anymore.

So, my doubt is: how can I test history and location with RTL (React Testing Library) with React Router DOM v6? Should I keep using the MemoryRouter?

Cystocele answered 11/12, 2021 at 8:32 Comment(3)
RRDv6 rather tucks away and internalizes its own history reference, so it's not directly exposed via any of the higher level routers. You can create your own custom router to use a custom history object.Demagogic
Yep! As I saw in the codebase, apparently they don't have any way to access the history reference on the Routers. So I think that I need to write a custom router to deal with history object as you say. Thank you for the help!Cystocele
You may find this answer helpful/useful. Cheers.Demagogic
L
17

The recommended testing method using React Router DOM v6.4.0 relies on createMemoryRouter which will return a RemixRouter. Instead of using history through createMemoryHistory the state object of the Router from createMemoryRouter can be used since this Router uses its own history in memory. The state object contains location which we can check for proper navigation.

Using the RouterProvider from React Router DOM we can pass the router made by createMemoryRouter to the provider and along to our tests.

import { render, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import {
  createMemoryRouter,
  RouterProvider,
  useNavigate,
} from 'react-router-dom'
import { screen } from '@testing-library/react'

// The element we want to render. Uses the hook useNavigate to send us somewhere.
const ElementToRender: React.FC = () => {
  const navigate = useNavigate()

  return <button onClick={() => navigate('/')}>Navigate to Home</button>
}

const setupMyTest = () => {
  const router = createMemoryRouter(
    [
      {
        path: '/',
        element: <>Navigated from Start</>,
      },
      {
        path: '/starting/path',
        // Render the component causing the navigate to '/'
        element: <ElementToRender />,
      },
    ],
    {
      // Set for where you want to start in the routes. Remember, KISS (Keep it simple, stupid) the routes.
      initialEntries: ['/starting/path'],
      // We don't need to explicitly set this, but it's nice to have.
      initialIndex: 0,
    }
  )

  render(<RouterProvider router={router} />)

  // Objectify the router so we can explicitly pull when calling setupMyTest
  return { router }
}

it('tests react router dom v6.4.0 navigates properly', async () => {
  const { router } = setupMyTest()

  // Given we do start where we want to start
  expect(router.state.location.pathname).toEqual('/starting/path')

  // Navigate away from /starting/path
  userEvent.click(screen.getByRole('button', { name: 'Navigate to Home' }))

  // Don't forget to await the update since not all actions are immediate
  await waitFor(() => {
    expect(router.state.location.pathname).toEqual('/')
  })
})

Liliuokalani answered 15/9, 2022 at 11:14 Comment(0)
A
-5

Using react router dom v6, Router use navigator prop to accept history.

const history = createMemoryHistory(); 
...
<MockedProvider mocks={[]}>
  <Router navigator={history} location={"/"}>
    <Home />
  </Router>
</MockedProvider> 
... 
await waitFor(() => {
  expect(history.location.pathname).toBe("/");
});
Alfano answered 6/2, 2022 at 13:0 Comment(1)
RRD V6's MemoryRouter doesn't accept navigator nor location propsFanniefannin

© 2022 - 2024 — McMap. All rights reserved.