1
+ /**
2
+ * ========================================
3
+ * React Native Testing Library Tutorial
4
+ * Chapter 4: Testing Events
5
+ * ========================================
6
+ *
7
+ * This chapter covers how to simulate and test user interactions in React Native
8
+ * applications using React Native Testing Library's userEvent API.
9
+ *
10
+ * Key concepts covered:
11
+ * - Setting up userEvent for realistic user interactions
12
+ * - Testing button press events
13
+ * - Testing text input events
14
+ * - Testing form submissions
15
+ * - Testing state changes triggered by events
16
+ * - Best practices for event testing
17
+ *
18
+ * Note: This example uses React Strict DOM (react-strict-dom) which provides
19
+ * HTML-like components that work across React Native and web platforms.
20
+ */
21
+
1
22
import * as React from 'react' ;
2
23
import { html } from 'react-strict-dom' ;
3
24
import { render , screen , userEvent } from '@testing-library/react-native' ;
4
25
26
+ /**
27
+ * ========================================
28
+ * Example 1: Basic Button Press Events
29
+ * ========================================
30
+ *
31
+ * This example demonstrates how to test button press events that trigger
32
+ * state changes in a component.
33
+ */
34
+
5
35
function Counter ( ) {
6
36
const [ count , setCount ] = React . useState ( 0 ) ;
7
37
@@ -15,19 +45,53 @@ function Counter() {
15
45
) ;
16
46
}
17
47
48
+ /**
49
+ * Testing button press events:
50
+ *
51
+ * 1. Setup userEvent - creates a user session for realistic interactions
52
+ * 2. Find the button element using queries
53
+ * 3. Simulate the press event using user.press()
54
+ * 4. Assert the expected state change occurred
55
+ *
56
+ * Key points:
57
+ * - userEvent.setup() creates a user session for the test
58
+ * - user.press() simulates a realistic button press (including focus, press, release)
59
+ * - Always use await when calling userEvent methods (they return promises)
60
+ * - Test both the initial state and the state after the event
61
+ */
18
62
test ( 'Counter should increment the count when the button is pressed' , async ( ) => {
63
+ // Setup userEvent - this creates a user session for the test
19
64
const user = userEvent . setup ( ) ;
20
65
66
+ // Render the component
21
67
render ( < Counter /> ) ;
68
+
69
+ // Verify initial state
22
70
expect ( screen . getByText ( '0' ) ) . toBeOnTheScreen ( ) ;
23
71
72
+ // Find the button element using role and accessible name
24
73
const button = screen . getByRole ( 'button' , { name : 'Increment' } ) ;
25
74
expect ( button ) . toBeOnTheScreen ( ) ;
26
75
76
+ // Simulate a button press event
27
77
await user . press ( button ) ;
78
+
79
+ // Verify the state change occurred
28
80
expect ( screen . getByText ( '1' ) ) . toBeOnTheScreen ( ) ;
29
81
} ) ;
30
82
83
+ /**
84
+ * ========================================
85
+ * Example 2: Text Input and Form Events
86
+ * ========================================
87
+ *
88
+ * This example demonstrates testing more complex user interactions including:
89
+ * - Text input events
90
+ * - Form submission events
91
+ * - Conditional rendering based on events
92
+ * - Error handling scenarios
93
+ */
94
+
31
95
function LoginForm ( ) {
32
96
const [ email , setEmail ] = React . useState ( '' ) ;
33
97
const [ password , setPassword ] = React . useState ( '' ) ;
@@ -69,24 +133,90 @@ function LoginForm() {
69
133
) ;
70
134
}
71
135
136
+ /**
137
+ * Testing text input events and successful form submission:
138
+ *
139
+ * This test demonstrates:
140
+ * - Using user.type() to simulate realistic text input
141
+ * - Finding input elements by placeholder text
142
+ * - Testing form submission with valid data
143
+ * - Verifying conditional rendering after successful submission
144
+ *
145
+ * Key points:
146
+ * - user.type() simulates realistic typing (including focus, keystrokes, blur)
147
+ * - Always await user.type() calls
148
+ * - Use placeholder text, labels, or roles to find input elements
149
+ * - Test the complete user flow from input to submission to result
150
+ */
72
151
test ( 'should login with valid credentials' , async ( ) => {
73
152
const user = userEvent . setup ( ) ;
74
153
render ( < LoginForm /> ) ;
75
154
155
+ // Simulate typing in the email field
76
156
await user . type ( screen . getByPlaceholderText ( 'Email' ) , '[email protected] ' ) ;
157
+
158
+ // Simulate typing in the password field
77
159
await user . type ( screen . getByPlaceholderText ( 'Password' ) , 'password' ) ;
160
+
161
+ // Simulate clicking the login button
78
162
await user . press ( screen . getByRole ( 'button' , { name : 'Login' } ) ) ;
79
163
164
+ // Verify successful login redirects to success page
80
165
expect ( screen . getByRole ( 'heading' , { name : 'Login successful' } ) ) . toBeOnTheScreen ( ) ;
81
166
} ) ;
82
167
168
+ /**
169
+ * Testing error scenarios:
170
+ *
171
+ * This test demonstrates:
172
+ * - Testing error handling with invalid inputs
173
+ * - Verifying error messages are displayed correctly
174
+ * - Using role="alert" for error messages (accessibility best practice)
175
+ *
176
+ * Key points:
177
+ * - Always test both success and error scenarios
178
+ * - Use role="alert" for error messages to ensure accessibility
179
+ * - Test that error messages have appropriate accessible names
180
+ * - Verify error states don't accidentally trigger success states
181
+ */
83
182
test ( 'should show error message with invalid credentials' , async ( ) => {
84
183
const user = userEvent . setup ( ) ;
85
184
render ( < LoginForm /> ) ;
86
185
186
+ // Enter valid email but invalid password
87
187
await user . type ( screen . getByPlaceholderText ( 'Email' ) , '[email protected] ' ) ;
88
188
await user . type ( screen . getByPlaceholderText ( 'Password' ) , 'wrong-password' ) ;
189
+
190
+ // Attempt to login
89
191
await user . press ( screen . getByRole ( 'button' , { name : 'Login' } ) ) ;
90
192
193
+ // Verify error message is displayed
91
194
expect ( screen . getByRole ( 'alert' , { name : 'Invalid credentials' } ) ) . toBeOnTheScreen ( ) ;
92
195
} ) ;
196
+
197
+ /**
198
+ * ========================================
199
+ * Best Practices for Event Testing
200
+ * ========================================
201
+ *
202
+ * 1. Always use userEvent.setup() to create a user session
203
+ * 2. Always await userEvent method calls (they return promises)
204
+ * 3. Use realistic user interactions (user.press, user.type) over fireEvent
205
+ * 4. Test both success and error scenarios
206
+ * 5. Find elements using accessible queries (role, label, placeholder)
207
+ * 6. Test the complete user flow, not just individual events
208
+ * 7. Verify state changes and side effects after events
209
+ * 8. Use role="alert" for error messages and test them appropriately
210
+ *
211
+ * ========================================
212
+ * Common userEvent Methods
213
+ * ========================================
214
+ *
215
+ * - user.press(element) - Simulates pressing a button or touchable element
216
+ * - user.type(element, text) - Simulates typing text into an input
217
+ * - user.clear(element) - Clears text from an input
218
+ * - user.selectText(element, options) - Selects text in an input
219
+ * - user.scroll(element, options) - Simulates scrolling gestures
220
+ *
221
+ * For more advanced event testing, see the userEvent documentation.
222
+ */
0 commit comments