mirror of
https://github.com/ansible/awx.git
synced 2026-05-14 21:07:39 -02:30
Merge pull request #7730 from mabashian/7339-test-button
Adds support for a Test button on the credential form when the credential type is 'external' Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
This commit is contained in:
@@ -27,6 +27,10 @@ class CredentialTypes extends Base {
|
|||||||
.concat(nextResults)
|
.concat(nextResults)
|
||||||
.filter(type => acceptableKinds.includes(type.kind));
|
.filter(type => acceptableKinds.includes(type.kind));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test(id, data) {
|
||||||
|
return this.http.post(`${this.baseUrl}${id}/test/`, data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default CredentialTypes;
|
export default CredentialTypes;
|
||||||
|
|||||||
@@ -25,6 +25,10 @@ class Credentials extends Base {
|
|||||||
params,
|
params,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test(id, data) {
|
||||||
|
return this.http.post(`${this.baseUrl}${id}/test/`, data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Credentials;
|
export default Credentials;
|
||||||
|
|||||||
@@ -1,16 +1,19 @@
|
|||||||
import React from 'react';
|
import React, { useState } from 'react';
|
||||||
import { Formik, useField } from 'formik';
|
import { Formik, useField } from 'formik';
|
||||||
import { withI18n } from '@lingui/react';
|
import { withI18n } from '@lingui/react';
|
||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import { arrayOf, func, object, shape } from 'prop-types';
|
import { arrayOf, func, object, shape } from 'prop-types';
|
||||||
import { Form, FormGroup } from '@patternfly/react-core';
|
import { ActionGroup, Button, Form, FormGroup } from '@patternfly/react-core';
|
||||||
import FormField, { FormSubmitError } from '../../../components/FormField';
|
import FormField, { FormSubmitError } from '../../../components/FormField';
|
||||||
import FormActionGroup from '../../../components/FormActionGroup/FormActionGroup';
|
import {
|
||||||
|
FormColumnLayout,
|
||||||
|
FormFullWidthLayout,
|
||||||
|
} from '../../../components/FormLayout';
|
||||||
import AnsibleSelect from '../../../components/AnsibleSelect';
|
import AnsibleSelect from '../../../components/AnsibleSelect';
|
||||||
import { required } from '../../../util/validators';
|
import { required } from '../../../util/validators';
|
||||||
import OrganizationLookup from '../../../components/Lookup/OrganizationLookup';
|
import OrganizationLookup from '../../../components/Lookup/OrganizationLookup';
|
||||||
import { FormColumnLayout } from '../../../components/FormLayout';
|
|
||||||
import TypeInputsSubForm from './TypeInputsSubForm';
|
import TypeInputsSubForm from './TypeInputsSubForm';
|
||||||
|
import ExternalTestModal from './ExternalTestModal';
|
||||||
|
|
||||||
function CredentialFormFields({
|
function CredentialFormFields({
|
||||||
i18n,
|
i18n,
|
||||||
@@ -139,6 +142,7 @@ function CredentialFormFields({
|
|||||||
}
|
}
|
||||||
|
|
||||||
function CredentialForm({
|
function CredentialForm({
|
||||||
|
i18n,
|
||||||
credential = {},
|
credential = {},
|
||||||
credentialTypes,
|
credentialTypes,
|
||||||
inputSources,
|
inputSources,
|
||||||
@@ -147,6 +151,7 @@ function CredentialForm({
|
|||||||
submitError,
|
submitError,
|
||||||
...rest
|
...rest
|
||||||
}) {
|
}) {
|
||||||
|
const [showExternalTestModal, setShowExternalTestModal] = useState(false);
|
||||||
const initialValues = {
|
const initialValues = {
|
||||||
name: credential.name || '',
|
name: credential.name || '',
|
||||||
description: credential.description || '',
|
description: credential.description || '',
|
||||||
@@ -205,21 +210,61 @@ function CredentialForm({
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{formik => (
|
{formik => (
|
||||||
<Form autoComplete="off" onSubmit={formik.handleSubmit}>
|
<>
|
||||||
<FormColumnLayout>
|
<Form autoComplete="off" onSubmit={formik.handleSubmit}>
|
||||||
<CredentialFormFields
|
<FormColumnLayout>
|
||||||
formik={formik}
|
<CredentialFormFields
|
||||||
initialValues={initialValues}
|
formik={formik}
|
||||||
credentialTypes={credentialTypes}
|
initialValues={initialValues}
|
||||||
{...rest}
|
credentialTypes={credentialTypes}
|
||||||
|
i18n={i18n}
|
||||||
|
{...rest}
|
||||||
|
/>
|
||||||
|
<FormSubmitError error={submitError} />
|
||||||
|
<FormFullWidthLayout>
|
||||||
|
<ActionGroup>
|
||||||
|
<Button
|
||||||
|
aria-label={i18n._(t`Save`)}
|
||||||
|
variant="primary"
|
||||||
|
type="button"
|
||||||
|
onClick={formik.handleSubmit}
|
||||||
|
>
|
||||||
|
{i18n._(t`Save`)}
|
||||||
|
</Button>
|
||||||
|
{formik?.values?.credential_type &&
|
||||||
|
credentialTypes[formik.values.credential_type]?.kind ===
|
||||||
|
'external' && (
|
||||||
|
<Button
|
||||||
|
aria-label={i18n._(t`Test`)}
|
||||||
|
variant="secondary"
|
||||||
|
type="button"
|
||||||
|
onClick={() => setShowExternalTestModal(true)}
|
||||||
|
isDisabled={!formik.isValid}
|
||||||
|
>
|
||||||
|
{i18n._(t`Test`)}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
<Button
|
||||||
|
aria-label={i18n._(t`Cancel`)}
|
||||||
|
variant="secondary"
|
||||||
|
type="button"
|
||||||
|
onClick={onCancel}
|
||||||
|
>
|
||||||
|
{i18n._(t`Cancel`)}
|
||||||
|
</Button>
|
||||||
|
</ActionGroup>
|
||||||
|
</FormFullWidthLayout>
|
||||||
|
</FormColumnLayout>
|
||||||
|
</Form>
|
||||||
|
{showExternalTestModal && (
|
||||||
|
<ExternalTestModal
|
||||||
|
credential={credential}
|
||||||
|
credentialType={credentialTypes[formik.values.credential_type]}
|
||||||
|
credentialFormValues={formik.values}
|
||||||
|
onClose={() => setShowExternalTestModal(false)}
|
||||||
/>
|
/>
|
||||||
<FormSubmitError error={submitError} />
|
)}
|
||||||
<FormActionGroup
|
</>
|
||||||
onCancel={onCancel}
|
|
||||||
onSubmit={formik.handleSubmit}
|
|
||||||
/>
|
|
||||||
</FormColumnLayout>
|
|
||||||
</Form>
|
|
||||||
)}
|
)}
|
||||||
</Formik>
|
</Formik>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -99,6 +99,9 @@ describe('<CredentialForm />', () => {
|
|||||||
test('should display form fields on add properly', async () => {
|
test('should display form fields on add properly', async () => {
|
||||||
addFieldExpects();
|
addFieldExpects();
|
||||||
});
|
});
|
||||||
|
test('should hide Test button initially', () => {
|
||||||
|
expect(wrapper.find('Button[children="Test"]').length).toBe(0);
|
||||||
|
});
|
||||||
test('should update form values', async () => {
|
test('should update form values', async () => {
|
||||||
// name and description change
|
// name and description change
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
@@ -221,6 +224,18 @@ describe('<CredentialForm />', () => {
|
|||||||
'There was an error parsing the file. Please check the file formatting and try again.'
|
'There was an error parsing the file. Please check the file formatting and try again.'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
test('should show Test button when external credential type is selected', async () => {
|
||||||
|
await act(async () => {
|
||||||
|
await wrapper
|
||||||
|
.find('AnsibleSelect[id="credential_type"]')
|
||||||
|
.invoke('onChange')(null, 21);
|
||||||
|
});
|
||||||
|
wrapper.update();
|
||||||
|
expect(wrapper.find('Button[children="Test"]').length).toBe(1);
|
||||||
|
expect(wrapper.find('Button[children="Test"]').props().isDisabled).toBe(
|
||||||
|
true
|
||||||
|
);
|
||||||
|
});
|
||||||
test('should call handleCancel when Cancel button is clicked', async () => {
|
test('should call handleCancel when Cancel button is clicked', async () => {
|
||||||
expect(onCancel).not.toHaveBeenCalled();
|
expect(onCancel).not.toHaveBeenCalled();
|
||||||
wrapper.find('button[aria-label="Cancel"]').invoke('onClick')();
|
wrapper.find('button[aria-label="Cancel"]').invoke('onClick')();
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ function CredentialField({ credentialType, fieldOptions, i18n }) {
|
|||||||
>
|
>
|
||||||
<AnsibleSelect
|
<AnsibleSelect
|
||||||
{...subFormField}
|
{...subFormField}
|
||||||
id="credential_type"
|
id={`credential-${fieldOptions.id}`}
|
||||||
data={selectOptions}
|
data={selectOptions}
|
||||||
onChange={(event, value) => {
|
onChange={(event, value) => {
|
||||||
helpers.setValue(value);
|
helpers.setValue(value);
|
||||||
|
|||||||
@@ -0,0 +1,91 @@
|
|||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import { withI18n } from '@lingui/react';
|
||||||
|
import { t } from '@lingui/macro';
|
||||||
|
import { string, shape } from 'prop-types';
|
||||||
|
import {
|
||||||
|
Alert,
|
||||||
|
AlertActionCloseButton,
|
||||||
|
AlertGroup,
|
||||||
|
} from '@patternfly/react-core';
|
||||||
|
|
||||||
|
function CredentialPluginTestAlert({
|
||||||
|
i18n,
|
||||||
|
credentialName,
|
||||||
|
successResponse,
|
||||||
|
errorResponse,
|
||||||
|
}) {
|
||||||
|
const [testMessage, setTestMessage] = useState('');
|
||||||
|
const [testVariant, setTestVariant] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (errorResponse) {
|
||||||
|
if (errorResponse?.response?.data?.inputs) {
|
||||||
|
if (errorResponse.response.data.inputs.startsWith('HTTP')) {
|
||||||
|
const [
|
||||||
|
errorCode,
|
||||||
|
errorStr,
|
||||||
|
] = errorResponse.response.data.inputs.split('\n');
|
||||||
|
try {
|
||||||
|
const errorJSON = JSON.parse(errorStr);
|
||||||
|
setTestMessage(
|
||||||
|
`${errorCode}${
|
||||||
|
errorJSON?.errors[0] ? `: ${errorJSON.errors[0]}` : ''
|
||||||
|
}`
|
||||||
|
);
|
||||||
|
} catch {
|
||||||
|
setTestMessage(errorResponse.response.data.inputs);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setTestMessage(errorResponse.response.data.inputs);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setTestMessage(
|
||||||
|
i18n._(
|
||||||
|
t`Something went wrong with the request to test this credential and metadata.`
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
setTestVariant('danger');
|
||||||
|
} else if (successResponse) {
|
||||||
|
setTestMessage(i18n._(t`Test passed`));
|
||||||
|
setTestVariant('success');
|
||||||
|
}
|
||||||
|
}, [i18n, successResponse, errorResponse]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AlertGroup isToast>
|
||||||
|
{testMessage && testVariant && (
|
||||||
|
<Alert
|
||||||
|
actionClose={
|
||||||
|
<AlertActionCloseButton
|
||||||
|
onClose={() => {
|
||||||
|
setTestMessage(null);
|
||||||
|
setTestVariant(null);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
title={
|
||||||
|
<>
|
||||||
|
<b id="credential-plugin-test-name">{credentialName}</b>
|
||||||
|
<p id="credential-plugin-test-message">{testMessage}</p>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
variant={testVariant}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</AlertGroup>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
CredentialPluginTestAlert.propTypes = {
|
||||||
|
credentialName: string.isRequired,
|
||||||
|
successResponse: shape({}),
|
||||||
|
errorResponse: shape({}),
|
||||||
|
};
|
||||||
|
|
||||||
|
CredentialPluginTestAlert.defaultProps = {
|
||||||
|
successResponse: null,
|
||||||
|
errorResponse: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default withI18n()(CredentialPluginTestAlert);
|
||||||
@@ -1,2 +1,3 @@
|
|||||||
export { default as CredentialPluginSelected } from './CredentialPluginSelected';
|
export { default as CredentialPluginSelected } from './CredentialPluginSelected';
|
||||||
export { default as CredentialPluginField } from './CredentialPluginField';
|
export { default as CredentialPluginField } from './CredentialPluginField';
|
||||||
|
export { default as CredentialPluginTestAlert } from './CredentialPluginTestAlert';
|
||||||
|
|||||||
198
awx/ui_next/src/screens/Credential/shared/ExternalTestModal.jsx
Normal file
198
awx/ui_next/src/screens/Credential/shared/ExternalTestModal.jsx
Normal file
@@ -0,0 +1,198 @@
|
|||||||
|
import React, { useCallback } from 'react';
|
||||||
|
import { withI18n } from '@lingui/react';
|
||||||
|
import { t } from '@lingui/macro';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import { func, shape } from 'prop-types';
|
||||||
|
import { Formik } from 'formik';
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Form,
|
||||||
|
FormGroup,
|
||||||
|
Modal,
|
||||||
|
Tooltip,
|
||||||
|
} from '@patternfly/react-core';
|
||||||
|
import { QuestionCircleIcon as PFQuestionCircleIcon } from '@patternfly/react-icons';
|
||||||
|
import { CredentialsAPI, CredentialTypesAPI } from '../../../api';
|
||||||
|
import AnsibleSelect from '../../../components/AnsibleSelect';
|
||||||
|
import FormField from '../../../components/FormField';
|
||||||
|
import { FormFullWidthLayout } from '../../../components/FormLayout';
|
||||||
|
import { required } from '../../../util/validators';
|
||||||
|
import useRequest from '../../../util/useRequest';
|
||||||
|
import { CredentialPluginTestAlert } from './CredentialFormFields/CredentialPlugins';
|
||||||
|
|
||||||
|
const QuestionCircleIcon = styled(PFQuestionCircleIcon)`
|
||||||
|
margin-left: 10px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
function ExternalTestModal({
|
||||||
|
i18n,
|
||||||
|
credential,
|
||||||
|
credentialType,
|
||||||
|
credentialFormValues,
|
||||||
|
onClose,
|
||||||
|
}) {
|
||||||
|
const {
|
||||||
|
result: testPluginSuccess,
|
||||||
|
error: testPluginError,
|
||||||
|
request: testPluginMetadata,
|
||||||
|
} = useRequest(
|
||||||
|
useCallback(
|
||||||
|
async values => {
|
||||||
|
const payload = {
|
||||||
|
inputs: credentialType.inputs.fields.reduce(
|
||||||
|
(filteredInputs, field) => {
|
||||||
|
filteredInputs[field.id] = credentialFormValues.inputs[field.id];
|
||||||
|
return filteredInputs;
|
||||||
|
},
|
||||||
|
{}
|
||||||
|
),
|
||||||
|
metadata: values,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (credential && credential.credential_type === credentialType.id) {
|
||||||
|
return CredentialsAPI.test(credential.id, payload);
|
||||||
|
}
|
||||||
|
return CredentialTypesAPI.test(credentialType.id, payload);
|
||||||
|
},
|
||||||
|
[
|
||||||
|
credential,
|
||||||
|
credentialType.id,
|
||||||
|
credentialType.inputs.fields,
|
||||||
|
credentialFormValues.inputs,
|
||||||
|
]
|
||||||
|
),
|
||||||
|
null
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleTest = async values => {
|
||||||
|
await testPluginMetadata(values);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Formik
|
||||||
|
initialValues={credentialType.inputs.metadata.reduce(
|
||||||
|
(initialValues, field) => {
|
||||||
|
if (field.type === 'string' && field.choices) {
|
||||||
|
initialValues[field.id] = field.default || field.choices[0];
|
||||||
|
} else {
|
||||||
|
initialValues[field.id] = '';
|
||||||
|
}
|
||||||
|
return initialValues;
|
||||||
|
},
|
||||||
|
{}
|
||||||
|
)}
|
||||||
|
onSubmit={values => handleTest(values)}
|
||||||
|
>
|
||||||
|
{({ handleSubmit, setFieldValue }) => (
|
||||||
|
<Modal
|
||||||
|
title={i18n._(t`Test External Credential`)}
|
||||||
|
isOpen
|
||||||
|
onClose={() => onClose()}
|
||||||
|
variant="small"
|
||||||
|
actions={[
|
||||||
|
<Button
|
||||||
|
id="run-external-credential-test"
|
||||||
|
key="confirm"
|
||||||
|
variant="primary"
|
||||||
|
onClick={() => handleSubmit()}
|
||||||
|
>
|
||||||
|
{i18n._(t`Run`)}
|
||||||
|
</Button>,
|
||||||
|
<Button
|
||||||
|
id="cancel-external-credential-test"
|
||||||
|
key="cancel"
|
||||||
|
variant="link"
|
||||||
|
onClick={() => onClose()}
|
||||||
|
>
|
||||||
|
{i18n._(t`Cancel`)}
|
||||||
|
</Button>,
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Form>
|
||||||
|
<FormFullWidthLayout>
|
||||||
|
{credentialType.inputs.metadata.map(field => {
|
||||||
|
const isRequired = credentialType.inputs?.required.includes(
|
||||||
|
field.id
|
||||||
|
);
|
||||||
|
if (field.type === 'string') {
|
||||||
|
if (field.choices) {
|
||||||
|
return (
|
||||||
|
<FormGroup
|
||||||
|
key={field.id}
|
||||||
|
fieldId={`credential-${field.id}`}
|
||||||
|
label={field.label}
|
||||||
|
labelIcon={
|
||||||
|
field.help_text && (
|
||||||
|
<Tooltip
|
||||||
|
content={field.help_text}
|
||||||
|
position="right"
|
||||||
|
>
|
||||||
|
<QuestionCircleIcon />
|
||||||
|
</Tooltip>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
isRequired={isRequired}
|
||||||
|
>
|
||||||
|
<AnsibleSelect
|
||||||
|
name={field.id}
|
||||||
|
value={field.default}
|
||||||
|
id={`credential-${field.id}`}
|
||||||
|
data={field.choices.map(choice => {
|
||||||
|
return {
|
||||||
|
value: choice,
|
||||||
|
key: choice,
|
||||||
|
label: choice,
|
||||||
|
};
|
||||||
|
})}
|
||||||
|
onChange={(event, value) => {
|
||||||
|
setFieldValue(field.id, value);
|
||||||
|
}}
|
||||||
|
validate={isRequired ? required(null, i18n) : null}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FormField
|
||||||
|
key={field.id}
|
||||||
|
id={`credential-${field.id}`}
|
||||||
|
label={field.label}
|
||||||
|
tooltip={field.help_text}
|
||||||
|
name={field.id}
|
||||||
|
type={field.multiline ? 'textarea' : 'text'}
|
||||||
|
isRequired={isRequired}
|
||||||
|
validate={isRequired ? required(null, i18n) : null}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
})}
|
||||||
|
</FormFullWidthLayout>
|
||||||
|
</Form>
|
||||||
|
</Modal>
|
||||||
|
)}
|
||||||
|
</Formik>
|
||||||
|
<CredentialPluginTestAlert
|
||||||
|
credentialName={credentialFormValues.name}
|
||||||
|
successResponse={testPluginSuccess}
|
||||||
|
errorResponse={testPluginError}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ExternalTestModal.proptype = {
|
||||||
|
credential: shape({}),
|
||||||
|
credentialType: shape({}).isRequired,
|
||||||
|
credentialFormValues: shape({}).isRequired,
|
||||||
|
onClose: func.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
ExternalTestModal.defaultProps = {
|
||||||
|
credential: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default withI18n()(ExternalTestModal);
|
||||||
@@ -0,0 +1,180 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { act } from 'react-dom/test-utils';
|
||||||
|
import { mountWithContexts } from '../../../../testUtils/enzymeHelpers';
|
||||||
|
import { CredentialsAPI, CredentialTypesAPI } from '../../../api';
|
||||||
|
import ExternalTestModal from './ExternalTestModal';
|
||||||
|
import credentialTypesArr from './data.credentialTypes.json';
|
||||||
|
|
||||||
|
jest.mock('../../../api/models/Credentials');
|
||||||
|
jest.mock('../../../api/models/CredentialTypes');
|
||||||
|
|
||||||
|
const credentialType = credentialTypesArr.find(
|
||||||
|
credType => credType.namespace === 'hashivault_kv'
|
||||||
|
);
|
||||||
|
|
||||||
|
const credentialFormValues = {
|
||||||
|
name: 'Foobar',
|
||||||
|
credential_type: credentialType.id,
|
||||||
|
inputs: {
|
||||||
|
api_version: 'v2',
|
||||||
|
token: '$encrypted$',
|
||||||
|
url: 'http://hashivault:8200',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const credential = {
|
||||||
|
id: 1,
|
||||||
|
name: 'A credential',
|
||||||
|
credential_type: credentialType.id,
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('<ExternalTestModal />', () => {
|
||||||
|
let wrapper;
|
||||||
|
afterEach(() => wrapper.unmount());
|
||||||
|
test('should display metadata fields correctly', async () => {
|
||||||
|
wrapper = mountWithContexts(
|
||||||
|
<ExternalTestModal
|
||||||
|
credentialType={credentialType}
|
||||||
|
credentialFormValues={credentialFormValues}
|
||||||
|
onClose={jest.fn()}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
expect(wrapper.find('FormField').length).toBe(5);
|
||||||
|
expect(wrapper.find('input#credential-secret_backend').length).toBe(1);
|
||||||
|
expect(wrapper.find('input#credential-secret_path').length).toBe(1);
|
||||||
|
expect(wrapper.find('input#credential-auth_path').length).toBe(1);
|
||||||
|
expect(wrapper.find('input#credential-secret_key').length).toBe(1);
|
||||||
|
expect(wrapper.find('input#credential-secret_version').length).toBe(1);
|
||||||
|
});
|
||||||
|
test('should make the test request correctly when testing an existing credential', async () => {
|
||||||
|
wrapper = mountWithContexts(
|
||||||
|
<ExternalTestModal
|
||||||
|
credential={credential}
|
||||||
|
credentialType={credentialType}
|
||||||
|
credentialFormValues={credentialFormValues}
|
||||||
|
onClose={jest.fn()}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
await act(async () => {
|
||||||
|
wrapper.find('input#credential-secret_path').simulate('change', {
|
||||||
|
target: { value: '/secret/foo/bar/baz', name: 'secret_path' },
|
||||||
|
});
|
||||||
|
wrapper.find('input#credential-secret_key').simulate('change', {
|
||||||
|
target: { value: 'password', name: 'secret_key' },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
wrapper.update();
|
||||||
|
await act(async () => {
|
||||||
|
wrapper.find('Button[children="Run"]').simulate('click');
|
||||||
|
});
|
||||||
|
expect(CredentialsAPI.test).toHaveBeenCalledWith(1, {
|
||||||
|
inputs: {
|
||||||
|
api_version: 'v2',
|
||||||
|
cacert: undefined,
|
||||||
|
role_id: undefined,
|
||||||
|
secret_id: undefined,
|
||||||
|
token: '$encrypted$',
|
||||||
|
url: 'http://hashivault:8200',
|
||||||
|
},
|
||||||
|
metadata: {
|
||||||
|
auth_path: '',
|
||||||
|
secret_backend: '',
|
||||||
|
secret_key: 'password',
|
||||||
|
secret_path: '/secret/foo/bar/baz',
|
||||||
|
secret_version: '',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
test('should make the test request correctly when testing a new credential', async () => {
|
||||||
|
wrapper = mountWithContexts(
|
||||||
|
<ExternalTestModal
|
||||||
|
credentialType={credentialType}
|
||||||
|
credentialFormValues={credentialFormValues}
|
||||||
|
onClose={jest.fn()}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
await act(async () => {
|
||||||
|
wrapper.find('input#credential-secret_path').simulate('change', {
|
||||||
|
target: { value: '/secret/foo/bar/baz', name: 'secret_path' },
|
||||||
|
});
|
||||||
|
wrapper.find('input#credential-secret_key').simulate('change', {
|
||||||
|
target: { value: 'password', name: 'secret_key' },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
wrapper.update();
|
||||||
|
await act(async () => {
|
||||||
|
wrapper.find('Button[children="Run"]').simulate('click');
|
||||||
|
});
|
||||||
|
expect(CredentialTypesAPI.test).toHaveBeenCalledWith(21, {
|
||||||
|
inputs: {
|
||||||
|
api_version: 'v2',
|
||||||
|
cacert: undefined,
|
||||||
|
role_id: undefined,
|
||||||
|
secret_id: undefined,
|
||||||
|
token: '$encrypted$',
|
||||||
|
url: 'http://hashivault:8200',
|
||||||
|
},
|
||||||
|
metadata: {
|
||||||
|
auth_path: '',
|
||||||
|
secret_backend: '',
|
||||||
|
secret_key: 'password',
|
||||||
|
secret_path: '/secret/foo/bar/baz',
|
||||||
|
secret_version: '',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
test('should display the alert after a successful test', async () => {
|
||||||
|
CredentialTypesAPI.test.mockResolvedValue({});
|
||||||
|
wrapper = mountWithContexts(
|
||||||
|
<ExternalTestModal
|
||||||
|
credentialType={credentialType}
|
||||||
|
credentialFormValues={credentialFormValues}
|
||||||
|
onClose={jest.fn()}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
await act(async () => {
|
||||||
|
wrapper.find('input#credential-secret_path').simulate('change', {
|
||||||
|
target: { value: '/secret/foo/bar/baz', name: 'secret_path' },
|
||||||
|
});
|
||||||
|
wrapper.find('input#credential-secret_key').simulate('change', {
|
||||||
|
target: { value: 'password', name: 'secret_key' },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
wrapper.update();
|
||||||
|
await act(async () => {
|
||||||
|
wrapper.find('Button[children="Run"]').simulate('click');
|
||||||
|
});
|
||||||
|
wrapper.update();
|
||||||
|
expect(wrapper.find('Alert').length).toBe(1);
|
||||||
|
expect(wrapper.find('Alert').props().variant).toBe('success');
|
||||||
|
});
|
||||||
|
test('should display the alert after a failed test', async () => {
|
||||||
|
CredentialTypesAPI.test.mockRejectedValue({
|
||||||
|
inputs: `HTTP 404
|
||||||
|
{"errors":["no handler for route '/secret/foo/bar/baz'"]}
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
wrapper = mountWithContexts(
|
||||||
|
<ExternalTestModal
|
||||||
|
credentialType={credentialType}
|
||||||
|
credentialFormValues={credentialFormValues}
|
||||||
|
onClose={jest.fn()}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
await act(async () => {
|
||||||
|
wrapper.find('input#credential-secret_path').simulate('change', {
|
||||||
|
target: { value: '/secret/foo/bar/baz', name: 'secret_path' },
|
||||||
|
});
|
||||||
|
wrapper.find('input#credential-secret_key').simulate('change', {
|
||||||
|
target: { value: 'password', name: 'secret_key' },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
wrapper.update();
|
||||||
|
await act(async () => {
|
||||||
|
wrapper.find('Button[children="Run"]').simulate('click');
|
||||||
|
});
|
||||||
|
wrapper.update();
|
||||||
|
expect(wrapper.find('Alert').length).toBe(1);
|
||||||
|
expect(wrapper.find('Alert').props().variant).toBe('danger');
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,2 +1,3 @@
|
|||||||
export { default as mockCredentials } from './data.credentials.json';
|
export { default as mockCredentials } from './data.credentials.json';
|
||||||
export { default as mockCredentialType } from './data.credential_type.json';
|
export { default as mockCredentialType } from './data.credential_type.json';
|
||||||
|
export { default as ExternalTestModal } from './ExternalTestModal';
|
||||||
|
|||||||
@@ -38,6 +38,9 @@ export default function useRequest(makeRequest, initialValue) {
|
|||||||
request: useCallback(
|
request: useCallback(
|
||||||
async (...args) => {
|
async (...args) => {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
|
if (isMounted.current) {
|
||||||
|
setError(null);
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
const response = await makeRequest(...args);
|
const response = await makeRequest(...args);
|
||||||
if (isMounted.current) {
|
if (isMounted.current) {
|
||||||
|
|||||||
Reference in New Issue
Block a user