Side Effect In A waitFor Callback

ID

javascript.side_effect_in_wait_for

Severity

high

Remediation Complexity

trivial

Remediation Risk

low

Remediation Effort

low

Resource

Reliability

Language

JavaScript

Tags

async, reliability, testing

Description

Reports a fireEvent, userEvent or render call inside a waitFor callback.

waitFor polls its callback — on a timer and again on every DOM mutation — until it stops throwing or the timeout expires. The callback is therefore run an unknown number of times, and it must only observe.

Queries are deliberately not reported: screen.getByText(…​) inside the callback is what waitFor is for. Only the calls that change the page are a defect.

Rationale

// Bad
await waitFor(() => {
  fireEvent.click(button);                       // dispatched once per poll
  expect(counter).toHaveTextContent('1');
});
  • The effect is applied on every attempt, so ten clicks are dispatched where the test meant one and the assertion is measured against a page the test never described.

  • The effect usually causes the very mutation that schedules the next poll, so the callback drives its own re-entry.

  • The test then either times out or passes for a reason unrelated to the behaviour under test, and both outcomes change with machine speed.

  • Nothing in the failure message mentions the extra dispatches, so the usual response is to raise the timeout, which makes it slower and no more correct.

Remediation

Dispatch the event once, then wait for the expectation on its own.

// Good
fireEvent.click(button);

await waitFor(() => {
  expect(counter).toHaveTextContent('1');
});

© 2026 Xygeni. All rights reserved