Enzyme test DOM document using jsdom - reactjs

I’m trying to test a simple component in React using jsdom dependency. On my component I want to use plain javascript and I’m using document.getElementById() and enzyme is complaining about that. So I decided to try jsdom. However I don’t know if I’m passing the proper html string to it and if I need to use enzyme’s mount instead of shallow.
This is my react component code:
import React, { Component } from 'react';
class LikesAmount extends Component {
constructor() {
super();
this.state = {
totalLikes: null,
counter: 0,
};
}
componentDidMount() {
if (this.state.counter === 0) {
this.setState({
totalLikes: document.getElementById('likesAmount').dataset.likes,
counter: this.state.counter += 1,
});
}
}
render() {
return (
<div id="likesAmount" data-likes="1000">
<h3>Total likes: {this.state.totalLikes}</h3>
</div>
);
}
}
export default LikesAmount;
And this is my react component TEST code:
import React from 'react';
import { shallow } from 'enzyme';
import jsdom from 'jsdom';
import LikesAmount from './likesAmount';
const { JSDOM } = jsdom;
const { document } = (new JSDOM('<!doctype html><html><body><div id="likesAmount" data-likes="1000"></div></body></html>')).window;
global.document = document;
const wait = () => new Promise(resolve => setImmediate(resolve));
describe('likesAmount component', () => {
let wrapper;
beforeEach(async () => {
wrapper = shallow(<LikesAmount />);
await wait();
wrapper.update();
});
it('Return 1000 likes', async () => {
const likes = 1000;
expect(wrapper.find('h3').text()).toBe(`Total likes: ${likes}`);
});
});
The error that I'm getting is "Cannot read property 'find' of undefined". So I suppose it has something to do with jsdom because with a normal shallow passing the component as a parameter reads me the wrapper variable assign to it and the .find method...

Related

Unable to stimulate change event in child component React redux jest

I am new to react redux and I am trying to unit test FormContainer component. But I am unable to simulate change event on Form component using enzyme or jest. In wrapper.instance() I am receiving null but I dont' think that should. If not then what is correct way of testing FormContainer.js file, because when I ran test coverage it showed me that change function was uncovered.
FormContainer.js
import React, { Component } from "react";
import { connect } from "react-redux";
import Form from "../components/Form";
import { isNumeric } from "../utils";
class FormContainer extends Component {
constructor(props) {
super(props);
this.state = {
number: "",
error: false
};
}
// componentWillReceiveProps(nextProps) {
// console.log(nextProps, this.props, "main component");
// }
change = event => {
let value = event.target.value;
if (isNumeric(value) && value < 1001 && value >= 0) {
if (value === "0") {
this.setState({
...this.state,
error: true
});
return;
} else {
this.setState({
...this.state,
[event.target.name]: value,
error: false
});
}
} else {
this.setState({ ...this.state, error: true });
}
};
render() {
return (
<Form change={this.change} value={this.state} error={this.state.error} />
);
}
}
const mapStateToProps = state => {
return { ...state };
};
export default connect(mapStateToProps)(FormContainer);
FormContainer.test.js
import React from 'react';
import configureStore from "redux-mock-store";
import { shallow,mount } from 'enzyme';
import FormContainer from '../../containers/FormContainer';
import { findByAttr } from '../../utils';
import Form from '../../components/Form';
describe("Testing Form container", () => {
let wrapper;
let store;
beforeEach(() => {
const initialState = {}
const mockStore = configureStore();
store = mockStore(initialState);
wrapper = shallow(<FormContainer store={store} />);
});
it("Should render without error ", () => {
expect(wrapper.exists()).toBe(true);
});
it("Simulate change", () => {
let component = wrapper.find(Form);
const mockChange = jest.fn();
wrapper.instance().change = mockChange;
});
});
Testing Form container › Simulate change
TypeError: Cannot set property 'change' of null
22 | let component = wrapper.find(Form);
23 | const mockChange = jest.fn();
> 24 | wrapper.instance().change = mockChange;
| ^
25 | });
26 | });
at Object.<anonymous> (src/__test__/containers/FormContainer.test.js:24:9)
Dont need to import import Form from '../../components/Form'; since we are only testing FormContainer component, so remove it.
and there is no event listener called change, Use onChange instead.
<Form onChange={this.change} value={this.state} error={this.state.error} />
Try changing this spec:
it("Simulate change", () => {
let component = wrapper.find(Form);
const mockChange = jest.fn();
wrapper.instance().change = mockChange;
});
To:
it("Simulate change", () => {
(wrapper.find(Form).first()).simulate('change');
expect(wrapper.instance().change.mock.calls.length).toBe(1);
});

Mock function is not identified as called, after a button click inside the <a> tag

