How to test bottom tab bar using react native test library - react-native

I am trying to do testing for my react bottom tab bar component while testing it I am getting below error.
I followed all the solutions available in this Link no luck for me.
https://github.com/react-navigation/react-navigation/issues/8669
/Users/apple/Documents/MM/myproject/node_modules/#react-navigation/elements/lib/commonjs/assets/back-icon.png:1
({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,global,jest){�PNG
My testing component
Tabbar.test.js
import React from "react";
import { render, fireEvent } from "#testing-library/react-native";
import Tabbar from "../Tabbar";
it("Tab tests", () => {
const addItemButton = render(<Tabbar />).toJSON;
}
My Tab bar component
Tab.js file
import React from "react";
import { createBottomTabNavigator } from "#react-navigation/bottom-tabs";
import { Image } from "react-native";
const Tab = createBottomTabNavigator();
const Tabbar = ({ tabData }) => {
return (
<Tab.Navigator
screenOptions={({ route }) => ({
headerShown: false,
tabBarActiveTintColor: "00000",
tabBarInactiveTintColor: "FFFF",
})}
>
{tabsInfo.map((element) => {
return (
<Tab.Screen
key={element.idx}
name={element.tabName}
component={element.component}
/>
);
})}
</Tab.Navigator>
);
};
export default Tabbar;
This is my jest config code
jest.config.js
module.exports = {
preset: "react-native",
moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"],
setupFilesAfterEnv: ["#testing-library/jest-native/extend-expect"],
transformIgnorePatterns: [
"node_modules/(?!(#react-native|react-native|react-native-vector-icons)/)",
],
};

Update transformIgnorePatterns in the jest config file.
transformIgnorePatterns: [
"node_modules/(?!(jest-)?react-native|react-clone-referenced-element|#react-native-community|rollbar-react-native|#fortawesome|#react-native|#react-navigation)",
],
Reference link https://github.com/react-navigation/react-navigation/issues/8669

Related

react navigation deep linking not working when use Tabs Stack

Version:
"dependencies": {
"react-native": "0.63.4",
"#react-navigation/bottom-tabs": "^5.11.2",
"#react-navigation/native": "^5.8.10",
"#react-navigation/stack": "^5.12.8",
}
Test website link test://info_register?token=1111 successfully, I can see route.params includes token
but when I get into my Tabs screen, and try to to use test://setting_register?token=1111, App just open it doesn't navigate to SettingScreen and route.params is undefined
I take reference from official document https://reactnavigation.org/docs/5.x/configuring-links
What is wrong with my deep linking for Tabs ?
Here is my code:
index.js
import * as React from 'react';
import {NavigationContainer} from '#react-navigation/native';
import LoginStack from './LoginStack';
import Linking from './Linking';
const AppContainer = () => {
return (
<NavigationContainer linking={Linking}>
<LoginStack />
</NavigationContainer>
);
};
export default AppContainer;
Linking.js
const config = {
screens: {
// set config for App init screen
PersonalInfoScreen: {
path: 'info_register/',
parse: {
token: (token) => `${token}`,
},
},
// set config for Tabs screen
Setting: {
screens: {
SettingScreen: 'setting_register/:token',
},
},
},
},
};
const Linking = {
prefixes: ['test://'],
config,
};
export default Linking;
LoginStack.js
import * as React from 'react';
import {useSelector} from 'react-redux';
import {createStackNavigator} from '#react-navigation/stack';
import LoginScreen from '../screens/Login/LoginScreen';
import PersonalInfoScreen from '../screens/Login/PersonalInfoScreen';
import TabStack from './TabStack';
const Stack = createStackNavigator();
const LoginStack = () => {
const {uid, userToken} = useSelector((state) => state.LoginRedux);
const showLoginFlow = uid === '' || userToken === '' ? true : false;
return (
<Stack.Navigator
initialRouteName={'LoginScreen'}
screenOptions={{headerShown: false, gestureEnabled: false}}>
{showLoginFlow ? (
<>
<Stack.Screen name="LoginScreen" component={LoginScreen} />
<Stack.Screen
name="PersonalInfoScreen"
component={PersonalInfoScreen}
/>
</>
) : (
<>
<Stack.Screen name="TabStack" component={TabStack} />
</>
)}}
</Stack.Navigator>
);
};
export default LoginStack;
TabStack.js
import {createBottomTabNavigator} from '#react-navigation/bottom-tabs';
const Tab = createBottomTabNavigator();
const TabStack = () => {
return (
<Tab.Navigator
screenOptions={...mySetting}
tabBarOptions={...myStyle},
}}>
<Tab.Screen name={'Free'} component={FreeStack} />
<Tab.Screen name={'Video'} component={VideoStack} />
<Tab.Screen name={'News'} component={NewsStack} />
<Tab.Screen name={'Consultation'} component={ConsulationStack} />
<Tab.Screen name={'Setting'} component={SettingStack} />
</Tab.Navigator>
);
};
export default TabStack;
If review nested navigation https://reactnavigation.org/docs/5.x/configuring-links/#handling-nested-navigators docs.
You have this navigation tree for SettingScreen:
TabStack -> Setting -> SettingStack -> SettingScreen.
Routes configuration should match this tree also as below:
const config = {
screens: {
// set config for App init screen
PersonalInfoScreen: {
path: "info_register/",
parse: {
token: (token) => `${token}`,
},
},
// set config for Tabs screen
TabStack: {
screens: {
Setting: {
screens: {
SettingScreen: "setting_register/:token",
},
},
},
},
},
};
Just a note, it seems like whenever you are enabling debug-chrome configuration deep linking with react-navigation is not working, I disabled it and it worked!

