mirror of
https://github.com/ansible/awx.git
synced 2026-01-27 08:31:28 -03:30
Convert Login.jsx to functional component in preparation for social auth integration
This commit is contained in:
parent
87e564026e
commit
8a8bfc5176
@ -1,5 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { act } from 'react-dom/test-utils';
|
||||||
import { mountWithContexts } from '../testUtils/enzymeHelpers';
|
import { mountWithContexts } from '../testUtils/enzymeHelpers';
|
||||||
|
|
||||||
import App from './App';
|
import App from './App';
|
||||||
@ -7,8 +7,11 @@ import App from './App';
|
|||||||
jest.mock('./api');
|
jest.mock('./api');
|
||||||
|
|
||||||
describe('<App />', () => {
|
describe('<App />', () => {
|
||||||
test('renders ok', () => {
|
test('renders ok', async () => {
|
||||||
const wrapper = mountWithContexts(<App />);
|
let wrapper;
|
||||||
|
await act(async () => {
|
||||||
|
wrapper = mountWithContexts(<App />);
|
||||||
|
});
|
||||||
expect(wrapper.length).toBe(1);
|
expect(wrapper.length).toBe(1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,12 +1,17 @@
|
|||||||
import React, { Component } from 'react';
|
import React, { useCallback, useEffect } from 'react';
|
||||||
import { Redirect, withRouter } from 'react-router-dom';
|
import { Redirect, withRouter } from 'react-router-dom';
|
||||||
import { withI18n } from '@lingui/react';
|
import { withI18n } from '@lingui/react';
|
||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
|
import { Formik } from 'formik';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { LoginForm, LoginPage as PFLoginPage } from '@patternfly/react-core';
|
import { LoginForm, LoginPage as PFLoginPage } from '@patternfly/react-core';
|
||||||
|
import useRequest, { useDismissableError } from '../../util/useRequest';
|
||||||
import { RootAPI } from '../../api';
|
import { RootAPI } from '../../api';
|
||||||
|
import { BrandName } from '../../variables';
|
||||||
|
import AlertModal from '../../components/AlertModal';
|
||||||
|
import ErrorDetail from '../../components/ErrorDetail';
|
||||||
|
|
||||||
const loginLogoSrc = '/static/media/logo-login.svg';
|
import brandLogo from './brand-logo.svg';
|
||||||
|
|
||||||
const LoginPage = styled(PFLoginPage)`
|
const LoginPage = styled(PFLoginPage)`
|
||||||
& .pf-c-brand {
|
& .pf-c-brand {
|
||||||
@ -14,149 +19,131 @@ const LoginPage = styled(PFLoginPage)`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
class AWXLogin extends Component {
|
function AWXLogin({ alt, i18n, isAuthenticated }) {
|
||||||
constructor(props) {
|
const {
|
||||||
super(props);
|
isLoading: isCustomLoginInfoLoading,
|
||||||
|
error: customLoginInfoError,
|
||||||
this.state = {
|
request: fetchCustomLoginInfo,
|
||||||
username: '',
|
result: { logo, loginInfo },
|
||||||
password: '',
|
} = useRequest(
|
||||||
hasAuthError: false,
|
useCallback(async () => {
|
||||||
hasValidationError: false,
|
const {
|
||||||
isAuthenticating: false,
|
data: { custom_logo, custom_login_info },
|
||||||
isLoading: true,
|
} = await RootAPI.read();
|
||||||
logo: null,
|
const logoSrc = custom_logo
|
||||||
loginInfo: null,
|
|
||||||
brandName: null,
|
|
||||||
};
|
|
||||||
|
|
||||||
this.handleChangeUsername = this.handleChangeUsername.bind(this);
|
|
||||||
this.handleChangePassword = this.handleChangePassword.bind(this);
|
|
||||||
this.handleLoginButtonClick = this.handleLoginButtonClick.bind(this);
|
|
||||||
this.loadCustomLoginInfo = this.loadCustomLoginInfo.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
async componentDidMount() {
|
|
||||||
await this.loadCustomLoginInfo();
|
|
||||||
}
|
|
||||||
|
|
||||||
async loadCustomLoginInfo() {
|
|
||||||
this.setState({ isLoading: true });
|
|
||||||
try {
|
|
||||||
const [
|
|
||||||
{
|
|
||||||
data: { custom_logo, custom_login_info },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
data: { BRAND_NAME },
|
|
||||||
},
|
|
||||||
] = await Promise.all([RootAPI.read(), RootAPI.readAssetVariables()]);
|
|
||||||
const logo = custom_logo
|
|
||||||
? `data:image/jpeg;${custom_logo}`
|
? `data:image/jpeg;${custom_logo}`
|
||||||
: loginLogoSrc;
|
: brandLogo;
|
||||||
this.setState({
|
return {
|
||||||
brandName: BRAND_NAME,
|
logo: logoSrc,
|
||||||
logo,
|
|
||||||
loginInfo: custom_login_info,
|
loginInfo: custom_login_info,
|
||||||
});
|
};
|
||||||
} catch (err) {
|
}, []),
|
||||||
this.setState({ brandName: 'AWX', logo: loginLogoSrc });
|
{ logo: brandLogo, loginInfo: null }
|
||||||
} finally {
|
);
|
||||||
this.setState({ isLoading: false });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async handleLoginButtonClick(event) {
|
const {
|
||||||
const { username, password, isAuthenticating } = this.state;
|
error: loginInfoError,
|
||||||
|
dismissError: dismissLoginInfoError,
|
||||||
|
} = useDismissableError(customLoginInfoError);
|
||||||
|
|
||||||
event.preventDefault();
|
useEffect(() => {
|
||||||
|
fetchCustomLoginInfo();
|
||||||
|
}, [fetchCustomLoginInfo]);
|
||||||
|
|
||||||
if (isAuthenticating) {
|
const {
|
||||||
return;
|
isLoading: isAuthenticating,
|
||||||
}
|
error: authenticationError,
|
||||||
|
request: authenticate,
|
||||||
this.setState({ hasAuthError: false, isAuthenticating: true });
|
} = useRequest(
|
||||||
try {
|
useCallback(async ({ username, password }) => {
|
||||||
// note: if authentication is successful, the appropriate cookie will be set automatically
|
|
||||||
// and isAuthenticated() (the source of truth) will start returning true.
|
|
||||||
await RootAPI.login(username, password);
|
await RootAPI.login(username, password);
|
||||||
} catch (err) {
|
}, [])
|
||||||
if (err && err.response && err.response.status === 401) {
|
);
|
||||||
this.setState({ hasValidationError: true });
|
|
||||||
} else {
|
const {
|
||||||
this.setState({ hasAuthError: true });
|
error: authError,
|
||||||
}
|
dismissError: dismissAuthError,
|
||||||
} finally {
|
} = useDismissableError(authenticationError);
|
||||||
this.setState({ isAuthenticating: false });
|
|
||||||
}
|
const handleSubmit = async values => {
|
||||||
|
dismissAuthError();
|
||||||
|
await authenticate(values);
|
||||||
|
};
|
||||||
|
|
||||||
|
const brandName = BrandName;
|
||||||
|
|
||||||
|
if (isCustomLoginInfoLoading) {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
handleChangeUsername(value) {
|
if (isAuthenticated(document.cookie)) {
|
||||||
this.setState({ username: value, hasValidationError: false });
|
return <Redirect to="/" />;
|
||||||
}
|
}
|
||||||
|
|
||||||
handleChangePassword(value) {
|
let helperText;
|
||||||
this.setState({ password: value, hasValidationError: false });
|
if (authError?.response?.status === 401) {
|
||||||
|
helperText = i18n._(t`Invalid username or password. Please try again.`);
|
||||||
|
} else {
|
||||||
|
helperText = i18n._(t`There was a problem signing in. Please try again.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
return (
|
||||||
const {
|
<LoginPage
|
||||||
brandName,
|
brandImgSrc={logo}
|
||||||
hasAuthError,
|
brandImgAlt={alt || brandName}
|
||||||
hasValidationError,
|
loginTitle={i18n._(t`Welcome to Ansible ${brandName}! Please Sign In.`)}
|
||||||
username,
|
textContent={loginInfo}
|
||||||
password,
|
>
|
||||||
isLoading,
|
<Formik
|
||||||
logo,
|
initialValues={{
|
||||||
loginInfo,
|
password: '',
|
||||||
} = this.state;
|
username: '',
|
||||||
const { alt, i18n, isAuthenticated } = this.props;
|
}}
|
||||||
|
onSubmit={handleSubmit}
|
||||||
if (isLoading) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isAuthenticated(document.cookie)) {
|
|
||||||
return <Redirect to="/" />;
|
|
||||||
}
|
|
||||||
|
|
||||||
let helperText;
|
|
||||||
if (hasValidationError) {
|
|
||||||
helperText = i18n._(t`Invalid username or password. Please try again.`);
|
|
||||||
} else {
|
|
||||||
helperText = i18n._(t`There was a problem signing in. Please try again.`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<LoginPage
|
|
||||||
brandImgSrc={logo}
|
|
||||||
brandImgAlt={alt || brandName}
|
|
||||||
loginTitle={
|
|
||||||
brandName
|
|
||||||
? i18n._(t`Welcome to Ansible ${brandName}! Please Sign In.`)
|
|
||||||
: ''
|
|
||||||
}
|
|
||||||
textContent={loginInfo}
|
|
||||||
>
|
>
|
||||||
<LoginForm
|
{formik => (
|
||||||
className={hasAuthError || hasValidationError ? 'pf-m-error' : ''}
|
<>
|
||||||
helperText={helperText}
|
<LoginForm
|
||||||
isValidPassword={!hasValidationError}
|
className={authError ? 'pf-m-error' : ''}
|
||||||
isValidUsername={!hasValidationError}
|
helperText={helperText}
|
||||||
loginButtonLabel={i18n._(t`Log In`)}
|
isLoginButtonDisabled={isAuthenticating}
|
||||||
onChangePassword={this.handleChangePassword}
|
isValidPassword={!authError}
|
||||||
onChangeUsername={this.handleChangeUsername}
|
isValidUsername={!authError}
|
||||||
onLoginButtonClick={this.handleLoginButtonClick}
|
loginButtonLabel={i18n._(t`Log In`)}
|
||||||
passwordLabel={i18n._(t`Password`)}
|
onChangePassword={val => {
|
||||||
passwordValue={password}
|
formik.setFieldValue('password', val);
|
||||||
showHelperText={hasAuthError || hasValidationError}
|
dismissAuthError();
|
||||||
usernameLabel={i18n._(t`Username`)}
|
}}
|
||||||
usernameValue={username}
|
onChangeUsername={val => {
|
||||||
/>
|
formik.setFieldValue('username', val);
|
||||||
</LoginPage>
|
dismissAuthError();
|
||||||
);
|
}}
|
||||||
}
|
onLoginButtonClick={formik.handleSubmit}
|
||||||
|
passwordLabel={i18n._(t`Password`)}
|
||||||
|
passwordValue={formik.values.password}
|
||||||
|
showHelperText={authError}
|
||||||
|
usernameLabel={i18n._(t`Username`)}
|
||||||
|
usernameValue={formik.values.username}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Formik>
|
||||||
|
{loginInfoError && (
|
||||||
|
<AlertModal
|
||||||
|
isOpen={loginInfoError}
|
||||||
|
variant="error"
|
||||||
|
title={i18n._(t`Error!`)}
|
||||||
|
onClose={dismissLoginInfoError}
|
||||||
|
>
|
||||||
|
{i18n._(
|
||||||
|
t`Failed to fetch custom login configuration settings. System defaults will be shown instead.`
|
||||||
|
)}
|
||||||
|
<ErrorDetail error={loginInfoError} />
|
||||||
|
</AlertModal>
|
||||||
|
)}
|
||||||
|
</LoginPage>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export { AWXLogin as _AWXLogin };
|
|
||||||
export default withI18n()(withRouter(AWXLogin));
|
export default withI18n()(withRouter(AWXLogin));
|
||||||
|
export { AWXLogin as _AWXLogin };
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { act } from 'react-dom/test-utils';
|
||||||
import { RootAPI } from '../../api';
|
import { RootAPI } from '../../api';
|
||||||
import {
|
import {
|
||||||
mountWithContexts,
|
mountWithContexts,
|
||||||
@ -55,11 +55,6 @@ describe('<Login />', () => {
|
|||||||
custom_logo: 'images/foo.jpg',
|
custom_logo: 'images/foo.jpg',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
RootAPI.readAssetVariables.mockResolvedValue({
|
|
||||||
data: {
|
|
||||||
BRAND_NAME: 'AWX',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
@ -67,27 +62,28 @@ describe('<Login />', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('initially renders without crashing', async done => {
|
test('initially renders without crashing', async done => {
|
||||||
const loginWrapper = mountWithContexts(
|
let wrapper;
|
||||||
<AWXLogin isAuthenticated={() => false} />
|
await act(async () => {
|
||||||
|
wrapper = mountWithContexts(<AWXLogin isAuthenticated={() => false} />);
|
||||||
|
});
|
||||||
|
const { usernameInput, passwordInput, submitButton } = await findChildren(
|
||||||
|
wrapper
|
||||||
);
|
);
|
||||||
const {
|
|
||||||
awxLogin,
|
|
||||||
usernameInput,
|
|
||||||
passwordInput,
|
|
||||||
submitButton,
|
|
||||||
} = await findChildren(loginWrapper);
|
|
||||||
expect(usernameInput.props().value).toBe('');
|
expect(usernameInput.props().value).toBe('');
|
||||||
expect(passwordInput.props().value).toBe('');
|
expect(passwordInput.props().value).toBe('');
|
||||||
expect(awxLogin.state('hasValidationError')).toBe(false);
|
|
||||||
expect(submitButton.props().isDisabled).toBe(false);
|
expect(submitButton.props().isDisabled).toBe(false);
|
||||||
|
expect(wrapper.find('AlertModal').length).toBe(0);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('custom logo renders Brand component with correct src and alt', async done => {
|
test('custom logo renders Brand component with correct src and alt', async done => {
|
||||||
const loginWrapper = mountWithContexts(
|
let wrapper;
|
||||||
<AWXLogin alt="Foo Application" isAuthenticated={() => false} />
|
await act(async () => {
|
||||||
);
|
wrapper = mountWithContexts(
|
||||||
const { loginHeaderLogo } = await findChildren(loginWrapper);
|
<AWXLogin alt="Foo Application" isAuthenticated={() => false} />
|
||||||
|
);
|
||||||
|
});
|
||||||
|
const { loginHeaderLogo } = await findChildren(wrapper);
|
||||||
const { alt, src } = loginHeaderLogo.props();
|
const { alt, src } = loginHeaderLogo.props();
|
||||||
expect([alt, src]).toEqual([
|
expect([alt, src]).toEqual([
|
||||||
'Foo Application',
|
'Foo Application',
|
||||||
@ -98,195 +94,172 @@ describe('<Login />', () => {
|
|||||||
|
|
||||||
test('default logo renders Brand component with correct src and alt', async done => {
|
test('default logo renders Brand component with correct src and alt', async done => {
|
||||||
RootAPI.read.mockResolvedValue({ data: {} });
|
RootAPI.read.mockResolvedValue({ data: {} });
|
||||||
const loginWrapper = mountWithContexts(
|
let wrapper;
|
||||||
<AWXLogin isAuthenticated={() => false} />
|
await act(async () => {
|
||||||
);
|
wrapper = mountWithContexts(<AWXLogin isAuthenticated={() => false} />);
|
||||||
const { loginHeaderLogo } = await findChildren(loginWrapper);
|
});
|
||||||
|
const { loginHeaderLogo } = await findChildren(wrapper);
|
||||||
const { alt, src } = loginHeaderLogo.props();
|
const { alt, src } = loginHeaderLogo.props();
|
||||||
expect(alt).toEqual('AWX');
|
expect([alt, src]).toEqual(['AWX', 'brand-logo.svg']);
|
||||||
expect(src).toContain('logo-login.svg');
|
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('default logo renders on data initialization error', async done => {
|
test('data initialization error is properly handled', async done => {
|
||||||
RootAPI.read.mockRejectedValueOnce({ response: { status: 500 } });
|
RootAPI.read.mockRejectedValueOnce(
|
||||||
const loginWrapper = mountWithContexts(
|
new Error({
|
||||||
<AWXLogin isAuthenticated={() => false} />
|
response: {
|
||||||
|
config: {
|
||||||
|
method: 'get',
|
||||||
|
url: '/api/v2',
|
||||||
|
},
|
||||||
|
data: 'An error occurred',
|
||||||
|
status: 500,
|
||||||
|
},
|
||||||
|
})
|
||||||
);
|
);
|
||||||
const { loginHeaderLogo } = await findChildren(loginWrapper);
|
let wrapper;
|
||||||
|
await act(async () => {
|
||||||
|
wrapper = mountWithContexts(<AWXLogin isAuthenticated={() => false} />);
|
||||||
|
});
|
||||||
|
const { loginHeaderLogo } = await findChildren(wrapper);
|
||||||
const { alt, src } = loginHeaderLogo.props();
|
const { alt, src } = loginHeaderLogo.props();
|
||||||
expect(alt).toEqual('AWX');
|
expect([alt, src]).toEqual(['AWX', 'brand-logo.svg']);
|
||||||
expect(src).toContain('logo-login.svg');
|
expect(wrapper.find('AlertModal').length).toBe(1);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('state maps to un/pw input value props', async done => {
|
test('state maps to un/pw input value props', async done => {
|
||||||
const loginWrapper = mountWithContexts(
|
let wrapper;
|
||||||
<AWXLogin isAuthenticated={() => false} />
|
await act(async () => {
|
||||||
);
|
wrapper = mountWithContexts(<AWXLogin isAuthenticated={() => false} />);
|
||||||
const { usernameInput, passwordInput } = await findChildren(loginWrapper);
|
});
|
||||||
usernameInput.props().onChange({ currentTarget: { value: 'un' } });
|
await waitForElement(wrapper, 'LoginForm', el => el.length === 1);
|
||||||
passwordInput.props().onChange({ currentTarget: { value: 'pw' } });
|
await act(async () => {
|
||||||
await waitForElement(
|
wrapper.find('TextInputBase#pf-login-username-id').prop('onChange')('un');
|
||||||
loginWrapper,
|
wrapper.find('TextInputBase#pf-login-password-id').prop('onChange')('pw');
|
||||||
'AWXLogin',
|
});
|
||||||
el => el.state('username') === 'un'
|
wrapper.update();
|
||||||
);
|
expect(
|
||||||
await waitForElement(
|
wrapper.find('TextInputBase#pf-login-username-id').prop('value')
|
||||||
loginWrapper,
|
).toEqual('un');
|
||||||
'AWXLogin',
|
expect(
|
||||||
el => el.state('password') === 'pw'
|
wrapper.find('TextInputBase#pf-login-password-id').prop('value')
|
||||||
);
|
).toEqual('pw');
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('handles input validation errors and clears on input value change', async done => {
|
test('handles input validation errors and clears on input value change', async done => {
|
||||||
const formError = '.pf-c-form__helper-text.pf-m-error';
|
RootAPI.login.mockRejectedValueOnce(
|
||||||
const loginWrapper = mountWithContexts(
|
new Error({
|
||||||
<AWXLogin isAuthenticated={() => false} />
|
response: {
|
||||||
);
|
config: {
|
||||||
const { usernameInput, passwordInput, submitButton } = await findChildren(
|
method: 'post',
|
||||||
loginWrapper
|
url: '/api/login/',
|
||||||
|
},
|
||||||
|
data: 'An error occurred',
|
||||||
|
status: 401,
|
||||||
|
},
|
||||||
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
RootAPI.login.mockRejectedValueOnce({ response: { status: 401 } });
|
let wrapper;
|
||||||
usernameInput.props().onChange({ currentTarget: { value: 'invalid' } });
|
await act(async () => {
|
||||||
passwordInput.props().onChange({ currentTarget: { value: 'invalid' } });
|
wrapper = mountWithContexts(<AWXLogin isAuthenticated={() => false} />);
|
||||||
submitButton.simulate('click');
|
});
|
||||||
await waitForElement(
|
await waitForElement(wrapper, 'LoginForm', el => el.length === 1);
|
||||||
loginWrapper,
|
|
||||||
'AWXLogin',
|
|
||||||
el => el.state('username') === 'invalid'
|
|
||||||
);
|
|
||||||
await waitForElement(
|
|
||||||
loginWrapper,
|
|
||||||
'AWXLogin',
|
|
||||||
el => el.state('password') === 'invalid'
|
|
||||||
);
|
|
||||||
await waitForElement(
|
|
||||||
loginWrapper,
|
|
||||||
'AWXLogin',
|
|
||||||
el => el.state('hasValidationError') === true
|
|
||||||
);
|
|
||||||
await waitForElement(loginWrapper, formError, el => el.length === 1);
|
|
||||||
|
|
||||||
usernameInput.props().onChange({ currentTarget: { value: 'dsarif' } });
|
expect(
|
||||||
passwordInput.props().onChange({ currentTarget: { value: 'freneticpny' } });
|
wrapper.find('TextInputBase#pf-login-username-id').prop('value')
|
||||||
await waitForElement(
|
).toEqual('');
|
||||||
loginWrapper,
|
expect(
|
||||||
'AWXLogin',
|
wrapper.find('TextInputBase#pf-login-password-id').prop('value')
|
||||||
el => el.state('username') === 'dsarif'
|
).toEqual('');
|
||||||
);
|
expect(wrapper.find('FormHelperText').prop('isHidden')).toEqual(true);
|
||||||
await waitForElement(
|
|
||||||
loginWrapper,
|
|
||||||
'AWXLogin',
|
|
||||||
el => el.state('password') === 'freneticpny'
|
|
||||||
);
|
|
||||||
await waitForElement(
|
|
||||||
loginWrapper,
|
|
||||||
'AWXLogin',
|
|
||||||
el => el.state('hasValidationError') === false
|
|
||||||
);
|
|
||||||
await waitForElement(loginWrapper, formError, el => el.length === 0);
|
|
||||||
|
|
||||||
done();
|
await act(async () => {
|
||||||
});
|
wrapper.find('TextInputBase#pf-login-username-id').prop('onChange')('un');
|
||||||
|
wrapper.find('TextInputBase#pf-login-password-id').prop('onChange')('pw');
|
||||||
|
});
|
||||||
|
wrapper.update();
|
||||||
|
|
||||||
test('handles other errors and clears on resubmit', async done => {
|
expect(
|
||||||
const loginWrapper = mountWithContexts(
|
wrapper.find('TextInputBase#pf-login-username-id').prop('value')
|
||||||
<AWXLogin isAuthenticated={() => false} />
|
).toEqual('un');
|
||||||
);
|
expect(
|
||||||
const { usernameInput, passwordInput, submitButton } = await findChildren(
|
wrapper.find('TextInputBase#pf-login-password-id').prop('value')
|
||||||
loginWrapper
|
).toEqual('pw');
|
||||||
);
|
|
||||||
|
|
||||||
RootAPI.login.mockRejectedValueOnce({ response: { status: 500 } });
|
await act(async () => {
|
||||||
submitButton.simulate('click');
|
wrapper.find('Button[type="submit"]').invoke('onClick')();
|
||||||
await waitForElement(
|
});
|
||||||
loginWrapper,
|
wrapper.update();
|
||||||
'AWXLogin',
|
|
||||||
el => el.state('hasAuthError') === true
|
|
||||||
);
|
|
||||||
|
|
||||||
usernameInput.props().onChange({ currentTarget: { value: 'sgrimes' } });
|
expect(wrapper.find('FormHelperText').prop('isHidden')).toEqual(false);
|
||||||
passwordInput.props().onChange({ currentTarget: { value: 'ovid' } });
|
expect(
|
||||||
await waitForElement(
|
wrapper.find('TextInput#pf-login-username-id').prop('validated')
|
||||||
loginWrapper,
|
).toEqual('error');
|
||||||
'AWXLogin',
|
expect(
|
||||||
el => el.state('username') === 'sgrimes'
|
wrapper.find('TextInput#pf-login-password-id').prop('validated')
|
||||||
);
|
).toEqual('error');
|
||||||
await waitForElement(
|
|
||||||
loginWrapper,
|
|
||||||
'AWXLogin',
|
|
||||||
el => el.state('password') === 'ovid'
|
|
||||||
);
|
|
||||||
await waitForElement(
|
|
||||||
loginWrapper,
|
|
||||||
'AWXLogin',
|
|
||||||
el => el.state('hasAuthError') === true
|
|
||||||
);
|
|
||||||
|
|
||||||
submitButton.simulate('click');
|
await act(async () => {
|
||||||
await waitForElement(
|
wrapper.find('TextInputBase#pf-login-username-id').prop('onChange')(
|
||||||
loginWrapper,
|
'foo'
|
||||||
'AWXLogin',
|
);
|
||||||
el => el.state('hasAuthError') === false
|
wrapper.find('TextInputBase#pf-login-password-id').prop('onChange')(
|
||||||
);
|
'bar'
|
||||||
done();
|
);
|
||||||
});
|
});
|
||||||
|
wrapper.update();
|
||||||
|
|
||||||
test('no login requests are made when already authenticating', async done => {
|
expect(
|
||||||
const loginWrapper = mountWithContexts(
|
wrapper.find('TextInputBase#pf-login-username-id').prop('value')
|
||||||
<AWXLogin isAuthenticated={() => false} />
|
).toEqual('foo');
|
||||||
);
|
expect(
|
||||||
const { awxLogin, submitButton } = await findChildren(loginWrapper);
|
wrapper.find('TextInputBase#pf-login-password-id').prop('value')
|
||||||
|
).toEqual('bar');
|
||||||
awxLogin.setState({ isAuthenticating: true });
|
expect(wrapper.find('FormHelperText').prop('isHidden')).toEqual(true);
|
||||||
submitButton.simulate('click');
|
expect(
|
||||||
submitButton.simulate('click');
|
wrapper.find('TextInput#pf-login-username-id').prop('validated')
|
||||||
expect(RootAPI.login).toHaveBeenCalledTimes(0);
|
).toEqual('default');
|
||||||
|
expect(
|
||||||
awxLogin.setState({ isAuthenticating: false });
|
wrapper.find('TextInput#pf-login-password-id').prop('validated')
|
||||||
submitButton.simulate('click');
|
).toEqual('default');
|
||||||
submitButton.simulate('click');
|
|
||||||
expect(RootAPI.login).toHaveBeenCalledTimes(1);
|
|
||||||
|
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('submit calls api.login successfully', async done => {
|
test('submit calls api.login successfully', async done => {
|
||||||
const loginWrapper = mountWithContexts(
|
let wrapper;
|
||||||
<AWXLogin isAuthenticated={() => false} />
|
await act(async () => {
|
||||||
);
|
wrapper = mountWithContexts(<AWXLogin isAuthenticated={() => false} />);
|
||||||
const { usernameInput, passwordInput, submitButton } = await findChildren(
|
});
|
||||||
loginWrapper
|
await waitForElement(wrapper, 'LoginForm', el => el.length === 1);
|
||||||
);
|
|
||||||
|
await act(async () => {
|
||||||
|
wrapper.find('TextInputBase#pf-login-username-id').prop('onChange')('un');
|
||||||
|
wrapper.find('TextInputBase#pf-login-password-id').prop('onChange')('pw');
|
||||||
|
});
|
||||||
|
wrapper.update();
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
wrapper.find('Button[type="submit"]').invoke('onClick')();
|
||||||
|
});
|
||||||
|
wrapper.update();
|
||||||
|
|
||||||
usernameInput.props().onChange({ currentTarget: { value: 'gthorpe' } });
|
|
||||||
passwordInput.props().onChange({ currentTarget: { value: 'hydro' } });
|
|
||||||
submitButton.simulate('click');
|
|
||||||
await waitForElement(
|
|
||||||
loginWrapper,
|
|
||||||
'AWXLogin',
|
|
||||||
el => el.state('isAuthenticating') === true
|
|
||||||
);
|
|
||||||
await waitForElement(
|
|
||||||
loginWrapper,
|
|
||||||
'AWXLogin',
|
|
||||||
el => el.state('isAuthenticating') === false
|
|
||||||
);
|
|
||||||
expect(RootAPI.login).toHaveBeenCalledTimes(1);
|
expect(RootAPI.login).toHaveBeenCalledTimes(1);
|
||||||
expect(RootAPI.login).toHaveBeenCalledWith('gthorpe', 'hydro');
|
expect(RootAPI.login).toHaveBeenCalledWith('un', 'pw');
|
||||||
|
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('render Redirect to / when already authenticated', async done => {
|
test('render Redirect to / when already authenticated', async done => {
|
||||||
const loginWrapper = mountWithContexts(
|
let wrapper;
|
||||||
<AWXLogin isAuthenticated={() => true} />
|
await act(async () => {
|
||||||
);
|
wrapper = mountWithContexts(<AWXLogin isAuthenticated={() => true} />);
|
||||||
await waitForElement(loginWrapper, 'Redirect', el => el.length === 1);
|
});
|
||||||
await waitForElement(loginWrapper, 'Redirect', el => el.props().to === '/');
|
await waitForElement(wrapper, 'Redirect', el => el.length === 1);
|
||||||
|
await waitForElement(wrapper, 'Redirect', el => el.props().to === '/');
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user