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