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

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.

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!

500 (Internal Server Error) when fetching from Django Rest API default router (to create object)

I am trying to make Django and React to work together, and although I have a basic knowledge of Django (I can develop a site), I am just starting to learn React.
My project structure is based on this video which is loosely based on this blog in turn.
My API works perfectly fine, I can create, retrieve, update, and delete objects without problems.
Also, on my frontend, I have managed to correctly display a list of the items from my database, by fetching the related API Url.
Just to make it clear, here is the main 'backend' urls.py:
from rest_framework import routers
from .views import AssessmentViewSet
router = routers.DefaultRouter()
router.register('api/assessments', AssessmentViewSet, 'assessments')
urlpatterns = router.urls
Now, from what I see on my API webpage, objects are created and retrieved on the same Url, and namely http://127.0.0.1:8000/api/assessments/, as you can see from this screenshot.
The only difference, I guess, is that when I want to create an object, all I have to do ist to specify inside the fetch function, that I am making a POST request. This is done in my CreateAssessmentForm.jsx file:
import React, { Component } from "react";
class CreateAssessmentForm extends Component {
state = {
loading: false,
title: "",
is_finished: false,
created_at: "",
};
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
let value = event.target.value;
console.log("Value: ", value);
this.setState({
title: value,
});
}
handleSubmit(event) {
event.preventDefault(console.log("Item:", this.state));
fetch("http://127.0.0.1:8000/api/assessments", {
method: "POST",
headers: {
"Content-type": "application/json",
},
body: JSON.stringify(this.state.title),
}).then(response => {
this.setState({
loading: false,
title: "",
is_finished: false,
created_at: "",
}).catch(function (error) {
console.log("ERROR:", error);
});
});
}
render() {
return (
<div>
{this.state.loading ? (
<div className="text-center">Loading...</div>
) : (
<form onSubmit={this.handleSubmit} method="POST">
<h2 className="text-center">Assessment Title</h2>
<div className="form-group">
<label htmlFor="Title">Title</label>
<input
onChange={this.handleChange}
type="text"
className="form-control"
id="TitleInput"
value={this.state.title}
placeholder="E.g. History Quiz"
/>
</div>
<button type="submit" className="btn btn-primary">
Submit
</button>
</form>
)}
</div>
);
}
}
export default CreateAssessmentForm;
When I type something inside the title input, the console logs properly every entry, and when I press the submit button it also logs an object (which as you can see from the code above is my actual state - {this.state}).
The problem is that when submitting, I get a 500 (Internal Server Error) and the object is not saved on the backend database.
Does anyone know what I am doing wrong?
Best shot would be to check what are the django logs as you get Internal Server Error. It should be clear enough of what you are doing wrong, if not, please post the error log.

Vue Template not updating after data is changed

I am new to Vue, and I have played around with small vue applications, but this is the first project I've done using vue and webpack (vue-cli).
I have a django rest API set up, for which I then use vue to consume the API and display the data. The issue i'm having is when I want to do anything the templates aren't updating. The data is loaded in correctly, but if I try and do a #click event and then a v-show (to toggle displaying an item) it doesn't change the view.
Example,
lets say I have a list of pubs that I consume from the API.
I get them via axios, and store them in the data function in a array called pubs:
<script>
import axios from 'axios'
export default {
name: 'Pubs',
data () {
return {
pubs: [
{
pub_id:'',
name:'',
address:'',
showPub: false,
},
]
}
},
created: function () {
this.loadPubs();
},
methods: {
loadPubs: function () {
var vm = this;
axios.get('http://127.0.0.1:8000/api/pubs/')
.then(function (response) {
vm.pubs = response.data;
vm.pubs.forEach(function (pub) {
pub.showPub = true;
});
console.log("loaded");
})
.catch(function (error) {
this.pubs = 'An error occured.' + error;
});
},
togglePub: function (pub) {
pub.showPub = !pub.showPub;
console.log(pub.showPub);
console.log(pub);
return pub;
},
}
}
</script>
The template could be:
<template>
<div class="pubs">
<h1>VuePubs</h1>
<ul>
<li v-for="pub in pubs">
<section>
<h2 #click="togglePub(pub)">{{ pub.name }}</h2>
<section v-show="pub.showPub">
<h3>{{ pub.address }}</h3>
<h3>{{ pub.postcode }}</h3>
<h3>{{ pub.showPub }}</h3>
</section>
</section>
</li>
</ul>
</div>
</template>
I can see that the data is changing in the model, thanks to the vue-dev tools, but the template doesn't change. The section doesn't hide and the h3 tags don't update with the new showPub field.
Any help would be greatly appreciated!

Binding ember model in dynamically generated form

I am learning ember js from last couple of weeks, and building an application to learn it. I am in a situation where I have to build a dynamic form which will be bind to ember model. (the simplest example for this problem could be nested form, where we can click on add more link/button to add form on the fly, and add values to them).
But for me, I am building survey like site, where we can have lots of option to select and user can select one of the option from available one:
what I have done so far?
readAnswer: Ember.computed(function() {
return this.get('store').query('answer', { filter:
{
question_id: this.get('question.id'),
submission_id: this.get('submission.id')
}
})
}),
buildAnswer: Ember.computed(function() {
this.get('store').createRecord('answer', {
question: this.get('question'),
submission: this.get('submission')
})
}),
answer: Ember.computed(function() {
const ans = this.get('readAnswer');
console.log(`question_id: ${this.get('question.id')}, submission_id: ${this.get('submission.id')}`);
if(Ember.isEmpty(ans)) {
return this.get('buildAnswer');
} else {
return ans;
}
})
answer.hbs
<div class="row col-sm-12">
<div class="form-group">
<label>{{model.title}}</label>
<p>
{{input type="text" value=answer.field01 class="form-control" placeholder="width"}}
</p>
<p>
{{input type="text" value=answer.field02 class="form-control" placeholder="depth"}}
</p>
</div>
</div>
NOTE here answer.hbs is a component, and these are call recursively (in loop) from parent route. So for 2 questions, we can have 4 textboxes, 2 text box for each question, first textbox for answer.field01 and second textbox for answer.field02
Let's say I have 2 questions, till now, I can see 2 answers build in the ember store if they don't already exists in database and then, I can see 4 textboxes generated in view. But they are not binding. Meaning, if I can value of the textbox, nothing happens in the ember store.
Expected Result
When I input answer in the textbox, it should bind with answer.fieldxx properly.
I extracted those codes from computed property to init() function and everything works now:
answer: null,
init() {
this._super(...arguments);
// build without computed property
this.get('store').query('answer', { filter:
{
question_id : this.get('question.id'),
submission_id : this.get('submission.id')
}
}).then((answers) => {
if(Ember.isEmpty(answers)) {
let a = this.get('store').createRecord('answer', {
question : this.get('question'),
submission : this.get('submission')
})
this.set('answer', a); // build new object and set answer
} else {
this.set('answer', answers.get('firstObject')); // get first answer and build it (because it is always 1 record)
}
}, (reason) => {
console.log(reason);
});
},

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

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.