User Events

userEvent (re-exported from @testing-library/user-event) simulates real user interactions — dispatching the same sequence of events a browser would, rather than a single synthetic event.

Setup

Create an instance once per test, then drive it:

JSX
import { render, userEvent } from "@opentf/web-test";

const user = userEvent.setup();
Always await

Every userEvent action returns a Promise. Await it so the resulting reactive updates have flushed before you assert.

Common interactions

JSX
const { getByRole, getByLabelText } = render(SignupForm);
const user = userEvent.setup();

await user.click(getByRole("button", { name: "Sign up" }));
await user.type(getByLabelText("Email"), "ada@example.com");
await user.clear(getByLabelText("Email"));
await user.keyboard("{Enter}");
await user.tab();

Typing and forms

type fires keydown / input / keyup per character, so field-level oninput handlers and validation run just as in the browser:

JSX
test("validates on input", async () => {
  const { getByLabelText, findByText } = render(SignupForm);
  const user = userEvent.setup();

  await user.type(getByLabelText("Email"), "not-an-email");
  await user.tab(); // blur
  expect(await findByText("Invalid email")).toBeDefined();
});

Selection and checkboxes

JSX
await user.selectOptions(getByLabelText("Country"), "NL");
await user.click(getByRole("checkbox", { name: "Subscribe" }));
expect(getByRole("checkbox", { name: "Subscribe" }).checked).toBe(true);
Prefer userEvent over fireEvent

userEvent reproduces the full event sequence (focus, key events, input), which catches bugs a single dispatched click/input would miss.