Passing property dynamically to react component doesn't recognized by karma test - reactjs

I'm trying to test whether a property that pass to a react component get the correct value, but it doesn't seems to work because (I think) the property get it's value dynamically depends on the parent component state. Here's the code (summarized):
import React from 'react';
import InfiniteScroll from 'react-infinite-scroller';
class myClass extends React.Component {
constructor(props) {
super(props);
this.state = {
....
inputChanged: 0
....
};
...
render() {
let items = [];
... code that retrieve items ...
return (
<InfiniteScroll
pageStart={this.state.inputChanged ? 1 : 0} // page start is changed according to this.state.inputChanged value
loadMore={this.loadMore.bind(this)}
hasMore={this.state.hasMoreItems}
loader={<div className="loader" key={0}>Loading ...</div>}
threshold={200}
>
<div className="container-fluid">
<div className="row">
{items}
</div>
</div>
</InfiniteScroll>
);
}
}
And in my spec file:
import React from 'react';
import {mount} from 'enzyme';
import {expect} from 'chai';
describe('myClass', () => {
let wrapper;
beforeEach(() => {
wrapper = mount(
<myClass/>,
{attachTo: document.createElement('div')}
);
});
it('set correct pageStart', done => {
const InfiniteScroll = wrapper.find('InfiniteScroll');
wrapper.setState({
inputChanged: 1
});
expect(InfiniteScroll.props().pageStart).to.equal(1);
done();
});
}
But no matter what, InfinteScroll.props().pageStart is 0 and the test fails.
As far as I understand, it should changed accounring to wrapper.state().inputChanged, but it doesn't. Any ideas why?
Thanks in advance!

You are holding on to the reference of InfiniteScroll before calling setState. You need to move the code to get the reference of InfiniteScroll after the setState. Here is the updated test.
it('set correct pageStart', done => {
wrapper.setState({
inputChanged: 1
});
const InfiniteScroll = wrapper.find('InfiniteScroll');
expect(InfiniteScroll.props().pageStart).to.equal(1);
done();
});

Related

How to test HOC with enzyme, chai

I have a HOC function that receives a React component and returns that react component with two new method properties (handleBack & moveitOnTop) like so:
import React, { Component } from "react";
import classNames from "classnames";
export default WrappedComponent => {
return class extends Component {
constructor(props) {
super(props);
this.moveitOnTop = this.moveitOnTop.bind(this);
this.handleBack = this.handleBack.bind(this);
this.state = {
isSearchActive: false
};
}
moveitOnTop(flag) {
this.setState({ isSearchActive: flag });
window.scrollTo(0, -100);
}
handleBack() {
this.setState({ isSearchActive: false });
if (document.body.classList.contains("lock-position")) {
document.body.classList.remove("lock-position");
}
}
render() {
const props = {
...this.props,
isSearchActive: this.state.isSearchActive,
moveitOnTop: this.moveitOnTop,
goBack: this.handleBack
};
const classes = classNames({ "c-ftsOnTop": this.state.isSearchActive });
return (
<div className={classes}>
<WrappedComponent {...props} />
</div>
);
}
};
};
The component:
//import fireAnalytics
import { fireAnalytics } from "#modules/js-utils/lib";
class MyComponent extender Component{
constructor(){
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick(e) {
// calling analytics function by passing vals
fireAnalytics({
event: "GAEvent",
category: "",
action: `Clicked on the ${e.target.id} input`,
label: "Click"
});
// I CALL THE HOC PROPERTY
this.props.moveitOnTop(true);
// I CALL THE HOC PROPERTY
this.props.handleBack();
}
render(){
return(
<div className="emailBlock">
<input type="text" onClick={handleClick} />
<Button className="submit">Submit</Button>
</div>
)
}
}
// export HOC component
export default hoc(MyComponent);
// export just MyComponent
export {MyComponent};
I want to test the HOC:
I need to check that class .c-ftsOnTop exists
I need to check onClick function that calls this.props.handleBack & `this.props.moveitOnTop'
I need to check if className emailBlock exists.
The test that I tried, but fails:
import { mount, shallow } from 'enzyme';
import sinon from 'sinon';
import React from 'react';
import { expect } from 'chai';
import hoc from '....';
import {MyComponent} from '...';
import MyComponent from '....';
it('renders component', () => {
const props = {}
const HocComponent = hoc(MyComponent);
const wrapper = mount(
<HocComponent {...props} />
);
console.log('wrapper:', wrapper);
expect(wrapper.find('.c-ftsOnTop')).to.have.lengthOf(1);
expect(wrapper.hasClass('c-fts-input-container')).toEqual(true);
})
Error
AssertionError: expected {} to have a length of 1 but got 0
console.log: wrapper: ReactWrapper {}
Can anybody help me on how to render the HOC?
Here is a working test:
import { mount } from 'enzyme';
import React from 'react';
import WrappedMyComponent from './MyComponent';
it('renders component', () => {
const props = {}
const moveitOnTopSpy = jest.spyOn(WrappedMyComponent.prototype, 'moveitOnTop');
const handleBackSpy = jest.spyOn(WrappedMyComponent.prototype, 'handleBack');
const wrapper = mount(
<WrappedMyComponent {...props} />
);
// 1. I need to check that class .c-ftsOnTop exists
wrapper.setState({ isSearchActive: true }); // <= set isSearchActive to true so .c-ftsOnTop is added
expect(wrapper.find('.c-ftsOnTop')).toHaveLength(1); // Success!
// 2. I need to check onClick function that calls this.props.handleBack & `this.props.moveitOnTop'
window.scrollTo = jest.fn(); // mock window.scrollTo
wrapper.find('input').props().onClick();
expect(moveitOnTopSpy).toHaveBeenCalled(); // Success!
expect(window.scrollTo).toHaveBeenCalledWith(0, -100); // Success!
expect(handleBackSpy).toHaveBeenCalled(); // Success!
// 3. I need to check if className emailBlock exists
expect(wrapper.find('.emailBlock')).toHaveLength(1); // Success!
})
Details
.c-ftsOnTop is only added when isSearchActive is true so just set the state of the component so the class is added.
If you create your spies on the prototype methods for moveitOnTop and handleBack, then when the hoc creates its instance methods by binding them to this in the constructor, the instance methods will be bound to your spies.
window.scrollTo logs an error to the console by default in jsdom so you can mock it to avoid that error message and to verify that it was called with the expected arguments.
Note that the above test requires the following typos to be fixed in MyComponent:
extender should be extends
constructor should take a props argument
onClick should be bound to this.handleClick instead of just handleClick
handleClick should call this.props.goBack() instead of this.props.handleBack()
(I'm guessing MyComponent was just thrown together as an example of the actual component)

Simple React/Enzyme test failing over map issue

I have a simple react component with a simple test. It is failing for some reason. Can someone take a look at this?
Here is the component.
import React from 'react';
import ToDoItem from '../ToDoItem/ToDoItem';
export default class ToDos extends React.Component {
render() {
const toDoItems = this.props.items.map((item, key) => (
<ToDoItem item={item} key={key} />
));
return (
<section>
{ toDoItems }
</section>
)
}
}
Not rocket science. Here is the test:
it('displays all ToDo items passed as props', () => {
const items = ['example1', 'example2'];
const toDos = mount(<ToDos items={items} />);
expect(toDos.contains(<ToDoItem />)).toEqual(true);
});
The test seems very bothered by "TypeError: Cannot read property 'map' of undefined" It can't seem to read the items props. Am I not passing the array properly in the test? I can't see what's wrong here. The code itself works fine. It's just the test... Help!
you're not declaring any props in your component
constructor(props){
super(props)
}

States undefined in unit testing

I'm trying a very simple shallow test for a component:
it('renders without crashing', () => {
shallow(<SampleComponent />);
});
In my sample component, I did a setState :
this.setState({myCurrentState: "InSample"});
Now somewhere in my return, I used that to output an element, say:
return ( <h1> this.state.myCurrentState </h1>)
When I try the above test, I get
TypeError: Cannot read property of undefined.
I know that I can pass in props to shallow, but I can't seem to figure how to do it with states. Is there a better way of doing it? Sorry I'm new to React and unit testing. thanks
Here you go
import React from 'react';
import { render } from 'enzyme';
import SampleComponent from './SampleComponent';
describe('< SampleComponent />', () => {
it('should render', () => {
const wrapper = shallow(<SampleComponent name="Example" />);
expect(wrapper).toHaveLength(1);
});
describe('check props', () => {
const wrapper = shallow(<SampleComponent name="Example" />);
console.log(warpper.instance().props); // you should see name='Example'
});
});
Please check out this link http://airbnb.io/enzyme/docs/api/
You can't call the state like this return ( <h1> this.state.myCurrentState </h1>) you have to put it into { } like
return ( <h1> {this.state.myCurrentState} </h1>)
If you still having a troubles watch this video
Use Something like this:
class SomeComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
myCurrentState: null
};
// somewhere in your code in one of the method
this.setState({myCurrentState: "InSample"});
render() {
return (
<h1>{this.state.myCurrentState}</h1>
);
}
}

