mirror of
https://github.com/ansible/awx.git
synced 2026-02-23 05:55:59 -03:30
adds test for new webhook component
This commit is contained in:
@@ -45,7 +45,7 @@ function Template({ i18n, me, setBreadcrumb }) {
|
|||||||
role_level: 'notification_admin_role',
|
role_level: 'notification_admin_role',
|
||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
if (data?.related?.webhook_key) {
|
if (data.webhook_service && data?.related?.webhook_key) {
|
||||||
const {
|
const {
|
||||||
data: { webhook_key },
|
data: { webhook_key },
|
||||||
} = await JobTemplatesAPI.readWebhookKey(templateId);
|
} = await JobTemplatesAPI.readWebhookKey(templateId);
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ import {
|
|||||||
import { JobTemplatesAPI, ProjectsAPI } from '@api';
|
import { JobTemplatesAPI, ProjectsAPI } from '@api';
|
||||||
import LabelSelect from './LabelSelect';
|
import LabelSelect from './LabelSelect';
|
||||||
import PlaybookSelect from './PlaybookSelect';
|
import PlaybookSelect from './PlaybookSelect';
|
||||||
import WebhookSubForm from './WebhooksSubForm';
|
import WebhookSubForm from './WebhookSubForm';
|
||||||
|
|
||||||
const { origin } = document.location;
|
const { origin } = document.location;
|
||||||
|
|
||||||
@@ -94,10 +94,6 @@ function JobTemplateForm({
|
|||||||
);
|
);
|
||||||
const [jobTagsField, , jobTagsHelpers] = useField('job_tags');
|
const [jobTagsField, , jobTagsHelpers] = useField('job_tags');
|
||||||
const [skipTagsField, , skipTagsHelpers] = useField('skip_tags');
|
const [skipTagsField, , skipTagsHelpers] = useField('skip_tags');
|
||||||
const webhookService = useField('webhook_service');
|
|
||||||
const webhookUrl = useField('webhook_url');
|
|
||||||
const webhookKey = useField('webhook_key');
|
|
||||||
const webhookCredential = useField('webhook_credential');
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
request: fetchProject,
|
request: fetchProject,
|
||||||
@@ -189,19 +185,11 @@ function JobTemplateForm({
|
|||||||
callbackUrl = `${origin}${path}`;
|
callbackUrl = `${origin}${path}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (instanceGroupLoading || hasProjectLoading) {
|
||||||
instanceGroupLoading ||
|
|
||||||
hasProjectLoading
|
|
||||||
// credentialContentLoading
|
|
||||||
) {
|
|
||||||
return <ContentLoading />;
|
return <ContentLoading />;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (instanceGroupError || projectContentError) {
|
||||||
instanceGroupError ||
|
|
||||||
projectContentError
|
|
||||||
// credentialContentError
|
|
||||||
) {
|
|
||||||
return <ContentError error={contentError} />;
|
return <ContentError error={contentError} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -530,23 +518,9 @@ function JobTemplateForm({
|
|||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
id="wfjt-enabled-webhooks"
|
id="wfjt-enabled-webhooks"
|
||||||
isChecked={
|
isChecked={enableWebhooks}
|
||||||
Boolean(webhookService[0].value) || enableWebhooks
|
|
||||||
}
|
|
||||||
onChange={checked => {
|
onChange={checked => {
|
||||||
setEnableWebhooks(checked);
|
setEnableWebhooks(checked);
|
||||||
webhookService[2].setValue(
|
|
||||||
!checked ? '' : webhookService[1].initialValue
|
|
||||||
);
|
|
||||||
webhookUrl[2].setValue(
|
|
||||||
!checked ? '' : webhookUrl[1].initialValue
|
|
||||||
);
|
|
||||||
webhookKey[2].setValue(
|
|
||||||
!checked ? '' : webhookKey[1].initialValue
|
|
||||||
);
|
|
||||||
webhookCredential[2].setValue(
|
|
||||||
!checked ? null : webhookCredential[1].initialValue
|
|
||||||
);
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<CheckboxField
|
<CheckboxField
|
||||||
@@ -624,7 +598,7 @@ JobTemplateForm.defaultProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const FormikApp = withFormik({
|
const FormikApp = withFormik({
|
||||||
mapPropsToValues({ template = {} }) {
|
mapPropsToValues({ template = {}, i18n }) {
|
||||||
const {
|
const {
|
||||||
summary_fields = {
|
summary_fields = {
|
||||||
labels: { results: [] },
|
labels: { results: [] },
|
||||||
@@ -671,8 +645,10 @@ const FormikApp = withFormik({
|
|||||||
webhook_service: template.webhook_service || '',
|
webhook_service: template.webhook_service || '',
|
||||||
webhook_url: template?.related?.webhook_receiver
|
webhook_url: template?.related?.webhook_receiver
|
||||||
? `${origin}${template.related.webhook_receiver}`
|
? `${origin}${template.related.webhook_receiver}`
|
||||||
: '',
|
: i18n._(t`a new webhook url will be generated on save.`).toUpperCase(),
|
||||||
webhook_key: template.webhook_key || '',
|
webhook_key:
|
||||||
|
template.webhook_key ||
|
||||||
|
i18n._(t`a new webhook key will be generated on save.`).toUpperCase(),
|
||||||
webhook_credential: template?.summary_fields?.webhook_credential || null,
|
webhook_credential: template?.summary_fields?.webhook_credential || null,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -292,6 +292,49 @@ describe('<JobTemplateForm />', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('webhooks should render properly, without data', async () => {
|
||||||
|
let wrapper;
|
||||||
|
const history = createMemoryHistory({
|
||||||
|
initialEntries: ['/templates/job_template/1/edit'],
|
||||||
|
});
|
||||||
|
await act(async () => {
|
||||||
|
wrapper = mountWithContexts(
|
||||||
|
<Route
|
||||||
|
path="/templates/job_template/:id/edit"
|
||||||
|
component={() => (
|
||||||
|
<JobTemplateForm
|
||||||
|
template={{
|
||||||
|
...mockData,
|
||||||
|
webhook_credential: null,
|
||||||
|
webhook_key: '',
|
||||||
|
webhook_service: 'github',
|
||||||
|
related: { webhook_receiver: '' },
|
||||||
|
}}
|
||||||
|
handleSubmit={jest.fn()}
|
||||||
|
handleCancel={jest.fn()}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>,
|
||||||
|
{
|
||||||
|
context: {
|
||||||
|
router: {
|
||||||
|
history,
|
||||||
|
route: {
|
||||||
|
location: history.location,
|
||||||
|
match: { params: { id: 1 } },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
expect(
|
||||||
|
wrapper.find('TextInputBase#template-webhook_key').prop('value')
|
||||||
|
).toBe('A NEW WEBHOOK KEY WILL BE GENERATED ON SAVE.');
|
||||||
|
expect(
|
||||||
|
wrapper.find('Button[aria-label="Update webhook key"]').prop('isDisabled')
|
||||||
|
).toBe(true);
|
||||||
|
});
|
||||||
test('should call handleSubmit when Submit button is clicked', async () => {
|
test('should call handleSubmit when Submit button is clicked', async () => {
|
||||||
const handleSubmit = jest.fn();
|
const handleSubmit = jest.fn();
|
||||||
let wrapper;
|
let wrapper;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React, { useEffect, useCallback, useState } from 'react';
|
import React, { useEffect, useCallback } from 'react';
|
||||||
import { SyncAltIcon } from '@patternfly/react-icons';
|
import { SyncAltIcon } from '@patternfly/react-icons';
|
||||||
import { useParams, useRouteMatch } from 'react-router-dom';
|
import { useParams, useLocation } from 'react-router-dom';
|
||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import { withI18n } from '@lingui/react';
|
import { withI18n } from '@lingui/react';
|
||||||
import {
|
import {
|
||||||
@@ -20,9 +20,8 @@ import { FieldTooltip } from '@components/FormField';
|
|||||||
import { JobTemplatesAPI, CredentialTypesAPI } from '@api';
|
import { JobTemplatesAPI, CredentialTypesAPI } from '@api';
|
||||||
|
|
||||||
function WebhookSubForm({ i18n, enableWebhooks }) {
|
function WebhookSubForm({ i18n, enableWebhooks }) {
|
||||||
const [contentError, setContentError] = useState(null);
|
const { id, templateType } = useParams();
|
||||||
const jtAddMatch = useRouteMatch('/templates/job_template/add');
|
const { pathname } = useLocation();
|
||||||
const { id } = useParams();
|
|
||||||
|
|
||||||
const { origin } = document.location;
|
const { origin } = document.location;
|
||||||
|
|
||||||
@@ -32,7 +31,9 @@ function WebhookSubForm({ i18n, enableWebhooks }) {
|
|||||||
webhookServiceHelpers,
|
webhookServiceHelpers,
|
||||||
] = useField('webhook_service');
|
] = useField('webhook_service');
|
||||||
|
|
||||||
const [webhookUrlField, , webhookUrlHelpers] = useField('webhook_url');
|
const [webhookUrlField, webhookUrlMeta, webhookUrlHelpers] = useField(
|
||||||
|
'webhook_url'
|
||||||
|
);
|
||||||
const [webhookKeyField, webhookKeyMeta, webhookKeyHelpers] = useField(
|
const [webhookKeyField, webhookKeyMeta, webhookKeyHelpers] = useField(
|
||||||
'webhook_key'
|
'webhook_key'
|
||||||
);
|
);
|
||||||
@@ -65,17 +66,37 @@ function WebhookSubForm({ i18n, enableWebhooks }) {
|
|||||||
loadCredentialType();
|
loadCredentialType();
|
||||||
}, [loadCredentialType]);
|
}, [loadCredentialType]);
|
||||||
|
|
||||||
const changeWebhookKey = async () => {
|
useEffect(() => {
|
||||||
try {
|
if (enableWebhooks) {
|
||||||
|
webhookServiceHelpers.setValue(webhookServiceMeta.initialValue);
|
||||||
|
webhookUrlHelpers.setValue(webhookUrlMeta.initialValue);
|
||||||
|
webhookKeyHelpers.setValue(webhookKeyMeta.initialValue);
|
||||||
|
webhookCredentialHelpers.setValue(webhookCredentialMeta.initialValue);
|
||||||
|
} else {
|
||||||
|
webhookServiceHelpers.setValue('');
|
||||||
|
webhookUrlHelpers.setValue('');
|
||||||
|
webhookKeyHelpers.setValue('');
|
||||||
|
webhookCredentialHelpers.setValue(null);
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [enableWebhooks]);
|
||||||
|
|
||||||
|
const { request: fetchWebhookKey, error: webhookKeyError } = useRequest(
|
||||||
|
useCallback(async () => {
|
||||||
const {
|
const {
|
||||||
data: { webhook_key: key },
|
data: { webhook_key: key },
|
||||||
} = await JobTemplatesAPI.updateWebhookKey(id);
|
} = await JobTemplatesAPI.updateWebhookKey(id);
|
||||||
webhookKeyHelpers.setValue(key);
|
webhookKeyHelpers.setValue(key);
|
||||||
} catch (err) {
|
}, [webhookKeyHelpers, id])
|
||||||
setContentError(err);
|
);
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
const changeWebhookKey = async () => {
|
||||||
|
await fetchWebhookKey();
|
||||||
|
};
|
||||||
|
const isUpdateKeyDisabled =
|
||||||
|
pathname.endsWith('/add') ||
|
||||||
|
webhookKeyMeta.initialValue ===
|
||||||
|
'A NEW WEBHOOK KEY WILL BE GENERATED ON SAVE.';
|
||||||
const webhookServiceOptions = [
|
const webhookServiceOptions = [
|
||||||
{
|
{
|
||||||
value: '',
|
value: '',
|
||||||
@@ -97,7 +118,7 @@ function WebhookSubForm({ i18n, enableWebhooks }) {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
if (error || contentError) {
|
if (error || webhookKeyError) {
|
||||||
return <ContentError error={error} />;
|
return <ContentError error={error} />;
|
||||||
}
|
}
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
@@ -120,7 +141,11 @@ function WebhookSubForm({ i18n, enableWebhooks }) {
|
|||||||
onChange={(event, val) => {
|
onChange={(event, val) => {
|
||||||
webhookServiceHelpers.setValue(val);
|
webhookServiceHelpers.setValue(val);
|
||||||
webhookUrlHelpers.setValue(
|
webhookUrlHelpers.setValue(
|
||||||
`${origin}/api/v2/job_templates/${id}/${val}/`
|
pathname.endsWith('/add')
|
||||||
|
? i18n
|
||||||
|
._(t`a new webhook url will be generated on save.`)
|
||||||
|
.toUpperCase()
|
||||||
|
: `${origin}/api/v2/${templateType}s/${id}/${val}/`
|
||||||
);
|
);
|
||||||
if (val === webhookServiceMeta.initialValue || val === '') {
|
if (val === webhookServiceMeta.initialValue || val === '') {
|
||||||
webhookKeyHelpers.setValue(webhookKeyMeta.initialValue);
|
webhookKeyHelpers.setValue(webhookKeyMeta.initialValue);
|
||||||
@@ -138,53 +163,53 @@ function WebhookSubForm({ i18n, enableWebhooks }) {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
{!jtAddMatch && (
|
<>
|
||||||
<>
|
<FormGroup
|
||||||
<FormGroup
|
type="text"
|
||||||
type="text"
|
fieldId="jt-webhookURL"
|
||||||
fieldId="jt-webhookURL"
|
label={i18n._(t`Webhook URL`)}
|
||||||
label={i18n._(t`Webhook URL`)}
|
name="webhook_url"
|
||||||
name="webhook_url"
|
>
|
||||||
>
|
<FieldTooltip
|
||||||
<FieldTooltip
|
content={i18n._(
|
||||||
content={i18n._(
|
t`Webhook services can launch jobs with this workflow job template by making a POST request to this URL.`
|
||||||
t`Webhook services can launch jobs with this workflow job template by making a POST request to this URL.`
|
)}
|
||||||
)}
|
/>
|
||||||
/>
|
<TextInput
|
||||||
|
id="t-webhookURL"
|
||||||
|
aria-label={i18n._(t`Webhook URL`)}
|
||||||
|
value={webhookUrlField.value}
|
||||||
|
isReadOnly
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
<FormGroup
|
||||||
|
label={i18n._(t`Webhook Key`)}
|
||||||
|
fieldId="template-webhook_key"
|
||||||
|
>
|
||||||
|
<FieldTooltip
|
||||||
|
content={i18n._(
|
||||||
|
t`Webhook services can use this as a shared secret.`
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<InputGroup>
|
||||||
<TextInput
|
<TextInput
|
||||||
id="t-webhookURL"
|
id="template-webhook_key"
|
||||||
aria-label={i18n._(t`Webhook URL`)}
|
|
||||||
value={webhookUrlField.value}
|
|
||||||
isReadOnly
|
isReadOnly
|
||||||
|
aria-label="wfjt-webhook-key"
|
||||||
|
value={webhookKeyField.value}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
<Button
|
||||||
<FormGroup
|
isDisabled={isUpdateKeyDisabled}
|
||||||
label={i18n._(t`Webhook Key`)}
|
variant="tertiary"
|
||||||
fieldId="template-webhook_key"
|
aria-label={i18n._(t`Update webhook key`)}
|
||||||
>
|
onClick={changeWebhookKey}
|
||||||
<FieldTooltip
|
>
|
||||||
content={i18n._(
|
<SyncAltIcon />
|
||||||
t`Webhook services can use this as a shared secret.`
|
</Button>
|
||||||
)}
|
</InputGroup>
|
||||||
/>
|
</FormGroup>
|
||||||
<InputGroup>
|
</>
|
||||||
<TextInput
|
|
||||||
id="template-webhook_key"
|
|
||||||
isReadOnly
|
|
||||||
aria-label="wfjt-webhook-key"
|
|
||||||
value={webhookKeyField.value}
|
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
variant="tertiary"
|
|
||||||
aria-label={i18n._(t`Update webhook key`)}
|
|
||||||
onClick={changeWebhookKey}
|
|
||||||
>
|
|
||||||
<SyncAltIcon />
|
|
||||||
</Button>
|
|
||||||
</InputGroup>
|
|
||||||
</FormGroup>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
{credTypeId && (
|
{credTypeId && (
|
||||||
<CredentialLookup
|
<CredentialLookup
|
||||||
label={i18n._(t`Webhook Credential`)}
|
label={i18n._(t`Webhook Credential`)}
|
||||||
124
awx/ui_next/src/screens/Template/shared/WebhookSubForm.test.jsx
Normal file
124
awx/ui_next/src/screens/Template/shared/WebhookSubForm.test.jsx
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { act } from 'react-dom/test-utils';
|
||||||
|
import { Route } from 'react-router-dom';
|
||||||
|
import { createMemoryHistory } from 'history';
|
||||||
|
|
||||||
|
import { mountWithContexts, waitForElement } from '@testUtils/enzymeHelpers';
|
||||||
|
import { CredentialsAPI } from '@api';
|
||||||
|
import { Formik } from 'formik';
|
||||||
|
|
||||||
|
import WebhookSubForm from './WebhookSubForm';
|
||||||
|
|
||||||
|
jest.mock('@api');
|
||||||
|
|
||||||
|
describe('<WebhooksSubForm />', () => {
|
||||||
|
let wrapper;
|
||||||
|
let history;
|
||||||
|
const initialValues = {
|
||||||
|
webhook_url: '/api/v2/job_templates/51/github/',
|
||||||
|
webhook_credential: { id: 1, name: 'Github credential' },
|
||||||
|
webhook_service: 'github',
|
||||||
|
webhook_key: 'webhook key',
|
||||||
|
};
|
||||||
|
beforeEach(async () => {
|
||||||
|
history = createMemoryHistory({
|
||||||
|
initialEntries: ['templates/job_template/51/edit'],
|
||||||
|
});
|
||||||
|
CredentialsAPI.read.mockResolvedValue({
|
||||||
|
data: { results: [{ id: 12, name: 'Github credential' }] },
|
||||||
|
});
|
||||||
|
await act(async () => {
|
||||||
|
wrapper = mountWithContexts(
|
||||||
|
<Route path="templates/:templateType/:id/edit">
|
||||||
|
<Formik initialValues={initialValues}>
|
||||||
|
<WebhookSubForm enableWebhooks />
|
||||||
|
</Formik>
|
||||||
|
</Route>,
|
||||||
|
{
|
||||||
|
context: {
|
||||||
|
router: {
|
||||||
|
history,
|
||||||
|
route: {
|
||||||
|
location: { pathname: 'templates/job_template/51/edit' },
|
||||||
|
match: { params: { id: 51, templateType: 'job_template' } },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
afterEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
test('mounts properly', () => {
|
||||||
|
expect(wrapper.length).toBe(1);
|
||||||
|
});
|
||||||
|
test('should render initial values properly', () => {
|
||||||
|
waitForElement(wrapper, 'Lookup__ChipHolder', el => el.lenth > 0);
|
||||||
|
expect(wrapper.find('AnsibleSelect').prop('value')).toBe('github');
|
||||||
|
expect(
|
||||||
|
wrapper.find('TextInputBase[aria-label="Webhook URL"]').prop('value')
|
||||||
|
).toContain('/api/v2/job_templates/51/github/');
|
||||||
|
expect(
|
||||||
|
wrapper.find('TextInputBase[aria-label="wfjt-webhook-key"]').prop('value')
|
||||||
|
).toBe('webhook key');
|
||||||
|
expect(
|
||||||
|
wrapper
|
||||||
|
.find('Chip')
|
||||||
|
.find('span')
|
||||||
|
.text()
|
||||||
|
).toBe('Github credential');
|
||||||
|
});
|
||||||
|
test('should make other credential type available', async () => {
|
||||||
|
CredentialsAPI.read.mockResolvedValue({
|
||||||
|
data: { results: [{ id: 13, name: 'GitLab credential' }] },
|
||||||
|
});
|
||||||
|
await act(async () =>
|
||||||
|
wrapper.find('AnsibleSelect').prop('onChange')({}, 'gitlab')
|
||||||
|
);
|
||||||
|
expect(CredentialsAPI.read).toHaveBeenCalledWith({
|
||||||
|
namespace: 'gitlab_token',
|
||||||
|
});
|
||||||
|
wrapper.update();
|
||||||
|
expect(
|
||||||
|
wrapper.find('TextInputBase[aria-label="Webhook URL"]').prop('value')
|
||||||
|
).toContain('/api/v2/job_templates/51/gitlab/');
|
||||||
|
expect(
|
||||||
|
wrapper.find('TextInputBase[aria-label="wfjt-webhook-key"]').prop('value')
|
||||||
|
).toBe('A NEW WEBHOOK KEY WILL BE GENERATED ON SAVE.');
|
||||||
|
});
|
||||||
|
test('should have disabled button to update webhook key', async () => {
|
||||||
|
let newWrapper;
|
||||||
|
await act(async () => {
|
||||||
|
newWrapper = mountWithContexts(
|
||||||
|
<Route path="templates/:templateType/:id/edit">
|
||||||
|
<Formik
|
||||||
|
initialValues={{
|
||||||
|
...initialValues,
|
||||||
|
webhook_key: 'A NEW WEBHOOK KEY WILL BE GENERATED ON SAVE.',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<WebhookSubForm enableWebhooks />
|
||||||
|
</Formik>
|
||||||
|
</Route>,
|
||||||
|
{
|
||||||
|
context: {
|
||||||
|
router: {
|
||||||
|
history,
|
||||||
|
route: {
|
||||||
|
location: { pathname: 'templates/job_template/51/edit' },
|
||||||
|
match: { params: { id: 51, templateType: 'job_template' } },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
expect(
|
||||||
|
newWrapper
|
||||||
|
.find("Button[aria-label='Update webhook key']")
|
||||||
|
.prop('isDisabled')
|
||||||
|
).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user