unit test a react component- jest, enzyme - javascript

I am unit testing react component. One component imports other component and use its props. Here are jsx files :
class First extends React.PureComponent {
render() {
const { name, isSelected, onClick } = this.props;
const activeClass = isSelected ? styles.active : '';
return (
<div
className={`${styles.first} ${activeClass}`}
role="button"
tabIndex={0}
onClick={() => onClick(name)}
>
{name}
</div>
);
}
}
First.propTypes = {
name: PropTypes.string.isRequired,
isSelected: PropTypes.bool,
onClick: PropTypes.func,
};
export default First;
Here is my second class that imports this class :
i
mport First from '../First/First';
const Second = ({ values, passedVal, onClick }) => {
const second = values.map(vlaue =>
<First
key={value}
name={value}
isSelected={value === passedVal}
onClick={onClick}
/>,
);
return (
<div >
{Second}
</div>
);
};
Second.propTypes = {
values: PropTypes.arrayOf(PropTypes.string),
passedVal: PropTypes.string,
onClick: PropTypes.func,
};
export default FilterList;
Here is my test. I want to test isSelected condition in my test :
describe('Second - Unit test', () => {
let props;
let secondComponent;
const second = () => {
if (!secondComponent) {
secondComponent = shallow(<Second {...props} />);
}
return secondComponent;
};
beforeEach(() => {
props = Second.defaultProps;
secondComponent = undefined;
});
it('verify value of isSelected ', () => {
props.passedVal='value01';
props.value=['value01'];
console.log(props.isSelected);
});
It gives me undefined as this is prop of First class. How can i verify this logic here. Need to make instance of first and then check?

props.isSelected will be undefined, as you do not pass any value to it, and it does not have a default prop.
I think that instead of:
props.passedVal='value01';
props.value=['value01'];
You'll want to use:
secondComponent.setProps({
passedVal: 'value01',
values: ['value01']
});
Notice that in your test, the component is already mounted, and thus assigning new values to the props object will not actually affect the component. Using enzyme's setProps will though. You can read a bit more about that: https://github.com/airbnb/enzyme/blob/master/docs/api/ShallowWrapper/setProps.md
Furthermore, isSelected is a prop of the First component, so please notice that when you try to check its value in the test.

Wen using shallowthe test is executed on component as a unit, and does not indirectly asserting on behavior of child components. However you could check for a property of a child component using find, example (not tested):
const wrapper = shallow(<First/>);
expect(wrapper.find(Second).first().prop('isSelected')).to.equal('yourValue');

Related

Can variable be passing by function (react js)?

I wrote a class component and mutilple functions in it(class) , but don't know how variable be passing between different function.
class App extends Component {
state = {
stringA:null,
stringB:null
};
set_A = (event) =>{
const stringA = 'text';
}
copy_A = (event) =>{
const stringB = stringA;
}
render() {
return (
<>
<button onClick={this.set_A} type="click">set</button>
<button onClick={this.copy_A} type="click">copy</button>
</>
);}
}
export default App;
I reference this docs , but it only said function component without class component.
https://www.robinwieruch.de/react-function-component
or, are state and props not a kind of variable?
You access your properties with this.props and your state with this.state. You change state by calling setState which accepts partial states and merges them into the full state. It also triggers a re-render so that state changes can be seen in the UI.
class App extends Component {
state = {
stringA:null,
stringB:null
};
set_A = (event) => {
this.setState({ stringA: 'text' });
}
copy_A = (event) => {
this.setState({ stringB: this.state.stringA });
}
render() {
return (
<>
<button onClick={this.set_A} type="click">set</button>
<button onClick={this.copy_A} type="click">copy</button>
</>
);
}
}
export default App;
So in React, you would not be assigning a value to a variable like that. You would be utilizing State functionality. For Class-based React you would be using this.setState({stringA: 'text'})
or
this.setState({stringB: stringA})
Once the values are in the state you can access them anywhere in the component from the state object this.state.stringB for instance would have the value that was set once you had clicked on copy button
Example
set_A = (event) => {
this.setState({ stringA: 'text' });
console.log(this.state.stringB)
}
copy_A = (event) => {
this.setState({ stringB: this.state.stringA });
}
React Documentation is also a great resource to reference for Class and Function based component behaviors. https://reactjs.org/docs/state-and-lifecycle.html#adding-local-state-to-a-class
You change state by calling setState in class based components
Try this :
class App extends Component {
state = { stringA:null, stringB:null };
set_A = (e) => {
this.setState({...state, stringA: 'text' });
}
copy_A = (e) => {
this.setState({ ...state,stringB: this.state.stringA });
}
render() {
return (
<>
<button onClick={this.set_A} type="click">set</button>
<button onClick={this.copy_A} type="click">copy</button>
</>
);
}
}
export default App;
Use the spread operator {...state} to change only the targeted piece of state you want to change with no change in the other pieces of the state.

React Classes: Referencing class as "this", within an object's function property

I have finally gotten into using react and ES6 and it's going well but I am finally stumped and could use some direction.
I have got my head around binding this to a method to reference the class, but I am trying to go a bit deeper. Take this for example...which works as expected:
class App extends Component {
state = {
myFirstState: false,
};
handleMyFirstState = () => {
this.setState( { myFirstState : true } );
};
render() {
return (
<MyComponent handleMySate={ this.handleMyState } />
);
}
}
export default App;
As the amount of methods increased I decided NOT to pass each method individually as props and to group them in an object first, and to just pass the object as a whole, as a prop. Like So...
class App extends Component {
state = {
myFirstState: false,
mySecondState: false
};
handleMyFirstState = () => {
this.setState( { myFirstState : true } );
};
handleMySecondSate = () => {
this.setState( { mySecondState : true } );
};
render() {
const handleStates = {
first : this.handleMyFirstState,
second : this.handleMySecondState
}
return (
<MyComponent handleStates={ handleStates } />
);
}
}
export default App;
Now, I am trying to avoid redundant code and just build the methods as one object with functions as properties before the render begins. Pretty much like this...
class App extends Component {
state = {
myFirstState: false,
mySecondState: false
};
handleStates = {
// Here is where 'this' does not reference the App class
// I get results from the console log but setstate doesn't pass correctly
first : () => { console.log("First Triggered"); this.setState( { myFirstState : true } ); },
second : () => { console.log("Second Triggered"); this.setState( { mySecondState : true } ); }
};
render() {
return (
<MyComponent handleStates={this.handleStates} />
);
}
}
export default App;
// I trigger the function like this within MyComponent and I get the console log, but `this.setState` breaks.
<Button onClick={ this.props.handleState.first } >Handle First</button>
I have successfully triggered the functions from the child component ,<MyComponent/>, using the latter code, but this no longer refers to the class and I can't figure out how to bind this to handleStates since it's not a function.
Is this just not possible or is there another way to handle what I am trying to achieve?
Thank you in advance!
ADDITIONAL
If I move the handleStates into the render() it works just fine...how could that be?
class App extends Component {
state = {
myFirstState: false,
mySecondState: false
};
render() {
const handleStates = {
first : () => { this.setState( { myFirstState : true } ); },
second : () => { this.setState( { mySecondState : true } ); }
};
return (
<MyComponent handleStates={this.handleStates} />
);
}
}
export default App;
First, in the second example, you pass this.handleStates as the value for the prop handleStates, but it's undefined. You built handleStates as a local variable, and thus you want your props to reference that local variable:
<MyComponent handleStates={handleStates} />
For your third (last) example, your issue is even simpler: you defined handleStates as an attribute on this which is assigned an object, itself with two attributes, first and second, each of which have a function as their value.
When you ultimately pass this.handleStates to MyComponent, you're passing an object, not a function. If you want to call one of first or second from MyComponent, you can do so like this:
this.props.handleStates.first()
Which has the desired result of updating the state in App.
For what it's worth, there's a more common pattern for this: simply pass a single updater function as the prop, named according to what it does:
class Sandwich extends React.Component {
this.state = {
bread: "",
meat: "",
veggie: "",
}
updateSandwich = (component, selection) => {
this.setState({ [component]: selection })
}
render() {
return(<IngredientSelector updateSandwich={this.updateSandwich} />)
}
}
class IngredientSelector extends React.Component {
return(){
<button value="Rye" onClick={() => this.updateSandwich("bread", "rye")} />
<button value="Wheat" onClick={() => this.updateSandwich("bread", "wheat")} />
<button value="Ham" onClick={() => this.updateSandwich("meat", "ham")} />
<button value="Turkey" onClick={() => this.updateSandwich("meat", "turkey")} />
}
}

React functional component default props vs default parameters

In a React functional component, which is the better approach to set default props, using Component.defaultProps, or using the default parameters on the function definition, examples:
Default props:
const Component = ({ prop1, prop2 }) => (
<div></div>
)
Component.defaultProps = {
prop1: false,
prop2: 'My Prop',
}
Default parameters:
const Component = ({ prop1 = false, prop2 = 'My Prop' }) => (
<div></div>
)
defaultProps on functional components will eventually be deprecated (as per Dan Abramov, one of the core team), so for future-proofing it's worth using default parameters.
In general (ES6), the second way is better.
In specific (in React context), the first is better since it is a main phase in the component lifecycle, namely, the initialization phase.
Remember, ReactJS was invented before ES6.
First one can cause some hard-to-debug performance problems, especially if you are using redux.
If you are using objects or lists or functions, those will be new objects on every render.
This can be bad if you have complex components that check the component idenitity to see if rerendering should be done.
const Component = ({ prop1 = {my:'prop'}, prop2 = ['My Prop'], prop3 = ()=>{} }) => {(
<div>Hello</div>
)}
Now that works fine, but if you have more complex component and state, such as react-redux connected components with database connection and/or react useffect hooks, and component state, this can cause a lot of rerending.
It is generally better practice to have default prop objects created separately, eg.
const Component = ({prop1, prop2, prop3 }) => (
<div>Hello</div>
)
Component.defaultProps = {
prop1: {my:'prop'},
prop2: ['My Prop'],
prop3: ()=>{}
}
or
const defaultProps = {
prop1: {my:'prop'},
prop2: ['My Prop'],
prop3: ()=>{}
}
const Component = ({
prop1 = defaultProps.prop1,
prop2 = defaultProps.prop2
prop3 = defaultProps.prop3
}) => (
<div>Hello</div>
)
Shameless Plug here, I'm the author of with-default-props.
If you are a TypeScript user, with-default-props might help you, which uses higher order function to provide correct component definition with defaultProps given.
Eg.
import { withDefaultProps } from 'with-default-props'
type Props = {
text: string;
onClick: () => void;
};
function Component(props: Props) {
return <div onClick={props.onClick}>{props.text}</div>;
}
// `onClick` is optional now.
const Wrapped = withDefaultProps(Component, { onClick: () => {} })
function App1() {
// ✅
return <Wrapped text="hello"></Wrapped>
}
function App2() {
// ✅
return <Wrapped text="hello" onClick={() => {}}></Wrapped>
}
function App3() {
// ❌
// Error: `text` is missing!
return <Wrapped onClick={() => {}}></Wrapped>
}
I don't know if is the best way but it works :)
export interface ButtonProps {
children: ReactNode;
type?: 'button' | 'submit';
}
const Button: React.FC<ButtonProps> = ({ children, type = 'button' }) => {
return (
<button type={type}
>
{children}
</button>
);
};
Here is the official announcement regarding the deprecation of the defaultProps.
https://github.com/reactjs/rfcs/pull/107
Even maybe you ask, why not use sth like below code with props || value instead of defaultProps :
class SomeComponent extends React.Component {
render() {
let data = this.props.data || {foo: 'bar'}
return (
<div>rendered</div>
)
}
}
// SomeComponent.defaultProps = {
// data: {foo: 'bar'}
// };
ReactDOM.render(
<AddAddressComponent />,
document.getElementById('app')
)
But remember defaultProps make code more readable , specially if you have more props and controlling them with || operator could make your code looks ugly
you can use a destructured approach, example :
const { inputFormat = 'dd/mm/yyyy', label = 'Default Text', ...restProps } = props;

Double setState method in one function

I am trying to create a autocomplete component. It's an input where user types the countru name and if letters match name of some country, the hints are displayed.
In my App Component i have method handleChange Within this method i change my state two times, which is bad idea.
How can I split it to change state in distinct methods ?
import React, { Component } from 'react';
import AutoComplete from './autoComplete.jsx';
import data from './data.json';
class App extends Component {
constructor(props) {
super(props);
this.state = {
inputValue: '',
resoults: []
}
}
handleChange() {
let inputValue = this.refs.input.value;
this.setState({
inputValue: inputValue
});
let regular = "^" + this.state.inputValue;
let reg = new RegExp(regular , "i");
let filtered = data.filter((i,index)=> {
return (reg.test(i.name)
);
});
console.log(filtered);
this.setState({resoults:filtered})
}
render() {
return (
<div>
<input onChange={this.handleChange.bind(this)} type="text" ref="input"/>
<h3>You typed: {this.state.inputValue}</h3>
<AutoComplete resoults={this.state.resoults} />
</div>
);
}
}
export default App;
import React, {Component} from 'react';
class AutoComplete extends Component {
render() {
return (
<div>
<h4>autocompleteComponent</h4>
{this.props.resoults.map((i)=> {
return (
<ul>
<li>{i.name}</li>
</ul>
);
})}
</div>
);
}
}
export default AutoComplete;
I found myself in this position many times, but I got to the conclusion that it's better to compute the autocomplete options (in your case) without having them in the state of your component.
As I have used them until now, the state and props of a component should represent minimal data needed to render that specific component. Since you have your input value in the state, having the autocomplete options there also seems redundant to me. So here is what I propose:
class App extends Component {
this.state = {
inputValue: '',
};
handleChange(e) {
const inputValue = e.target.value;
this.setState({
inputValue,
});
}
computeResults() {
const {inputValue} = this.state;
// your functionality for computing results here
}
render() {
const {inputValue} = this.state;
const results = this.computeResults();
return (
<div>
<input type="text" onChange={this.handleChange.bind(this)} value={inputValue} />
<h2>You typed: {inputValue}</h2>
<Autocomplete results={results} />
</div>
);
}
}
Notes
Since your results come synchronously, via the .json import, this seems the perfect solution to me. If you want to get them via fetch or anything else, then you'll have to figure out a slightly different approach, but keep in mind that the state of your component should not contain redundant data.
Stop using ref with string value! and use refs when there is absolutely no other way because a React component should not generally deal with DOM operations directly. If you really need to use refs, use ref callbacks.
Hope this helps!
Use another function and setState callBack:
handleChange() {
let inputValue = this.refs.input.value;
this.setState(
{
inputValue: inputValue
},
() => this.secondFunc()
);
}
secondFunc() {
let regular = '^' + this.state.inputValue;
let reg = new RegExp(regular, 'i');
let filtered = data.filter((i, index) => {
return reg.test(i.name);
});
console.log(filtered);
this.setState({ resoults: filtered });
}

jest, enzyme - testing a method that returns jsx

I have the following component:
import React, { Component } from 'react';
import {Link, IndexLink} from 'react-router';
class Navbar extends Component {
renderLinks = (linksData) => {
return linksData.map((linkData) => {
if(linkData.to === '/') {
return(
<div className="navbar-link-container" key={linkData.to}>
<IndexLink activeClassName="navbar-active-link" to={linkData.to}>
<i className="navbar-icon material-icons">{linkData.icon}</i>
<span className="navbar-link-text">{linkData.text}</span>
</IndexLink>
</div>
)
}
else {
return(
<div className="navbar-link-container" key={linkData.to}>
<Link activeClassName="navbar-active-link" to={linkData.to}>
<i className="navbar-icon material-icons">{linkData.icon}</i>
<span className="navbar-link-text">{linkData.text}</span>
</Link>
</div>
)
}
})
};
render() {
return (
<div className={`navbar navbar-${this.props.linksData.length}`}>
{this.renderLinks(this.props.linksData)}
</div>
)
}
}
Navbar.propTypes = {
linksData: React.PropTypes.array.isRequired,
};
export default Navbar;
Now I am trying to write a unit test that will check the if condition (returning IndexLink or Link depending on the .to property):
But I can't seem to test for the exact jsx return of the function, since when I console.log one of the returns I get this:
{ '$$typeof': Symbol(react.element),
type: 'div',
key: '/',
ref: null,
props:
{ className: 'navbar-link-container',
children:
{ '$$typeof': Symbol(react.element),
type: [Object],
key: null,
ref: null,
props: [Object],
_owner: null,
_store: {} } },
_owner: null,
_store: {} }
This is the test I have written so far:
it('renderLinks should return a IndexLink', () => {
const wrapper = shallow(<Navbar linksData={mockLinksData}/>);
const renderLinksReturn = wrapper.instance().renderLinks(mockLinksData);
let foundIndexLink = false;
renderLinksReturn.map((linkHtml) => {
console.log(linkHtml);
});
expect(foundIndexLink).toBe(true);
})
Now I do not know what to test against to see if the function is running correctly. Is there a way to 'mount' the return of the function like a component? Or is there a simple method to return a html string of the actual return that I can check against?
Faced similar issue where we were passing a jsx component to another component as a prop.
You can shallow render the returned jsx since it's like a valid React Function/Stateless Component.
eg:
const renderLinks = shallow(wrapper.instance().renderLinks(mockLinksData))
And continue with your usual enzyme assertions.
To build on top of #Nachiketha 's answer, that syntax won't work when what's returned is a fragment, this can be solved by wrapping the result in a div like:
const renderLinks = shallow(<div>
{wrapper.instance().renderLinks(mockLinksData)
</div>
)}
as suggested in this tread.
I think you don't need to call
const renderLinksReturn = wrapper.instance().renderLinks(mockLinksData);
as it will be called when Navbar will be rendered.
Your solution is correct but in case you want some alternative robust ways to test it.
Since this test specifically tests for IndexLink and assumes that mockLinksData contains to = "/"
it('renderLinks should return a IndexLink when passed a link with to:\"/\"', () => {
const wrapper = shallow(<Navbar linksData={mockLinksData}/>);
// You can use both component name or component displayname
// IndexLink or "IndexLink"
expect(wrapper.find('IndexLink')).to.have.length(1);
// In case you want to test whether indexLink has appropriate props or classes.
const indexLink = wrapper.find(IndexLink).first();
// Check whether indexLink has pass prop
expect(indexLink.props().to).to.equal("/");
// Check whether indexLink has correct classes set.
expect(indexLink.hasClass('navbar-active-link')).to.equal(true);
// Check whether indexLink displays the link test correctly
expect(indexLink.find('navbar-link-text').text()).to.equal(mockLinksData.text);
});
Turns out that the type of the element is stored in the object. So the condition is:
props.children.type.displayName
And the final test I wrote looks like this for IndexLink:
it('renderLinks should return a IndexLink when passed a link with to:\"/\"', () => {
const wrapper = shallow(<Navbar linksData={mockLinksData}/>);
const renderLinksReturn = wrapper.instance().renderLinks(mockLinksData);
let foundIndexLink = false;
renderLinksReturn.map((linkHtml) => {
{linkHtml.props.children.type.displayName === 'IndexLink' ? foundIndexLink = true : null};
});
expect(foundIndexLink).toBe(true);
});

Categories

Resources