For anyone viewing this in 2024, I tried the accepted answer and while it does work, a colleague mentioned that it does not actually test the routing logic in App.jsx
or App.tsx
(which is true).
So, here's how you should actually go about it:
- You need to wait for the component to actually render before checking whether it did
For example, if you have a private route for /home
that passes the auth token checks and renders the component below:
const HomePage = () => {
//...some logic
return <div>Home page</div>
}
And your App.jsx
looks like this:
import { Outlet } from 'react-router-dom';
// other imports
export const App = (props) => {
//... app logic
return (
<Routes>
<Route path='/' element={<Outlet />}>
<Route element={<CheckAuth token={authToken} />}>
// ... all private routes including '/home' ...
</Route>
</Route>
</Routes>
)
}
index.js
:
import App from 'App'
const root = createRoot(document.getElementById('root'))
root.render(
<BrowserRouter>
<App />
</BrowserRouter>
)
Then, your tests should look like this:
describe("App page", async () => {
...
delete window.location;
window.location = { replace: jest.fn() };
...
test('should test for private route with invalid authToken', () => {
// example private route with no valid auth: '/not-valid'
render(
<MemoryRouter initialEntries={['/not-valid']}>
<App />
</MemoryRouter>
)
expect(window.location.replace).toHaveBeenCalledWith(loginUrl)
})
test('should test for private route with valid authToken', () => {
render(
<MemoryRouter initialEntries={['/home']}>
<App />
</MemoryRouter>
)
expect(await screen.findByText('Home page')).toBeInTheDocument()
})
})
The key point here is to ensure you wait for the rendering to have happened before checking for whether it has successfully rendered the component for /home
And you do this by checking with an await i.e expect(await screen.findBy.......
.
window.location
? You probably want to render a redirect, i.e.<Navigate to={loginUrl} replace />
instead of mutating the location. – Thallus