'How to test link using react-testing-library and jest

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


Solution 1:[1]

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.

Solution 2:[2]

@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.

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 exaucae
Solution 2 lortimer