From 222fecc5f686d29de90b06e309eaeec048fbe371 Mon Sep 17 00:00:00 2001 From: Alex Corey Date: Mon, 13 Apr 2020 15:08:06 -0400 Subject: [PATCH] adds test for new webhook component --- awx/ui_next/src/screens/Template/Template.jsx | 2 +- .../Template/shared/JobTemplateForm.jsx | 42 ++---- .../Template/shared/JobTemplateForm.test.jsx | 43 ++++++ ...WebhooksSubForm.jsx => WebhookSubForm.jsx} | 141 +++++++++++------- .../Template/shared/WebhookSubForm.test.jsx | 124 +++++++++++++++ 5 files changed, 260 insertions(+), 92 deletions(-) rename awx/ui_next/src/screens/Template/shared/{WebhooksSubForm.jsx => WebhookSubForm.jsx} (60%) create mode 100644 awx/ui_next/src/screens/Template/shared/WebhookSubForm.test.jsx diff --git a/awx/ui_next/src/screens/Template/Template.jsx b/awx/ui_next/src/screens/Template/Template.jsx index b95cf64b88..80e4df8539 100644 --- a/awx/ui_next/src/screens/Template/Template.jsx +++ b/awx/ui_next/src/screens/Template/Template.jsx @@ -45,7 +45,7 @@ function Template({ i18n, me, setBreadcrumb }) { role_level: 'notification_admin_role', }), ]); - if (data?.related?.webhook_key) { + if (data.webhook_service && data?.related?.webhook_key) { const { data: { webhook_key }, } = await JobTemplatesAPI.readWebhookKey(templateId); diff --git a/awx/ui_next/src/screens/Template/shared/JobTemplateForm.jsx b/awx/ui_next/src/screens/Template/shared/JobTemplateForm.jsx index 621b5d9e64..0e96b8995d 100644 --- a/awx/ui_next/src/screens/Template/shared/JobTemplateForm.jsx +++ b/awx/ui_next/src/screens/Template/shared/JobTemplateForm.jsx @@ -40,7 +40,7 @@ import { import { JobTemplatesAPI, ProjectsAPI } from '@api'; import LabelSelect from './LabelSelect'; import PlaybookSelect from './PlaybookSelect'; -import WebhookSubForm from './WebhooksSubForm'; +import WebhookSubForm from './WebhookSubForm'; const { origin } = document.location; @@ -94,10 +94,6 @@ function JobTemplateForm({ ); const [jobTagsField, , jobTagsHelpers] = useField('job_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 { request: fetchProject, @@ -189,19 +185,11 @@ function JobTemplateForm({ callbackUrl = `${origin}${path}`; } - if ( - instanceGroupLoading || - hasProjectLoading - // credentialContentLoading - ) { + if (instanceGroupLoading || hasProjectLoading) { return ; } - if ( - instanceGroupError || - projectContentError - // credentialContentError - ) { + if (instanceGroupError || projectContentError) { return ; } @@ -530,23 +518,9 @@ function JobTemplateForm({ } id="wfjt-enabled-webhooks" - isChecked={ - Boolean(webhookService[0].value) || enableWebhooks - } + isChecked={enableWebhooks} onChange={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 - ); }} /> ', () => { ); }); + test('webhooks should render properly, without data', async () => { + let wrapper; + const history = createMemoryHistory({ + initialEntries: ['/templates/job_template/1/edit'], + }); + await act(async () => { + wrapper = mountWithContexts( + ( + + )} + />, + { + 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 () => { const handleSubmit = jest.fn(); let wrapper; diff --git a/awx/ui_next/src/screens/Template/shared/WebhooksSubForm.jsx b/awx/ui_next/src/screens/Template/shared/WebhookSubForm.jsx similarity index 60% rename from awx/ui_next/src/screens/Template/shared/WebhooksSubForm.jsx rename to awx/ui_next/src/screens/Template/shared/WebhookSubForm.jsx index 6430fc1f6a..7211990c90 100644 --- a/awx/ui_next/src/screens/Template/shared/WebhooksSubForm.jsx +++ b/awx/ui_next/src/screens/Template/shared/WebhookSubForm.jsx @@ -1,6 +1,6 @@ -import React, { useEffect, useCallback, useState } from 'react'; +import React, { useEffect, useCallback } from 'react'; 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 { withI18n } from '@lingui/react'; import { @@ -20,9 +20,8 @@ import { FieldTooltip } from '@components/FormField'; import { JobTemplatesAPI, CredentialTypesAPI } from '@api'; function WebhookSubForm({ i18n, enableWebhooks }) { - const [contentError, setContentError] = useState(null); - const jtAddMatch = useRouteMatch('/templates/job_template/add'); - const { id } = useParams(); + const { id, templateType } = useParams(); + const { pathname } = useLocation(); const { origin } = document.location; @@ -32,7 +31,9 @@ function WebhookSubForm({ i18n, enableWebhooks }) { webhookServiceHelpers, ] = useField('webhook_service'); - const [webhookUrlField, , webhookUrlHelpers] = useField('webhook_url'); + const [webhookUrlField, webhookUrlMeta, webhookUrlHelpers] = useField( + 'webhook_url' + ); const [webhookKeyField, webhookKeyMeta, webhookKeyHelpers] = useField( 'webhook_key' ); @@ -65,17 +66,37 @@ function WebhookSubForm({ i18n, enableWebhooks }) { loadCredentialType(); }, [loadCredentialType]); - const changeWebhookKey = async () => { - try { + useEffect(() => { + 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 { data: { webhook_key: key }, } = await JobTemplatesAPI.updateWebhookKey(id); webhookKeyHelpers.setValue(key); - } catch (err) { - setContentError(err); - } - }; + }, [webhookKeyHelpers, id]) + ); + const changeWebhookKey = async () => { + await fetchWebhookKey(); + }; + const isUpdateKeyDisabled = + pathname.endsWith('/add') || + webhookKeyMeta.initialValue === + 'A NEW WEBHOOK KEY WILL BE GENERATED ON SAVE.'; const webhookServiceOptions = [ { value: '', @@ -97,7 +118,7 @@ function WebhookSubForm({ i18n, enableWebhooks }) { }, ]; - if (error || contentError) { + if (error || webhookKeyError) { return ; } if (isLoading) { @@ -120,7 +141,11 @@ function WebhookSubForm({ i18n, enableWebhooks }) { onChange={(event, val) => { webhookServiceHelpers.setValue(val); 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 === '') { webhookKeyHelpers.setValue(webhookKeyMeta.initialValue); @@ -138,53 +163,53 @@ function WebhookSubForm({ i18n, enableWebhooks }) { }} /> - {!jtAddMatch && ( - <> - - + <> + + + + + + + - - - - - - - - - - )} + + + + + {credTypeId && ( ', () => { + 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( + + + + + , + { + 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( + + + + + , + { + 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); + }); +});