Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

waitFor doesn't work if jest fake timers are used #631

Open
Labels
@DennisSkoko

Description

  • react-hooks-testing-library version: 7.0.0
  • react version: 17.0.2
  • react-dom version: 17.0.2
  • node version: 14.16.0
  • npm version: 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())
})

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

        AltStyle によって変換されたページ (->オリジナル) /