From 43ac5a0574f0ae3ed76aa6383b39d3d4bc0ddd79 Mon Sep 17 00:00:00 2001 From: Keith Grant Date: Fri, 21 Aug 2020 16:35:37 -0700 Subject: [PATCH] add some notification form tests; notification add screen --- .../components/FormField/ArrayTextField.jsx | 3 +- .../NotificationTemplate.jsx | 6 - .../NotificationTemplateAdd.jsx | 87 ++++++++++++- .../NotificationTemplateEdit.jsx | 1 - .../shared/NotificationTemplateForm.jsx | 31 +++-- .../shared/NotificationTemplateForm.test.jsx | 118 ++++++++++++++++++ 6 files changed, 227 insertions(+), 19 deletions(-) create mode 100644 awx/ui_next/src/screens/NotificationTemplate/shared/NotificationTemplateForm.test.jsx diff --git a/awx/ui_next/src/components/FormField/ArrayTextField.jsx b/awx/ui_next/src/components/FormField/ArrayTextField.jsx index 9b94036e3f..5f6609e1e1 100644 --- a/awx/ui_next/src/components/FormField/ArrayTextField.jsx +++ b/awx/ui_next/src/components/FormField/ArrayTextField.jsx @@ -20,6 +20,7 @@ function ArrayTextField(props) { const [field, meta, helpers] = useField({ name, validate }); const isValid = !(meta.touched && meta.error); + const value = field.value || []; return ( { helpers.setValue(value.split('\n').map(v => v.trim())); }} diff --git a/awx/ui_next/src/screens/NotificationTemplate/NotificationTemplate.jsx b/awx/ui_next/src/screens/NotificationTemplate/NotificationTemplate.jsx index 5cebeef78c..cc32c9eb64 100644 --- a/awx/ui_next/src/screens/NotificationTemplate/NotificationTemplate.jsx +++ b/awx/ui_next/src/screens/NotificationTemplate/NotificationTemplate.jsx @@ -94,12 +94,6 @@ function NotificationTemplate({ setBreadcrumb, i18n }) { to="/notification_templates/:id/details" exact /> - {/* - - */} {template && ( <> diff --git a/awx/ui_next/src/screens/NotificationTemplate/NotificationTemplateAdd.jsx b/awx/ui_next/src/screens/NotificationTemplate/NotificationTemplateAdd.jsx index bbf39b61a9..b5f464db17 100644 --- a/awx/ui_next/src/screens/NotificationTemplate/NotificationTemplateAdd.jsx +++ b/awx/ui_next/src/screens/NotificationTemplate/NotificationTemplateAdd.jsx @@ -1,5 +1,86 @@ -import React from 'react'; +import React, { useState, useEffect, useCallback } from 'react'; +import PropTypes from 'prop-types'; +import { useHistory, Link } from 'react-router-dom'; +import { t } from '@lingui/macro'; +import { withI18n } from '@lingui/react'; +import { Card, PageSection } from '@patternfly/react-core'; +import { CardBody } from '../../components/Card'; +import { NotificationTemplatesAPI } from '../../api'; +import useRequest from '../../util/useRequest'; +import ContentError from '../../components/ContentError'; +import NotificationTemplateForm from './shared/NotificationTemplateForm'; -export default function NotificationTemplateAdd() { - return
; +function NotificationTemplateAdd({ i18n }) { + const history = useHistory(); + const [formError, setFormError] = useState(null); + const { + result: defaultMessages, + error, + request: fetchDefaultMessages, + } = useRequest( + useCallback(async () => { + const { data } = await NotificationTemplatesAPI.readOptions(); + return data.actions.POST.messages; + }, []) + ); + + useEffect(() => { + fetchDefaultMessages(); + }, [fetchDefaultMessages]); + + const handleSubmit = async values => { + try { + const { data } = await NotificationTemplatesAPI.create(values); + history.push(`/notification_templates/${data.id}`); + } catch (err) { + setFormError(err); + } + }; + + const handleCancel = () => { + history.push('/notification_templates'); + }; + + if (error) { + return ( + + + + {error.response.status === 404 && ( + + {i18n._(t`Notification Template not found.`)}{' '} + + {i18n._(t`View all Notification Templates.`)} + + + )} + + + + ); + } + + return ( + + + + {defaultMessages && ( + + )} + + + + ); } + +NotificationTemplateAdd.contextTypes = { + custom_virtualenvs: PropTypes.arrayOf(PropTypes.string), +}; + +export { NotificationTemplateAdd as _NotificationTemplateAdd }; +export default withI18n()(NotificationTemplateAdd); diff --git a/awx/ui_next/src/screens/NotificationTemplate/NotificationTemplateEdit/NotificationTemplateEdit.jsx b/awx/ui_next/src/screens/NotificationTemplate/NotificationTemplateEdit/NotificationTemplateEdit.jsx index 91844a477c..3cff9b0833 100644 --- a/awx/ui_next/src/screens/NotificationTemplate/NotificationTemplateEdit/NotificationTemplateEdit.jsx +++ b/awx/ui_next/src/screens/NotificationTemplate/NotificationTemplateEdit/NotificationTemplateEdit.jsx @@ -3,7 +3,6 @@ import PropTypes from 'prop-types'; import { useHistory } from 'react-router-dom'; import { CardBody } from '../../../components/Card'; import { NotificationTemplatesAPI } from '../../../api'; - import NotificationTemplateForm from '../shared/NotificationTemplateForm'; function NotificationTemplateEdit({ template, defaultMessages }) { diff --git a/awx/ui_next/src/screens/NotificationTemplate/shared/NotificationTemplateForm.jsx b/awx/ui_next/src/screens/NotificationTemplate/shared/NotificationTemplateForm.jsx index 3588fe142f..5f3b6d3df5 100644 --- a/awx/ui_next/src/screens/NotificationTemplate/shared/NotificationTemplateForm.jsx +++ b/awx/ui_next/src/screens/NotificationTemplate/shared/NotificationTemplateForm.jsx @@ -14,7 +14,6 @@ import { FormColumnLayout } from '../../../components/FormLayout'; import TypeInputsSubForm from './TypeInputsSubForm'; import CustomMessagesSubForm from './CustomMessagesSubForm'; import typeFieldNames, { initialConfigValues } from './typeFieldNames'; -import { NotificationTemplate } from '../../../types'; function NotificationTemplateFormFields({ i18n, defaultMessages }) { const [orgField, orgMeta, orgHelpers] = useField('organization'); @@ -98,12 +97,20 @@ function NotificationTemplateForm({ i18n, }) { const handleSubmit = values => { - onSubmit(normalizeFields(values, defaultMessages)); + onSubmit( + normalizeFields( + { + ...values, + organization: values.organization?.id, + }, + defaultMessages + ) + ); }; let emailOptions = ''; if (template.notification_type === 'email') { - emailOptions = template.notification_configuration.use_ssl ? 'ssl' : 'tls'; + emailOptions = template.notification_configuration?.use_ssl ? 'ssl' : 'tls'; } const messages = template.messages || { workflow_approval: {} }; const defs = defaultMessages[template.notification_type || 'email']; @@ -125,6 +132,7 @@ function NotificationTemplateForm({ ...template.notification_configuration, }, emailOptions, + organization: template.summary_fields?.organization, messages: { started: { ...mergeDefaultMessages(messages.started, defs.started) }, success: { ...mergeDefaultMessages(messages.success, defs.success) }, @@ -180,7 +188,7 @@ function NotificationTemplateForm({ } NotificationTemplateForm.propTypes = { - template: NotificationTemplate, + template: shape(), defaultMessages: shape().isRequired, onSubmit: func.isRequired, onCancel: func.isRequired, @@ -244,6 +252,7 @@ function normalizeFields(values, defaultMessages) { function normalizeTypeFields(values) { const stripped = {}; const fields = typeFieldNames[values.notification_type]; + fields.forEach(fieldName => { if (typeof values.notification_configuration[fieldName] !== 'undefined') { stripped[fieldName] = values.notification_configuration[fieldName]; @@ -253,16 +262,21 @@ function normalizeTypeFields(values) { stripped.use_ssl = values.emailOptions === 'ssl'; stripped.use_tls = !stripped.use_ssl; } + const { emailOptions, ...rest } = values; return { - ...values, + ...rest, notification_configuration: stripped, }; } function normalizeMessageFields(values, defaults) { - if (!values.useCustomMessages) { - return values; + const { useCustomMessages, ...rest } = values; + if (!useCustomMessages) { + return { + ...rest, + messages: null, + }; } const { messages } = values; const defs = defaults[values.notification_type]; @@ -297,8 +311,9 @@ function normalizeMessageFields(values, defaults) { ), }, }; + return { - ...values, + ...rest, messages: nonDefaultMessages, }; } diff --git a/awx/ui_next/src/screens/NotificationTemplate/shared/NotificationTemplateForm.test.jsx b/awx/ui_next/src/screens/NotificationTemplate/shared/NotificationTemplateForm.test.jsx new file mode 100644 index 0000000000..c828b1450f --- /dev/null +++ b/awx/ui_next/src/screens/NotificationTemplate/shared/NotificationTemplateForm.test.jsx @@ -0,0 +1,118 @@ +import React from 'react'; +import { act } from 'react-dom/test-utils'; +import { mountWithContexts } from '../../../../testUtils/enzymeHelpers'; +import NotificationTemplateForm from './NotificationTemplateForm'; + +jest.mock('../../../api/models/NotificationTemplates'); + +const template = { + id: 3, + notification_type: 'slack', + name: 'Test Notification', + description: 'a sample notification', + url: '/notification_templates/3', + organization: 1, + summary_fields: { + user_capabilities: { + edit: true, + }, + recent_notifications: [ + { + status: 'success', + }, + ], + organization: { + id: 1, + name: 'The Organization', + }, + }, +}; + +const messageDef = { + message: 'default message', + body: 'default body', +}; +const defaults = { + started: messageDef, + success: messageDef, + error: messageDef, + workflow_approval: { + approved: messageDef, + denied: messageDef, + running: messageDef, + timed_out: messageDef, + }, +}; +const defaultMessages = { + email: defaults, + slack: defaults, + twilio: defaults, +}; + +describe('', () => { + test('should render form fields', () => { + const wrapper = mountWithContexts( + + ); + + expect(wrapper.find('input#notification-name').prop('value')).toEqual( + 'Test Notification' + ); + expect( + wrapper.find('input#notification-description').prop('value') + ).toEqual('a sample notification'); + expect(wrapper.find('OrganizationLookup').prop('value')).toEqual({ + id: 1, + name: 'The Organization', + }); + expect(wrapper.find('AnsibleSelect').prop('value')).toEqual('slack'); + expect(wrapper.find('TypeInputsSubForm').prop('type')).toEqual('slack'); + expect(wrapper.find('CustomMessagesSubForm').prop('type')).toEqual('slack'); + expect( + wrapper.find('CustomMessagesSubForm').prop('defaultMessages') + ).toEqual(defaultMessages); + }); + + test('should submit', async () => { + const handleSubmit = jest.fn(); + const wrapper = mountWithContexts( + + ); + + await act(async () => { + wrapper.find('FormActionGroup').invoke('onSubmit')(); + }); + wrapper.update(); + + expect(handleSubmit).toHaveBeenCalledWith({ + name: 'Test Notification', + description: 'a sample notification', + organization: 1, + notification_type: 'slack', + notification_configuration: { + channels: ['#foo'], + hex_color: '', + token: 'abc123', + }, + messages: null, + }); + }); +});