Not getting route.params when using deep linking

I'm trying to implement Deep Linking on my APP, I'm following expo-cli react-native-navigation documentation about this subject. After basic configuration I can't get params from route.params from any of the links I set.
This is an example of my code
linking.js
import * as Linking from "expo-linking";
const prefix = Linking.createURL("/");
const config = {
screens: {
userStartSession: {
path: "home/:itemInfo",
parse: { itemInfo: (itemInfo) => `${itemInfo}` },
},
},
};
const linking = {
prefixes: [prefix],
config,
};
export default linking;
Docs here: https://reactnavigation.org/docs/deep-linking/
And here: https://reactnavigation.org/docs/configuring-links/
Then import linking into Navigation.js
import React from "react";
import linking from "../utils/linking/linking";
export default function Navigation() {
return (
<NavigationContainer linking={linking} fallback={<Text>Cargando...</Text>}>
<Stack.Navigator>
<Stack.Screen
name="userStartSession"
options={{ headerShown: false, headerLeft: null }}
>
{(props) => (
<UserSessionStack
{...props}
somePropsHere={somePropsHere}
/>
)}
</Stack.Screen>
</Stack.Navigator>
</NavigationContainer>
);
}
UserSessionStack.js
import React, { useState, useEffect } from "react";
import { createStackNavigator } from "#react-navigation/stack";
import { useRoute } from "#react-navigation/native";
const Stack = createStackNavigator();
export default function UserSessionStack(props) {
const { somePropsHere } = props;
const route = useRoute();
console.log(route.params);
return (
<Stack.Navigator
initialRouteName="slide-home"
>
<Stack.Screen
name="slide-home"
component={SlideHome}
options={{ headerShown: false }}
/>
</Stack.Navigator>
);
}
Then following react-navigation-native I run this comand to test the link
npx uri-scheme open exp://192.168.1.101:19000/--/home/this_is_a_test --ios
Docs here:
https://reactnavigation.org/docs/deep-linking/#test-deep-linking-on-ios
As you may see I'm passing "this_is_a_test" as a param to home link, but if I do console.log(route) in userStartSession component, I always get undefined in params
This is the response
Object {
"key": "userStartSession-CLSUaGx7QkCe8qlrvuvTf",
"name": "userStartSession",
"params": undefined,
"state": Object {
"index": 0,
"key": "stack-cyZDx2Q4gdGaZ1dMr2V1Q",
"routeNames": Array [
"slide-home",
"walkthrough-slides",
"sign-up-invitation",
"login-form",
"recover-account-password",
"sign-up-form",
"sign-up-payment-form",
"sign-up-payment-success",
],
"routes": Array [
Object {
"key": "sign-up-invitation-SSf39_Tj79VFv34ydou3N",
"name": "sign-up-invitation",
"params": undefined,
},
],
"stale": false,
"type": "stack",
},
}

Pointing to StackNavigator from TabNavigator throws Maximum update depth exceeded error

