Mocking moment.js react props with jest and enzyme - reactjs

Say i have a child component that takes two moment objects as props such as:
import React from 'react';
import PropTypes from 'prop-types';
import moment from 'moment';
class ChildComponent extends React.Component {
constructor(props) {
super(props);
}
startAndEndDateOnSameDay() {
return this.props.startDate.isSame(this.props.endDate, 'date')
}
render() {
let formattedDate;
if(this.startAndEndDateOnSameDay()) {
formattedDate = this.props.startDate.format();
}
else {
formattedDate = this.props.endDate.fromNow();
}
return (
<div>{formattedDate}</div>
);
}
}
ChildComponent.propTypes = {
startDate: PropTypes.instanceOf(moment).isRequired,
endDate: PropTypes.instanceOf(moment).isRequired
}
export default ChildComponent;
And a parent component that passes down two moment objects to the child component like:
import React from 'react';
import PropTypes from 'prop-types';
import moment from 'moment';
import ChildComponent from './ChildComponent';
class ParentComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
startDate: moment(),
endDate: moment()
};
}
render() {
return (
<ChildComponent startDate={this.state.startDate} endDate={this.state.endDate}/>
);
}
}
export default ParentComponent;
And I am trying to test these components with jest and enzyme with:
import React from 'react';
import { shallow } from 'enzyme';
import ParentComponent from '../components/ParentComponent';
describe('<ParentComponent />', () => {
let wrapper;
beforeAll(() => {
wrapper = shallow(<ParentComponent/>);
});
it('should render the component correctly', () => {
expect(wrapper).toMatchSnapshot();
});
});
And
import React from 'react';
import { shallow } from 'enzyme';
import ChildComponent from '../components/ChildComponent';
import moment from 'moment';
describe('<ChildComponent />', () => {
let wrapper;
beforeAll(() => {
wrapper = shallow(<ChildComponent startDate={moment()} endDate={moment()}/>);
});
it('should render the component correctly', () => {
expect(wrapper).toMatchSnapshot();
});
describe('when the the start date and end date are on the same day', () => {
it('should print a the formatted start date', () => {
expect(wrapper.text()).toEqual('mock format here');
});
});
describe('when the start and end date are not on the same day', () => {
it('should print the the end date from now', () => {
expect(wrapper.text()).toEqual('mock from now here');
});
});
});
How do I mock the moment.js library functions in order for my tests to work?
I am trying to create a manual mock file within the __mocks__ folder that will mock the moment library's functions for both test suites. The current issues I am running into:
How do i mock the moment() constructor from within the mock file to always return the same date, so my snapshot tests will always pass?
How do i mock the the .isSame(), .format(), and .fromNow() functions to always return the same value regardless of the current time?
So far, I have the follow test file that breaks all my tests. I was following the documentation here:
const moment = require('moment');
function isSame() {
return true;
}
function fromNow() {
return 'Tomorrow at 12:00 pm'
}
function format() {
return 'Sept 16th 19';
}
exports.isSame = isSame;
exports.fromNow = fromNow;
exports.format = format;
When I use this file, i get errors within my component that say startDate and endDate are undefined.

I ended up solving this by creating a manual mock in the mocks folder. The key is that moment exports it's prototype with moment.fn.
import fixtures from '../__fixtures__/moment';
const moment = require.requireActual('moment');
// By default, the isSame function will always return true
let isSame = true;
moment.fn.isSame = () => isSame;
moment.fn.calendar = () => fixtures.CALENDAR;
moment.fn.fromNow = () => fixtures.FROM_NOW;
// Since format is often called with a variety of format strings, we need to
// differentiate how we are calling the function
moment.fn.format = (format) => {
switch (format) {
case 'MMM Do YY':
return fixtures.DATE;
case 'H:mm a':
return fixtures.TIME;
case 'MMM Do YY H:mm a':
return fixtures.DATETIME;
default:
return Error('Unsupported format in moment mock. Add case to __mocks__/moment.js');
}
};
moment.duration.fn.humanize = () => fixtures.DURATION;
// This function is added to moment's prototype in order for our tests to
// change moment's isSame behaviour
moment.fn.__isSame = (value) => { isSame = value; };
// This resets the isSame behaviour back to default
moment.fn.__reset = () => {
moment.fn.isSame = () => true;
};
export default moment;
Jest automatically loads this file when running the tests, and I can then test my components like:
it('renders a moment duration', () => {
expect(wrapper.text()).toEqual(fixtures.DURATION);
});
If i need to change the behavior of the .isSame function, I can modify it with:
import moment from 'moment';
beforeAll(() => {
moment.fn.__isSame(false);
});

