@@ -83,26 +83,19 @@ We will go through some real-world case test code examples. Each code example co
8383
8484### Example 1
8585
86- Navigate to settings screen by "Go to Settings" button press.
86+ Navigate to settings screen by tab bar button press.
8787
8888<Tabs groupId =" example " queryString =" example " >
8989<TabItem value =" static " label =" Static " default >
9090
9191``` 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' ;
9594
9695const HomeScreen = () => {
97- const navigation = useNavigation ();
98-
9996 return (
10097 < View style= {{ flex: 1 , justifyContent: ' center' , alignItems: ' center' }}>
10198 < Text > Home screen < / Text >
102- < Button
103- onPress= {() => navigation .navigate (' Settings' )}
104- title= " Go to Settings"
105- / >
10699 < / View>
107100 );
108101};
@@ -115,10 +108,23 @@ const SettingsScreen = () => {
115108 );
116109};
117110
118- export const StackNavigator = createStackNavigator ({
111+ export const TabNavigator = createBottomTabNavigator ({
119112 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 ,
122128 },
123129});
124130```
@@ -127,17 +133,13 @@ export const StackNavigator = createStackNavigator({
127133<TabItem value =" dynamic " label =" Dynamic " >
128134
129135``` 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' ;
132138
133- const HomeScreen = ({ navigation } ) => {
139+ const HomeScreen = () => {
134140 return (
135141 < View style= {{ flex: 1 , justifyContent: ' center' , alignItems: ' center' }}>
136142 < Text > Home screen < / Text >
137- < Button
138- onPress= {() => navigation .navigate (' Settings' )}
139- title= " Go to Settings"
140- / >
141143 < / View>
142144 );
143145};
@@ -150,13 +152,26 @@ const SettingsScreen = () => {
150152 );
151153};
152154
153- export const StackNavigator = () => {
154- const Stack = createStackNavigator ();
155+ const Tab = createBottomTabNavigator ();
156+
157+ export const TabNavigator = () => {
155158 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 >
160175 );
161176};
162177```
@@ -172,14 +187,18 @@ import { expect, test } from '@jest/globals';
172187import { createStaticNavigation } from ' @react-navigation/native' ;
173188import { fireEvent , render , screen } from ' @testing-library/react-native' ;
174189
175- import { StackNavigator } from ' ./StackNavigator ' ;
190+ import { TabNavigator } from ' ./TabNavigator ' ;
176191
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 / > );
180195
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 ();
183202});
184203```
185204
@@ -191,59 +210,84 @@ import { expect, test } from '@jest/globals';
191210import { NavigationContainer } from ' @react-navigation/native' ;
192211import { fireEvent , render , screen } from ' @testing-library/react-native' ;
193212
194- import { StackNavigator } from ' ./StackNavigator ' ;
213+ import { TabNavigator } from ' ./TabNavigator ' ;
195214
196- test (' navigates to settings by "Go to Settings" button press' , () => {
215+ test (' navigates to settings by tab bar button press' , () => {
197216 render (
198217 < NavigationContainer>
199- < StackNavigator / >
218+ < TabNavigator / >
200219 < / NavigationContainer>
201220 );
202221
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 ();
205228});
206229```
207230
208231</TabItem >
209232</Tabs >
210233
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+ ```
212243
213244### Example 2
214245
215- Navigate to settings screen by tab bar button press .
246+ Show text on another screen after transition to it ends .
216247
217248<Tabs groupId =" example " queryString =" example " >
218249<TabItem value =" static " label =" Static " default >
219250
220251``` 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' ;
223256
224257const HomeScreen = () => {
258+ const navigation = useNavigation ();
259+
225260 return (
226261 < View style= {{ flex: 1 , justifyContent: ' center' , alignItems: ' center' }}>
227262 < Text > Home screen < / Text >
263+ < Button
264+ onPress= {() => navigation .navigate (' Surprise' )}
265+ title= " Click here!"
266+ / >
228267 < / View>
229268 );
230269};
231270
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+
233280 return (
234281 < View style= {{ flex: 1 , justifyContent: ' center' , alignItems: ' center' }}>
235- < Text > Settings screen < / Text >
282+ {textVisible ? < Text > Surprise ! < / Text > : ' ' }
236283 < / View>
237284 );
238285};
239286
240- export const TabNavigator = createBottomTabNavigator ({
287+ export const StackNavigator = createStackNavigator ({
241288 screens: {
242289 Home: HomeScreen,
243- Settings: SettingsScreen,
244- },
245- screenOptions: {
246- headerShown: false ,
290+ Surprise: SurpriseScreen,
247291 },
248292});
249293```
@@ -252,33 +296,43 @@ export const TabNavigator = createBottomTabNavigator({
252296<TabItem value =" dynamic " label =" Dynamic " >
253297
254298``` 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' ;
257302
258- const HomeScreen = () => {
303+ const HomeScreen = ({ navigation } ) => {
259304 return (
260305 < View style= {{ flex: 1 , justifyContent: ' center' , alignItems: ' center' }}>
261306 < Text > Home screen < / Text >
307+ < Button
308+ onPress= {() => navigation .navigate (' Surprise' )}
309+ title= " Click here!"
310+ / >
262311 < / View>
263312 );
264313};
265314
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+
267322 return (
268323 < View style= {{ flex: 1 , justifyContent: ' center' , alignItems: ' center' }}>
269- < Text > Settings screen < / Text >
324+ {textVisible ? < Text > Surprise ! < / Text > : ' ' }
270325 < / View>
271326 );
272327};
273328
274- const Tab = createBottomTabNavigator ();
275-
276- export const TabNavigator = () => {
329+ export const StackNavigator = () => {
330+ const Stack = createStackNavigator ();
277331 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 >
282336 );
283337};
284338```
@@ -294,21 +348,19 @@ import { expect, jest, test } from '@jest/globals';
294348import { createStaticNavigation } from ' @react-navigation/native' ;
295349import { act , fireEvent , render , screen } from ' @testing-library/react-native' ;
296350
297- import { TabNavigator } from ' ./TabNavigator ' ;
351+ import { StackNavigator } from ' ./StackNavigator ' ;
298352
299- test (' navigates to settings by tab bar button press ' , () => {
353+ test (' surprise text appears after transition to surprise screen is complete ' , () => {
300354 jest .useFakeTimers ();
301355
302- const TabNavigation = createStaticNavigation (TabNavigator );
303- render (< TabNavigation / > );
356+ const StackNavigation = createStaticNavigation (StackNavigator );
357+ render (< StackNavigation / > );
304358
305- const button = screen .getByRole ( ' button ' , { name : ' Settings, tab, 2 of 2 ' } );
359+ fireEvent . press ( screen .queryByText ( ' Click here! ' ) );
306360
307- const event = {};
308- fireEvent .press (button, event );
361+ expect (screen .queryByText (' Surprise!' )).not .toBeOnTheScreen ();
309362 act (() => jest .runAllTimers ());
310-
311- expect (screen .queryByText (' Settings screen' )).toBeOnTheScreen ();
363+ expect (screen .getByText (' Surprise!' )).toBeOnTheScreen ();
312364});
313365```
314366
@@ -320,48 +372,31 @@ import { expect, jest, test } from '@jest/globals';
320372import { NavigationContainer } from ' @react-navigation/native' ;
321373import { act , fireEvent , render , screen } from ' @testing-library/react-native' ;
322374
323- import { TabNavigator } from ' ./TabNavigator ' ;
375+ import { StackNavigator } from ' ./StackNavigator ' ;
324376
325- test (' navigates to settings by tab bar button press ' , () => {
377+ test (' surprise text appears after transition to surprise screen is complete ' , () => {
326378 jest .useFakeTimers ();
327379
328380 render (
329381 < NavigationContainer>
330- < TabNavigator / >
382+ < StackNavigator / >
331383 < / NavigationContainer>
332384 );
333385
334- const button = screen .getByRole ( ' button ' , { name : ' Settings, tab, 2 of 2 ' } );
386+ fireEvent . press ( screen .queryByText ( ' Click here! ' ) );
335387
336- const event = {};
337- fireEvent .press (button, event );
388+ expect (screen .queryByText (' Surprise!' )).not .toBeOnTheScreen ();
338389 act (() => jest .runAllTimers ());
339-
340- expect (screen .queryByText (' Settings screen' )).toBeOnTheScreen ();
390+ expect (screen .getByText (' Surprise!' )).toBeOnTheScreen ();
341391});
342392```
343393
344394</TabItem >
345395</Tabs >
346396
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.
357398
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 ` .
365400
366401``` js
367402// Enable fake timers
@@ -374,6 +409,8 @@ jest.useFakeTimers();
374409act (() => jest .runAllTimers ());
375410```
376411
412+ If we hadn't used fake timers in this example, the test would have failed.
413+
377414### Example 3
378415
379416Always displays settings screen after settings tab bar button press.
0 commit comments