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.
Run the tests
Use the test configuration. userEvent runs in esdev's native DOM (esdev 0.17 or newer) and in a real browser:
esdev test --config=esdev.test.json --dom esdev test --config=esdev.test.json --browser
Choose --browser when an interaction depends on layout, CSS or real browser event timing; see the official DOM realm guide for its scope. Install a browser and matching driver following the official browser testing guide.
Setup
Create an instance once per test, then drive it:
import { render, userEvent } from "@opentf/afw-test"; const user = userEvent.setup();
Every userEvent action returns a Promise. Await it so the resulting reactive updates have flushed before you assert.
Common interactions
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:
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
await user.selectOptions(getByLabelText("Country"), "NL"); await user.click(getByRole("checkbox", { name: "Subscribe" })); expect(getByRole("checkbox", { name: "Subscribe" }).checked).toBe(true);
userEvent reproduces the full event sequence (focus, key events, input), which catches bugs a single dispatched click/input would miss.