Skip to content

Commit

Permalink
[Cases] Add Severity bulk action & row action (#142826)
Browse files Browse the repository at this point in the history
* Add severity action

* Change translation

* Add unit tests

* Add e2e tests

* Fix i18n

* PR feedback

Co-authored-by: Kibana Machine <[email protected]>
  • Loading branch information
cnasikas and kibanamachine authored Oct 10, 2022
1 parent dfede09 commit b7de426
Show file tree
Hide file tree
Showing 17 changed files with 795 additions and 41 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,10 @@
import { i18n } from '@kbn/i18n';
export { DELETED_CASES } from '../../../common/translations';

export const BULK_ACTION_DELETE_LABEL = i18n.translate(
'xpack.cases.caseTable.bulkActions.deleteCases',
{
defaultMessage: 'Delete cases',
}
);
export const BULK_ACTION_DELETE_LABEL = i18n.translate('xpack.cases.actions.deleteMultipleCases', {
defaultMessage: 'Delete cases',
});

export const DELETE_ACTION_LABEL = i18n.translate('xpack.cases.caseTable.action.deleteCase', {
export const DELETE_ACTION_LABEL = i18n.translate('xpack.cases.actions.deleteSingleCase', {
defaultMessage: 'Delete case',
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* 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 { i18n } from '@kbn/i18n';
import { CaseSeverity } from '../../../../common/api';
import { severities } from '../../severity/config';

const SET_SEVERITY = ({
totalCases,
severity,
caseTitle,
}: {
totalCases: number;
severity: string;
caseTitle?: string;
}) =>
i18n.translate('xpack.cases.actions.severity', {
values: { caseTitle, totalCases, severity },
defaultMessage:
'{totalCases, plural, =1 {Case "{caseTitle}" was} other {{totalCases} cases were}} set to {severity}',
});

export const SET_SEVERITY_LOW = ({
totalCases,
caseTitle,
}: {
totalCases: number;
caseTitle?: string;
}) =>
SET_SEVERITY({
totalCases,
caseTitle,
severity: severities[CaseSeverity.LOW].label,
});

export const SET_SEVERITY_MEDIUM = ({
totalCases,
caseTitle,
}: {
totalCases: number;
caseTitle?: string;
}) =>
SET_SEVERITY({
totalCases,
caseTitle,
severity: severities[CaseSeverity.MEDIUM].label,
});

export const SET_SEVERITY_HIGH = ({
totalCases,
caseTitle,
}: {
totalCases: number;
caseTitle?: string;
}) =>
SET_SEVERITY({
totalCases,
caseTitle,
severity: severities[CaseSeverity.HIGH].label,
});

export const SET_SEVERITY_CRITICAL = ({
totalCases,
caseTitle,
}: {
totalCases: number;
caseTitle?: string;
}) =>
SET_SEVERITY({
totalCases,
caseTitle,
severity: severities[CaseSeverity.CRITICAL].label,
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
/*
* 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 { AppMockRenderer, createAppMockRenderer } from '../../../common/mock';
import { act, renderHook } from '@testing-library/react-hooks';
import { useSeverityAction } from './use_severity_action';

import * as api from '../../../containers/api';
import { basicCase } from '../../../containers/mock';
import { CaseSeverity } from '../../../../common/api';

jest.mock('../../../containers/api');

describe('useSeverityAction', () => {
let appMockRender: AppMockRenderer;
const onAction = jest.fn();
const onActionSuccess = jest.fn();

beforeEach(() => {
appMockRender = createAppMockRenderer();
jest.clearAllMocks();
});

it('renders an action', async () => {
const { result } = renderHook(
() =>
useSeverityAction({
onAction,
onActionSuccess,
isDisabled: false,
}),
{
wrapper: appMockRender.AppWrapper,
}
);

expect(result.current.getActions([basicCase])).toMatchInlineSnapshot(`
Array [
Object {
"data-test-subj": "cases-bulk-action-severity-low",
"disabled": true,
"icon": "empty",
"key": "cases-bulk-action-severity-low",
"name": "Low",
"onClick": [Function],
},
Object {
"data-test-subj": "cases-bulk-action-severity-medium",
"disabled": false,
"icon": "empty",
"key": "cases-bulk-action-severity-medium",
"name": "Medium",
"onClick": [Function],
},
Object {
"data-test-subj": "cases-bulk-action-severity-high",
"disabled": false,
"icon": "empty",
"key": "cases-bulk-action-severity-high",
"name": "High",
"onClick": [Function],
},
Object {
"data-test-subj": "cases-bulk-action-severity-critical",
"disabled": false,
"icon": "empty",
"key": "cases-bulk-action-severity-critical",
"name": "Critical",
"onClick": [Function],
},
]
`);
});

it('update the severity cases', async () => {
const updateSpy = jest.spyOn(api, 'updateCases');

const { result, waitFor } = renderHook(
() => useSeverityAction({ onAction, onActionSuccess, isDisabled: false }),
{
wrapper: appMockRender.AppWrapper,
}
);

const actions = result.current.getActions([basicCase]);

for (const [index, severity] of [
CaseSeverity.LOW,
CaseSeverity.MEDIUM,
CaseSeverity.HIGH,
CaseSeverity.CRITICAL,
].entries()) {
act(() => {
// @ts-expect-error: onClick expects a MouseEvent argument
actions[index]!.onClick();
});

await waitFor(() => {
expect(onAction).toHaveBeenCalled();
expect(onActionSuccess).toHaveBeenCalled();
expect(updateSpy).toHaveBeenCalledWith(
[{ severity, id: basicCase.id, version: basicCase.version }],
expect.anything()
);
});
}
});

const singleCaseTests = [
[CaseSeverity.LOW, 0, 'Case "Another horrible breach!!" was set to Low'],
[CaseSeverity.MEDIUM, 1, 'Case "Another horrible breach!!" was set to Medium'],
[CaseSeverity.HIGH, 2, 'Case "Another horrible breach!!" was set to High'],
[CaseSeverity.CRITICAL, 3, 'Case "Another horrible breach!!" was set to Critical'],
];

it.each(singleCaseTests)(
'shows the success toaster correctly when updating the severity of the case: %s',
async (_, index, expectedMessage) => {
const { result, waitFor } = renderHook(
() => useSeverityAction({ onAction, onActionSuccess, isDisabled: false }),
{
wrapper: appMockRender.AppWrapper,
}
);

const actions = result.current.getActions([basicCase]);

act(() => {
// @ts-expect-error: onClick expects a MouseEvent argument
actions[index]!.onClick();
});

await waitFor(() => {
expect(appMockRender.coreStart.notifications.toasts.addSuccess).toHaveBeenCalledWith(
expectedMessage
);
});
}
);

const multipleCasesTests: Array<[CaseSeverity, number, string]> = [
[CaseSeverity.LOW, 0, '2 cases were set to Low'],
[CaseSeverity.MEDIUM, 1, '2 cases were set to Medium'],
[CaseSeverity.HIGH, 2, '2 cases were set to High'],
[CaseSeverity.CRITICAL, 3, '2 cases were set to Critical'],
];

it.each(multipleCasesTests)(
'shows the success toaster correctly when updating the severity of the case: %s',
async (_, index, expectedMessage) => {
const { result, waitFor } = renderHook(
() => useSeverityAction({ onAction, onActionSuccess, isDisabled: false }),
{
wrapper: appMockRender.AppWrapper,
}
);

const actions = result.current.getActions([basicCase, basicCase]);

act(() => {
// @ts-expect-error: onClick expects a MouseEvent argument
actions[index]!.onClick();
});

await waitFor(() => {
expect(appMockRender.coreStart.notifications.toasts.addSuccess).toHaveBeenCalledWith(
expectedMessage
);
});
}
);

const disabledTests: Array<[CaseSeverity, number]> = [
[CaseSeverity.LOW, 0],
[CaseSeverity.MEDIUM, 1],
[CaseSeverity.HIGH, 2],
[CaseSeverity.CRITICAL, 3],
];

it.each(disabledTests)('disables the severity button correctly: %s', async (severity, index) => {
const { result } = renderHook(
() => useSeverityAction({ onAction, onActionSuccess, isDisabled: false }),
{
wrapper: appMockRender.AppWrapper,
}
);

const actions = result.current.getActions([{ ...basicCase, severity }]);
expect(actions[index].disabled).toBe(true);
});

it.each(disabledTests)(
'disables the severity button correctly if isDisabled=true: %s',
async (severity, index) => {
const { result } = renderHook(
() => useSeverityAction({ onAction, onActionSuccess, isDisabled: true }),
{
wrapper: appMockRender.AppWrapper,
}
);

const actions = result.current.getActions([basicCase]);
expect(actions[index].disabled).toBe(true);
}
);
});
Loading

0 comments on commit b7de426

Please sign in to comment.