I have a mocha based test which finishes before my onChange handler in a jsdom based enzyme test of my React component, despite that handler being synchronous using babel+ES2017. If I do a setTimeout()
of 1ms to put my expect()
calls in; the test passes.
Just wondering where the break down is? I'm sure there is some simple concept here I'm not considering. I'm thinking jsdom or enzyme does not wait around for the event handler to finish? A problem compounded by the length of time mocking fetch()
with fetch-mock
is taking (because it is asynchronous normally).
Is it resolvable without setTimeout()
, sinon
or lolex
, and if not; is it possible with simon
/ lolex
?
Tomorrow I expect I'll refactor it to avoid mocking fetch() in the tests.
Test output
</div>
1) flashes a nice message upon success
Success now!!
End of function now.
10 passing (4s)
1 failing
1) <Signup /> flashes a nice message upon success:
Uncaught AssertionError: expected { Object (root, unrendered, ...) } to have a length of 1 but got 0
at test/integration/jsx/components/signup.test.js:38:54
at _combinedTickCallback (internal/process/next_tick.js:67:7)
at process._tickDomainCallback (internal/process/next_tick.js:122:9)
Bootstrap
require('babel-register')();
require('babel-polyfill');
...
var jsdom = require('jsdom').jsdom;
var exposedProperties = ['window', 'navigator', 'document'];
global.document = jsdom('');
global.window = document.defaultView;
global.FormData = document.defaultView.FormData;
Object.keys(document.defaultView).forEach((property) => {
if (typeof global[property] === 'undefined') {
exposedProperties.push(property);
global[property] = document.defaultView[property];
}
});
global.navigator = {
userAgent: 'node.js'
};
documentRef = document;
Test
import React from 'react';
import { expect } from 'chai';
import { shallow, mount, render } from 'enzyme';
import Signup from '../../../../assets/js/components/signup.jsx';
import fetchMock from 'fetch-mock';
import sinon from 'sinon';
import 'isomorphic-fetch';
...
it("flashes a nice message upon success", function(){
fetchMock.mock("*", {body: {}});
const wrapper = shallow(<Signup />);
wrapper.find('#email').simulate('change', {target: {id: 'email', value: validUser.email}});
const signupEvent = {preventDefault: sinon.spy()};
wrapper.find('#signupForm').simulate('submit', signupEvent);
wrapper.update();
console.log(wrapper.debug());
expect(signupEvent.preventDefault.calledOnce).to.be.true;
expect(wrapper.find('.alert-success')).to.have.length(1);
expect(wrapper.find('.alert-success').text()).to.contain('Your sign up was successful!');
fetchMock.restore();
});
Component
async handleSubmit(e) {
e.preventDefault();
this.setState({ type: 'info', message: 'Sending...', shouldValidateForm: true });
let form = new FormData(this.form);
let response;
let responseJson = {};
try {
response = await fetch("/signup", {
method: "POST",
body: form
});
responseJson = await response.json();
if(!response.ok){
throw new Error("There was a non networking error. ");
}
this.setState({ type: 'success', message: 'Your sign up was successful!' });
console.log("Success now!!");
} catch(err) {
this.setState({ type: 'danger', message: "There was a technical problem. "});
}
console.log("End of function now. ");
}
...
<form method="POST" onSubmit={this.handleSubmit} ref={(form) => {this.form = form;} } id="signupForm">