{ const location = u" /> { const location = u" /> { const location = u"/>

Can't see that window.location has changed - react testing library

3.1k Views Asked by At

I have a simple 404 component that will change the window.location to prepend "/404" to the current location.

const My404 = () => {
    const location = useLocation();

    useEffect(() => {
        window.location.replace(`/404${location.pathname}`);
    }, []);

    ...

I am attempting to test this with React Testing Library but I can't seem to get it to see the change, even after using await waitFor...

it('Should add /404 to start of pathname', async () => {
        const history = createMemoryHistory()
        history.push('/some-route')
        const rendered = render(
            <Router history={history}>
                <My404 />
            </Router>
        );
        
        await waitFor(() => expect(window.location.pathname).toBe('/404/some-route'));
    });

The test fails:

Expected: "/404/some-route"
Received: "/"

UPDATE I just did a quick check by logging out both the location and the window.location.pathname before and after the change...

location:

{ location:
   { 
      pathname: '/some-route',
      search: '',
      hash: '',
      key: '14xswz' 
   } 
}

window.location.pathname:

/
1

There are 1 best solutions below

0
physicsboy On

I managed to solve the issue before @Chris gave me the suggestion to use the React-specific useHistory hook instead. I will outline both ways in which I fixed the test.

Not using useHistory - The old-fashioned way

it('Should add /404 to start of pathname', () => {
    // Create a mock of the window.location.replace function
    const replaceMock = jest.fn();

    // Define the window to use the mocked function above
    Object.defineProperty(window, "location", {
        value: {
            replace: replaceMock,
        },
        writable: true
    });

    const rendered = render(
        <MemoryRouter initialEntries={['/some-route'}>
            <My404 />
        </MemoryRouter>
    );

    // Expect mocked function to be called once with arg
    expect(replaceMock).toHaveBeenCalledTimes(1);
    expect(replaceMock).toHaveBeenLastCalledWith('/404/some-route');
});

Using useHistory I reworked the component to use the useHistory hook rather than relying on window.location.

useEffect(() => {
    history.replace(`/404${location.pathname}`);
}, []);

I then updated the test to reflect the changes

it('Should add /404 to start of pathname', () => {
    const history = createMemoryHistory({ initialEntries: ['/some-url'] });

    const rendered = render(
        <Router history={history}>
            <My404 />
        </Router>
    );

    expect(history.location.pathname).toBe('/404/some-url');
});