Skip to content

Commit

Permalink
Tests for the Delete modal
Browse files Browse the repository at this point in the history
  • Loading branch information
paul-tavares committed May 11, 2021
1 parent a9e70cb commit 144e570
Show file tree
Hide file tree
Showing 3 changed files with 185 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -212,3 +212,12 @@ export const wasDeletionSuccessful: EventFiltersSelector<boolean> = createSelect
return isLoadedResourceState(status);
}
);

export const getDeleteError: EventFiltersSelector<ServerApiError | undefined> = createSelector(
getDeletionState,
({ status }) => {
if (isFailedResourceState(status)) {
return status.error;
}
}
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import {
AppContextTestRender,
createAppRootMockRenderer,
} from '../../../../../common/mock/endpoint';
import { act } from '@testing-library/react';
import React from 'react';
import { EventFilterDeleteModal } from './event_filter_delete_modal';
import { fireEvent } from '@testing-library/dom';
import { showDeleteModal } from '../../store/selector';
import { isFailedResourceState, isLoadedResourceState } from '../../../../state';

describe('When event filters delete modal is shown', () => {
let renderAndSetup: () => Promise<ReturnType<AppContextTestRender['render']>>;
let renderResult: ReturnType<AppContextTestRender['render']>;
let coreStart: AppContextTestRender['coreStart'];
let history: AppContextTestRender['history'];
let waitForAction: AppContextTestRender['middlewareSpy']['waitForAction'];
let store: AppContextTestRender['store'];

const getBody = () =>
renderResult.baseElement.querySelector('[data-test-subj="eventFilterDeleteModalBody"]')!;

const getConfirmButton = () =>
renderResult.baseElement.querySelector(
'[data-test-subj="eventFilterDeleteModalConfirmButton"]'
)! as HTMLButtonElement;

const getCancelButton = () =>
renderResult.baseElement.querySelector(
'[data-test-subj="eventFilterDeleteModalCancelButton"]'
)! as HTMLButtonElement;

const getCurrentState = () => store.getState().management.eventFilters;

beforeEach(() => {
const mockedContext = createAppRootMockRenderer();

({ history, store, coreStart } = mockedContext);
renderAndSetup = async () => {
renderResult = mockedContext.render(<EventFilterDeleteModal />);

await act(async () => {
history.push('/event_filters');

await waitForAction('userChangedUrl');

mockedContext.store.dispatch({
type: 'eventFilterForDeletion',
payload: {
id: '123',
name: 'tic-tac-toe',
},
});
});

return renderResult;
};

waitForAction = mockedContext.middlewareSpy.waitForAction;
mockedContext.setExperimentalFlag({ eventFilteringEnabled: true });
});

it('should display name of event filter in body message', async () => {
await renderAndSetup();
expect(getBody().textContent).toMatch(/You are removing event filter "tic-tac-toe"/);
});

it('should close dialog if cancel button is clicked', async () => {
await renderAndSetup();
act(() => {
fireEvent.click(getCancelButton());
});

expect(showDeleteModal(getCurrentState())).toBe(false);
});

it('should close dialog if the close X button is clicked', async () => {
await renderAndSetup();
const dialogCloseButton = renderResult.baseElement.querySelector(
'[aria-label="Closes this modal window"]'
)!;
act(() => {
fireEvent.click(dialogCloseButton);
});

expect(showDeleteModal(getCurrentState())).toBe(false);
});

it('should disable action buttons when confirmed', async () => {
await renderAndSetup();
act(() => {
fireEvent.click(getConfirmButton());
});

expect(getCancelButton().disabled).toBe(true);
expect(getConfirmButton().disabled).toBe(true);
});

it('should set confirm button to loading', async () => {
await renderAndSetup();
act(() => {
fireEvent.click(getConfirmButton());
});

expect(getConfirmButton().querySelector('.euiLoadingSpinner')).not.toBeNull();
});

it('should show success toast', async () => {
await renderAndSetup();
const updateCompleted = waitForAction('eventFilterDeleteStatusChanged', {
validate(action) {
return isLoadedResourceState(action.payload);
},
});

await act(async () => {
fireEvent.click(getConfirmButton());
await updateCompleted;
});

expect(coreStart.notifications.toasts.addSuccess).toHaveBeenCalledWith(
'"tic-tac-toe" has been removed from the Event Filters list.'
);
});

it('should show error toast if error is countered', async () => {
coreStart.http.delete.mockRejectedValue(new Error('oh oh'));
await renderAndSetup();
const updateFailure = waitForAction('eventFilterDeleteStatusChanged', {
validate(action) {
return isFailedResourceState(action.payload);
},
});

await act(async () => {
fireEvent.click(getConfirmButton());
await updateFailure;
});

expect(coreStart.notifications.toasts.addDanger).toHaveBeenCalledWith(
'Unable to remove "tic-tac-toe" from the Event Filters list. Reason: oh oh'
);
expect(showDeleteModal(getCurrentState())).toBe(true);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,12 @@ import { useDispatch } from 'react-redux';
import { Dispatch } from 'redux';
import { i18n } from '@kbn/i18n';
import { useEventFiltersSelector } from '../hooks';
import { getItemToDelete, isDeletionInProgress, wasDeletionSuccessful } from '../../store/selector';
import {
getDeleteError,
getItemToDelete,
isDeletionInProgress,
wasDeletionSuccessful,
} from '../../store/selector';
import { AppAction } from '../../../../../common/store/actions';
import { useToasts } from '../../../../../common/lib/kibana';

Expand All @@ -32,6 +37,7 @@ export const EventFilterDeleteModal = memo<{}>(() => {
const isDeleting = useEventFiltersSelector(isDeletionInProgress);
const eventFilter = useEventFiltersSelector(getItemToDelete);
const wasDeleted = useEventFiltersSelector(wasDeletionSuccessful);
const deleteError = useEventFiltersSelector(getDeleteError);

const onCancel = useCallback(() => {
dispatch({ type: 'eventFilterDeletionReset' });
Expand All @@ -41,6 +47,7 @@ export const EventFilterDeleteModal = memo<{}>(() => {
dispatch({ type: 'eventFilterDeleteSubmit' });
}, [dispatch]);

// Show toast for success
useEffect(() => {
if (wasDeleted) {
toasts.addSuccess(
Expand All @@ -54,6 +61,19 @@ export const EventFilterDeleteModal = memo<{}>(() => {
}
}, [dispatch, eventFilter?.name, toasts, wasDeleted]);

// show toast for failures
useEffect(() => {
if (deleteError) {
toasts.addDanger(
i18n.translate('xpack.securitySolution.eventFilters.deletionDialog.deleteFailure', {
defaultMessage:
'Unable to remove "{name}" from the Event Filters list. Reason: {message}',
values: { name: eventFilter?.name, message: deleteError.message },
})
);
}
}, [deleteError, eventFilter?.name, toasts]);

return (
<EuiModal onClose={onCancel}>
<EuiModalHeader data-test-subj="eventFilterDeleteModalHeader">
Expand All @@ -65,7 +85,7 @@ export const EventFilterDeleteModal = memo<{}>(() => {
</EuiModalHeaderTitle>
</EuiModalHeader>

<EuiModalBody>
<EuiModalBody data-test-subj="eventFilterDeleteModalBody">
<EuiText>
<p>
<FormattedMessage
Expand All @@ -87,7 +107,7 @@ export const EventFilterDeleteModal = memo<{}>(() => {
<EuiButtonEmpty
onClick={onCancel}
isDisabled={isDeleting}
data-test-subj="modalCancelButton"
data-test-subj="eventFilterDeleteModalCancelButton"
>
<FormattedMessage
id="xpack.securitySolution.eventFilters.deletionDialog.cancelButton"
Expand All @@ -100,7 +120,7 @@ export const EventFilterDeleteModal = memo<{}>(() => {
color="danger"
onClick={onConfirm}
isLoading={isDeleting}
data-test-subj="modalConfirmButton"
data-test-subj="eventFilterDeleteModalConfirmButton"
>
<FormattedMessage
id="xpack.securitySolution.eventFilters.deletionDialog.confirmButton"
Expand Down

0 comments on commit 144e570

Please sign in to comment.