Skip to content

Commit

Permalink
[Cases] Add severity field to create case (#131626)
Browse files Browse the repository at this point in the history
  • Loading branch information
academo authored May 6, 2022
1 parent e55f8c8 commit c4b554a
Show file tree
Hide file tree
Showing 7 changed files with 184 additions and 3 deletions.
5 changes: 5 additions & 0 deletions x-pack/plugins/cases/public/components/create/form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import { CreateCaseOwnerSelector } from './owner_selector';
import { useCasesContext } from '../cases_context/use_cases_context';
import { useAvailableCasesOwners } from '../app/use_available_owners';
import { CaseAttachments } from '../../types';
import { Severity } from './severity';

interface ContainerProps {
big?: boolean;
Expand Down Expand Up @@ -88,6 +89,9 @@ export const CreateCaseFormFields: React.FC<CreateCaseFormFieldsProps> = React.m
<Container>
<Tags isLoading={isSubmitting} />
</Container>
<Container>
<Severity isLoading={isSubmitting} />
</Container>
{canShowCaseSolutionSelection && (
<Container big>
<CreateCaseOwnerSelector
Expand All @@ -99,6 +103,7 @@ export const CreateCaseFormFields: React.FC<CreateCaseFormFieldsProps> = React.m
<Container big>
<Description isLoading={isSubmitting} />
</Container>
<Container />
</>
),
}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { mount, ReactWrapper } from 'enzyme';
import { act, RenderResult, waitFor, within } from '@testing-library/react';
import { EuiComboBox, EuiComboBoxOptionOption } from '@elastic/eui';

import { CommentType, ConnectorTypes } from '../../../common/api';
import { CaseSeverity, CommentType, ConnectorTypes } from '../../../common/api';
import { useKibana } from '../../common/lib/kibana';
import { AppMockRenderer, createAppMockRenderer, TestProviders } from '../../common/mock';
import { usePostCase } from '../../containers/use_post_case';
Expand Down Expand Up @@ -182,6 +182,7 @@ describe('Create case', () => {
</FormContext>
);
expect(renderResult.getByTestId('caseTitle')).toBeTruthy();
expect(renderResult.getByTestId('caseSeverity')).toBeTruthy();
expect(renderResult.getByTestId('caseDescription')).toBeTruthy();
expect(renderResult.getByTestId('caseTags')).toBeTruthy();
expect(renderResult.getByTestId('caseConnectors')).toBeTruthy();
Expand All @@ -208,6 +209,34 @@ describe('Create case', () => {
});
});

it('should post a case on submit click with the selected severity', async () => {
useConnectorsMock.mockReturnValue({
...sampleConnectorData,
connectors: connectorsMock,
});

const renderResult = mockedContext.render(
<FormContext onSuccess={onFormSubmitSuccess}>
<CreateCaseFormFields {...defaultCreateCaseForm} />
<SubmitCaseButton />
</FormContext>
);

await fillFormReactTestingLib(renderResult);

userEvent.click(renderResult.getByTestId('case-severity-selection'));
expect(renderResult.getByTestId('case-severity-selection-high')).toBeTruthy();
userEvent.click(renderResult.getByTestId('case-severity-selection-high'));

userEvent.click(renderResult.getByTestId('create-case-submit'));
await waitFor(() => {
expect(postCase).toBeCalledWith({
...sampleData,
severity: CaseSeverity.HIGH,
});
});
});

it('does not submits the title when the length is longer than 64 characters', async () => {
const longTitle =
'This is a title that should not be saved as it is longer than 64 characters.';
Expand Down Expand Up @@ -285,6 +314,18 @@ describe('Create case', () => {
);
});

it('should select LOW as the default severity', async () => {
const renderResult = mockedContext.render(
<FormContext onSuccess={onFormSubmitSuccess}>
<CreateCaseFormFields {...defaultCreateCaseForm} />
<SubmitCaseButton />
</FormContext>
);
expect(renderResult.getByTestId('caseSeverity')).toBeTruthy();
// there should be 2 low elements. one for the options popover and one for the displayed one.
expect(renderResult.getAllByTestId('case-severity-selection-low').length).toBe(2);
});

it('should select the default connector set in the configuration', async () => {
useCaseConfigureMock.mockImplementation(() => ({
...useCaseConfigureResponse,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { usePostPushToService } from '../../containers/use_post_push_to_service'

import { useConnectors } from '../../containers/configure/use_connectors';
import { Case } from '../../containers/types';
import { NONE_CONNECTOR_ID } from '../../../common/api';
import { CaseSeverity, NONE_CONNECTOR_ID } from '../../../common/api';
import {
UseCreateAttachments,
useCreateAttachments,
Expand All @@ -28,6 +28,7 @@ const initialCaseValue: FormProps = {
description: '',
tags: [],
title: '',
severity: CaseSeverity.LOW,
connectorId: NONE_CONNECTOR_ID,
fields: null,
syncAlerts: true,
Expand Down
3 changes: 2 additions & 1 deletion x-pack/plugins/cases/public/components/create/mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@
* 2.0.
*/

import { CasePostRequest, ConnectorTypes } from '../../../common/api';
import { CasePostRequest, CaseSeverity, ConnectorTypes } from '../../../common/api';
import { SECURITY_SOLUTION_OWNER } from '../../../common/constants';
import { choices } from '../connectors/mock';

export const sampleTags = ['coke', 'pepsi'];
export const sampleData: CasePostRequest = {
description: 'what a great description',
tags: sampleTags,
severity: CaseSeverity.LOW,
title: 'what a cool title',
connector: {
fields: null,
Expand Down
4 changes: 4 additions & 0 deletions x-pack/plugins/cases/public/components/create/schema.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
import * as i18n from './translations';

import { OptionalFieldLabel } from './optional_field_label';
import { SEVERITY_TITLE } from '../severity/translations';
const { emptyField, maxLengthField } = fieldValidators;

export const schemaTags = {
Expand Down Expand Up @@ -83,6 +84,9 @@ export const schema: FormSchema<FormProps> = {
],
},
tags: schemaTags,
severity: {
label: SEVERITY_TITLE,
},
connectorId: {
type: FIELD_TYPES.SUPER_SELECT,
label: i18n.CONNECTORS,
Expand Down
79 changes: 79 additions & 0 deletions x-pack/plugins/cases/public/components/create/severity.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* 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 { CaseSeverity } from '../../../common/api';
import React from 'react';
import { AppMockRenderer, createAppMockRenderer } from '../../common/mock';
import { Form, FormHook, useForm } from '../../common/shared_imports';
import { Severity } from './severity';
import { FormProps, schema } from './schema';
import userEvent from '@testing-library/user-event';
import { waitFor } from '@testing-library/dom';

let globalForm: FormHook;
const MockHookWrapperComponent: React.FC = ({ children }) => {
const { form } = useForm<FormProps>({
defaultValue: { severity: CaseSeverity.LOW },
schema: {
severity: schema.severity,
},
});

globalForm = form;

return <Form form={form}>{children}</Form>;
};
describe('Severity form field', () => {
let appMockRender: AppMockRenderer;
beforeEach(() => {
appMockRender = createAppMockRenderer();
});
it('renders', () => {
const result = appMockRender.render(
<MockHookWrapperComponent>
<Severity isLoading={false} />
</MockHookWrapperComponent>
);
expect(result.getByTestId('caseSeverity')).toBeTruthy();
expect(result.getByTestId('case-severity-selection')).not.toHaveAttribute('disabled');
});

// default to LOW in this test configuration
it('defaults to the correct value', () => {
const result = appMockRender.render(
<MockHookWrapperComponent>
<Severity isLoading={false} />
</MockHookWrapperComponent>
);
expect(result.getByTestId('caseSeverity')).toBeTruthy();
// two items. one for the popover one for the selected field
expect(result.getAllByTestId('case-severity-selection-low').length).toBe(2);
});

it('selects the correct value when changed', async () => {
const result = appMockRender.render(
<MockHookWrapperComponent>
<Severity isLoading={false} />
</MockHookWrapperComponent>
);
expect(result.getByTestId('caseSeverity')).toBeTruthy();
userEvent.click(result.getByTestId('case-severity-selection'));
userEvent.click(result.getByTestId('case-severity-selection-high'));
await waitFor(() => {
expect(globalForm.getFormData()).toEqual({ severity: 'high' });
});
});

it('disables when loading data', () => {
const result = appMockRender.render(
<MockHookWrapperComponent>
<Severity isLoading={true} />
</MockHookWrapperComponent>
);
expect(result.getByTestId('case-severity-selection')).toHaveAttribute('disabled');
});
});
50 changes: 50 additions & 0 deletions x-pack/plugins/cases/public/components/create/severity.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* 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 { EuiFormRow } from '@elastic/eui';
import React, { memo } from 'react';
import { CaseSeverity } from '../../../common/api';
import { UseField, useFormContext, useFormData } from '../../common/shared_imports';
import { SeveritySelector } from '../severity/selector';
import { SEVERITY_TITLE } from '../severity/translations';

interface Props {
isLoading: boolean;
}

const SeverityFieldFormComponent = ({ isLoading }: { isLoading: boolean }) => {
const { setFieldValue } = useFormContext();
const [{ severity }] = useFormData({ watch: ['severity'] });
const onSeverityChange = (newSeverity: CaseSeverity) => {
setFieldValue('severity', newSeverity);
};
return (
<EuiFormRow data-test-subj="caseSeverity" fullWidth={true} label={SEVERITY_TITLE}>
<SeveritySelector
isLoading={isLoading}
isDisabled={isLoading}
selectedSeverity={severity ?? CaseSeverity.LOW}
onSeverityChange={onSeverityChange}
/>
</EuiFormRow>
);
};
SeverityFieldFormComponent.displayName = 'SeverityFieldForm';

const SeverityComponent: React.FC<Props> = ({ isLoading }) => (
<UseField
path={'severity'}
component={SeverityFieldFormComponent}
componentProps={{
isLoading,
}}
/>
);

SeverityComponent.displayName = 'SeverityComponent';

export const Severity = memo(SeverityComponent);

0 comments on commit c4b554a

Please sign in to comment.