Skip to content

Commit

Permalink
Adds a Reason indicator to the onClose handler in AddAlert and EditAl…
Browse files Browse the repository at this point in the history
…ert (#92149)

Adds a `AlertFlyoutCloseReason` on the `onClose` handlers of `AlertAdd` and `AlertEdit` indicating the reason for the handler being called.
  • Loading branch information
gmmorris committed Feb 22, 2021
1 parent 6eb2636 commit c867bc6
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,19 @@
* 2.0.
*/

import * as React from 'react';
import uuid from 'uuid';
import React, { FunctionComponent } from 'react';
import { mountWithIntl, nextTick } from '@kbn/test/jest';
import { act } from 'react-dom/test-utils';
import { FormattedMessage } from '@kbn/i18n/react';
import { EuiFormLabel } from '@elastic/eui';
import { coreMock } from '../../../../../../../src/core/public/mocks';
import AlertAdd from './alert_add';
import AlertAdd, { AlertAddProps } from './alert_add';
import { createAlert } from '../../lib/alert_api';
import { actionTypeRegistryMock } from '../../action_type_registry.mock';
import {
Alert,
AlertFlyoutCloseReason,
ConnectorValidationResult,
GenericValidationResult,
ValidationResult,
Expand All @@ -23,10 +26,12 @@ import { alertTypeRegistryMock } from '../../alert_type_registry.mock';
import { ReactWrapper } from 'enzyme';
import { ALERTS_FEATURE_ID } from '../../../../../alerts/common';
import { useKibana } from '../../../common/lib/kibana';

jest.mock('../../../common/lib/kibana');

jest.mock('../../lib/alert_api', () => ({
loadAlertTypes: jest.fn(),
createAlert: jest.fn(),
alertingFrameworkHealth: jest.fn(() => ({
isSufficientlySecure: true,
hasPermanentEncryptionKey: true,
Expand All @@ -41,7 +46,12 @@ const actionTypeRegistry = actionTypeRegistryMock.create();
const alertTypeRegistry = alertTypeRegistryMock.create();
const useKibanaMock = useKibana as jest.Mocked<typeof useKibana>;

export const TestExpression: React.FunctionComponent<any> = () => {
const delay = (wait: number = 1000) =>
new Promise((resolve) => {
setTimeout(resolve, wait);
});

export const TestExpression: FunctionComponent<any> = () => {
return (
<EuiFormLabel>
<FormattedMessage
Expand All @@ -56,7 +66,10 @@ export const TestExpression: React.FunctionComponent<any> = () => {
describe('alert_add', () => {
let wrapper: ReactWrapper<any>;

async function setup(initialValues?: Partial<Alert>) {
async function setup(
initialValues?: Partial<Alert>,
onClose: AlertAddProps['onClose'] = jest.fn()
) {
const mocks = coreMock.createSetup();
const { loadAlertTypes } = jest.requireMock('../../lib/alert_api');
const alertTypes = [
Expand Down Expand Up @@ -141,7 +154,7 @@ describe('alert_add', () => {
wrapper = mountWithIntl(
<AlertAdd
consumer={ALERTS_FEATURE_ID}
onClose={() => {}}
onClose={onClose}
initialValues={initialValues}
reloadAlerts={() => {
return new Promise<void>(() => {});
Expand All @@ -160,11 +173,9 @@ describe('alert_add', () => {
}

it('renders alert add flyout', async () => {
await setup();

await new Promise((resolve) => {
setTimeout(resolve, 1000);
});
const onClose = jest.fn();
await setup({}, onClose);
await delay(1000);

expect(wrapper.find('[data-test-subj="addAlertFlyoutTitle"]').exists()).toBeTruthy();
expect(wrapper.find('[data-test-subj="saveAlertButton"]').exists()).toBeTruthy();
Expand All @@ -176,25 +187,87 @@ describe('alert_add', () => {
expect(wrapper.find('[data-test-subj="tagsComboBox"]').first().text()).toBe('');

expect(wrapper.find('.euiSelect').first().props().value).toBe('m');

wrapper.find('[data-test-subj="cancelSaveAlertButton"]').first().simulate('click');
expect(onClose).toHaveBeenCalledWith(AlertFlyoutCloseReason.CANCELED);
});

it('renders alert add flyout with initial values', async () => {
await setup({
name: 'Simple status alert',
tags: ['uptime', 'logs'],
schedule: {
interval: '1h',
const onClose = jest.fn();
await setup(
{
name: 'Simple status alert',
tags: ['uptime', 'logs'],
schedule: {
interval: '1h',
},
},
});
onClose
);

await new Promise((resolve) => {
setTimeout(resolve, 1000);
});
await delay(1000);

expect(wrapper.find('input#alertName').props().value).toBe('Simple status alert');

expect(wrapper.find('[data-test-subj="tagsComboBox"]').first().text()).toBe('uptimelogs');

expect(wrapper.find('.euiSelect').first().props().value).toBe('h');
});

it('emit an onClose event when the alert is saved', async () => {
const onClose = jest.fn();
const alert = mockAlert();

(createAlert as jest.MockedFunction<typeof createAlert>).mockResolvedValue(alert);

await setup(
{
name: 'Simple status alert',
alertTypeId: 'my-alert-type',
tags: ['uptime', 'logs'],
schedule: {
interval: '1h',
},
},
onClose
);

wrapper.find('[data-test-subj="saveAlertButton"]').first().simulate('click');

// Wait for handlers to fire
await act(async () => {
await nextTick();
wrapper.update();
});

expect(onClose).toHaveBeenCalledWith(AlertFlyoutCloseReason.SAVED);
});
});

function mockAlert(overloads: Partial<Alert> = {}): Alert {
return {
id: uuid.v4(),
enabled: true,
name: `alert-${uuid.v4()}`,
tags: [],
alertTypeId: '.noop',
consumer: 'consumer',
schedule: { interval: '1m' },
actions: [],
params: {},
createdBy: null,
updatedBy: null,
createdAt: new Date(),
updatedAt: new Date(),
apiKeyOwner: null,
throttle: null,
notifyWhen: null,
muteAll: false,
mutedInstanceIds: [],
executionStatus: {
status: 'unknown',
lastExecutionDate: new Date('2020-08-20T19:23:38Z'),
},
...overloads,
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
AlertTypeRegistryContract,
AlertTypeParams,
AlertUpdates,
AlertFlyoutCloseReason,
} from '../../../types';
import { AlertForm, getAlertErrors, isValidAlert } from './alert_form';
import { alertReducer, InitialAlert, InitialAlertReducer } from './alert_reducer';
Expand All @@ -34,7 +35,7 @@ export interface AlertAddProps<MetaData = Record<string, any>> {
consumer: string;
alertTypeRegistry: AlertTypeRegistryContract;
actionTypeRegistry: ActionTypeRegistryContract;
onClose: () => void;
onClose: (reason: AlertFlyoutCloseReason) => void;
alertTypeId?: string;
canChangeTrigger?: boolean;
initialValues?: Partial<Alert>;
Expand Down Expand Up @@ -119,15 +120,15 @@ const AlertAdd = ({
) {
setIsConfirmAlertCloseModalOpen(true);
} else {
onClose();
onClose(AlertFlyoutCloseReason.CANCELED);
}
};

const saveAlertAndCloseFlyout = async () => {
const savedAlert = await onSaveAlert();
setIsSaving(false);
if (savedAlert) {
onClose();
onClose(AlertFlyoutCloseReason.SAVED);
if (reloadAlerts) {
reloadAlerts();
}
Expand Down Expand Up @@ -245,7 +246,7 @@ const AlertAdd = ({
<ConfirmAlertClose
onConfirm={() => {
setIsConfirmAlertCloseModalOpen(false);
onClose();
onClose(AlertFlyoutCloseReason.CANCELED);
}}
onCancel={() => {
setIsConfirmAlertCloseModalOpen(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,12 @@ import {
} from '@elastic/eui';
import { cloneDeep } from 'lodash';
import { i18n } from '@kbn/i18n';
import { ActionTypeRegistryContract, Alert, AlertTypeRegistryContract } from '../../../types';
import {
ActionTypeRegistryContract,
Alert,
AlertFlyoutCloseReason,
AlertTypeRegistryContract,
} from '../../../types';
import { AlertForm, getAlertErrors, isValidAlert } from './alert_form';
import { alertReducer, ConcreteAlertReducer } from './alert_reducer';
import { updateAlert } from '../../lib/alert_api';
Expand All @@ -38,7 +43,7 @@ export interface AlertEditProps<MetaData = Record<string, any>> {
initialAlert: Alert;
alertTypeRegistry: AlertTypeRegistryContract;
actionTypeRegistry: ActionTypeRegistryContract;
onClose(): void;
onClose: (reason: AlertFlyoutCloseReason) => void;
reloadAlerts?: () => Promise<void>;
metadata?: MetaData;
}
Expand Down Expand Up @@ -81,7 +86,7 @@ export const AlertEdit = ({
if (hasAlertChanged(alert, initialAlert, true)) {
setIsConfirmAlertCloseModalOpen(true);
} else {
onClose();
onClose(AlertFlyoutCloseReason.CANCELED);
}
};

Expand Down Expand Up @@ -197,7 +202,7 @@ export const AlertEdit = ({
const savedAlert = await onSaveAlert();
setIsSaving(false);
if (savedAlert) {
onClose();
onClose(AlertFlyoutCloseReason.SAVED);
if (reloadAlerts) {
reloadAlerts();
}
Expand All @@ -218,7 +223,7 @@ export const AlertEdit = ({
<ConfirmAlertClose
onConfirm={() => {
setIsConfirmAlertCloseModalOpen(false);
onClose();
onClose(AlertFlyoutCloseReason.CANCELED);
}}
onCancel={() => {
setIsConfirmAlertCloseModalOpen(false);
Expand Down
1 change: 1 addition & 0 deletions x-pack/plugins/triggers_actions_ui/public/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export {
ActionVariables,
ActionConnector,
IErrorObject,
AlertFlyoutCloseReason,
} from './types';
export {
ConnectorAddFlyout,
Expand Down
5 changes: 5 additions & 0 deletions x-pack/plugins/triggers_actions_ui/public/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@ export interface ActionConnectorFieldsProps<TActionConnector> {
consumer?: string;
}

export enum AlertFlyoutCloseReason {
SAVED,
CANCELED,
}

export interface ActionParamsProps<TParams> {
actionParams: Partial<TParams>;
index: number;
Expand Down

0 comments on commit c867bc6

Please sign in to comment.