add some notification form tests; notification add screen

This commit is contained in:
Keith Grant 2020-08-21 16:35:37 -07:00
parent 9bb834a422
commit 43ac5a0574
6 changed files with 227 additions and 19 deletions

View File

@ -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 (
<FormGroup
@ -38,7 +39,7 @@ function ArrayTextField(props) {
resizeOrientation="vertical"
{...rest}
{...field}
value={field.value.join('\n')}
value={value.join('\n')}
onChange={value => {
helpers.setValue(value.split('\n').map(v => v.trim()));
}}

View File

@ -94,12 +94,6 @@ function NotificationTemplate({ setBreadcrumb, i18n }) {
to="/notification_templates/:id/details"
exact
/>
{/* <Route path="/notification_templates/add">
<NotificationTemplateAdd
defaultMessages={defaultMessages}
isLoading={isLoading}
/>
</Route> */}
{template && (
<>
<Route path="/notification_templates/:id/edit">

View File

@ -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 <div />;
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 (
<PageSection>
<Card>
<ContentError error={error}>
{error.response.status === 404 && (
<span>
{i18n._(t`Notification Template not found.`)}{' '}
<Link to="/notification_templates">
{i18n._(t`View all Notification Templates.`)}
</Link>
</span>
)}
</ContentError>
</Card>
</PageSection>
);
}
return (
<PageSection>
<Card>
<CardBody>
{defaultMessages && (
<NotificationTemplateForm
defaultMessages={defaultMessages}
onSubmit={handleSubmit}
onCancel={handleCancel}
submitError={formError}
/>
)}
</CardBody>
</Card>
</PageSection>
);
}
NotificationTemplateAdd.contextTypes = {
custom_virtualenvs: PropTypes.arrayOf(PropTypes.string),
};
export { NotificationTemplateAdd as _NotificationTemplateAdd };
export default withI18n()(NotificationTemplateAdd);

View File

@ -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 }) {

View File

@ -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,
};
}

View File

@ -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('<NotificationTemplateForm />', () => {
test('should render form fields', () => {
const wrapper = mountWithContexts(
<NotificationTemplateForm
template={template}
defaultMessages={defaultMessages}
detailUrl="/notification_templates/3/detail"
onSubmit={jest.fn()}
onCancel={jest.fn()}
/>
);
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(
<NotificationTemplateForm
template={{
...template,
notification_configuration: {
channels: ['#foo'],
token: 'abc123',
},
}}
defaultMessages={defaultMessages}
detailUrl="/notification_templates/3/detail"
onSubmit={handleSubmit}
onCancel={jest.fn()}
/>
);
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,
});
});
});