How to mock e.preventDefault in react component's child - unit-testing

Hy, I don't know how to mock an inline function in React component's child
My stack: sinon, chai, enzyme;
Component usage:
<ListItem onClick={() => someFn()} />
Component's render:
render() {
return (
<li>
<a href="#" onClick={e => {
e.preventDefault();
this.props.onClick();
}}
> whatever </a>
</li>
);
}
Here we have onClick function that calls e.preventDefault(). How to tell to <a href>(link) to not to call e.preventDefault()? How can I mock an onClick?
Below is what I have tried in tests:
Shallow copy setup
function setup() {
const someFn = sinon.stub();
const component = shallow(
<ListItem
onClick={() => {
someFn();
}}
/>
);
return {
component: component,
actions: someFn,
link: component.find('a'),
listItem: component.find('li'),
}
}
And the test
it('simulates click events', () => {
const { link, actions } = setup();
link.simulate('click'); //Click on <a href>
expect(actions).to.have.property('callCount', 1); //will be fine if we remove e.preventDefault()
});
Test's output error:
TypeError: Cannot read property 'preventDefault' of undefined

Try this
link.simulate('click', {
preventDefault: () => {
}
});

test('simulates click events', () => {
const e = { stopPropagation: jest.fn() };
const component = shallow(<ListItem{...props} />);
const li = component.find('li').at(0).childAt(0)
li.props().onClick(e)
expect();
});

For those using Jest and #testing-library or react-testing-librarys fireEvent, you need to provide an initialised event object, otherwise the event can't be dispatched via your element.
One can then assert on e.preventDefault being called by assigning a property to that initialised event:
test('prevents default on click', () => {
const {getByText} = render(<MyComponent />);
const button = getByText(/click me/);
// initialise an event, and assign your own preventDefault
const clickEvent = new MouseEvent('click');
Object.assign(clickEvent, {preventDefault: jest.fn()});
fireEvent(button, clickEvent);
expect(clickEvent.preventDefault).toHaveBeenCalledTimes(1);
});
Similarly for stopPropagation.
Anton Karpenko's answer for Jest was useful.

Just to note that this is an issue only when using shallow enzyme renderer. In case of full DOM renderer mount, the event object contains the preventDefault method, therefore you don't have to mock it.

You can define an object with regarding function you will mock via some testing tool, for example look at Jest and Enzyme
describe('Form component', () => {
test('deos not reload page after submition', () => {
const wrapper = shallow(<TodosForm />)
// an object with some function
const event = { preventDefault: () => {} }
// mocks for this function
jest.spyOn(event, 'preventDefault')
wrapper.find('form').simulate('submit', event)
// how would you know that function is called
expect(event.preventDefault).toBeCalled()
})
})

I would suggest to create new object based on jest.fn() with
const event = Object.assign(jest.fn(), {preventDefault: () => {}})
then use it:
element.simulate('click', event);

I am using Web Components and this works for me -
const callback = jest.fn();
MouseEvent.prototype.stopPropagation = callback;
const element = createElement({});
element.shadowRoot.querySelector('ul').click();
expect(callback).toHaveBeenCalledTimes(1);

Related

How to test methods in functional component for nextjs components

Component:
const Demo = () => {
const handler = () => {
// some logic written
};
return (
<div>
<button data-testid={'handler_id'} onClick={() => handler()}>
Run Handler method
</button>
</div>
);
};
export default Demo;
test:
test('test handler method', () => {
render(<Demo />)
act(() => {
fireEvent.click(screen.getByTestId('handler_id'));
});
// some expectations below
})
I am unable to trigger handler method. Even in codeCoverage i dont see this method is covered.
Please help on how to write test cases for methods inside a component. All examples show that the method is passed to component from the props. But here my method is in the component itself.

How to stop redux-form or React from changing htmlFor and id when creating Jest snapshots?

