I was trying to test my ponents and everytime my tests fail, and i'm not able to figure out where the problem is located.
Login.test.js :
import { Meteor } from 'meteor/meteor';
import React from 'react';
import expect from 'expect';
import { mount } from 'enzyme';
import { Login } from './Login';
if (Meteor.isClient) {
describe("Login", function() {
it("should show error messages", function() {
const error = "This is not working";
const wrapper = mount(<Login loginWithPassword={() => {}}/>);
wrapper.setState({ error: error });
expect(wrapper.find("p").text()).toBe(error);
wrapper.setState({ error: "" });
expect(wrapper.find("p").length).toBe(0);
});
it("should called loginWithPassword with the form data", function() {
const email = "[email protected]";
const password = "password1234";
const spy = expect.createSpy();
const wrapper = mount(<Login loginWithPassword={spy}/>);
wrapper.ref("email").node.value = email;
wrapper.ref("password").node.value = password;
wrapper.find("form").simulate("submit");
expect(spy.calls[0].arguments[0]).toEqual({email: email});
expect(spy.calls[0].arguments[1]).toEqual(password);
});
it("should set loginWithPassword callback errors", function() {
});
})
}
Login.js :
import React from "react";
import { Link, Redirect } from "react-router-dom";
import { Meteor } from "meteor/meteor";
import { createContainer } from 'meteor/react-meteor-data';
export class Login extends React.Component {
constructor(props) {
super(props);
this.onSubmit = this.onSubmit.bind(this);
this.state = {
error: ""
};
}
onSubmit(e) {
e.preventDefault();
let email = this.refs.email.value.trim();
let password = this.refs.password.value.trim();
this.props.loginWithPassword({ email }, password, err => {
if (err) {
this.setState({ error: "Unable to login. Check your credentials." });
} else {
this.setState({ error: "" });
}
});
}
render() {
return (
<div className="boxed-view">
<div className="boxed-view__box">
<h1>Login</h1>
{this.state.error
? <p className="boxed-view__box__error">{this.state.error}</p>
: undefined}
<form
onSubmit={this.onSubmit}
noValidate
className="boxed-view__form"
>
<input type="email" ref="email" name="email" placeholder="Email" />
<input
type="password"
ref="password"
name="password"
placeholder="Password"
/>
<button className="button">Login</button>
</form>
<Link to="/signup">Not registered yet?</Link>
</div>
</div>
);
}
}
Login.PropTypes = {
loginWithPassword: React.PropTypes.func.isRequired
};
export default createContainer(() => {
return {
loginWithPassword: Meteor.loginWithPassword
}
}, Login);
Mocha client and server test :
Screenshot Chrome Mocha tests
Thank you for helping me out!
I was trying to test my ponents and everytime my tests fail, and i'm not able to figure out where the problem is located.
Login.test.js :
import { Meteor } from 'meteor/meteor';
import React from 'react';
import expect from 'expect';
import { mount } from 'enzyme';
import { Login } from './Login';
if (Meteor.isClient) {
describe("Login", function() {
it("should show error messages", function() {
const error = "This is not working";
const wrapper = mount(<Login loginWithPassword={() => {}}/>);
wrapper.setState({ error: error });
expect(wrapper.find("p").text()).toBe(error);
wrapper.setState({ error: "" });
expect(wrapper.find("p").length).toBe(0);
});
it("should called loginWithPassword with the form data", function() {
const email = "[email protected]";
const password = "password1234";
const spy = expect.createSpy();
const wrapper = mount(<Login loginWithPassword={spy}/>);
wrapper.ref("email").node.value = email;
wrapper.ref("password").node.value = password;
wrapper.find("form").simulate("submit");
expect(spy.calls[0].arguments[0]).toEqual({email: email});
expect(spy.calls[0].arguments[1]).toEqual(password);
});
it("should set loginWithPassword callback errors", function() {
});
})
}
Login.js :
import React from "react";
import { Link, Redirect } from "react-router-dom";
import { Meteor } from "meteor/meteor";
import { createContainer } from 'meteor/react-meteor-data';
export class Login extends React.Component {
constructor(props) {
super(props);
this.onSubmit = this.onSubmit.bind(this);
this.state = {
error: ""
};
}
onSubmit(e) {
e.preventDefault();
let email = this.refs.email.value.trim();
let password = this.refs.password.value.trim();
this.props.loginWithPassword({ email }, password, err => {
if (err) {
this.setState({ error: "Unable to login. Check your credentials." });
} else {
this.setState({ error: "" });
}
});
}
render() {
return (
<div className="boxed-view">
<div className="boxed-view__box">
<h1>Login</h1>
{this.state.error
? <p className="boxed-view__box__error">{this.state.error}</p>
: undefined}
<form
onSubmit={this.onSubmit}
noValidate
className="boxed-view__form"
>
<input type="email" ref="email" name="email" placeholder="Email" />
<input
type="password"
ref="password"
name="password"
placeholder="Password"
/>
<button className="button">Login</button>
</form>
<Link to="/signup">Not registered yet?</Link>
</div>
</div>
);
}
}
Login.PropTypes = {
loginWithPassword: React.PropTypes.func.isRequired
};
export default createContainer(() => {
return {
loginWithPassword: Meteor.loginWithPassword
}
}, Login);
Mocha client and server test :
Screenshot Chrome Mocha tests
Thank you for helping me out!
it's a shame that nobody answered this yet.
Hopefully you've eventually realized that the issue is the result of React Router's ponent rendering outside of the context of a Router ponent.
You need to wrap your ponent in a ponent to resolve this and then make the appropriate changes as your wrapper variable will change and your ponent will no longer be the root ponent, which will change how you operate on the wrapper variable.
You have a couple of issues with your code, Please use the following & let me know if you still have any problems
import { Meteor } from 'meteor/meteor';
import React from 'react';
import expect from 'expect';
import { configure } from 'enzyme';
import { mount } from 'enzyme';
import Adapter from 'enzyme-adapter-react-15';
configure({ adapter: new Adapter() });
import { Login } from './Login';
//notice we are importing the "raw" named-export Login not the containerized default one.
if (Meteor.isClient) {
describe('Login', () => {
it('Should show error messages', () => {
const error = 'This is not working';
const wrapper = mount(<Login loginWithPassword={() => { }} />);
wrapper.setState({ error: error });
expect(wrapper.find('p').text()).toBe(error);
wrapper.setState({error: ''});
expect(wrapper.find('p').length).toBe(0);
});
it('should call loginWithPassword with the form data', () => {
const email = '[email protected]';
const password = 'password123';
const spy = expect.createSpy();
const wrapper = mount (<Login loginWithPassword={spy}/>);
//notice the change, node ==> instance() due to deprecation of use of private properties by enzyme.
wrapper.find('input[name="email"]').instance().value = email;
wrapper.find('input[name="password"]').instance().value = password;
wrapper.find('form').simulate('submit');
expect(spy.calls[0].arguments[0]).toEqual({email:email});
expect(spy.calls[0].arguments[1]).toBe(password);
//above we used toBe (instead of toEqual) because second argument is a variable not object
});
});
}
Summary of issues in the original code
[1] Use of node
in referencing DOM objects as in the below snippet is deprecated by enzyme
wrapper.ref("email").node.value = email;
Furthermore, React has deprecated the use of string refs. You should use callbacks to avoid console warnings.
Hence, The right way is to use instance()
and find()
as per the below snippet
wrapper.find('input[name="email"]').instance().value = email;
[2] declaration in the original code defined email as object and password as variable (snippet below)
this.props.loginWithPassword({ email }, password, err => {
Hence, we should pick the right assertion method from expect
to handle each case as per the below snippet
expect(spy.calls[0].arguments[0]).toEqual({email:email});
expect(spy.calls[0].arguments[1]).toBe(password);