React: onChange is not a function - reactjs

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.

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)

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

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();
});

How to mock a wrapper component with Jest?

Assume the component is working, I have just removed useless code. In my component to test, I have this:
import {Container} from "flux/utils";
import Dialog from "material-ui/Dialog";
import React from "react";
...
class Component extends React.Component {
state;
static calculateState() {
return {
...
};
}
static getStores() {
return [...];
}
...
render() {
var actions = [...];
return <Dialog
actions={actions}
open={this.state.open}
title="foo"
...
>
<form ...>
{/* My inputs and their values are according to the state. */}
</form>
</Dialog>;
}
}
const MyComponent = Container.create(Component);
export default MyComponent;
In the UI, the inputs and their values are working. However, when I use Jest, it is not.
jest.mock('react-dom');
jest.mock('material-ui/Dialog', () => 'Dialog');
import Dispatcher from "../../dispatcher/Dispatcher";
import MyComponent from "../MyComponent";
import React from "react";
import Renderer from "react-test-renderer";
describe('MyComponent', () => {
it('creates', () => {
const component = Renderer.create(<MyComponent/>);
let tree = component.toJSON();
expect(tree).toMatchSnapshot();
Dispatcher.dispatch({
action: 'myComponent/open'
});
tree = component.toJSON();
expect(tree).toMatchSnapshot();
tree.children[0].children[1].children[0].props.onChange(undefined, undefined, 'john');
tree = component.toJSON();
expect(tree).toMatchSnapshot();
});
});
The first snapshot is good. The second snapshot is good too, the open property is changing. However, when I trigger the onChange of an input, the component state is updating, all good... but when I do the last snapshot, the children are still the same, event if the input values are different (and the state is well updated, confirmed).
I know this is due to the Dialog. If I remove it from the component class, the component works as expected... So how should I declare the mock?
Here is my last try:
jest.mock('material-ui/Dialog', () => {
var React = require("react");
return class extends React.Component {
shouldComponentUpdate() {
return true;
}
render() {
var {children, ...rest} = this.props;
return children;
}
}
});
The children are still the same :(

Testing React Component with Jest/Enzyme

I am trying to use Enzyme's shallow wrapper to get the instance of my component and calling my class function over it. It shows me this error:
TypeError: tree.instance(...).onCampaignSelected is not a function
class ToolbarPage extends Component {
constructor(){
super();
this.onCampaignSelected = this.onCampaignSelected.bind(this);
this.state = {
item: null
}
}
onCampaignSelected (item) {
this.setState({item})
}
render () {
return (
<MyComponent onItemSelected={this.onCampaignSelected} />
)
}
}
function mapStateToProps(state){
buttons: state.toolbar.buttons
}
export default connect(mapStateToProps)(ToolbarPage);
My test case looks like this
import { shallow, mount } from 'enzyme';
import ToolbarPage from './ToolbarPage';
import configureStore from 'configureStore';
const store = configureStore();
const props = {
store,
isLoggedIn: false,
messageCounter: 0
}
describe('<ToolbarPage />', () => {
it('allows to select campaign', () => {
const tree = shallow(<ToolbarPage {...props}/>);
tree.instance().onCampaignSelected();
})
})
I also figured out that it is a wrapped component, so I won't get this function on the wrapped component. How do I access this function?
shallow does not render the full set of components with all of their properties & methods. It is intended for basic "did this thing render what I expected?" testing.
mount will give you everything and should allow you to test whatever you need. It is very useful for testing event handling & manipulating the state of components to test the interactions between components.

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.

Resources