Unit testing using Jest and Enzym in React Native - react-native

How can I find element inside component with jest and enzyme?
Let say, I have 1 component parent (Login) and 2 child component (Title and Form), so in component Login, I want to find is there TextInput element inside Form component or something else maybe another element inside Form Component, with jest and enzyme, then how can I get that just with 1 unit testing (Login.test.js)?
This is my login component for the ilustration
<Login>
<Title title='Login Page' />
<Form
email={this.state.email}
password={this.state.password}
/>
</Login>
Title Component
<Text>{this.props.title}</Text>
Form Component
<View>
<TextInput value={this.props.email} placeHolder="Your Email" />
<TextInput value={this.props.password} placeHolder="Your Password" />
</View>
This is my current Login.test.js
import React from 'react';
import { shallow } from 'enzyme';
import Login from './Login';
import renderer from 'react-test-renderer';
describe('Login', () => {
const wrapper = shallow(<Login />);
const instaceOf = wrapper.instance();
it('renders correctly', () => {
const rendered = renderer.create(<Login />).toJSON();
expect(rendered).toBeTruthy();
});
it('should render the Text Input Element', () => {
let form = wrapper.find('Form');
expect(form.find('TextInput"]')).toHaveLength(2);
});
});

When you use enzyme shallow method, you are only rendering the first level of your component.
Therefore, the statement:
const wrapper = shallow(<Login />);
is only rendering the Title and Form components but not their children.
If you want to render all the component tree, you should use mount instead. That being said, the tests for your Login component should only test its first children. If you want to test that Form component renders two TextInput components, you should do so in the unit tests belonging to Form component (not in Login component).

Related

How can I close a Popover programatically with native base?

