Skip to content

waitFor with fake timers does not work with getByRole #1108

Closed as not planned
@danny-does-stuff

Description

@danny-does-stuff
  • @testing-library/dom version:
    7.22.6 (via @testing-library/react v10.4.9)
  • Testing Framework and version:
    jest v26.6.0 (via react-scripts v4.0.3)
  • DOM Environment:
    jsdom 16.6.0 (via react-scripts v4.0.3)

Relevant code or config:

import React, { useEffect, useState } from 'react'
import { render, screen, waitFor } from '@testing-library/react'

/**
 * Displays the passed `message` after `delay`
 */
function Test({ delay, message }) {
	console.log('rendering')
	const [delayedMessage, setDelayedMessage] = useState(null)
	useEffect(() => {
		setTimeout(() => {
			setDelayedMessage(message)
		}, delay)
	}, []) // Only run once

	return <h2>{delayedMessage}</h2>
}

describe('Fake Timers and waitFor and getByRole', () => {
	test('waitFor should work when used with fake timers and a long WAIT_TIME', async () => {
		jest.useFakeTimers()
		const WAIT_TIME = 80000
		const MESSAGE = 'Displayed Message'
		render(<Test delay={WAIT_TIME} message={MESSAGE} />)

		await waitFor(
			() => {
				expect(screen.getByRole('heading', { name: MESSAGE })).toBeTruthy()
				expect(screen.getByText(MESSAGE)).toBeTruthy()
			}
			// ,{ timeout: WAIT_TIME }
		)

		jest.useRealTimers()
	})
})

What you did:

Trying to test a component that depends on an interval to fetch some data. The code above is a much more simplified version with no network, no promises, etc.

What happened:

When trying to query the screen with getByRole, the test would fail. Using getByText allowed it to succeed. This only happens when the wait time is significantly larger than the default waitFor timeout.

Reproduction:

The code above is all that is needed to break it. Here's a minimal repro https://github.com/dannyharding10/waitFor-useFakeTimers-fail

Problem description:

This was extremely confusing to come across, especially after I saw #661 and the corresponding PR that fixed it (at least mostly 😅 ). I expected waitFor to either always work or always fail in this situation, but it seems to work only sometimes.

Suggested solution:

I don't think it makes sense to allow waitFor to just go forever, but it would be nice if the behavior were more consistent. If the asynchronous action took longer than the timeout for waitFor, then it should fail 100% of the time. Right now, it seems like it succeeds and fails conditionally depending on the complexity of the query (*ByRole vs *ByText), the duration (always fails with a crazy high delay, like 1_000_000+), etc.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions