This rule raises an issue in JavaScript and TypeScript tests when a Testing Library waitForElementToBeRemoved callback uses a getBy* or getAllBy* query, or when the utility receives the promise returned by an asynchronous query.

Why is this an issue?

waitForElementToBeRemoved waits for an element or elements to leave the DOM. It accepts an element, an array of elements, or a callback that returns one. When the callback runs after removal, it needs to return null or an empty array so the utility can observe that the target is gone.

Testing Library’s getBy* and getAllBy* queries throw when no element matches. A callback that uses one can still observe removal because the utility treats this Testing Library error as successful removal. However, when the element is already absent before waiting starts, the query reports a generic lookup error instead of the utility’s specific precondition error. Use queryBy* or queryAllBy* instead so the callback returns null or an empty array in that case.

The findBy* and findAllBy* queries return promises. Passing one directly to waitForElementToBeRemoved makes the utility treat the promise as a DOM element and immediately rejects with a TypeError. Returning one from its callback keeps the wait active until it times out because the promise is neither null nor an empty array. When the test must wait for an element to appear before waiting for its removal, await the asynchronous query first, then pass the resulting element to the removal utility.

Passing a direct getBy* or getAllBy* result is valid because the query runs once and the utility receives the returned element or array. The rule reports only direct queries on an imported Testing Library screen object:

Custom queries, computed calls, and complex callbacks are outside its scope.

Code examples

Noncompliant code example

import { screen, waitForElementToBeRemoved } from '@testing-library/react';

test('hides the alert', async () => {
  await waitForElementToBeRemoved(
    () => screen.getByRole('alert'), // Noncompliant: Use a queryBy* callback so waitForElementToBeRemoved can report a clear error when the element is already absent.
  );
});

Compliant solution

import { screen, waitForElementToBeRemoved } from '@testing-library/react';

test('hides the alert', async () => {
  await waitForElementToBeRemoved(
    () => screen.queryByRole('alert'),
  );
});

Noncompliant code example

import { screen, waitForElementToBeRemoved } from '@testing-library/react';

test('hides the alert after it appears', async () => {
  await waitForElementToBeRemoved(
    screen.findByRole('alert'), // Noncompliant: A findBy* query returns a promise, not the element required by this disappearance wait.
  );
});

Compliant solution

import { screen, waitForElementToBeRemoved } from '@testing-library/react';

test('hides the alert after it appears', async () => {
  const alert = await screen.findByRole('alert');

  await waitForElementToBeRemoved(alert);
});

Noncompliant code example

import { screen, waitForElementToBeRemoved } from '@testing-library/react';

test('hides the alert after it appears', async () => {
  await waitForElementToBeRemoved(
    () => screen.findByRole('alert'), // Noncompliant: A findBy* query returns a promise, not the element required by this disappearance wait.
  );
});

Compliant solution

import { screen, waitForElementToBeRemoved } from '@testing-library/react';

test('hides the alert after it appears', async () => {
  const alert = await screen.findByRole('alert');

  await waitForElementToBeRemoved(alert);
});

Resources

Documentation