dan-klasson
dan-klasson

Reputation: 14190

Testing redirect after submit with React Testing Library

I'm trying to test a login component. Specifically that it redirects on a successful login. It works fine when testing it manually. But in my test it never does the redirect and it can therefor not find the "Logout" link:

test('successfully logs the in the user', async () => {
  const fakeUserResponse = {success: true, token: 'fake_user_token'}
  jest.spyOn(window, 'fetch').mockImplementationOnce(() => {
    return Promise.resolve({
      json: () => Promise.resolve(fakeUserResponse),
    })
  })
  const { getByText, getByLabelText, findByTestId } = render(<Router><Login /></Router>)

  fireEvent.change(getByLabelText(/email/i), {target: {value: '[email protected]'}})
  fireEvent.change(getByLabelText(/password/i), {target: {value: 'password1234'}})
  fireEvent.click(getByText(/submit/i))

  await waitForElement(() => getByText(/logout/i));
})

I'm redirecting with react-router version 4, like so:

{state.resolved ? <Redirect to="/" /> : null}

Am I going about this the wrong way?

Upvotes: 17

Views: 36344

Answers (4)

Matthieu Dsprz
Matthieu Dsprz

Reputation: 477

In my case, this gets the job done:

it('should do stuff and redirect to /some/route', async () => {
  // given
  const history = createMemoryHistory();

  // when
  render(<Router history={history}><MyComponent /></Router>);

  // do stuff which leads to redirection

  // then
  await waitFor(() => {
    expect(history.location.pathname).toBe('/some/route');
  });
});

Upvotes: 10

4FCG
4FCG

Reputation: 91

Personally I mock the history.replace function that is used by the Redirect component.

const history = createBrowserHistory();
history.replace = jest.fn();

render(
  <Router history={history} >
    <Component />
  </Router>
);

// trigger redirect

expect(history.replace).toHaveBeenCalledWith(expect.objectContaining({
  "pathname": "/SamplePath",
  "search": "?SampleSearch",
  "state": { "Sample": "State" }
}));

This allows you to check for more than just the correct path. Please ensure you are using Router and not BrowserRouter in your test. The latter does not accept a history prop.

Upvotes: 9

clemlatz
clemlatz

Reputation: 8063

You can mock the Redirect component's implementation to show some text including the path instead of redirecting to it:

jest.mock('react-router-dom', () => {
  return {
    Redirect: jest.fn(({ to }) => `Redirected to ${to}`),
  };
});

and expect your component to display the text with the correct path:

expect(screen.getByText('Redirected to /')).toBeInTheDocument();

Upvotes: 15

dan-klasson
dan-klasson

Reputation: 14190

So I ended up doing this:

const { getByText, getByLabelText, } = render(
  <Router>
    <Login/>
    <Switch>
      <Route path="/">
        <div>logged out</div>
      </Route>
    </Switch>
  </Router>
)

fireEvent.change(getByLabelText(/email/i), {target: {value: '[email protected]'}})
fireEvent.change(getByLabelText(/password/i), {target: {value: 'password1234'}})
fireEvent.click(getByText(/submit/i))

await waitForElement(() => getByText(/logged out/i))

Upvotes: 4

Related Questions