Using Jest to mock a component which has other components as properties - unit-testing

I'm trying to mock react-bootstrap <Modal> component with jest. <Modal> contains some "sub-components" as properties, for example <Modal.Header>. I'm trying to find out the correct way to mock this kind of components using Jest.
Here's a simple component using <Modal>:
// mymodal.js
import React from 'react'
import {Modal, Button} from 'react-bootstrap'
const MyModal = ({ visible, hide, title, onOk }) =>
<Modal show={visible} onHide={hide}>
<div className='simple-modal'>
<Modal.Header closeButton>{title}</Modal.Header>
<Modal.Body>
<div>I'm body</div>
</Modal.Body>
<Modal.Footer>
<Button className='invert-primary' onClick={hide}>
Cancel
</Button>
<Button bsStyle='primary' onClick={onOk}>
Ok
</Button>
</Modal.Footer>
</div>
</Modal>
export default MyModal
And here's basic snapshot test for it:
// mymodal.test.js
import renderer from 'react-test-renderer'
import * as React from 'react'
import MyModal from './mymodal'
jest.mock('react-bootstrap', () => {
function Modal(props) {
return <div>{props.children}</div>
}
Modal.Header = 'Modal.Header'
Modal.Body = 'Modal.Body'
Modal.Footer = 'Modal.Footer'
return({
Modal: Modal,
Button: 'Button',
})
})
describe('MyModal component', () => {
test('should render a modal', () => {
const modal = renderer.create(<MyModal
visible={true}
hide={() => ''}
onOk={() => ''}
title='Title' />)
expect(modal.toJSON()).toMatchSnapshot()
})
})
And here's snapshot:
// Jest Snapshot v1
exports[`MyModal component should render a modal 1`] = `
<div>
<div
className="simple-modal"
>
<Modal.Header
closeButton={true}
>
Title
</Modal.Header>
<Modal.Body>
<div>
I'm body
</div>
</Modal.Body>
<Modal.Footer>
<Button
className="invert-primary"
onClick={[Function]}
>
Cancel
</Button>
<Button
bsStyle="primary"
onClick={[Function]}
>
Ok
</Button>
</Modal.Footer>
</div>
</div>
`;
I'm quite happy with the snapshot result, but I'd like to get better output for the <Modal> component itself so that the snapshot would contain also component's name (currenlty <div>) and props (currently no props shown).
How should the mocking be done to achieve this?

I couldn't find way to achieve this with jest mocking. Finally I went with enzyme shallow rendering, which handles the basic mocking out of box. To do spanshot matching, I serialized the enzyme wrappers using enzyme-to-json npm package.

Related

Unit Testing Login Vue Jest ValidationProvider

I am new to Jest and I am trying to mock the store, an action and to assert that the method was indeed called. Basically I want to check the login function.
I cannot query the button because I am retrieving only a part of the component and I don't know what I'm missing.
Where my Vue component looks like:
<ValidationObserver id="observer" v-slot="{ invalid }" :class="['w-full h-full']">
<form class="flex flex-col justify-around h-full" #submit.prevent="onSubmit">
<ValidationProvider v-slot="{ errors }" name="accessCode" :class="['w-full py-6']">
<sd-field :class="['sd-field_secondary', { ['sd-invalid ']: errors && errors.length > 0 }]">
<label for="email">{{ $t("form.access-code") }}</label>
<sd-input v-model="accessCode" type="accessCode" />
<span class="sd-error">{{ errors[0] }}</span>
</sd-field>
</ValidationProvider>
<div class="btn-group-y">
<button class="btn btn-fixed btn-blueDark" type="submit">
<span>{{ $t("account.login") }}</span>
</button>
<!-- <button class="btn btn-link" type=""> //TODO: temporary hide
<span>{{ $t("account.forgot_id") }}</span>
</button> -->
</div>
</form>
</ValidationObserver>
***MY TEST FILE***
import login from "../../pages/index/login";
import Vuex from "vuex";
import { mount, createLocalVue } from "#vue/test-utils";
const localVue = createLocalVue();
localVue.use(Vuex);
describe("Login form", () => {
it("calls the login action correctly", () => {
const loginMock = jest.fn(() => Promise.resolve());
const store = new Vuex.Store({
actions: {
onSubmit: loginMock,
},
});
const wrapper = mount(login, { localVue, store, stubs: { ValidationObserver: true } });
console.log(wrapper.html());
// wrapper.find("button").trigger("click");
// expect(loginMock).toHaveBeenCalled();
});
});
console.log(wrapper.html()) returns only a part of component
<div class="h-full w-full"><validationobserver id="observer"
class="w-full h-full"> </validationobserver>
</div>
With a warning also:
> console.error node_modules/vue/dist/vue.runtime.common.dev.js:621
> [Vue warn]: Unknown custom element: <ValidationObserver> - did you register the
> component correctly? For recursive components, make sure to provide the "name" option.
> found in
> ---> <Anonymous>
> <Root>
I would like to understand how this works, I have tried to stub and other ways found but with no success.
I can get rid of the warning by adding the stubs: { ValidationObserver: true } to mount but I actually need to access the elements inside.
Thank you!