Testing react component with enzyme and expect fails

I am testing a React component with Enzyme, Mocha, and Expect. The test case is shown below:
import React from 'react';
import expect from 'expect';
import { shallow } from 'enzyme';
import Add from '../src/client/components/add.jsx';
describe('Add', () => {
let add;
let onAdd;
before(() => {
onAdd = expect.createSpy();
add = shallow(<Add onAdd={onAdd} />);
});
it('Add requires onAdd prop', () => {
console.log(add.props());
expect(add.props().onAdd).toExist();
});
});
I am creating a spy using expect and attaching it to the onAdd prop of the Add component. My test checks if the prop exists on the component. For some reason, onAdd is undefined and the test fails. Any help?
The problem is that add isn't wrapping the <Add> component, it wraps what it returns. So, if your component looks like:
class Add extends React.Component {
render() {
return (
<div>
{this.props.foo}
</div>
);
}
}
This statement add.props().onAdd will try to access onAdd prop from <div> not from <Add>, and obviously it will fail.
This assertion:
expect(add.props().onAdd).toExist();
Will succeed, in the component will look like:
class Add extends React.Component {
render() {
return (
<div onAdd={this.props.onAdd}>
{this.props.foo}
</div>
);
}
}
Example shown in enzyme docs, is a little bit confusing.

