mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[Detection Engine] Stabilize failing or skipped exception jest tests (#188891)
Addresses skipped/flakey tickets. Things done in this PR: - The `add_exception_flyout` jest tests had a lot of tests that did not check interactions between the components - these could easily be moved into the respective components' unit tests. Any tests removed from `add_exception_flyout` either already existed in the respective component or were added. These tests were flakey, simply because it was sometimes taking so much time to setup the test. Reduced the time it takes to run the tests by about ~9 seconds. - Moved out some of the header/footer logic for the flyouts into components to be unit tested to remove these tests from the ballooning parent component unit tests. - Moved some logic from `add_exception_flyout` out into utils that could be unit tested.
This commit is contained in:
parent
f59aab7e25
commit
ae1ff9edbf
22 changed files with 2071 additions and 1196 deletions
|
@ -0,0 +1,472 @@
|
|||
/*
|
||||
* 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 { getExceptionListItemSchemaMock } from '@kbn/lists-plugin/common/schemas/response/exception_list_item_schema.mock';
|
||||
import { getExceptionListSchemaMock } from '@kbn/lists-plugin/common/schemas/response/exception_list_schema.mock';
|
||||
import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types';
|
||||
|
||||
import { getRulesSchemaMock } from '../../../../../common/api/detection_engine/model/rule_schema/rule_response_schema.mock';
|
||||
import { isSubmitDisabled, prepareNewItemsForSubmission, prepareToCloseAlerts } from './helpers';
|
||||
import type { Rule } from '../../../rule_management/logic/types';
|
||||
import type { AlertData } from '../../utils/types';
|
||||
|
||||
const items = [
|
||||
{
|
||||
...getExceptionListItemSchemaMock(),
|
||||
},
|
||||
];
|
||||
|
||||
const alertDataMock: AlertData = {
|
||||
'@timestamp': '1234567890',
|
||||
_id: 'test-id',
|
||||
file: { path: 'test/path' },
|
||||
};
|
||||
|
||||
describe('add_exception_flyout#helpers', () => {
|
||||
describe('isSubmitDisabled', () => {
|
||||
it('returns true if "isSubmitting" is "true"', () => {
|
||||
expect(
|
||||
isSubmitDisabled({
|
||||
isSubmitting: true,
|
||||
isClosingAlerts: false,
|
||||
errorSubmitting: null,
|
||||
exceptionItemName: 'Item name',
|
||||
exceptionItems: items,
|
||||
itemConditionValidationErrorExists: false,
|
||||
commentErrorExists: false,
|
||||
expireErrorExists: false,
|
||||
addExceptionToRadioSelection: 'add_to_lists',
|
||||
selectedRulesToAddTo: [],
|
||||
listType: ExceptionListTypeEnum.RULE_DEFAULT,
|
||||
exceptionListsToAddTo: [getExceptionListSchemaMock()],
|
||||
})
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
it('returns true if "isClosingAlerts" is "true"', () => {
|
||||
expect(
|
||||
isSubmitDisabled({
|
||||
isSubmitting: false,
|
||||
isClosingAlerts: true,
|
||||
errorSubmitting: null,
|
||||
exceptionItemName: 'Item name',
|
||||
exceptionItems: items,
|
||||
itemConditionValidationErrorExists: false,
|
||||
commentErrorExists: false,
|
||||
expireErrorExists: false,
|
||||
addExceptionToRadioSelection: 'add_to_lists',
|
||||
selectedRulesToAddTo: [],
|
||||
listType: ExceptionListTypeEnum.RULE_DEFAULT,
|
||||
exceptionListsToAddTo: [getExceptionListSchemaMock()],
|
||||
})
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
it('returns true if "itemConditionValidationErrorExists" is "true"', () => {
|
||||
expect(
|
||||
isSubmitDisabled({
|
||||
isSubmitting: false,
|
||||
isClosingAlerts: false,
|
||||
errorSubmitting: null,
|
||||
exceptionItemName: 'Item name',
|
||||
exceptionItems: items,
|
||||
itemConditionValidationErrorExists: true,
|
||||
commentErrorExists: false,
|
||||
expireErrorExists: false,
|
||||
addExceptionToRadioSelection: 'add_to_lists',
|
||||
selectedRulesToAddTo: [],
|
||||
listType: ExceptionListTypeEnum.RULE_DEFAULT,
|
||||
exceptionListsToAddTo: [getExceptionListSchemaMock()],
|
||||
})
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
it('returns true if "commentErrorExists" is "true"', () => {
|
||||
expect(
|
||||
isSubmitDisabled({
|
||||
isSubmitting: false,
|
||||
isClosingAlerts: false,
|
||||
errorSubmitting: null,
|
||||
exceptionItemName: 'Item name',
|
||||
exceptionItems: items,
|
||||
itemConditionValidationErrorExists: false,
|
||||
commentErrorExists: true,
|
||||
expireErrorExists: false,
|
||||
addExceptionToRadioSelection: 'add_to_lists',
|
||||
selectedRulesToAddTo: [],
|
||||
listType: ExceptionListTypeEnum.RULE_DEFAULT,
|
||||
exceptionListsToAddTo: [getExceptionListSchemaMock()],
|
||||
})
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
it('returns true if "expireErrorExists" is "true"', () => {
|
||||
expect(
|
||||
isSubmitDisabled({
|
||||
isSubmitting: false,
|
||||
isClosingAlerts: false,
|
||||
errorSubmitting: null,
|
||||
exceptionItemName: 'Item name',
|
||||
exceptionItems: items,
|
||||
itemConditionValidationErrorExists: false,
|
||||
commentErrorExists: false,
|
||||
expireErrorExists: true,
|
||||
addExceptionToRadioSelection: 'add_to_lists',
|
||||
selectedRulesToAddTo: [],
|
||||
listType: ExceptionListTypeEnum.RULE_DEFAULT,
|
||||
exceptionListsToAddTo: [getExceptionListSchemaMock()],
|
||||
})
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
it('returns true if item name is empty', () => {
|
||||
expect(
|
||||
isSubmitDisabled({
|
||||
isSubmitting: false,
|
||||
isClosingAlerts: false,
|
||||
errorSubmitting: null,
|
||||
exceptionItemName: ' ',
|
||||
exceptionItems: items,
|
||||
itemConditionValidationErrorExists: false,
|
||||
commentErrorExists: false,
|
||||
expireErrorExists: false,
|
||||
addExceptionToRadioSelection: 'add_to_lists',
|
||||
selectedRulesToAddTo: [],
|
||||
listType: ExceptionListTypeEnum.DETECTION,
|
||||
exceptionListsToAddTo: [getExceptionListSchemaMock()],
|
||||
})
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
it('returns true if error submitting exists', () => {
|
||||
expect(
|
||||
isSubmitDisabled({
|
||||
isSubmitting: false,
|
||||
isClosingAlerts: false,
|
||||
errorSubmitting: new Error('uh oh'),
|
||||
exceptionItemName: 'Item name',
|
||||
exceptionItems: items,
|
||||
itemConditionValidationErrorExists: false,
|
||||
commentErrorExists: false,
|
||||
expireErrorExists: false,
|
||||
addExceptionToRadioSelection: 'add_to_lists',
|
||||
selectedRulesToAddTo: [],
|
||||
listType: ExceptionListTypeEnum.DETECTION,
|
||||
exceptionListsToAddTo: [getExceptionListSchemaMock()],
|
||||
})
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
it('returns true if all items do not include any entries', () => {
|
||||
expect(
|
||||
isSubmitDisabled({
|
||||
isSubmitting: false,
|
||||
isClosingAlerts: false,
|
||||
errorSubmitting: null,
|
||||
exceptionItemName: 'Item name',
|
||||
exceptionItems: [
|
||||
{
|
||||
...getExceptionListItemSchemaMock(),
|
||||
entries: [],
|
||||
},
|
||||
],
|
||||
itemConditionValidationErrorExists: false,
|
||||
commentErrorExists: false,
|
||||
expireErrorExists: false,
|
||||
addExceptionToRadioSelection: 'add_to_lists',
|
||||
selectedRulesToAddTo: [],
|
||||
listType: ExceptionListTypeEnum.DETECTION,
|
||||
exceptionListsToAddTo: [getExceptionListSchemaMock()],
|
||||
})
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
it('returns true if exception is to be added to a list, but no list is specified', () => {
|
||||
expect(
|
||||
isSubmitDisabled({
|
||||
isSubmitting: false,
|
||||
isClosingAlerts: false,
|
||||
errorSubmitting: null,
|
||||
exceptionItemName: 'Item name',
|
||||
exceptionItems: items,
|
||||
itemConditionValidationErrorExists: false,
|
||||
commentErrorExists: false,
|
||||
expireErrorExists: false,
|
||||
addExceptionToRadioSelection: 'add_to_lists',
|
||||
selectedRulesToAddTo: [],
|
||||
listType: ExceptionListTypeEnum.DETECTION,
|
||||
exceptionListsToAddTo: [],
|
||||
})
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
it('returns true if exception is to be added to a rule but no rule is specified', () => {
|
||||
expect(
|
||||
isSubmitDisabled({
|
||||
isSubmitting: false,
|
||||
isClosingAlerts: false,
|
||||
errorSubmitting: null,
|
||||
exceptionItemName: 'Item name',
|
||||
exceptionItems: items,
|
||||
itemConditionValidationErrorExists: false,
|
||||
commentErrorExists: false,
|
||||
expireErrorExists: false,
|
||||
addExceptionToRadioSelection: 'select_rules_to_add_to',
|
||||
selectedRulesToAddTo: [],
|
||||
listType: ExceptionListTypeEnum.RULE_DEFAULT,
|
||||
exceptionListsToAddTo: [],
|
||||
})
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
it('returns false if conditions are met for adding exception to a rule', () => {
|
||||
expect(
|
||||
isSubmitDisabled({
|
||||
isSubmitting: false,
|
||||
isClosingAlerts: false,
|
||||
errorSubmitting: null,
|
||||
exceptionItemName: 'Item name',
|
||||
exceptionItems: items,
|
||||
itemConditionValidationErrorExists: false,
|
||||
commentErrorExists: false,
|
||||
expireErrorExists: false,
|
||||
addExceptionToRadioSelection: 'select_rules_to_add_to',
|
||||
selectedRulesToAddTo: [
|
||||
{
|
||||
...getRulesSchemaMock(),
|
||||
exceptions_list: [],
|
||||
} as Rule,
|
||||
],
|
||||
listType: ExceptionListTypeEnum.RULE_DEFAULT,
|
||||
exceptionListsToAddTo: [],
|
||||
})
|
||||
).toBeFalsy();
|
||||
});
|
||||
|
||||
it('returns false if conditions are met for adding exception to a list', () => {
|
||||
expect(
|
||||
isSubmitDisabled({
|
||||
isSubmitting: false,
|
||||
isClosingAlerts: false,
|
||||
errorSubmitting: null,
|
||||
exceptionItemName: 'Item name',
|
||||
exceptionItems: items,
|
||||
itemConditionValidationErrorExists: false,
|
||||
commentErrorExists: false,
|
||||
expireErrorExists: false,
|
||||
addExceptionToRadioSelection: 'add_to_lists',
|
||||
selectedRulesToAddTo: [],
|
||||
listType: ExceptionListTypeEnum.DETECTION,
|
||||
exceptionListsToAddTo: [getExceptionListSchemaMock()],
|
||||
})
|
||||
).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
// Doesn't explicitly test "enrichNewExceptionItems" used within helper as that function
|
||||
// is covered with unit tests itself.
|
||||
describe('prepareNewItemsForSubmission', () => {
|
||||
it('returns "addToLists" true and the sharedListToAddTo lists to add to if correct radio selection and lists are referenced', () => {
|
||||
const { addToRules, addToLists, listsToAddTo } = prepareNewItemsForSubmission({
|
||||
sharedListToAddTo: [getExceptionListSchemaMock()],
|
||||
addExceptionToRadioSelection: 'add_to_lists',
|
||||
exceptionListsToAddTo: [],
|
||||
exceptionItemName: 'Test item',
|
||||
newComment: '',
|
||||
listType: ExceptionListTypeEnum.DETECTION,
|
||||
osTypesSelection: [],
|
||||
expireTime: undefined,
|
||||
exceptionItems: [],
|
||||
});
|
||||
|
||||
expect(addToLists).toBeTruthy();
|
||||
expect(addToRules).toBeFalsy();
|
||||
expect(listsToAddTo).toEqual([getExceptionListSchemaMock()]);
|
||||
});
|
||||
|
||||
it('returns "addToLists" true and the exceptionListsToAddTo if correct radio selection and lists are referenced', () => {
|
||||
const { addToRules, addToLists, listsToAddTo } = prepareNewItemsForSubmission({
|
||||
sharedListToAddTo: [],
|
||||
addExceptionToRadioSelection: 'add_to_lists',
|
||||
exceptionListsToAddTo: [getExceptionListSchemaMock()],
|
||||
exceptionItemName: 'Test item',
|
||||
newComment: '',
|
||||
listType: ExceptionListTypeEnum.DETECTION,
|
||||
osTypesSelection: [],
|
||||
expireTime: undefined,
|
||||
exceptionItems: [],
|
||||
});
|
||||
|
||||
expect(addToLists).toBeTruthy();
|
||||
expect(addToRules).toBeFalsy();
|
||||
expect(listsToAddTo).toEqual([getExceptionListSchemaMock()]);
|
||||
});
|
||||
|
||||
it('returns "addToLists" false if no exception lists are specified as the lists to add to', () => {
|
||||
const { addToRules, addToLists, listsToAddTo } = prepareNewItemsForSubmission({
|
||||
sharedListToAddTo: [],
|
||||
addExceptionToRadioSelection: 'add_to_lists',
|
||||
exceptionListsToAddTo: [],
|
||||
exceptionItemName: 'Test item',
|
||||
newComment: '',
|
||||
listType: ExceptionListTypeEnum.DETECTION,
|
||||
osTypesSelection: [],
|
||||
expireTime: undefined,
|
||||
exceptionItems: [],
|
||||
});
|
||||
|
||||
expect(addToLists).toBeFalsy();
|
||||
expect(addToRules).toBeFalsy();
|
||||
expect(listsToAddTo).toEqual([]);
|
||||
});
|
||||
|
||||
it('returns "addToRules" true if radio selection is "add_to_rule"', () => {
|
||||
const { addToRules, addToLists, listsToAddTo } = prepareNewItemsForSubmission({
|
||||
sharedListToAddTo: [],
|
||||
addExceptionToRadioSelection: 'add_to_rule',
|
||||
exceptionListsToAddTo: [],
|
||||
exceptionItemName: 'Test item',
|
||||
newComment: '',
|
||||
listType: ExceptionListTypeEnum.DETECTION,
|
||||
osTypesSelection: [],
|
||||
expireTime: undefined,
|
||||
exceptionItems: [],
|
||||
});
|
||||
|
||||
expect(addToLists).toBeFalsy();
|
||||
expect(addToRules).toBeTruthy();
|
||||
expect(listsToAddTo).toEqual([]);
|
||||
});
|
||||
|
||||
it('returns "addToRules" true if radio selection is "add_to_rules"', () => {
|
||||
const { addToRules, addToLists, listsToAddTo } = prepareNewItemsForSubmission({
|
||||
sharedListToAddTo: [],
|
||||
addExceptionToRadioSelection: 'add_to_rules',
|
||||
exceptionListsToAddTo: [],
|
||||
exceptionItemName: 'Test item',
|
||||
newComment: '',
|
||||
listType: ExceptionListTypeEnum.DETECTION,
|
||||
osTypesSelection: [],
|
||||
expireTime: undefined,
|
||||
exceptionItems: [],
|
||||
});
|
||||
|
||||
expect(addToLists).toBeFalsy();
|
||||
expect(addToRules).toBeTruthy();
|
||||
expect(listsToAddTo).toEqual([]);
|
||||
});
|
||||
|
||||
it('returns "addToRules" true if radio selection is "select_rules_to_add_to"', () => {
|
||||
const { addToRules, addToLists, listsToAddTo } = prepareNewItemsForSubmission({
|
||||
sharedListToAddTo: [],
|
||||
addExceptionToRadioSelection: 'select_rules_to_add_to',
|
||||
exceptionListsToAddTo: [],
|
||||
exceptionItemName: 'Test item',
|
||||
newComment: '',
|
||||
listType: ExceptionListTypeEnum.DETECTION,
|
||||
osTypesSelection: [],
|
||||
expireTime: undefined,
|
||||
exceptionItems: [],
|
||||
});
|
||||
|
||||
expect(addToLists).toBeFalsy();
|
||||
expect(addToRules).toBeTruthy();
|
||||
expect(listsToAddTo).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('prepareToCloseAlerts', () => {
|
||||
it('returns "shouldCloseAlerts" false if no rule ids defined', () => {
|
||||
const { shouldCloseAlerts, ruleStaticIds } = prepareToCloseAlerts({
|
||||
alertData: undefined,
|
||||
closeSingleAlert: false,
|
||||
addToRules: true,
|
||||
rules: [],
|
||||
bulkCloseAlerts: true,
|
||||
selectedRulesToAddTo: [],
|
||||
});
|
||||
|
||||
expect(shouldCloseAlerts).toBeFalsy();
|
||||
expect(ruleStaticIds).toEqual([]);
|
||||
});
|
||||
|
||||
it('returns "shouldCloseAlerts" false if neither closeSingleAlert or bulkCloseAlerts are true', () => {
|
||||
const { shouldCloseAlerts, ruleStaticIds } = prepareToCloseAlerts({
|
||||
alertData: undefined,
|
||||
closeSingleAlert: false,
|
||||
addToRules: true,
|
||||
rules: [],
|
||||
bulkCloseAlerts: false,
|
||||
selectedRulesToAddTo: [
|
||||
{
|
||||
...getRulesSchemaMock(),
|
||||
exceptions_list: [],
|
||||
} as Rule,
|
||||
],
|
||||
});
|
||||
|
||||
expect(shouldCloseAlerts).toBeFalsy();
|
||||
expect(ruleStaticIds).toEqual(['query-rule-id']);
|
||||
});
|
||||
|
||||
it('returns "alertIdToClose" if "alertData" defined and "closeSingleAlert" selected', () => {
|
||||
const { alertIdToClose, ruleStaticIds } = prepareToCloseAlerts({
|
||||
alertData: alertDataMock,
|
||||
closeSingleAlert: true,
|
||||
addToRules: true,
|
||||
rules: [],
|
||||
bulkCloseAlerts: false,
|
||||
selectedRulesToAddTo: [
|
||||
{
|
||||
...getRulesSchemaMock(),
|
||||
exceptions_list: [],
|
||||
} as Rule,
|
||||
],
|
||||
});
|
||||
|
||||
expect(alertIdToClose).toEqual('test-id');
|
||||
expect(ruleStaticIds).toEqual(['query-rule-id']);
|
||||
});
|
||||
|
||||
it('returns "alertIdToClose" of undefined if "alertData" defined but "closeSingleAlert" is not selected', () => {
|
||||
const { alertIdToClose, ruleStaticIds } = prepareToCloseAlerts({
|
||||
alertData: alertDataMock,
|
||||
closeSingleAlert: false,
|
||||
addToRules: true,
|
||||
rules: [],
|
||||
bulkCloseAlerts: false,
|
||||
selectedRulesToAddTo: [
|
||||
{
|
||||
...getRulesSchemaMock(),
|
||||
exceptions_list: [],
|
||||
} as Rule,
|
||||
],
|
||||
});
|
||||
|
||||
expect(alertIdToClose).toBeUndefined();
|
||||
expect(ruleStaticIds).toEqual(['query-rule-id']);
|
||||
});
|
||||
|
||||
it('returns rule ids from "rules" if "addToRules" is false', () => {
|
||||
const { ruleStaticIds } = prepareToCloseAlerts({
|
||||
alertData: alertDataMock,
|
||||
closeSingleAlert: false,
|
||||
addToRules: false,
|
||||
rules: [
|
||||
{
|
||||
...getRulesSchemaMock(),
|
||||
exceptions_list: [],
|
||||
} as Rule,
|
||||
],
|
||||
bulkCloseAlerts: false,
|
||||
selectedRulesToAddTo: [],
|
||||
});
|
||||
|
||||
expect(ruleStaticIds).toEqual(['query-rule-id']);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,185 @@
|
|||
/*
|
||||
* 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 type { Moment } from 'moment';
|
||||
import type { ExceptionListSchema, OsTypeArray } from '@kbn/securitysolution-io-ts-list-types';
|
||||
import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types';
|
||||
import { isEmpty } from 'lodash/fp';
|
||||
import type { ExceptionsBuilderReturnExceptionItem } from '@kbn/securitysolution-list-utils';
|
||||
|
||||
import type { Rule } from '../../../rule_management/logic/types';
|
||||
import { enrichNewExceptionItems } from '../flyout_components/utils';
|
||||
import type { AlertData } from '../../utils/types';
|
||||
|
||||
const RULE_DEFAULT_OPTIONS = ['add_to_rule', 'add_to_rules', 'select_rules_to_add_to'];
|
||||
|
||||
/**
|
||||
* Determines whether add exception flyout submit button
|
||||
* should be disabled.
|
||||
* @param isSubmitting Is submition completed
|
||||
* @param isClosingAlerts Waiting on close alerts actions to complete
|
||||
* @param errorSubmitting Any submission errors
|
||||
* @param exceptionItemName Item name
|
||||
* @param exceptionItems Items to be created
|
||||
* @param itemConditionValidationErrorExists Item conditions are invalid
|
||||
* @param commentErrorExists Comment invalid or errors exist
|
||||
* @param expireErrorExists Expire time invalid or error exists
|
||||
* @param addExceptionToRadioSelection Radio selection value denoting whether to add item to lists or rules
|
||||
* @param selectedRulesToAddTo List of rules item/s should be added to
|
||||
* @param listType list type of the item being added
|
||||
* @param exceptionListsToAddTo User selected exception lists to add item to
|
||||
*/
|
||||
export const isSubmitDisabled = ({
|
||||
isSubmitting,
|
||||
isClosingAlerts,
|
||||
errorSubmitting,
|
||||
exceptionItemName,
|
||||
exceptionItems,
|
||||
itemConditionValidationErrorExists,
|
||||
commentErrorExists,
|
||||
expireErrorExists,
|
||||
addExceptionToRadioSelection,
|
||||
selectedRulesToAddTo,
|
||||
listType,
|
||||
exceptionListsToAddTo,
|
||||
}: {
|
||||
isSubmitting: boolean;
|
||||
isClosingAlerts: boolean;
|
||||
errorSubmitting: Error | null;
|
||||
exceptionItemName: string;
|
||||
exceptionItems: ExceptionsBuilderReturnExceptionItem[];
|
||||
itemConditionValidationErrorExists: boolean;
|
||||
commentErrorExists: boolean;
|
||||
expireErrorExists: boolean;
|
||||
addExceptionToRadioSelection: string;
|
||||
selectedRulesToAddTo: Rule[];
|
||||
listType: ExceptionListTypeEnum;
|
||||
exceptionListsToAddTo: ExceptionListSchema[];
|
||||
}): boolean => {
|
||||
return (
|
||||
isSubmitting ||
|
||||
isClosingAlerts ||
|
||||
errorSubmitting != null ||
|
||||
exceptionItemName.trim() === '' ||
|
||||
exceptionItems.every((item) => item.entries.length === 0) ||
|
||||
itemConditionValidationErrorExists ||
|
||||
commentErrorExists ||
|
||||
expireErrorExists ||
|
||||
(addExceptionToRadioSelection === 'add_to_lists' && isEmpty(exceptionListsToAddTo)) ||
|
||||
(addExceptionToRadioSelection === 'select_rules_to_add_to' &&
|
||||
isEmpty(selectedRulesToAddTo) &&
|
||||
listType === ExceptionListTypeEnum.RULE_DEFAULT)
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper method for determining if user has selected to add exception
|
||||
* items to specific rules or to exception lists. It also returns the
|
||||
* exception items enriched with various flyout values.
|
||||
* @param sharedListToAddTo Exception list passed into add exception item flyout component
|
||||
* @param addExceptionToRadioSelection Radio selection value denoting whether to add item to lists or rules
|
||||
* @param exceptionListsToAddTo User selected exception lists to add item to
|
||||
* @param exceptionItemName Item name
|
||||
* @param newComment User added comment
|
||||
* @param listType list type of the item being added
|
||||
* @param osTypesSelection For endpoint exceptions, OS selected
|
||||
* @param expireTime User defined item expire time
|
||||
* @param exceptionItems Items to be added
|
||||
*/
|
||||
export const prepareNewItemsForSubmission = ({
|
||||
sharedListToAddTo,
|
||||
addExceptionToRadioSelection,
|
||||
exceptionListsToAddTo,
|
||||
exceptionItemName,
|
||||
newComment,
|
||||
listType,
|
||||
osTypesSelection,
|
||||
expireTime,
|
||||
exceptionItems,
|
||||
}: {
|
||||
sharedListToAddTo: ExceptionListSchema[] | undefined;
|
||||
addExceptionToRadioSelection: string;
|
||||
exceptionListsToAddTo: ExceptionListSchema[];
|
||||
exceptionItemName: string;
|
||||
newComment: string;
|
||||
listType: ExceptionListTypeEnum;
|
||||
osTypesSelection: OsTypeArray;
|
||||
expireTime: Moment | undefined;
|
||||
exceptionItems: ExceptionsBuilderReturnExceptionItem[];
|
||||
}): {
|
||||
listsToAddTo: ExceptionListSchema[];
|
||||
addToLists: boolean;
|
||||
addToRules: boolean;
|
||||
items: ExceptionsBuilderReturnExceptionItem[];
|
||||
} => {
|
||||
const addToRules = RULE_DEFAULT_OPTIONS.includes(addExceptionToRadioSelection);
|
||||
const addToLists =
|
||||
!!sharedListToAddTo?.length ||
|
||||
(addExceptionToRadioSelection === 'add_to_lists' && !isEmpty(exceptionListsToAddTo));
|
||||
const listsToAddTo = sharedListToAddTo?.length ? sharedListToAddTo : exceptionListsToAddTo;
|
||||
|
||||
const items = enrichNewExceptionItems({
|
||||
itemName: exceptionItemName,
|
||||
commentToAdd: newComment,
|
||||
addToRules,
|
||||
addToSharedLists: addToLists,
|
||||
sharedLists: listsToAddTo,
|
||||
listType,
|
||||
selectedOs: osTypesSelection,
|
||||
expireTime,
|
||||
items: exceptionItems,
|
||||
});
|
||||
|
||||
return {
|
||||
listsToAddTo,
|
||||
addToLists,
|
||||
addToRules,
|
||||
items,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Determine whether to close a single alert or bulk close
|
||||
* alerts. Depending on the selection, need to know alert id
|
||||
* and rule ids.
|
||||
* @param alertData Alert the item is being added to
|
||||
* @param closeSingleAlert User selected to close a single alert
|
||||
* @param addToRules User selected to add item to 'x' rules
|
||||
* @param rules Rules to determine which alerts to target when bulk closing
|
||||
* @param bulkCloseAlerts User selected to close all alerts matching new exception
|
||||
* @param selectedRulesToAddTo User selected rules to add item to
|
||||
*/
|
||||
export const prepareToCloseAlerts = ({
|
||||
alertData,
|
||||
closeSingleAlert,
|
||||
addToRules,
|
||||
rules,
|
||||
bulkCloseAlerts,
|
||||
selectedRulesToAddTo,
|
||||
}: {
|
||||
alertData: AlertData | undefined;
|
||||
closeSingleAlert: boolean;
|
||||
addToRules: boolean;
|
||||
rules: Rule[] | null;
|
||||
bulkCloseAlerts: boolean;
|
||||
selectedRulesToAddTo: Rule[];
|
||||
}): {
|
||||
shouldCloseAlerts: boolean;
|
||||
alertIdToClose: string | undefined;
|
||||
ruleStaticIds: string[];
|
||||
} => {
|
||||
const alertIdToClose = closeSingleAlert && alertData ? alertData._id : undefined;
|
||||
const ruleStaticIds = addToRules
|
||||
? selectedRulesToAddTo.map(({ rule_id: ruleId }) => ruleId)
|
||||
: (rules ?? []).map(({ rule_id: ruleId }) => ruleId);
|
||||
|
||||
return {
|
||||
shouldCloseAlerts: !isEmpty(ruleStaticIds) && (bulkCloseAlerts || closeSingleAlert),
|
||||
alertIdToClose,
|
||||
ruleStaticIds,
|
||||
};
|
||||
};
|
File diff suppressed because it is too large
Load diff
|
@ -11,15 +11,11 @@ import { isEmpty } from 'lodash/fp';
|
|||
|
||||
import {
|
||||
EuiFlyout,
|
||||
EuiFlyoutHeader,
|
||||
EuiTitle,
|
||||
EuiFlyoutFooter,
|
||||
EuiFlyoutBody,
|
||||
EuiButton,
|
||||
EuiButtonEmpty,
|
||||
EuiHorizontalRule,
|
||||
EuiSpacer,
|
||||
EuiFlexGroup,
|
||||
EuiSkeletonText,
|
||||
EuiCallOut,
|
||||
EuiText,
|
||||
|
@ -56,13 +52,15 @@ import type { Rule } from '../../../rule_management/logic/types';
|
|||
import { ExceptionItemsFlyoutAlertsActions } from '../flyout_components/alerts_actions';
|
||||
import { ExceptionsAddToRulesOrLists } from '../flyout_components/add_exception_to_rule_or_list';
|
||||
import { useAddNewExceptionItems } from './use_add_new_exceptions';
|
||||
import { enrichNewExceptionItems } from '../flyout_components/utils';
|
||||
import { useCloseAlertsFromExceptions } from '../../logic/use_close_alerts';
|
||||
import { ruleTypesThatAllowLargeValueLists } from '../../utils/constants';
|
||||
import { useInvalidateFetchRuleByIdQuery } from '../../../rule_management/api/hooks/use_fetch_rule_by_id_query';
|
||||
import { ExceptionsExpireTime } from '../flyout_components/expire_time';
|
||||
import { CONFIRM_WARNING_MODAL_LABELS } from '../../../../management/common/translations';
|
||||
import { ArtifactConfirmModal } from '../../../../management/components/artifact_list_page/components/artifact_confirm_modal';
|
||||
import { ExceptionFlyoutFooter } from '../flyout_components/footer';
|
||||
import { ExceptionFlyoutHeader } from '../flyout_components/header';
|
||||
import { isSubmitDisabled, prepareNewItemsForSubmission, prepareToCloseAlerts } from './helpers';
|
||||
|
||||
const SectionHeader = styled(EuiTitle)`
|
||||
${() => css`
|
||||
|
@ -97,18 +95,6 @@ const FlyoutBodySection = styled(EuiFlyoutBody)`
|
|||
`}
|
||||
`;
|
||||
|
||||
const FlyoutHeader = styled(EuiFlyoutHeader)`
|
||||
${({ theme }) => css`
|
||||
border-bottom: 1px solid ${theme.eui.euiColorLightShade};
|
||||
`}
|
||||
`;
|
||||
|
||||
const FlyoutFooterGroup = styled(EuiFlexGroup)`
|
||||
${({ theme }) => css`
|
||||
padding: ${theme.eui.euiSizeS};
|
||||
`}
|
||||
`;
|
||||
|
||||
export const AddExceptionFlyout = memo(function AddExceptionFlyout({
|
||||
rules,
|
||||
isBulkAction,
|
||||
|
@ -395,23 +381,16 @@ export const AddExceptionFlyout = memo(function AddExceptionFlyout({
|
|||
if (submitNewExceptionItems == null) return;
|
||||
|
||||
try {
|
||||
const ruleDefaultOptions = ['add_to_rule', 'add_to_rules', 'select_rules_to_add_to'];
|
||||
const addToRules = ruleDefaultOptions.includes(addExceptionToRadioSelection);
|
||||
const addToSharedLists =
|
||||
!!sharedListToAddTo?.length ||
|
||||
(addExceptionToRadioSelection === 'add_to_lists' && !isEmpty(exceptionListsToAddTo));
|
||||
const sharedLists = sharedListToAddTo?.length ? sharedListToAddTo : exceptionListsToAddTo;
|
||||
|
||||
const items = enrichNewExceptionItems({
|
||||
itemName: exceptionItemName,
|
||||
commentToAdd: newComment,
|
||||
addToRules,
|
||||
addToSharedLists,
|
||||
sharedLists,
|
||||
const { listsToAddTo, addToLists, addToRules, items } = prepareNewItemsForSubmission({
|
||||
sharedListToAddTo,
|
||||
addExceptionToRadioSelection,
|
||||
exceptionListsToAddTo,
|
||||
exceptionItemName,
|
||||
newComment,
|
||||
listType,
|
||||
selectedOs: osTypesSelection,
|
||||
osTypesSelection,
|
||||
expireTime,
|
||||
items: exceptionItems,
|
||||
exceptionItems,
|
||||
});
|
||||
|
||||
const addedItems = await submitNewExceptionItems({
|
||||
|
@ -419,16 +398,20 @@ export const AddExceptionFlyout = memo(function AddExceptionFlyout({
|
|||
selectedRulesToAddTo,
|
||||
listType,
|
||||
addToRules: addToRules && !isEmpty(selectedRulesToAddTo),
|
||||
addToSharedLists,
|
||||
sharedLists,
|
||||
addToSharedLists: addToLists,
|
||||
sharedLists: listsToAddTo,
|
||||
});
|
||||
|
||||
const alertIdToClose = closeSingleAlert && alertData ? alertData._id : undefined;
|
||||
const ruleStaticIds = addToRules
|
||||
? selectedRulesToAddTo.map(({ rule_id: ruleId }) => ruleId)
|
||||
: (rules ?? []).map(({ rule_id: ruleId }) => ruleId);
|
||||
const { shouldCloseAlerts, alertIdToClose, ruleStaticIds } = prepareToCloseAlerts({
|
||||
alertData,
|
||||
closeSingleAlert,
|
||||
addToRules,
|
||||
rules,
|
||||
bulkCloseAlerts,
|
||||
selectedRulesToAddTo,
|
||||
});
|
||||
|
||||
if (closeAlerts != null && !isEmpty(ruleStaticIds) && (bulkCloseAlerts || closeSingleAlert)) {
|
||||
if (closeAlerts != null && shouldCloseAlerts) {
|
||||
await closeAlerts(ruleStaticIds, addedItems, alertIdToClose, bulkCloseIndex);
|
||||
}
|
||||
|
||||
|
@ -470,35 +453,20 @@ export const AddExceptionFlyout = memo(function AddExceptionFlyout({
|
|||
}
|
||||
}, [wildcardWarningExists, submitException]);
|
||||
|
||||
const isSubmitButtonDisabled = useMemo(
|
||||
(): boolean =>
|
||||
isSubmitting ||
|
||||
isClosingAlerts ||
|
||||
errorSubmitting != null ||
|
||||
exceptionItemName.trim() === '' ||
|
||||
exceptionItems.every((item) => item.entries.length === 0) ||
|
||||
itemConditionValidationErrorExists ||
|
||||
commentErrorExists ||
|
||||
expireErrorExists ||
|
||||
(addExceptionToRadioSelection === 'add_to_lists' && isEmpty(exceptionListsToAddTo)) ||
|
||||
(addExceptionToRadioSelection === 'select_rules_to_add_to' &&
|
||||
isEmpty(selectedRulesToAddTo) &&
|
||||
listType === ExceptionListTypeEnum.RULE_DEFAULT),
|
||||
[
|
||||
isSubmitting,
|
||||
isClosingAlerts,
|
||||
errorSubmitting,
|
||||
exceptionItemName,
|
||||
exceptionItems,
|
||||
itemConditionValidationErrorExists,
|
||||
addExceptionToRadioSelection,
|
||||
exceptionListsToAddTo,
|
||||
expireErrorExists,
|
||||
selectedRulesToAddTo,
|
||||
listType,
|
||||
commentErrorExists,
|
||||
]
|
||||
);
|
||||
const isSubmitButtonDisabled = isSubmitDisabled({
|
||||
isSubmitting,
|
||||
isClosingAlerts,
|
||||
errorSubmitting,
|
||||
exceptionItemName,
|
||||
exceptionItems,
|
||||
itemConditionValidationErrorExists,
|
||||
commentErrorExists,
|
||||
expireErrorExists,
|
||||
addExceptionToRadioSelection,
|
||||
selectedRulesToAddTo,
|
||||
listType,
|
||||
exceptionListsToAddTo,
|
||||
});
|
||||
|
||||
const handleDismissError = useCallback((): void => {
|
||||
setErrorSubmitting(null);
|
||||
|
@ -508,12 +476,6 @@ export const AddExceptionFlyout = memo(function AddExceptionFlyout({
|
|||
onCancel(false);
|
||||
}, [onCancel]);
|
||||
|
||||
const addExceptionMessage = useMemo(() => {
|
||||
return listType === ExceptionListTypeEnum.ENDPOINT
|
||||
? i18n.ADD_ENDPOINT_EXCEPTION
|
||||
: i18n.CREATE_RULE_EXCEPTION;
|
||||
}, [listType]);
|
||||
|
||||
const exceptionFlyoutTitleId = useGeneratedHtmlId({
|
||||
prefix: 'exceptionFlyoutTitle',
|
||||
});
|
||||
|
@ -545,14 +507,11 @@ export const AddExceptionFlyout = memo(function AddExceptionFlyout({
|
|||
// EUI TODO: This z-index override of EuiOverlayMask is a workaround, and ideally should be resolved with a cleaner UI/UX flow long-term
|
||||
maskProps={{ style: `z-index: ${(euiTheme.levels.flyout as number) + 3}` }} // we need this flyout to be above the timeline flyout (which has a z-index of 1002)
|
||||
>
|
||||
<FlyoutHeader>
|
||||
<EuiTitle>
|
||||
<h2 id={exceptionFlyoutTitleId} data-test-subj="exceptionFlyoutTitle">
|
||||
{addExceptionMessage}
|
||||
</h2>
|
||||
</EuiTitle>
|
||||
<EuiSpacer size="m" />
|
||||
</FlyoutHeader>
|
||||
<ExceptionFlyoutHeader
|
||||
listType={listType}
|
||||
titleId={exceptionFlyoutTitleId}
|
||||
dataTestSubjId={'exceptionFlyoutTitle'}
|
||||
/>
|
||||
<FlyoutBodySection className="builder-section">
|
||||
{
|
||||
// TODO: This is a quick fix to make sure that we do not lose conditions state on refetching index patterns via `useFetchIndexPatterns`
|
||||
|
@ -660,22 +619,14 @@ export const AddExceptionFlyout = memo(function AddExceptionFlyout({
|
|||
</>
|
||||
)}
|
||||
</FlyoutBodySection>
|
||||
<EuiFlyoutFooter>
|
||||
<FlyoutFooterGroup justifyContent="spaceBetween">
|
||||
<EuiButtonEmpty data-test-subj="cancelExceptionAddButton" onClick={handleCloseFlyout}>
|
||||
{i18n.CANCEL}
|
||||
</EuiButtonEmpty>
|
||||
|
||||
<EuiButton
|
||||
data-test-subj="addExceptionConfirmButton"
|
||||
onClick={handleOnSubmit}
|
||||
isDisabled={isSubmitButtonDisabled}
|
||||
fill
|
||||
>
|
||||
{addExceptionMessage}
|
||||
</EuiButton>
|
||||
</FlyoutFooterGroup>
|
||||
</EuiFlyoutFooter>
|
||||
<ExceptionFlyoutFooter
|
||||
listType={listType}
|
||||
isSubmitButtonDisabled={isSubmitButtonDisabled}
|
||||
cancelButtonDataTestSubjId={'cancelExceptionAddButton'}
|
||||
submitButtonDataTestSubjId={'addExceptionConfirmButton'}
|
||||
handleOnSubmit={handleOnSubmit}
|
||||
handleCloseFlyout={handleCloseFlyout}
|
||||
/>
|
||||
{showConfirmModal && confirmModal}
|
||||
</EuiFlyout>
|
||||
);
|
||||
|
|
|
@ -11,8 +11,21 @@ import { mount } from 'enzyme';
|
|||
import { ExceptionsViewerUtility } from './utility_bar';
|
||||
import { TestProviders } from '../../../../common/mock';
|
||||
|
||||
// FLAKY: https://github.com/elastic/kibana/issues/185023
|
||||
describe.skip('ExceptionsViewerUtility', () => {
|
||||
jest.mock('@kbn/i18n-react', () => {
|
||||
const { i18n } = jest.requireActual('@kbn/i18n');
|
||||
i18n.init({ locale: 'en' });
|
||||
|
||||
const originalModule = jest.requireActual('@kbn/i18n-react');
|
||||
const FormattedRelative = jest.fn();
|
||||
FormattedRelative.mockImplementation(() => '20 hours ago');
|
||||
|
||||
return {
|
||||
...originalModule,
|
||||
FormattedRelative,
|
||||
};
|
||||
});
|
||||
|
||||
describe('ExceptionsViewerUtility', () => {
|
||||
it('it renders correct item counts', () => {
|
||||
const wrapper = mount(
|
||||
<TestProviders>
|
||||
|
@ -55,7 +68,7 @@ describe.skip('ExceptionsViewerUtility', () => {
|
|||
);
|
||||
|
||||
expect(wrapper.find('[data-test-subj="exceptionsViewerLastUpdated"]').at(0).text()).toEqual(
|
||||
'Updated now'
|
||||
'Updated 20 hours ago'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -33,7 +33,6 @@ import { getExceptionListSchemaMock } from '@kbn/lists-plugin/common/schemas/res
|
|||
import { useFetchIndexPatterns } from '../../logic/use_exception_flyout_data';
|
||||
import { useCreateOrUpdateException } from '../../logic/use_create_update_exception';
|
||||
import { useFindExceptionListReferences } from '../../logic/use_find_references';
|
||||
import * as i18n from './translations';
|
||||
import { MAX_COMMENT_LENGTH } from '../../../../../common/constants';
|
||||
|
||||
const mockTheme = getMockTheme({
|
||||
|
@ -260,15 +259,6 @@ describe('When the edit exception modal is opened', () => {
|
|||
);
|
||||
});
|
||||
|
||||
it('displays proper flyout and button text', () => {
|
||||
expect(wrapper.find('[data-test-subj="exceptionFlyoutTitle"]').at(1).text()).toEqual(
|
||||
i18n.EDIT_ENDPOINT_EXCEPTION_TITLE
|
||||
);
|
||||
expect(wrapper.find('[data-test-subj="editExceptionConfirmButton"]').at(1).text()).toEqual(
|
||||
i18n.EDIT_ENDPOINT_EXCEPTION_TITLE
|
||||
);
|
||||
});
|
||||
|
||||
it('should render item name input', () => {
|
||||
expect(wrapper.find('[data-test-subj="exceptionFlyoutNameInput"]').exists()).toBeTruthy();
|
||||
});
|
||||
|
@ -466,15 +456,6 @@ describe('When the edit exception modal is opened', () => {
|
|||
);
|
||||
});
|
||||
|
||||
it('displays proper flyout and button text', () => {
|
||||
expect(wrapper.find('[data-test-subj="exceptionFlyoutTitle"]').at(1).text()).toEqual(
|
||||
i18n.EDIT_EXCEPTION_TITLE
|
||||
);
|
||||
expect(wrapper.find('[data-test-subj="editExceptionConfirmButton"]').at(1).text()).toEqual(
|
||||
i18n.EDIT_EXCEPTION_TITLE
|
||||
);
|
||||
});
|
||||
|
||||
it('should render item name input', () => {
|
||||
expect(wrapper.find('[data-test-subj="exceptionFlyoutNameInput"]').exists()).toBeTruthy();
|
||||
});
|
||||
|
@ -581,15 +562,6 @@ describe('When the edit exception modal is opened', () => {
|
|||
);
|
||||
});
|
||||
|
||||
it('displays proper flyout and button text', () => {
|
||||
expect(wrapper.find('[data-test-subj="exceptionFlyoutTitle"]').at(1).text()).toEqual(
|
||||
i18n.EDIT_EXCEPTION_TITLE
|
||||
);
|
||||
expect(wrapper.find('[data-test-subj="editExceptionConfirmButton"]').at(1).text()).toEqual(
|
||||
i18n.EDIT_EXCEPTION_TITLE
|
||||
);
|
||||
});
|
||||
|
||||
it('should render item name input', () => {
|
||||
expect(wrapper.find('[data-test-subj="exceptionFlyoutNameInput"]').exists()).toBeTruthy();
|
||||
});
|
||||
|
|
|
@ -9,16 +9,10 @@ import { isEmpty } from 'lodash/fp';
|
|||
import React, { useCallback, useEffect, useMemo, useReducer, useState } from 'react';
|
||||
import styled, { css } from 'styled-components';
|
||||
import {
|
||||
EuiButton,
|
||||
EuiButtonEmpty,
|
||||
EuiHorizontalRule,
|
||||
EuiSpacer,
|
||||
EuiFlyoutHeader,
|
||||
EuiFlyoutBody,
|
||||
EuiFlexGroup,
|
||||
EuiTitle,
|
||||
EuiFlyout,
|
||||
EuiFlyoutFooter,
|
||||
EuiSkeletonText,
|
||||
useGeneratedHtmlId,
|
||||
} from '@elastic/eui';
|
||||
|
@ -65,6 +59,8 @@ import { RULE_EXCEPTION, ENDPOINT_EXCEPTION } from '../../utils/translations';
|
|||
import { ExceptionsExpireTime } from '../flyout_components/expire_time';
|
||||
import { CONFIRM_WARNING_MODAL_LABELS } from '../../../../management/common/translations';
|
||||
import { ArtifactConfirmModal } from '../../../../management/components/artifact_list_page/components/artifact_confirm_modal';
|
||||
import { ExceptionFlyoutFooter } from '../flyout_components/footer';
|
||||
import { ExceptionFlyoutHeader } from '../flyout_components/header';
|
||||
|
||||
interface EditExceptionFlyoutProps {
|
||||
list: ExceptionListSchema;
|
||||
|
@ -76,12 +72,6 @@ interface EditExceptionFlyoutProps {
|
|||
onConfirm: (arg: boolean) => void;
|
||||
}
|
||||
|
||||
const FlyoutHeader = styled(EuiFlyoutHeader)`
|
||||
${({ theme }) => css`
|
||||
border-bottom: 1px solid ${theme.eui.euiColorLightShade};
|
||||
`}
|
||||
`;
|
||||
|
||||
const FlyoutBodySection = styled(EuiFlyoutBody)`
|
||||
${() => css`
|
||||
&.builder-section {
|
||||
|
@ -90,12 +80,6 @@ const FlyoutBodySection = styled(EuiFlyoutBody)`
|
|||
`}
|
||||
`;
|
||||
|
||||
const FlyoutFooterGroup = styled(EuiFlexGroup)`
|
||||
${({ theme }) => css`
|
||||
padding: ${theme.eui.euiSizeS};
|
||||
`}
|
||||
`;
|
||||
|
||||
const SectionHeader = styled(EuiTitle)`
|
||||
${() => css`
|
||||
font-weight: ${({ theme }) => theme.eui.euiFontWeightSemiBold};
|
||||
|
@ -357,14 +341,6 @@ const EditExceptionFlyoutComponent: React.FC<EditExceptionFlyoutProps> = ({
|
|||
}
|
||||
}, [wildcardWarningExists, handleSubmitException]);
|
||||
|
||||
const editExceptionMessage = useMemo(
|
||||
() =>
|
||||
listType === ExceptionListTypeEnum.ENDPOINT
|
||||
? i18n.EDIT_ENDPOINT_EXCEPTION_TITLE
|
||||
: i18n.EDIT_EXCEPTION_TITLE,
|
||||
[listType]
|
||||
);
|
||||
|
||||
const isSubmitButtonDisabled = useMemo(
|
||||
() =>
|
||||
isSubmitting ||
|
||||
|
@ -414,14 +390,11 @@ const EditExceptionFlyoutComponent: React.FC<EditExceptionFlyoutProps> = ({
|
|||
data-test-subj="editExceptionFlyout"
|
||||
aria-labelledby={exceptionFlyoutTitleId}
|
||||
>
|
||||
<FlyoutHeader>
|
||||
<EuiTitle>
|
||||
<h2 id={exceptionFlyoutTitleId} data-test-subj="exceptionFlyoutTitle">
|
||||
{editExceptionMessage}
|
||||
</h2>
|
||||
</EuiTitle>
|
||||
<EuiSpacer size="m" />
|
||||
</FlyoutHeader>
|
||||
<ExceptionFlyoutHeader
|
||||
listType={listType}
|
||||
titleId={exceptionFlyoutTitleId}
|
||||
dataTestSubjId={'exceptionFlyoutTitle'}
|
||||
/>
|
||||
<FlyoutBodySection className="builder-section">
|
||||
{isLoading && <EuiSkeletonText data-test-subj="loadingEditExceptionFlyout" lines={4} />}
|
||||
<ExceptionsFlyoutMeta
|
||||
|
@ -499,22 +472,14 @@ const EditExceptionFlyoutComponent: React.FC<EditExceptionFlyoutProps> = ({
|
|||
</>
|
||||
)}
|
||||
</FlyoutBodySection>
|
||||
<EuiFlyoutFooter>
|
||||
<FlyoutFooterGroup justifyContent="spaceBetween">
|
||||
<EuiButtonEmpty data-test-subj="cancelExceptionEditButton" onClick={handleCloseFlyout}>
|
||||
{i18n.CANCEL}
|
||||
</EuiButtonEmpty>
|
||||
|
||||
<EuiButton
|
||||
data-test-subj="editExceptionConfirmButton"
|
||||
onClick={handleOnSubmit}
|
||||
isDisabled={isSubmitButtonDisabled}
|
||||
fill
|
||||
>
|
||||
{editExceptionMessage}
|
||||
</EuiButton>
|
||||
</FlyoutFooterGroup>
|
||||
</EuiFlyoutFooter>
|
||||
<ExceptionFlyoutFooter
|
||||
listType={listType}
|
||||
isSubmitButtonDisabled={isSubmitButtonDisabled}
|
||||
cancelButtonDataTestSubjId={'cancelExceptionEditButton'}
|
||||
submitButtonDataTestSubjId={'editExceptionConfirmButton'}
|
||||
handleOnSubmit={handleOnSubmit}
|
||||
handleCloseFlyout={handleCloseFlyout}
|
||||
/>
|
||||
{showConfirmModal && confirmModal}
|
||||
</EuiFlyout>
|
||||
);
|
||||
|
|
|
@ -7,24 +7,6 @@
|
|||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
export const CANCEL = i18n.translate('xpack.securitySolution.ruleExceptions.editException.cancel', {
|
||||
defaultMessage: 'Cancel',
|
||||
});
|
||||
|
||||
export const EDIT_EXCEPTION_TITLE = i18n.translate(
|
||||
'xpack.securitySolution.ruleExceptions.editException.editExceptionTitle',
|
||||
{
|
||||
defaultMessage: 'Edit rule exception',
|
||||
}
|
||||
);
|
||||
|
||||
export const EDIT_ENDPOINT_EXCEPTION_TITLE = i18n.translate(
|
||||
'xpack.securitySolution.ruleExceptions.editException.editEndpointExceptionTitle',
|
||||
{
|
||||
defaultMessage: 'Edit endpoint exception',
|
||||
}
|
||||
);
|
||||
|
||||
export const EDIT_RULE_EXCEPTION_SUCCESS_TITLE = i18n.translate(
|
||||
'xpack.securitySolution.ruleExceptions.editException.editRuleExceptionToastSuccessTitle',
|
||||
{
|
||||
|
|
|
@ -13,8 +13,13 @@ import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types';
|
|||
import { ExceptionItemsFlyoutAlertsActions } from '.';
|
||||
import { TestProviders } from '../../../../../common/mock';
|
||||
import type { AlertData } from '../../../utils/types';
|
||||
import { useFetchIndex } from '../../../../../common/containers/source';
|
||||
import { useSignalIndex } from '../../../../../detections/containers/detection_engine/alerts/use_signal_index';
|
||||
import { stubIndexPattern } from '@kbn/data-plugin/common/stubs';
|
||||
|
||||
jest.mock('../../../../../common/lib/kibana');
|
||||
jest.mock('../../../../../common/containers/source');
|
||||
jest.mock('../../../../../detections/containers/detection_engine/alerts/use_signal_index');
|
||||
|
||||
const alertDataMock: AlertData = {
|
||||
'@timestamp': '1234567890',
|
||||
|
@ -22,181 +27,364 @@ const alertDataMock: AlertData = {
|
|||
file: { path: 'test/path' },
|
||||
};
|
||||
|
||||
const mockUseSignalIndex = useSignalIndex as jest.Mock<Partial<ReturnType<typeof useSignalIndex>>>;
|
||||
const mockUseFetchIndex = useFetchIndex as jest.Mock;
|
||||
|
||||
describe('ExceptionItemsFlyoutAlertsActions', () => {
|
||||
it('it displays single alert close checkbox if alert status is not "closed" and "alertData" exists', () => {
|
||||
const wrapper = mountWithIntl(
|
||||
<TestProviders>
|
||||
<ExceptionItemsFlyoutAlertsActions
|
||||
exceptionListItems={[getExceptionListItemSchemaMock()]}
|
||||
exceptionListType={ExceptionListTypeEnum.DETECTION}
|
||||
shouldCloseSingleAlert={false}
|
||||
shouldBulkCloseAlert={false}
|
||||
disableBulkClose={false}
|
||||
alertData={alertDataMock}
|
||||
alertStatus="open"
|
||||
onDisableBulkClose={jest.fn()}
|
||||
onUpdateBulkCloseIndex={jest.fn()}
|
||||
onBulkCloseCheckboxChange={jest.fn()}
|
||||
onSingleAlertCloseCheckboxChange={jest.fn()}
|
||||
isAlertDataLoading={false}
|
||||
/>
|
||||
</TestProviders>
|
||||
);
|
||||
beforeEach(() => {
|
||||
mockUseSignalIndex.mockImplementation(() => ({
|
||||
loading: false,
|
||||
signalIndexName: 'mock-siem-signals-index',
|
||||
}));
|
||||
|
||||
expect(
|
||||
wrapper.find('[data-test-subj="closeAlertOnAddExceptionCheckbox"]').exists()
|
||||
).toBeTruthy();
|
||||
mockUseFetchIndex.mockImplementation(() => [
|
||||
false,
|
||||
{
|
||||
indexPatterns: stubIndexPattern,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('it does not display single alert close checkbox if alert status is "closed"', () => {
|
||||
const wrapper = mountWithIntl(
|
||||
<TestProviders>
|
||||
<ExceptionItemsFlyoutAlertsActions
|
||||
exceptionListItems={[getExceptionListItemSchemaMock()]}
|
||||
exceptionListType={ExceptionListTypeEnum.DETECTION}
|
||||
shouldCloseSingleAlert={false}
|
||||
shouldBulkCloseAlert={false}
|
||||
disableBulkClose={false}
|
||||
alertData={alertDataMock}
|
||||
alertStatus="closed"
|
||||
onDisableBulkClose={jest.fn()}
|
||||
onUpdateBulkCloseIndex={jest.fn()}
|
||||
onBulkCloseCheckboxChange={jest.fn()}
|
||||
onSingleAlertCloseCheckboxChange={jest.fn()}
|
||||
isAlertDataLoading={false}
|
||||
/>
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(
|
||||
wrapper.find('[data-test-subj="closeAlertOnAddExceptionCheckbox"]').exists()
|
||||
).toBeFalsy();
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('it does not display single alert close checkbox if "alertData" does not exist', () => {
|
||||
const wrapper = mountWithIntl(
|
||||
<TestProviders>
|
||||
<ExceptionItemsFlyoutAlertsActions
|
||||
exceptionListItems={[getExceptionListItemSchemaMock()]}
|
||||
exceptionListType={ExceptionListTypeEnum.DETECTION}
|
||||
shouldCloseSingleAlert={false}
|
||||
shouldBulkCloseAlert={false}
|
||||
disableBulkClose={false}
|
||||
alertData={undefined}
|
||||
alertStatus="open"
|
||||
onDisableBulkClose={jest.fn()}
|
||||
onUpdateBulkCloseIndex={jest.fn()}
|
||||
onBulkCloseCheckboxChange={jest.fn()}
|
||||
onSingleAlertCloseCheckboxChange={jest.fn()}
|
||||
isAlertDataLoading={false}
|
||||
/>
|
||||
</TestProviders>
|
||||
);
|
||||
describe('Endpoint specific logic', () => {
|
||||
it('it displays endpoint quarantine text if exception list type is "endpoint"', () => {
|
||||
const wrapper = mountWithIntl(
|
||||
<TestProviders>
|
||||
<ExceptionItemsFlyoutAlertsActions
|
||||
exceptionListItems={[getExceptionListItemSchemaMock()]}
|
||||
exceptionListType={ExceptionListTypeEnum.ENDPOINT}
|
||||
shouldCloseSingleAlert={false}
|
||||
shouldBulkCloseAlert={false}
|
||||
disableBulkClose={false}
|
||||
alertData={alertDataMock}
|
||||
alertStatus="open"
|
||||
onDisableBulkClose={jest.fn()}
|
||||
onUpdateBulkCloseIndex={jest.fn()}
|
||||
onBulkCloseCheckboxChange={jest.fn()}
|
||||
onSingleAlertCloseCheckboxChange={jest.fn()}
|
||||
isAlertDataLoading={false}
|
||||
/>
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(
|
||||
wrapper.find('[data-test-subj="closeAlertOnAddExceptionCheckbox"]').exists()
|
||||
).toBeFalsy();
|
||||
expect(wrapper.find('[data-test-subj="addExceptionEndpointText"]').exists()).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
it('it displays bulk close checkbox', () => {
|
||||
const wrapper = mountWithIntl(
|
||||
<TestProviders>
|
||||
<ExceptionItemsFlyoutAlertsActions
|
||||
exceptionListItems={[getExceptionListItemSchemaMock()]}
|
||||
exceptionListType={ExceptionListTypeEnum.DETECTION}
|
||||
shouldCloseSingleAlert={false}
|
||||
shouldBulkCloseAlert={false}
|
||||
disableBulkClose={false}
|
||||
alertData={alertDataMock}
|
||||
alertStatus="open"
|
||||
onDisableBulkClose={jest.fn()}
|
||||
onUpdateBulkCloseIndex={jest.fn()}
|
||||
onBulkCloseCheckboxChange={jest.fn()}
|
||||
onSingleAlertCloseCheckboxChange={jest.fn()}
|
||||
isAlertDataLoading={false}
|
||||
/>
|
||||
</TestProviders>
|
||||
);
|
||||
describe('alert data exists', () => {
|
||||
it('it displays single alert close checkbox if alert status is not "closed" and "alertData" exists', () => {
|
||||
const wrapper = mountWithIntl(
|
||||
<TestProviders>
|
||||
<ExceptionItemsFlyoutAlertsActions
|
||||
exceptionListItems={[getExceptionListItemSchemaMock()]}
|
||||
exceptionListType={ExceptionListTypeEnum.DETECTION}
|
||||
shouldCloseSingleAlert={false}
|
||||
shouldBulkCloseAlert={false}
|
||||
disableBulkClose={false}
|
||||
alertData={alertDataMock}
|
||||
alertStatus="open"
|
||||
onDisableBulkClose={jest.fn()}
|
||||
onUpdateBulkCloseIndex={jest.fn()}
|
||||
onBulkCloseCheckboxChange={jest.fn()}
|
||||
onSingleAlertCloseCheckboxChange={jest.fn()}
|
||||
isAlertDataLoading={false}
|
||||
/>
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(
|
||||
wrapper.find('[data-test-subj="bulkCloseAlertOnAddExceptionCheckbox"]').exists()
|
||||
).toBeTruthy();
|
||||
expect(
|
||||
wrapper.find('[data-test-subj="closeAlertOnAddExceptionCheckbox"]').exists()
|
||||
).toBeTruthy();
|
||||
expect(
|
||||
wrapper.find('[data-test-subj="closeAlertOnAddExceptionCheckbox"] input').prop('disabled')
|
||||
).toBeFalsy();
|
||||
});
|
||||
|
||||
it('it displays single alert close checkbox disabled if "isAlertDataLoading" is true', () => {
|
||||
const wrapper = mountWithIntl(
|
||||
<TestProviders>
|
||||
<ExceptionItemsFlyoutAlertsActions
|
||||
exceptionListItems={[getExceptionListItemSchemaMock()]}
|
||||
exceptionListType={ExceptionListTypeEnum.DETECTION}
|
||||
shouldCloseSingleAlert={false}
|
||||
shouldBulkCloseAlert={false}
|
||||
disableBulkClose={false}
|
||||
alertData={alertDataMock}
|
||||
alertStatus="open"
|
||||
onDisableBulkClose={jest.fn()}
|
||||
onUpdateBulkCloseIndex={jest.fn()}
|
||||
onBulkCloseCheckboxChange={jest.fn()}
|
||||
onSingleAlertCloseCheckboxChange={jest.fn()}
|
||||
isAlertDataLoading
|
||||
/>
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(
|
||||
wrapper.find('[data-test-subj="closeAlertOnAddExceptionCheckbox"] input').prop('disabled')
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
it('it displays single alert close checkbox disabled if "isSignalIndexLoading" is true', () => {
|
||||
mockUseSignalIndex.mockImplementation(() => ({
|
||||
loading: true,
|
||||
signalIndexName: 'mock-siem-signals-index',
|
||||
}));
|
||||
|
||||
const wrapper = mountWithIntl(
|
||||
<TestProviders>
|
||||
<ExceptionItemsFlyoutAlertsActions
|
||||
exceptionListItems={[getExceptionListItemSchemaMock()]}
|
||||
exceptionListType={ExceptionListTypeEnum.DETECTION}
|
||||
shouldCloseSingleAlert={false}
|
||||
shouldBulkCloseAlert={false}
|
||||
disableBulkClose={false}
|
||||
alertData={alertDataMock}
|
||||
alertStatus="open"
|
||||
onDisableBulkClose={jest.fn()}
|
||||
onUpdateBulkCloseIndex={jest.fn()}
|
||||
onBulkCloseCheckboxChange={jest.fn()}
|
||||
onSingleAlertCloseCheckboxChange={jest.fn()}
|
||||
isAlertDataLoading={false}
|
||||
/>
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(
|
||||
wrapper.find('[data-test-subj="closeAlertOnAddExceptionCheckbox"] input').prop('disabled')
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
it('it displays single alert close checkbox disabled if "isSignalIndexPatternLoading" is true', () => {
|
||||
mockUseFetchIndex.mockImplementation(() => [
|
||||
true,
|
||||
{
|
||||
indexPatterns: stubIndexPattern,
|
||||
},
|
||||
]);
|
||||
|
||||
const wrapper = mountWithIntl(
|
||||
<TestProviders>
|
||||
<ExceptionItemsFlyoutAlertsActions
|
||||
exceptionListItems={[getExceptionListItemSchemaMock()]}
|
||||
exceptionListType={ExceptionListTypeEnum.DETECTION}
|
||||
shouldCloseSingleAlert={false}
|
||||
shouldBulkCloseAlert={false}
|
||||
disableBulkClose={false}
|
||||
alertData={alertDataMock}
|
||||
alertStatus="open"
|
||||
onDisableBulkClose={jest.fn()}
|
||||
onUpdateBulkCloseIndex={jest.fn()}
|
||||
onBulkCloseCheckboxChange={jest.fn()}
|
||||
onSingleAlertCloseCheckboxChange={jest.fn()}
|
||||
isAlertDataLoading={false}
|
||||
/>
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(
|
||||
wrapper.find('[data-test-subj="closeAlertOnAddExceptionCheckbox"] input').prop('disabled')
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
it('it does not display single alert close checkbox if alert status is "closed"', () => {
|
||||
const wrapper = mountWithIntl(
|
||||
<TestProviders>
|
||||
<ExceptionItemsFlyoutAlertsActions
|
||||
exceptionListItems={[getExceptionListItemSchemaMock()]}
|
||||
exceptionListType={ExceptionListTypeEnum.DETECTION}
|
||||
shouldCloseSingleAlert={false}
|
||||
shouldBulkCloseAlert={false}
|
||||
disableBulkClose={false}
|
||||
alertData={alertDataMock}
|
||||
alertStatus="closed"
|
||||
onDisableBulkClose={jest.fn()}
|
||||
onUpdateBulkCloseIndex={jest.fn()}
|
||||
onBulkCloseCheckboxChange={jest.fn()}
|
||||
onSingleAlertCloseCheckboxChange={jest.fn()}
|
||||
isAlertDataLoading={false}
|
||||
/>
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(
|
||||
wrapper.find('[data-test-subj="closeAlertOnAddExceptionCheckbox"]').exists()
|
||||
).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
it('it displays checkboxes disabled if "isAlertDataLoading" is "true"', () => {
|
||||
const wrapper = mountWithIntl(
|
||||
<TestProviders>
|
||||
<ExceptionItemsFlyoutAlertsActions
|
||||
exceptionListItems={[getExceptionListItemSchemaMock()]}
|
||||
exceptionListType={ExceptionListTypeEnum.DETECTION}
|
||||
shouldCloseSingleAlert={false}
|
||||
shouldBulkCloseAlert={false}
|
||||
disableBulkClose={false}
|
||||
alertData={alertDataMock}
|
||||
alertStatus="open"
|
||||
onDisableBulkClose={jest.fn()}
|
||||
onUpdateBulkCloseIndex={jest.fn()}
|
||||
onBulkCloseCheckboxChange={jest.fn()}
|
||||
onSingleAlertCloseCheckboxChange={jest.fn()}
|
||||
isAlertDataLoading
|
||||
/>
|
||||
</TestProviders>
|
||||
);
|
||||
describe('bulk close alert', () => {
|
||||
it('it does not display single alert close checkbox if "alertData" does not exist', () => {
|
||||
const wrapper = mountWithIntl(
|
||||
<TestProviders>
|
||||
<ExceptionItemsFlyoutAlertsActions
|
||||
exceptionListItems={[getExceptionListItemSchemaMock()]}
|
||||
exceptionListType={ExceptionListTypeEnum.DETECTION}
|
||||
shouldCloseSingleAlert={false}
|
||||
shouldBulkCloseAlert={false}
|
||||
disableBulkClose={false}
|
||||
alertData={undefined}
|
||||
alertStatus="open"
|
||||
onDisableBulkClose={jest.fn()}
|
||||
onUpdateBulkCloseIndex={jest.fn()}
|
||||
onBulkCloseCheckboxChange={jest.fn()}
|
||||
onSingleAlertCloseCheckboxChange={jest.fn()}
|
||||
isAlertDataLoading={false}
|
||||
/>
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(
|
||||
wrapper.find('[data-test-subj="bulkCloseAlertOnAddExceptionCheckbox"]').at(0).props().disabled
|
||||
).toBeTruthy();
|
||||
expect(
|
||||
wrapper.find('[data-test-subj="closeAlertOnAddExceptionCheckbox"]').at(0).props().disabled
|
||||
).toBeTruthy();
|
||||
});
|
||||
expect(
|
||||
wrapper.find('[data-test-subj="closeAlertOnAddExceptionCheckbox"]').exists()
|
||||
).toBeFalsy();
|
||||
});
|
||||
|
||||
it('it displays bulk close checkbox disabled if "disableBulkCloseAlert" is "true"', () => {
|
||||
const wrapper = mountWithIntl(
|
||||
<TestProviders>
|
||||
<ExceptionItemsFlyoutAlertsActions
|
||||
exceptionListItems={[getExceptionListItemSchemaMock()]}
|
||||
exceptionListType={ExceptionListTypeEnum.DETECTION}
|
||||
shouldCloseSingleAlert={false}
|
||||
shouldBulkCloseAlert={false}
|
||||
disableBulkClose={true}
|
||||
alertData={alertDataMock}
|
||||
alertStatus="open"
|
||||
onDisableBulkClose={jest.fn()}
|
||||
onUpdateBulkCloseIndex={jest.fn()}
|
||||
onBulkCloseCheckboxChange={jest.fn()}
|
||||
onSingleAlertCloseCheckboxChange={jest.fn()}
|
||||
isAlertDataLoading={false}
|
||||
/>
|
||||
</TestProviders>
|
||||
);
|
||||
it('it displays bulk close checkbox', () => {
|
||||
const wrapper = mountWithIntl(
|
||||
<TestProviders>
|
||||
<ExceptionItemsFlyoutAlertsActions
|
||||
exceptionListItems={[getExceptionListItemSchemaMock()]}
|
||||
exceptionListType={ExceptionListTypeEnum.DETECTION}
|
||||
shouldCloseSingleAlert={false}
|
||||
shouldBulkCloseAlert={false}
|
||||
disableBulkClose={false}
|
||||
alertData={alertDataMock}
|
||||
alertStatus="open"
|
||||
onDisableBulkClose={jest.fn()}
|
||||
onUpdateBulkCloseIndex={jest.fn()}
|
||||
onBulkCloseCheckboxChange={jest.fn()}
|
||||
onSingleAlertCloseCheckboxChange={jest.fn()}
|
||||
isAlertDataLoading={false}
|
||||
/>
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(
|
||||
wrapper.find('[data-test-subj="bulkCloseAlertOnAddExceptionCheckbox"]').at(0).props().disabled
|
||||
).toBeTruthy();
|
||||
expect(wrapper.find('[data-test-subj="addExceptionEndpointText"]').exists()).toBeFalsy();
|
||||
});
|
||||
expect(
|
||||
wrapper.find('[data-test-subj="bulkCloseAlertOnAddExceptionCheckbox"]').exists()
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
it('it displays endpoint quarantine text if exception list type is "endpoint"', () => {
|
||||
const wrapper = mountWithIntl(
|
||||
<TestProviders>
|
||||
<ExceptionItemsFlyoutAlertsActions
|
||||
exceptionListItems={[getExceptionListItemSchemaMock()]}
|
||||
exceptionListType={ExceptionListTypeEnum.ENDPOINT}
|
||||
shouldCloseSingleAlert={false}
|
||||
shouldBulkCloseAlert={false}
|
||||
disableBulkClose={false}
|
||||
alertData={alertDataMock}
|
||||
alertStatus="open"
|
||||
onDisableBulkClose={jest.fn()}
|
||||
onUpdateBulkCloseIndex={jest.fn()}
|
||||
onBulkCloseCheckboxChange={jest.fn()}
|
||||
onSingleAlertCloseCheckboxChange={jest.fn()}
|
||||
isAlertDataLoading={false}
|
||||
/>
|
||||
</TestProviders>
|
||||
);
|
||||
it('it displays checkboxes disabled if "isAlertDataLoading" is "true"', () => {
|
||||
const wrapper = mountWithIntl(
|
||||
<TestProviders>
|
||||
<ExceptionItemsFlyoutAlertsActions
|
||||
exceptionListItems={[getExceptionListItemSchemaMock()]}
|
||||
exceptionListType={ExceptionListTypeEnum.DETECTION}
|
||||
shouldCloseSingleAlert={false}
|
||||
shouldBulkCloseAlert={false}
|
||||
disableBulkClose={false}
|
||||
alertData={alertDataMock}
|
||||
alertStatus="open"
|
||||
onDisableBulkClose={jest.fn()}
|
||||
onUpdateBulkCloseIndex={jest.fn()}
|
||||
onBulkCloseCheckboxChange={jest.fn()}
|
||||
onSingleAlertCloseCheckboxChange={jest.fn()}
|
||||
isAlertDataLoading
|
||||
/>
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(wrapper.find('[data-test-subj="addExceptionEndpointText"]').exists()).toBeTruthy();
|
||||
expect(
|
||||
wrapper.find('[data-test-subj="bulkCloseAlertOnAddExceptionCheckbox"]').at(0).props()
|
||||
.disabled
|
||||
).toBeTruthy();
|
||||
expect(
|
||||
wrapper.find('[data-test-subj="closeAlertOnAddExceptionCheckbox"]').at(0).props().disabled
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
it('it displays bulk close checkbox disabled if "disableBulkCloseAlert" is "true"', () => {
|
||||
const wrapper = mountWithIntl(
|
||||
<TestProviders>
|
||||
<ExceptionItemsFlyoutAlertsActions
|
||||
exceptionListItems={[getExceptionListItemSchemaMock()]}
|
||||
exceptionListType={ExceptionListTypeEnum.DETECTION}
|
||||
shouldCloseSingleAlert={false}
|
||||
shouldBulkCloseAlert={false}
|
||||
disableBulkClose={true}
|
||||
alertData={alertDataMock}
|
||||
alertStatus="open"
|
||||
onDisableBulkClose={jest.fn()}
|
||||
onUpdateBulkCloseIndex={jest.fn()}
|
||||
onBulkCloseCheckboxChange={jest.fn()}
|
||||
onSingleAlertCloseCheckboxChange={jest.fn()}
|
||||
isAlertDataLoading={false}
|
||||
/>
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(
|
||||
wrapper.find('[data-test-subj="bulkCloseAlertOnAddExceptionCheckbox"]').at(0).props()
|
||||
.disabled
|
||||
).toBeTruthy();
|
||||
expect(wrapper.find('[data-test-subj="addExceptionEndpointText"]').exists()).toBeFalsy();
|
||||
});
|
||||
|
||||
it('it displays bulk close checkbox disabled if "isSignalIndexLoading" is "true"', () => {
|
||||
mockUseSignalIndex.mockImplementation(() => ({
|
||||
loading: true,
|
||||
signalIndexName: 'mock-siem-signals-index',
|
||||
}));
|
||||
|
||||
const wrapper = mountWithIntl(
|
||||
<TestProviders>
|
||||
<ExceptionItemsFlyoutAlertsActions
|
||||
exceptionListItems={[getExceptionListItemSchemaMock()]}
|
||||
exceptionListType={ExceptionListTypeEnum.DETECTION}
|
||||
shouldCloseSingleAlert={false}
|
||||
shouldBulkCloseAlert={false}
|
||||
disableBulkClose={false}
|
||||
alertData={alertDataMock}
|
||||
alertStatus="open"
|
||||
onDisableBulkClose={jest.fn()}
|
||||
onUpdateBulkCloseIndex={jest.fn()}
|
||||
onBulkCloseCheckboxChange={jest.fn()}
|
||||
onSingleAlertCloseCheckboxChange={jest.fn()}
|
||||
isAlertDataLoading={false}
|
||||
/>
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(
|
||||
wrapper.find('[data-test-subj="bulkCloseAlertOnAddExceptionCheckbox"]').at(0).props()
|
||||
.disabled
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
it('it displays bulk close checkbox disabled if "isSignalIndexPatternLoading" is "true"', () => {
|
||||
mockUseFetchIndex.mockImplementation(() => [
|
||||
true,
|
||||
{
|
||||
indexPatterns: stubIndexPattern,
|
||||
},
|
||||
]);
|
||||
|
||||
const wrapper = mountWithIntl(
|
||||
<TestProviders>
|
||||
<ExceptionItemsFlyoutAlertsActions
|
||||
exceptionListItems={[getExceptionListItemSchemaMock()]}
|
||||
exceptionListType={ExceptionListTypeEnum.DETECTION}
|
||||
shouldCloseSingleAlert={false}
|
||||
shouldBulkCloseAlert={false}
|
||||
disableBulkClose={false}
|
||||
alertData={alertDataMock}
|
||||
alertStatus="open"
|
||||
onDisableBulkClose={jest.fn()}
|
||||
onUpdateBulkCloseIndex={jest.fn()}
|
||||
onBulkCloseCheckboxChange={jest.fn()}
|
||||
onSingleAlertCloseCheckboxChange={jest.fn()}
|
||||
isAlertDataLoading={false}
|
||||
/>
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(
|
||||
wrapper.find('[data-test-subj="bulkCloseAlertOnAddExceptionCheckbox"]').at(0).props()
|
||||
.disabled
|
||||
).toBeTruthy();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -15,7 +15,7 @@ import type { ExceptionsBuilderReturnExceptionItem } from '@kbn/securitysolution
|
|||
import { useSignalIndex } from '../../../../../detections/containers/detection_engine/alerts/use_signal_index';
|
||||
import type { Status } from '../../../../../../common/api/detection_engine';
|
||||
import { useFetchIndex } from '../../../../../common/containers/source';
|
||||
import { entryHasListType, entryHasNonEcsType } from './utils';
|
||||
import { shouldDisableBulkClose } from './utils';
|
||||
import * as i18n from './translations';
|
||||
import type { AlertData } from '../../../utils/types';
|
||||
|
||||
|
@ -103,9 +103,7 @@ const ExceptionItemsFlyoutAlertsActionsComponent: React.FC<
|
|||
useEffect((): void => {
|
||||
if (isSignalIndexPatternLoading === false && isSignalIndexLoading === false) {
|
||||
onDisableBulkClose(
|
||||
entryHasListType(exceptionListItems) ||
|
||||
entryHasNonEcsType(exceptionListItems, signalIndexPatterns) ||
|
||||
exceptionListItems.every((item) => item.entries.length === 0)
|
||||
shouldDisableBulkClose({ items: exceptionListItems, signalIndexPatterns })
|
||||
);
|
||||
}
|
||||
}, [
|
||||
|
|
|
@ -8,9 +8,24 @@
|
|||
import { getExceptionListItemSchemaMock } from '@kbn/lists-plugin/common/schemas/response/exception_list_item_schema.mock';
|
||||
import type { EntriesArray, ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types';
|
||||
import { ListOperatorTypeEnum } from '@kbn/securitysolution-io-ts-list-types';
|
||||
import { entryHasNonEcsType, entryHasListType } from './utils';
|
||||
import { entryHasNonEcsType, entryHasListType, shouldDisableBulkClose } from './utils';
|
||||
import type { DataViewBase } from '@kbn/es-query';
|
||||
|
||||
const mockEcsIndexPattern = {
|
||||
title: 'testIndex',
|
||||
fields: [
|
||||
{
|
||||
name: 'some.parentField',
|
||||
},
|
||||
{
|
||||
name: 'some.not.nested.field',
|
||||
},
|
||||
{
|
||||
name: 'nested.field',
|
||||
},
|
||||
],
|
||||
} as DataViewBase;
|
||||
|
||||
describe('alerts_actions#utils', () => {
|
||||
describe('#entryHasListType', () => {
|
||||
test('it should return false with an empty array', () => {
|
||||
|
@ -39,21 +54,6 @@ describe('alerts_actions#utils', () => {
|
|||
});
|
||||
|
||||
describe('#entryHasNonEcsType', () => {
|
||||
const mockEcsIndexPattern = {
|
||||
title: 'testIndex',
|
||||
fields: [
|
||||
{
|
||||
name: 'some.parentField',
|
||||
},
|
||||
{
|
||||
name: 'some.not.nested.field',
|
||||
},
|
||||
{
|
||||
name: 'nested.field',
|
||||
},
|
||||
],
|
||||
} as DataViewBase;
|
||||
|
||||
test('it should return false with an empty array', () => {
|
||||
const payload: ExceptionListItemSchema[] = [];
|
||||
const result = entryHasNonEcsType(payload, mockEcsIndexPattern);
|
||||
|
@ -78,4 +78,73 @@ describe('alerts_actions#utils', () => {
|
|||
expect(result).toEqual(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#shouldDisableBulkClose', () => {
|
||||
it('returns true if items include large value lists', () => {
|
||||
expect(
|
||||
shouldDisableBulkClose({
|
||||
items: [
|
||||
{
|
||||
...getExceptionListItemSchemaMock(),
|
||||
entries: [
|
||||
{
|
||||
field: 'host.name',
|
||||
list: { type: 'text', id: 'blob' },
|
||||
operator: 'included',
|
||||
type: 'list',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
signalIndexPatterns: mockEcsIndexPattern,
|
||||
})
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
it('returns true if items include non ECS types', () => {
|
||||
expect(
|
||||
shouldDisableBulkClose({
|
||||
items: [
|
||||
{
|
||||
...getExceptionListItemSchemaMock(),
|
||||
entries: [{ field: 'some.nonEcsField' }] as EntriesArray,
|
||||
},
|
||||
],
|
||||
signalIndexPatterns: mockEcsIndexPattern,
|
||||
})
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
it('returns true if all items have no entries', () => {
|
||||
expect(
|
||||
shouldDisableBulkClose({
|
||||
items: [
|
||||
{
|
||||
...getExceptionListItemSchemaMock(),
|
||||
entries: [] as EntriesArray,
|
||||
},
|
||||
],
|
||||
signalIndexPatterns: mockEcsIndexPattern,
|
||||
})
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
it('returns true if no items exist', () => {
|
||||
expect(
|
||||
shouldDisableBulkClose({
|
||||
items: [],
|
||||
signalIndexPatterns: mockEcsIndexPattern,
|
||||
})
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
it('returns false if no large value list entries exist and all are ECS compliant', () => {
|
||||
expect(
|
||||
shouldDisableBulkClose({
|
||||
items: [getExceptionListItemSchemaMock(), getExceptionListItemSchemaMock()],
|
||||
signalIndexPatterns: mockEcsIndexPattern,
|
||||
})
|
||||
).toBeFalsy();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -58,3 +58,20 @@ export const entryHasNonEcsType = (
|
|||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Determines whether the bulk close alerts option should be disabled.
|
||||
*/
|
||||
export const shouldDisableBulkClose = ({
|
||||
items,
|
||||
signalIndexPatterns,
|
||||
}: {
|
||||
items: ExceptionsBuilderReturnExceptionItem[];
|
||||
signalIndexPatterns: DataViewBase;
|
||||
}): boolean => {
|
||||
return (
|
||||
entryHasListType(items) ||
|
||||
entryHasNonEcsType(items, signalIndexPatterns) ||
|
||||
items.every((item) => item.entries.length === 0)
|
||||
);
|
||||
};
|
||||
|
|
|
@ -0,0 +1,158 @@
|
|||
/*
|
||||
* 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 React from 'react';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types';
|
||||
|
||||
import { TestProviders } from '../../../../../common/mock';
|
||||
import { ExceptionFlyoutFooter } from '.';
|
||||
import * as i18n from './translations';
|
||||
|
||||
describe('Exception flyout footer', () => {
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should render button disabled if "isSubmitButtonDisabled" is "true"', () => {
|
||||
render(
|
||||
<TestProviders>
|
||||
<ExceptionFlyoutFooter
|
||||
listType={ExceptionListTypeEnum.ENDPOINT}
|
||||
isSubmitButtonDisabled
|
||||
cancelButtonDataTestSubjId={'cancelExceptionAddButton'}
|
||||
submitButtonDataTestSubjId={'addExceptionConfirmButton'}
|
||||
handleOnSubmit={jest.fn()}
|
||||
handleCloseFlyout={jest.fn()}
|
||||
/>
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(screen.getByTestId('addExceptionConfirmButton')).toBeDisabled();
|
||||
});
|
||||
|
||||
describe('"isEdit" is "false"', () => {
|
||||
it('should render proper text when endpoint exception', () => {
|
||||
render(
|
||||
<TestProviders>
|
||||
<ExceptionFlyoutFooter
|
||||
listType={ExceptionListTypeEnum.ENDPOINT}
|
||||
isSubmitButtonDisabled={false}
|
||||
cancelButtonDataTestSubjId={'cancelExceptionAddButton'}
|
||||
submitButtonDataTestSubjId={'addExceptionConfirmButton'}
|
||||
handleOnSubmit={jest.fn()}
|
||||
handleCloseFlyout={jest.fn()}
|
||||
/>
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(screen.getByTestId('addExceptionConfirmButton')).toHaveTextContent(
|
||||
i18n.ADD_ENDPOINT_EXCEPTION
|
||||
);
|
||||
});
|
||||
|
||||
it('should render proper text when list type of "rule_default"', () => {
|
||||
render(
|
||||
<TestProviders>
|
||||
<ExceptionFlyoutFooter
|
||||
listType={ExceptionListTypeEnum.RULE_DEFAULT}
|
||||
isSubmitButtonDisabled={false}
|
||||
cancelButtonDataTestSubjId={'cancelExceptionAddButton'}
|
||||
submitButtonDataTestSubjId={'addExceptionConfirmButton'}
|
||||
handleOnSubmit={jest.fn()}
|
||||
handleCloseFlyout={jest.fn()}
|
||||
/>
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(screen.getByTestId('addExceptionConfirmButton')).toHaveTextContent(
|
||||
i18n.CREATE_RULE_EXCEPTION
|
||||
);
|
||||
});
|
||||
|
||||
it('should render proper text when list type of "detection"', () => {
|
||||
render(
|
||||
<TestProviders>
|
||||
<ExceptionFlyoutFooter
|
||||
listType={ExceptionListTypeEnum.DETECTION}
|
||||
isSubmitButtonDisabled={false}
|
||||
cancelButtonDataTestSubjId={'cancelExceptionAddButton'}
|
||||
submitButtonDataTestSubjId={'addExceptionConfirmButton'}
|
||||
handleOnSubmit={jest.fn()}
|
||||
handleCloseFlyout={jest.fn()}
|
||||
/>
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(screen.getByTestId('addExceptionConfirmButton')).toHaveTextContent(
|
||||
i18n.CREATE_RULE_EXCEPTION
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('"isEdit" is "true"', () => {
|
||||
it('should render proper text when endpoint exception', () => {
|
||||
render(
|
||||
<TestProviders>
|
||||
<ExceptionFlyoutFooter
|
||||
isEdit
|
||||
listType={ExceptionListTypeEnum.ENDPOINT}
|
||||
isSubmitButtonDisabled={false}
|
||||
cancelButtonDataTestSubjId={'cancelExceptionAddButton'}
|
||||
submitButtonDataTestSubjId={'addExceptionConfirmButton'}
|
||||
handleOnSubmit={jest.fn()}
|
||||
handleCloseFlyout={jest.fn()}
|
||||
/>
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(screen.getByTestId('addExceptionConfirmButton')).toHaveTextContent(
|
||||
i18n.EDIT_ENDPOINT_EXCEPTION_TITLE
|
||||
);
|
||||
});
|
||||
|
||||
it('should render proper text when list type of "rule_default"', () => {
|
||||
render(
|
||||
<TestProviders>
|
||||
<ExceptionFlyoutFooter
|
||||
isEdit
|
||||
listType={ExceptionListTypeEnum.RULE_DEFAULT}
|
||||
isSubmitButtonDisabled={false}
|
||||
cancelButtonDataTestSubjId={'cancelExceptionAddButton'}
|
||||
submitButtonDataTestSubjId={'addExceptionConfirmButton'}
|
||||
handleOnSubmit={jest.fn()}
|
||||
handleCloseFlyout={jest.fn()}
|
||||
/>
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(screen.getByTestId('addExceptionConfirmButton')).toHaveTextContent(
|
||||
i18n.EDIT_EXCEPTION_TITLE
|
||||
);
|
||||
});
|
||||
|
||||
it('should render proper text when list type of "detection"', () => {
|
||||
render(
|
||||
<TestProviders>
|
||||
<ExceptionFlyoutFooter
|
||||
isEdit
|
||||
listType={ExceptionListTypeEnum.DETECTION}
|
||||
isSubmitButtonDisabled={false}
|
||||
cancelButtonDataTestSubjId={'cancelExceptionAddButton'}
|
||||
submitButtonDataTestSubjId={'addExceptionConfirmButton'}
|
||||
handleOnSubmit={jest.fn()}
|
||||
handleCloseFlyout={jest.fn()}
|
||||
/>
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(screen.getByTestId('addExceptionConfirmButton')).toHaveTextContent(
|
||||
i18n.EDIT_EXCEPTION_TITLE
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* 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 React, { memo, useMemo } from 'react';
|
||||
import styled, { css } from 'styled-components';
|
||||
|
||||
import { EuiFlyoutFooter, EuiButton, EuiButtonEmpty, EuiFlexGroup } from '@elastic/eui';
|
||||
import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types';
|
||||
import * as i18n from './translations';
|
||||
|
||||
const FlyoutFooterGroup = styled(EuiFlexGroup)`
|
||||
${({ theme }) => css`
|
||||
padding: ${theme.eui.euiSizeS};
|
||||
`}
|
||||
`;
|
||||
|
||||
export interface ExceptionFlyoutFooterProps {
|
||||
isEdit?: boolean;
|
||||
listType: ExceptionListTypeEnum;
|
||||
isSubmitButtonDisabled: boolean;
|
||||
cancelButtonDataTestSubjId: string;
|
||||
submitButtonDataTestSubjId: string;
|
||||
handleOnSubmit: () => Promise<void> | undefined;
|
||||
handleCloseFlyout: () => void;
|
||||
}
|
||||
|
||||
export const ExceptionFlyoutFooter = memo(function ExceptionFlyoutFooter({
|
||||
isEdit = false,
|
||||
listType,
|
||||
isSubmitButtonDisabled,
|
||||
cancelButtonDataTestSubjId,
|
||||
submitButtonDataTestSubjId,
|
||||
handleOnSubmit,
|
||||
handleCloseFlyout,
|
||||
}: ExceptionFlyoutFooterProps) {
|
||||
const addButtonMessage = useMemo(() => {
|
||||
return listType === ExceptionListTypeEnum.ENDPOINT
|
||||
? i18n.ADD_ENDPOINT_EXCEPTION
|
||||
: i18n.CREATE_RULE_EXCEPTION;
|
||||
}, [listType]);
|
||||
|
||||
const editButtonMessage = useMemo(() => {
|
||||
return listType === ExceptionListTypeEnum.ENDPOINT
|
||||
? i18n.EDIT_ENDPOINT_EXCEPTION_TITLE
|
||||
: i18n.EDIT_EXCEPTION_TITLE;
|
||||
}, [listType]);
|
||||
|
||||
const submitButtonMessage = isEdit ? editButtonMessage : addButtonMessage;
|
||||
|
||||
return (
|
||||
<EuiFlyoutFooter>
|
||||
<FlyoutFooterGroup justifyContent="spaceBetween">
|
||||
<EuiButtonEmpty data-test-subj={cancelButtonDataTestSubjId} onClick={handleCloseFlyout}>
|
||||
{i18n.CANCEL}
|
||||
</EuiButtonEmpty>
|
||||
|
||||
<EuiButton
|
||||
data-test-subj={submitButtonDataTestSubjId}
|
||||
onClick={handleOnSubmit}
|
||||
isDisabled={isSubmitButtonDisabled}
|
||||
fill
|
||||
>
|
||||
{submitButtonMessage}
|
||||
</EuiButton>
|
||||
</FlyoutFooterGroup>
|
||||
</EuiFlyoutFooter>
|
||||
);
|
||||
});
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* 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';
|
||||
|
||||
export const CREATE_RULE_EXCEPTION = i18n.translate(
|
||||
'xpack.securitySolution.ruleExceptions.flyoutComponents.footer.addException',
|
||||
{
|
||||
defaultMessage: 'Add rule exception',
|
||||
}
|
||||
);
|
||||
|
||||
export const ADD_ENDPOINT_EXCEPTION = i18n.translate(
|
||||
'xpack.securitySolution.ruleExceptions.flyoutComponents.footer.addEndpointException',
|
||||
{
|
||||
defaultMessage: 'Add Endpoint Exception',
|
||||
}
|
||||
);
|
||||
|
||||
export const EDIT_ENDPOINT_EXCEPTION_TITLE = i18n.translate(
|
||||
'xpack.securitySolution.ruleExceptions..flyoutComponents.footer.editEndpointExceptionTitle',
|
||||
{
|
||||
defaultMessage: 'Edit endpoint exception',
|
||||
}
|
||||
);
|
||||
|
||||
export const EDIT_EXCEPTION_TITLE = i18n.translate(
|
||||
'xpack.securitySolution.ruleExceptions.flyoutComponents.footer.editExceptionTitle',
|
||||
{
|
||||
defaultMessage: 'Edit rule exception',
|
||||
}
|
||||
);
|
||||
|
||||
export const CANCEL = i18n.translate(
|
||||
'xpack.securitySolution.ruleExceptions.flyoutComponents.footer.cancel',
|
||||
{
|
||||
defaultMessage: 'Cancel',
|
||||
}
|
||||
);
|
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
* 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 React from 'react';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types';
|
||||
|
||||
import { TestProviders } from '../../../../../common/mock';
|
||||
import { ExceptionFlyoutHeader } from '.';
|
||||
import * as i18n from './translations';
|
||||
|
||||
describe('Exception flyout header', () => {
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('"isEdit" is "false"', () => {
|
||||
it('should render proper text when endpoint exception', () => {
|
||||
render(
|
||||
<TestProviders>
|
||||
<ExceptionFlyoutHeader
|
||||
listType={ExceptionListTypeEnum.ENDPOINT}
|
||||
titleId={'someId'}
|
||||
dataTestSubjId={'addExceptionConfirmButton'}
|
||||
/>
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(screen.getByTestId('addExceptionConfirmButton')).toHaveTextContent(
|
||||
i18n.ADD_ENDPOINT_EXCEPTION
|
||||
);
|
||||
});
|
||||
|
||||
it('should render proper text when list type of "rule_default"', () => {
|
||||
render(
|
||||
<TestProviders>
|
||||
<ExceptionFlyoutHeader
|
||||
listType={ExceptionListTypeEnum.RULE_DEFAULT}
|
||||
titleId={'someId'}
|
||||
dataTestSubjId={'addExceptionConfirmButton'}
|
||||
/>
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(screen.getByTestId('addExceptionConfirmButton')).toHaveTextContent(
|
||||
i18n.CREATE_RULE_EXCEPTION
|
||||
);
|
||||
});
|
||||
|
||||
it('should render proper text when list type of "detection"', () => {
|
||||
render(
|
||||
<TestProviders>
|
||||
<ExceptionFlyoutHeader
|
||||
listType={ExceptionListTypeEnum.DETECTION}
|
||||
titleId={'someId'}
|
||||
dataTestSubjId={'addExceptionConfirmButton'}
|
||||
/>
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(screen.getByTestId('addExceptionConfirmButton')).toHaveTextContent(
|
||||
i18n.CREATE_RULE_EXCEPTION
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('"isEdit" is "true"', () => {
|
||||
it('should render proper text when endpoint exception', () => {
|
||||
render(
|
||||
<TestProviders>
|
||||
<ExceptionFlyoutHeader
|
||||
isEdit
|
||||
listType={ExceptionListTypeEnum.ENDPOINT}
|
||||
titleId={'someId'}
|
||||
dataTestSubjId={'addExceptionConfirmButton'}
|
||||
/>
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(screen.getByTestId('addExceptionConfirmButton')).toHaveTextContent(
|
||||
i18n.EDIT_ENDPOINT_EXCEPTION_TITLE
|
||||
);
|
||||
});
|
||||
|
||||
it('should render proper text when list type of "rule_default"', () => {
|
||||
render(
|
||||
<TestProviders>
|
||||
<ExceptionFlyoutHeader
|
||||
isEdit
|
||||
listType={ExceptionListTypeEnum.RULE_DEFAULT}
|
||||
titleId={'someId'}
|
||||
dataTestSubjId={'addExceptionConfirmButton'}
|
||||
/>
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(screen.getByTestId('addExceptionConfirmButton')).toHaveTextContent(
|
||||
i18n.EDIT_EXCEPTION_TITLE
|
||||
);
|
||||
});
|
||||
|
||||
it('should render proper text when list type of "detection"', () => {
|
||||
render(
|
||||
<TestProviders>
|
||||
<ExceptionFlyoutHeader
|
||||
isEdit
|
||||
listType={ExceptionListTypeEnum.DETECTION}
|
||||
titleId={'someId'}
|
||||
dataTestSubjId={'addExceptionConfirmButton'}
|
||||
/>
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(screen.getByTestId('addExceptionConfirmButton')).toHaveTextContent(
|
||||
i18n.EDIT_EXCEPTION_TITLE
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* 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 React, { memo, useMemo } from 'react';
|
||||
import styled, { css } from 'styled-components';
|
||||
|
||||
import { EuiTitle, EuiSpacer, EuiFlyoutHeader } from '@elastic/eui';
|
||||
import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types';
|
||||
import * as i18n from './translations';
|
||||
|
||||
const FlyoutHeader = styled(EuiFlyoutHeader)`
|
||||
${({ theme }) => css`
|
||||
border-bottom: 1px solid ${theme.eui.euiColorLightShade};
|
||||
`}
|
||||
`;
|
||||
|
||||
export interface ExceptionFlyoutHeaderProps {
|
||||
isEdit?: boolean;
|
||||
listType: ExceptionListTypeEnum;
|
||||
titleId: string;
|
||||
dataTestSubjId: string;
|
||||
}
|
||||
|
||||
export const ExceptionFlyoutHeader = memo(function ExceptionFlyoutHeader({
|
||||
isEdit = false,
|
||||
listType,
|
||||
titleId,
|
||||
dataTestSubjId,
|
||||
}: ExceptionFlyoutHeaderProps) {
|
||||
const addTitle = useMemo(() => {
|
||||
return listType === ExceptionListTypeEnum.ENDPOINT
|
||||
? i18n.ADD_ENDPOINT_EXCEPTION
|
||||
: i18n.CREATE_RULE_EXCEPTION;
|
||||
}, [listType]);
|
||||
|
||||
const editTitle = useMemo(() => {
|
||||
return listType === ExceptionListTypeEnum.ENDPOINT
|
||||
? i18n.EDIT_ENDPOINT_EXCEPTION_TITLE
|
||||
: i18n.EDIT_EXCEPTION_TITLE;
|
||||
}, [listType]);
|
||||
|
||||
const title = isEdit ? editTitle : addTitle;
|
||||
|
||||
return (
|
||||
<FlyoutHeader>
|
||||
<EuiTitle>
|
||||
<h2 id={titleId} data-test-subj={dataTestSubjId}>
|
||||
{title}
|
||||
</h2>
|
||||
</EuiTitle>
|
||||
<EuiSpacer size="m" />
|
||||
</FlyoutHeader>
|
||||
);
|
||||
});
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* 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';
|
||||
|
||||
export const CREATE_RULE_EXCEPTION = i18n.translate(
|
||||
'xpack.securitySolution.ruleExceptions.flyoutComponents.footer.addException',
|
||||
{
|
||||
defaultMessage: 'Add rule exception',
|
||||
}
|
||||
);
|
||||
|
||||
export const ADD_ENDPOINT_EXCEPTION = i18n.translate(
|
||||
'xpack.securitySolution.ruleExceptions.flyoutComponents.footer.addEndpointException',
|
||||
{
|
||||
defaultMessage: 'Add Endpoint Exception',
|
||||
}
|
||||
);
|
||||
|
||||
export const EDIT_ENDPOINT_EXCEPTION_TITLE = i18n.translate(
|
||||
'xpack.securitySolution.ruleExceptions..flyoutComponents.footer.editEndpointExceptionTitle',
|
||||
{
|
||||
defaultMessage: 'Edit endpoint exception',
|
||||
}
|
||||
);
|
||||
|
||||
export const EDIT_EXCEPTION_TITLE = i18n.translate(
|
||||
'xpack.securitySolution.ruleExceptions.flyoutComponents.footer.editExceptionTitle',
|
||||
{
|
||||
defaultMessage: 'Edit rule exception',
|
||||
}
|
||||
);
|
||||
|
||||
export const CANCEL = i18n.translate(
|
||||
'xpack.securitySolution.ruleExceptions.flyoutComponents.footer.cancel',
|
||||
{
|
||||
defaultMessage: 'Cancel',
|
||||
}
|
||||
);
|
|
@ -30,8 +30,13 @@ jest.mock('react-router-dom', () => {
|
|||
};
|
||||
});
|
||||
jest.mock('@kbn/i18n-react', () => {
|
||||
const { i18n } = jest.requireActual('@kbn/i18n');
|
||||
i18n.init({ locale: 'en' });
|
||||
|
||||
const originalModule = jest.requireActual('@kbn/i18n-react');
|
||||
const FormattedRelative = jest.fn().mockImplementation(() => '20 hours ago');
|
||||
const FormattedRelative = jest.fn();
|
||||
FormattedRelative.mockImplementationOnce(() => '2 days ago');
|
||||
FormattedRelative.mockImplementation(() => '20 hours ago');
|
||||
|
||||
return {
|
||||
...originalModule,
|
||||
|
@ -43,10 +48,7 @@ jest.mock('../../../detections/containers/detection_engine/lists/use_lists_confi
|
|||
useListsConfig: jest.fn().mockReturnValue({ loading: false }),
|
||||
}));
|
||||
|
||||
// FLAKY: https://github.com/elastic/kibana/issues/177670
|
||||
// FLAKY: https://github.com/elastic/kibana/issues/177671
|
||||
// FLAKY: https://github.com/elastic/kibana/issues/177672
|
||||
describe.skip('SharedLists', () => {
|
||||
describe('SharedLists', () => {
|
||||
const mockHistory = generateHistoryMock();
|
||||
const exceptionList1 = getExceptionListSchemaMock();
|
||||
const exceptionList2 = { ...getExceptionListSchemaMock(), list_id: 'not_endpoint_list', id: '2' };
|
||||
|
|
|
@ -37507,9 +37507,6 @@
|
|||
"xpack.securitySolution.ruleExceptions.allExceptionItems.noSearchResultsPromptTitle": "Aucun résultat ne correspond à vos critères de recherche.",
|
||||
"xpack.securitySolution.ruleExceptions.allExceptionItems.paginationAriaLabel": "Pagination du tableau d'éléments d'exception",
|
||||
"xpack.securitySolution.ruleExceptions.allExceptionItems.searchPlaceholder": "Exceptions de filtre utilisant une syntaxe de requête simple, par exemple, le nom :\"ma liste\"",
|
||||
"xpack.securitySolution.ruleExceptions.editException.cancel": "Annuler",
|
||||
"xpack.securitySolution.ruleExceptions.editException.editEndpointExceptionTitle": "Modifier une exception de point de terminaison",
|
||||
"xpack.securitySolution.ruleExceptions.editException.editExceptionTitle": "Modifier une exception à une règle",
|
||||
"xpack.securitySolution.ruleExceptions.editException.editRuleExceptionToastErrorTitle": "Erreur lors de la mise à jour de l'exception",
|
||||
"xpack.securitySolution.ruleExceptions.editException.editRuleExceptionToastSuccessText": "{numItems, plural, =1 {L'exception} other {Les exceptions}} - {exceptionItemName} - {numItems, plural, =1 {a été mise à jour} other {ont été mises à jour}}.",
|
||||
"xpack.securitySolution.ruleExceptions.editException.editRuleExceptionToastSuccessTitle": "Exception à la règle mise à jour",
|
||||
|
|
|
@ -37376,9 +37376,6 @@
|
|||
"xpack.securitySolution.ruleExceptions.allExceptionItems.noSearchResultsPromptTitle": "検索条件と一致する結果がありません。",
|
||||
"xpack.securitySolution.ruleExceptions.allExceptionItems.paginationAriaLabel": "例外アイテムの表のページ制御",
|
||||
"xpack.securitySolution.ruleExceptions.allExceptionItems.searchPlaceholder": "シンプルなクエリ構文(name:\"my list\"など)を使用して例外をフィルタリング",
|
||||
"xpack.securitySolution.ruleExceptions.editException.cancel": "キャンセル",
|
||||
"xpack.securitySolution.ruleExceptions.editException.editEndpointExceptionTitle": "エンドポイント例外の編集",
|
||||
"xpack.securitySolution.ruleExceptions.editException.editExceptionTitle": "ルール例外を編集",
|
||||
"xpack.securitySolution.ruleExceptions.editException.editRuleExceptionToastErrorTitle": "例外の更新エラー",
|
||||
"xpack.securitySolution.ruleExceptions.editException.editRuleExceptionToastSuccessText": "{numItems, plural, other {例外}} - {exceptionItemName} - {numItems, plural, other {が}}更新されました。",
|
||||
"xpack.securitySolution.ruleExceptions.editException.editRuleExceptionToastSuccessTitle": "ルール例外が更新されました",
|
||||
|
|
|
@ -37547,9 +37547,6 @@
|
|||
"xpack.securitySolution.ruleExceptions.allExceptionItems.noSearchResultsPromptTitle": "没有任何结果匹配您的搜索条件",
|
||||
"xpack.securitySolution.ruleExceptions.allExceptionItems.paginationAriaLabel": "例外项表分页",
|
||||
"xpack.securitySolution.ruleExceptions.allExceptionItems.searchPlaceholder": "使用简单的查询语法筛选例外,例如 name:\"my list\"",
|
||||
"xpack.securitySolution.ruleExceptions.editException.cancel": "取消",
|
||||
"xpack.securitySolution.ruleExceptions.editException.editEndpointExceptionTitle": "编辑终端例外",
|
||||
"xpack.securitySolution.ruleExceptions.editException.editExceptionTitle": "编辑规则例外",
|
||||
"xpack.securitySolution.ruleExceptions.editException.editRuleExceptionToastErrorTitle": "更新例外时出错",
|
||||
"xpack.securitySolution.ruleExceptions.editException.editRuleExceptionToastSuccessText": "{numItems, plural, other {例外}} - {exceptionItemName} - {numItems, plural, other {已}}更新。",
|
||||
"xpack.securitySolution.ruleExceptions.editException.editRuleExceptionToastSuccessTitle": "已更新规则例外",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue