mirror of
https://github.com/ansible/awx.git
synced 2026-05-24 00:57:48 -02:30
Merge pull request #7338 from mabashian/cred-plugin-test-button
Hook up Test button on Metadata step in credential plugin wizard Reviewed-by: John Hill <johill@redhat.com> https://github.com/unlikelyzero
This commit is contained in:
@@ -14,7 +14,7 @@ import { FieldTooltip, PasswordInput } from '../../../../components/FormField';
|
|||||||
import AnsibleSelect from '../../../../components/AnsibleSelect';
|
import AnsibleSelect from '../../../../components/AnsibleSelect';
|
||||||
import { CredentialType } from '../../../../types';
|
import { CredentialType } from '../../../../types';
|
||||||
import { required } from '../../../../util/validators';
|
import { required } from '../../../../util/validators';
|
||||||
import { CredentialPluginField } from './CredentialPlugins';
|
import { CredentialPluginField } from '../CredentialPlugins';
|
||||||
import BecomeMethodField from './BecomeMethodField';
|
import BecomeMethodField from './BecomeMethodField';
|
||||||
|
|
||||||
const FileUpload = styled(PFFileUpload)`
|
const FileUpload = styled(PFFileUpload)`
|
||||||
|
|||||||
@@ -1,69 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { func, shape } from 'prop-types';
|
|
||||||
import { Formik, useField } from 'formik';
|
|
||||||
import { withI18n } from '@lingui/react';
|
|
||||||
import { t } from '@lingui/macro';
|
|
||||||
import { Wizard } from '@patternfly/react-core';
|
|
||||||
import CredentialsStep from './CredentialsStep';
|
|
||||||
import MetadataStep from './MetadataStep';
|
|
||||||
|
|
||||||
function CredentialPluginWizard({ i18n, handleSubmit, onClose }) {
|
|
||||||
const [selectedCredential] = useField('credential');
|
|
||||||
const steps = [
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
name: i18n._(t`Credential`),
|
|
||||||
component: <CredentialsStep />,
|
|
||||||
enableNext: !!selectedCredential.value,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
name: i18n._(t`Metadata`),
|
|
||||||
component: <MetadataStep />,
|
|
||||||
canJumpTo: !!selectedCredential.value,
|
|
||||||
nextButtonText: i18n._(t`OK`),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Wizard
|
|
||||||
isOpen
|
|
||||||
onClose={onClose}
|
|
||||||
title={i18n._(t`External Secret Management System`)}
|
|
||||||
steps={steps}
|
|
||||||
onSave={handleSubmit}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function CredentialPluginPrompt({ i18n, onClose, onSubmit, initialValues }) {
|
|
||||||
return (
|
|
||||||
<Formik
|
|
||||||
initialValues={{
|
|
||||||
credential: initialValues?.credential || null,
|
|
||||||
inputs: initialValues?.inputs || {},
|
|
||||||
}}
|
|
||||||
onSubmit={onSubmit}
|
|
||||||
>
|
|
||||||
{({ handleSubmit }) => (
|
|
||||||
<CredentialPluginWizard
|
|
||||||
handleSubmit={handleSubmit}
|
|
||||||
i18n={i18n}
|
|
||||||
onClose={onClose}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Formik>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
CredentialPluginPrompt.propTypes = {
|
|
||||||
onClose: func.isRequired,
|
|
||||||
onSubmit: func.isRequired,
|
|
||||||
initialValues: shape({}),
|
|
||||||
};
|
|
||||||
|
|
||||||
CredentialPluginPrompt.defaultProps = {
|
|
||||||
initialValues: {},
|
|
||||||
};
|
|
||||||
|
|
||||||
export default withI18n()(CredentialPluginPrompt);
|
|
||||||
@@ -11,8 +11,8 @@ import {
|
|||||||
Tooltip,
|
Tooltip,
|
||||||
} from '@patternfly/react-core';
|
} from '@patternfly/react-core';
|
||||||
import { KeyIcon } from '@patternfly/react-icons';
|
import { KeyIcon } from '@patternfly/react-icons';
|
||||||
import { FieldTooltip } from '../../../../../components/FormField';
|
import { FieldTooltip } from '../../../../components/FormField';
|
||||||
import FieldWithPrompt from '../../../../../components/FieldWithPrompt';
|
import FieldWithPrompt from '../../../../components/FieldWithPrompt';
|
||||||
import { CredentialPluginPrompt } from './CredentialPluginPrompt';
|
import { CredentialPluginPrompt } from './CredentialPluginPrompt';
|
||||||
import CredentialPluginSelected from './CredentialPluginSelected';
|
import CredentialPluginSelected from './CredentialPluginSelected';
|
||||||
|
|
||||||
@@ -55,6 +55,7 @@ function CredentialPluginInput(props) {
|
|||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
|
id={`credential-${fieldOptions.id}-external-button`}
|
||||||
variant={ButtonVariant.control}
|
variant={ButtonVariant.control}
|
||||||
aria-label={i18n._(
|
aria-label={i18n._(
|
||||||
t`Populate field from an external secret management system`
|
t`Populate field from an external secret management system`
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Formik } from 'formik';
|
import { Formik } from 'formik';
|
||||||
import { TextInput } from '@patternfly/react-core';
|
import { TextInput } from '@patternfly/react-core';
|
||||||
import { mountWithContexts } from '../../../../../../testUtils/enzymeHelpers';
|
import { mountWithContexts } from '../../../../../testUtils/enzymeHelpers';
|
||||||
import CredentialPluginField from './CredentialPluginField';
|
import CredentialPluginField from './CredentialPluginField';
|
||||||
|
|
||||||
const fieldOptions = {
|
const fieldOptions = {
|
||||||
@@ -0,0 +1,158 @@
|
|||||||
|
import React, { useCallback } from 'react';
|
||||||
|
import { func, shape } from 'prop-types';
|
||||||
|
import { Formik, useField } from 'formik';
|
||||||
|
import { withI18n } from '@lingui/react';
|
||||||
|
import { t } from '@lingui/macro';
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Tooltip,
|
||||||
|
Wizard,
|
||||||
|
WizardContextConsumer,
|
||||||
|
WizardFooter,
|
||||||
|
} from '@patternfly/react-core';
|
||||||
|
import CredentialsStep from './CredentialsStep';
|
||||||
|
import MetadataStep from './MetadataStep';
|
||||||
|
import { CredentialsAPI } from '../../../../../api';
|
||||||
|
import useRequest from '../../../../../util/useRequest';
|
||||||
|
import { CredentialPluginTestAlert } from '..';
|
||||||
|
|
||||||
|
function CredentialPluginWizard({ i18n, handleSubmit, onClose }) {
|
||||||
|
const [selectedCredential] = useField('credential');
|
||||||
|
const [inputValues] = useField('inputs');
|
||||||
|
|
||||||
|
const {
|
||||||
|
result: testPluginSuccess,
|
||||||
|
error: testPluginError,
|
||||||
|
request: testPluginMetadata,
|
||||||
|
} = useRequest(
|
||||||
|
useCallback(
|
||||||
|
async () =>
|
||||||
|
CredentialsAPI.test(selectedCredential.value.id, {
|
||||||
|
metadata: inputValues.value,
|
||||||
|
}),
|
||||||
|
[selectedCredential, inputValues]
|
||||||
|
),
|
||||||
|
null
|
||||||
|
);
|
||||||
|
|
||||||
|
const steps = [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
name: i18n._(t`Credential`),
|
||||||
|
key: 'credential',
|
||||||
|
component: <CredentialsStep />,
|
||||||
|
enableNext: !!selectedCredential.value,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
name: i18n._(t`Metadata`),
|
||||||
|
key: 'metadata',
|
||||||
|
component: <MetadataStep />,
|
||||||
|
canJumpTo: !!selectedCredential.value,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const CustomFooter = (
|
||||||
|
<WizardFooter>
|
||||||
|
<WizardContextConsumer>
|
||||||
|
{({ activeStep, onNext, onBack }) => (
|
||||||
|
<>
|
||||||
|
<Button
|
||||||
|
id="credential-plugin-prompt-next"
|
||||||
|
variant="primary"
|
||||||
|
onClick={onNext}
|
||||||
|
isDisabled={!selectedCredential.value}
|
||||||
|
>
|
||||||
|
{activeStep.key === 'metadata' ? i18n._(t`OK`) : i18n._(t`Next`)}
|
||||||
|
</Button>
|
||||||
|
{activeStep && activeStep.key === 'metadata' && (
|
||||||
|
<>
|
||||||
|
<Tooltip
|
||||||
|
content={i18n._(
|
||||||
|
t`Click this button to verify connection to the secret management system using the selected credential and specified inputs.`
|
||||||
|
)}
|
||||||
|
position="right"
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
id="credential-plugin-prompt-test"
|
||||||
|
variant="secondary"
|
||||||
|
onClick={() => testPluginMetadata()}
|
||||||
|
>
|
||||||
|
{i18n._(t`Test`)}
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
id="credential-plugin-prompt-back"
|
||||||
|
variant="secondary"
|
||||||
|
onClick={onBack}
|
||||||
|
>
|
||||||
|
{i18n._(t`Back`)}
|
||||||
|
</Button>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
<Button
|
||||||
|
id="credential-plugin-prompt-cancel"
|
||||||
|
variant="link"
|
||||||
|
onClick={onClose}
|
||||||
|
>
|
||||||
|
{i18n._(t`Cancel`)}
|
||||||
|
</Button>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</WizardContextConsumer>
|
||||||
|
</WizardFooter>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Wizard
|
||||||
|
isOpen
|
||||||
|
onClose={onClose}
|
||||||
|
title={i18n._(t`External Secret Management System`)}
|
||||||
|
steps={steps}
|
||||||
|
onSave={handleSubmit}
|
||||||
|
footer={CustomFooter}
|
||||||
|
/>
|
||||||
|
{selectedCredential.value && (
|
||||||
|
<CredentialPluginTestAlert
|
||||||
|
credentialName={selectedCredential.value.name}
|
||||||
|
successResponse={testPluginSuccess}
|
||||||
|
errorResponse={testPluginError}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function CredentialPluginPrompt({ i18n, onClose, onSubmit, initialValues }) {
|
||||||
|
return (
|
||||||
|
<Formik
|
||||||
|
initialValues={{
|
||||||
|
credential: initialValues?.credential || null,
|
||||||
|
inputs: initialValues?.inputs || {},
|
||||||
|
}}
|
||||||
|
onSubmit={onSubmit}
|
||||||
|
>
|
||||||
|
{({ handleSubmit }) => (
|
||||||
|
<CredentialPluginWizard
|
||||||
|
handleSubmit={handleSubmit}
|
||||||
|
i18n={i18n}
|
||||||
|
onClose={onClose}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Formik>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
CredentialPluginPrompt.propTypes = {
|
||||||
|
onClose: func.isRequired,
|
||||||
|
onSubmit: func.isRequired,
|
||||||
|
initialValues: shape({}),
|
||||||
|
};
|
||||||
|
|
||||||
|
CredentialPluginPrompt.defaultProps = {
|
||||||
|
initialValues: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default withI18n()(CredentialPluginPrompt);
|
||||||
@@ -3,15 +3,17 @@ import { act } from 'react-dom/test-utils';
|
|||||||
import {
|
import {
|
||||||
mountWithContexts,
|
mountWithContexts,
|
||||||
waitForElement,
|
waitForElement,
|
||||||
} from '../../../../../../../testUtils/enzymeHelpers';
|
} from '../../../../../../testUtils/enzymeHelpers';
|
||||||
import { CredentialsAPI, CredentialTypesAPI } from '../../../../../../api';
|
import { CredentialsAPI, CredentialTypesAPI } from '../../../../../api';
|
||||||
import selectedCredential from '../../../data.cyberArkCredential.json';
|
import selectedCredential from '../../data.cyberArkCredential.json';
|
||||||
import azureVaultCredential from '../../../data.azureVaultCredential.json';
|
import azureVaultCredential from '../../data.azureVaultCredential.json';
|
||||||
import hashiCorpCredential from '../../../data.hashiCorpCredential.json';
|
import hashiCorpCredential from '../../data.hashiCorpCredential.json';
|
||||||
import CredentialPluginPrompt from './CredentialPluginPrompt';
|
import CredentialPluginPrompt from './CredentialPluginPrompt';
|
||||||
|
|
||||||
jest.mock('../../../../../../api/models/Credentials');
|
jest.mock('../../../../../api/models/Credentials');
|
||||||
jest.mock('../../../../../../api/models/CredentialTypes');
|
jest.mock('../../../../../api/models/CredentialTypes');
|
||||||
|
|
||||||
|
CredentialsAPI.test.mockResolvedValue({});
|
||||||
|
|
||||||
CredentialsAPI.read.mockResolvedValue({
|
CredentialsAPI.read.mockResolvedValue({
|
||||||
data: {
|
data: {
|
||||||
@@ -234,5 +236,13 @@ describe('<CredentialPluginPrompt />', () => {
|
|||||||
wrapper.find('input#credential-secret_version').prop('value')
|
wrapper.find('input#credential-secret_version').prop('value')
|
||||||
).toBe('9000');
|
).toBe('9000');
|
||||||
});
|
});
|
||||||
|
test('clicking Test button makes correct call', async () => {
|
||||||
|
await act(async () => {
|
||||||
|
wrapper.find('Button[children="Test"]').simulate('click');
|
||||||
|
});
|
||||||
|
expect(CredentialsAPI.test).toHaveBeenCalledWith(1, {
|
||||||
|
metadata: { secret_path: '/foo/bar', secret_version: '9000' },
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -3,13 +3,13 @@ import { useHistory } 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 { useField } from 'formik';
|
import { useField } from 'formik';
|
||||||
import { CredentialsAPI } from '../../../../../../api';
|
import { CredentialsAPI } from '../../../../../api';
|
||||||
import CheckboxListItem from '../../../../../../components/CheckboxListItem';
|
import CheckboxListItem from '../../../../../components/CheckboxListItem';
|
||||||
import ContentError from '../../../../../../components/ContentError';
|
import ContentError from '../../../../../components/ContentError';
|
||||||
import DataListToolbar from '../../../../../../components/DataListToolbar';
|
import DataListToolbar from '../../../../../components/DataListToolbar';
|
||||||
import PaginatedDataList from '../../../../../../components/PaginatedDataList';
|
import PaginatedDataList from '../../../../../components/PaginatedDataList';
|
||||||
import { getQSConfig, parseQueryString } from '../../../../../../util/qs';
|
import { getQSConfig, parseQueryString } from '../../../../../util/qs';
|
||||||
import useRequest from '../../../../../../util/useRequest';
|
import useRequest from '../../../../../util/useRequest';
|
||||||
|
|
||||||
const QS_CONFIG = getQSConfig('credential', {
|
const QS_CONFIG = getQSConfig('credential', {
|
||||||
page: 1,
|
page: 1,
|
||||||
@@ -1,27 +1,22 @@
|
|||||||
import React, { useCallback, useEffect } from 'react';
|
import React, { useCallback, useEffect } from 'react';
|
||||||
import { withI18n } from '@lingui/react';
|
import { withI18n } from '@lingui/react';
|
||||||
import { t } from '@lingui/macro';
|
|
||||||
import { useField, useFormikContext } from 'formik';
|
import { useField, useFormikContext } from 'formik';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { Button, Form, FormGroup, Tooltip } from '@patternfly/react-core';
|
import { Form, FormGroup, Tooltip } from '@patternfly/react-core';
|
||||||
import { QuestionCircleIcon as PFQuestionCircleIcon } from '@patternfly/react-icons';
|
import { QuestionCircleIcon as PFQuestionCircleIcon } from '@patternfly/react-icons';
|
||||||
import { CredentialTypesAPI } from '../../../../../../api';
|
import { CredentialTypesAPI } from '../../../../../api';
|
||||||
import AnsibleSelect from '../../../../../../components/AnsibleSelect';
|
import AnsibleSelect from '../../../../../components/AnsibleSelect';
|
||||||
import ContentError from '../../../../../../components/ContentError';
|
import ContentError from '../../../../../components/ContentError';
|
||||||
import ContentLoading from '../../../../../../components/ContentLoading';
|
import ContentLoading from '../../../../../components/ContentLoading';
|
||||||
import FormField from '../../../../../../components/FormField';
|
import FormField from '../../../../../components/FormField';
|
||||||
import { FormFullWidthLayout } from '../../../../../../components/FormLayout';
|
import { FormFullWidthLayout } from '../../../../../components/FormLayout';
|
||||||
import useRequest from '../../../../../../util/useRequest';
|
import useRequest from '../../../../../util/useRequest';
|
||||||
import { required } from '../../../../../../util/validators';
|
import { required } from '../../../../../util/validators';
|
||||||
|
|
||||||
const QuestionCircleIcon = styled(PFQuestionCircleIcon)`
|
const QuestionCircleIcon = styled(PFQuestionCircleIcon)`
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const TestButton = styled(Button)`
|
|
||||||
margin-top: 20px;
|
|
||||||
`;
|
|
||||||
|
|
||||||
function MetadataStep({ i18n }) {
|
function MetadataStep({ i18n }) {
|
||||||
const form = useFormikContext();
|
const form = useFormikContext();
|
||||||
const [selectedCredential] = useField('credential');
|
const [selectedCredential] = useField('credential');
|
||||||
@@ -65,10 +60,6 @@ function MetadataStep({ i18n }) {
|
|||||||
fetchMetadataOptions();
|
fetchMetadataOptions();
|
||||||
}, [fetchMetadataOptions]);
|
}, [fetchMetadataOptions]);
|
||||||
|
|
||||||
const testMetadata = () => {
|
|
||||||
// https://github.com/ansible/awx/issues/7126
|
|
||||||
};
|
|
||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
return <ContentLoading />;
|
return <ContentLoading />;
|
||||||
}
|
}
|
||||||
@@ -136,20 +127,6 @@ function MetadataStep({ i18n }) {
|
|||||||
</FormFullWidthLayout>
|
</FormFullWidthLayout>
|
||||||
</Form>
|
</Form>
|
||||||
)}
|
)}
|
||||||
<Tooltip
|
|
||||||
content={i18n._(
|
|
||||||
t`Click this button to verify connection to the secret management system using the selected credential and specified inputs.`
|
|
||||||
)}
|
|
||||||
position="right"
|
|
||||||
>
|
|
||||||
<TestButton
|
|
||||||
variant="primary"
|
|
||||||
type="submit"
|
|
||||||
onClick={() => testMetadata()}
|
|
||||||
>
|
|
||||||
{i18n._(t`Test`)}
|
|
||||||
</TestButton>
|
|
||||||
</Tooltip>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -5,8 +5,8 @@ import { t, Trans } from '@lingui/macro';
|
|||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { Button, ButtonVariant, Tooltip } from '@patternfly/react-core';
|
import { Button, ButtonVariant, Tooltip } from '@patternfly/react-core';
|
||||||
import { KeyIcon } from '@patternfly/react-icons';
|
import { KeyIcon } from '@patternfly/react-icons';
|
||||||
import CredentialChip from '../../../../../components/CredentialChip';
|
import CredentialChip from '../../../../components/CredentialChip';
|
||||||
import { Credential } from '../../../../../types';
|
import { Credential } from '../../../../types';
|
||||||
|
|
||||||
const SelectedCredential = styled.div`
|
const SelectedCredential = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { mountWithContexts } from '../../../../../../testUtils/enzymeHelpers';
|
import { mountWithContexts } from '../../../../../testUtils/enzymeHelpers';
|
||||||
import selectedCredential from '../../data.cyberArkCredential.json';
|
import selectedCredential from '../data.cyberArkCredential.json';
|
||||||
import CredentialPluginSelected from './CredentialPluginSelected';
|
import CredentialPluginSelected from './CredentialPluginSelected';
|
||||||
|
|
||||||
describe('<CredentialPluginSelected />', () => {
|
describe('<CredentialPluginSelected />', () => {
|
||||||
@@ -16,7 +16,6 @@ function CredentialPluginTestAlert({
|
|||||||
}) {
|
}) {
|
||||||
const [testMessage, setTestMessage] = useState('');
|
const [testMessage, setTestMessage] = useState('');
|
||||||
const [testVariant, setTestVariant] = useState(false);
|
const [testVariant, setTestVariant] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (errorResponse) {
|
if (errorResponse) {
|
||||||
if (errorResponse?.response?.data?.inputs) {
|
if (errorResponse?.response?.data?.inputs) {
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { mountWithContexts } from '../../../../../testUtils/enzymeHelpers';
|
||||||
|
import CredentialPluginTestAlert from './CredentialPluginTestAlert';
|
||||||
|
|
||||||
|
describe('<CredentialPluginTestAlert />', () => {
|
||||||
|
let wrapper;
|
||||||
|
afterEach(() => {
|
||||||
|
wrapper.unmount();
|
||||||
|
});
|
||||||
|
test('renders expected content when test is successful', () => {
|
||||||
|
wrapper = mountWithContexts(
|
||||||
|
<CredentialPluginTestAlert
|
||||||
|
credentialName="Foobar"
|
||||||
|
successResponse={{}}
|
||||||
|
errorResponse={null}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
expect(wrapper.find('b#credential-plugin-test-name').text()).toBe('Foobar');
|
||||||
|
expect(wrapper.find('p#credential-plugin-test-message').text()).toBe(
|
||||||
|
'Test passed'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
test('renders expected content when test fails with the expected return string formatting', () => {
|
||||||
|
wrapper = mountWithContexts(
|
||||||
|
<CredentialPluginTestAlert
|
||||||
|
credentialName="Foobar"
|
||||||
|
successResponse={null}
|
||||||
|
errorResponse={{
|
||||||
|
response: {
|
||||||
|
data: {
|
||||||
|
inputs: `HTTP 404
|
||||||
|
{"errors":["not found"]}
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
expect(wrapper.find('b#credential-plugin-test-name').text()).toBe('Foobar');
|
||||||
|
expect(wrapper.find('p#credential-plugin-test-message').text()).toBe(
|
||||||
|
'HTTP 404: not found'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
test('renders expected content when test fails without the expected return string formatting', () => {
|
||||||
|
wrapper = mountWithContexts(
|
||||||
|
<CredentialPluginTestAlert
|
||||||
|
credentialName="Foobar"
|
||||||
|
successResponse={null}
|
||||||
|
errorResponse={{
|
||||||
|
response: {
|
||||||
|
data: {
|
||||||
|
inputs: 'usernamee is not present at /secret/foo/bar/baz',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
expect(wrapper.find('b#credential-plugin-test-name').text()).toBe('Foobar');
|
||||||
|
expect(wrapper.find('p#credential-plugin-test-message').text()).toBe(
|
||||||
|
'usernamee is not present at /secret/foo/bar/baz'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -18,7 +18,7 @@ import FormField from '../../../components/FormField';
|
|||||||
import { FormFullWidthLayout } from '../../../components/FormLayout';
|
import { FormFullWidthLayout } from '../../../components/FormLayout';
|
||||||
import { required } from '../../../util/validators';
|
import { required } from '../../../util/validators';
|
||||||
import useRequest from '../../../util/useRequest';
|
import useRequest from '../../../util/useRequest';
|
||||||
import { CredentialPluginTestAlert } from './CredentialFormFields/CredentialPlugins';
|
import { CredentialPluginTestAlert } from './CredentialPlugins';
|
||||||
|
|
||||||
const QuestionCircleIcon = styled(PFQuestionCircleIcon)`
|
const QuestionCircleIcon = styled(PFQuestionCircleIcon)`
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ export default function useRequest(makeRequest, initialValue) {
|
|||||||
async (...args) => {
|
async (...args) => {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
if (isMounted.current) {
|
if (isMounted.current) {
|
||||||
|
setResult(initialValue);
|
||||||
setError(null);
|
setError(null);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
@@ -56,6 +57,7 @@ export default function useRequest(makeRequest, initialValue) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
/* eslint-disable-next-line react-hooks/exhaustive-deps */
|
||||||
[makeRequest]
|
[makeRequest]
|
||||||
),
|
),
|
||||||
setValue: setResult,
|
setValue: setResult,
|
||||||
|
|||||||
@@ -96,6 +96,37 @@ describe('useRequest hooks', () => {
|
|||||||
expect(wrapper.find('TestInner').prop('error')).toEqual(error);
|
expect(wrapper.find('TestInner').prop('error')).toEqual(error);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('should reset error/result on each request', async () => {
|
||||||
|
const error = new Error('error');
|
||||||
|
const makeRequest = throwError => {
|
||||||
|
if (throwError) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return { data: 'foo' };
|
||||||
|
};
|
||||||
|
const wrapper = mount(<Test makeRequest={makeRequest} />);
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
wrapper.find('TestInner').invoke('request')(true);
|
||||||
|
});
|
||||||
|
wrapper.update();
|
||||||
|
expect(wrapper.find('TestInner').prop('result')).toEqual({});
|
||||||
|
expect(wrapper.find('TestInner').prop('error')).toEqual(error);
|
||||||
|
await act(async () => {
|
||||||
|
wrapper.find('TestInner').invoke('request')();
|
||||||
|
});
|
||||||
|
wrapper.update();
|
||||||
|
expect(wrapper.find('TestInner').prop('result')).toEqual({ data: 'foo' });
|
||||||
|
expect(wrapper.find('TestInner').prop('error')).toEqual(null);
|
||||||
|
await act(async () => {
|
||||||
|
wrapper.find('TestInner').invoke('request')(true);
|
||||||
|
});
|
||||||
|
wrapper.update();
|
||||||
|
expect(wrapper.find('TestInner').prop('result')).toEqual({});
|
||||||
|
expect(wrapper.find('TestInner').prop('error')).toEqual(error);
|
||||||
|
});
|
||||||
|
|
||||||
test('should not update state after unmount', async () => {
|
test('should not update state after unmount', async () => {
|
||||||
const makeRequest = jest.fn();
|
const makeRequest = jest.fn();
|
||||||
let resolve;
|
let resolve;
|
||||||
|
|||||||
Reference in New Issue
Block a user