I have a local function that should be called on a button click and set the state of a Boolean variable inside it. I tried to add unit test to this module to identify whether the button is clicked and the function is called following the button click.
But my test is failing. I tried by mock the function inside the 'describe' method yet it didn't work.
SomeComponent.js
class SomeComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
openImagesDialog: false,
}
}
fuctionToBeCalled = (val) => {
this.setState({ openImagesDialog: val })
}
render() {
return (
<a onClick={() => this.fuctionToBeCalled(true)} className="img-container float">
{child components....}
</a>
)
}
}
SomeComponent.test.js
import React from 'react';
import Enzyme, { shallow, mount } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
import SomeComponent from '../SomeComponent';
import SomeAnotherComp from "../SomeAnotherComp";
Enzyme.configure({ adapter: new Adapter() })
function setup() {
const props = {
openImagesDialog: false
}
let enzymeWrapper = shallow(<SomeComponent {...props} />)
return {
props,
enzymeWrapper
}
}
describe('components', () => {
const { props, enzymeWrapper } = setup()
it('should call fuctionToBeCalled(){}', () => {
const SomeAnotherCompProps = enzymeWrapper.find(SomeAnotherComp).props()
const fuctionToBeCalled = jest.fn(()=>true);
enzymeWrapper.find('a').simulate('click')
expect(fuctionToBeCalled).toBeCalled();
//expect(SomeAnotherCompProps.dialogOpen).toBe(true)
})
})
I'd like to know is there any other way to try this out.
Firstly, openImagesDialog is not a prop but a state in the component.
Secondly, fuctionToBeCalled is a function defined on component instance and you need spy on it instead of just creating a mock function . In order to do that you use spyOn on component instance. You can also check the state after simulating click
import React from 'react'
import Enzyme, { shallow, mount } from 'enzyme'
import Adapter from 'enzyme-adapter-react-16'
import SomeComponent from '../SomeComponent'
import SomeAnotherComp from "../SomeAnotherComp";
Enzyme.configure({ adapter: new Adapter() })
function setup() {
const props = {
openImagesDialog: false
}
let enzymeWrapper = shallow(<SomeComponent {...props} />)
return {
props,
enzymeWrapper,
}
}
describe('components', () => {
const { props, enzymeWrapper } = setup()
it('should call fuctionToBeCalled(){}', () => {
const SomeAnotherCompProps = enzymeWrapper.find(SomeAnotherComp).props()
const instance = enzymeWrapper.instance();
jest.spyOn(instance, 'fuctionToBeCalled');
enzymeWrapper.find('a').simulate('click')
expect(instance.fuctionToBeCalled).toBeCalled();
expect(enzymeWrapper.state('openImagesDialog')).toEqual(true);
})
})

how to test a react component after data is fetch in componentDidMount?

I have a react component that renders conditionally (renders if data is fetched otherwise returns null) and I want to test this with jest & enzyme. The problem that I'm having is I want to test one of the methods in the class but .instance() keeps returning null so it doesn't allow me to test the instance.
my code looks something like this
export default class MyComponent extends React.Component<Props, State> {
componentDidMount() {
this.props.fetchData.then(() =>
this.setState({ loaded: true });
);
}
methodThatIWantToTest() {
//do some stuff here
}
render() {
if (this.loaded) {
// render stuff here
} else {
return null;
}
}
}
and in the test I want to test
describe('myComponent', () => {
it('should do some stuff', () => {
const shallowWrapper = shallow(<MyComponent {...props}/>);
const method = shallowWrapper.instance().methodThatIWantToTest();
....such and such
});
});
but it looks like MyComponent only returns null so shallowWrapper.instance() returns null as well. I tried shallowWrapper.update() and many other things but it seems it doesn't want to render at all.. How do I wait for my component to be updated and then starts expect statement?
has anyone had a similar issue as mine and know how to work around this?
It is render result and not an instance that is null. shallowWrapper.instance() is an instance of component class, it cannot be null for stateful component. As the reference states:
Returns (React 16.x)
ReactComponent: The stateful React component instance.
null: If stateless React component was wrapped.
While shallowWrapper.html() will be initially null indeed.
There is a mistake in original code, it should be this.state.loaded and not this.loaded:
MyComponent extends React.Component {
state = { loaded: false };
componentDidMount() {
this.props.fetchData.then(() => {
this.setState({ loaded: true });
});
}
methodThatIWantToTest() {
//do some stuff here
}
render() {
if (this.state.loaded) {
return <p>hi</p>;
} else {
return null;
}
}
}
componentDidMount and methodThatIWantToTest should be preferably considered different units. They belong to different tests. In case methodThatIWantToTest is called in lifecycle hooks, it may be stubbed in componentDidMount test:
it('should fetch data', async () => {
const props = { fetchData: Promise.resolve('data') };
const shallowWrapper = shallow(<MyComponent {...props}/>);
expect(shallowWrapper.html()).toBe(null);
await props.fetchData;
expect(shallowWrapper.html()).toBe('<p>hi</p>');
});
Then the method can be tested separately. Lifecycle hooks can be disabled to reduce number of moving parts:
it('should do some stuff', () => {
const shallowWrapper = shallow(<MyComponent {...props}/>, {disableLifecycleMethods: true});
const result = shallowWrapper.instance().methodThatIWantToTest();
expect(result).toBe(...);
});
Here is a working example:
myComponent.js
import * as React from 'react';
export default class MyComponent extends React.Component {
constructor(...props) {
super(...props);
this.state = { loaded: false };
}
componentDidMount() {
this.props.fetchData().then(() =>
this.setState({ loaded: true })
);
}
methodThatIWantToTest() {
return 'result';
}
render() {
if (this.state.loaded) {
return <div>loaded</div>;
} else {
return null;
}
}
}
myComponent.test.js
import * as React from 'react';
import { shallow } from 'enzyme';
import MyComponent from './myComponent';
describe('myComponent', () => {
it('should do some stuff', async () => {
const fetchData = jest.fn(() => Promise.resolve());
const props = { fetchData };
const shallowWrapper = shallow(<MyComponent {...props}/>);
expect(shallowWrapper.html()).toBe(null);
expect(shallowWrapper.instance().methodThatIWantToTest()).toBe('result');
// pause the test and let the event loop cycle so the callback
// queued by then() within componentDidMount can run
await Promise.resolve();
expect(shallowWrapper.html()).toBe('<div>loaded</div>');
});
});

