Asynchronous Query Not Awaited

ID

javascript.testing_library_query_not_awaited

Severity

high

Remediation Complexity

trivial

Remediation Risk

low

Remediation Effort

low

Resource

Reliability

Language

JavaScript

Tags

async, reliability, testing

Description

Reports a Testing Library findBy* or findAllBy* query written as a bare statement.

The find* family is the asynchronous half of the query API: it retries until the element appears and returns a promise, where get* and query* answer at once. A statement that neither awaits nor returns that promise starts the polling and throws the handle away.

Only a discarded result is reported. An await, a return, a .then() continuation and an argument position all hand the promise on. A result assigned to a variable is not reported either, even without an await: the variable may be awaited on a later line, which is a legitimate way to start two queries in parallel.

javascript.promise_result_ignored cannot host this — it resolves the callee to a locally declared async function, and these queries come from the library.

Rationale

// Bad
it('shows the alert', async () => {
  submit();
  screen.findByRole('alert');                // starts polling, waits for nothing
  expect(log.entries).toHaveLength(1);
});
  • Nothing is waited for, so the assertions that follow run against the page as it was before the element arrived.

  • When the element never appears, the query’s rejection has no owner: it becomes an unhandled rejection charged to a later test, or a bare "test timed out" that never names the missing element.

  • The two query families differ by four characters, so the mistake reads as correct code in review.

  • The result depends on how fast the machine is, which is what makes this a leading cause of tests that pass locally and fail in CI.

Remediation

Await the query.

// Good
it('shows the alert', async () => {
  submit();
  await screen.findByRole('alert');
  expect(log.entries).toHaveLength(1);
});

© 2026 Xygeni. All rights reserved