If you are using JEST to test, I can recommend you https://github.com/hustcc/jest-date-mock Then you can mock current date using
advanceTo(new Date(2018, 5, 27, 0, 0, 0));
Best practice to test is to mock fixed date and compare with result

Related

Context API Unit test failing with TypeError

I have a HOC component WithContext (in a file conveniently named withContext.js) as follows
import React from 'react';
import { DEFAULT_STATE } from './store';
const MyContext = React.createContext(DEFAULT_STATE);
export function WithContext(Component) {
return function WrapperComponent(props) {
return (
<MyContext.Consumer>
{state => <Component {...props} context={state} />}
</MyContext.Consumer>
);
};
};
and a component that uses it as follows
import React from "react";
import { WithContext } from './withContext';
const MyComp = (context) => {
return (
<div className="flex dir-col" id="MyComp">
<p>This is a test</p>
</div>
)
};
export default WithContext(MyComp);
I also have a unit test that aims to test this MyComp component. The unit test follows
import React from "react";
import {shallow} from "enzyme";
import Enzyme from "enzyme";
import Adapter from "enzyme-adapter-react-16";
import { WithContext } from './withContext';
// We need to configure our DOM
import jsdom from 'jsdom'
const {JSDOM} = jsdom;
const {document} = (new JSDOM('<!doctype html><html><body></body></html>')).window;
global.document = document;
global.window = document.defaultView
Enzyme.configure({
adapter : new Adapter()
})
beforeEach(() => {
jest.resetModules()
})
//Takes the context data we want to test, or uses defaults
const getMyContext = (context = {
state : {}
}) => {
// Will then mock the MyContext module being used in our MyComp component
jest.doMock('withContext', () => {
return {
MyContext: {
Consumer: (props) => props.children(context)
}
}
});
// We will need to re-require after calling jest.doMock.
// return the updated MyComp module that now includes the mocked context
return require('./MyComp').default;
};
describe("MyComp component loading check", () => {
test("Renders the MyComp component correctly", () => {
const MyCompContext = getMyContext();
const wrapper = shallow(<MyComp/>);
// make sure that we are able to find the header component
expect(wrapper.find(".flex").hostNodes().length).toBe(1);
});
});
However this test keeps failing with the message
TypeError: (0 , _withContext.WithContext) is not a function
};
export default WithContext(MyComp);
Can you please let me know what is wrong here ?
Thanks
Looks like you are mocking withContext with jest.doMock but the mock you are returning from the factory function does not contain a WithContext function.
Then when you require('./MyComp').default it is using the withContext mock within your MyComp module and failing when it tries to export default WithContext(MyComp); since the withContext mock does not define a WithContext function.

Props aren't passing inside component in test cases written with Jest and Enzyme

This is my test case
import React from 'react';
import { mount } from 'enzyme';
import CustomForm from '../index';
describe('Custom Form mount testing', () => {
let props;
let mountedCustomForm;
beforeEach(() => {
props = {
nav_module_id: 'null',
};
mountedCustomForm = undefined;
});
const customform = () => {
if (!mountedCustomForm) {
mountedCustomForm = mount(
<CustomForm {...props} />
);
}
return mountedCustomForm;
};
it('always renders a div', () => {
const divs = customform().find('div');
console.log(divs);
});
});
Whenever I run the test case using yarn test. It gives the following error TypeError: Cannot read property 'nav_module_id' of undefined.
I have already placed console at multiple places in order to see the value of props. It is getting set. But it couldn't just pass through the components and give the error mentioned above.
Any help would be appreciated been stuck for almost 2-3 days now.
You have to wrap the component that you want to test in beforeEach method such that it becomes available for all the 'it' blocks, and also you have to take the mocked props that you think you are getting into the original component.
import React from 'react'
import {expect} from 'chai'
import {shallow} from 'enzyme'
import sinon from 'sinon'
import {Map} from 'immutable'
import {ClusterToggle} from '../../../src/MapView/components/ClusterToggle'
describe('component tests for ClusterToggle', () => {
let dummydata
let wrapper
let props
beforeEach(() => {
dummydata = {
showClusters: true,
toggleClustering: () => {}
}
wrapper = shallow(<ClusterToggle {...dummydata} />)
props = wrapper.props()
})
describe(`ClusterToggle component tests`, () => {
it(`1. makes sure that component exists`, () => {
expect(wrapper).to.exist
})
it('2. makes sure that cluster toggle comp has input and label', () => {
expect(wrapper.find('input').length).to.eql(1)
expect(wrapper.find('label').length).to.eql(1)
})
it('3. simulating onChange for the input element', () => {
const spy = sinon.spy()
const hct = sinon.spy()
hct(wrapper.props(), 'toggleClustering')
spy(wrapper.instance(), 'handleClusterToggle')
wrapper.find('input').simulate('change')
expect(spy.calledOnce).to.eql(true)
expect(hct.calledOnce).to.eql(true)
})
})
})

