reactjsasynchronousreact-routermouseeventreact-testing-library

fireEvent.click() on NavLink not triggering history.push() in react testing library


I'm trying to verify that a certain route is called after clicking a link that is generated after an async api call. I tested for the occurrence of the link and the correct href attribute beforehand and now want to simulate a user click on the react NavLink (mocking the methods that should be called by the click).

It seems neither history.push() nor handleClick() are being called by the fireEvent. I always get this error:

Error: expect(jest.fn()).toHaveBeenCalled()

Expected number of calls: >= 1
Received number of calls:    0

I tried adding the mouse button and bubbling (according to Test : Click (fireEvent.click) on Link in react-router-dom with react-testing-library doesn't trigger and other posts), without success. Here's the code:

import {findByTestId, fireEvent, render, screen, waitFor} from '@testing-library/react';
import { MemoryRouter } from "react-router-dom";
import { createMemoryHistory } from 'history';
import Card from "./Card";

test('click on card leads to newgame/gameName route', async () => {
    const history = createMemoryHistory();
    history.push = jest.fn();
    render(<MemoryRouter history={history}><Card element={{name: "3ts", title: "3Ts", description: "Tic Tac Toe"}}/></MemoryRouter>);
    const card = await screen.findByTestId('card-link');
    expect(card).toHaveAttribute('href', '/newGame/3ts');
    card.handleClick = jest.fn(); // I tried this after history.push didn't seem to work
    fireEvent(card, new MouseEvent('click', {bubbles: true})); // I tried adding bubbling after I read another user's situation where this helped.

    expect(card.handleClick).toHaveBeenCalled();
    expect(history.push).toHaveBeenCalled();
})

Solution

  • If you want to test a single component in a MemoryWrapper you have to configure some Routes as shown in the docu. It depends on your testing goal but if you have a NavLink in your Card component that routes to "aGame" url your test could look like this:

    test('click card and jump to agame', async () => {
    //arrange
    render(<MemoryRouter>
        <Routes>
            <Route path="/" element={<Card
                element={
                    {
                        name: "aName",
                        title: "a Title",
                        description: "A description",
                    }
                }
            />}/>
            <Route path="/aGame/:something" element={<NewGame/>}/>
        </Routes>
    </MemoryRouter>);
    
    let navLink = await screen.findByTestId('card-link');
    
    //act
    fireEvent.click(navLink);
    
    //assert
    let aGameDiv = screen.getByTestId('agame');
    expect(aGameDiv).toBeInTheDocument();
    });
    

    If you want to test your routing setup, you should test this in App.js or wherever the routing setup happens.