React: onChange is not a function

Am new to react and am trying to run unit test for a react app using jest,
The app using the Flux pattern and I have already wrote one component "WEATHER" and before move on to another one I wanted to write test for that component and take TDD approach.
My code running fine but the test fail with this error
TypeError: tree.props.onChange is not a function
The weather component code :
// #flow
import React from 'react';
import api from '../api';
import weatherStore from '../stores/weatherStore';
//read from weather store
let _getState = () => {
return {weather: weatherStore.returnWeather()};
};
export default class Weather extends React.Component {
constructor(proporties) {
super(proporties);
this._onChange = this._onChange.bind(this);
this.state = _getState();
}
componentWillUnmount() {
weatherStore.removeListener('change', this._onChange);
}
componentDidMount() {
api.getWeather();
weatherStore.on('change', this._onChange);
}
_onChange() {
this.setState(_getState());
}
render() {
let weatherState = this.state.weather.map(weather => {
return <div key={weather.id} className="pull-right row">
<div>
<span>{weather.main.temp} C</span><br />
</div>
</div>;
})
return <div>{weatherState}</div>;
}
}
Where the test code:
import React from 'react';
import Weather from '../js/components/Weather';
import renderer from 'react-test-renderer';
it('should render weather temp and city', ()=> {
const component = renderer.create(
<Weather />
);
let tree = component.toJSON();
expect(tree).toMatchSnapshot();
// manually trigger the callback
tree.props._onChange();
// re-rendering
tree = component.toJSON();
expect(tree).toMatchSnapshot();
});
Note since am using flow static type checker it always high light this line of code this._onChange = this._onChange.bind(this);
with error message saying : property '_onChange' property not found in the weather.
TypeError: tree.props.onChange is not a function
Have you tried updating your test with:
// manually trigger the callback
tree.props._onChange();
Note the dash.

Resources