CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-testing-library--react

Simple and complete React DOM testing utilities that encourage good testing practices

Pending
Overview
Eval results
Files

events.mddocs/

Firing Events

Simulate user interactions with automatic act() wrapping for React state updates.

API

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;
};

Common Patterns

Click Events

const button = screen.getByRole('button', { name: /submit/i });
fireEvent.click(button);

// With modifier keys
fireEvent.click(button, { ctrlKey: true, shiftKey: true });

Form Input

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' } });

Keyboard Events

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,
});

Focus/Blur

const input = screen.getByRole('textbox');

fireEvent.focus(input);
expect(document.activeElement).toBe(input);

fireEvent.blur(input);

Mouse Hover

const button = screen.getByRole('button');

fireEvent.mouseEnter(button);  // Shows hover state
fireEvent.mouseLeave(button);  // Hides hover state

Testing Patterns

Button Click

test('increments counter', () => {
  render(<Counter />);

  const button = screen.getByRole('button', { name: /increment/i });
  fireEvent.click(button);

  expect(screen.getByText('Count: 1')).toBeInTheDocument();
});

Form Submission

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'
  });
});

Keyboard Navigation

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
});

Input Validation

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();
});

Hover Interactions

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-Specific Event Behaviors

React Testing Library enhances certain events with React-specific behavior:

mouseEnter / mouseLeave

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);

pointerEnter / pointerLeave

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);

select Event

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);

focus / blur Events

React tracks native focusin/focusout events for focus/blur handlers:

// Fires focusIn then focus
fireEvent.focus(element);

// Fires focusOut then blur
fireEvent.blur(element);

Event Options

All event methods accept an optional options object with event-specific properties:

MouseEventInit

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,
});

KeyboardEventInit

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,
});

InputEventInit

interface InputEventInit {
  data?: string | null;
  inputType?: string;
}

FocusEventInit

interface FocusEventInit {
  relatedTarget?: EventTarget | null;
}

Common Keys

// 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 }

Important Notes

Automatic act() Wrapping

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();

Synthetic Events vs Native Events

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.

Event Bubbling

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"

User Event Alternative

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');

Testing Complex Interactions

Multi-Step Form

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();
});

Drag and Drop

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');
});

Alternative: user-event

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:

  • Simpler, faster tests
  • Testing specific event handlers
  • Legacy codebase

When to use user-event:

  • More realistic user behavior
  • Testing complex interactions
  • Accessibility testing

Install with Tessl CLI

npx tessl i tessl/npm-testing-library--react

docs

async.md

configuration.md

events.md

hooks.md

index.md

queries.md

rendering.md

tile.json