← Back to React Testing Library Mastery
Intermediate18 min read

User Interactions

Simulate realistic clicks, typing, and form submission with userEvent.

userEvent Setup

@testing-library/user-event v14+ requires userEvent.setup() per test for isolated pointer/keyboard state. Async API—await each interaction.

userEvent fires full event sequence closer to real browser than fireEvent alone.

import userEvent from "@testing-library/user-event";

test("submits form", async () => {
  const user = userEvent.setup();
  render(<LoginForm onSubmit={jest.fn()} />);
  await user.type(screen.getByLabelText(/email/i), "ada@example.com");
  await user.click(screen.getByRole("button", { name: /sign in/i }));
});

Click and Keyboard

user.click, dblClick, hover, tab, keyboard shortcuts. clear empties input before type. selectOptions for native select elements.

For keyboard-only flows test tab order and Enter activation on buttons.

await user.click(screen.getByRole("checkbox", { name: /remember/i }));
await user.keyboard("{Tab}{Enter}");

fireEvent When Appropriate

fireEvent dispatches single synthetic event—lower level. Use when userEvent does not support edge case or testing error boundary on specific event.

Prefer userEvent for integration confidence; fireEvent for isolated unit triggering one event.

  • userEvent.type handles focus and keydown/keyup sequence
  • upload file with user.upload or fireEvent on input
  • pointer events for custom sliders may need low-level sequence
import { fireEvent } from "@testing-library/react";

fireEvent.scroll(window, { target: { scrollY: 500 } });

Form Submission

Fill fields with user.type or user.selectOptions, submit with click submit button or user.keyboard("{Enter}") in field. Assert onSubmit called with expected payload or success UI.

Validation errors: assert error messages appear without submit when client validation blocks.

const handleSubmit = jest.fn();
render(<ContactForm onSubmit={handleSubmit} />);
await user.type(screen.getByLabelText(/message/i), "Hello");
await user.click(screen.getByRole("button", { name: /send/i }));
expect(handleSubmit).toHaveBeenCalledWith({ message: "Hello" });

Pointer and Drag

userEvent pointer API supports press, move, release for drag-drop libraries. Coordinate with library docs—some DnD needs HTML5 DnD events fireEvent supplies.

Avoid testing CSS hover-only UI as sole path—ensure keyboard accessible alternative exists and test that too.

  • Use { delay: null } in setup for faster typing in large forms
  • Disabled controls should fail user.click—assert disabled state first
  • Double-check async await on every userEvent call

Get In Touch


Ready to discuss your next project? Drop me a message.