In my React Native app, I'm using react-navigation version 5 along with react-native-paper.
I'm trying to have one of the items in my bottom tab to point to a stack navigator. When I do that, I get the following error:
Maximum update depth exceeded. This can happen when a component calls
setState inside useEffect, but useEffect either doesn't have a
dependency array, or one of the dependencies changes on every render.
If I point the tab item to a regular screen, everything works fine. As per the react-navigation documentation, I use the BottomNavigation component from react-paper as opposed to using their tab solution -- https://reactnavigation.org/docs/material-bottom-tab-navigator/#using-with-react-native-paper-optional
Here's my tabs component:
import React from 'react';
import { BottomNavigation } from 'react-native-paper';
// Components
import CodeScanner from '../../../screens/vendors/CodeScannerScreen';
import Home from '../../../screens/home/HomeScreen';
import Vendors from '../vendors/VendorsStack';
// Stylesheet
import { styles } from './style-home-tabs';
const HomeTabs = (props) => {
const [index, setIndex] = React.useState(0);
// Routes
const homeRoute = () => <Home />;
const vendorsRoute = () => <Vendors />;
const scanRoute = () => <CodeScanner />;
const [routes] = React.useState([
{ key: 'home', title: 'Home', icon: 'newspaper-variant-multiple' },
{ key: 'vendors', title: 'Vendors', icon: 'storefront' },
{ key: 'codescanner', title: 'Scan', icon: 'qrcode-scan' }
]);
const renderScene = BottomNavigation.SceneMap({
home: homeRoute,
vendors: vendorsRoute,
codescanner: scanRoute
});
return (
<BottomNavigation
navigationState={{ index, routes }}
onIndexChange={setIndex}
renderScene={renderScene}
labeled={false} />
);
}
export default HomeTabs;
And here's the stack navigator that I'm pointing to which causes the error:
import React from 'react';
import { createStackNavigator } from '#react-navigation/stack';
// Components
import VendorsScreen from '../../../screens/vendors/VendorsScreen';
// Navigator
const VendorStack = new createStackNavigator();
const VendorsStack = (props) => {
return(
<VendorStack.Navigator screenOptions={{ headerShown: false }}>
<VendorStack.Screen name="Vendors" component={VendorsScreen} />
</VendorStack.Navigator>
);
}
export default VendorsStack;
As I said, if I were to point to a regular screen, everything works fine. Any idea what's going on here and how to fix it?
Moving the route declarations outside the component fixed the issue. Every time the component got mounted, it was calling these functions and that was causing the issue.
import React from 'react';
import { BottomNavigation } from 'react-native-paper';
// Components
import CodeScanner from '../../../screens/vendors/CodeScannerScreen';
import Home from '../../../screens/home/HomeScreen';
import Vendors from '../vendors/VendorsStack';
// Stylesheet
import { styles } from './style-home-tabs';
// Routes
const homeRoute = () => <Home />;
const vendorsRoute = () => <Vendors />;
const scanRoute = () => <CodeScanner />;
const HomeTabs = (props) => {
const [index, setIndex] = React.useState(0);
const [routes] = React.useState([
{ key: 'home', title: 'Home', icon: 'newspaper-variant-multiple' },
{ key: 'vendors', title: 'Vendors', icon: 'storefront' },
{ key: 'codescanner', title: 'Scan', icon: 'qrcode-scan' }
]);
const renderScene = BottomNavigation.SceneMap({
home: homeRoute,
vendors: vendorsRoute,
codescanner: scanRoute
});
return (
<BottomNavigation
navigationState={{ index, routes }}
onIndexChange={setIndex}
renderScene={renderScene}
labeled={false} />
);
}
export default HomeTabs;

Unit Test Not Reach 100% Coverage IF/ELSE Not Taken in Imports

