I'm trying to test that a component that gets its data from an API loads as expected using react-router-dom
. It seems like the test needs to wait for the component's Promise to resolve, but I can't seem to find an approach that works.
Here's the really simple App
I'm testing.
class App extends React.Component {
render() {
return (
<div className="container">
<Switch >
<Route path='/object/:id'>
<ObjectDetail />
</Route>
<Route path='/'>
<Table />
</Route>
</Switch>
</div>
)
}
}
export default App
And here's the objectDetail
component (which uses a service to handle the API call):
class ObjectDetail extends React.Component {
state = {
data: {},
dataResolved: false,
}
componentDidMount() {
const id = this.props.match.params.id;
objectService.getObjectDetail(id).then((result) => {
this.setState({
data: result,
dataResolved: true,
});
});
document.title = 'Object Detail';
}
render() {
const { data, dataResolved } = this.state;
if(dataResolved) {
return (
<div>
<h1>
Object Detail
</h1>
// ... etc.
);
} else {
return null;
}
}
}
export default withRouter(ObjectDetail)
The Table
component renders a table with objects along with a details link, and the ObjectDetail
component renders a heading, so I'm trying to test that the link navigation works as expected, like so:
test('navigates to details when you click the details link', async () => {
render(
<MemoryRouter>
<App />
</MemoryRouter>
);
await wait();
const rows = within(screen.getByRole('rowgroup', {name: 'Table Body'})).getAllByRole('row');
const link = within(rows[0]).getByRole('link')
fireEvent.click(link)
expect(screen.getByRole('heading', {name: 'Object Detail'})).toBeInTheDocument();
});
Everything works as I'd expect when I run the app and test manually as well as using Cypress for integration testing, but the test fails because it only renders this (falling back to else { return null; }
):
<body>
<div>
<div
class="container"
/>
</div>
</body>
I've tried adding another await wait();
between the .click()
and expect
with the same result, and I tried what was proposed in this answer to a similar question without success.
I've tried some other approaches that seem to force the test to wait naively (like using setTimeout
) and await waitForElement(() => screen.getByRole('heading'))
. Interestingly enough, those cause the test to error out with a Error: Request failed with status code 404
, which is even more baffling.
How can I write a test that will render the routed component correctly?