mirror of
https://github.com/ansible/awx.git
synced 2026-01-16 20:30:46 -03:30
Use Webhook Fields in WFJT Form
Update WorkflowJobTemplateForm to use `WebhookSubForm`. Also, update related unit-tests. closes: https://github.com/ansible/awx/issues/6742
This commit is contained in:
parent
8c57a92a65
commit
0b8aabbd16
@ -59,7 +59,7 @@ class WorkflowJobTemplate extends Component {
|
||||
try {
|
||||
const { data } = await WorkflowJobTemplatesAPI.readDetail(id);
|
||||
let webhookKey;
|
||||
if (data?.related?.webhook_key) {
|
||||
if (data?.webhook_service && data?.related?.webhook_key) {
|
||||
webhookKey = await WorkflowJobTemplatesAPI.readWebhookKey(id);
|
||||
}
|
||||
if (data?.summary_fields?.webhook_credential) {
|
||||
@ -80,7 +80,7 @@ class WorkflowJobTemplate extends Component {
|
||||
});
|
||||
setBreadcrumb(data);
|
||||
this.setState({
|
||||
template: { ...data, webhook_key: webhookKey.data.webhook_key },
|
||||
template: { ...data, webhook_key: webhookKey?.data.webhook_key },
|
||||
isNotifAdmin: notifAdminRes.data.results.length > 0,
|
||||
});
|
||||
} catch (err) {
|
||||
|
||||
@ -32,6 +32,7 @@ describe('<WorkflowJobTemplate/>', () => {
|
||||
created: '2015-07-07T17:21:26.429745Z',
|
||||
modified: '2019-08-11T19:47:37.980466Z',
|
||||
extra_vars: '',
|
||||
webhook_service: 'github',
|
||||
summary_fields: {
|
||||
webhook_credential: { id: 1234567, name: 'Foo Webhook Credential' },
|
||||
created_by: { id: 1, username: 'Athena' },
|
||||
|
||||
@ -512,9 +512,7 @@ function JobTemplateForm({
|
||||
{i18n._(t`Enable Webhook`)}
|
||||
|
||||
<FieldTooltip
|
||||
content={i18n._(
|
||||
t`Enable webhook for this workflow job template.`
|
||||
)}
|
||||
content={i18n._(t`Enable webhook for this template.`)}
|
||||
/>
|
||||
</span>
|
||||
}
|
||||
@ -542,7 +540,10 @@ function JobTemplateForm({
|
||||
</FormCheckboxLayout>
|
||||
</FormGroup>
|
||||
</FormFullWidthLayout>
|
||||
<WebhookSubForm enableWebhooks={enableWebhooks} />
|
||||
<WebhookSubForm
|
||||
enableWebhooks={enableWebhooks}
|
||||
templateType={template.type}
|
||||
/>
|
||||
{allowCallbacks && (
|
||||
<>
|
||||
{callbackUrl && (
|
||||
|
||||
@ -36,7 +36,7 @@ describe('<JobTemplateForm />', () => {
|
||||
{ id: 2, kind: 'ssh', name: 'Bar' },
|
||||
],
|
||||
},
|
||||
related: { webhook_receiver: '/api/v2/workflow_job_templates/57/gitlab/' },
|
||||
related: { webhook_receiver: '/api/v2/job_templates/57/gitlab/' },
|
||||
webhook_key: 'webhook key',
|
||||
webhook_service: 'github',
|
||||
webhook_credential: 7,
|
||||
@ -273,7 +273,7 @@ describe('<JobTemplateForm />', () => {
|
||||
expect(JobTemplatesAPI.updateWebhookKey).toBeCalledWith('1');
|
||||
expect(
|
||||
wrapper.find('TextInputBase[aria-label="Webhook URL"]').prop('value')
|
||||
).toContain('/api/v2/workflow_job_templates/57/gitlab/');
|
||||
).toContain('/api/v2/job_templates/57/gitlab/');
|
||||
|
||||
wrapper.update();
|
||||
|
||||
|
||||
@ -17,10 +17,15 @@ import { FormColumnLayout } from '@components/FormLayout';
|
||||
import { CredentialLookup } from '@components/Lookup';
|
||||
import AnsibleSelect from '@components/AnsibleSelect';
|
||||
import { FieldTooltip } from '@components/FormField';
|
||||
import { JobTemplatesAPI, CredentialTypesAPI } from '@api';
|
||||
import {
|
||||
JobTemplatesAPI,
|
||||
WorkflowJobTemplatesAPI,
|
||||
CredentialTypesAPI,
|
||||
} from '@api';
|
||||
|
||||
function WebhookSubForm({ i18n, enableWebhooks, templateType }) {
|
||||
const { id } = useParams();
|
||||
|
||||
function WebhookSubForm({ i18n, enableWebhooks }) {
|
||||
const { id, templateType } = useParams();
|
||||
const { pathname } = useLocation();
|
||||
|
||||
const { origin } = document.location;
|
||||
@ -83,11 +88,15 @@ function WebhookSubForm({ i18n, enableWebhooks }) {
|
||||
|
||||
const { request: fetchWebhookKey, error: webhookKeyError } = useRequest(
|
||||
useCallback(async () => {
|
||||
const updateWebhookKey =
|
||||
templateType === 'job_template'
|
||||
? JobTemplatesAPI.updateWebhookKey(id)
|
||||
: WorkflowJobTemplatesAPI.updateWebhookKey(id);
|
||||
const {
|
||||
data: { webhook_key: key },
|
||||
} = await JobTemplatesAPI.updateWebhookKey(id);
|
||||
} = await updateWebhookKey;
|
||||
webhookKeyHelpers.setValue(key);
|
||||
}, [webhookKeyHelpers, id])
|
||||
}, [webhookKeyHelpers, id, templateType])
|
||||
);
|
||||
|
||||
const changeWebhookKey = async () => {
|
||||
|
||||
@ -11,7 +11,7 @@ import WebhookSubForm from './WebhookSubForm';
|
||||
|
||||
jest.mock('@api');
|
||||
|
||||
describe('<WebhooksSubForm />', () => {
|
||||
describe('<WebhookSubForm />', () => {
|
||||
let wrapper;
|
||||
let history;
|
||||
const initialValues = {
|
||||
@ -31,7 +31,7 @@ describe('<WebhooksSubForm />', () => {
|
||||
wrapper = mountWithContexts(
|
||||
<Route path="templates/:templateType/:id/edit">
|
||||
<Formik initialValues={initialValues}>
|
||||
<WebhookSubForm enableWebhooks />
|
||||
<WebhookSubForm enableWebhooks templateType="job_template" />
|
||||
</Formik>
|
||||
</Route>,
|
||||
{
|
||||
@ -50,6 +50,7 @@ describe('<WebhooksSubForm />', () => {
|
||||
});
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
wrapper.unmount();
|
||||
});
|
||||
test('mounts properly', () => {
|
||||
expect(wrapper.length).toBe(1);
|
||||
@ -99,7 +100,7 @@ describe('<WebhooksSubForm />', () => {
|
||||
webhook_key: 'A NEW WEBHOOK KEY WILL BE GENERATED ON SAVE.',
|
||||
}}
|
||||
>
|
||||
<WebhookSubForm enableWebhooks />
|
||||
<WebhookSubForm enableWebhooks templateType="job_template" />
|
||||
</Formik>
|
||||
</Route>,
|
||||
{
|
||||
@ -121,4 +122,39 @@ describe('<WebhooksSubForm />', () => {
|
||||
.prop('isDisabled')
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
test('test whether the workflow template type is part of the webhook url', async () => {
|
||||
let newWrapper;
|
||||
const webhook_url = '/api/v2/workflow_job_templates/42/github/';
|
||||
await act(async () => {
|
||||
newWrapper = mountWithContexts(
|
||||
<Route path="templates/:templateType/:id/edit">
|
||||
<Formik initialValues={{ ...initialValues, webhook_url }}>
|
||||
<WebhookSubForm
|
||||
enableWebhooks
|
||||
templateType="workflow_job_template"
|
||||
/>
|
||||
</Formik>
|
||||
</Route>,
|
||||
{
|
||||
context: {
|
||||
router: {
|
||||
history,
|
||||
route: {
|
||||
location: {
|
||||
pathname: 'templates/workflow_job_template/51/edit',
|
||||
},
|
||||
match: {
|
||||
params: { id: 51, templateType: 'workflow_job_template' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
);
|
||||
});
|
||||
expect(
|
||||
newWrapper.find('TextInputBase[aria-label="Webhook URL"]').prop('value')
|
||||
).toContain(webhook_url);
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,26 +1,13 @@
|
||||
import React, { useState, useEffect, useCallback } from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import { t } from '@lingui/macro';
|
||||
import { useRouteMatch, useParams } from 'react-router-dom';
|
||||
|
||||
import PropTypes, { shape } from 'prop-types';
|
||||
|
||||
import { withI18n } from '@lingui/react';
|
||||
import { useField, withFormik } from 'formik';
|
||||
import {
|
||||
Form,
|
||||
FormGroup,
|
||||
InputGroup,
|
||||
Button,
|
||||
TextInput,
|
||||
Checkbox,
|
||||
} from '@patternfly/react-core';
|
||||
import { Form, FormGroup, Checkbox } from '@patternfly/react-core';
|
||||
import { required } from '@util/validators';
|
||||
import { SyncAltIcon } from '@patternfly/react-icons';
|
||||
|
||||
import AnsibleSelect from '@components/AnsibleSelect';
|
||||
import { WorkflowJobTemplatesAPI, CredentialTypesAPI } from '@api';
|
||||
|
||||
import useRequest from '@util/useRequest';
|
||||
import FormField, {
|
||||
FieldTooltip,
|
||||
FormSubmitError,
|
||||
@ -30,26 +17,25 @@ import {
|
||||
FormFullWidthLayout,
|
||||
FormCheckboxLayout,
|
||||
} from '@components/FormLayout';
|
||||
import ContentLoading from '@components/ContentLoading';
|
||||
import OrganizationLookup from '@components/Lookup/OrganizationLookup';
|
||||
import CredentialLookup from '@components/Lookup/CredentialLookup';
|
||||
import { InventoryLookup } from '@components/Lookup';
|
||||
import { VariablesField } from '@components/CodeMirrorInput';
|
||||
import FormActionGroup from '@components/FormActionGroup';
|
||||
import ContentError from '@components/ContentError';
|
||||
import CheckboxField from '@components/FormField/CheckboxField';
|
||||
import LabelSelect from './LabelSelect';
|
||||
import WebhookSubForm from './WebhookSubForm';
|
||||
import { WorkFlowJobTemplate } from '@types';
|
||||
|
||||
const urlOrigin = window.location.origin;
|
||||
|
||||
function WorkflowJobTemplateForm({
|
||||
template,
|
||||
handleSubmit,
|
||||
handleCancel,
|
||||
i18n,
|
||||
submitError,
|
||||
}) {
|
||||
const { id } = useParams();
|
||||
const wfjtAddMatch = useRouteMatch('/templates/workflow_job_template/add');
|
||||
|
||||
const [hasContentError, setContentError] = useState(null);
|
||||
|
||||
const [organizationField, organizationMeta, organizationHelpers] = useField(
|
||||
@ -60,125 +46,12 @@ function WorkflowJobTemplateForm({
|
||||
);
|
||||
const [labelsField, , labelsHelpers] = useField('labels');
|
||||
|
||||
const [
|
||||
webhookServiceField,
|
||||
webhookServiceMeta,
|
||||
webhookServiceHelpers,
|
||||
] = useField('webhook_service');
|
||||
|
||||
const [webhookKeyField, webhookKeyMeta, webhookKeyHelpers] = useField(
|
||||
'webhook_key'
|
||||
const [enableWebhooks, setEnableWebhooks] = useState(
|
||||
Boolean(template.webhook_service)
|
||||
);
|
||||
|
||||
const [hasWebhooks, setHasWebhooks] = useState(
|
||||
Boolean(webhookServiceField.value)
|
||||
);
|
||||
|
||||
const [
|
||||
webhookCredentialField,
|
||||
webhookCredentialMeta,
|
||||
webhookCredentialHelpers,
|
||||
] = useField('webhook_credential');
|
||||
|
||||
const [webhookUrlField, webhookUrlMeta, webhookUrlHelpers] = useField(
|
||||
'webhook_url'
|
||||
);
|
||||
|
||||
const webhookServiceOptions = [
|
||||
{
|
||||
value: '',
|
||||
key: '',
|
||||
label: i18n._(t`Choose a Webhook Service`),
|
||||
isDisabled: true,
|
||||
},
|
||||
{
|
||||
value: 'github',
|
||||
key: 'github',
|
||||
label: i18n._(t`GitHub`),
|
||||
isDisabled: false,
|
||||
},
|
||||
{
|
||||
value: 'gitlab',
|
||||
key: 'gitlab',
|
||||
label: i18n._(t`GitLab`),
|
||||
isDisabled: false,
|
||||
},
|
||||
];
|
||||
|
||||
const storeWebhookValues = webhookServiceValue => {
|
||||
if (
|
||||
webhookServiceValue === webhookServiceMeta.initialValue ||
|
||||
webhookServiceValue === ''
|
||||
) {
|
||||
webhookCredentialHelpers.setValue(webhookCredentialMeta.initialValue);
|
||||
webhookUrlHelpers.setValue(webhookUrlMeta.initialValue);
|
||||
webhookServiceHelpers.setValue(webhookServiceMeta.initialValue);
|
||||
webhookKeyHelpers.setValue(webhookKeyMeta.initialValue);
|
||||
} else {
|
||||
webhookCredentialHelpers.setValue(null);
|
||||
webhookUrlHelpers.setValue(
|
||||
`${urlOrigin}/api/v2/workflow_job_templates/${id}/${webhookServiceValue}/`
|
||||
);
|
||||
webhookKeyHelpers.setValue(
|
||||
i18n._(t`a new webhook key will be generated on save.`).toUpperCase()
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const handleWebhookEnablement = (enabledWebhooks, webhookServiceValue) => {
|
||||
if (!enabledWebhooks) {
|
||||
webhookCredentialHelpers.setValue(null);
|
||||
webhookServiceHelpers.setValue('');
|
||||
webhookUrlHelpers.setValue('');
|
||||
webhookKeyHelpers.setValue('');
|
||||
} else {
|
||||
storeWebhookValues(webhookServiceValue);
|
||||
}
|
||||
};
|
||||
|
||||
const {
|
||||
request: loadCredentialType,
|
||||
error: contentError,
|
||||
contentLoading,
|
||||
result: credTypeId,
|
||||
} = useRequest(
|
||||
useCallback(async () => {
|
||||
let results;
|
||||
if (webhookServiceField.value) {
|
||||
results = await CredentialTypesAPI.read({
|
||||
namespace: `${webhookServiceField.value}_token`,
|
||||
});
|
||||
// TODO: Consider how to handle the situation where the results returns
|
||||
// and empty array, or any of the other values is undefined or null (data, results, id)
|
||||
}
|
||||
return results?.data?.results[0]?.id;
|
||||
}, [webhookServiceField.value])
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
loadCredentialType();
|
||||
}, [loadCredentialType]);
|
||||
|
||||
// TODO: Convert this function below to useRequest. Might want to create a new
|
||||
// webhookkey component that handles all of that api calls. Will also need
|
||||
// to move this api call out of WorkflowJobTemplate.jsx and add it to workflowJobTemplateDetai.jsx
|
||||
const changeWebhookKey = async () => {
|
||||
try {
|
||||
const {
|
||||
data: { webhook_key: key },
|
||||
} = await WorkflowJobTemplatesAPI.updateWebhookKey(id);
|
||||
webhookKeyHelpers.setValue(key);
|
||||
} catch (err) {
|
||||
setContentError(err);
|
||||
}
|
||||
};
|
||||
|
||||
if (hasContentError || contentError) {
|
||||
return <ContentError error={contentError || hasContentError} />;
|
||||
}
|
||||
|
||||
if (contentLoading) {
|
||||
return <ContentLoading />;
|
||||
if (hasContentError) {
|
||||
return <ContentError error={hasContentError} />;
|
||||
}
|
||||
|
||||
return (
|
||||
@ -232,7 +105,7 @@ function WorkflowJobTemplateForm({
|
||||
/>
|
||||
<FormField
|
||||
type="text"
|
||||
label={i18n._(t`SCM Branch`)}
|
||||
label={i18n._(t`Source Control Branch`)}
|
||||
tooltip={i18n._(
|
||||
t`Select a branch for the workflow. This branch is applied to all job template nodes that prompt for a branch.`
|
||||
)}
|
||||
@ -274,16 +147,15 @@ function WorkflowJobTemplateForm({
|
||||
|
||||
<FieldTooltip
|
||||
content={i18n._(
|
||||
t`Enable webhook for this workflow job template.`
|
||||
t`Enable Webhook for this workflow job template.`
|
||||
)}
|
||||
/>
|
||||
</span>
|
||||
}
|
||||
id="wfjt-enabled-webhooks"
|
||||
isChecked={Boolean(webhookServiceField.value) || hasWebhooks}
|
||||
isChecked={enableWebhooks}
|
||||
onChange={checked => {
|
||||
setHasWebhooks(checked);
|
||||
handleWebhookEnablement(checked, webhookServiceField.value);
|
||||
setEnableWebhooks(checked);
|
||||
}}
|
||||
/>
|
||||
<CheckboxField
|
||||
@ -295,92 +167,10 @@ function WorkflowJobTemplateForm({
|
||||
label={i18n._(t`Enable Concurrent Jobs`)}
|
||||
/>
|
||||
</FormCheckboxLayout>
|
||||
{hasWebhooks && (
|
||||
<FormColumnLayout>
|
||||
<FormGroup
|
||||
name="webhook_service"
|
||||
fieldId="webhook_service"
|
||||
helperTextInvalid={webhookServiceMeta.error}
|
||||
isValid={!(webhookServiceMeta.touched || webhookServiceMeta.error)}
|
||||
label={i18n._(t`Webhook Service`)}
|
||||
>
|
||||
<FieldTooltip content={i18n._(t`Select a webhook service`)} />
|
||||
<AnsibleSelect
|
||||
id="webhook_service"
|
||||
data={webhookServiceOptions}
|
||||
value={webhookServiceField.value}
|
||||
onChange={(event, val) => {
|
||||
storeWebhookValues(val);
|
||||
|
||||
webhookServiceHelpers.setValue(val);
|
||||
}}
|
||||
/>
|
||||
</FormGroup>
|
||||
{!wfjtAddMatch && (
|
||||
<>
|
||||
<FormGroup
|
||||
type="text"
|
||||
fieldId="wfjt-webhookURL"
|
||||
label={i18n._(t`Webhook URL`)}
|
||||
id="wfjt-webhook-url"
|
||||
name="webhook_url"
|
||||
>
|
||||
<FieldTooltip
|
||||
content={i18n._(
|
||||
t`Webhook services can launch jobs with this workflow job template by making a POST request to this URL.`
|
||||
)}
|
||||
/>
|
||||
<TextInput
|
||||
aria-label={i18n._(t`Webhook URL`)}
|
||||
value={webhookUrlField.value}
|
||||
isReadOnly
|
||||
/>
|
||||
</FormGroup>
|
||||
<FormGroup
|
||||
fieldId="wfjt-webhook-key"
|
||||
type="text"
|
||||
id="wfjt-webhook-key"
|
||||
name="webhook_key"
|
||||
label={i18n._(t`Webhook Key`)}
|
||||
>
|
||||
<FieldTooltip
|
||||
content={i18n._(
|
||||
t`Webhook services can use this as a shared secret.`
|
||||
)}
|
||||
/>
|
||||
<InputGroup>
|
||||
<TextInput
|
||||
isReadOnly
|
||||
aria-label="wfjt-webhook-key"
|
||||
value={webhookKeyField.value}
|
||||
/>
|
||||
<Button variant="tertiary" onClick={changeWebhookKey}>
|
||||
<SyncAltIcon />
|
||||
</Button>
|
||||
</InputGroup>
|
||||
</FormGroup>
|
||||
</>
|
||||
)}
|
||||
{credTypeId && (
|
||||
// TODO: Consider how to handle the situation where the results returns
|
||||
// an empty array, or any of the other values is undefined or null
|
||||
// (data, results, id)
|
||||
<CredentialLookup
|
||||
label={i18n._(t`Webhook Credential`)}
|
||||
tooltip={i18n._(
|
||||
t`Optionally select the credential to use to send status updates back to the webhook service.`
|
||||
)}
|
||||
credentialTypeId={credTypeId}
|
||||
onChange={value => {
|
||||
webhookCredentialHelpers.setValue(value || null);
|
||||
}}
|
||||
isValid={!webhookCredentialMeta.error}
|
||||
helperTextInvalid={webhookCredentialMeta.error}
|
||||
value={webhookCredentialField.value}
|
||||
/>
|
||||
)}
|
||||
</FormColumnLayout>
|
||||
)}
|
||||
<WebhookSubForm
|
||||
enableWebhooks={enableWebhooks}
|
||||
templateType={template.type}
|
||||
/>
|
||||
{submitError && <FormSubmitError error={submitError} />}
|
||||
<FormActionGroup onCancel={handleCancel} onSubmit={handleSubmit} />
|
||||
</Form>
|
||||
@ -388,6 +178,7 @@ function WorkflowJobTemplateForm({
|
||||
}
|
||||
|
||||
WorkflowJobTemplateForm.propTypes = {
|
||||
template: WorkFlowJobTemplate,
|
||||
handleSubmit: PropTypes.func.isRequired,
|
||||
handleCancel: PropTypes.func.isRequired,
|
||||
submitError: shape({}),
|
||||
@ -395,6 +186,12 @@ WorkflowJobTemplateForm.propTypes = {
|
||||
|
||||
WorkflowJobTemplateForm.defaultProps = {
|
||||
submitError: null,
|
||||
template: {
|
||||
name: '',
|
||||
description: '',
|
||||
inventory: undefined,
|
||||
project: undefined,
|
||||
},
|
||||
};
|
||||
|
||||
const FormikApp = withFormik({
|
||||
|
||||
@ -13,6 +13,7 @@ import {
|
||||
InventoriesAPI,
|
||||
} from '@api';
|
||||
|
||||
jest.mock('@api/models/CredentialTypes');
|
||||
jest.mock('@api/models/WorkflowJobTemplates');
|
||||
jest.mock('@api/models/Labels');
|
||||
jest.mock('@api/models/Organizations');
|
||||
@ -183,7 +184,6 @@ describe('<WorkflowJobTemplateForm/>', () => {
|
||||
expect(
|
||||
wrapper.find('Checkbox[aria-label="Enable Webhook"]').prop('isChecked')
|
||||
).toBe(true);
|
||||
|
||||
expect(
|
||||
wrapper.find('input[aria-label="wfjt-webhook-key"]').prop('readOnly')
|
||||
).toBe(true);
|
||||
@ -191,23 +191,25 @@ describe('<WorkflowJobTemplateForm/>', () => {
|
||||
wrapper.find('input[aria-label="wfjt-webhook-key"]').prop('value')
|
||||
).toBe('sdfghjklmnbvcdsew435678iokjhgfd');
|
||||
await act(() =>
|
||||
wrapper
|
||||
.find('FormGroup[name="webhook_key"]')
|
||||
.find('Button[variant="tertiary"]')
|
||||
.prop('onClick')()
|
||||
wrapper.find('Button[aria-label="Update webhook key"]').prop('onClick')()
|
||||
);
|
||||
expect(WorkflowJobTemplatesAPI.updateWebhookKey).toBeCalledWith('6');
|
||||
expect(
|
||||
wrapper.find('TextInputBase[aria-label="Webhook URL"]').prop('value')
|
||||
).toContain('/api/v2/workflow_job_templates/57/gitlab/');
|
||||
|
||||
wrapper.update();
|
||||
expect(wrapper.find('FormGroup[name="webhook_service"]').length).toBe(1);
|
||||
|
||||
act(() => wrapper.find('AnsibleSelect').prop('onChange')({}, 'gitlab'));
|
||||
expect(wrapper.find('AnsibleSelect#webhook_service').length).toBe(1);
|
||||
await act(async () => {
|
||||
wrapper.find('AnsibleSelect#webhook_service').prop('onChange')(
|
||||
{},
|
||||
'gitlab'
|
||||
);
|
||||
});
|
||||
wrapper.update();
|
||||
|
||||
expect(wrapper.find('AnsibleSelect').prop('value')).toBe('gitlab');
|
||||
expect(wrapper.find('AnsibleSelect#webhook_service').prop('value')).toBe(
|
||||
'gitlab'
|
||||
);
|
||||
});
|
||||
|
||||
test('handleSubmit is called on submit button click', async () => {
|
||||
|
||||
@ -73,6 +73,12 @@ export const JobTemplate = shape({
|
||||
project: number,
|
||||
});
|
||||
|
||||
export const WorkFlowJobTemplate = shape({
|
||||
name: string.isRequired,
|
||||
description: string,
|
||||
inventory: number,
|
||||
});
|
||||
|
||||
export const Inventory = shape({
|
||||
id: number.isRequired,
|
||||
name: string,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user