How to get value of react app state['Key'] in test?

I'm trying to make an test for a react application, but I have run into a problem. I couldn't get jest to work properly so I have been trying to work around it which has caused my current problem. The reason my test isn't working is because the way I'm calling for the values of the keys in state requires me to be using enzyme in jest. Is their a way to get the values of the keys in state inside the react app without using jest and how do I do it?
this is the function in my react app:
setTimeMonth = (time) => {
const today = moment().format('YYYY-MM-DD');
const before = moment().subtract(time, 'months').format('YYYY-MM-DD');
this.setState({Date2: today});
this.setState({Date1: before});
}
this is the test for the function:
it('setTimeMonth(number)', () => {
const wrapper = new ReactPage;
expect(wrapper.state('Date1').toMatch(""));
expect(wrapper.state('Date2').toMatch(""));
wrapper.setTimeMonth(1);
expect(wrapper.state('Date1').toMatch(moment().format('YYYY-MM-DD')));
expect(wrapper.state('Date2').toMatch(moment().subtract(1, 'Month').format('YYYY-MM-DD')));
});
Here is a working example:
example.js:
import * as React from 'react';
import moment from 'moment';
export class Example extends React.Component{
constructor(props) {
super(props);
this.state = { Date1: '', Date2: '' };
}
setTimeMonth = (time) => {
const today = moment().format('YYYY-MM-DD');
const before = moment().subtract(time, 'months').format('YYYY-MM-DD');
this.setState({Date2: today});
this.setState({Date1: before});
};
render() {
return <div/>
}
}
example.test.js:
import * as React from 'react';
import { shallow } from 'enzyme';
import moment from 'moment';
import { Example } from './example';
describe('Example', () => {
it('setTimeMonth(number)', () => {
const wrapper = shallow(<Example/>);
expect(wrapper.state('Date1')).toBe('');
expect(wrapper.state('Date2')).toBe('');
wrapper.instance().setTimeMonth(1);
expect(wrapper.state('Date2')).toMatch(moment().format('YYYY-MM-DD'));
expect(wrapper.state('Date1')).toMatch(moment().subtract(1, 'months').format('YYYY-MM-DD'));
});
});

Ho to simulate 'this.props' inside React Component when using Enzyme for tests?

