add more login page tests

This commit is contained in:
John Mitchell 2018-10-30 15:08:45 -04:00
parent ecd8427a51
commit 7c97989e84
No known key found for this signature in database
GPG Key ID: FE6A9B5BD4EB5C94
2 changed files with 175 additions and 24 deletions

View File

@ -1,37 +1,185 @@
import React from 'react';
import { MemoryRouter } from 'react-router-dom';
import { shallow, mount } from 'enzyme';
import { mount, shallow } from 'enzyme';
import LoginPage from '../../src/pages/Login';
import api from '../../src/api';
import Dashboard from '../../src/pages/Dashboard';
const LOGIN_ERROR_MESSAGE = 'Invalid username or password. Please try again.';
describe('<LoginPage />', () => {
let loginWrapper, usernameInput, passwordInput, errorTextArea, submitButton;
let loginWrapper;
let loginPage;
let loginForm;
let usernameInput;
let passwordInput;
let errorTextArea;
let submitButton;
let defaultLogo;
beforeEach(() => {
loginWrapper = mount(<MemoryRouter><LoginPage /></MemoryRouter>);
const findChildren = () => {
loginPage = loginWrapper.find('LoginPage');
loginForm = loginWrapper.find('form.pf-c-form');
usernameInput = loginWrapper.find('.pf-c-form__group#username TextInput');
passwordInput = loginWrapper.find('.pf-c-form__group#password TextInput');
errorTextArea = loginWrapper.find('.pf-c-form__helper-text.pf-m-error');
submitButton = loginWrapper.find('Button[type="submit"]');
defaultLogo = loginWrapper.find('TowerLogo');
};
beforeEach(() => {
loginWrapper = mount(<MemoryRouter><LoginPage /></MemoryRouter>);
findChildren();
});
afterEach(() => {
loginWrapper.unmount();
});
test('initially renders without crashing', () => {
expect(loginWrapper.length).toBe(1);
expect(loginForm.length).toBe(1);
expect(usernameInput.length).toBe(1);
expect(usernameInput.props().value).toBe('');
expect(passwordInput.length).toBe(1);
expect(passwordInput.props().value).toBe('');
expect(errorTextArea.length).toBe(1);
expect(loginPage.state().error).toBe('');
expect(submitButton.length).toBe(1);
expect(submitButton.props().isDisabled).toBe(false);
expect(defaultLogo.length).toBe(1);
});
// initially renders empty username and password fields, sets empty error message and makes submit button not disabled
test('custom logo renders Brand component', () => {
loginWrapper = mount(<MemoryRouter><LoginPage logo="hey" /></MemoryRouter>);
findChildren();
expect(defaultLogo.length).toBe(0);
});
// typing into username and password fields (if the component is not unmounting) will clear out any error message
test('state maps to un/pw input value props', () => {
loginPage.setState({ username: 'un', password: 'pw' });
expect(loginPage.state().username).toBe('un');
expect(loginPage.state().password).toBe('pw');
findChildren();
expect(usernameInput.props().value).toBe('un');
expect(passwordInput.props().value).toBe('pw');
});
// when the submit Button is clicked, as long as it is not disabled state.loading is set to true
// api.login is called with param 1 username and param 2 password
// if api.login returns an error, the state.error should be set to LOGIN_ERROR_MESSAGE, if the error object returned has response.status set to 401.
// regardless of error or not, after api.login returns, state.loading should be set to false
test('updating un/pw clears out error', () => {
loginPage.setState({ error: 'error!' });
usernameInput.instance().props.onChange('uname');
expect(loginPage.state().username).toBe('uname');
expect(loginPage.state().error).toBe('');
loginPage.setState({ error: 'error!' });
passwordInput.instance().props.onChange('pword');
expect(loginPage.state().password).toBe('pword');
expect(loginPage.state().error).toBe('');
});
// if api.isAuthenticated mock returns true, Redirect to / should be rendered
});
test('submit calls api.login successfully', (done) => {
api.login = jest.fn().mockImplementation(() => {
const loginPromise = Promise.resolve({});
// wrapped in timeout so this .then happens after state.loading is set to
// false in the actual .then handler
//
// would be improved by using .finally but that's not available for
// some reason
setTimeout(() => {
loginPromise.then(() => {
expect(loginPage.state().loading).toBe(false);
done();
});
}, 50);
return loginPromise;
});
expect(loginPage.state().loading).toBe(false);
loginPage.setState({ username: 'unamee', password: 'pwordd', loading: true });
submitButton.simulate('submit');
expect(api.login).toHaveBeenCalledTimes(0);
loginPage.setState({ loading: false });
submitButton.simulate('submit');
expect(api.login).toHaveBeenCalledTimes(1);
expect(api.login).toHaveBeenCalledWith('unamee', 'pwordd');
expect(loginPage.state().loading).toBe(true);
});
test('submit calls api.login handles 401 error', (done) => {
api.login = jest.fn().mockImplementation(() => {
const err = {
response: { status: 401, message: 'problem' },
};
const loginPromise = Promise.reject(err);
// wrapped in timeout so this .then happens after state.loading is set to
// false in the actual .then handler
//
// would be improved by using .finally but that's not available for
// some reason
setTimeout(() => {
loginPromise.catch(() => {
expect(loginPage.state().loading).toBe(false);
expect(loginPage.state().error).toBe(LOGIN_ERROR_MESSAGE);
done();
});
}, 50);
return loginPromise;
});
expect(loginPage.state().loading).toBe(false);
loginPage.setState({ username: 'unamee', password: 'pwordd', loading: true });
submitButton.simulate('submit');
expect(api.login).toHaveBeenCalledTimes(0);
loginPage.setState({ loading: false });
expect(loginPage.state().error).toBe('');
submitButton.simulate('submit');
expect(api.login).toHaveBeenCalledTimes(1);
expect(api.login).toHaveBeenCalledWith('unamee', 'pwordd');
expect(loginPage.state().loading).toBe(true);
});
test('submit calls api.login handles 401 error', (done) => {
api.login = jest.fn().mockImplementation(() => {
const err = {
response: { status: 500, message: 'problem' },
};
const loginPromise = Promise.reject(err);
// wrapped in timeout so this .then happens after state.loading is set to
// false in the actual .then handler
//
// would be improved by using .finally but that's not available for
// some reason
setTimeout(() => {
loginPromise.catch(() => {
expect(loginPage.state().loading).toBe(false);
expect(loginPage.state().error).toBe('');
done();
});
}, 50);
return loginPromise;
});
expect(loginPage.state().loading).toBe(false);
loginPage.setState({ username: 'unamee', password: 'pwordd', loading: true });
submitButton.simulate('submit');
expect(api.login).toHaveBeenCalledTimes(0);
loginPage.setState({ loading: false });
expect(loginPage.state().error).toBe('');
submitButton.simulate('submit');
expect(api.login).toHaveBeenCalledTimes(1);
expect(api.login).toHaveBeenCalledWith('unamee', 'pwordd');
expect(loginPage.state().loading).toBe(true);
});
test('render Redirect to / when already authenticated', () => {
api.isAuthenticated = jest.fn();
api.isAuthenticated.mockReturnValue(true);
loginWrapper = shallow(<LoginPage />);
const redirectElem = loginWrapper.find('Redirect');
expect(redirectElem.length).toBe(1);
expect(redirectElem.props().to).toBe('/');
});
});

View File

@ -42,21 +42,24 @@ class LoginPage extends Component {
handlePasswordChange = value => this.safeSetState({ password: value, error: '' });
handleSubmit = event => {
const { username, password } = this.state;
const { username, password, loading } = this.state;
event.preventDefault();
this.safeSetState({ loading: true });
if (!loading) {
this.safeSetState({ loading: true });
api.login(username, password)
.catch(error => {
if (error.response.status === 401) {
this.safeSetState({ error: LOGIN_ERROR_MESSAGE });
}
})
.finally(() => {
this.safeSetState({ loading: false });
});
api.login(username, password)
.then(() => {
this.safeSetState({ loading: false });
})
.catch(error => {
this.safeSetState({ loading: false });
if (error.response.status === 401) {
this.safeSetState({ error: LOGIN_ERROR_MESSAGE });
}
});
}
}
render () {