0
# Jest Matchers
1
2
Extended Jest matchers specifically designed for React Native component testing and assertions. These matchers integrate with Jest's expect API to provide intuitive and readable assertions for React Native elements.
3
4
## Capabilities
5
6
### Visibility and Presence Matchers
7
8
Matchers for testing element visibility and presence in the component tree.
9
10
```typescript { .api }
11
/**
12
* Assert element is currently on the screen (in the rendered tree)
13
*/
14
expect(element).toBeOnTheScreen(): void;
15
expect(element).not.toBeOnTheScreen(): void;
16
17
/**
18
* Assert element is visible to users (considering accessibility and style)
19
*/
20
expect(element).toBeVisible(): void;
21
expect(element).not.toBeVisible(): void;
22
23
/**
24
* Assert element is empty (has no children)
25
*/
26
expect(element).toBeEmptyElement(): void;
27
expect(element).not.toBeEmptyElement(): void;
28
```
29
30
**Usage Examples:**
31
32
```typescript
33
import { render, screen } from "@testing-library/react-native";
34
35
test("visibility matchers", () => {
36
render(
37
<View>
38
<Text testID="visible-text">Visible text</Text>
39
<View testID="empty-container" />
40
<Text
41
testID="hidden-text"
42
style={{ opacity: 0 }}
43
>
44
Hidden text
45
</Text>
46
<View testID="accessibility-hidden" accessibilityElementsHidden>
47
<Text>Hidden from accessibility</Text>
48
</View>
49
</View>
50
);
51
52
const visibleText = screen.getByTestId("visible-text");
53
const emptyContainer = screen.getByTestId("empty-container");
54
const hiddenText = screen.getByTestId("hidden-text");
55
const a11yHidden = screen.getByTestId("accessibility-hidden");
56
57
// Element presence
58
expect(visibleText).toBeOnTheScreen();
59
expect(hiddenText).toBeOnTheScreen(); // Still in tree, just not visible
60
61
// Element visibility
62
expect(visibleText).toBeVisible();
63
expect(hiddenText).not.toBeVisible(); // Has opacity: 0
64
expect(a11yHidden).not.toBeVisible(); // Hidden from accessibility
65
66
// Empty elements
67
expect(emptyContainer).toBeEmptyElement();
68
expect(visibleText).not.toBeEmptyElement(); // Has text content
69
});
70
71
test("conditional rendering", () => {
72
const ConditionalComponent = ({ show }) => (
73
<View>
74
{show && <Text testID="conditional">Conditional text</Text>}
75
</View>
76
);
77
78
const { rerender } = render(<ConditionalComponent show={false} />);
79
80
// Element not present when show=false
81
expect(screen.queryByTestId("conditional")).not.toBeOnTheScreen();
82
83
rerender(<ConditionalComponent show={true} />);
84
85
// Element present when show=true
86
expect(screen.getByTestId("conditional")).toBeOnTheScreen();
87
expect(screen.getByTestId("conditional")).toBeVisible();
88
});
89
```
90
91
### State and Interaction Matchers
92
93
Matchers for testing interactive element states and accessibility properties.
94
95
```typescript { .api }
96
/**
97
* Assert element is disabled/enabled
98
*/
99
expect(element).toBeDisabled(): void;
100
expect(element).not.toBeDisabled(): void;
101
expect(element).toBeEnabled(): void;
102
expect(element).not.toBeEnabled(): void;
103
104
/**
105
* Assert element is busy (has accessibilityState.busy = true)
106
*/
107
expect(element).toBeBusy(): void;
108
expect(element).not.toBeBusy(): void;
109
110
/**
111
* Assert element is selected
112
*/
113
expect(element).toBeSelected(): void;
114
expect(element).not.toBeSelected(): void;
115
116
/**
117
* Assert element is expanded/collapsed
118
*/
119
expect(element).toBeExpanded(): void;
120
expect(element).not.toBeExpanded(): void;
121
expect(element).toBeCollapsed(): void;
122
expect(element).not.toBeCollapsed(): void;
123
```
124
125
**Usage Examples:**
126
127
```typescript
128
test("interactive state matchers", () => {
129
render(
130
<View>
131
<Pressable
132
testID="disabled-button"
133
disabled={true}
134
accessibilityState={{ disabled: true }}
135
>
136
<Text>Disabled Button</Text>
137
</Pressable>
138
139
<Pressable
140
testID="enabled-button"
141
disabled={false}
142
>
143
<Text>Enabled Button</Text>
144
</Pressable>
145
146
<View
147
testID="busy-indicator"
148
accessibilityState={{ busy: true }}
149
>
150
<Text>Loading...</Text>
151
</View>
152
153
<View
154
testID="selected-item"
155
accessibilityState={{ selected: true }}
156
>
157
<Text>Selected Item</Text>
158
</View>
159
160
<View
161
testID="expanded-section"
162
accessibilityState={{ expanded: true }}
163
>
164
<Text>Expanded Section</Text>
165
</View>
166
</View>
167
);
168
169
const disabledButton = screen.getByTestId("disabled-button");
170
const enabledButton = screen.getByTestId("enabled-button");
171
const busyIndicator = screen.getByTestId("busy-indicator");
172
const selectedItem = screen.getByTestId("selected-item");
173
const expandedSection = screen.getByTestId("expanded-section");
174
175
// Disabled/enabled state
176
expect(disabledButton).toBeDisabled();
177
expect(enabledButton).toBeEnabled();
178
expect(enabledButton).not.toBeDisabled();
179
180
// Busy state
181
expect(busyIndicator).toBeBusy();
182
expect(enabledButton).not.toBeBusy();
183
184
// Selected state
185
expect(selectedItem).toBeSelected();
186
expect(disabledButton).not.toBeSelected();
187
188
// Expanded/collapsed state
189
expect(expandedSection).toBeExpanded();
190
expect(expandedSection).not.toBeCollapsed();
191
});
192
193
test("form element states", () => {
194
render(
195
<View>
196
<TextInput
197
testID="disabled-input"
198
editable={false}
199
accessibilityState={{ disabled: true }}
200
/>
201
202
<Switch
203
testID="toggle-switch"
204
value={true}
205
accessibilityState={{ checked: true }}
206
/>
207
</View>
208
);
209
210
const disabledInput = screen.getByTestId("disabled-input");
211
const toggleSwitch = screen.getByTestId("toggle-switch");
212
213
expect(disabledInput).toBeDisabled();
214
expect(toggleSwitch).toBeChecked();
215
});
216
```
217
218
### Checkbox and Selection Matchers
219
220
Specialized matchers for checkbox-like components and selection states.
221
222
```typescript { .api }
223
/**
224
* Assert element is checked (for checkboxes, switches, radio buttons)
225
*/
226
expect(element).toBeChecked(): void;
227
expect(element).not.toBeChecked(): void;
228
229
/**
230
* Assert element is partially checked (for indeterminate checkboxes)
231
*/
232
expect(element).toBePartiallyChecked(): void;
233
expect(element).not.toBePartiallyChecked(): void;
234
```
235
236
**Usage Examples:**
237
238
```typescript
239
test("checkbox matchers", () => {
240
const CheckboxComponent = ({ checked, indeterminate }) => (
241
<Pressable
242
testID="checkbox"
243
accessibilityRole="checkbox"
244
accessibilityState={{
245
checked: indeterminate ? "mixed" : checked
246
}}
247
>
248
<Text>{indeterminate ? "[-]" : checked ? "[×]" : "[ ]"}</Text>
249
</Pressable>
250
);
251
252
// Unchecked checkbox
253
const { rerender } = render(
254
<CheckboxComponent checked={false} indeterminate={false} />
255
);
256
257
const checkbox = screen.getByTestId("checkbox");
258
expect(checkbox).not.toBeChecked();
259
expect(checkbox).not.toBePartiallyChecked();
260
261
// Checked checkbox
262
rerender(<CheckboxComponent checked={true} indeterminate={false} />);
263
expect(checkbox).toBeChecked();
264
expect(checkbox).not.toBePartiallyChecked();
265
266
// Indeterminate checkbox
267
rerender(<CheckboxComponent checked={false} indeterminate={true} />);
268
expect(checkbox).toBePartiallyChecked();
269
expect(checkbox).not.toBeChecked();
270
});
271
272
test("switch component", () => {
273
render(
274
<Switch
275
testID="notification-switch"
276
value={true}
277
accessibilityLabel="Enable notifications"
278
accessibilityState={{ checked: true }}
279
/>
280
);
281
282
const notificationSwitch = screen.getByTestId("notification-switch");
283
expect(notificationSwitch).toBeChecked();
284
});
285
```
286
287
### Content and Text Matchers
288
289
Matchers for testing text content and display values.
290
291
```typescript { .api }
292
/**
293
* Assert element has specific text content
294
* @param text - Expected text content (string or RegExp)
295
*/
296
expect(element).toHaveTextContent(text: string | RegExp): void;
297
expect(element).not.toHaveTextContent(text: string | RegExp): void;
298
299
/**
300
* Assert form element has specific display value
301
* @param value - Expected display value (string or RegExp)
302
*/
303
expect(element).toHaveDisplayValue(value: string | RegExp): void;
304
expect(element).not.toHaveDisplayValue(value: string | RegExp): void;
305
```
306
307
**Usage Examples:**
308
309
```typescript
310
test("text content matchers", () => {
311
render(
312
<View>
313
<Text testID="greeting">Hello, World!</Text>
314
<Text testID="user-name">John Doe</Text>
315
<Text testID="empty-text"></Text>
316
<View testID="container">
317
<Text>First line</Text>
318
<Text>Second line</Text>
319
</View>
320
</View>
321
);
322
323
const greeting = screen.getByTestId("greeting");
324
const userName = screen.getByTestId("user-name");
325
const emptyText = screen.getByTestId("empty-text");
326
const container = screen.getByTestId("container");
327
328
// Exact text content
329
expect(greeting).toHaveTextContent("Hello, World!");
330
expect(userName).toHaveTextContent("John Doe");
331
332
// RegExp matching
333
expect(greeting).toHaveTextContent(/hello.*world/i);
334
expect(userName).toHaveTextContent(/john/i);
335
336
// Empty content
337
expect(emptyText).toHaveTextContent("");
338
expect(emptyText).not.toHaveTextContent("anything");
339
340
// Container with multiple text elements
341
expect(container).toHaveTextContent("First line Second line");
342
expect(container).toHaveTextContent(/first.*second/i);
343
});
344
345
test("display value matchers", () => {
346
render(
347
<View>
348
<TextInput
349
testID="email-input"
350
value="user@example.com"
351
placeholder="Email"
352
/>
353
354
<TextInput
355
testID="empty-input"
356
value=""
357
placeholder="Empty input"
358
/>
359
360
<Text testID="display-text">Current value: 42</Text>
361
</View>
362
);
363
364
const emailInput = screen.getByTestId("email-input");
365
const emptyInput = screen.getByTestId("empty-input");
366
const displayText = screen.getByTestId("display-text");
367
368
// Input values
369
expect(emailInput).toHaveDisplayValue("user@example.com");
370
expect(emailInput).toHaveDisplayValue(/@example\.com$/);
371
expect(emptyInput).toHaveDisplayValue("");
372
373
// Text element values
374
expect(displayText).toHaveDisplayValue("Current value: 42");
375
expect(displayText).toHaveDisplayValue(/value: \d+/);
376
377
// Negative assertions
378
expect(emailInput).not.toHaveDisplayValue("wrong@email.com");
379
expect(emptyInput).not.toHaveDisplayValue("something");
380
});
381
```
382
383
### Accessibility Matchers
384
385
Matchers for testing accessibility properties and labels.
386
387
```typescript { .api }
388
/**
389
* Assert element has specific accessible name
390
* @param name - Expected accessible name (string or RegExp)
391
*/
392
expect(element).toHaveAccessibleName(name: string | RegExp): void;
393
expect(element).not.toHaveAccessibleName(name: string | RegExp): void;
394
395
/**
396
* Assert element has specific accessibility value
397
* @param value - Expected accessibility value object
398
*/
399
expect(element).toHaveAccessibilityValue(value: {
400
min?: number;
401
max?: number;
402
now?: number;
403
text?: string;
404
}): void;
405
expect(element).not.toHaveAccessibilityValue(value: object): void;
406
```
407
408
**Usage Examples:**
409
410
```typescript
411
test("accessibility matchers", () => {
412
render(
413
<View>
414
<Pressable
415
testID="submit-button"
416
accessibilityLabel="Submit form"
417
accessibilityHint="Tap to submit the form"
418
>
419
<Text>Submit</Text>
420
</Pressable>
421
422
<Slider
423
testID="volume-slider"
424
minimumValue={0}
425
maximumValue={100}
426
value={75}
427
accessibilityLabel="Volume control"
428
accessibilityValue={{
429
min: 0,
430
max: 100,
431
now: 75,
432
text: "75 percent"
433
}}
434
/>
435
436
<TextInput
437
testID="name-input"
438
accessibilityLabel="Full name"
439
placeholder="Enter your full name"
440
/>
441
</View>
442
);
443
444
const submitButton = screen.getByTestId("submit-button");
445
const volumeSlider = screen.getByTestId("volume-slider");
446
const nameInput = screen.getByTestId("name-input");
447
448
// Accessible names
449
expect(submitButton).toHaveAccessibleName("Submit form");
450
expect(submitButton).toHaveAccessibleName(/submit/i);
451
expect(volumeSlider).toHaveAccessibleName("Volume control");
452
expect(nameInput).toHaveAccessibleName("Full name");
453
454
// Accessibility values
455
expect(volumeSlider).toHaveAccessibilityValue({
456
min: 0,
457
max: 100,
458
now: 75,
459
text: "75 percent"
460
});
461
462
// Partial accessibility value matching
463
expect(volumeSlider).toHaveAccessibilityValue({ now: 75 });
464
expect(volumeSlider).toHaveAccessibilityValue({ text: "75 percent" });
465
466
// Negative assertions
467
expect(submitButton).not.toHaveAccessibleName("Cancel");
468
expect(volumeSlider).not.toHaveAccessibilityValue({ now: 50 });
469
});
470
471
test("accessibility value updates", () => {
472
const ProgressComponent = ({ progress }) => (
473
<View
474
testID="progress-bar"
475
accessibilityRole="progressbar"
476
accessibilityValue={{
477
min: 0,
478
max: 100,
479
now: progress,
480
text: `${progress}% complete`
481
}}
482
/>
483
);
484
485
const { rerender } = render(<ProgressComponent progress={25} />);
486
487
const progressBar = screen.getByTestId("progress-bar");
488
489
// Initial progress
490
expect(progressBar).toHaveAccessibilityValue({
491
now: 25,
492
text: "25% complete"
493
});
494
495
// Updated progress
496
rerender(<ProgressComponent progress={75} />);
497
expect(progressBar).toHaveAccessibilityValue({
498
now: 75,
499
text: "75% complete"
500
});
501
});
502
```
503
504
### Property and Style Matchers
505
506
Matchers for testing component props and styles.
507
508
```typescript { .api }
509
/**
510
* Assert element has specific prop with optional value
511
* @param prop - Prop name to check
512
* @param value - Optional expected prop value
513
*/
514
expect(element).toHaveProp(prop: string, value?: any): void;
515
expect(element).not.toHaveProp(prop: string, value?: any): void;
516
517
/**
518
* Assert element has specific styles
519
* @param styles - Expected style object
520
*/
521
expect(element).toHaveStyle(styles: object): void;
522
expect(element).not.toHaveStyle(styles: object): void;
523
```
524
525
**Usage Examples:**
526
527
```typescript
528
test("prop matchers", () => {
529
render(
530
<View>
531
<TextInput
532
testID="styled-input"
533
placeholder="Enter text"
534
multiline={true}
535
numberOfLines={4}
536
keyboardType="email-address"
537
autoCapitalize="none"
538
/>
539
540
<Pressable
541
testID="custom-button"
542
disabled={false}
543
onPress={() => {}}
544
customProp="custom-value"
545
>
546
<Text>Button</Text>
547
</Pressable>
548
</View>
549
);
550
551
const styledInput = screen.getByTestId("styled-input");
552
const customButton = screen.getByTestId("custom-button");
553
554
// Check prop existence
555
expect(styledInput).toHaveProp("placeholder");
556
expect(styledInput).toHaveProp("multiline");
557
expect(customButton).toHaveProp("onPress");
558
559
// Check prop values
560
expect(styledInput).toHaveProp("placeholder", "Enter text");
561
expect(styledInput).toHaveProp("multiline", true);
562
expect(styledInput).toHaveProp("numberOfLines", 4);
563
expect(styledInput).toHaveProp("keyboardType", "email-address");
564
expect(customButton).toHaveProp("disabled", false);
565
expect(customButton).toHaveProp("customProp", "custom-value");
566
567
// Negative assertions
568
expect(styledInput).not.toHaveProp("nonexistent");
569
expect(styledInput).not.toHaveProp("multiline", false);
570
expect(customButton).not.toHaveProp("disabled", true);
571
});
572
573
test("style matchers", () => {
574
render(
575
<View>
576
<View
577
testID="styled-view"
578
style={{
579
backgroundColor: "blue",
580
padding: 10,
581
margin: 5,
582
borderRadius: 8,
583
flexDirection: "row"
584
}}
585
/>
586
587
<Text
588
testID="styled-text"
589
style={[
590
{ fontSize: 16, color: "red" },
591
{ fontWeight: "bold" }
592
]}
593
>
594
Styled Text
595
</Text>
596
</View>
597
);
598
599
const styledView = screen.getByTestId("styled-view");
600
const styledText = screen.getByTestId("styled-text");
601
602
// Single style properties
603
expect(styledView).toHaveStyle({ backgroundColor: "blue" });
604
expect(styledView).toHaveStyle({ padding: 10 });
605
expect(styledText).toHaveStyle({ fontSize: 16 });
606
expect(styledText).toHaveStyle({ color: "red" });
607
608
// Multiple style properties
609
expect(styledView).toHaveStyle({
610
backgroundColor: "blue",
611
padding: 10,
612
margin: 5
613
});
614
615
expect(styledText).toHaveStyle({
616
fontSize: 16,
617
color: "red",
618
fontWeight: "bold"
619
});
620
621
// Negative assertions
622
expect(styledView).not.toHaveStyle({ backgroundColor: "red" });
623
expect(styledView).not.toHaveStyle({ padding: 20 });
624
expect(styledText).not.toHaveStyle({ fontSize: 20 });
625
});
626
```
627
628
### Element Relationship Matchers
629
630
Matchers for testing element containment and hierarchy.
631
632
```typescript { .api }
633
/**
634
* Assert element contains another element
635
* @param element - Element that should be contained
636
*/
637
expect(container).toContainElement(element: ReactTestInstance): void;
638
expect(container).not.toContainElement(element: ReactTestInstance): void;
639
```
640
641
**Usage Examples:**
642
643
```typescript
644
test("containment matchers", () => {
645
render(
646
<View testID="outer-container">
647
<View testID="inner-container">
648
<Text testID="nested-text">Nested content</Text>
649
<Pressable testID="nested-button">
650
<Text>Button</Text>
651
</Pressable>
652
</View>
653
<Text testID="sibling-text">Sibling content</Text>
654
</View>
655
);
656
657
const outerContainer = screen.getByTestId("outer-container");
658
const innerContainer = screen.getByTestId("inner-container");
659
const nestedText = screen.getByTestId("nested-text");
660
const nestedButton = screen.getByTestId("nested-button");
661
const siblingText = screen.getByTestId("sibling-text");
662
663
// Direct containment
664
expect(outerContainer).toContainElement(innerContainer);
665
expect(outerContainer).toContainElement(siblingText);
666
expect(innerContainer).toContainElement(nestedText);
667
expect(innerContainer).toContainElement(nestedButton);
668
669
// Nested containment (transitive)
670
expect(outerContainer).toContainElement(nestedText);
671
expect(outerContainer).toContainElement(nestedButton);
672
673
// Negative containment
674
expect(innerContainer).not.toContainElement(siblingText);
675
expect(nestedText).not.toContainElement(nestedButton);
676
expect(siblingText).not.toContainElement(nestedText);
677
});
678
679
test("dynamic containment", () => {
680
const DynamicContainer = ({ showNested }) => (
681
<View testID="dynamic-container">
682
<Text testID="always-present">Always here</Text>
683
{showNested && (
684
<View testID="conditional-nested">
685
<Text testID="nested-content">Conditional content</Text>
686
</View>
687
)}
688
</View>
689
);
690
691
const { rerender } = render(<DynamicContainer showNested={false} />);
692
693
const container = screen.getByTestId("dynamic-container");
694
const alwaysPresent = screen.getByTestId("always-present");
695
696
// Always present element
697
expect(container).toContainElement(alwaysPresent);
698
699
// Conditional element not present initially
700
expect(screen.queryByTestId("conditional-nested")).not.toBeOnTheScreen();
701
702
// Show nested content
703
rerender(<DynamicContainer showNested={true} />);
704
705
const conditionalNested = screen.getByTestId("conditional-nested");
706
const nestedContent = screen.getByTestId("nested-content");
707
708
// Conditional elements now present and contained
709
expect(container).toContainElement(conditionalNested);
710
expect(container).toContainElement(nestedContent);
711
expect(conditionalNested).toContainElement(nestedContent);
712
});
713
```
714
715
## Custom Matcher Configuration
716
717
While the library provides comprehensive built-in matchers, you can also extend functionality if needed.
718
719
```typescript { .api }
720
/**
721
* Jest matcher result type for custom matchers
722
*/
723
interface MatcherResult {
724
pass: boolean;
725
message: () => string;
726
}
727
728
/**
729
* Custom matcher function signature
730
*/
731
type CustomMatcher<T = ReactTestInstance> = (
732
received: T,
733
...args: any[]
734
) => MatcherResult;
735
```
736
737
**Usage Examples:**
738
739
```typescript
740
// Custom matcher example (typically in test setup file)
741
expect.extend({
742
toHaveCustomProperty(received, expectedValue) {
743
const pass = received.props.customProperty === expectedValue;
744
745
return {
746
pass,
747
message: () =>
748
pass
749
? `Expected element not to have customProperty: ${expectedValue}`
750
: `Expected element to have customProperty: ${expectedValue}, but got: ${received.props.customProperty}`
751
};
752
}
753
});
754
755
// Usage in tests
756
test("custom matcher", () => {
757
render(
758
<View testID="custom-element" customProperty="special-value">
759
<Text>Custom Element</Text>
760
</View>
761
);
762
763
const element = screen.getByTestId("custom-element");
764
expect(element).toHaveCustomProperty("special-value");
765
});
766
```