mirror of
https://github.com/ansible/awx.git
synced 2026-01-14 11:20:39 -03:30
Adds test coverage for cred plugin prompt
This commit is contained in:
parent
df069f3874
commit
5367bc4d3b
@ -72,7 +72,7 @@ function CredentialPluginField(props) {
|
||||
)}
|
||||
{showPluginWizard && (
|
||||
<CredentialPluginPrompt
|
||||
initialValues={field.value}
|
||||
initialValues={typeof field.value === 'object' ? field.value : {}}
|
||||
onClose={() => setShowPluginWizard(false)}
|
||||
onSubmit={val => {
|
||||
val.touched = true;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { func } from 'prop-types';
|
||||
import { func, shape } from 'prop-types';
|
||||
import { Formik, useField } from 'formik';
|
||||
import { withI18n } from '@lingui/react';
|
||||
import { t } from '@lingui/macro';
|
||||
@ -14,6 +14,7 @@ function CredentialPluginWizard({ i18n, handleSubmit, onClose }) {
|
||||
id: 1,
|
||||
name: i18n._(t`Credential`),
|
||||
component: <CredentialsStep />,
|
||||
enableNext: !!selectedCredential.value,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
@ -58,8 +59,11 @@ function CredentialPluginPrompt({ i18n, onClose, onSubmit, initialValues }) {
|
||||
CredentialPluginPrompt.propTypes = {
|
||||
onClose: func.isRequired,
|
||||
onSubmit: func.isRequired,
|
||||
initialValues: shape({}),
|
||||
};
|
||||
|
||||
CredentialPluginPrompt.defaultProps = {};
|
||||
CredentialPluginPrompt.defaultProps = {
|
||||
initialValues: {},
|
||||
};
|
||||
|
||||
export default withI18n()(CredentialPluginPrompt);
|
||||
|
||||
@ -1,18 +1,228 @@
|
||||
import React from 'react';
|
||||
import { mountWithContexts } from '../../../../../../testUtils/enzymeHelpers';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import {
|
||||
mountWithContexts,
|
||||
waitForElement,
|
||||
} from '../../../../../../testUtils/enzymeHelpers';
|
||||
import { CredentialsAPI, CredentialTypesAPI } from '../../../../../api';
|
||||
import selectedCredential from '../../data.cyberArkCredential.json';
|
||||
import azureVaultCredential from '../../data.azureVaultCredential.json';
|
||||
import hashiCorpCredential from '../../data.hashiCorpCredential.json';
|
||||
import CredentialPluginPrompt from './CredentialPluginPrompt';
|
||||
|
||||
jest.mock('../../../../../api/models/Credentials');
|
||||
jest.mock('../../../../../api/models/CredentialTypes');
|
||||
|
||||
CredentialsAPI.read.mockResolvedValue({
|
||||
data: {
|
||||
count: 3,
|
||||
results: [selectedCredential, azureVaultCredential, hashiCorpCredential],
|
||||
},
|
||||
});
|
||||
|
||||
CredentialTypesAPI.readDetail.mockResolvedValue({
|
||||
data: {
|
||||
id: 20,
|
||||
type: 'credential_type',
|
||||
url: '/api/v2/credential_types/20/',
|
||||
related: {
|
||||
named_url:
|
||||
'/api/v2/credential_types/CyberArk Conjur Secret Lookup+external/',
|
||||
credentials: '/api/v2/credential_types/20/credentials/',
|
||||
activity_stream: '/api/v2/credential_types/20/activity_stream/',
|
||||
},
|
||||
summary_fields: { user_capabilities: { edit: false, delete: false } },
|
||||
created: '2020-05-18T21:53:35.398260Z',
|
||||
modified: '2020-05-18T21:54:05.451444Z',
|
||||
name: 'CyberArk Conjur Secret Lookup',
|
||||
description: '',
|
||||
kind: 'external',
|
||||
namespace: 'conjur',
|
||||
managed_by_tower: true,
|
||||
inputs: {
|
||||
fields: [
|
||||
{ id: 'url', label: 'Conjur URL', type: 'string', format: 'url' },
|
||||
{ id: 'api_key', label: 'API Key', type: 'string', secret: true },
|
||||
{ id: 'account', label: 'Account', type: 'string' },
|
||||
{ id: 'username', label: 'Username', type: 'string' },
|
||||
{
|
||||
id: 'cacert',
|
||||
label: 'Public Key Certificate',
|
||||
type: 'string',
|
||||
multiline: true,
|
||||
},
|
||||
],
|
||||
metadata: [
|
||||
{
|
||||
id: 'secret_path',
|
||||
label: 'Secret Identifier',
|
||||
type: 'string',
|
||||
help_text: 'The identifier for the secret e.g., /some/identifier',
|
||||
},
|
||||
{
|
||||
id: 'secret_version',
|
||||
label: 'Secret Version',
|
||||
type: 'string',
|
||||
help_text:
|
||||
'Used to specify a specific secret version (if left empty, the latest version will be used).',
|
||||
},
|
||||
],
|
||||
required: ['url', 'api_key', 'account', 'username'],
|
||||
},
|
||||
injectors: {},
|
||||
},
|
||||
});
|
||||
|
||||
describe('<CredentialPluginPrompt />', () => {
|
||||
let wrapper;
|
||||
beforeAll(() => {
|
||||
wrapper = mountWithContexts(
|
||||
<CredentialPluginPrompt onClose={jest.fn()} onSubmit={jest.fn()} />
|
||||
);
|
||||
describe('Plugin not configured', () => {
|
||||
let wrapper;
|
||||
const onClose = jest.fn();
|
||||
const onSubmit = jest.fn();
|
||||
beforeAll(async () => {
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(
|
||||
<CredentialPluginPrompt onClose={onClose} onSubmit={onSubmit} />
|
||||
);
|
||||
});
|
||||
});
|
||||
afterAll(() => {
|
||||
wrapper.unmount();
|
||||
});
|
||||
test('should render Wizard with all steps', async () => {
|
||||
const wizard = await waitForElement(wrapper, 'Wizard');
|
||||
const steps = wizard.prop('steps');
|
||||
|
||||
expect(steps).toHaveLength(2);
|
||||
expect(steps[0].name).toEqual('Credential');
|
||||
expect(steps[1].name).toEqual('Metadata');
|
||||
});
|
||||
test('credentials step renders correctly', () => {
|
||||
expect(wrapper.find('CredentialsStep').length).toBe(1);
|
||||
expect(wrapper.find('DataListItem').length).toBe(3);
|
||||
expect(
|
||||
wrapper.find('Radio').filterWhere(radio => radio.isChecked).length
|
||||
).toBe(0);
|
||||
});
|
||||
test('next button disabled until credential selected', () => {
|
||||
expect(wrapper.find('Button[children="Next"]').prop('isDisabled')).toBe(
|
||||
true
|
||||
);
|
||||
});
|
||||
test('clicking cancel button calls correct function', () => {
|
||||
wrapper.find('Button[children="Cancel"]').simulate('click');
|
||||
expect(onClose).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
test('clicking credential row enables next button', async () => {
|
||||
await act(async () => {
|
||||
wrapper
|
||||
.find('Radio')
|
||||
.at(0)
|
||||
.invoke('onChange')(true);
|
||||
});
|
||||
wrapper.update();
|
||||
expect(
|
||||
wrapper
|
||||
.find('Radio')
|
||||
.at(0)
|
||||
.prop('isChecked')
|
||||
).toBe(true);
|
||||
expect(wrapper.find('Button[children="Next"]').prop('isDisabled')).toBe(
|
||||
false
|
||||
);
|
||||
});
|
||||
test('clicking next button shows metatdata step', async () => {
|
||||
await act(async () => {
|
||||
wrapper.find('Button[children="Next"]').simulate('click');
|
||||
});
|
||||
wrapper.update();
|
||||
expect(wrapper.find('MetadataStep').length).toBe(1);
|
||||
expect(wrapper.find('FormField').length).toBe(2);
|
||||
});
|
||||
test('submit button calls correct function with parameters', async () => {
|
||||
await act(async () => {
|
||||
wrapper.find('input#credential-secret_path').simulate('change', {
|
||||
target: { value: '/foo/bar', name: 'secret_path' },
|
||||
});
|
||||
});
|
||||
await act(async () => {
|
||||
wrapper.find('input#credential-secret_version').simulate('change', {
|
||||
target: { value: '9000', name: 'secret_version' },
|
||||
});
|
||||
});
|
||||
await act(async () => {
|
||||
wrapper.find('Button[children="OK"]').simulate('click');
|
||||
});
|
||||
// expect(wrapper.debug()).toBe(false);
|
||||
// wrapper.find('Button[children="OK"]').simulate('click');
|
||||
expect(onSubmit).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
credential: selectedCredential,
|
||||
secret_path: '/foo/bar',
|
||||
secret_version: '9000',
|
||||
}),
|
||||
expect.anything()
|
||||
);
|
||||
});
|
||||
});
|
||||
afterAll(() => {
|
||||
wrapper.unmount();
|
||||
});
|
||||
test('renders the expected content', () => {
|
||||
expect(wrapper).toHaveLength(1);
|
||||
|
||||
describe('Plugin already configured', () => {
|
||||
let wrapper;
|
||||
const onClose = jest.fn();
|
||||
const onSubmit = jest.fn();
|
||||
beforeAll(async () => {
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(
|
||||
<CredentialPluginPrompt
|
||||
onClose={onClose}
|
||||
onSubmit={onSubmit}
|
||||
initialValues={{
|
||||
credential: selectedCredential,
|
||||
inputs: {
|
||||
secret_path: '/foo/bar',
|
||||
secret_version: '9000',
|
||||
},
|
||||
}}
|
||||
/>
|
||||
);
|
||||
});
|
||||
});
|
||||
afterAll(() => {
|
||||
wrapper.unmount();
|
||||
});
|
||||
test('should render Wizard with all steps', async () => {
|
||||
const wizard = await waitForElement(wrapper, 'Wizard');
|
||||
const steps = wizard.prop('steps');
|
||||
|
||||
expect(steps).toHaveLength(2);
|
||||
expect(steps[0].name).toEqual('Credential');
|
||||
expect(steps[1].name).toEqual('Metadata');
|
||||
});
|
||||
test('credentials step renders correctly', () => {
|
||||
expect(wrapper.find('CredentialsStep').length).toBe(1);
|
||||
expect(wrapper.find('DataListItem').length).toBe(3);
|
||||
expect(
|
||||
wrapper
|
||||
.find('Radio')
|
||||
.at(0)
|
||||
.prop('isChecked')
|
||||
).toBe(true);
|
||||
expect(wrapper.find('Button[children="Next"]').prop('isDisabled')).toBe(
|
||||
false
|
||||
);
|
||||
});
|
||||
test('metadata step renders correctly', async () => {
|
||||
await act(async () => {
|
||||
wrapper.find('Button[children="Next"]').simulate('click');
|
||||
});
|
||||
wrapper.update();
|
||||
expect(wrapper.find('MetadataStep').length).toBe(1);
|
||||
expect(wrapper.find('FormField').length).toBe(2);
|
||||
expect(wrapper.find('input#credential-secret_path').prop('value')).toBe(
|
||||
'/foo/bar'
|
||||
);
|
||||
expect(
|
||||
wrapper.find('input#credential-secret_version').prop('value')
|
||||
).toBe('9000');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -66,7 +66,7 @@ function MetadataStep({ i18n }) {
|
||||
}, [fetchMetadataOptions]);
|
||||
|
||||
const testMetadata = () => {
|
||||
// todo: implement
|
||||
// https://github.com/ansible/awx/issues/7126
|
||||
};
|
||||
|
||||
if (isLoading) {
|
||||
|
||||
@ -11,7 +11,6 @@ import { Credential } from '../../../../types';
|
||||
const SelectedCredential = styled.div`
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-top: 10px;
|
||||
background-color: white;
|
||||
border-bottom-color: var(--pf-global--BorderColor--200);
|
||||
`;
|
||||
@ -20,6 +19,10 @@ const SpacedCredentialChip = styled(CredentialChip)`
|
||||
margin: 5px 8px;
|
||||
`;
|
||||
|
||||
const PluginHelpText = styled.p`
|
||||
margin-top: 5px;
|
||||
`;
|
||||
|
||||
function CredentialPluginSelected({
|
||||
i18n,
|
||||
credential,
|
||||
@ -28,12 +31,6 @@ function CredentialPluginSelected({
|
||||
}) {
|
||||
return (
|
||||
<>
|
||||
<p>
|
||||
<Trans>
|
||||
This field will be retrieved from an external secret management system
|
||||
using the following credential:
|
||||
</Trans>
|
||||
</p>
|
||||
<SelectedCredential>
|
||||
<SpacedCredentialChip onClick={onClearPlugin} credential={credential} />
|
||||
<Tooltip
|
||||
@ -49,6 +46,12 @@ function CredentialPluginSelected({
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</SelectedCredential>
|
||||
<PluginHelpText>
|
||||
<Trans>
|
||||
This field will be retrieved from an external secret management system
|
||||
using the specified credential.
|
||||
</Trans>
|
||||
</PluginHelpText>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@ -0,0 +1,85 @@
|
||||
{
|
||||
"id": 12,
|
||||
"type": "credential",
|
||||
"url": "/api/v2/credentials/12/",
|
||||
"related": {
|
||||
"created_by": "/api/v2/users/1/",
|
||||
"modified_by": "/api/v2/users/1/",
|
||||
"activity_stream": "/api/v2/credentials/12/activity_stream/",
|
||||
"access_list": "/api/v2/credentials/12/access_list/",
|
||||
"object_roles": "/api/v2/credentials/12/object_roles/",
|
||||
"owner_users": "/api/v2/credentials/12/owner_users/",
|
||||
"owner_teams": "/api/v2/credentials/12/owner_teams/",
|
||||
"copy": "/api/v2/credentials/12/copy/",
|
||||
"input_sources": "/api/v2/credentials/12/input_sources/",
|
||||
"credential_type": "/api/v2/credential_types/19/",
|
||||
"user": "/api/v2/users/1/"
|
||||
},
|
||||
"summary_fields": {
|
||||
"credential_type": {
|
||||
"id": 19,
|
||||
"name": "Microsoft Azure Key Vault",
|
||||
"description": ""
|
||||
},
|
||||
"created_by": {
|
||||
"id": 1,
|
||||
"username": "admin",
|
||||
"first_name": "",
|
||||
"last_name": ""
|
||||
},
|
||||
"modified_by": {
|
||||
"id": 1,
|
||||
"username": "admin",
|
||||
"first_name": "",
|
||||
"last_name": ""
|
||||
},
|
||||
"object_roles": {
|
||||
"admin_role": {
|
||||
"description": "Can manage all aspects of the credential",
|
||||
"name": "Admin",
|
||||
"id": 60
|
||||
},
|
||||
"use_role": {
|
||||
"description": "Can use the credential in a job template",
|
||||
"name": "Use",
|
||||
"id": 61
|
||||
},
|
||||
"read_role": {
|
||||
"description": "May view settings for the credential",
|
||||
"name": "Read",
|
||||
"id": 62
|
||||
}
|
||||
},
|
||||
"user_capabilities": {
|
||||
"edit": true,
|
||||
"delete": true,
|
||||
"copy": true,
|
||||
"use": true
|
||||
},
|
||||
"owners": [
|
||||
{
|
||||
"id": 1,
|
||||
"type": "user",
|
||||
"name": "admin",
|
||||
"description": " ",
|
||||
"url": "/api/v2/users/1/"
|
||||
}
|
||||
]
|
||||
},
|
||||
"created": "2020-05-26T14:54:45.612847Z",
|
||||
"modified": "2020-05-26T14:54:45.612861Z",
|
||||
"name": "Microsoft Azure Key Vault",
|
||||
"description": "",
|
||||
"organization": null,
|
||||
"credential_type": 19,
|
||||
"inputs": {
|
||||
"url": "https://localhost",
|
||||
"client": "foo",
|
||||
"secret": "$encrypted$",
|
||||
"tenant": "9000",
|
||||
"cloud_name": "AzureCloud"
|
||||
},
|
||||
"kind": "azure_kv",
|
||||
"cloud": false,
|
||||
"kubernetes": false
|
||||
}
|
||||
@ -0,0 +1,82 @@
|
||||
{
|
||||
"id": 11,
|
||||
"type": "credential",
|
||||
"url": "/api/v2/credentials/11/",
|
||||
"related": {
|
||||
"created_by": "/api/v2/users/1/",
|
||||
"modified_by": "/api/v2/users/1/",
|
||||
"activity_stream": "/api/v2/credentials/11/activity_stream/",
|
||||
"access_list": "/api/v2/credentials/11/access_list/",
|
||||
"object_roles": "/api/v2/credentials/11/object_roles/",
|
||||
"owner_users": "/api/v2/credentials/11/owner_users/",
|
||||
"owner_teams": "/api/v2/credentials/11/owner_teams/",
|
||||
"copy": "/api/v2/credentials/11/copy/",
|
||||
"input_sources": "/api/v2/credentials/11/input_sources/",
|
||||
"credential_type": "/api/v2/credential_types/21/",
|
||||
"user": "/api/v2/users/1/"
|
||||
},
|
||||
"summary_fields": {
|
||||
"credential_type": {
|
||||
"id": 21,
|
||||
"name": "HashiCorp Vault Secret Lookup",
|
||||
"description": ""
|
||||
},
|
||||
"created_by": {
|
||||
"id": 1,
|
||||
"username": "admin",
|
||||
"first_name": "",
|
||||
"last_name": ""
|
||||
},
|
||||
"modified_by": {
|
||||
"id": 1,
|
||||
"username": "admin",
|
||||
"first_name": "",
|
||||
"last_name": ""
|
||||
},
|
||||
"object_roles": {
|
||||
"admin_role": {
|
||||
"description": "Can manage all aspects of the credential",
|
||||
"name": "Admin",
|
||||
"id": 57
|
||||
},
|
||||
"use_role": {
|
||||
"description": "Can use the credential in a job template",
|
||||
"name": "Use",
|
||||
"id": 58
|
||||
},
|
||||
"read_role": {
|
||||
"description": "May view settings for the credential",
|
||||
"name": "Read",
|
||||
"id": 59
|
||||
}
|
||||
},
|
||||
"user_capabilities": {
|
||||
"edit": true,
|
||||
"delete": true,
|
||||
"copy": true,
|
||||
"use": true
|
||||
},
|
||||
"owners": [
|
||||
{
|
||||
"id": 1,
|
||||
"type": "user",
|
||||
"name": "admin",
|
||||
"description": " ",
|
||||
"url": "/api/v2/users/1/"
|
||||
}
|
||||
]
|
||||
},
|
||||
"created": "2020-05-26T14:54:00.674404Z",
|
||||
"modified": "2020-05-26T14:54:00.674418Z",
|
||||
"name": "HashiCorp Vault Secret Lookup",
|
||||
"description": "",
|
||||
"organization": null,
|
||||
"credential_type": 21,
|
||||
"inputs": {
|
||||
"url": "https://localhost",
|
||||
"api_version": "v1"
|
||||
},
|
||||
"kind": "hashivault_kv",
|
||||
"cloud": false,
|
||||
"kubernetes": false
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user