Simple and complete React DOM testing utilities that encourage good testing practices
—
Simulate user interactions with automatic act() wrapping for React state updates.
Main utility for firing DOM events. Automatically wrapped in act() for proper React state updates.
/**
* Fire a DOM event on an element
* @param element - Target element
* @param event - Event object to fire
* @returns true if event was not cancelled
*/
const fireEvent: {
(element: HTMLElement, event: Event): boolean;
// Mouse Events
click(element: HTMLElement, options?: MouseEventInit): void;
dblClick(element: HTMLElement, options?: MouseEventInit): void;
mouseDown(element: HTMLElement, options?: MouseEventInit): void;
mouseUp(element: HTMLElement, options?: MouseEventInit): void;
mouseMove(element: HTMLElement, options?: MouseEventInit): void;
mouseOver(element: HTMLElement, options?: MouseEventInit): void;
mouseOut(element: HTMLElement, options?: MouseEventInit): void;
mouseEnter(element: HTMLElement, options?: MouseEventInit): void;
mouseLeave(element: HTMLElement, options?: MouseEventInit): void;
contextMenu(element: HTMLElement, options?: MouseEventInit): void;
wheel(element: HTMLElement, options?: WheelEventInit): void;
// Pointer Events
pointerDown(element: HTMLElement, options?: PointerEventInit): void;
pointerUp(element: HTMLElement, options?: PointerEventInit): void;
pointerMove(element: HTMLElement, options?: PointerEventInit): void;
pointerOver(element: HTMLElement, options?: PointerEventInit): void;
pointerOut(element: HTMLElement, options?: PointerEventInit): void;
pointerEnter(element: HTMLElement, options?: PointerEventInit): void;
pointerLeave(element: HTMLElement, options?: PointerEventInit): void;
pointerCancel(element: HTMLElement, options?: PointerEventInit): void;
gotPointerCapture(element: HTMLElement, options?: PointerEventInit): void;
lostPointerCapture(element: HTMLElement, options?: PointerEventInit): void;
// Keyboard Events
keyDown(element: HTMLElement, options?: KeyboardEventInit): void;
keyUp(element: HTMLElement, options?: KeyboardEventInit): void;
keyPress(element: HTMLElement, options?: KeyboardEventInit): void;
// Focus Events
focus(element: HTMLElement, options?: FocusEventInit): void;
blur(element: HTMLElement, options?: FocusEventInit): void;
focusIn(element: HTMLElement, options?: FocusEventInit): void;
focusOut(element: HTMLElement, options?: FocusEventInit): void;
// Form Events
change(element: HTMLElement, options?: EventInit): void;
input(element: HTMLElement, options?: InputEventInit): void;
invalid(element: HTMLElement, options?: EventInit): void;
submit(element: HTMLElement, options?: EventInit): void;
reset(element: HTMLElement, options?: EventInit): void;
select(element: HTMLElement, options?: EventInit): void;
// Clipboard Events
copy(element: HTMLElement, options?: ClipboardEventInit): void;
cut(element: HTMLElement, options?: ClipboardEventInit): void;
paste(element: HTMLElement, options?: ClipboardEventInit): void;
// Composition Events
compositionStart(element: HTMLElement, options?: CompositionEventInit): void;
compositionUpdate(element: HTMLElement, options?: CompositionEventInit): void;
compositionEnd(element: HTMLElement, options?: CompositionEventInit): void;
// Drag Events
drag(element: HTMLElement, options?: DragEventInit): void;
dragEnd(element: HTMLElement, options?: DragEventInit): void;
dragEnter(element: HTMLElement, options?: DragEventInit): void;
dragExit(element: HTMLElement, options?: DragEventInit): void;
dragLeave(element: HTMLElement, options?: DragEventInit): void;
dragOver(element: HTMLElement, options?: DragEventInit): void;
dragStart(element: HTMLElement, options?: DragEventInit): void;
drop(element: HTMLElement, options?: DragEventInit): void;
// Touch Events
touchStart(element: HTMLElement, options?: TouchEventInit): void;
touchMove(element: HTMLElement, options?: TouchEventInit): void;
touchEnd(element: HTMLElement, options?: TouchEventInit): void;
touchCancel(element: HTMLElement, options?: TouchEventInit): void;
// Animation Events
animationStart(element: HTMLElement, options?: AnimationEventInit): void;
animationEnd(element: HTMLElement, options?: AnimationEventInit): void;
animationIteration(element: HTMLElement, options?: AnimationEventInit): void;
// Transition Events
transitionEnd(element: HTMLElement, options?: TransitionEventInit): void;
// Media Events
abort(element: HTMLElement, options?: EventInit): void;
canPlay(element: HTMLElement, options?: EventInit): void;
canPlayThrough(element: HTMLElement, options?: EventInit): void;
durationChange(element: HTMLElement, options?: EventInit): void;
emptied(element: HTMLElement, options?: EventInit): void;
encrypted(element: HTMLElement, options?: EventInit): void;
ended(element: HTMLElement, options?: EventInit): void;
error(element: HTMLElement, options?: EventInit): void;
load(element: HTMLElement, options?: EventInit): void;
loadedData(element: HTMLElement, options?: EventInit): void;
loadedMetadata(element: HTMLElement, options?: EventInit): void;
loadStart(element: HTMLElement, options?: EventInit): void;
pause(element: HTMLElement, options?: EventInit): void;
play(element: HTMLElement, options?: EventInit): void;
playing(element: HTMLElement, options?: EventInit): void;
progress(element: HTMLElement, options?: EventInit): void;
rateChange(element: HTMLElement, options?: EventInit): void;
seeked(element: HTMLElement, options?: EventInit): void;
seeking(element: HTMLElement, options?: EventInit): void;
stalled(element: HTMLElement, options?: EventInit): void;
suspend(element: HTMLElement, options?: EventInit): void;
timeUpdate(element: HTMLElement, options?: EventInit): void;
volumeChange(element: HTMLElement, options?: EventInit): void;
waiting(element: HTMLElement, options?: EventInit): void;
// Other Events
scroll(element: HTMLElement, options?: EventInit): void;
};const button = screen.getByRole('button', { name: /submit/i });
fireEvent.click(button);
// With modifier keys
fireEvent.click(button, { ctrlKey: true, shiftKey: true });const input = screen.getByLabelText(/email/i);
// Text input
fireEvent.change(input, { target: { value: 'user@example.com' } });
// Checkbox
const checkbox = screen.getByRole('checkbox');
fireEvent.click(checkbox);
// Select dropdown
const select = screen.getByLabelText(/country/i);
fireEvent.change(select, { target: { value: 'USA' } });const input = screen.getByRole('textbox');
// Single key
fireEvent.keyDown(input, { key: 'Enter', code: 'Enter' });
fireEvent.keyDown(input, { key: 'Escape', code: 'Escape' });
// With modifiers
fireEvent.keyDown(input, {
key: 's',
code: 'KeyS',
ctrlKey: true,
});const input = screen.getByRole('textbox');
fireEvent.focus(input);
expect(document.activeElement).toBe(input);
fireEvent.blur(input);const button = screen.getByRole('button');
fireEvent.mouseEnter(button); // Shows hover state
fireEvent.mouseLeave(button); // Hides hover statetest('increments counter', () => {
render(<Counter />);
const button = screen.getByRole('button', { name: /increment/i });
fireEvent.click(button);
expect(screen.getByText('Count: 1')).toBeInTheDocument();
});test('submits form', () => {
const handleSubmit = jest.fn();
render(<Form onSubmit={handleSubmit} />);
fireEvent.change(screen.getByLabelText(/email/i), {
target: { value: 'user@example.com' }
});
fireEvent.click(screen.getByRole('button', { name: /submit/i }));
expect(handleSubmit).toHaveBeenCalledWith({
email: 'user@example.com'
});
});test('navigates with keyboard', () => {
render(<Menu />);
const menu = screen.getByRole('menu');
fireEvent.keyDown(menu, { key: 'ArrowDown', code: 'ArrowDown' });
expect(screen.getByRole('menuitem', { name: 'Item 1' })).toHaveFocus();
fireEvent.keyDown(menu, { key: 'Enter', code: 'Enter' });
// Menu item activated
});test('validates email on blur', () => {
render(<EmailInput />);
const input = screen.getByLabelText(/email/i);
fireEvent.change(input, { target: { value: 'invalid' } });
fireEvent.blur(input);
expect(screen.getByText(/invalid email/i)).toBeInTheDocument();
});test('shows tooltip on hover', () => {
render(<Button tooltip="Click me" />);
const button = screen.getByRole('button');
fireEvent.mouseEnter(button);
expect(screen.getByRole('tooltip')).toHaveTextContent('Click me');
fireEvent.mouseLeave(button);
expect(screen.queryByRole('tooltip')).not.toBeInTheDocument();
});React Testing Library enhances certain events with React-specific behavior:
In React, mouseEnter and mouseLeave are tracked by native mouseOver and mouseOut events. fireEvent automatically fires both:
// Fires both mouseEnter and mouseOver
fireEvent.mouseEnter(element);
// Fires both mouseLeave and mouseOut
fireEvent.mouseLeave(element);Similar to mouse events, pointer enter/leave events fire both the synthetic and native events:
// Fires both pointerEnter and pointerOver
fireEvent.pointerEnter(element);
// Fires both pointerLeave and pointerOut
fireEvent.pointerLeave(element);React's select event requires the element to be focused and fires a keyUp event:
// Fires select, focuses element, then fires keyUp
fireEvent.select(element);React tracks native focusin/focusout events for focus/blur handlers:
// Fires focusIn then focus
fireEvent.focus(element);
// Fires focusOut then blur
fireEvent.blur(element);All event methods accept an optional options object with event-specific properties:
interface MouseEventInit {
altKey?: boolean;
button?: number; // 0=left, 1=middle, 2=right
buttons?: number;
clientX?: number;
clientY?: number;
ctrlKey?: boolean;
metaKey?: boolean;
relatedTarget?: EventTarget | null;
screenX?: number;
screenY?: number;
shiftKey?: boolean;
}Example:
fireEvent.click(element, {
ctrlKey: true,
shiftKey: true,
button: 0,
});interface KeyboardEventInit {
key?: string; // 'Enter', 'Escape', 'a', etc.
code?: string; // 'Enter', 'Escape', 'KeyA', etc.
altKey?: boolean;
ctrlKey?: boolean;
metaKey?: boolean;
shiftKey?: boolean;
repeat?: boolean;
}Example:
fireEvent.keyDown(element, {
key: 'Enter',
code: 'Enter',
ctrlKey: false,
});interface InputEventInit {
data?: string | null;
inputType?: string;
}interface FocusEventInit {
relatedTarget?: EventTarget | null;
}// Special keys
{ key: 'Enter', code: 'Enter' }
{ key: 'Escape', code: 'Escape' }
{ key: 'Tab', code: 'Tab' }
{ key: 'ArrowDown', code: 'ArrowDown' }
{ key: 'ArrowUp', code: 'ArrowUp' }
{ key: 'ArrowLeft', code: 'ArrowLeft' }
{ key: 'ArrowRight', code: 'ArrowRight' }
{ key: 'Backspace', code: 'Backspace' }
{ key: 'Delete', code: 'Delete' }
// Letter keys
{ key: 'a', code: 'KeyA' }
{ key: 'A', code: 'KeyA', shiftKey: true }All fireEvent calls are automatically wrapped in React's act() function, ensuring state updates are flushed before assertions:
// No need to manually wrap in act()
fireEvent.click(button);
expect(screen.getByText('Updated')).toBeInTheDocument();React uses a synthetic event system. fireEvent fires native DOM events which are then processed by React's event system, matching how real browser events work.
Events fired with fireEvent bubble through the DOM tree like real browser events:
render(
<div onClick={() => console.log('Div clicked')}>
<button onClick={() => console.log('Button clicked')}>Click</button>
</div>
);
const button = screen.getByRole('button');
fireEvent.click(button);
// Logs: "Button clicked"
// Then logs: "Div clicked"For more realistic user interactions, consider using @testing-library/user-event which simulates complete user interactions (e.g., typing includes keydown, keypress, input, keyup events). However, fireEvent is simpler and sufficient for most cases.
// fireEvent - simple, fast
fireEvent.change(input, { target: { value: 'hello' } });
// userEvent - more realistic (requires separate package)
import userEvent from '@testing-library/user-event';
await userEvent.type(input, 'hello');test('completes multi-step form', () => {
render(<WizardForm />);
// Step 1
fireEvent.change(screen.getByLabelText(/name/i), {
target: { value: 'John' }
});
fireEvent.click(screen.getByRole('button', { name: /next/i }));
// Step 2
fireEvent.change(screen.getByLabelText(/email/i), {
target: { value: 'john@example.com' }
});
fireEvent.click(screen.getByRole('button', { name: /submit/i }));
expect(screen.getByText(/success/i)).toBeInTheDocument();
});test('reorders items with drag and drop', () => {
render(<DragList />);
const item = screen.getByText('Item 1');
fireEvent.dragStart(item);
fireEvent.dragEnter(screen.getByText('Item 3'));
fireEvent.drop(screen.getByText('Item 3'));
fireEvent.dragEnd(item);
const items = screen.getAllByRole('listitem');
expect(items[2]).toHaveTextContent('Item 1');
});For more realistic interactions, consider @testing-library/user-event (separate package):
import userEvent from '@testing-library/user-event';
// More realistic typing (includes keyDown, keyPress, input, keyUp)
await userEvent.type(input, 'Hello');
// Realistic clicking (includes mouseDown, mouseUp, click)
await userEvent.click(button);When to use fireEvent:
When to use user-event:
Install with Tessl CLI
npx tessl i tessl/npm-testing-library--react