Files
awx/awx/ui_next/src/screens/Login/Login.test.jsx
2021-05-05 14:10:28 -04:00

391 lines
12 KiB
JavaScript

import React from 'react';
import { act } from 'react-dom/test-utils';
import { AuthAPI, RootAPI } from '../../api';
import {
mountWithContexts,
waitForElement,
} from '../../../testUtils/enzymeHelpers';
import AWXLogin from './Login';
jest.mock('../../api');
RootAPI.readAssetVariables.mockResolvedValue({
data: {
BRAND_NAME: 'AWX',
},
});
AuthAPI.read.mockResolvedValue({
data: {},
});
describe('<Login />', () => {
async function findChildren(wrapper) {
const [
awxLogin,
loginForm,
usernameInput,
passwordInput,
submitButton,
loginHeaderLogo,
] = await Promise.all([
waitForElement(wrapper, 'AWXLogin', el => el.length === 1),
waitForElement(wrapper, 'LoginForm', el => el.length === 1),
waitForElement(
wrapper,
'input#pf-login-username-id',
el => el.length === 1
),
waitForElement(
wrapper,
'input#pf-login-password-id',
el => el.length === 1
),
waitForElement(wrapper, 'Button[type="submit"]', el => el.length === 1),
waitForElement(wrapper, 'img', el => el.length === 1),
]);
return {
awxLogin,
loginForm,
usernameInput,
passwordInput,
submitButton,
loginHeaderLogo,
};
}
beforeEach(() => {
RootAPI.readAssetVariables.mockResolvedValue({
data: {
BRAND_NAME: 'AWX',
},
});
AuthAPI.read.mockResolvedValue({
data: {},
});
RootAPI.read.mockResolvedValue({
data: {
custom_login_info:
'<div id="custom-button" onmouseover="alert()">TEST</div>',
custom_logo: 'images/foo.jpg',
},
});
});
afterEach(() => {
jest.clearAllMocks();
});
test('initially renders without crashing', async () => {
let wrapper;
await act(async () => {
wrapper = mountWithContexts(<AWXLogin isAuthenticated={() => false} />);
});
const { usernameInput, passwordInput, submitButton } = await findChildren(
wrapper
);
expect(usernameInput.props().value).toBe('');
expect(passwordInput.props().value).toBe('');
expect(submitButton.props().isDisabled).toBe(false);
expect(wrapper.find('AlertModal').length).toBe(0);
expect(wrapper.find('LoginMainHeader').prop('subtitle')).toBe(
'Please log in'
);
expect(wrapper.find('LoginMainHeader').prop('title')).toBe(
'Welcome to Ansible AWX!'
);
});
test('custom logo renders Brand component with correct src and alt', async () => {
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',
'data:image/jpeg;images/foo.jpg',
]);
});
test('default logo renders Brand component with correct src and alt', async () => {
RootAPI.read.mockResolvedValue({ data: {} });
let wrapper;
await act(async () => {
wrapper = mountWithContexts(<AWXLogin isAuthenticated={() => false} />);
});
const { loginHeaderLogo } = await findChildren(wrapper);
const { alt, src } = loginHeaderLogo.props();
expect([alt, src]).toEqual(['AWX', '/static/media/logo-login.svg']);
});
test('custom login info handled correctly', async () => {
let wrapper;
await act(async () => {
wrapper = mountWithContexts(<AWXLogin isAuthenticated={() => false} />);
});
await findChildren(wrapper);
expect(wrapper.find('footer').html()).toContain('<div>TEST</div>');
});
test('data initialization error is properly handled', async () => {
RootAPI.read.mockRejectedValueOnce(
new Error({
response: {
config: {
method: 'get',
url: '/api/v2',
},
data: 'An error occurred',
status: 500,
},
})
);
let wrapper;
await act(async () => {
wrapper = mountWithContexts(<AWXLogin isAuthenticated={() => false} />);
});
const { loginHeaderLogo } = await findChildren(wrapper);
const { alt, src } = loginHeaderLogo.props();
expect([alt, src]).toEqual([null, '/static/media/logo-login.svg']);
expect(wrapper.find('AlertModal').length).toBe(1);
});
test('state maps to un/pw input value props', async () => {
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');
});
test('handles input validation errors and clears on input value change', async () => {
RootAPI.login.mockRejectedValueOnce(
new Error({
response: {
config: {
method: 'post',
url: '/api/login/',
},
data: 'An error occurred',
status: 401,
},
})
);
let wrapper;
await act(async () => {
wrapper = mountWithContexts(<AWXLogin isAuthenticated={() => false} />);
});
await waitForElement(wrapper, 'LoginForm', el => el.length === 1);
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);
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');
await act(async () => {
wrapper.find('Button[type="submit"]').invoke('onClick')();
});
wrapper.update();
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');
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();
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');
});
test('submit calls api.login successfully', async () => {
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();
expect(RootAPI.login).toHaveBeenCalledTimes(1);
expect(RootAPI.login).toHaveBeenCalledWith('un', 'pw');
});
test('render Redirect to / when already authenticated', async () => {
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 === '/');
});
test('GitHub auth buttons shown', async () => {
AuthAPI.read.mockResolvedValue({
data: {
github: {
login_url: '/sso/login/github/',
complete_url: 'https://localhost:8043/sso/complete/github/',
},
'github-org': {
login_url: '/sso/login/github-org/',
complete_url: 'https://localhost:8043/sso/complete/github-org/',
},
'github-team': {
login_url: '/sso/login/github-team/',
complete_url: 'https://localhost:8043/sso/complete/github-team/',
},
},
});
let wrapper;
await act(async () => {
wrapper = mountWithContexts(<AWXLogin isAuthenticated={() => false} />);
});
wrapper.update();
expect(wrapper.find('GithubIcon').length).toBe(3);
expect(wrapper.find('AzureIcon').length).toBe(0);
expect(wrapper.find('GoogleIcon').length).toBe(0);
expect(wrapper.find('UserCircleIcon').length).toBe(0);
});
test('Google auth button shown', async () => {
AuthAPI.read.mockResolvedValue({
data: {
'google-oauth2': {
login_url: '/sso/login/google-oauth2/',
complete_url: 'https://localhost:8043/sso/complete/google-oauth2/',
},
},
});
let wrapper;
await act(async () => {
wrapper = mountWithContexts(<AWXLogin isAuthenticated={() => false} />);
});
wrapper.update();
expect(wrapper.find('GithubIcon').length).toBe(0);
expect(wrapper.find('AzureIcon').length).toBe(0);
expect(wrapper.find('GoogleIcon').length).toBe(1);
expect(wrapper.find('UserCircleIcon').length).toBe(0);
});
test('Azure AD auth button shown', async () => {
AuthAPI.read.mockResolvedValue({
data: {
'azuread-oauth2': {
login_url: '/sso/login/azuread-oauth2/',
complete_url: 'https://localhost:8043/sso/complete/azuread-oauth2/',
},
},
});
let wrapper;
await act(async () => {
wrapper = mountWithContexts(<AWXLogin isAuthenticated={() => false} />);
});
wrapper.update();
expect(wrapper.find('GithubIcon').length).toBe(0);
expect(wrapper.find('AzureIcon').length).toBe(1);
expect(wrapper.find('GoogleIcon').length).toBe(0);
expect(wrapper.find('UserCircleIcon').length).toBe(0);
});
test('SAML auth buttons shown', async () => {
AuthAPI.read.mockResolvedValue({
data: {
saml: {
login_url: '/sso/login/saml/',
complete_url: 'https://localhost:8043/sso/complete/saml/',
metadata_url: '/sso/metadata/saml/',
},
'saml:onelogin': {
login_url: '/sso/login/saml/?idp=onelogin',
complete_url: 'https://localhost:8043/sso/complete/saml/',
metadata_url: '/sso/metadata/saml/',
},
'saml:someotheridp': {
login_url: '/sso/login/saml/?idp=someotheridp',
complete_url: 'https://localhost:8043/sso/complete/saml/',
metadata_url: '/sso/metadata/saml/',
},
},
});
let wrapper;
await act(async () => {
wrapper = mountWithContexts(<AWXLogin isAuthenticated={() => false} />);
});
wrapper.update();
expect(wrapper.find('GithubIcon').length).toBe(0);
expect(wrapper.find('AzureIcon').length).toBe(0);
expect(wrapper.find('GoogleIcon').length).toBe(0);
expect(wrapper.find('UserCircleIcon').length).toBe(3);
});
});