This rule raises an issue when a promise returned by a Testing Library userEvent asynchronous method is not handled.
Starting with version 14, @testing-library/user-event asynchronous event methods return promises. These methods simulate interactions
as a user would perform them and may trigger asynchronous application behavior. Ignoring a returned promise lets the test continue before the
interaction finishes, so assertions can run against an intermediate state and produce unreliable results.
The asynchronous userEvent methods covered by this rule are clear, click, copy,
cut, dblClick, deselectOptions, hover, keyboard, pointer,
paste, selectOptions, tripleClick, type, unhover, upload, and
tab. Handle the promise by awaiting it, returning it to a caller that handles it, chaining it with then, or including it in
Promise.all or Promise.allSettled. The rule covers direct userEvent calls, calls through an instance created by
userEvent.setup(), and calls to wrappers that return a userEvent promise.
The rule applies to projects using a known @testing-library/user-event version 14 or later. Asynchronous fireEvent
methods are not covered.
import userEvent from '@testing-library/user-event';
import { screen } from '@testing-library/dom';
test('submits the form', async () => {
userEvent.click(screen.getByRole('button', { name: 'Submit' })); // Noncompliant: the promise returned by async event method "click" is unhandled
});
import userEvent from '@testing-library/user-event';
import { screen } from '@testing-library/dom';
test('submits the form', async () => {
await userEvent.click(screen.getByRole('button', { name: 'Submit' }));
});
import userEvent from '@testing-library/user-event';
import { screen } from '@testing-library/dom';
test('types a name', async () => {
const user = userEvent.setup();
user.type(screen.getByRole('textbox'), 'Ada'); // Noncompliant: the promise returned by async event method "type" is unhandled
});
import userEvent from '@testing-library/user-event';
import { screen } from '@testing-library/dom';
test('types a name', async () => {
const user = userEvent.setup();
await user.type(screen.getByRole('textbox'), 'Ada');
});
import userEvent from '@testing-library/user-event';
const pressEscape = () => userEvent.keyboard('{Escape}');
test('closes the dialog', async () => {
pressEscape(); // Noncompliant: the promise returned by "pressEscape" is unhandled
});
import userEvent from '@testing-library/user-event';
const pressEscape = () => userEvent.keyboard('{Escape}');
test('closes the dialog', async () => {
await pressEscape();
});
import userEvent from '@testing-library/user-event';
import { screen } from '@testing-library/dom';
test('dismisses notifications', async () => {
userEvent.click(screen.getByRole('button', { name: 'Dismiss warning' })); // Noncompliant: the promise returned by async event method "click" is unhandled
userEvent.click(screen.getByRole('button', { name: 'Dismiss update' })); // Noncompliant: the promise returned by async event method "click" is unhandled
});
import userEvent from '@testing-library/user-event';
import { screen } from '@testing-library/dom';
test('dismisses notifications', async () => {
await Promise.all([
userEvent.click(screen.getByRole('button', { name: 'Dismiss warning' })),
userEvent.click(screen.getByRole('button', { name: 'Dismiss update' })),
]);
});
userEvent