-
Notifications
You must be signed in to change notification settings - Fork 231
Description
react-hooks-testing-libraryversion: 7.0.0reactversion: 17.0.2react-domversion: 17.0.2nodeversion: 14.16.0npmversion: 7.10.0
Problem
When using waitFor when Jest has been configured to use fake timers then the waitFor will not work and only "polls" once. After that the test just hangs until Jest comes in and fails the test with that the test exceeds the timeout time. Below is some code that showcases the problem.
import { renderHook } from '@testing-library/react-hooks'
it('does not work', async () => {
jest.useFakeTimers()
const { waitFor } = renderHook(() => {})
await waitFor(() => {
console.log('poll') // Is printed just once
expect(false).toBe(true)
}, { timeout: 25, interval: 10 })
// Fails with Exceeded timeout of 5000 ms for a test.
})Basically the waitFor from @testing-library/react-hooks is using the faked setTimeout or setInterval which prevents it from working correctly.
There is a workaround (see suggested solution) but I recommend providing a nice error message when waitFor is used together with faked timers or maybe change the implemenation so it will work with fake timers.
Suggested solution
I found this issue and it seems that person has already been fixed in @testing-library/dom. From my perspective I can suggest maybe reuse that function instead of implementing it yourselves but I don't really know the internal structure / code.
But after finding that issue and realizing that is has been fixed there, then I use the following code as a workaround which works fine.
import { waitFor } from '@testing-library/react'
it('works', async () => {
jest.useFakeTimers()
await waitFor(() => {
console.log('poll') // Is printed twice
expect(false).toBe(true)
}, { timeout: 25, interval: 10 })
// Fails with false is not equal to true
})A more real world scenario
If curios on the actual problem I'm facing is to test the following hook:
function useSomething({ onSuccess }) {
const poll = useCallback(async () => {
const result = await fetch(/* ... */)
if (result.ok) onSuccess()
}, [onSuccess])
useEffect(() => {
const id = setInterval(() => { poll() }, 2000)
return () => clearInterval(id)
}, [poll])
}What I want to do is test that it invokes the onSuccess function on a successfull poll.
it('invokes the `onSuccess` on successfull poll', async () => {
const onSuccess = jest.fn()
jest.useFakeTimers()
const { waitFor } = renderHook(() => useSomething({ onSuccess }))
jest.runOnlyPendingTimers()
await waitFor(() => expect(onSuccess).toHaveBeenCalled())
})