Reputation: 14190
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
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
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
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
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