I've got a wizard form made with redux-forms v6 and it looks something like:
--
index.js - Holds page number in local state, is connected to application level state
PageOne - wrapped with reduxForm decorator (form: 'wizForm')
PageTwo - wrapped with reduxForm decorator (form: 'wizForm')
--
PageOne and PageTwo both contain additional components that render sections of the form (initial fields, vehicle information, driver information...), and each of those sections render their own components for each question in that section.
Since there's a lot of nested components and I want to test that PageOne and PageTwo call the props passed from index.js, I've resorted to using Enzyme's mount() function with a fake store. I want to MatchSnapshot() with Jest to compare whether index.js is rendering PageOne or PageTwo, after certain buttons are clicked to go back and forth from pages.
The problem is when I do create snapshots, other than creating a 16,000 line snapshot, the snapshot will NEVER match the previous one even if I don't change anything. I'm not sure if it's redux-form that's doing it or React, but the htmlFor and the id keep changing between snapshots, test after test after test.
We use css-modules too, but I don't think that's causing the problem, and we did configure Jest to work with css-modules too, modifying "moduleNameWrapper" to mock .css files. Does anyone know how to fix this or where I should look?
tests:
describe('<VehicleAddition />', () => {
let props;
beforeEach(() => {
props = {
...,
};
});
it('Renders initially', () => {
const component = shallow(<VehicleAddition {...props} />);
expect(toJson(component)).toMatchSnapshot();
});
it('Renders <PageTwo> when <PageOne> form is submitted', () => {
const component = shallow(<VehicleAddition {...props} />);
expect(toJson(component)).toMatchSnapshot();
component.find('ReduxForm') // reduxForm HOC wraps the <form> in a <ReduxForm> component
.first()
.simulate('submit');
expect(toJson(component)).toMatchSnapshot();
expect(component.state().page).toEqual(2);
});
it('PageTwoStuffs', () => {
// Render the form, click 'next', assert it's page two
// click 'previous'
jest.enableAutomock();
const store = createStore(
combineReducers({
route: jest.fn(() => Immutable.fromJS({})),
language: jest.fn(() => Immutable.fromJS({})),
global: jest.fn(() => Immutable.fromJS({})),
form: formReducer,
}),
Immutable.fromJS({}),
);
const component = mount(
<Provider store={store}>
<VehicleAddition {...props} />
</Provider>
);
// CAN'T check the state of <VehicleAddition /> because it can only be done on root component, says the error message.
expect(toJson(component)).toMatchSnapshot();
index.js:
export class VehicleAddition extends React.Component { // eslint-disable-line
constructor(props) {
super(props);
this.state = {
page: 1,
};
}
nextPage = () => {
this.setState({ page: this.state.page + 1 });
}
previousPage = () => {
this.setState({ page: this.state.page - 1 });
}
render() {
return (
<div>
{page === 1 &&
<PageOne
{...this.props}
/>
}
{page === 2 &&
<PageTwo
{...this.props}
/>
}
</div>
);
}
}
PageOne.js
class PageOne extends React.Component { // eslint-disable-line
render() {
const {
...
} = this.props;
return (
<form onSubmit={handleSubmit}>
<div>
<InitialFields
autoPolicies={autoPolicies}
changeField={this.changeField}
getFormValues={getFormValues}
policies={policies}
primary={primary}
/>
<VehicleBeingAddedFields
changeField={this.changeField}
getFormValues={getFormValues}
fetchVehMakes={fetchVehMakes}
fetchVehModels={fetchVehModels}
policies={policies}
vehMakes={vehMakes}
vehModels={vehModels}
/>
...
<div className="btn-group btn-group-float-right">
<button
type="submit"
onClick={this.handleClick}
disabled={pristine || submitting}
className="btn-primary"
>
Next
</button>
</div>
</form>
);
}
}
PageTwo.js:
class PageTwo extends React.Component { // eslint-disable-line
render() {
const {
...
} = this.props;
return (
<form onSubmit={handleSubmit}>
...
<div className="btn-group btn-group-float-right">
<button type="button" className="btn" onClick={previousPage}>Previous</button>{' '}
<button type="submit" disabled={pristine || submitting} className="btn-primary">Submit</button>
</div>
</form>
);
}
}
Example of the parts of the snapshot that constantly changes:
I solved it by passing a hardcoded id value from the test cases
import React from 'react';
import renderer from 'react-test-renderer';
import { reduxForm } from 'redux-form';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import { mount } from 'enzyme'
import TodoItem from './TodoItem';
import injectTapEventPlugin from 'react-tap-event-plugin';
function setup() {
const spy = jest.fn();
const store = createStore(() => ({}));
const Decorated = reduxForm({ form: 'testForm' })(TodoItem);
const props = {
remove: jest.fn(),
TodoItemReduxFormInitialName: "fullName",
snapshotTestId:"4"
}
const mockedComponent = <Provider store={store}>
<Decorated {...props} />
</Provider>;
const enzymeWrapper = mount(mockedComponent)
injectTapEventPlugin();
return {
props,
mockedComponent,
enzymeWrapper
}
}
describe('TodoItem Component', () => {
it('should render the snapshot', () => {
const {mockedComponent} = setup()
const tree = renderer.create(
mockedComponent
).toJSON();
expect(tree).toMatchSnapshot();
});
//not required as snapshot testing covers it
it('should render Number', () => {
const {enzymeWrapper} = setup()
const fieldProps = enzymeWrapper.find('Field').at(0).props();
expect(fieldProps.hintText).toEqual('Item Number');
expect(fieldProps.name).toEqual('fullName.itemNumber');
});
//not required as snapshot testing covers it
it('should render remove button', () => {
const {enzymeWrapper} = setup()
const button = enzymeWrapper.find('RaisedButton').at(0).props();
expect(button.label).toEqual("remove")
});
});

What does jest.fn() do and how can I use it?

Can anyone explain how jest.fn() actually works, with a real world example, as I'm confused on how to use it and where it has to be used.
For example if I have the component Countries which fetches country List on click of a button with help of the Utils Function
export default class Countries extends React.Component {
constructor(props) {
super(props)
this.state = {
countryList:''
}
}
getList() {
//e.preventDefault();
//do an api call here
let list = getCountryList();
list.then((response)=>{ this.setState({ countryList:response }) });
}
render() {
var cListing = "Click button to load Countries List";
if(this.state.countryList) {
let cList = JSON.parse(this.state.countryList);
cListing = cList.RestResponse.result.map((item)=> { return(<li key={item.alpha3_code}> {item.name} </li>); });
}
return (
<div>
<button onClick={()=>this.getList()} className="buttonStyle"> Show Countries List </button>
<ul>
{cListing}
</ul>
</div>
);
}
}
Utils function used
const http = require('http');
export function getCountryList() {
return new Promise(resolve => {
let url = "/country/get/all";
http.get({host:'services.groupkt.com',path: url,withCredentials:false}, response => {
let data = '';
response.on('data', _data => data += _data);
response.on('end', () => resolve(data));
});
});
}
Where could I use jest.fn() or how can I test that getList() function is called when I click on the button?
Jest Mock Functions
Mock functions are also known as "spies", because they let you spy on the behavior of a function that is called indirectly by some other code, rather than just testing the output. You can create a mock function with jest.fn().
Check the documentation for jest.fn()
Returns a new, unused mock function. Optionally takes a mock implementation.
const mockFn = jest.fn();
mockFn();
expect(mockFn).toHaveBeenCalled();
With a mock implementation:
const returnsTrue = jest.fn(() => true);
console.log(returnsTrue()) // true;
So you can mock getList using jest.fn() as follows:
jest.dontMock('./Countries.jsx');
const React = require('react/addons');
const TestUtils = React.addons.TestUtils;
const Countries = require('./Countries.jsx');
describe('Component', function() {
it('must call getList on button click', function() {
var renderedNode = TestUtils.renderIntoDocument(<Countries />);
renderedNode.prototype.getList = jest.fn()
var button = TestUtils.findRenderedDOMComponentWithTag(renderedNode, 'button');
TestUtils.Simulate.click(button);
expect(renderedNode.prototype.getList).toBeCalled();
});
});

Enzyme: How to test onSubmit function passed as prop?

I am fairly new with enzyme. I have two components under test.
form.jsx
const LoginForm = ({ style, handleSubmit }) => {
return (
<form onSubmit={handleSubmit}>
<Button type='submit'>
Login
</Button>
</form>
);
};
LoginForm.propTypes = {
handleSubmit: PropTypes.func.isRequired
};
I am using this component in another component as follows:
Component.jsx
export default class Login extends Component {
constructor(props) {
super(props);
this.onLogin = this.onLogin.bind(this);
}
onLogin(event) {
event.preventDefault();
this.props.loginUser();
}
render() {
return (
<LoginForm style={loginFormStyles} handleSubmit={this.onLogin} />
);
}
}
Login.propTypes = {
auth: PropTypes.object.isRequired, //mapStateToProps
loginUser: PropTypes.func.isRequired //mapDispatchToProps
};
I have written tests for form and they are passing.
form-test.js
it('should have a onSubmit handler', () => {
const props = {
handleSubmit: () => {}
};
const wrapper = shallow(<LoginForm {...props} />);
expect(_.isFunction(wrapper.props().onSubmit)).to.be.true;
});
it('should should call handlesubmit on form submission', () => {
const handleSubmit = sinon.spy();
const wrapper = shallow(<LoginForm handleSubmit={handleSubmit} />);
wrapper.simulate('submit');
expect(handleSubmit).to.have.been.called;
});
These tests are passing. The confusing part is:
1- How do I test onLogin function in Component.jsx from form.jsx?
2- Vice versa, if I have to trigger onSubmit of form.jsx from component.jsx how would I do that?
First of all, you can rename the Component.jsx to something else.
And for the test you can do something as below,
import Component from '../src/login';
import { stub } from 'sinon';
describe('login', () => {
it('should call onsubmit', () => {
const onSubmit = stub()
.withArgs('username', 'password');
const loginComponent = mount(<LoginForm handleSubmit={onSubmit} /> );
loginComponent.simulate('submit');
expect(onSubmit.calledOnce).to.equal(true);
});
});
I have not tested this but it is close to what you are looking at.
Update:
I tested this and it is working.

Enzyme: Test fails on BrowserHistory function

I am fairly new to unit tests. I am using react+redux and I have created a NotFound page/component and I am writings its unit test. There's an onClick event that I need to test which is failing.
404.jsx
const GenericNotFound = () => {
const goBack = () => {
browserHistory.goBack();
};
return (
<section >
<h1>Sorry. The requested URL is not found</h1>
<a onClick={goBack}>Go back</a>
</section>
);
};
Test.js
const wrapper = shallow(<GenericNotFound />);
const browserHistory = {
goBack: sinon.spy()
};
const onClick = wrapper.find('a').props().onClick;
onClick();
expect(browserHistory.goBack).to.have.been.called;
Even with this, it throws me an error Cannot read property 'goBack' of undefined
Thank you in advance.
By following #anoop answer:
it('should render an anchor tag', () => {
sinon.spy(browserHistory, 'goBack');
const wrapper = mount(<GenericNotFound />;
const onClick = wrapper.find('a').props().onClick;
onClick();
expect(browserHistory.goBack).to.have.been.called;
browserHistory.goBack.restore();
});
As updated on the comments,
some changes
Use mount instead of shallow
const mount = mount(<GenericNotFound />);
Use spy as below,
sinon.spy(browserHistory.prototype, 'goBack')