5

Trying to learn testing. Using testing-library, Jest, and React-Router v6, and Typescript. I'm trying to figure out how to test a link. I've been looking all over the place for a solution and I can't find one. Using React-Router v6. Code looks like the following (link is just a regular element with an href) just want to make sure the user gets to the new page (in this case the login page from the forgot password page).

//omitted imports but imported all appropriate items from below

describe('ForgotPassword', () => {
  test('User can navigate to login screen', async () => {
    render(
      <MemoryRouter initialEntries={['/forgot-password' ]}>
        <ForgotPassword />
      </MemoryRouter>)

    userEvent.click(screen.getByRole('link', { name: 'Back to Login' }))

    await waitFor(() => {
      expect(screen.getByRole('heading', { name: 'Login' })).toBeInTheDocument()
    })
  })

//also tried:

describe('ForgotPassword', () => {
  test('User can navigate to login screen', async () => {
    render(
      <MemoryRouter initialEntries={['/forgot-password' ]}>
        <Routes>
            <Route path='/forgot-password' component={<ForgotPassword />} />
            <Route path='/login' component={<Login />} />
        <Routes>
      </MemoryRouter>)

    userEvent.click(screen.getByRole('link', { name: 'Back to Login' }))

    await waitFor(() => {
      expect(screen.getByRole('heading', { name: 'Login' })).toBeInTheDocument()
    })
  })

//also tried the following:

const history = createMemoryHistory({ initialEntries: ['/home'] });
    const { getByText } = render(
      <Router history={history}>
        <ButtonLogin />
      </Router>
    );

got a TS error: Property 'history' does not exist on type 'IntrinsicAttributes & RouterProps'.

//also tried using fireEvent instead of userEvent
Drew Reese
  • 165,259
  • 14
  • 153
  • 181
Juan Portillo
  • 61
  • 1
  • 1
  • 3
  • Does this answer your question? [How can I testi React Router with Jest](https://stackoverflow.com/questions/69878146/how-can-i-testi-react-router-with-jest) – Willow Jan 31 '22 at 22:03
  • Is the link you are trying to test a component you created? Don't test 3rd-party code you didn't write. You mentioned using a href, please also include all the relevant code you are testing in your question. https://stackoverflow.com/help/minimal-reproducible-example – Drew Reese Feb 02 '22 at 01:10

2 Answers2

6

@exaucae's answer is perfect for regular Links. If you're using reloadDocument in your Link, your test will fail, and the console will show an error that says "Error: Not implemented: navigation (except hash changes)".

I want to use reloadDocument in my links so my whole app is refreshed when the user navigates. Below is how I'm testing those links. It's not how I would prefer to test them, but it gives me confidence that the links are working.

// NavMenu.tsx
import React from "react";
import { Link } from "react-router-dom";

export const NavMenu = () => {
    return (
        <div data-testid={"nav-menu"}>
            <Link reloadDocument to={"/some-page"}>Some Page</Link>
        </div>
    );
};


// NavMenu.test.tsx
import { NavMenu } from "./NavMenu";
import { render, screen } from "@testing-library/react";
import React from "react";
import { MemoryRouter, Route, Routes } from "react-router-dom";

describe(NavMenu.name, () => {
    test("should link", () => {
        render(
            <MemoryRouter>
                <Routes>
                    <Route path="/" element={<NavMenu/>}/>
                </Routes>
            </MemoryRouter>,
        );
        const links: HTMLAnchorElement[] = screen.getAllByRole("link");

        expect(links[0].textContent).toEqual("Some Page");
        expect(links[0].href).toContain("/some-page");
    });

I'm also going to implement my own wrapper around Link called RefreshingLink which always has reloadDocument. That way, any other developer who wants to add a link to the NavMenu can just follow the pattern and use a RefreshingLink, and not think about whether it refreshes or not.

lortimer
  • 701
  • 9
  • 14
3

Your second try was nearly good. You'd have to change component prop to element in react-router v6.x:


describe('ForgotPassword', () => {
  test('User can navigate to login screen', async () => {

    function ForgotPassword() {
      return (
        <div>
          <h1>Home</h1>
          <Link to="../login">Back to Login</Link>
        </div>
      );
    }
    render(
      <MemoryRouter initialEntries={['/forgot-password' ]}>
        <Routes>
            <Route path='/forgot-password' element={<ForgotPassword/>} />
            <Route path='/login' element={<h1>Login</h1>} />
        <Routes>
      </MemoryRouter>)

    userEvent.click(screen.getByRole('link', { name: 'Back to Login' }))

    await waitFor(() => {
      expect(screen.getByRole('heading', { name: 'Login' })).toBeInTheDocument()
    })
  })
})

Note: whenever in doubt, React-router-dom 's internal tests are a great way to have a hint.

exaucae
  • 2,071
  • 1
  • 14
  • 24
  • Sadly, this didn't work either. I got this error: "Not implemented: navigation (except hash changes)". By the way, I'm importing the Login component, and using material-ui if that offers any clues. Thanks so much for your help though! – Juan Portillo Feb 01 '22 at 14:40
  • In my case, it is still in the same view, even when it works in the browser, it navigates back to home. – SalahAdDin Mar 21 '22 at 20:17
  • I have the same problem. I think it's because the tests run in NodeJS, not a browser, and JSDOM doesn't implement some of the window methods. I'll post here if I ever figure it out... – lortimer Apr 03 '22 at 19:53
  • @JuanPortillo @SalahAdDin My tests were failing because I was using `` to refresh the whole page when clicking. React Router uses that flag to decide whether to use it's own click implementation or let the click pass through to the anchor tag. When it gets passed to the anchor during a Jest test, navigation fails because the tests are running in a NodeJS environment, and `window.location.reload` doesn't exist. I tried many ways to mock `window.location.reload` and none worked. I'll post an answer below with how I'm testing those links. – lortimer Apr 03 '22 at 20:53