How to access Vue-test-util console error

I am using Jest to test this Vue component:
import { mount } from '#vue/test-utils'
import ExampleComponent from '../Components/Example.vue'
describe("Test", () => {
it('shows no errors', () => {
jest.spyOn(global.console, 'error');
jest.spyOn(global.console, 'warn');
mount(ExampleComponent)
expect(console.warn).not.toHaveBeenCalled()
expect(console.error).not.toHaveBeenCalled()
})
})
I am expecting this test to Fail since I have this component:
Example.vue
<template>
<div>
{{ number }}
</div>
</template>
<script>
export default {}
</script>
as you can see number is not defined, and if opened this component using the browser it will show me:
[Vue warn]: Property or method "number" is not defined on the instance but referenced during render.
but if I test it, the test will pass. How can If the Vue component has warnings or errors?

How do i unit test a react app that has all the text fields in the children components?

I have created a react application that has all the logic (like onchange functions) in the parent and all the html rendering in the children components.
In order to test if the right state changes are happening i have to enter text to the input fields and enter values but the only problem is I dont know how to access the children elements when i mount the parent in js dom.
Should i move logic into the child components or should i only unit test the functions of the parent component?
This is from the parent
render() {
if (!this.state.accessTokenEntered) {
return <AccessTokenPage _onChange={this._onChange}
accessToken={this.state.inputs.accessToken}
env={this.state.inputs.env}
_onFirstClick={this._onFirstClick}/>;
and this is the child
const AccessToken = props =>(
<Layout>
<Input name={"accessToken"} displayName={"Access Token"} _onChange={props._onChange}
value={props.accessToken}/>
<DropDown name={"env"} displayName={"Environment"} _onChange={props._onChange}
data={['ppe', 'prod']} multiple={false}
value={props.env}/>
<br/>
<div style={{"textAlign": "center"}}>
<input type="button" onClick={props._onFirstClick} className="btn btn-primary" value="Submit"/>
</div>
</Layout>
);
and this is the childs child
const Input = props => (
<div className="form-group row">
<label className="col-xs-2 col-form-label">{props.displayName}</label>
<div className="col-xs-10">
<input name={props.name} className="form-control" value={props.value}
onChange={props._onChange}/></div>
</div>
);
You should be testing your child component. When the onChange event of the textbox is simulated, test if the onChange prop is called. This can be done by creating a mock or spy for the onChange prop.
An example test is shown below:
Mocking a prop.
beforeEach(() => {
onAdd = jest.fn();
add = TestUtils.renderIntoDocument(<Add onAdd={onAdd} />);
});
Test if the mock method is called:
it('Button click calls onAdd', () => {
const button = TestUtils.findRenderedDOMComponentWithTag(add, 'button');
const input = TestUtils.findRenderedDOMComponentWithTag(add, 'input');
input.value = 'Name 4';
TestUtils.Simulate.change(input);
TestUtils.Simulate.click(button);
expect(onAdd).toBeCalledWith(input.value);
});
I am using Jest and React TestUtils. Similar code is available for enzyme in my github project.

Unable to locate button to simulate click - Unit Testing React w/ Mocha, Chai, Enzyme

I'm attempting to simulate a button click with Enzyme. I've been able to write simple tests if an element renders, however the fun tests such as button clicks etc. are falling short.
In this example the error in terminal is:
1) Front End #Profile Component clicks a button :
Error: This method is only meant to be run on single node. 0 found instead.
at ShallowWrapper.single (node_modules/enzyme/build/ShallowWrapper.js:1093:17)
at ShallowWrapper.props (node_modules/enzyme/build/ShallowWrapper.js:532:21)
at ShallowWrapper.prop (node_modules/enzyme/build/ShallowWrapper.js:732:21)
at ShallowWrapper.simulate (node_modules/enzyme/build/ShallowWrapper.js:505:28)
at Context.<anonymous> (cmpnt-profile.spec.js:36:32)
the unit test is:
describe('Front End #Profile Component', () => {
const wrapper = shallow(<Profile/>);
...(other tests here)...
it('clicks a button ', () => {
wrapper.find('button').simulate('click');
expect(onButtonClick.calledOnce).to.equal(true);
})
});
the component is:
import _ from 'lodash';
import React, { Component, PropTypes } from 'react';
import { reduxForm } from 'redux-form';
import ExpireAlert from '../components/alert';
import SocialAccount from '../components/socialAccounts'
const FIELDS = {
name : {
type : 'input',
label : 'name'
},
username : {
type : 'input',
label: 'username'
},
email : {
type : 'input',
label: 'email'
}
};
let alert = false;
export default class Profile extends Component {
componentWillReceiveProps(nextProps){
if(nextProps.userIsUpdated){
alert = !alert
}
}
handleSubmit(userData) { // update profile
userData.id = this.props.userInfo.id
this.props.updateUserInfo(userData)
}
renderField(fieldConfig, field) { // one helper per ea field declared
const fieldHelper = this.props.fields[field];
return (
<label>{fieldConfig.label}
<fieldConfig.type type="text" placeholder={fieldConfig.label} {...fieldHelper}/>
{fieldHelper.touched && fieldHelper.error && <div>{fieldHelper.error}</div>}
</label>
);
}
render() {
const {resetForm, handleSubmit, submitting, initialValues} = this.props;
return (
<div className="login">
<div className="row">
<div className="small-12 large-7 large-centered columns">
<div className="component-wrapper">
<ExpireAlert
set={this.props.userIsUpdated}
reset={this.props.resetAlert}
status="success"
delay={3000}>
<strong> That was a splendid update! </strong>
</ExpireAlert>
<h3>Your Profile</h3>
<SocialAccount
userInfo={this.props.userInfo}
unlinkSocialAcc={this.props.unlinkSocialAcc}
/>
<form className="profile-form" onSubmit={this.props.handleSubmit(this.handleSubmit.bind(this))}>
<div className="row">
<div className="small-12 large-4 columns">
<img className="image" src={this.props.userInfo.img_url}/>
</div>
<div className="small-12 large-8 columns">
{_.map(FIELDS, this.renderField.bind(this))}
</div>
<div className="small-12 columns">
<button type="submit" className="primary button expanded" disabled={submitting}>
{submitting ? <i/> : <i/>} Update
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
);
}
}
const validate = values => {
const errors = {}
if (!values.name) {
errors.name = 'Name is Required'
}
if (!values.username) {
errors.username = 'Username is Required'
} else if (values.username.length > 30) {
errors.username = 'Must be 30 characters or less'
}
if (!values.email) {
errors.email = 'Email is Required'
} else if (!/^[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(values.email)) {
errors.email = 'Invalid email address'
}
return errors;
}
Profile.propTypes = {
fields: PropTypes.object.isRequired,
handleSubmit: PropTypes.func.isRequired,
resetForm: PropTypes.func.isRequired,
submitting: PropTypes.bool.isRequired
}
export default reduxForm({
form: 'Profile',
fields: _.keys(FIELDS),
validate
})(Profile)
Any suggestions appreciated. I would love to make some amazing unit tests!
ZekeDroid is correct. Another way to handle the situation though would be to use mount, which is Enzyme's way to deep render components, meaning the entire tree of components are rendered. This would be considered an integration test, instead of a unit test. ZekeDroid mentions "This assumes your component can handle not receiving whatever reduxForm supplies it with, which is good practice anyhow as you will find it easier to test.", and I believe he's referencing unit testing. Ideally, your tests should include both unit tests and integration tests. I created a simple project to show how to do both unit and integration tests with redux-form. See this repo.
You have two export defaults on your file. This will naturally mean that the second one is overriding the first. If you really want to be able to export the reduxForm version and the component itself, as you should for testing, then remove the word default from the component.
On your test, you shallow render, meaning no children are rendered. Since you import the reduxForm, nothing much will get rendered, definitely not your Profile. Therefore, if you removed the default keyword like I mentioned, your test will start working when you import it using a named import:
import { Profile } from 'path/to/component';
*This assumes your component can handle not receiving whatever reduxForm supplies it with, which is good practice anyhow as you will find it easier to test.

Integration test for component containing a component

I have a component that uses another component. But it doesn't work because the test for the first component says it's missing a helper called curit-select.
In 0.10 you could add a needs to themoduleForComponent` function but doing that now will force it to unit-testing mode. (see)
You do not require dependencies through needs:. Doing so will force the test into unit mode.
component 1:
<div class='change-device-status'>
Status: {{curit-select content=statuses selection=device.status}}
<button class="save-button" {{action 'save'}}>opslaan</button>
</div>
component 2:
<select {{action 'change' on='change'}}>
{{#if prompt}}
<option disabled selected={{is-not selection}}>
{{prompt}}
</option>
{{/if}}
{{#each content key="#identity" as |item|}}
<option value="{{read-path item optionValuePath}}"
selected={{is-equal item selection}}>
{{read-path item optionLabelPath}}
</option>
{{/each}}
</select>
Now I'm trying to write an integration test for the first component:
import { moduleForComponent, test } from 'ember-qunit';
import hbs from 'htmlbars-inline-precompile';
moduleForComponent('change-device-status', 'Integration | Component | change device status', {
integration: true
});
test('it renders', function(assert) {
assert.expect(3);
// Set any properties with this.set('myProperty', 'value');
// Handle any actions with this.on('myAction', function(val) { ... });
this.set('dev', {
status: 'InUse'
});
this.render(hbs`{{change-device-status content=dev}}`);
assert.equals(this.$().text().trim(), '');
});
It'll just work! Remember restart ember serve.