mirror of
https://github.com/ansible/awx.git
synced 2026-04-06 18:49:21 -02:30
add some notification form tests; notification add screen
This commit is contained in:
@@ -20,6 +20,7 @@ function ArrayTextField(props) {
|
|||||||
|
|
||||||
const [field, meta, helpers] = useField({ name, validate });
|
const [field, meta, helpers] = useField({ name, validate });
|
||||||
const isValid = !(meta.touched && meta.error);
|
const isValid = !(meta.touched && meta.error);
|
||||||
|
const value = field.value || [];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FormGroup
|
<FormGroup
|
||||||
@@ -38,7 +39,7 @@ function ArrayTextField(props) {
|
|||||||
resizeOrientation="vertical"
|
resizeOrientation="vertical"
|
||||||
{...rest}
|
{...rest}
|
||||||
{...field}
|
{...field}
|
||||||
value={field.value.join('\n')}
|
value={value.join('\n')}
|
||||||
onChange={value => {
|
onChange={value => {
|
||||||
helpers.setValue(value.split('\n').map(v => v.trim()));
|
helpers.setValue(value.split('\n').map(v => v.trim()));
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -94,12 +94,6 @@ function NotificationTemplate({ setBreadcrumb, i18n }) {
|
|||||||
to="/notification_templates/:id/details"
|
to="/notification_templates/:id/details"
|
||||||
exact
|
exact
|
||||||
/>
|
/>
|
||||||
{/* <Route path="/notification_templates/add">
|
|
||||||
<NotificationTemplateAdd
|
|
||||||
defaultMessages={defaultMessages}
|
|
||||||
isLoading={isLoading}
|
|
||||||
/>
|
|
||||||
</Route> */}
|
|
||||||
{template && (
|
{template && (
|
||||||
<>
|
<>
|
||||||
<Route path="/notification_templates/:id/edit">
|
<Route path="/notification_templates/:id/edit">
|
||||||
|
|||||||
@@ -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() {
|
function NotificationTemplateAdd({ i18n }) {
|
||||||
return <div />;
|
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);
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import PropTypes from 'prop-types';
|
|||||||
import { useHistory } from 'react-router-dom';
|
import { useHistory } from 'react-router-dom';
|
||||||
import { CardBody } from '../../../components/Card';
|
import { CardBody } from '../../../components/Card';
|
||||||
import { NotificationTemplatesAPI } from '../../../api';
|
import { NotificationTemplatesAPI } from '../../../api';
|
||||||
|
|
||||||
import NotificationTemplateForm from '../shared/NotificationTemplateForm';
|
import NotificationTemplateForm from '../shared/NotificationTemplateForm';
|
||||||
|
|
||||||
function NotificationTemplateEdit({ template, defaultMessages }) {
|
function NotificationTemplateEdit({ template, defaultMessages }) {
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ import { FormColumnLayout } from '../../../components/FormLayout';
|
|||||||
import TypeInputsSubForm from './TypeInputsSubForm';
|
import TypeInputsSubForm from './TypeInputsSubForm';
|
||||||
import CustomMessagesSubForm from './CustomMessagesSubForm';
|
import CustomMessagesSubForm from './CustomMessagesSubForm';
|
||||||
import typeFieldNames, { initialConfigValues } from './typeFieldNames';
|
import typeFieldNames, { initialConfigValues } from './typeFieldNames';
|
||||||
import { NotificationTemplate } from '../../../types';
|
|
||||||
|
|
||||||
function NotificationTemplateFormFields({ i18n, defaultMessages }) {
|
function NotificationTemplateFormFields({ i18n, defaultMessages }) {
|
||||||
const [orgField, orgMeta, orgHelpers] = useField('organization');
|
const [orgField, orgMeta, orgHelpers] = useField('organization');
|
||||||
@@ -98,12 +97,20 @@ function NotificationTemplateForm({
|
|||||||
i18n,
|
i18n,
|
||||||
}) {
|
}) {
|
||||||
const handleSubmit = values => {
|
const handleSubmit = values => {
|
||||||
onSubmit(normalizeFields(values, defaultMessages));
|
onSubmit(
|
||||||
|
normalizeFields(
|
||||||
|
{
|
||||||
|
...values,
|
||||||
|
organization: values.organization?.id,
|
||||||
|
},
|
||||||
|
defaultMessages
|
||||||
|
)
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
let emailOptions = '';
|
let emailOptions = '';
|
||||||
if (template.notification_type === 'email') {
|
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 messages = template.messages || { workflow_approval: {} };
|
||||||
const defs = defaultMessages[template.notification_type || 'email'];
|
const defs = defaultMessages[template.notification_type || 'email'];
|
||||||
@@ -125,6 +132,7 @@ function NotificationTemplateForm({
|
|||||||
...template.notification_configuration,
|
...template.notification_configuration,
|
||||||
},
|
},
|
||||||
emailOptions,
|
emailOptions,
|
||||||
|
organization: template.summary_fields?.organization,
|
||||||
messages: {
|
messages: {
|
||||||
started: { ...mergeDefaultMessages(messages.started, defs.started) },
|
started: { ...mergeDefaultMessages(messages.started, defs.started) },
|
||||||
success: { ...mergeDefaultMessages(messages.success, defs.success) },
|
success: { ...mergeDefaultMessages(messages.success, defs.success) },
|
||||||
@@ -180,7 +188,7 @@ function NotificationTemplateForm({
|
|||||||
}
|
}
|
||||||
|
|
||||||
NotificationTemplateForm.propTypes = {
|
NotificationTemplateForm.propTypes = {
|
||||||
template: NotificationTemplate,
|
template: shape(),
|
||||||
defaultMessages: shape().isRequired,
|
defaultMessages: shape().isRequired,
|
||||||
onSubmit: func.isRequired,
|
onSubmit: func.isRequired,
|
||||||
onCancel: func.isRequired,
|
onCancel: func.isRequired,
|
||||||
@@ -244,6 +252,7 @@ function normalizeFields(values, defaultMessages) {
|
|||||||
function normalizeTypeFields(values) {
|
function normalizeTypeFields(values) {
|
||||||
const stripped = {};
|
const stripped = {};
|
||||||
const fields = typeFieldNames[values.notification_type];
|
const fields = typeFieldNames[values.notification_type];
|
||||||
|
|
||||||
fields.forEach(fieldName => {
|
fields.forEach(fieldName => {
|
||||||
if (typeof values.notification_configuration[fieldName] !== 'undefined') {
|
if (typeof values.notification_configuration[fieldName] !== 'undefined') {
|
||||||
stripped[fieldName] = values.notification_configuration[fieldName];
|
stripped[fieldName] = values.notification_configuration[fieldName];
|
||||||
@@ -253,16 +262,21 @@ function normalizeTypeFields(values) {
|
|||||||
stripped.use_ssl = values.emailOptions === 'ssl';
|
stripped.use_ssl = values.emailOptions === 'ssl';
|
||||||
stripped.use_tls = !stripped.use_ssl;
|
stripped.use_tls = !stripped.use_ssl;
|
||||||
}
|
}
|
||||||
|
const { emailOptions, ...rest } = values;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...values,
|
...rest,
|
||||||
notification_configuration: stripped,
|
notification_configuration: stripped,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizeMessageFields(values, defaults) {
|
function normalizeMessageFields(values, defaults) {
|
||||||
if (!values.useCustomMessages) {
|
const { useCustomMessages, ...rest } = values;
|
||||||
return values;
|
if (!useCustomMessages) {
|
||||||
|
return {
|
||||||
|
...rest,
|
||||||
|
messages: null,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
const { messages } = values;
|
const { messages } = values;
|
||||||
const defs = defaults[values.notification_type];
|
const defs = defaults[values.notification_type];
|
||||||
@@ -297,8 +311,9 @@ function normalizeMessageFields(values, defaults) {
|
|||||||
),
|
),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...values,
|
...rest,
|
||||||
messages: nonDefaultMessages,
|
messages: nonDefaultMessages,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user