Testing High-Order component with enzyme and jasmine

Testing with Jasmine and Enzyme and I've been trying to test a HOC which is connected to redux. Take for instance the following HOC:
import React, { Component } from 'react';
import { connect } from 'react-redux';
const WithAreas = (WrappedComponent) => {
class WithAreasComponent extends Component {
constructor(props) {
super(props);
}
shouldRenderWrappedComponent(userObj) {
return !!userObj.areas;
}
render() {
const { userObj } = this.props;
return shouldRenderWrappedComponent(userObj)
? <WrappedComponent {...this.props} />
: null;
}
}
function mapStateToProps(state) {
const { info } = state;
const { userObj } = info.userObj;
return { userObj };
}
return connect(mapStateToProps)(WithAreasComponent);
};
export default WithAreas;
Let's say I want to test this HOC in order to check if the wrapped component is being render according to the userObj. I thought about doing a mock component and pass it to the HOC, but this is not working.
Test.js File:
import React from 'react';
import { shallow } from 'enzyme';
import jasmineEnzyme from 'jasmine-enzyme';
import WithAreas from './';
class MockComponent extends React.Component {
render() {
return(
<div> MOCK </div>
);
}
}
function setup(extraProps) {
const props = {
info: {
userObj: {
id: 'example1'
}
},
};
Object.assign(props, extraProps);
const WithAreasInstance = WithAreas(MockComponent);
const wrapper = shallow(<WithAreasInstance {...props} />);
return {
props,
wrapper
};
}
fdescribe('<WithAreas />', () => {
beforeEach(() => {
jasmineEnzyme();
});
it('should render the Mock Component', () => {
const { wrapper } = setup();
expect(wrapper.find(MockComponent).exists()).toBe(true);
});
});
But it gives me this error:
TypeError: (0 , _.WithAreas) is not a function
at setup (webpack:///src/user/containers/WithAreas/test.js:20:47 <- src/test_index.js:9:18060087)
at UserContext.<anonymous> (webpack:///src/user/containers//WithAreas/test.js:34:24 <- src/test_index.js:9:18060822)
What am I doing wrong? Or what approach might you recommend?
Thanks for any given help.
You're not importing the HOC in test correctly. You've set it as the default export, but are using named export deconstruction.
Try this in your test file:
import WithAreas from './';
Also, don't forget to pass the store to your component in test, otherwise the connect HOC won't work as expected.
shallow(
<Provider store={store}>
<WithAreasInstance ...>
</Provider>)

Testing React Component with Jest and Enzyme. Error: Can't read property of map is undefined

So i haven't been able to get the todos passed down from the parent component nor have i been able to successfully set them myself. I'm not trying to test anything in particular at this point. Just that the component 'exists' without error
Component code:
import React, { Component } from 'react';
import TodoItem from './TodoItem';
class TodoList extends Component {
renderTodos = () => {
const { todos } = this.props;
return todos.map(todo => {
return <TodoItem key={todo.id} {...todo} />;
});
};
render() {
return (
<div>
{this.renderTodos()}
</div>
);
}
}
export default TodoList;
Test code:
import React from 'react';
import { shallow } from 'enzyme';
import renderer from 'react-test-renderer';
import TodoList from './TodoList';
import TodoItem from './TodoItem';
describe(TodoList, () => {
const todos = [
{
id: 1,
text: 'Walk the walk'
},
{
id: 2,
text: 'Talk the talk'
}
];
const component = shallow(<TodoList todos={todos} />);
it('should exist', () => {
const component = renderer.create(<TodoList />);
const tree = component.toJSON();
expect(tree).toMatchSnapshot();
});
});
Please help. New to Jest and pretty new to testing in general.
When you create a shallow instance of your component you need to pass the props which you are using without conditional check in your component
const component = shallow(<TodoList todos={[]}/>);
and
const component = renderer.create(<TodoList todos={[]}/>);

Resources