diff --git a/__tests__/pages/Login.jsx b/__tests__/pages/Login.jsx index 63675a98ab..4bac8b5af0 100644 --- a/__tests__/pages/Login.jsx +++ b/__tests__/pages/Login.jsx @@ -2,33 +2,31 @@ import React from 'react'; import { MemoryRouter } from 'react-router-dom'; import { mount, shallow } from 'enzyme'; import { asyncFlush } from '../../jest.setup'; -import LoginPage from '../../src/pages/Login'; +import AtLogin from '../../src/pages/Login'; import api from '../../src/api'; -const LOGIN_ERROR_MESSAGE = 'Invalid username or password. Please try again.'; - -describe('', () => { +describe('', () => { let loginWrapper; + let atLogin; let loginPage; let loginForm; let usernameInput; let passwordInput; - let errorTextArea; let submitButton; - let defaultLogo; + let loginHeaderLogo; const findChildren = () => { + atLogin = loginWrapper.find('AtLogin'); 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'); + loginForm = loginWrapper.find('LoginForm'); + usernameInput = loginWrapper.find('input#pf-login-username-id'); + passwordInput = loginWrapper.find('input#pf-login-password-id'); submitButton = loginWrapper.find('Button[type="submit"]'); - defaultLogo = loginWrapper.find('TowerLogo'); + loginHeaderLogo = loginWrapper.find('LoginHeaderBrand Brand'); }; beforeEach(() => { - loginWrapper = mount(); + loginWrapper = mount(); findChildren(); }); @@ -38,62 +36,78 @@ describe('', () => { test('initially renders without crashing', () => { expect(loginWrapper.length).toBe(1); + expect(loginPage.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(atLogin.state().isValidPassword).toBe(true); expect(submitButton.length).toBe(1); expect(submitButton.props().isDisabled).toBe(false); - expect(defaultLogo.length).toBe(1); + expect(loginHeaderLogo.length).toBe(1); }); - test('custom logo renders Brand component', () => { - loginWrapper = mount(); + test('custom logo renders Brand component with correct src and alt', () => { + loginWrapper = mount(); findChildren(); - expect(defaultLogo.length).toBe(0); + expect(loginHeaderLogo.length).toBe(1); + expect(loginHeaderLogo.props().src).toBe('data:image/jpeg;images/foo.jpg'); + expect(loginHeaderLogo.props().alt).toBe('Foo Application'); + }); + + test('default logo renders Brand component with correct src and alt', () => { + loginWrapper = mount(); + findChildren(); + expect(loginHeaderLogo.length).toBe(1); + expect(loginHeaderLogo.props().src).toBe('tower-logo-header.svg'); + expect(loginHeaderLogo.props().alt).toBe('Ansible Tower'); }); 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'); + atLogin.setState({ username: 'un', password: 'pw' }); + expect(atLogin.state().username).toBe('un'); + expect(atLogin.state().password).toBe('pw'); findChildren(); expect(usernameInput.props().value).toBe('un'); expect(passwordInput.props().value).toBe('pw'); }); 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(''); + atLogin.setState({ isValidPassword: false }); + expect(loginWrapper.find('.pf-c-form__helper-text.pf-m-error').length).toBe(1); + usernameInput.instance().value = 'uname'; + usernameInput.simulate('change'); + expect(atLogin.state().username).toBe('uname'); + expect(atLogin.state().isValidPassword).toBe(true); + expect(loginWrapper.find('.pf-c-form__helper-text.pf-m-error').length).toBe(0); + atLogin.setState({ isValidPassword: false }); + expect(loginWrapper.find('.pf-c-form__helper-text.pf-m-error').length).toBe(1); + passwordInput.instance().value = 'pword'; + passwordInput.simulate('change'); + expect(atLogin.state().password).toBe('pword'); + expect(atLogin.state().isValidPassword).toBe(true); + expect(loginWrapper.find('.pf-c-form__helper-text.pf-m-error').length).toBe(0); }); test('api.login not called when loading', () => { api.login = jest.fn().mockImplementation(() => Promise.resolve({})); - expect(loginPage.state().loading).toBe(false); - loginPage.setState({ loading: true }); - submitButton.simulate('submit'); + expect(atLogin.state().loading).toBe(false); + atLogin.setState({ loading: true }); + submitButton.simulate('click'); expect(api.login).toHaveBeenCalledTimes(0); }); test('submit calls api.login successfully', async () => { api.login = jest.fn().mockImplementation(() => Promise.resolve({})); - expect(loginPage.state().loading).toBe(false); - loginPage.setState({ username: 'unamee', password: 'pwordd' }); - submitButton.simulate('submit'); + expect(atLogin.state().loading).toBe(false); + atLogin.setState({ username: 'unamee', password: 'pwordd' }); + submitButton.simulate('click'); expect(api.login).toHaveBeenCalledTimes(1); expect(api.login).toHaveBeenCalledWith('unamee', 'pwordd'); - expect(loginPage.state().loading).toBe(true); + expect(atLogin.state().loading).toBe(true); await asyncFlush(); - expect(loginPage.state().loading).toBe(false); + expect(atLogin.state().loading).toBe(false); }); test('submit calls api.login handles 401 error', async () => { @@ -102,15 +116,16 @@ describe('', () => { err.response = { status: 401, message: 'problem' }; return Promise.reject(err); }); - expect(loginPage.state().loading).toBe(false); - loginPage.setState({ username: 'unamee', password: 'pwordd' }); - submitButton.simulate('submit'); + expect(atLogin.state().loading).toBe(false); + expect(atLogin.state().isValidPassword).toBe(true); + atLogin.setState({ username: 'unamee', password: 'pwordd' }); + submitButton.simulate('click'); expect(api.login).toHaveBeenCalledTimes(1); expect(api.login).toHaveBeenCalledWith('unamee', 'pwordd'); - expect(loginPage.state().loading).toBe(true); + expect(atLogin.state().loading).toBe(true); await asyncFlush(); - expect(loginPage.state().error).toBe(LOGIN_ERROR_MESSAGE); - expect(loginPage.state().loading).toBe(false); + expect(atLogin.state().isValidPassword).toBe(false); + expect(atLogin.state().loading).toBe(false); }); test('submit calls api.login handles non-401 error', async () => { @@ -119,21 +134,20 @@ describe('', () => { err.response = { status: 500, message: 'problem' }; return Promise.reject(err); }); - expect(loginPage.state().loading).toBe(false); - loginPage.setState({ username: 'unamee', password: 'pwordd' }); - submitButton.simulate('submit'); + expect(atLogin.state().loading).toBe(false); + atLogin.setState({ username: 'unamee', password: 'pwordd' }); + submitButton.simulate('click'); expect(api.login).toHaveBeenCalledTimes(1); expect(api.login).toHaveBeenCalledWith('unamee', 'pwordd'); - expect(loginPage.state().loading).toBe(true); + expect(atLogin.state().loading).toBe(true); await asyncFlush(); - expect(loginPage.state().error).toBe(''); - expect(loginPage.state().loading).toBe(false); + expect(atLogin.state().loading).toBe(false); }); test('render Redirect to / when already authenticated', () => { api.isAuthenticated = jest.fn(); api.isAuthenticated.mockReturnValue(true); - loginWrapper = shallow(); + loginWrapper = shallow(); const redirectElem = loginWrapper.find('Redirect'); expect(redirectElem.length).toBe(1); expect(redirectElem.props().to).toBe('/'); diff --git a/src/pages/Login.jsx b/src/pages/Login.jsx index c806ef24b4..128b1d4be2 100644 --- a/src/pages/Login.jsx +++ b/src/pages/Login.jsx @@ -1,33 +1,22 @@ import React, { Component } from 'react'; import { Redirect } from 'react-router-dom'; import { - Brand, - Button, - Level, - LevelItem, - Login, - LoginBox, - LoginBoxHeader, - LoginBoxBody, - LoginFooter, - LoginHeaderBrand, - TextInput, + LoginForm, + LoginPage, } from '@patternfly/react-core'; -import TowerLogo from '../components/TowerLogo'; +import towerLogo from '../../images/tower-logo-header.svg'; import api from '../api'; -const LOGIN_ERROR_MESSAGE = 'Invalid username or password. Please try again.'; - -class LoginPage extends Component { +class AtLogin extends Component { constructor (props) { super(props); this.state = { username: '', password: '', - error: '', - loading: false, + isValidPassword: true, + loading: false }; } @@ -37,9 +26,9 @@ class LoginPage extends Component { safeSetState = obj => !this.unmounting && this.setState(obj); - handleUsernameChange = value => this.safeSetState({ username: value, error: '' }); + handleUsernameChange = value => this.safeSetState({ username: value, isValidPassword: true }); - handlePasswordChange = value => this.safeSetState({ password: value, error: '' }); + handlePasswordChange = value => this.safeSetState({ password: value, isValidPassword: true }); handleSubmit = async event => { const { username, password, loading } = this.state; @@ -53,7 +42,7 @@ class LoginPage extends Component { await api.login(username, password); } catch (error) { if (error.response.status === 401) { - this.safeSetState({ error: LOGIN_ERROR_MESSAGE }); + this.safeSetState({ isValidPassword: false }); } } finally { this.safeSetState({ loading: false }); @@ -62,75 +51,39 @@ class LoginPage extends Component { } render () { - const { username, password, loading, error } = this.state; - const { logo, loginInfo } = this.props; + const { username, password, isValidPassword } = this.state; + const { logo, alt } = this.props; + const logoSrc = logo ? `data:image/jpeg;${logo}` : towerLogo; + const logoAlt = alt || 'Ansible Tower'; + const LOGIN_ERROR_MESSAGE = 'Invalid username or password. Please try again.'; + const LOGIN_TITLE = 'Welcome to Ansible Tower! Please Sign In.'; + const LOGIN_USERNAME = 'Username'; + const LOGIN_PASSWORD = 'Password'; if (api.isAuthenticated()) { return (); } return ( - - {logo ? : } - - )} - footer={{ loginInfo }} + - - - Welcome to Ansible Tower! Please Sign In. - - -
-
- - -
-
- - -
- - -

- { error } -

-
- - - -
-
-
-
-
+ +
); } } -export default LoginPage; +export default AtLogin;