The Unit test with jest / Enzyme or React-Native-Testing-Library never reaches 100% of code coverage for problems with the import, some import show warning IEEIEIE if/else not taken
These modules are in node_modules (and node_modules is include in the coveragePathIgnorePatterns and exclude from collectCoverageFrom) or custom modules that do not even have if / else statements
import React from 'react';
import { cleanup, render } from 'react-native-testing-library';
import ItemListaComprobante from './ItemListaComprobante';
describe('test container ConfiguracionPin', () => {
let wrapper;
const props = {
label: 'Test',
value: 'Test',
};
afterEach(() => cleanup);
beforeEach(() => {
wrapper = render(<ItemListaComprobante {...props} />);
});
test('test container ConfiguracionPin render properly ', () => {
expect(wrapper).toMatchSnapshot();
});
});
import React, { memo } from 'react';
import { Text } from 'react-native'; // (without IEEI)
import PropTypes from 'prop-types';
import { // IEEI
Left,
Right,
CardItem,
} from 'native-base';
const propTypes = {
label: PropTypes.string.isRequired,
value: PropTypes.string.isRequired,
};
const ItemListaComprobante = ({ label, value }) => (
<CardItem
bordered
borderedWhite
>
<Left>
<Text>{label}</Text>
</Left>
<Right>
<Text
bold
numberOfLines={1}
ellipsizeMode="tail"
>
{value}
</Text>
</Right>
</CardItem>
);
ItemListaComprobante.propTypes = propTypes;
export default memo(ItemListaComprobante);
My settings of jest
module.exports = {
verbose: true,
preset: 'react-native',
testEnvironment: 'jsdom',
transform: { '^.+\\.js$': '<rootDir>/node_modules/react-native/jest/preprocessor.js' },
setupFiles: ['<rootDir>/jest.setup.js'],
transformIgnorePatterns: ['/node_modules/*.js'],
coveragePathIgnorePatterns: [
'<rootDir>/index.js',
'<rootDir>/App.js',
'<rootDir>/commitlint.config.js',
'/node_modules/',
'jest.setup.js',
'ReactotronConfig.js',
'LogUtils.js',
'jest.config.js',
'rn-cli.config.js',
'transformer.js',
'super-wallet/coverage/lcov-report',
'Str.js',
],
setupFilesAfterEnv: [
'<rootDir>/__mocks__/react-native-camera.js',
'<rootDir>/__mocks__/react-native-fetch-blob.js',
'<rootDir>/__mocks__/react-native-firebase.js',
'<rootDir>/__mocks__/react-navigation.js',
'<rootDir>/__mocks__/react-native-reactotron.js',
'<rootDir>/__mocks__/react-native-user-agent.js',
'<rootDir>/__mocks__/osiris.js',
'<rootDir>/__mocks__/react-native-check-app-install.js',
'<rootDir>/__mocks__/react-native-image-crop-picker.js',
'<rootDir>/__mocks__/react-native-app-link.js',
],
collectCoverageFrom: ['**/*.{js,jsx}', '!**/node_modules/**', '!**/vendor/**'],
coverageThreshold: {
global: {
branches: 80,
functions: 80,
lines: 80,
statements: -10,
},
},
};
My settings of babelrc
{
"presets": ["module:metro-react-native-babel-preset"]
}
I would appreciate very much if somebody can help me, I have reviewed documentation of jest / istanbul and enzyme and I have not seen anything, even here I have also searched in case someone has had the same problem as me
This is error in istanbul coverage
Istanbul Report Printscreen error

Testing component that uses react-navigation with Jest

I'm working on a React Native application that also use Redux and I want to write tests with Jest. I'm not able to mock the "navigation" prop that is added by react-navigation.
Here is my component:
import React from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import { Text, View } from 'react-native';
const Loading = (props) => {
if (props.rehydrated === true) {
const { navigate } = props.navigation;
navigate('Main');
}
return (
<View>
<Text>Loading...</Text>
</View>
);
};
Loading.propTypes = {
rehydrated: PropTypes.bool.isRequired,
navigation: PropTypes.shape({
navigate: PropTypes.func.isRequired,
}).isRequired,
};
const mapStateToProps = state => ({
rehydrated: state.rehydrated,
});
export default connect(mapStateToProps)(Loading);
The Loading component is added as a screen to a DrawerNavigator.
And here is the test:
import React from 'react';
import renderer from 'react-test-renderer';
import mockStore from 'redux-mock-store';
import Loading from '../';
describe('Loading screen', () => {
it('should display loading text if not rehydrated', () => {
const store = mockStore({
rehydrated: false,
navigation: { navigate: jest.fn() },
});
expect(renderer.create(<Loading store={store} />)).toMatchSnapshot();
});
});
When I run the test, I get the following error:
Warning: Failed prop type: The prop `navigation` is marked as required in `Loading`, but its value is `undefined`.
in Loading (created by Connect(Loading))
in Connect(Loading)
Any idea on how to mock the navigation property?
Try to pass navigation directly via props:
it('should display loading text if not rehydrated', () => {
const store = mockStore({
rehydrated: false,
});
const navigation = { navigate: jest.fn() };
expect(renderer.create(<Loading store={store} navigation={navigation} />)).toMatchSnapshot();
});