@@ -83,26 +83,19 @@ We will go through some real-world case test code examples. Each code example co
83
83
84
84
### Example 1
85
85
86
- Navigate to settings screen by "Go to Settings" button press.
86
+ Navigate to settings screen by tab bar button press.
87
87
88
88
<Tabs groupId =" example " queryString =" example " >
89
89
<TabItem value =" static " label =" Static " default >
90
90
91
91
``` js
92
- import { useNavigation } from ' @react-navigation/native' ;
93
- import { createStackNavigator } from ' @react-navigation/stack' ;
94
- import { Button , Text , View } from ' react-native' ;
92
+ import { createBottomTabNavigator } from ' @react-navigation/bottom-tabs' ;
93
+ import { Text , View } from ' react-native' ;
95
94
96
95
const HomeScreen = () => {
97
- const navigation = useNavigation ();
98
-
99
96
return (
100
97
< View style= {{ flex: 1 , justifyContent: ' center' , alignItems: ' center' }}>
101
98
< Text > Home screen < / Text >
102
- < Button
103
- onPress= {() => navigation .navigate (' Settings' )}
104
- title= " Go to Settings"
105
- / >
106
99
< / View>
107
100
);
108
101
};
@@ -115,10 +108,23 @@ const SettingsScreen = () => {
115
108
);
116
109
};
117
110
118
- export const StackNavigator = createStackNavigator ({
111
+ export const TabNavigator = createBottomTabNavigator ({
119
112
screens: {
120
- Home: HomeScreen,
121
- Settings: SettingsScreen,
113
+ Home: {
114
+ screen : HomeScreen,
115
+ options: {
116
+ tabBarButtonTestID: ' homeTabBarButton' ,
117
+ },
118
+ },
119
+ Settings: {
120
+ screen : SettingsScreen,
121
+ options: {
122
+ tabBarButtonTestID: ' settingsTabBarButton' ,
123
+ },
124
+ },
125
+ },
126
+ screenOptions: {
127
+ headerShown: false ,
122
128
},
123
129
});
124
130
```
@@ -127,17 +133,13 @@ export const StackNavigator = createStackNavigator({
127
133
<TabItem value =" dynamic " label =" Dynamic " >
128
134
129
135
``` js
130
- import { createStackNavigator } from ' @react-navigation/stack ' ;
131
- import { Button , Text , View } from ' react-native' ;
136
+ import { createBottomTabNavigator } from ' @react-navigation/bottom-tabs ' ;
137
+ import { Text , View } from ' react-native' ;
132
138
133
- const HomeScreen = ({ navigation } ) => {
139
+ const HomeScreen = () => {
134
140
return (
135
141
< View style= {{ flex: 1 , justifyContent: ' center' , alignItems: ' center' }}>
136
142
< Text > Home screen < / Text >
137
- < Button
138
- onPress= {() => navigation .navigate (' Settings' )}
139
- title= " Go to Settings"
140
- / >
141
143
< / View>
142
144
);
143
145
};
@@ -150,13 +152,26 @@ const SettingsScreen = () => {
150
152
);
151
153
};
152
154
153
- export const StackNavigator = () => {
154
- const Stack = createStackNavigator ();
155
+ const Tab = createBottomTabNavigator ();
156
+
157
+ export const TabNavigator = () => {
155
158
return (
156
- < Stack .Navigator >
157
- < Stack .Screen name= " Home" component= {HomeScreen} / >
158
- < Stack .Screen name= " Settings" component= {SettingsScreen} / >
159
- < / Stack .Navigator >
159
+ < Tab .Navigator screenOptions= {{ headerShown: false }}>
160
+ < Tab .Screen
161
+ name= " Home"
162
+ component= {HomeScreen}
163
+ options= {{
164
+ tabBarButtonTestID: ' homeTabBarButton' ,
165
+ }}
166
+ / >
167
+ < Tab .Screen
168
+ name= " Settings"
169
+ component= {SettingsScreen}
170
+ options= {{
171
+ tabBarButtonTestID: ' settingsTabBarButton' ,
172
+ }}
173
+ / >
174
+ < / Tab .Navigator >
160
175
);
161
176
};
162
177
```
@@ -172,14 +187,18 @@ import { expect, test } from '@jest/globals';
172
187
import { createStaticNavigation } from ' @react-navigation/native' ;
173
188
import { fireEvent , render , screen } from ' @testing-library/react-native' ;
174
189
175
- import { StackNavigator } from ' ./StackNavigator ' ;
190
+ import { TabNavigator } from ' ./TabNavigator ' ;
176
191
177
- test (' navigates to settings by "Go to Settings" button press' , () => {
178
- const StackNavigation = createStaticNavigation (StackNavigator );
179
- render (< StackNavigation / > );
192
+ test (' navigates to settings by tab bar button press' , () => {
193
+ const TabNavigation = createStaticNavigation (TabNavigator );
194
+ render (< TabNavigation / > );
180
195
181
- fireEvent .press (screen .queryByText (' Go to Settings' ));
182
- expect (screen .queryByText (' Settings screen' )).toBeOnTheScreen ();
196
+ const button = screen .getByTestId (' settingsTabBarButton' );
197
+
198
+ const event = {};
199
+ fireEvent .press (button, event );
200
+
201
+ expect (screen .getByText (' Settings screen' )).toBeOnTheScreen ();
183
202
});
184
203
```
185
204
@@ -191,59 +210,84 @@ import { expect, test } from '@jest/globals';
191
210
import { NavigationContainer } from ' @react-navigation/native' ;
192
211
import { fireEvent , render , screen } from ' @testing-library/react-native' ;
193
212
194
- import { StackNavigator } from ' ./StackNavigator ' ;
213
+ import { TabNavigator } from ' ./TabNavigator ' ;
195
214
196
- test (' navigates to settings by "Go to Settings" button press' , () => {
215
+ test (' navigates to settings by tab bar button press' , () => {
197
216
render (
198
217
< NavigationContainer>
199
- < StackNavigator / >
218
+ < TabNavigator / >
200
219
< / NavigationContainer>
201
220
);
202
221
203
- fireEvent .press (screen .queryByText (' Go to Settings' ));
204
- expect (screen .queryByText (' Settings screen' )).toBeOnTheScreen ();
222
+ const button = screen .getByTestId (' settingsTabBarButton' );
223
+
224
+ const event = {};
225
+ fireEvent .press (button, event );
226
+
227
+ expect (screen .getByText (' Settings screen' )).toBeOnTheScreen ();
205
228
});
206
229
```
207
230
208
231
</TabItem >
209
232
</Tabs >
210
233
211
- We use ` FireEvent ` to press button and ` expect ` to check if rendered screen's content matches settings screen.
234
+ We get the settings tab bar button using a ` testID ` assigned to it, press it using ` fireEvent ` and check if rendered content is correct.
235
+
236
+ Tab bar buttons ` handlePress ` function expects to receive ` GestureResponderEvent ` . To avoid error you should pass ` event ` object as the second argument of ` fireEvent ` .
237
+
238
+ ``` js
239
+ // Pass event object to avoid error
240
+ const event = {};
241
+ fireEvent .press (button, event );
242
+ ```
212
243
213
244
### Example 2
214
245
215
- Navigate to settings screen by tab bar button press .
246
+ Show text on another screen after transition to it ends .
216
247
217
248
<Tabs groupId =" example " queryString =" example " >
218
249
<TabItem value =" static " label =" Static " default >
219
250
220
251
``` js
221
- import { createBottomTabNavigator } from ' @react-navigation/bottom-tabs' ;
222
- import { Text , View } from ' react-native' ;
252
+ import { useNavigation } from ' @react-navigation/native' ;
253
+ import { createStackNavigator } from ' @react-navigation/stack' ;
254
+ import { Button , Text , View } from ' react-native' ;
255
+ import { useEffect , useState } from ' react' ;
223
256
224
257
const HomeScreen = () => {
258
+ const navigation = useNavigation ();
259
+
225
260
return (
226
261
< View style= {{ flex: 1 , justifyContent: ' center' , alignItems: ' center' }}>
227
262
< Text > Home screen < / Text >
263
+ < Button
264
+ onPress= {() => navigation .navigate (' Surprise' )}
265
+ title= " Click here!"
266
+ / >
228
267
< / View>
229
268
);
230
269
};
231
270
232
- const SettingsScreen = () => {
271
+ const SurpriseScreen = () => {
272
+ const navigation = useNavigation ();
273
+
274
+ const [textVisible , setTextVisible ] = useState (false );
275
+
276
+ useEffect (() => {
277
+ navigation .addListener (' transitionEnd' , () => setTextVisible (true ));
278
+ }, [navigation]);
279
+
233
280
return (
234
281
< View style= {{ flex: 1 , justifyContent: ' center' , alignItems: ' center' }}>
235
- < Text > Settings screen < / Text >
282
+ {textVisible ? < Text > Surprise ! < / Text > : ' ' }
236
283
< / View>
237
284
);
238
285
};
239
286
240
- export const TabNavigator = createBottomTabNavigator ({
287
+ export const StackNavigator = createStackNavigator ({
241
288
screens: {
242
289
Home: HomeScreen,
243
- Settings: SettingsScreen,
244
- },
245
- screenOptions: {
246
- headerShown: false ,
290
+ Surprise: SurpriseScreen,
247
291
},
248
292
});
249
293
```
@@ -252,33 +296,43 @@ export const TabNavigator = createBottomTabNavigator({
252
296
<TabItem value =" dynamic " label =" Dynamic " >
253
297
254
298
``` js
255
- import { createBottomTabNavigator } from ' @react-navigation/bottom-tabs' ;
256
- import { Text , View } from ' react-native' ;
299
+ import { createStackNavigator } from ' @react-navigation/stack' ;
300
+ import { useEffect , useState } from ' react' ;
301
+ import { Button , Text , View } from ' react-native' ;
257
302
258
- const HomeScreen = () => {
303
+ const HomeScreen = ({ navigation } ) => {
259
304
return (
260
305
< View style= {{ flex: 1 , justifyContent: ' center' , alignItems: ' center' }}>
261
306
< Text > Home screen < / Text >
307
+ < Button
308
+ onPress= {() => navigation .navigate (' Surprise' )}
309
+ title= " Click here!"
310
+ / >
262
311
< / View>
263
312
);
264
313
};
265
314
266
- const SettingsScreen = () => {
315
+ const SurpriseScreen = ({ navigation }) => {
316
+ const [textVisible , setTextVisible ] = useState (false );
317
+
318
+ useEffect (() => {
319
+ navigation .addListener (' transitionEnd' , () => setTextVisible (true ));
320
+ }, [navigation]);
321
+
267
322
return (
268
323
< View style= {{ flex: 1 , justifyContent: ' center' , alignItems: ' center' }}>
269
- < Text > Settings screen < / Text >
324
+ {textVisible ? < Text > Surprise ! < / Text > : ' ' }
270
325
< / View>
271
326
);
272
327
};
273
328
274
- const Tab = createBottomTabNavigator ();
275
-
276
- export const TabNavigator = () => {
329
+ export const StackNavigator = () => {
330
+ const Stack = createStackNavigator ();
277
331
return (
278
- < Tab .Navigator screenOptions = {{ headerShown : false }} >
279
- < Tab .Screen name= " Home" component= {HomeScreen} / >
280
- < Tab .Screen name= " Settings " component= {SettingsScreen } / >
281
- < / Tab .Navigator >
332
+ < Stack .Navigator >
333
+ < Stack .Screen name= " Home" component= {HomeScreen} / >
334
+ < Stack .Screen name= " Surprise " component= {SurpriseScreen } / >
335
+ < / Stack .Navigator >
282
336
);
283
337
};
284
338
```
@@ -294,21 +348,19 @@ import { expect, jest, test } from '@jest/globals';
294
348
import { createStaticNavigation } from ' @react-navigation/native' ;
295
349
import { act , fireEvent , render , screen } from ' @testing-library/react-native' ;
296
350
297
- import { TabNavigator } from ' ./TabNavigator ' ;
351
+ import { StackNavigator } from ' ./StackNavigator ' ;
298
352
299
- test (' navigates to settings by tab bar button press ' , () => {
353
+ test (' surprise text appears after transition to surprise screen is complete ' , () => {
300
354
jest .useFakeTimers ();
301
355
302
- const TabNavigation = createStaticNavigation (TabNavigator );
303
- render (< TabNavigation / > );
356
+ const StackNavigation = createStaticNavigation (StackNavigator );
357
+ render (< StackNavigation / > );
304
358
305
- const button = screen .getByRole ( ' button ' , { name : ' Settings, tab, 2 of 2 ' } );
359
+ fireEvent . press ( screen .queryByText ( ' Click here! ' ) );
306
360
307
- const event = {};
308
- fireEvent .press (button, event );
361
+ expect (screen .queryByText (' Surprise!' )).not .toBeOnTheScreen ();
309
362
act (() => jest .runAllTimers ());
310
-
311
- expect (screen .queryByText (' Settings screen' )).toBeOnTheScreen ();
363
+ expect (screen .getByText (' Surprise!' )).toBeOnTheScreen ();
312
364
});
313
365
```
314
366
@@ -320,48 +372,31 @@ import { expect, jest, test } from '@jest/globals';
320
372
import { NavigationContainer } from ' @react-navigation/native' ;
321
373
import { act , fireEvent , render , screen } from ' @testing-library/react-native' ;
322
374
323
- import { TabNavigator } from ' ./TabNavigator ' ;
375
+ import { StackNavigator } from ' ./StackNavigator ' ;
324
376
325
- test (' navigates to settings by tab bar button press ' , () => {
377
+ test (' surprise text appears after transition to surprise screen is complete ' , () => {
326
378
jest .useFakeTimers ();
327
379
328
380
render (
329
381
< NavigationContainer>
330
- < TabNavigator / >
382
+ < StackNavigator / >
331
383
< / NavigationContainer>
332
384
);
333
385
334
- const button = screen .getByRole ( ' button ' , { name : ' Settings, tab, 2 of 2 ' } );
386
+ fireEvent . press ( screen .queryByText ( ' Click here! ' ) );
335
387
336
- const event = {};
337
- fireEvent .press (button, event );
388
+ expect (screen .queryByText (' Surprise!' )).not .toBeOnTheScreen ();
338
389
act (() => jest .runAllTimers ());
339
-
340
- expect (screen .queryByText (' Settings screen' )).toBeOnTheScreen ();
390
+ expect (screen .getByText (' Surprise!' )).toBeOnTheScreen ();
341
391
});
342
392
```
343
393
344
394
</TabItem >
345
395
</Tabs >
346
396
347
- We get settings tab bar button, press it and check if rendered content is correct.
348
-
349
- To find settings tab bar button you cannot use ` queryByText ` , because there is no text that can be queried. You can use ` getByRole ` instead and pass object with ` name ` as the second argument.
350
-
351
- ``` js
352
- // Pass object with settings tab name
353
- const button = screen .getByRole (' button' , { name: ' Settings, tab, 2 of 2' });
354
- ```
355
-
356
- Tab bar buttons ` handlePress ` function expects to receive ` GestureResponderEvent ` . To avoid error you should pass ` event ` object as the second argument of ` fireEvent ` .
397
+ We press the "Click here!" button using ` fireEvent ` and check that the text does not appear right away but only after the transition between screens ends.
357
398
358
- ``` js
359
- // Pass event object to avoid error
360
- const event = {};
361
- fireEvent .press (button, event );
362
- ```
363
-
364
- While writing tests containing navigation with animations you need to wait until animations finish before querying components. To do so, you have to use ` fake timers ` . [ ` Fake Timers ` ] ( https://jestjs.io/docs/timer-mocks ) replace real implementation of times function to use fake clock. They allow you to instantly skip animation time. To avoid getting state change error, wrap ` runAllTimers ` in ` act ` .
399
+ While writing tests containing navigation with animations (in this example we have a ` StackNavigator ` , which uses an animation for the transition based on the platform and OS version) you need to wait until animations finish before proceeding further. To do so, you have to use ` fake timers ` . [ ` Fake Timers ` ] ( https://jestjs.io/docs/timer-mocks ) replace real implementation of times function to use fake clock. They allow you to instantly skip animation time. To avoid getting state change error, wrap ` runAllTimers ` in ` act ` .
365
400
366
401
``` js
367
402
// Enable fake timers
@@ -374,6 +409,8 @@ jest.useFakeTimers();
374
409
act (() => jest .runAllTimers ());
375
410
```
376
411
412
+ If we hadn't used fake timers in this example, the test would have failed.
413
+
377
414
### Example 3
378
415
379
416
Always displays settings screen after settings tab bar button press.
0 commit comments