I have been learning React, Babel, Semantic UI, and Jest over the last couple of weeks. I haven't really run into too many issues with my components not rendering in the browser, but I have run into issues with rendering when writing unit tests with Jest.
The SUT is as follows:
EditUser.jsx
var React = require('react');
var { browserHistory, Link } = require('react-router');
var $ = require('jquery');
import Navigation from '../Common/Navigation';
const apiUrl = process.env.API_URL;
const phoneRegex = /^[(]{0,1}[0-9]{3}[)]{0,1}[-\s\.]{0,1}[0-9]{3}[-\s\.]{0,1}[0-9]{4}$/;
var EditUser = React.createClass({
getInitialState: function() {
return {
email: '',
firstName: '',
lastName: '',
phone: '',
role: ''
};
},
handleSubmit: function(e) {
e.preventDefault();
var data = {
"email": this.state.email,
"firstName": this.state.firstName,
"lastName": this.state.lastName,
"phone": this.state.phone,
"role": this.state.role
};
if($('.ui.form').form('is valid')) {
$.ajax({
url: apiUrl + '/api/users/' + this.props.params.userId,
dataType: 'json',
contentType: 'application/json',
type: 'PUT',
data: JSON.stringify(data),
success: function(data) {
this.setState({data: data});
browserHistory.push('/Users');
$('.toast').addClass('happy');
$('.toast').html(data["firstName"] + ' ' + data["lastName"] + ' was updated successfully.');
$('.toast').transition('fade up', '500ms');
setTimeout(function(){
$('.toast').transition('fade up', '500ms').onComplete(function() {
$('.toast').removeClass('happy');
});
}, 3000);
}.bind(this),
error: function(xhr, status, err) {
console.error(this.props.url, status, err.toString());
$('.toast').addClass('sad');
$('.toast').html("Something bad happened: " + err.toString());
$('.toast').transition('fade up', '500ms');
setTimeout(function(){
$('.toast').transition('fade up', '500ms').onComplete(function() {
$('.toast').removeClass('sad');
});
}, 3000);
}.bind(this)
});
}
},
handleChange: function(e) {
var nextState = {};
nextState[e.target.name] = e.target.value;
this.setState(nextState);
},
componentDidMount: function() {
$('.dropdown').dropdown();
$('.ui.form').form({
fields: {
firstName: {
identifier: 'firstName',
rules: [
{
type: 'empty',
prompt: 'Please enter a first name.'
},
{
type: 'doesntContain[<script>]',
prompt: 'Please enter a valid first name.'
}
]
},
lastName: {
identifier: 'lastName',
rules: [
{
type: 'empty',
prompt: 'Please enter a last name.'
},
{
type: 'doesntContain[<script>]',
prompt: 'Please enter a valid last name.'
}
]
},
email: {
identifier: 'email',
rules: [
{
type: 'email',
prompt: 'Please enter a valid email address.'
},
{
type: 'empty',
prompt: 'Please enter an email address.'
},
{
type: 'doesntContain[<script>]',
prompt: 'Please enter a valid email address.'
}
]
},
role: {
identifier: 'role',
rules: [
{
type: 'empty',
prompt: 'Please select a role.'
}
]
},
phone: {
identifier: 'phone',
optional: true,
rules: [
{
type: 'minLength[10]',
prompt: 'Please enter a valid phone number of at least {ruleValue} digits.'
},
{
type: 'regExp',
value: phoneRegex,
prompt: 'Please enter a valid phone number.'
}
]
}
}
});
$.ajax({
url: apiUrl + '/api/users/' + this.props.params.userId,
dataType:'json',
cache: false,
success: function(data) {
this.setState({data: data});
this.setState({email: data.email});
this.setState({firstName: data.firstName});
this.setState({lastName: data.lastName});
this.setState({phone: data.phone});
this.setState({role: data.role});
}.bind(this),
error: function(xhr, status, err) {
console.error(this.props.url, status, err.toString());
}.bind(this)
});
},
render: function () {
return (
<div className="container">
<Navigation active="Users"/>
<div className="ui segment">
<h2>Edit User</h2>
<div className="required warning">
<span className="red text">*</span><span> Required</span>
</div>
<form className="ui form" onSubmit={this.handleSubmit} data={this.state}>
<h4 className="ui dividing header">User Information</h4>
<div className="ui three column grid field">
<div className="row fields">
<div className="column field required">
<label>First Name</label>
<input type="text" name="firstName" value={this.state.firstName}
onChange={this.handleChange}/>
</div>
<div className="column field required">
<label>Last Name</label>
<input type="text" name="lastName" value={this.state.lastName}
onChange={this.handleChange}/>
</div>
<div className="column field required">
<label>Email</label>
<input type="text" name="email" value={this.state.email}
onChange={this.handleChange}/>
</div>
</div>
</div>
<div className="ui three column grid field">
<div className="row fields">
<div className="column field required">
<label>User Role</label>
<select className="ui dropdown" name="role"
onChange={this.handleChange} value={this.state.role}>
<option value="SuperAdmin">Super Admin</option>
</select>
</div>
<div className="column field">
<label>Phone</label>
<input name="phone" value={this.state.phone}
onChange={this.handleChange}/>
</div>
</div>
</div>
<div className="ui three column grid">
<div className="row">
<div className="right floated column">
<div className="right floated large ui buttons">
<Link to="/Users" className="ui button">Cancel</Link>
<button className="ui button primary" type="submit">Save</button>
</div>
</div>
</div>
</div>
<div className="ui error message"></div>
</form>
</div>
</div>
);
}
});
module.exports = EditUser;
The associated test file is as follows:
EditUser.test.js
var React = require('react');
var Renderer = require('react-test-renderer');
var jQuery = require('jquery');
require('../../../semantic/dist/components/dropdown');
import EditUser from '../../../app/components/Users/EditUser';
it('renders correctly', () => {
const component = Renderer.create(
<EditUser />
).toJSON();
expect(component).toMatchSnapshot();
});
The issue that I am seeing when I run jest
:
FAIL test/components/Users/EditUser.test.js
● Test suite failed to run
ReferenceError: jQuery is not defined
at Object.<anonymous> (semantic/dist/components/dropdown.min.js:11:21523)
at Object.<anonymous> (test/components/Users/EditUser.test.js:6:370)
at process._tickCallback (node.js:369:9)