I am using Native Base Popover.
scenario
As a user,
I can press on the list,
So that I can select a fruit
My problem is I don't understand how to close the <Popover /> from the outside of the component.
Here is my organization
<Formik>
<Popover>
<FlatList>
<Pressable onPress={() => handlePress(item.id)} /> //Banaba
<Pressable onPress={() => handlePress(item.id)} /> //Potato
<Pressable onPress={() => handlePress(item.id)} /> //Ananas
NativeBase offers a useDisclose() hook for handling opening/closing of modals and other popup windows.
That hook provides an isOpen state (as #mainak's answer mentions) as well as onOpen() and onClose() functions to manipulate that state. You can pass these helpers as arguments as-needed into the props of the same name within the Popover component to handle open/close state.
Optionally, you can in addition pass true or false into useDisclose() to override the starting value of isOpen (defaults to false).
Here is an example below for reference.
import React from "react";
import { Popover, useDisclose } from "native-base";
function MyComponent() {
const { isOpen, onClose, onOpen } = useDisclose()
return (
<>
<Button onPress={onOpen}>Open the Popover</Button>
<Popover isOpen={isOpen} onClose={onClose}>
<Popover.Content>
<Popover.Arrow />
<Popover.CloseButton />
<Popover.Header>My Popover Title</Popover.Header>
<Popover.Body>You can place the content of your popover inside the body.</Popover.Body>
<Popover.Footer>
<Button onPress={onClose} variant="ghost">Cancel</Button>
</Popover.Footer>
</Popover.Content>
</Popover>
</>
)
}
can you try isOpen prop in Popover tag and have it as a state value like
const [isOpen, setOpen] = React.useState(true);
...
<Formik>
<Popover isOpen={isOpen}>
<FlatList>
...

Prevent re render of shared ListHeaderComponent

I am working on a social media app where i have a container component that has the following structure
<MyContainer>
<SelectionBar/>
{condition? <FlatListA header={header}/> : <FlatListB header={header}/>}
<MyContainer/>
the selection bar has buttons that determine which FlatList to display for the purpose of this question lets say messages FlatList vs posts FlatList
these two FlatLists have different listeners and data so they need to be their own component but they share the same ListHeaderComponent which is a feature similar to snapchat stories
the problem is when the user switches between two FlatLists the stories flicker because the component is re rendered because its two different FlatLists
the header needs to be inside the flatlist as a ListHeaderComponent because when the user scrolls down the stories should not stick to the top
is there any way to prevent this re rendering?
I've tried React.memo but that did not work
You can prevent re-rendering of same component by using React.memo
You can define your header component and pass it as a prop like:
import { memo } from "react";
import FlatListA from "./FlatListA";
import FlatListB from "./FlatListB";
const header = memo((props) => {
console.log("header render");
return <h1>this is header</h1>;
});
export default function App() {
return (
<div className="App">
<FlatListA header={header} />
<FlatListB header={header} />
</div>
);
}
and you can use it in your FlatList components like:
import { useState } from "react";
export default function FlatListA(props) {
console.log("flatlista render");
const [toggle, setToggle] = useState(false);
return (
<div>
<props.header />
FlatlistA {toggle}
<button onClick={() => setToggle(!toggle)}>toogle state</button>
</div>
);
}
You can take a look at this example codesandbox and click buttons to change state and see console outputs that it does not re-render header components.

FlatList `onEndReached` callback is not called when swipe to the end of the list

The FlatList component has a onEndReached prop. I am implementing a simple feature that when user swipe the screen to the end of the list, the app sends a new request to get more items to the list from backend. The following is what I tried:
I firstly created a custom component MyCustomList which is just a wrapper of FlatList. There are two props items and getMore.
const MyCustomList = ({items, getMore}) => {
...
return (
<FlatList
keyExtractor={keyExtractor}
data={items}
renderItem={renderItem}
onEndReached={getMore}
onEndReachedThreshold={0}
ListFooterComponent={myFooter}
/>
);
...
export default MyCustomList;
}
items are the data to be shown on the FlatList,
getMore is the function that send request to backend to get more items. It is supposed to be called when swiped to the end of the list.
In my screen component, I use above MyCustomList component as below:
import React, {useState} from 'react';
import MyCustomList from '../components/MyCustomList';
import {useQuery} from 'react-query';
const MyScreen = ({navigation}) => {
const [page, setPage] = useState(1);
// when screen is rendered, get the data from backend
const {data, status} = useQuery('my-items', httpClient.fetchItems);
// this is the function passed to MyCustomList component
const getMoreItems = () => {
// I expected to see this log every time when user swiped to the end of the list
console.log('get next 10 items');
setPage(page + 1);
};
return (
<View>
...
<MyCustomList items={data} getMore={getMoreItems} />
</View>);
};
As you can see, in my screen component I pass the getMoreItems function to MyCustomList.
When I run my app, I only see that log message I put inside getMoreItems function once when the screen is first time shown. It's weird to me that I haven't swiped to the end of the list at that point. After that, every time when I swipe to the end of the list, the getMoreItems is not triggered (I don't see that log message). Why is that?
P.S. when swiping to the bottom of the list, my screen is not re-rendered now because the getMoreItems is not triggered the setPage(page+1) is not called.

How to Create Drawer menu without Navigation?

I am looking to create a drawer, similar to a drawer navigator, but without the routes/navigation requirement.
I plan on placing some other components there that update a query. Any recommendations? Specifically the drawer would be used to display picklists, sliders, and date range components that would update the state and variables used in updating markers rendered on a map shown on the home page.
With Redux
You can use the contentComponent of the createDrawerNavigator to create your own custom drawer and bind it to redux-store.
By dispatching the actions with relevant queries you can update the variables as they are passed from the store to your Component.
Without Redux
You can either create a CustomDrawer component with similar animation and render it in your Component or use this react-native-drawer.
import Drawer from 'react-native-drawer'
class Application extends Component {
closeControlPanel = () => {
this._drawer.close()
};
openControlPanel = () => {
this._drawer.open()
};
render () {
return (
<Drawer
ref={(ref) => this._drawer = ref}
content={<DrawerContentComponent {...// Your Updater props}/>}
>
<Component />
</Drawer>
)
}
})

props automaticaly given as parameter

I would like to use a custom component and I need it to have the navigation props in order to navigate inside this component.
For now I'm passing navigation as a classic prop:
<MyVideo videoSource={require('../Image/S01E01DB.mp4')}
thumbnailSource={require('../Image/S01E01DB.mp4')}
navigation={this.props.navigation}/>
But is it possible to always have him set as it was given as a prop?
I need that in order to simplify the usage of my prop.
As said in the comments section by #jonrsharpe, the navigation props is only automatically given to routes declared with createStackNavigator or the others react-navigationnavigators`
There's a tool you can use from react-navigation to connect any component to your parent navigator:
import { withNavigation } from 'react-navigation';
class MyBackButton extends React.Component {
render() {
return <Button title="Back" onPress={() => { this.props.navigation.goBack() }} />;
}
}
// withNavigation returns a component that wraps MyBackButton and passes in the
// navigation prop
export default withNavigation(MyBackButton);
In case of nested navigators, the withNavigation function will get the closest parent of your component.
You will find more informations on this link