How to test link using react-testing-library and jest
Asked Answered
B

2

6

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
Basinger answered 31/1, 2022 at 21:57 Comment(2)
Does this answer your question? How can I testi React Router with JestTithonus
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. stackoverflow.com/help/minimal-reproducible-exampleSharronsharyl
G
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.

Groce answered 3/4, 2022 at 21:2 Comment(0)
C
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.

Constantin answered 31/1, 2022 at 23:45 Comment(4)
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!Basinger
In my case, it is still in the same view, even when it works in the browser, it navigates back to home.Corum
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...Groce
@JuanPortillo @Corum My tests were failing because I was using <Link reloadDocument ...> 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.Groce

© 2022 - 2024 — McMap. All rights reserved.