Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SIEM] [Cases] Unit tests for case UI components #63005

Merged
merged 14 commits into from
Apr 10, 2020
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ export const FilterPopoverComponent = ({
{options.map((option, index) => (
<EuiFilterSelectItem
checked={selectedOptions.includes(option) ? 'on' : undefined}
data-test-subj={`options-filter-popover-item-${index}`}
key={`${index}-${option}`}
onClick={toggleSelectedGroupCb.bind(null, option)}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,9 @@ const EditableTitleComponent: React.FC<Props> = ({
}, [changedTitle, title]);

const handleOnChange = useCallback(
(e: ChangeEvent<HTMLInputElement>) => {
onTitleChange(e.target.value);
},
[onTitleChange]
(e: ChangeEvent<HTMLInputElement>) => onTitleChange(e.target.value),
[]
);

return editMode ? (
<EuiFlexGroup alignItems="center" gutterSize="m" justifyContent="spaceBetween">
<EuiFlexItem grow={false}>
Expand Down Expand Up @@ -107,7 +104,7 @@ const EditableTitleComponent: React.FC<Props> = ({
<Title title={title} />
</EuiFlexItem>
<EuiFlexItem grow={false}>
{isLoading && <MySpinner />}
{isLoading && <MySpinner data-test-subj="editable-title-loading" />}
{!isLoading && (
<MyEuiButtonIcon
isDisabled={disabled}
Expand Down
4 changes: 2 additions & 2 deletions x-pack/legacy/plugins/siem/public/containers/case/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -204,13 +204,13 @@ export const patchComment = async (
return convertToCamelCase<CaseResponse, Case>(decodeCaseResponse(response));
};

export const deleteCases = async (caseIds: string[], signal: AbortSignal): Promise<boolean> => {
export const deleteCases = async (caseIds: string[], signal: AbortSignal): Promise<string> => {
const response = await KibanaServices.get().http.fetch<string>(CASES_URL, {
method: 'DELETE',
query: { ids: JSON.stringify(caseIds) },
signal,
});
return response === 'true' ? true : false;
return response;
};

export const pushCase = async (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ export const useCaseConfigure = ({
setLoading(true);
const res = await getCaseConfigure({ signal: abortCtrl.signal });
if (!didCancel) {
setLoading(false);
if (res != null) {
setConnector(res.connectorId, res.connectorName);
if (setClosureType != null) {
Expand All @@ -73,6 +72,7 @@ export const useCaseConfigure = ({
}
}
}
setLoading(false);
}
} catch (error) {
if (!didCancel) {
Expand Down Expand Up @@ -117,7 +117,6 @@ export const useCaseConfigure = ({
abortCtrl.signal
);
if (!didCancel) {
setPersistLoading(false);
setConnector(res.connectorId);
if (setClosureType) {
setClosureType(res.closureType);
Expand All @@ -131,6 +130,7 @@ export const useCaseConfigure = ({
}

displaySuccessToast(i18n.SUCCESS_CONFIGURE, dispatchToaster);
setPersistLoading(false);
}
} catch (error) {
if (!didCancel) {
Expand Down
9 changes: 4 additions & 5 deletions x-pack/legacy/plugins/siem/public/pages/case/case.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,9 @@ import { useGetUserSavedObjectPermissions } from '../../lib/kibana';
import { SpyRoute } from '../../utils/route/spy_routes';
import { AllCases } from './components/all_cases';

import { getSavedObjectReadOnly, CaseCallOut } from './components/callout';
import { savedObjectReadOnly, CaseCallOut } from './components/callout';
import { CaseSavedObjectNoPermissions } from './saved_object_no_permissions';

const infoReadSavedObject = getSavedObjectReadOnly();

export const CasesPage = React.memo(() => {
const userPermissions = useGetUserSavedObjectPermissions();

Expand All @@ -24,10 +22,11 @@ export const CasesPage = React.memo(() => {
<WrapperPage>
{userPermissions != null && !userPermissions?.crud && userPermissions?.read && (
<CaseCallOut
title={infoReadSavedObject.title}
message={infoReadSavedObject.description}
title={savedObjectReadOnly.title}
message={savedObjectReadOnly.description}
/>
)}
<CaseCallOut title={savedObjectReadOnly.title} message={savedObjectReadOnly.description} />
<AllCases userCanCrud={userPermissions?.crud ?? false} />
</WrapperPage>
<SpyRoute />
Expand Down
6 changes: 2 additions & 4 deletions x-pack/legacy/plugins/siem/public/pages/case/case_details.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,7 @@ import { SpyRoute } from '../../utils/route/spy_routes';
import { getCaseUrl } from '../../components/link_to';
import { navTabs } from '../home/home_navigations';
import { CaseView } from './components/case_view';
import { getSavedObjectReadOnly, CaseCallOut } from './components/callout';

const infoReadSavedObject = getSavedObjectReadOnly();
import { savedObjectReadOnly, CaseCallOut } from './components/callout';

export const CaseDetailsPage = React.memo(() => {
const userPermissions = useGetUserSavedObjectPermissions();
Expand All @@ -29,7 +27,7 @@ export const CaseDetailsPage = React.memo(() => {
return caseId != null ? (
<>
{userPermissions != null && !userPermissions?.crud && userPermissions?.read && (
<CaseCallOut title={infoReadSavedObject.title} message={infoReadSavedObject.description} />
<CaseCallOut title={savedObjectReadOnly.title} message={savedObjectReadOnly.description} />
)}
<CaseView caseId={caseId} userCanCrud={userPermissions?.crud ?? false} />
<SpyRoute />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import { CaseProps } from '../case_view';
import { Case, Comment, SortFieldCase } from '../../../../containers/case/types';
import { UseGetCasesState } from '../../../../containers/case/use_get_cases';
import { UserAction, UserActionField } from '../../../../../../../../plugins/case/common/api/cases';

const updateCase = jest.fn();
const fetchCase = jest.fn();

const basicCaseId = 'basic-case-id';
const basicCommentId = 'basic-comment-id';
const basicCreatedAt = '2020-02-20T23:06:33.798Z';
const elasticUser = {
fullName: 'Leslie Knope',
username: 'lknope',
email: '[email protected]',
};

export const basicComment: Comment = {
comment: 'Solve this fast!',
id: basicCommentId,
createdAt: basicCreatedAt,
createdBy: elasticUser,
pushedAt: null,
pushedBy: null,
updatedAt: '2020-02-20T23:06:33.798Z',
updatedBy: {
username: 'elastic',
},
version: 'WzQ3LDFc',
};

export const basicCase: Case = {
closedAt: null,
closedBy: null,
id: basicCaseId,
comments: [basicComment],
createdAt: '2020-02-13T19:44:23.627Z',
createdBy: elasticUser,
description: 'Security banana Issue',
externalService: null,
status: 'open',
tags: ['defacement'],
title: 'Another horrible breach!!',
totalComment: 1,
updatedAt: '2020-02-19T15:02:57.995Z',
updatedBy: {
username: 'elastic',
},
version: 'WzQ3LDFd',
};

export const caseProps: CaseProps = {
caseId: basicCaseId,
userCanCrud: true,
caseData: basicCase,
fetchCase,
updateCase,
};

export const caseClosedProps: CaseProps = {
...caseProps,
caseData: {
...caseProps.caseData,
closedAt: '2020-02-20T23:06:33.798Z',
closedBy: {
username: 'elastic',
},
status: 'closed',
},
};

export const basicCaseClosed: Case = {
...caseClosedProps.caseData,
};

const basicAction = {
actionAt: basicCreatedAt,
actionBy: elasticUser,
oldValue: null,
newValue: 'what a cool value',
caseId: basicCaseId,
commentId: null,
};
export const caseUserActions = [
{
...basicAction,
actionBy: elasticUser,
actionField: ['comment'],
action: 'create',
actionId: 'tt',
},
];

export const useGetCasesMockState: UseGetCasesState = {
data: {
countClosedCases: 0,
countOpenCases: 5,
cases: [
basicCase,
{
closedAt: null,
closedBy: null,
id: '362a5c10-4e99-11ea-9290-35d05cb55c15',
createdAt: '2020-02-13T19:44:13.328Z',
createdBy: { username: 'elastic' },
comments: [],
description: 'Security banana Issue',
externalService: {
pushedAt: '2020-02-13T19:45:01.901Z',
pushedBy: 'elastic',
connectorId: 'string',
connectorName: 'string',
externalId: 'string',
externalTitle: 'string',
externalUrl: 'string',
},
status: 'open',
tags: ['phishing'],
title: 'Bad email',
totalComment: 0,
updatedAt: '2020-02-13T15:45:01.901Z',
updatedBy: { username: 'elastic' },
version: 'WzQ3LDFd',
},
{
closedAt: null,
closedBy: null,
id: '34f8b9e0-4e99-11ea-9290-35d05cb55c15',
createdAt: '2020-02-13T19:44:11.328Z',
createdBy: { username: 'elastic' },
comments: [],
description: 'Security banana Issue',
externalService: {
pushedAt: '2020-02-13T19:45:01.901Z',
pushedBy: 'elastic',
connectorId: 'string',
connectorName: 'string',
externalId: 'string',
externalTitle: 'string',
externalUrl: 'string',
},
status: 'open',
tags: ['phishing'],
title: 'Bad email',
totalComment: 0,
updatedAt: '2020-02-14T19:45:01.901Z',
updatedBy: { username: 'elastic' },
version: 'WzQ3LDFd',
},
{
closedAt: '2020-02-13T19:44:13.328Z',
closedBy: { username: 'elastic' },
id: '31890e90-4e99-11ea-9290-35d05cb55c15',
createdAt: '2020-02-13T19:44:05.563Z',
createdBy: { username: 'elastic' },
comments: [],
description: 'Security banana Issue',
externalService: null,
status: 'closed',
tags: ['phishing'],
title: 'Uh oh',
totalComment: 0,
updatedAt: null,
updatedBy: null,
version: 'WzQ3LDFd',
},
{
closedAt: null,
closedBy: null,
id: '2f5b3210-4e99-11ea-9290-35d05cb55c15',
createdAt: '2020-02-13T19:44:01.901Z',
createdBy: { username: 'elastic' },
comments: [],
description: 'Security banana Issue',
externalService: null,
status: 'open',
tags: ['phishing'],
title: 'Uh oh',
totalComment: 0,
updatedAt: null,
updatedBy: null,
version: 'WzQ3LDFd',
},
],
page: 1,
perPage: 5,
total: 10,
},
loading: [],
selectedCases: [],
isError: false,
queryParams: {
page: 1,
perPage: 5,
sortField: SortFieldCase.createdAt,
sortOrder: 'desc',
},
filterOptions: { search: '', reporters: [], tags: [], status: 'open' },
};

const basicPush = {
connector_id: 'connector_id',
connector_name: 'connector name',
external_id: 'external_id',
external_title: 'external title',
external_url: 'basicPush.com',
pushed_at: basicCreatedAt,
pushed_by: elasticUser,
};
export const getUserAction = (af: UserActionField, a: UserAction) => ({
...basicAction,
actionId: `${af[0]}-${a}`,
actionField: af,
action: a,
commentId: af[0] === 'comment' ? basicCommentId : null,
newValue:
a === 'push-to-service' && af[0] === 'pushed'
? JSON.stringify(basicPush)
: basicAction.newValue,
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
export const mockFormHook = {
isSubmitted: false,
isSubmitting: false,
isValid: true,
submit: jest.fn(),
subscribe: jest.fn(),
setFieldValue: jest.fn(),
setFieldErrors: jest.fn(),
getFields: jest.fn(),
getFormData: jest.fn(),
getFieldDefaultValue: jest.fn(),
/* Returns a list of all errors in the form */
getErrors: jest.fn(),
reset: jest.fn(),
__options: {},
stephmilovic marked this conversation as resolved.
Show resolved Hide resolved
__formData$: {},
__addField: jest.fn(),
__removeField: jest.fn(),
__validateFields: jest.fn(),
__updateFormDataAt: jest.fn(),
__readFieldConfigFromSchema: jest.fn(),
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const getFormMock = (sampleData: any) => ({
...mockFormHook,
submit: () =>
Promise.resolve({
data: sampleData,
isValid: true,
}),
getFormData: () => sampleData,
});
Loading