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.
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:
getBy*, getAllBy*, findBy*, or findAllBy* query;findBy* or findAllBy* query passed as the first argument.Custom queries, computed calls, and complex callbacks are outside its scope.
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.
);
});
import { screen, waitForElementToBeRemoved } from '@testing-library/react';
test('hides the alert', async () => {
await waitForElementToBeRemoved(
() => screen.queryByRole('alert'),
);
});
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.
);
});
import { screen, waitForElementToBeRemoved } from '@testing-library/react';
test('hides the alert after it appears', async () => {
const alert = await screen.findByRole('alert');
await waitForElementToBeRemoved(alert);
});
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.
);
});
import { screen, waitForElementToBeRemoved } from '@testing-library/react';
test('hides the alert after it appears', async () => {
const alert = await screen.findByRole('alert');
await waitForElementToBeRemoved(alert);
});