You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
-**Don't assert on props directly** - use semantic matchers instead
17
+
-**Use RNTL matchers** - prefer semantic matchers over prop assertions
19
18
-**Combine queries with matchers**: `expect(screen.getByText('Hello')).toBeOnTheScreen()`
20
19
-**No redundant null checks** - `getBy*` already throws if not found
21
20
21
+
## Jest Matchers Reference
22
+
23
+
| Matcher | Description |
24
+
|---------|-------------|
25
+
|`toBeOnTheScreen()`| Element is present in the element tree |
26
+
|`toBeVisible()`| Element is visible (checks style, `aria-hidden`, `accessibilityElementsHidden`, ancestors) |
27
+
|`toBeEmptyElement()`| Element has no children or text content |
28
+
|`toContainElement(element)`| Element contains another element |
29
+
|`toBeEnabled()`| Element is not disabled (checks `aria-disabled`, `accessibilityState`, ancestors) |
30
+
|`toBeDisabled()`| Element has `aria-disabled` or `accessibilityState={{ disabled: true }}` (checks ancestors) |
31
+
|`toBeBusy()`| Element has `aria-busy` or `accessibilityState={{ busy: true }}`|
32
+
|`toBeChecked()`| Element has `aria-checked` or `accessibilityState={{ checked: true }}`|
33
+
|`toBePartiallyChecked()`| Element has `aria-checked="mixed"` or `accessibilityState={{ checked: 'mixed' }}`|
34
+
|`toBeSelected()`| Element has `aria-selected` or `accessibilityState={{ selected: true }}`|
35
+
|`toBeExpanded()`| Element has `aria-expanded` or `accessibilityState={{ expanded: true }}`|
36
+
|`toBeCollapsed()`| Element has `aria-expanded={false}` or `accessibilityState={{ expanded: false }}`|
37
+
|`toHaveTextContent(text)`| Element has matching text content |
38
+
|`toHaveDisplayValue(value)`| TextInput has matching display value |
39
+
|`toHaveAccessibleName(name?)`| Element has matching `aria-label`, `accessibilityLabel`, or text content |
40
+
|`toHaveAccessibilityValue(value)`| Element has matching `aria-value*` or `accessibilityValue`|
41
+
|`toHaveStyle(style)`| Element has matching style |
42
+
|`toHaveProp(name, value?)`| Element has prop (use semantic matchers when possible) |
43
+
44
+
## User Interactions
45
+
46
+
**Prefer `userEvent`** over `fireEvent` for realistic user interaction simulation. `userEvent` triggers the complete event sequence that real users would produce.
47
+
48
+
### userEvent (Preferred, Async)
49
+
50
+
```tsx
51
+
const user =userEvent.setup();
52
+
```
53
+
54
+
| Method | Description |
55
+
|--------|-------------|
56
+
|`await user.press(element)`| Press an element (triggers `pressIn`, `pressOut`, `press`) |
57
+
|`await user.longPress(element, options?)`| Long press with optional `{ duration }`|
58
+
|`await user.type(element, text, options?)`| Type into TextInput (triggers `focus`, `keyPress`, `change`, `changeText` per char) |
59
+
|`await user.clear(element)`| Clear TextInput (select all + backspace) |
60
+
|`await user.paste(element, text)`| Paste text into TextInput |
61
+
|`await user.scrollTo(element, options)`| Scroll a ScrollView with `{ y }` or `{ x }` offset |
62
+
63
+
### fireEvent (Low-level, Sync in v13)
64
+
65
+
Use only when `userEvent` doesn't support the event or when you need direct control.
66
+
67
+
| Method | Description |
68
+
|--------|-------------|
69
+
|`fireEvent(element, eventName, ...data)`| Fire any event by name |
70
+
|`fireEvent.press(element)`| Fire `onPress` only (no `pressIn`/`pressOut`) |
71
+
|`fireEvent.changeText(element, text)`| Fire `onChangeText` directly |
72
+
|`fireEvent.scroll(element, eventData)`| Fire `onScroll` with event data |
73
+
22
74
## Sync vs Async APIs (v13)
23
75
24
76
-**`render()`, `fireEvent.*`, `renderHook()` are synchronous** - no `await` needed
-**Don't assert on props directly** - use semantic matchers instead
17
+
-**Use RNTL matchers** - prefer semantic matchers over prop assertions
19
18
-**Combine queries with matchers**: `expect(screen.getByText('Hello')).toBeOnTheScreen()`
20
19
-**No redundant null checks** - `getBy*` already throws if not found
21
20
21
+
## Jest Matchers Reference
22
+
23
+
| Matcher | Description |
24
+
|---------|-------------|
25
+
|`toBeOnTheScreen()`| Element is present in the element tree |
26
+
|`toBeVisible()`| Element is visible (checks style, `aria-hidden`, `accessibilityElementsHidden`, ancestors) |
27
+
|`toBeEmptyElement()`| Element has no children or text content |
28
+
|`toContainElement(element)`| Element contains another element |
29
+
|`toBeEnabled()`| Element is not disabled (checks `aria-disabled`, `accessibilityState`, ancestors) |
30
+
|`toBeDisabled()`| Element has `aria-disabled` or `accessibilityState={{ disabled: true }}` (checks ancestors) |
31
+
|`toBeBusy()`| Element has `aria-busy` or `accessibilityState={{ busy: true }}`|
32
+
|`toBeChecked()`| Element has `aria-checked` or `accessibilityState={{ checked: true }}`|
33
+
|`toBePartiallyChecked()`| Element has `aria-checked="mixed"` or `accessibilityState={{ checked: 'mixed' }}`|
34
+
|`toBeSelected()`| Element has `aria-selected` or `accessibilityState={{ selected: true }}`|
35
+
|`toBeExpanded()`| Element has `aria-expanded` or `accessibilityState={{ expanded: true }}`|
36
+
|`toBeCollapsed()`| Element has `aria-expanded={false}` or `accessibilityState={{ expanded: false }}`|
37
+
|`toHaveTextContent(text)`| Element has matching text content |
38
+
|`toHaveDisplayValue(value)`| TextInput has matching display value |
39
+
|`toHaveAccessibleName(name?)`| Element has matching `aria-label`, `accessibilityLabel`, or text content |
40
+
|`toHaveAccessibilityValue(value)`| Element has matching `aria-value*` or `accessibilityValue`|
41
+
|`toHaveStyle(style)`| Element has matching style |
42
+
|`toHaveProp(name, value?)`| Element has prop (use semantic matchers when possible) |
43
+
44
+
## User Interactions
45
+
46
+
**Prefer `userEvent`** over `fireEvent` for realistic user interaction simulation. `userEvent` triggers the complete event sequence that real users would produce.
47
+
48
+
### userEvent (Preferred)
49
+
50
+
```tsx
51
+
const user =userEvent.setup();
52
+
```
53
+
54
+
| Method | Description |
55
+
|--------|-------------|
56
+
|`user.press(element)`| Press an element (triggers `pressIn`, `pressOut`, `press`) |
57
+
|`user.longPress(element, options?)`| Long press with optional `{ duration }`|
58
+
|`user.type(element, text, options?)`| Type into TextInput (triggers `focus`, `keyPress`, `change`, `changeText` per char) |
59
+
|`user.clear(element)`| Clear TextInput (select all + backspace) |
60
+
|`user.paste(element, text)`| Paste text into TextInput |
61
+
|`user.scrollTo(element, options)`| Scroll a ScrollView with `{ y }` or `{ x }` offset |
62
+
63
+
### fireEvent (Low-level)
64
+
65
+
Use only when `userEvent` doesn't support the event or when you need direct control.
66
+
67
+
| Method | Description |
68
+
|--------|-------------|
69
+
|`fireEvent(element, eventName, ...data)`| Fire any event by name |
70
+
|`fireEvent.press(element)`| Fire `onPress` only (no `pressIn`/`pressOut`) |
71
+
|`fireEvent.changeText(element, text)`| Fire `onChangeText` directly |
72
+
|`fireEvent.scroll(element, eventData)`| Fire `onScroll` with event data |
0 commit comments