0
# Event Simulation
1
2
React-aware event firing with proper event bubbling and React synthetic event handling. React Testing Library enhances DOM Testing Library's `fireEvent` with React-specific behavior for certain events.
3
4
## Capabilities
5
6
### FireEvent Object
7
8
The main event simulation utility that provides methods for triggering various DOM events with React-specific enhancements.
9
10
```typescript { .api }
11
/**
12
* Fire a DOM event on an element
13
* @param element - Target element
14
* @param event - Event object to fire
15
* @returns Boolean indicating if event was cancelled
16
*/
17
const fireEvent: {
18
(element: Element, event: Event): boolean;
19
20
// Mouse Events
21
click(element: Element, eventProperties?: MouseEventInit): boolean;
22
dblClick(element: Element, eventProperties?: MouseEventInit): boolean;
23
mouseDown(element: Element, eventProperties?: MouseEventInit): boolean;
24
mouseUp(element: Element, eventProperties?: MouseEventInit): boolean;
25
mouseMove(element: Element, eventProperties?: MouseEventInit): boolean;
26
mouseEnter(element: Element, eventProperties?: MouseEventInit): boolean;
27
mouseLeave(element: Element, eventProperties?: MouseEventInit): boolean;
28
mouseOver(element: Element, eventProperties?: MouseEventInit): boolean;
29
mouseOut(element: Element, eventProperties?: MouseEventInit): boolean;
30
31
// Keyboard Events
32
keyDown(element: Element, eventProperties?: KeyboardEventInit): boolean;
33
keyUp(element: Element, eventProperties?: KeyboardEventInit): boolean;
34
keyPress(element: Element, eventProperties?: KeyboardEventInit): boolean;
35
36
// Form Events
37
change(element: Element, eventProperties?: {target: {value: any}}): boolean;
38
input(element: Element, eventProperties?: {target: {value: any}}): boolean;
39
submit(element: Element, eventProperties?: Event): boolean;
40
reset(element: Element, eventProperties?: Event): boolean;
41
42
// Focus Events
43
focus(element: Element, eventProperties?: FocusEventInit): boolean;
44
blur(element: Element, eventProperties?: FocusEventInit): boolean;
45
focusIn(element: Element, eventProperties?: FocusEventInit): boolean;
46
focusOut(element: Element, eventProperties?: FocusEventInit): boolean;
47
48
// Selection Events
49
select(element: Element, eventProperties?: Event): boolean;
50
51
// Touch Events
52
touchStart(element: Element, eventProperties?: TouchEventInit): boolean;
53
touchMove(element: Element, eventProperties?: TouchEventInit): boolean;
54
touchEnd(element: Element, eventProperties?: TouchEventInit): boolean;
55
56
// Pointer Events
57
pointerDown(element: Element, eventProperties?: PointerEventInit): boolean;
58
pointerUp(element: Element, eventProperties?: PointerEventInit): boolean;
59
pointerMove(element: Element, eventProperties?: PointerEventInit): boolean;
60
pointerEnter(element: Element, eventProperties?: PointerEventInit): boolean;
61
pointerLeave(element: Element, eventProperties?: PointerEventInit): boolean;
62
pointerOver(element: Element, eventProperties?: PointerEventInit): boolean;
63
pointerOut(element: Element, eventProperties?: PointerEventInit): boolean;
64
65
// Drag Events
66
drag(element: Element, eventProperties?: DragEventInit): boolean;
67
dragEnd(element: Element, eventProperties?: DragEventInit): boolean;
68
dragEnter(element: Element, eventProperties?: DragEventInit): boolean;
69
dragExit(element: Element, eventProperties?: DragEventInit): boolean;
70
dragLeave(element: Element, eventProperties?: DragEventInit): boolean;
71
dragOver(element: Element, eventProperties?: DragEventInit): boolean;
72
dragStart(element: Element, eventProperties?: DragEventInit): boolean;
73
drop(element: Element, eventProperties?: DragEventInit): boolean;
74
};
75
```
76
77
## React-Specific Enhancements
78
79
React Testing Library provides enhanced behavior for certain events to match React's event system.
80
81
### Enhanced Mouse Events
82
83
```typescript { .api }
84
/**
85
* Enhanced mouseEnter - also fires mouseOver for React event handling
86
*/
87
fireEvent.mouseEnter(element: Element, eventProperties?: MouseEventInit): boolean;
88
89
/**
90
* Enhanced mouseLeave - also fires mouseOut for React event handling
91
*/
92
fireEvent.mouseLeave(element: Element, eventProperties?: MouseEventInit): boolean;
93
```
94
95
**Usage Examples:**
96
97
```typescript
98
function HoverComponent() {
99
const [isHovered, setIsHovered] = useState(false);
100
101
return (
102
<div
103
onMouseEnter={() => setIsHovered(true)}
104
onMouseLeave={() => setIsHovered(false)}
105
data-testid="hover-target"
106
>
107
{isHovered ? 'Hovered!' : 'Not hovered'}
108
</div>
109
);
110
}
111
112
render(<HoverComponent />);
113
114
const element = screen.getByTestId('hover-target');
115
116
// Test mouse enter (fires both mouseEnter and mouseOver)
117
fireEvent.mouseEnter(element);
118
expect(screen.getByText('Hovered!')).toBeInTheDocument();
119
120
// Test mouse leave (fires both mouseLeave and mouseOut)
121
fireEvent.mouseLeave(element);
122
expect(screen.getByText('Not hovered')).toBeInTheDocument();
123
```
124
125
### Enhanced Pointer Events
126
127
```typescript { .api }
128
/**
129
* Enhanced pointerEnter - also fires pointerOver for React event handling
130
*/
131
fireEvent.pointerEnter(element: Element, eventProperties?: PointerEventInit): boolean;
132
133
/**
134
* Enhanced pointerLeave - also fires pointerOut for React event handling
135
*/
136
fireEvent.pointerLeave(element: Element, eventProperties?: PointerEventInit): boolean;
137
```
138
139
### Enhanced Focus Events
140
141
```typescript { .api }
142
/**
143
* Enhanced focus - also fires focusIn for React event handling
144
*/
145
fireEvent.focus(element: Element, eventProperties?: FocusEventInit): boolean;
146
147
/**
148
* Enhanced blur - also fires focusOut for React event handling
149
*/
150
fireEvent.blur(element: Element, eventProperties?: FocusEventInit): boolean;
151
```
152
153
**Usage Examples:**
154
155
```typescript
156
function FocusComponent() {
157
const [focused, setFocused] = useState(false);
158
159
return (
160
<input
161
onFocus={() => setFocused(true)}
162
onBlur={() => setFocused(false)}
163
placeholder={focused ? 'Focused!' : 'Not focused'}
164
data-testid="focus-input"
165
/>
166
);
167
}
168
169
render(<FocusComponent />);
170
171
const input = screen.getByTestId('focus-input');
172
173
// Test focus (fires both focus and focusIn)
174
fireEvent.focus(input);
175
expect(input).toHaveAttribute('placeholder', 'Focused!');
176
177
// Test blur (fires both blur and focusOut)
178
fireEvent.blur(input);
179
expect(input).toHaveAttribute('placeholder', 'Not focused');
180
```
181
182
### Enhanced Select Event
183
184
```typescript { .api }
185
/**
186
* Enhanced select - focuses element and fires keyUp for React event handling
187
*/
188
fireEvent.select(element: Element, eventProperties?: Event): boolean;
189
```
190
191
**Usage Examples:**
192
193
```typescript
194
function SelectableInput() {
195
const [selected, setSelected] = useState(false);
196
197
return (
198
<input
199
onSelect={() => setSelected(true)}
200
defaultValue="Selectable text"
201
data-testid="selectable-input"
202
/>
203
);
204
}
205
206
render(<SelectableInput />);
207
208
const input = screen.getByTestId('selectable-input');
209
210
// Enhanced select event focuses and triggers selection
211
fireEvent.select(input);
212
213
// Input should be focused after select
214
expect(input).toHaveFocus();
215
```
216
217
## Common Event Patterns
218
219
### Form Interactions
220
221
```typescript
222
function LoginForm() {
223
const [credentials, setCredentials] = useState({ username: '', password: '' });
224
const [submitted, setSubmitted] = useState(false);
225
226
const handleSubmit = (e) => {
227
e.preventDefault();
228
setSubmitted(true);
229
};
230
231
return (
232
<form onSubmit={handleSubmit}>
233
<input
234
placeholder="Username"
235
value={credentials.username}
236
onChange={(e) => setCredentials(prev => ({ ...prev, username: e.target.value }))}
237
data-testid="username"
238
/>
239
<input
240
type="password"
241
placeholder="Password"
242
value={credentials.password}
243
onChange={(e) => setCredentials(prev => ({ ...prev, password: e.target.value }))}
244
data-testid="password"
245
/>
246
<button type="submit">Login</button>
247
{submitted && <p>Form submitted!</p>}
248
</form>
249
);
250
}
251
252
render(<LoginForm />);
253
254
const usernameInput = screen.getByTestId('username');
255
const passwordInput = screen.getByTestId('password');
256
const submitButton = screen.getByText('Login');
257
258
// Type in username
259
fireEvent.change(usernameInput, { target: { value: 'testuser' } });
260
expect(usernameInput).toHaveValue('testuser');
261
262
// Type in password
263
fireEvent.change(passwordInput, { target: { value: 'password123' } });
264
expect(passwordInput).toHaveValue('password123');
265
266
// Submit form
267
fireEvent.click(submitButton);
268
expect(screen.getByText('Form submitted!')).toBeInTheDocument();
269
270
// Alternative: submit via form
271
fireEvent.submit(screen.getByRole('form'));
272
```
273
274
### Keyboard Interactions
275
276
```typescript
277
function KeyboardComponent() {
278
const [keys, setKeys] = useState([]);
279
280
const handleKeyDown = (e) => {
281
setKeys(prev => [...prev, e.key]);
282
};
283
284
return (
285
<div>
286
<input onKeyDown={handleKeyDown} data-testid="keyboard-input" />
287
<ul>
288
{keys.map((key, index) => (
289
<li key={index}>{key}</li>
290
))}
291
</ul>
292
</div>
293
);
294
}
295
296
render(<KeyboardComponent />);
297
298
const input = screen.getByTestId('keyboard-input');
299
300
// Type individual keys
301
fireEvent.keyDown(input, { key: 'H', code: 'KeyH' });
302
fireEvent.keyDown(input, { key: 'i', code: 'KeyI' });
303
fireEvent.keyDown(input, { key: 'Enter', code: 'Enter' });
304
305
// Check keys were registered
306
expect(screen.getByText('H')).toBeInTheDocument();
307
expect(screen.getByText('i')).toBeInTheDocument();
308
expect(screen.getByText('Enter')).toBeInTheDocument();
309
310
// Special keys
311
fireEvent.keyDown(input, { key: 'Escape', code: 'Escape' });
312
fireEvent.keyDown(input, { key: 'Tab', code: 'Tab', shiftKey: true });
313
```
314
315
### Mouse Interactions
316
317
```typescript
318
function ClickCounter() {
319
const [clicks, setClicks] = useState(0);
320
const [position, setPosition] = useState({ x: 0, y: 0 });
321
322
const handleClick = (e) => {
323
setClicks(prev => prev + 1);
324
setPosition({ x: e.clientX, y: e.clientY });
325
};
326
327
const handleDoubleClick = () => {
328
setClicks(0);
329
};
330
331
return (
332
<div
333
onClick={handleClick}
334
onDoubleClick={handleDoubleClick}
335
style={{ width: 200, height: 200, border: '1px solid black' }}
336
data-testid="click-area"
337
>
338
Clicks: {clicks}
339
<br />
340
Position: {position.x}, {position.y}
341
</div>
342
);
343
}
344
345
render(<ClickCounter />);
346
347
const clickArea = screen.getByTestId('click-area');
348
349
// Single click with position
350
fireEvent.click(clickArea, { clientX: 100, clientY: 50 });
351
expect(screen.getByText(/Clicks: 1/)).toBeInTheDocument();
352
expect(screen.getByText(/Position: 100, 50/)).toBeInTheDocument();
353
354
// Another click
355
fireEvent.click(clickArea, { clientX: 150, clientY: 75 });
356
expect(screen.getByText(/Clicks: 2/)).toBeInTheDocument();
357
358
// Double click to reset
359
fireEvent.dblClick(clickArea);
360
expect(screen.getByText(/Clicks: 0/)).toBeInTheDocument();
361
362
// Mouse button variations
363
fireEvent.click(clickArea, { button: 1 }); // Middle click
364
fireEvent.click(clickArea, { button: 2 }); // Right click
365
```
366
367
### Drag and Drop
368
369
```typescript
370
function DragDropComponent() {
371
const [dragData, setDragData] = useState('');
372
const [dropped, setDropped] = useState(false);
373
374
const handleDragStart = (e) => {
375
e.dataTransfer.setData('text/plain', 'dragged data');
376
};
377
378
const handleDrop = (e) => {
379
e.preventDefault();
380
const data = e.dataTransfer.getData('text/plain');
381
setDragData(data);
382
setDropped(true);
383
};
384
385
const handleDragOver = (e) => {
386
e.preventDefault();
387
};
388
389
return (
390
<div>
391
<div
392
draggable
393
onDragStart={handleDragStart}
394
data-testid="drag-source"
395
style={{ padding: 20, background: 'lightblue' }}
396
>
397
Drag me
398
</div>
399
<div
400
onDrop={handleDrop}
401
onDragOver={handleDragOver}
402
data-testid="drop-target"
403
style={{ padding: 20, background: 'lightgreen', marginTop: 10 }}
404
>
405
Drop here
406
{dropped && <p>Dropped: {dragData}</p>}
407
</div>
408
</div>
409
);
410
}
411
412
render(<DragDropComponent />);
413
414
const dragSource = screen.getByTestId('drag-source');
415
const dropTarget = screen.getByTestId('drop-target');
416
417
// Start drag
418
const dragStartEvent = new DragEvent('dragstart', {
419
bubbles: true,
420
cancelable: true,
421
dataTransfer: new DataTransfer()
422
});
423
dragStartEvent.dataTransfer.setData = jest.fn();
424
425
fireEvent(dragSource, dragStartEvent);
426
427
// Drag over target
428
fireEvent.dragOver(dropTarget);
429
430
// Drop
431
const dropEvent = new DragEvent('drop', {
432
bubbles: true,
433
cancelable: true,
434
dataTransfer: new DataTransfer()
435
});
436
dropEvent.dataTransfer.getData = jest.fn(() => 'dragged data');
437
438
fireEvent(dropTarget, dropEvent);
439
440
expect(screen.getByText('Dropped: dragged data')).toBeInTheDocument();
441
```
442
443
### Touch Events
444
445
```typescript
446
function TouchComponent() {
447
const [touches, setTouches] = useState([]);
448
449
const handleTouch = (e) => {
450
const touchList = Array.from(e.touches).map(touch => ({
451
x: touch.clientX,
452
y: touch.clientY
453
}));
454
setTouches(touchList);
455
};
456
457
return (
458
<div
459
onTouchStart={handleTouch}
460
onTouchMove={handleTouch}
461
onTouchEnd={() => setTouches([])}
462
data-testid="touch-area"
463
style={{ width: 200, height: 200, background: 'lightgray' }}
464
>
465
Touches: {touches.length}
466
{touches.map((touch, index) => (
467
<div key={index}>Touch {index}: {touch.x}, {touch.y}</div>
468
))}
469
</div>
470
);
471
}
472
473
render(<TouchComponent />);
474
475
const touchArea = screen.getByTestId('touch-area');
476
477
// Single touch
478
fireEvent.touchStart(touchArea, {
479
touches: [{ clientX: 100, clientY: 100 }]
480
});
481
expect(screen.getByText('Touches: 1')).toBeInTheDocument();
482
483
// Multi-touch
484
fireEvent.touchStart(touchArea, {
485
touches: [
486
{ clientX: 50, clientY: 50 },
487
{ clientX: 150, clientY: 150 }
488
]
489
});
490
expect(screen.getByText('Touches: 2')).toBeInTheDocument();
491
492
// End touches
493
fireEvent.touchEnd(touchArea);
494
expect(screen.getByText('Touches: 0')).toBeInTheDocument();
495
```
496
497
### Custom Events
498
499
```typescript
500
function CustomEventComponent() {
501
const [customData, setCustomData] = useState('');
502
503
useEffect(() => {
504
const handleCustomEvent = (e) => {
505
setCustomData(e.detail.message);
506
};
507
508
document.addEventListener('customEvent', handleCustomEvent);
509
return () => document.removeEventListener('customEvent', handleCustomEvent);
510
}, []);
511
512
return <div>Custom data: {customData}</div>;
513
}
514
515
render(<CustomEventComponent />);
516
517
// Fire custom event
518
const customEvent = new CustomEvent('customEvent', {
519
detail: { message: 'Hello from custom event!' }
520
});
521
522
fireEvent(document, customEvent);
523
524
expect(screen.getByText('Custom data: Hello from custom event!')).toBeInTheDocument();
525
```