So, my goal is to reach the func cashedImageCheker which must return the string , based on recived from this.props -> imageFetchedURL value. And my problem is that I cannot understand how to put this value into the corresponding func cashedImageCheker of my component Banners.
I tried to use Enzyme .setProps({}) method to transfer mock props inside my testing Component, but for now I just gets undefined. Here is my test and component codes below.
Thank you for any help...
TEST FILE:
import React from 'react'
import { mount, shallow } from 'enzyme'
import Banners from '../'
describe('<Banners />', () => {
it('imageFetchedURL must return true', () => {
const Component = shallow(<Banners />)
Component.setProps({
imageFetchedURL: 'https://google.com/la-la-la/1.jpg'
})
const { imageFetchedURL } = Component.instance().cashedImageCheker()
expect(imageFetchedURL.length).toBe(33)
})
})
COMPONENT FILE:
import PropTypes from 'prop-types'
import React, { Component } from 'react'
class Banners extends Component {
cashedImageCheker = () => {
const { imageFetchedURL } = this.props
const imageFetchCached = imageFetchedURL && imageFetchedURL.length > 1
return imageFetchCached
}
render() {
const isLocalImgCached = this.cashedImageCheker()
return (
<div className='bannerWrap'>
{isLocalImgCached}
</div>
)
}
}
export default Banners
When testing your component, you can pass the props required by it while shallow mounting the component like
describe('<Banners />', () => {
it('imageFetchedURL must return true', () => {
const Component = shallow(<Banners imageFetchedURL={'https://google.com/la-la-la/1.jpg'}/>)
const imageFetchedURL = Component.instance().cashedImageCheker()
expect(imageFetchedURL.length).toBe(33)
})
})
Also the major problem in code test case is that cashedImageChecker doesn't return you an object but a value and hence you need to write
const imageFetchedURL = Component.instance().cashedImageCheker()
instead of
const { imageFetchedURL } = Component.instance().cashedImageCheker()
Doing the above change will allow you to setProps and test the component method too.
describe('<Banners />', () => {
it('imageFetchedURL must return true', () => {
const Component = shallow(<Banners />)
Component.setProps({
imageFetchedURL: 'https://google.com/la-la-la/1.jpg'
})
const imageFetchedURL = Component.instance().cashedImageCheker()
expect(imageFetchedURL.length).toBe(33)
})
})
So, my goal is to reach the func cashedImageCheker which must return
the string , based on recived from this.props -> imageFetchedURL
value.
Based on this one way is this
import React from 'react';
import { shallow } from 'enzyme';
import Banners from '../'
describe('<Banners />', () => {
const url = 'https://google.com/la-la-la/1.jpg';
it('imageFetchedURL must return true', () => {
const Component = shallow(<Banners imageFetchedURL= url />)
const imageFetchedURL = Component.instance().cashedImageCheker();
expect(imageFetchedURL.length).toBe(33)
})
})
And my problem is that I cannot understand how to put this value into
the corresponding func cashedImageCheker of my component Banners.
For this query you must know a thing when something is connected to props then during testing you can pass it on as actual props as I have done it
<Banners imageFetchedURL= url />
I tried to use Enzyme .setProps({}) method to transfer mock props
inside my testing Component
This is absolutely correct way too. It is another way of doing so.
But you were getting undefined because you were destructuring the return value which had nothing to do in your case.

Testing React/Redux app with componentWillMount() this.props.dispatch() call

Here is a stripped down version of my Redux/React component:
import React from "react";
import { connect } from "react-redux";
import Header from "./components/Header";
import { getMoviesInit } from "./actions/movieActions";
export class Layout extends React.Component {
componentWillMount() {
if (this.props.movies.length == 0) {
this.props.dispatch((dispatch) => {
dispatch(getMoviesInit());
});
}
}
render() {
return (
<div id="content-wrap">
<Header/>
{this.props.children}
</div>
);
}
};
export default connect((store) => {
return {
movies: store.movies
};
})(Layout);
When I try to run a test like the following, I'm told I this.props.dispatch() isn't a function which makes sense because there is a dispatch() call in the componentWillMount() function and I'm not using the connect() wrapped Layout:
import React from "react";
import LayoutWithConnect, { Layout } from "./Layout";
import { mount, shallow } from 'enzyme';
import { expect } from 'chai';
describe("Layout tests", function() {
beforeEach(() => {
let movies = {
featuredId: null,
currentTitles: {
movies: []
}
};
let subject = shallow(<Layout movies={movies} />);
});
});
However, if I try to use LayoutWithConnect in my test I get issues with there not being a store.
I'm not sure how I should be going about testing this component?
You can simply pass the dispatch function as a prop:
let dispatchedAction;
let dispatch = (action) => dispatchedAction = action;
let subject = shallow(<Layout movies={movies} dispatch={dispatch} />);
You can then also test that the view dispatched the correct action by comparing dispatchedAction with the expected action object.
If you find yourself doing this often, you can also use the excellent Sinon stubbing/mocking library, which allows you to easily create spy functions:
let dispatch = sinon.spy();
let subject = shallow(<Layout movies={movies} dispatch={dispatch} />);
expect(dispatch.calledWith(expectedAction)).to.be.true;

Resources