I'm using React Testing Library to test a component that fetches data in componentDidMount via redux thunks, with a MSW JS-mocked backend.
My test is essentially:
- render app
- wait for the component's "loading state/spinner" to clear (after all the redux thunk stuff succeeds), which is signified by the presence of a button with the text "Create Card"
- click the "Create Card" button
- wait for redux thunk stuff to succeed in creating the card, similarly to #2
- query the component for evidence of the created card
const history = createMemoryHistory()
history.push(`/${DECK_ID}`)
const { getByText } = render(
<Provider store={store}>
<Router basename="/games" history={history}>
<App />
</Router>
</Provider>
)
await waitFor(() => getByText('Create Card'))
fireEvent.click(getByText('Create Card'))
await waitFor(() => getByText(`Id: ${NEW_CARD_TYPE_ID}`))
The weird thing is that 'Create Card' isn't found:
TestingLibraryElementError: Unable to find an element with the text: Create Card...
...
124 | await waitFor(() => getByText('Create Card'))
> 125 | fireEvent.click(getByText('Create Card'))
But it IS found when I either put an additional (redundant) waitFor before it, like:
await waitFor(() => getByText('Create Card'))
await waitFor(() => getByText('Create Card'))
fireEvent.click(getByText('Create Card'))
await waitFor(() => getByText(`Id: ${NEW_CARD_TYPE_ID}`))
... or wrap the click action in its own waitFor, like this:
await waitFor(() => getByText('Create Card'))
await waitFor(() => {
fireEvent.click(getByText('Create Card'))
})
await waitFor(() => getByText(`Id: ${NEW_CARD_TYPE_ID}`))
And what's even weirder, when I add an assertion at the end, I start getting Warning: Encountered two children with the same key errors from the test. I never called the click event twice, but it appears as though more than 1 card was created:
await waitFor(() => getByText('Create Card'))
await waitFor(() => {
fireEvent.click(getByText('Create Card'))
})
await waitFor(() => getByText(`Id: ${NEW_CARD_TYPE_ID}`))
expect(getByText(`Id: ${NEW_CARD_TYPE_ID}`).textContent).toBe(`Id: ${NEW_CARD_TYPE_ID}`)
Anyone have any idea why this might be happening? The example in the Testing Library link shows that you can mix non-async events with async waiting, so I'm not sure what's up.
Thanks