From 3b4a9e669cca96d4b17c8548d49a7d90fc3de263 Mon Sep 17 00:00:00 2001 From: Yara Tercero Date: Mon, 27 Jun 2022 12:37:23 -0700 Subject: [PATCH 01/16] added unit tests --- .../exception_item_card_conditions.test.tsx | 60 +++++++ .../exception_item_card_conditions.tsx | 139 +++++++++++++++ .../exception_item_card_header.test.tsx | 121 +++++++++++++ .../exception_item_card_header.tsx | 73 ++++++++ .../exception_item_card_meta.test.tsx | 51 ++++++ .../exception_item_card_meta.tsx | 110 ++++++++++++ .../viewer/exception_item_2/index.test.tsx | 161 ++++++++++++++++++ .../viewer/exception_item_2/index.tsx | 119 +++++++++++++ .../viewer/exception_item_2/translations.ts | 105 ++++++++++++ .../viewer/exceptions_viewer_items.tsx | 14 +- 10 files changed, 942 insertions(+), 11 deletions(-) create mode 100644 x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_2/exception_item_card_conditions.test.tsx create mode 100644 x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_2/exception_item_card_conditions.tsx create mode 100644 x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_2/exception_item_card_header.test.tsx create mode 100644 x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_2/exception_item_card_header.tsx create mode 100644 x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_2/exception_item_card_meta.test.tsx create mode 100644 x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_2/exception_item_card_meta.tsx create mode 100644 x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_2/index.test.tsx create mode 100644 x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_2/index.tsx create mode 100644 x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_2/translations.ts diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_2/exception_item_card_conditions.test.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_2/exception_item_card_conditions.test.tsx new file mode 100644 index 0000000000000..91c73636654b0 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_2/exception_item_card_conditions.test.tsx @@ -0,0 +1,60 @@ +/* + * 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 { mount } from 'enzyme'; + +import { TestProviders } from '../../../../mock'; +import { ExceptionItemCardConditions } from './exception_item_card_conditions'; + +describe('ExceptionItemCardConditions', () => { + it('it renders item conditions', () => { + const wrapper = mount( + + + + ); + + // Text is gonna look a bit off unformatted + expect( + wrapper.find('[data-test-subj="exceptionItemConditions-condition"]').at(0).text() + ).toEqual('WHEN host.nameIS host'); + expect( + wrapper.find('[data-test-subj="exceptionItemConditions-condition"]').at(1).text() + ).toEqual('AND threat.indicator.portexists '); + expect( + wrapper.find('[data-test-subj="exceptionItemConditions-condition"]').at(2).text() + ).toEqual('AND file.Ext.code_signature validIS true'); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_2/exception_item_card_conditions.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_2/exception_item_card_conditions.tsx new file mode 100644 index 0000000000000..7f5376e3a856b --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_2/exception_item_card_conditions.tsx @@ -0,0 +1,139 @@ +/* + * 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, useCallback } from 'react'; +import { + EuiExpression, + EuiToken, + EuiFlexGroup, + EuiFlexItem, + EuiBadge, +} from '@elastic/eui'; +import styled from 'styled-components'; +import { ExceptionListItemSchema, ListOperatorTypeEnum, NonEmptyNestedEntriesArray } from '@kbn/securitysolution-io-ts-list-types'; + +import * as i18n from './translations'; + +const OPERATOR_TYPE_LABELS_INCLUDED = Object.freeze({ + [ListOperatorTypeEnum.NESTED]: i18n.CONDITION_OPERATOR_TYPE_NESTED, + [ListOperatorTypeEnum.MATCH_ANY]: i18n.CONDITION_OPERATOR_TYPE_MATCH_ANY, + [ListOperatorTypeEnum.MATCH]: i18n.CONDITION_OPERATOR_TYPE_MATCH, + [ListOperatorTypeEnum.WILDCARD]: i18n.CONDITION_OPERATOR_TYPE_WILDCARD_MATCHES, + [ListOperatorTypeEnum.EXISTS]: i18n.CONDITION_OPERATOR_TYPE_EXISTS, + [ListOperatorTypeEnum.LIST]: i18n.CONDITION_OPERATOR_TYPE_LIST, +}); + +const OPERATOR_TYPE_LABELS_EXCLUDED = Object.freeze({ + [ListOperatorTypeEnum.MATCH_ANY]: i18n.CONDITION_OPERATOR_TYPE_NOT_MATCH_ANY, + [ListOperatorTypeEnum.MATCH]: i18n.CONDITION_OPERATOR_TYPE_NOT_MATCH, +}); + +const EuiFlexGroupNested = styled(EuiFlexGroup)` + margin-left: ${({ theme }) => theme.eui.euiSizeXL}; +`; + +const EuiFlexItemNested = styled(EuiFlexItem)` + margin-bottom: 6px !important; + margin-top: 6px !important; +`; + +export interface CriteriaConditionsProps { + entries: ExceptionListItemSchema['entries']; + dataTestSubj: string; +} + +export const ExceptionItemCardConditions = memo( + ({ entries, dataTestSubj }) => { + const getEntryValue = (type: string, value: string | string[] | undefined) => { + if (type === 'match_any' && Array.isArray(value)) { + return value.map((currentValue) => {currentValue}); + } + return value ?? ''; + }; + + const getEntryOperator = (type: string, operator: string) => { + if (type === 'nested') return ''; + return operator === 'included' + ? OPERATOR_TYPE_LABELS_INCLUDED[type as keyof typeof OPERATOR_TYPE_LABELS_INCLUDED] ?? type + : OPERATOR_TYPE_LABELS_EXCLUDED[type as keyof typeof OPERATOR_TYPE_LABELS_EXCLUDED] ?? type; + }; + + const getNestedEntriesContent = useCallback( + (type: string, nestedEntries: NonEmptyNestedEntriesArray) => { + if (type === 'nested' && nestedEntries.length) { + return nestedEntries.map( + (entry) => { + const { + field: nestedField, + type: nestedType, + operator: nestedOperator, + } = entry; + const nestedValue = "value" in entry ? entry.value : ''; + + return ( + + + + + + + + + + + + ); + } + ); + } + }, + [] + ); + + return ( +
+ {entries.map((entry, index) => { + const { + field, + type + } = entry; + const value = "value" in entry ? entry.value : ''; + const nestedEntries = "entries" in entry ? entry.entries : []; + const operator = "operator" in entry ? entry.operator : ''; + + return ( +
+
+ + +
+ {nestedEntries != null && getNestedEntriesContent(type, nestedEntries)} +
+ ); + })} +
+ ); + } +); +ExceptionItemCardConditions.displayName = 'ExceptionItemCardConditions'; diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_2/exception_item_card_header.test.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_2/exception_item_card_header.test.tsx new file mode 100644 index 0000000000000..a5048987a8b6d --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_2/exception_item_card_header.test.tsx @@ -0,0 +1,121 @@ +/* + * 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 { mount } from 'enzyme'; +import { getExceptionListItemSchemaMock } from '@kbn/lists-plugin/common/schemas/response/exception_list_item_schema.mock'; +import { ThemeProvider } from 'styled-components'; + +import * as i18n from './translations'; +import { ExceptionItemCardHeader } from './exception_item_card_header'; +import { getMockTheme } from '../../../../lib/kibana/kibana_react.mock'; + +const mockTheme = getMockTheme({ + eui: { + euiSize: '10px', + euiColorPrimary: '#ece', + euiColorDanger: '#ece', + }, +}); + +describe('ExceptionItemCardHeader', () => { + it('it renders item name', () => { + const wrapper = mount( + + + + ); + + expect( + wrapper.find('[data-test-subj="exceptionItemHeader-title"]').at(0).text() + ).toEqual('some name'); + }); + + it('it displays actions', () => { + const handleEdit = jest.fn(); + const handleDelete = jest.fn(); + const wrapper = mount( + + + + ); + + // click on popover + wrapper.find('button[data-test-subj="exceptionItemHeader-actionButton"]').at(0).simulate('click'); + expect(wrapper.find('button[data-test-subj="exceptionItemHeader-actionItem"]')).toHaveLength(2); + + wrapper.find('button[data-test-subj="exceptionItemHeader-actionItem"]').at(0).simulate('click'); + expect(handleEdit).toHaveBeenCalled(); + + wrapper.find('button[data-test-subj="exceptionItemHeader-actionItem"]').at(1).simulate('click'); + expect(handleDelete).toHaveBeenCalled(); + }); + + it('it disables actions if disableActions is true', () => { + const handleEdit = jest.fn(); + const handleDelete = jest.fn(); + const wrapper = mount( + + + + ); + + expect(wrapper.find('button[data-test-subj="exceptionItemHeader-actionButton"]').at(0).props().disabled).toBeTruthy(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_2/exception_item_card_header.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_2/exception_item_card_header.tsx new file mode 100644 index 0000000000000..7a93a0b9258ca --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_2/exception_item_card_header.tsx @@ -0,0 +1,73 @@ +/* + * 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, useState } from 'react'; +import { EuiButtonIcon, EuiContextMenuPanelProps, EuiContextMenuPanel, EuiFlexGroup, EuiFlexItem, EuiPopover, EuiTitle, EuiContextMenuItem } from '@elastic/eui'; +import { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; + +export interface ExceptionItemCardHeaderProps { + item: ExceptionListItemSchema; + actions: Array<{ key: string; icon: string; label: string; onClick: () => void; }>; + disableActions?: boolean; + dataTestSubj: string; +} + +export const ExceptionItemCardHeader = memo( + ({ item, actions, disableActions = false, dataTestSubj }) => { + const [isPopoverOpen, setIsPopoverOpen] = useState(false); + + const onItemActionsClick = () => setIsPopoverOpen((isPopoverOpen) => !isPopoverOpen); + const onClosePopover = () => setIsPopoverOpen(false); + + const itemActions = useMemo((): EuiContextMenuPanelProps['items'] => { + return actions.map((action) => ( + { + onClosePopover(); + action.onClick(); + }} + > + {action.label} + + )) + }, []); + + return ( + + + +

{item.name}

+
+
+ + + )} + panelPaddingSize="none" + isOpen={isPopoverOpen} + closePopover={onClosePopover} + data-test-subj={`${dataTestSubj}-items`} + > + + + +
+ ); + } +); + +ExceptionItemCardHeader.displayName = 'ExceptionItemCardHeader'; diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_2/exception_item_card_meta.test.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_2/exception_item_card_meta.test.tsx new file mode 100644 index 0000000000000..5080e1074b7dd --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_2/exception_item_card_meta.test.tsx @@ -0,0 +1,51 @@ +/* + * 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 { mount } from 'enzyme'; +import { getExceptionListItemSchemaMock } from '@kbn/lists-plugin/common/schemas/response/exception_list_item_schema.mock'; + +import { TestProviders } from '../../../../mock'; +import { ExceptionItemCardMetaInfo } from './exception_item_card_meta'; + +describe('ExceptionItemCardMetaInfo', () => { + it('it renders item creation info', () => { + const wrapper = mount( + + + + ); + + expect( + wrapper.find('[data-test-subj="exceptionItemMeta-createdBy-value1"]').at(0).text() + ).toEqual('04/20/20'); + expect( + wrapper.find('[data-test-subj="exceptionItemMeta-createdBy-value2"]').at(0).text() + ).toEqual('some user'); + }); + + it('it renders item update info', () => { + const wrapper = mount( + + + + ); + + expect( + wrapper.find('[data-test-subj="exceptionItemMeta-updatedBy-value1"]').at(0).text() + ).toEqual('Apr 20, 2020 @ 15:25:31.830'); + expect( + wrapper.find('[data-test-subj="exceptionItemMeta-updatedBy-value2"]').at(0).text() + ).toEqual('some user'); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_2/exception_item_card_meta.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_2/exception_item_card_meta.tsx new file mode 100644 index 0000000000000..1153f49ae6088 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_2/exception_item_card_meta.tsx @@ -0,0 +1,110 @@ +/* + * 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 } from 'react'; +import { EuiAvatar, EuiBetaBadge, EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; +import { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; + +import * as i18n from './translations'; +import { FormattedDate, FormattedRelativePreferenceDate } from '../../../formatted_date'; + +export interface ExceptionItemCardMetaInfoProps { + item: ExceptionListItemSchema; + dataTestSubj: string; +} + +export const ExceptionItemCardMetaInfo = memo( + ({ item, dataTestSubj }) => { + + return ( + + + + )} + value2={item.created_by} + dataTestSubj={`${dataTestSubj}-createdBy`} + /> + + + + )} + value2={item.updated_by} + dataTestSubj={`${dataTestSubj}-updatedBy`} + /> + + + ); + } +); +ExceptionItemCardMetaInfo.displayName = 'ExceptionItemCardMetaInfo'; + +interface MetaInfoDetailsProps { + fieldName: string; + label: string; + value1: JSX.Element | string; + value2: string; + dataTestSubj: string; +} + +const MetaInfoDetails = memo(({ label, value1, value2, dataTestSubj }) => { + return ( + + + + + + {value1} + + + {i18n.EXCEPTION_ITEM_META_BY} + + + + + + + + {value2} + + + + + ); +}); + +MetaInfoDetails.displayName = 'MetaInfoDetails'; diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_2/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_2/index.test.tsx new file mode 100644 index 0000000000000..898f17a36604f --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_2/index.test.tsx @@ -0,0 +1,161 @@ +/* + * 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 { ThemeProvider } from 'styled-components'; +import { mount } from 'enzyme'; + +import { ExceptionItemCard } from '.'; +import { getExceptionListItemSchemaMock } from '@kbn/lists-plugin/common/schemas/response/exception_list_item_schema.mock'; +import { getCommentsArrayMock } from '@kbn/lists-plugin/common/schemas/types/comment.mock'; +import { getMockTheme } from '../../../../lib/kibana/kibana_react.mock'; + +jest.mock('../../../../lib/kibana'); + +const mockTheme = getMockTheme({ + eui: { + euiColorDanger: '#ece', + euiColorLightestShade: '#ece', + euiColorPrimary: '#ece', + euiFontWeightSemiBold: 1, + }, +}); + +describe('ExceptionItemCard', () => { + it('it renders header, item meta information and conditions', () => { + const exceptionItem = {...getExceptionListItemSchemaMock(), comments: []}; + + const wrapper = mount( + + + + ); + + expect(wrapper.find('ExceptionItemCardHeader')).toHaveLength(1); + expect(wrapper.find('ExceptionItemCardMetaInfo')).toHaveLength(1); + expect(wrapper.find('ExceptionItemCardConditions')).toHaveLength(1); + expect(wrapper.find('[data-test-subj="exceptionsViewerCommentAccordion"]').exists()).toBeFalsy(); + }); + + it('it renders header, item meta information, conditions, and comments if any exist', () => { + const exceptionItem = { ...getExceptionListItemSchemaMock(), comments: getCommentsArrayMock() }; + + const wrapper = mount( + + + + ); + + expect(wrapper.find('ExceptionItemCardHeader')).toHaveLength(1); + expect(wrapper.find('ExceptionItemCardMetaInfo')).toHaveLength(1); + expect(wrapper.find('ExceptionItemCardConditions')).toHaveLength(1); + expect(wrapper.find('[data-test-subj="exceptionsViewerCommentAccordion"]').exists()).toBeTruthy(); + }); + + it('it does not render edit or delete action buttons when "disableActions" is "true"', () => { + const exceptionItem = getExceptionListItemSchemaMock(); + + const wrapper = mount( + + + + ); + + expect(wrapper.find('button[data-test-subj="item-actionButton"]').exists()).toBeFalsy(); + }); + + it('it invokes "onEditException" when edit button clicked', () => { + const mockOnEditException = jest.fn(); + const exceptionItem = getExceptionListItemSchemaMock(); + + const wrapper = mount( + + + + ); + + // click on popover + wrapper.find('button[data-test-subj="exceptionItemCardHeader-actionButton"]').at(0).simulate('click'); + wrapper.find('button[data-test-subj="exceptionItemCardHeader-actionItem"]').at(0).simulate('click'); + + expect(mockOnEditException).toHaveBeenCalledWith(getExceptionListItemSchemaMock()); + }); + + it('it invokes "onDeleteException" when delete button clicked', () => { + const mockOnDeleteException = jest.fn(); + const exceptionItem = getExceptionListItemSchemaMock(); + + const wrapper = mount( + + + + ); + + // click on popover + wrapper.find('button[data-test-subj="exceptionItemCardHeader-actionButton"]').at(0).simulate('click'); + wrapper.find('button[data-test-subj="exceptionItemCardHeader-actionItem"]').at(0).simulate('click'); + + expect(mockOnDeleteException).toHaveBeenCalledWith({ + id: '1', + namespaceType: 'single', + }); + }); + + it('it renders comment accordion closed to begin with', () => { + const exceptionItem = getExceptionListItemSchemaMock(); + exceptionItem.comments = getCommentsArrayMock(); + const wrapper = mount( + + + + ); + + expect(wrapper.find('.euiAccordion-isOpen')).toHaveLength(0); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_2/index.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_2/index.tsx new file mode 100644 index 0000000000000..972734687eadf --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_2/index.tsx @@ -0,0 +1,119 @@ +/* + * 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 { + EuiPanel, + EuiFlexGroup, + EuiCommentProps, + EuiCommentList, + EuiAccordion, + EuiFlexItem, + EuiText, +} from '@elastic/eui'; +import React, { useMemo, useCallback } from 'react'; +import type { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; + +import { getFormattedComments } from '../../helpers'; +import type { ExceptionListItemIdentifiers } from '../../types'; +import * as i18n from './translations'; +import { ExceptionItemCardHeader } from './exception_item_card_header'; +import { ExceptionItemCardConditions } from './exception_item_card_conditions'; +import { ExceptionItemCardMetaInfo } from './exception_item_card_meta'; + +export interface ExceptionItemProps { + loadingItemIds: ExceptionListItemIdentifiers[]; + exceptionItem: ExceptionListItemSchema; + onDeleteException: (arg: ExceptionListItemIdentifiers) => void; + onEditException: (item: ExceptionListItemSchema) => void; + disableActions: boolean; + dataTestSubj: string; +} + +const ExceptionItemCardComponent = ({ + disableActions, + loadingItemIds, + exceptionItem, + onDeleteException, + onEditException, + dataTestSubj, +}: ExceptionItemProps): JSX.Element => { + const handleDelete = useCallback((): void => { + onDeleteException({ + id: exceptionItem.id, + namespaceType: exceptionItem.namespace_type, + }); + }, [onDeleteException, exceptionItem.id, exceptionItem.namespace_type]); + + const handleEdit = useCallback((): void => { + onEditException(exceptionItem); + }, [onEditException, exceptionItem]); + + const formattedComments = useMemo((): EuiCommentProps[] => { + return getFormattedComments(exceptionItem.comments); + }, [exceptionItem.comments]); + + const disableItemActions = useMemo((): boolean => { + const foundItems = loadingItemIds.filter(({ id }) => id === exceptionItem.id); + return disableActions || foundItems.length > 0; + }, [loadingItemIds, exceptionItem.id, disableActions]); + + return ( + + + + + + + + + + + + {formattedComments.length > 0 && ( + + {i18n.exceptionItemCommentsAccordion(formattedComments.length)}} + data-test-subj="exceptionsViewerCommentAccordion" + > + + + + + + )} + + + ); +}; + +ExceptionItemCardComponent.displayName = 'ExceptionItemCardComponent'; + +export const ExceptionItemCard = React.memo(ExceptionItemCardComponent); + +ExceptionItemCard.displayName = 'ExceptionItemCard'; diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_2/translations.ts b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_2/translations.ts new file mode 100644 index 0000000000000..070e26720238d --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_2/translations.ts @@ -0,0 +1,105 @@ +/* + * 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 EXCEPTION_ITEM_EDIT_BUTTON = i18n.translate('xpack.securitySolution.exceptions.exceptionItem.editItemButton', { + defaultMessage: 'Edit item', +}); + +export const EXCEPTION_ITEM_DELETE_BUTTON = i18n.translate('xpack.securitySolution.exceptions.exceptionItem.deleteItemButton', { + defaultMessage: 'Delete item', +}); + +export const EXCEPTION_ITEM_CREATED_LABEL = i18n.translate('xpack.securitySolution.exceptions.exceptionItem.createdLabel', { + defaultMessage: 'Created', +}); + +export const EXCEPTION_ITEM_UPDATED_LABEL = i18n.translate('xpack.securitySolution.exceptions.exceptionItem.updatedLabel', { + defaultMessage: 'Updated', +}); + +export const EXCEPTION_ITEM_META_BY = i18n.translate('xpack.securitySolution.exceptions.exceptionItem.metaDetailsBy', { + defaultMessage: 'by', +}); + +export const exceptionItemCommentsAccordion = (comments: number) => + i18n.translate('xpack.securitySolution.exceptions.exceptionItem.showCommentsLabel', { + values: { comments }, + defaultMessage: 'Show {comments, plural, =1 {comment} other {comments}} ({comments})', + }); + +export const DESCRIPTOR_WHEN = i18n.translate( + 'xpack.securitySolution.exceptions.exceptionItem.conditions.whenDescriptor', + { + defaultMessage: 'WHEN', + } +); + +export const CONDITION_OPERATOR_TYPE_MATCH = i18n.translate( + 'xpack.securitySolution.exceptions.exceptionItem.conditions.matchOperator', + { + defaultMessage: 'IS', + } +); + +export const CONDITION_OPERATOR_TYPE_NOT_MATCH = i18n.translate( + 'xpack.securitySolution.exceptions.exceptionItem.conditions.matchOperator.not', + { + defaultMessage: 'IS NOT', + } +); + +export const CONDITION_OPERATOR_TYPE_WILDCARD_MATCHES = i18n.translate( + 'xpack.securitySolution.exceptions.exceptionItem.conditions.wildcardMatchesOperator', + { + defaultMessage: 'MATCHES', + } +); + +export const CONDITION_OPERATOR_TYPE_NESTED = i18n.translate( + 'xpack.securitySolution.exceptions.exceptionItem.conditions.nestedOperator', + { + defaultMessage: 'has', + } +); + +export const CONDITION_OPERATOR_TYPE_MATCH_ANY = i18n.translate( + 'xpack.securitySolution.exceptions.exceptionItem.conditions.matchAnyOperator', + { + defaultMessage: 'is one of', + } +); + +export const CONDITION_OPERATOR_TYPE_NOT_MATCH_ANY = i18n.translate( + 'xpack.securitySolution.exceptions.exceptionItem.conditions.matchAnyOperator.not', + { + defaultMessage: 'is not one of', + } +); + +export const CONDITION_OPERATOR_TYPE_EXISTS = i18n.translate( + 'xpack.securitySolution.exceptions.exceptionItem.conditions.existsOperator', + { + defaultMessage: 'exists', + } +); + +export const CONDITION_OPERATOR_TYPE_LIST = i18n.translate( + 'xpack.securitySolution.exceptions.exceptionItem.conditions.listOperator', + { + defaultMessage: 'included in', + } +); + +export const CONDITION_OS = i18n.translate('xpack.securitySolution.exceptions.exceptionItem.conditions.os', { + defaultMessage: 'OS', +}); + +export const CONDITION_AND = i18n.translate('xpack.securitySolution.exceptions.exceptionItem.conditions.and', { + defaultMessage: 'AND', +}); diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exceptions_viewer_items.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exceptions_viewer_items.tsx index 30b5b3e4d1339..f2fa7dd70bd82 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exceptions_viewer_items.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exceptions_viewer_items.tsx @@ -6,13 +6,12 @@ */ import React from 'react'; -import { EuiEmptyPrompt, EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; +import { EuiEmptyPrompt, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import styled from 'styled-components'; import type { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; import * as i18n from '../translations'; -import { ExceptionItem } from './exception_item'; -import { AndOrBadge } from '../../and_or_badge'; +import { ExceptionItem } from './exception_item_2'; import type { ExceptionListItemIdentifiers } from '../types'; const MyFlexItem = styled(EuiFlexItem)` @@ -81,14 +80,6 @@ const ExceptionsViewerItemsComponent: React.FC = ({ exceptions.length > 0 && exceptions.map((exception, index) => ( - {index !== 0 ? ( - <> - - - - ) : ( - - )} = ({ exceptionItem={exception} onDeleteException={onDeleteException} onEditException={onEditExceptionItem} + dataTestSubj={`exceptionItemCard-${exception.name}`} /> ))} From 921596684d00ddd3e80f2a254639c6d70e141451 Mon Sep 17 00:00:00 2001 From: Yara Tercero Date: Mon, 27 Jun 2022 12:38:30 -0700 Subject: [PATCH 02/16] edited unit tests --- .../exceptions/viewer/exception_item_2/index.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_2/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_2/index.test.tsx index 898f17a36604f..bc6e6ecc4cacd 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_2/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_2/index.test.tsx @@ -132,7 +132,7 @@ describe('ExceptionItemCard', () => { // click on popover wrapper.find('button[data-test-subj="exceptionItemCardHeader-actionButton"]').at(0).simulate('click'); - wrapper.find('button[data-test-subj="exceptionItemCardHeader-actionItem"]').at(0).simulate('click'); + wrapper.find('button[data-test-subj="exceptionItemCardHeader-actionItem"]').at(1).simulate('click'); expect(mockOnDeleteException).toHaveBeenCalledWith({ id: '1', From 4fc70e339e29af06ee8c45480b04b593a4cb67da Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Mon, 27 Jun 2022 20:05:42 +0000 Subject: [PATCH 03/16] [CI] Auto-commit changed files from 'node scripts/precommit_hook.js --ref HEAD~1..HEAD --fix' --- .../viewer/exception_item_2/index.test.tsx | 30 ++++++++++++++----- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_2/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_2/index.test.tsx index bc6e6ecc4cacd..c22d405df62a2 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_2/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_2/index.test.tsx @@ -27,7 +27,7 @@ const mockTheme = getMockTheme({ describe('ExceptionItemCard', () => { it('it renders header, item meta information and conditions', () => { - const exceptionItem = {...getExceptionListItemSchemaMock(), comments: []}; + const exceptionItem = { ...getExceptionListItemSchemaMock(), comments: [] }; const wrapper = mount( @@ -45,7 +45,9 @@ describe('ExceptionItemCard', () => { expect(wrapper.find('ExceptionItemCardHeader')).toHaveLength(1); expect(wrapper.find('ExceptionItemCardMetaInfo')).toHaveLength(1); expect(wrapper.find('ExceptionItemCardConditions')).toHaveLength(1); - expect(wrapper.find('[data-test-subj="exceptionsViewerCommentAccordion"]').exists()).toBeFalsy(); + expect( + wrapper.find('[data-test-subj="exceptionsViewerCommentAccordion"]').exists() + ).toBeFalsy(); }); it('it renders header, item meta information, conditions, and comments if any exist', () => { @@ -67,7 +69,9 @@ describe('ExceptionItemCard', () => { expect(wrapper.find('ExceptionItemCardHeader')).toHaveLength(1); expect(wrapper.find('ExceptionItemCardMetaInfo')).toHaveLength(1); expect(wrapper.find('ExceptionItemCardConditions')).toHaveLength(1); - expect(wrapper.find('[data-test-subj="exceptionsViewerCommentAccordion"]').exists()).toBeTruthy(); + expect( + wrapper.find('[data-test-subj="exceptionsViewerCommentAccordion"]').exists() + ).toBeTruthy(); }); it('it does not render edit or delete action buttons when "disableActions" is "true"', () => { @@ -107,8 +111,14 @@ describe('ExceptionItemCard', () => { ); // click on popover - wrapper.find('button[data-test-subj="exceptionItemCardHeader-actionButton"]').at(0).simulate('click'); - wrapper.find('button[data-test-subj="exceptionItemCardHeader-actionItem"]').at(0).simulate('click'); + wrapper + .find('button[data-test-subj="exceptionItemCardHeader-actionButton"]') + .at(0) + .simulate('click'); + wrapper + .find('button[data-test-subj="exceptionItemCardHeader-actionItem"]') + .at(0) + .simulate('click'); expect(mockOnEditException).toHaveBeenCalledWith(getExceptionListItemSchemaMock()); }); @@ -131,8 +141,14 @@ describe('ExceptionItemCard', () => { ); // click on popover - wrapper.find('button[data-test-subj="exceptionItemCardHeader-actionButton"]').at(0).simulate('click'); - wrapper.find('button[data-test-subj="exceptionItemCardHeader-actionItem"]').at(1).simulate('click'); + wrapper + .find('button[data-test-subj="exceptionItemCardHeader-actionButton"]') + .at(0) + .simulate('click'); + wrapper + .find('button[data-test-subj="exceptionItemCardHeader-actionItem"]') + .at(1) + .simulate('click'); expect(mockOnDeleteException).toHaveBeenCalledWith({ id: '1', From 95cc843d6732027669d83b97d4816cf0e4186cd6 Mon Sep 17 00:00:00 2001 From: Yara Tercero Date: Mon, 27 Jun 2022 13:06:58 -0700 Subject: [PATCH 04/16] some cleanup --- .../exception_item/exception_details.test.tsx | 252 ------------- .../exception_item/exception_details.tsx | 121 ------ .../exception_item/exception_entries.test.tsx | 193 ---------- .../exception_item/exception_entries.tsx | 218 ----------- .../viewer/exception_item/index.stories.tsx | 162 -------- .../viewer/exception_item/index.test.tsx | 192 ---------- .../viewer/exception_item/index.tsx | 127 ------- .../exception_item_card_conditions.test.tsx | 0 .../exception_item_card_conditions.tsx | 0 .../exception_item_card_header.test.tsx | 0 .../exception_item_card_header.tsx | 0 .../exception_item_card_meta.test.tsx | 0 .../exception_item_card_meta.tsx | 0 .../index.test.tsx | 0 .../index.tsx | 0 .../translations.ts | 4 - .../viewer/exceptions_viewer_items.test.tsx | 7 - .../viewer/exceptions_viewer_items.tsx | 7 +- .../exceptions/viewer/helpers.test.tsx | 354 ------------------ .../components/exceptions/viewer/helpers.tsx | 166 -------- .../components/exceptions/viewer/index.tsx | 1 - 21 files changed, 2 insertions(+), 1802 deletions(-) delete mode 100644 x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/exception_details.test.tsx delete mode 100644 x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/exception_details.tsx delete mode 100644 x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/exception_entries.test.tsx delete mode 100644 x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/exception_entries.tsx delete mode 100644 x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/index.stories.tsx delete mode 100644 x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/index.test.tsx delete mode 100644 x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/index.tsx rename x-pack/plugins/security_solution/public/common/components/exceptions/viewer/{exception_item_2 => exception_item_card}/exception_item_card_conditions.test.tsx (100%) rename x-pack/plugins/security_solution/public/common/components/exceptions/viewer/{exception_item_2 => exception_item_card}/exception_item_card_conditions.tsx (100%) rename x-pack/plugins/security_solution/public/common/components/exceptions/viewer/{exception_item_2 => exception_item_card}/exception_item_card_header.test.tsx (100%) rename x-pack/plugins/security_solution/public/common/components/exceptions/viewer/{exception_item_2 => exception_item_card}/exception_item_card_header.tsx (100%) rename x-pack/plugins/security_solution/public/common/components/exceptions/viewer/{exception_item_2 => exception_item_card}/exception_item_card_meta.test.tsx (100%) rename x-pack/plugins/security_solution/public/common/components/exceptions/viewer/{exception_item_2 => exception_item_card}/exception_item_card_meta.tsx (100%) rename x-pack/plugins/security_solution/public/common/components/exceptions/viewer/{exception_item_2 => exception_item_card}/index.test.tsx (100%) rename x-pack/plugins/security_solution/public/common/components/exceptions/viewer/{exception_item_2 => exception_item_card}/index.tsx (100%) rename x-pack/plugins/security_solution/public/common/components/exceptions/viewer/{exception_item_2 => exception_item_card}/translations.ts (95%) delete mode 100644 x-pack/plugins/security_solution/public/common/components/exceptions/viewer/helpers.test.tsx delete mode 100644 x-pack/plugins/security_solution/public/common/components/exceptions/viewer/helpers.tsx diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/exception_details.test.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/exception_details.test.tsx deleted file mode 100644 index 20c58985344f8..0000000000000 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/exception_details.test.tsx +++ /dev/null @@ -1,252 +0,0 @@ -/* - * 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 { ThemeProvider } from 'styled-components'; -import { mount } from 'enzyme'; -import moment from 'moment-timezone'; - -import { ExceptionDetails } from './exception_details'; -import { getExceptionListItemSchemaMock } from '@kbn/lists-plugin/common/schemas/response/exception_list_item_schema.mock'; -import { getCommentsArrayMock } from '@kbn/lists-plugin/common/schemas/types/comment.mock'; -import { getMockTheme } from '../../../../lib/kibana/kibana_react.mock'; - -const mockTheme = getMockTheme({ - eui: { - euiColorLightestShade: '#ece', - }, -}); - -describe('ExceptionDetails', () => { - beforeEach(() => { - moment.tz.setDefault('UTC'); - }); - - afterEach(() => { - moment.tz.setDefault('Browser'); - }); - - test('it renders no comments button if no comments exist', () => { - const exceptionItem = getExceptionListItemSchemaMock(); - exceptionItem.comments = []; - - const wrapper = mount( - - - - ); - - expect(wrapper.find('[data-test-subj="exceptionsViewerItemCommentsBtn"]')).toHaveLength(0); - }); - - test('it renders comments button if comments exist', () => { - const exceptionItem = getExceptionListItemSchemaMock(); - exceptionItem.comments = getCommentsArrayMock(); - const wrapper = mount( - - - - ); - - expect( - wrapper.find('.euiButtonEmpty[data-test-subj="exceptionsViewerItemCommentsBtn"]') - ).toHaveLength(1); - }); - - test('it renders correct number of comments', () => { - const exceptionItem = getExceptionListItemSchemaMock(); - exceptionItem.comments = [getCommentsArrayMock()[0]]; - const wrapper = mount( - - - - ); - - expect(wrapper.find('[data-test-subj="exceptionsViewerItemCommentsBtn"]').at(0).text()).toEqual( - 'Show (1) Comment' - ); - }); - - test('it renders comments plural if more than one', () => { - const exceptionItem = getExceptionListItemSchemaMock(); - exceptionItem.comments = getCommentsArrayMock(); - const wrapper = mount( - - - - ); - - expect(wrapper.find('[data-test-subj="exceptionsViewerItemCommentsBtn"]').at(0).text()).toEqual( - 'Show (2) Comments' - ); - }); - - test('it renders comments show text if "showComments" is false', () => { - const exceptionItem = getExceptionListItemSchemaMock(); - exceptionItem.comments = getCommentsArrayMock(); - const wrapper = mount( - - - - ); - - expect(wrapper.find('[data-test-subj="exceptionsViewerItemCommentsBtn"]').at(0).text()).toEqual( - 'Show (2) Comments' - ); - }); - - test('it renders comments hide text if "showComments" is true', () => { - const exceptionItem = getExceptionListItemSchemaMock(); - exceptionItem.comments = getCommentsArrayMock(); - const wrapper = mount( - - - - ); - - expect(wrapper.find('[data-test-subj="exceptionsViewerItemCommentsBtn"]').at(0).text()).toEqual( - 'Hide (2) Comments' - ); - }); - - test('it invokes "onCommentsClick" when comments button clicked', () => { - const mockOnCommentsClick = jest.fn(); - const exceptionItem = getExceptionListItemSchemaMock(); - exceptionItem.comments = getCommentsArrayMock(); - const wrapper = mount( - - - - ); - const commentsBtn = wrapper.find('[data-test-subj="exceptionsViewerItemCommentsBtn"]').at(0); - commentsBtn.simulate('click'); - - expect(mockOnCommentsClick).toHaveBeenCalledTimes(1); - }); - - test('it renders the operating system if one is specified in the exception item', () => { - const exceptionItem = getExceptionListItemSchemaMock({ os_types: ['linux'] }); - const wrapper = mount( - - - - ); - - expect(wrapper.find('EuiDescriptionListTitle').at(0).text()).toEqual('OS'); - expect(wrapper.find('EuiDescriptionListDescription').at(0).text()).toEqual('Linux'); - }); - - test('it renders the exception item creator', () => { - const exceptionItem = getExceptionListItemSchemaMock({ os_types: ['linux'] }); - const wrapper = mount( - - - - ); - - expect(wrapper.find('EuiDescriptionListTitle').at(1).text()).toEqual('Date created'); - expect(wrapper.find('EuiDescriptionListDescription').at(1).text()).toEqual( - 'April 20th 2020 @ 15:25:31' - ); - }); - - test('it renders the exception item creation timestamp', () => { - const exceptionItem = getExceptionListItemSchemaMock({ os_types: ['linux'] }); - const wrapper = mount( - - - - ); - - expect(wrapper.find('EuiDescriptionListTitle').at(2).text()).toEqual('Created by'); - expect(wrapper.find('EuiDescriptionListDescription').at(2).text()).toEqual('some user'); - }); - - test('it renders the description if one is included on the exception item', () => { - const exceptionItem = getExceptionListItemSchemaMock({ os_types: ['linux'] }); - const wrapper = mount( - - - - ); - - expect(wrapper.find('EuiDescriptionListTitle').at(3).text()).toEqual('Description'); - expect(wrapper.find('EuiDescriptionListDescription').at(3).text()).toEqual('some description'); - }); - - test('it renders with Name and Modified info when showName and showModified props are true', () => { - const exceptionItem = getExceptionListItemSchemaMock({ os_types: ['linux'] }); - exceptionItem.comments = []; - - const wrapper = mount( - - - - ); - - expect(wrapper.find('EuiDescriptionListTitle').at(0).text()).toEqual('Name'); - expect(wrapper.find('EuiDescriptionListDescription').at(0).text()).toEqual('some name'); - - expect(wrapper.find('EuiDescriptionListTitle').at(4).text()).toEqual('Date modified'); - expect(wrapper.find('EuiDescriptionListDescription').at(4).text()).toEqual( - 'April 20th 2020 @ 15:25:31' - ); - - expect(wrapper.find('EuiDescriptionListTitle').at(5).text()).toEqual('Modified by'); - expect(wrapper.find('EuiDescriptionListDescription').at(5).text()).toEqual('some user'); - }); -}); diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/exception_details.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/exception_details.tsx deleted file mode 100644 index 429f9672aece5..0000000000000 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/exception_details.tsx +++ /dev/null @@ -1,121 +0,0 @@ -/* - * 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 { - EuiFlexItem, - EuiFlexGroup, - EuiDescriptionList, - EuiButtonEmpty, - EuiDescriptionListTitle, - EuiToolTip, -} from '@elastic/eui'; -import React, { useMemo, Fragment } from 'react'; -import styled, { css } from 'styled-components'; - -import type { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; -import type { DescriptionListItem } from '../../types'; -import { getDescriptionListContent } from '../helpers'; -import * as i18n from '../../translations'; - -const MyExceptionDetails = styled(EuiFlexItem)` - ${({ theme }) => css` - background-color: ${theme.eui.euiColorLightestShade}; - padding: ${theme.eui.euiSize}; - .eventFiltersDescriptionList { - margin: ${theme.eui.euiSize} ${theme.eui.euiSize} 0 ${theme.eui.euiSize}; - } - .eventFiltersDescriptionListTitle { - width: 40%; - margin-top: 0; - margin-bottom: ${theme.eui.euiSizeS}; - } - .eventFiltersDescriptionListDescription { - width: 60%; - margin-top: 0; - margin-bottom: ${theme.eui.euiSizeS}; - } - `} -`; - -const StyledCommentsSection = styled(EuiFlexItem)` - ${({ theme }) => css` - &&& { - margin: ${theme.eui.euiSizeXS} ${theme.eui.euiSize} ${theme.eui.euiSizeL} ${theme.eui.euiSize}; - } - `} -`; - -const ExceptionDetailsComponent = ({ - showComments, - showModified = false, - showName = false, - onCommentsClick, - exceptionItem, -}: { - showComments: boolean; - showModified?: boolean; - showName?: boolean; - exceptionItem: ExceptionListItemSchema; - onCommentsClick: () => void; -}): JSX.Element => { - const descriptionListItems = useMemo( - (): DescriptionListItem[] => getDescriptionListContent(exceptionItem, showModified, showName), - [exceptionItem, showModified, showName] - ); - - const commentsSection = useMemo((): JSX.Element => { - const { comments } = exceptionItem; - if (comments.length > 0) { - return ( - - {!showComments - ? i18n.COMMENTS_SHOW(comments.length) - : i18n.COMMENTS_HIDE(comments.length)} - - ); - } else { - return <>; - } - }, [showComments, onCommentsClick, exceptionItem]); - - return ( - - - - - {descriptionListItems.map((item) => ( - - - - {item.title} - - - {item.description} - - ))} - - - {commentsSection} - - - ); -}; - -ExceptionDetailsComponent.displayName = 'ExceptionDetailsComponent'; - -export const ExceptionDetails = React.memo(ExceptionDetailsComponent); - -ExceptionDetails.displayName = 'ExceptionDetails'; diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/exception_entries.test.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/exception_entries.test.tsx deleted file mode 100644 index a53be08380698..0000000000000 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/exception_entries.test.tsx +++ /dev/null @@ -1,193 +0,0 @@ -/* - * 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 { ThemeProvider } from 'styled-components'; -import { mount } from 'enzyme'; - -import { ExceptionEntries } from './exception_entries'; -import { getFormattedEntryMock } from '../../exceptions.mock'; -import { getEmptyValue } from '../../../empty_value'; -import { getMockTheme } from '../../../../lib/kibana/kibana_react.mock'; - -const mockTheme = getMockTheme({ - eui: { euiSize: '10px', euiColorPrimary: '#ece', euiColorDanger: '#ece' }, -}); - -describe('ExceptionEntries', () => { - test('it does NOT render the and badge if only one exception item entry exists', () => { - const wrapper = mount( - - - - ); - - expect(wrapper.find('[data-test-subj="exceptionsViewerAndBadge"]')).toHaveLength(0); - }); - - test('it renders the and badge if more than one exception item exists', () => { - const wrapper = mount( - - - - ); - - expect(wrapper.find('[data-test-subj="exceptionsViewerAndBadge"]')).toHaveLength(1); - }); - - test('it invokes "onEdit" when edit button clicked', () => { - const mockOnEdit = jest.fn(); - const wrapper = mount( - - - - ); - const editBtn = wrapper.find('[data-test-subj="exceptionsViewerEditBtn"] button').at(0); - editBtn.simulate('click'); - - expect(mockOnEdit).toHaveBeenCalledTimes(1); - }); - - test('it invokes "onDelete" when delete button clicked', () => { - const mockOnDelete = jest.fn(); - const wrapper = mount( - - - - ); - const deleteBtn = wrapper.find('[data-test-subj="exceptionsViewerDeleteBtn"] button').at(0); - deleteBtn.simulate('click'); - - expect(mockOnDelete).toHaveBeenCalledTimes(1); - }); - - test('it does not render edit button if "disableActions" is "true"', () => { - const wrapper = mount( - - - - ); - const editBtns = wrapper.find('[data-test-subj="exceptionsViewerEditBtn"] button'); - - expect(editBtns).toHaveLength(0); - }); - - test('it does not render delete button if "disableActions" is "true"', () => { - const wrapper = mount( - - - - ); - const deleteBtns = wrapper.find('[data-test-subj="exceptionsViewerDeleteBtn"] button').at(0); - - expect(deleteBtns).toHaveLength(0); - }); - - test('it renders nested entry', () => { - const parentEntry = getFormattedEntryMock(); - parentEntry.operator = undefined; - parentEntry.value = undefined; - - const wrapper = mount( - - - - ); - - const parentField = wrapper - .find('[data-test-subj="exceptionFieldNameCell"] .euiTableCellContent') - .at(0); - const parentOperator = wrapper - .find('[data-test-subj="exceptionFieldOperatorCell"] .euiTableCellContent') - .at(0); - const parentValue = wrapper - .find('[data-test-subj="exceptionFieldValueCell"] .euiTableCellContent') - .at(0); - - const nestedField = wrapper - .find('[data-test-subj="exceptionFieldNameCell"] .euiTableCellContent') - .at(1); - const nestedOperator = wrapper - .find('[data-test-subj="exceptionFieldOperatorCell"] .euiTableCellContent') - .at(1); - const nestedValue = wrapper - .find('[data-test-subj="exceptionFieldValueCell"] .euiTableCellContent') - .at(1); - - expect(parentField.text()).toEqual('host.name'); - expect(parentOperator.text()).toEqual(getEmptyValue()); - expect(parentValue.text()).toEqual(getEmptyValue()); - - expect(nestedField.exists('.euiToolTipAnchor')).toBeTruthy(); - expect(nestedField.text()).toContain('host.name'); - expect(nestedOperator.text()).toEqual('is'); - expect(nestedValue.text()).toEqual('some name'); - }); - - test('it renders non-nested entries', () => { - const wrapper = mount( - - - - ); - - const field = wrapper - .find('[data-test-subj="exceptionFieldNameCell"] .euiTableCellContent') - .at(0); - const operator = wrapper - .find('[data-test-subj="exceptionFieldOperatorCell"] .euiTableCellContent') - .at(0); - const value = wrapper - .find('[data-test-subj="exceptionFieldValueCell"] .euiTableCellContent') - .at(0); - - expect(field.exists('.euiToolTipAnchor')).toBeFalsy(); - expect(field.text()).toEqual('host.name'); - expect(operator.text()).toEqual('is'); - expect(value.text()).toEqual('some name'); - }); -}); diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/exception_entries.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/exception_entries.tsx deleted file mode 100644 index 4db00bea5c932..0000000000000 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/exception_entries.tsx +++ /dev/null @@ -1,218 +0,0 @@ -/* - * 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 { - EuiBasicTable, - EuiIconTip, - EuiFlexItem, - EuiFlexGroup, - EuiButton, - EuiTableFieldDataColumnType, - EuiHideFor, - EuiBadge, - EuiBadgeGroup, - EuiToolTip, -} from '@elastic/eui'; -import React, { useMemo } from 'react'; -import styled, { css } from 'styled-components'; -import { transparentize } from 'polished'; - -import { AndOrBadge } from '../../../and_or_badge'; -import { getEmptyValue } from '../../../empty_value'; -import * as i18n from '../../translations'; -import { FormattedEntry } from '../../types'; - -const MyEntriesDetails = styled(EuiFlexItem)` - ${({ theme }) => css` - padding: ${theme.eui.euiSize} ${theme.eui.euiSizeL} ${theme.eui.euiSizeL} ${theme.eui.euiSizeXS}; - &&& { - margin-left: 0; - } - `} -`; - -const MyEditButton = styled(EuiButton)` - ${({ theme }) => css` - background-color: ${transparentize(0.9, theme.eui.euiColorPrimary)}; - border: none; - font-weight: ${theme.eui.euiFontWeightSemiBold}; - `} -`; - -const MyRemoveButton = styled(EuiButton)` - ${({ theme }) => css` - background-color: ${transparentize(0.9, theme.eui.euiColorDanger)}; - border: none; - font-weight: ${theme.eui.euiFontWeightSemiBold}; - `} -`; - -const MyAndOrBadgeContainer = styled(EuiFlexItem)` - ${({ theme }) => css` - padding: ${theme.eui.euiSizeXL} ${theme.eui.euiSize} ${theme.eui.euiSizeS} 0; - `} -`; - -const MyActionButton = styled(EuiFlexItem)` - align-self: flex-end; -`; - -const MyNestedValueContainer = styled.div` - margin-left: ${({ theme }) => theme.eui.euiSizeL}; -`; - -const MyNestedValue = styled.span` - margin-left: ${({ theme }) => theme.eui.euiSizeS}; -`; - -const ValueBadgeGroup = styled(EuiBadgeGroup)` - width: 100%; -`; - -interface ExceptionEntriesComponentProps { - entries: FormattedEntry[]; - disableActions: boolean; - onDelete: () => void; - onEdit: () => void; -} - -const ExceptionEntriesComponent = ({ - entries, - disableActions, - onDelete, - onEdit, -}: ExceptionEntriesComponentProps): JSX.Element => { - const columns = useMemo( - (): Array> => [ - { - field: 'fieldName', - name: 'Field', - sortable: false, - truncateText: true, - textOnly: true, - 'data-test-subj': 'exceptionFieldNameCell', - width: '30%', - render: (value: string | null, data: FormattedEntry) => { - if (value != null && data.isNested) { - return ( - - - {value} - - ); - } else { - return value ?? getEmptyValue(); - } - }, - }, - { - field: 'operator', - name: 'Operator', - sortable: false, - truncateText: true, - 'data-test-subj': 'exceptionFieldOperatorCell', - width: '20%', - render: (value: string | null) => value ?? getEmptyValue(), - }, - { - field: 'value', - name: 'Value', - sortable: false, - truncateText: true, - 'data-test-subj': 'exceptionFieldValueCell', - width: '60%', - render: (values: string | string[] | null) => { - if (Array.isArray(values)) { - return ( - - {values.map((value) => { - return ( - - {value} - - ); - })} - - ); - } else { - return values ? ( - - {values} - - ) : ( - getEmptyValue() - ); - } - }, - }, - ], - [] - ); - - return ( - - - - - {entries.length > 1 && ( - - - - - - )} - - - - - - {!disableActions && ( - - - - - {i18n.EDIT} - - - - - {i18n.REMOVE} - - - - - )} - - - ); -}; - -ExceptionEntriesComponent.displayName = 'ExceptionEntriesComponent'; - -export const ExceptionEntries = React.memo(ExceptionEntriesComponent); - -ExceptionEntries.displayName = 'ExceptionEntries'; diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/index.stories.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/index.stories.tsx deleted file mode 100644 index 30ca4428aa008..0000000000000 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/index.stories.tsx +++ /dev/null @@ -1,162 +0,0 @@ -/* - * 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 { storiesOf, addDecorator } from '@storybook/react'; -import { action } from '@storybook/addon-actions'; -import React from 'react'; -import { ThemeProvider } from 'styled-components'; -import { euiLightVars } from '@kbn/ui-theme'; - -import { getExceptionListItemSchemaMock } from '@kbn/lists-plugin/common/schemas/response/exception_list_item_schema.mock'; -import { getCommentsArrayMock } from '@kbn/lists-plugin/common/schemas/types/comment.mock'; -import { ExceptionItem } from '.'; - -addDecorator((storyFn) => ( - ({ eui: euiLightVars, darkMode: false })}>{storyFn()} -)); - -storiesOf('Components/ExceptionItem', module) - .add('with os', () => { - const payload = getExceptionListItemSchemaMock(); - payload.description = ''; - payload.comments = []; - payload.entries = [ - { - field: 'actingProcess.file.signer', - type: 'match', - operator: 'included', - value: 'Elastic, N.V.', - }, - ]; - - return ( - - ); - }) - .add('with description', () => { - const payload = getExceptionListItemSchemaMock(); - payload.comments = []; - payload.entries = [ - { - field: 'actingProcess.file.signer', - type: 'match', - operator: 'included', - value: 'Elastic, N.V.', - }, - ]; - - return ( - - ); - }) - .add('with comments', () => { - const payload = getExceptionListItemSchemaMock(); - payload.description = ''; - payload.comments = getCommentsArrayMock(); - payload.entries = [ - { - field: 'actingProcess.file.signer', - type: 'match', - operator: 'included', - value: 'Elastic, N.V.', - }, - ]; - - return ( - - ); - }) - .add('with nested entries', () => { - const payload = getExceptionListItemSchemaMock(); - payload.description = ''; - payload.comments = []; - - return ( - - ); - }) - .add('with everything', () => { - const payload = getExceptionListItemSchemaMock(); - payload.comments = getCommentsArrayMock(); - return ( - - ); - }) - .add('with loadingItemIds', () => { - // eslint-disable-next-line @typescript-eslint/naming-convention - const { id, namespace_type, ...rest } = getExceptionListItemSchemaMock(); - - return ( - - ); - }) - .add('with actions disabled', () => { - const payload = getExceptionListItemSchemaMock(); - payload.description = ''; - payload.comments = getCommentsArrayMock(); - payload.entries = [ - { - field: 'actingProcess.file.signer', - type: 'match', - operator: 'included', - value: 'Elastic, N.V.', - }, - ]; - - return ( - - ); - }); diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/index.test.tsx deleted file mode 100644 index e1afc8f44b354..0000000000000 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/index.test.tsx +++ /dev/null @@ -1,192 +0,0 @@ -/* - * 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 { ThemeProvider } from 'styled-components'; -import { mount } from 'enzyme'; - -import { ExceptionItem } from '.'; -import { getExceptionListItemSchemaMock } from '@kbn/lists-plugin/common/schemas/response/exception_list_item_schema.mock'; -import { getCommentsArrayMock } from '@kbn/lists-plugin/common/schemas/types/comment.mock'; -import { getMockTheme } from '../../../../lib/kibana/kibana_react.mock'; - -jest.mock('../../../../lib/kibana'); - -const mockTheme = getMockTheme({ - eui: { - euiColorDanger: '#ece', - euiColorLightestShade: '#ece', - euiColorPrimary: '#ece', - euiFontWeightSemiBold: 1, - }, -}); - -describe('ExceptionItem', () => { - it('it renders ExceptionDetails and ExceptionEntries', () => { - const exceptionItem = getExceptionListItemSchemaMock(); - - const wrapper = mount( - - - - ); - - expect(wrapper.find('ExceptionDetails')).toHaveLength(1); - expect(wrapper.find('ExceptionEntries')).toHaveLength(1); - }); - - it('it renders ExceptionDetails with Name and Modified info when showName and showModified are true ', () => { - const exceptionItem = getExceptionListItemSchemaMock(); - - const wrapper = mount( - - - - ); - - expect(wrapper.find('ExceptionDetails').props()).toEqual( - expect.objectContaining({ - showModified: true, - showName: true, - }) - ); - }); - - it('it does not render edit or delete action buttons when "disableActions" is "true"', () => { - const mockOnEditException = jest.fn(); - const exceptionItem = getExceptionListItemSchemaMock(); - - const wrapper = mount( - - - - ); - - const editBtn = wrapper.find('[data-test-subj="exceptionsViewerEditBtn"] button'); - const deleteBtn = wrapper.find('[data-test-subj="exceptionsViewerDeleteBtn"] button'); - - expect(editBtn).toHaveLength(0); - expect(deleteBtn).toHaveLength(0); - }); - - it('it invokes "onEditException" when edit button clicked', () => { - const mockOnEditException = jest.fn(); - const exceptionItem = getExceptionListItemSchemaMock(); - - const wrapper = mount( - - - - ); - - const editBtn = wrapper.find('[data-test-subj="exceptionsViewerEditBtn"] button').at(0); - editBtn.simulate('click'); - - expect(mockOnEditException).toHaveBeenCalledWith(getExceptionListItemSchemaMock()); - }); - - it('it invokes "onDeleteException" when delete button clicked', () => { - const mockOnDeleteException = jest.fn(); - const exceptionItem = getExceptionListItemSchemaMock(); - - const wrapper = mount( - - - - ); - - const deleteBtn = wrapper.find('[data-test-subj="exceptionsViewerDeleteBtn"] button').at(0); - deleteBtn.simulate('click'); - - expect(mockOnDeleteException).toHaveBeenCalledWith({ - id: '1', - namespaceType: 'single', - }); - }); - - it('it renders comment accordion closed to begin with', () => { - const mockOnDeleteException = jest.fn(); - const exceptionItem = getExceptionListItemSchemaMock(); - exceptionItem.comments = getCommentsArrayMock(); - const wrapper = mount( - - - - ); - - expect(wrapper.find('.euiAccordion-isOpen')).toHaveLength(0); - }); - - it('it renders comment accordion open when showComments is true', () => { - const mockOnDeleteException = jest.fn(); - const exceptionItem = getExceptionListItemSchemaMock(); - exceptionItem.comments = getCommentsArrayMock(); - const wrapper = mount( - - - - ); - - const commentsBtn = wrapper - .find('.euiButtonEmpty[data-test-subj="exceptionsViewerItemCommentsBtn"]') - .at(0); - commentsBtn.simulate('click'); - - expect(wrapper.find('.euiAccordion-isOpen')).toHaveLength(1); - }); -}); diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/index.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/index.tsx deleted file mode 100644 index f47f802e558ca..0000000000000 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/index.tsx +++ /dev/null @@ -1,127 +0,0 @@ -/* - * 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 { - EuiPanel, - EuiFlexGroup, - EuiCommentProps, - EuiCommentList, - EuiAccordion, - EuiFlexItem, -} from '@elastic/eui'; -import React, { useEffect, useState, useMemo, useCallback } from 'react'; -import styled from 'styled-components'; - -import type { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; -import { ExceptionDetails } from './exception_details'; -import { ExceptionEntries } from './exception_entries'; -import { getFormattedComments } from '../../helpers'; -import { getFormattedEntries } from '../helpers'; -import type { FormattedEntry, ExceptionListItemIdentifiers } from '../../types'; - -const MyFlexItem = styled(EuiFlexItem)` - &.comments--show { - padding: ${({ theme }) => theme.eui.euiSize}; - border-top: ${({ theme }) => `${theme.eui.euiBorderThin}`}; - } -`; - -export interface ExceptionItemProps { - loadingItemIds: ExceptionListItemIdentifiers[]; - exceptionItem: ExceptionListItemSchema; - commentsAccordionId: string; - onDeleteException: (arg: ExceptionListItemIdentifiers) => void; - onEditException: (item: ExceptionListItemSchema) => void; - showName?: boolean; - showModified?: boolean; - disableActions: boolean; - 'data-test-subj'?: string; -} - -const ExceptionItemComponent = ({ - disableActions, - loadingItemIds, - exceptionItem, - commentsAccordionId, - onDeleteException, - onEditException, - showModified = false, - showName = false, - 'data-test-subj': dataTestSubj, -}: ExceptionItemProps): JSX.Element => { - const [entryItems, setEntryItems] = useState([]); - const [showComments, setShowComments] = useState(false); - - useEffect((): void => { - const formattedEntries = getFormattedEntries(exceptionItem.entries); - setEntryItems(formattedEntries); - }, [exceptionItem.entries]); - - const handleDelete = useCallback((): void => { - onDeleteException({ - id: exceptionItem.id, - namespaceType: exceptionItem.namespace_type, - }); - }, [onDeleteException, exceptionItem.id, exceptionItem.namespace_type]); - - const handleEdit = useCallback((): void => { - onEditException(exceptionItem); - }, [onEditException, exceptionItem]); - - const onCommentsClick = useCallback((): void => { - setShowComments(!showComments); - }, [setShowComments, showComments]); - - const formattedComments = useMemo((): EuiCommentProps[] => { - return getFormattedComments(exceptionItem.comments); - }, [exceptionItem.comments]); - - const disableItemActions = useMemo((): boolean => { - const foundItems = loadingItemIds.filter(({ id }) => id === exceptionItem.id); - return foundItems.length > 0; - }, [loadingItemIds, exceptionItem.id]); - - return ( - - - - - - - - - - - - - - - - ); -}; - -ExceptionItemComponent.displayName = 'ExceptionItemComponent'; - -export const ExceptionItem = React.memo(ExceptionItemComponent); - -ExceptionItem.displayName = 'ExceptionItem'; diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_2/exception_item_card_conditions.test.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_conditions.test.tsx similarity index 100% rename from x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_2/exception_item_card_conditions.test.tsx rename to x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_conditions.test.tsx diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_2/exception_item_card_conditions.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_conditions.tsx similarity index 100% rename from x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_2/exception_item_card_conditions.tsx rename to x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_conditions.tsx diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_2/exception_item_card_header.test.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_header.test.tsx similarity index 100% rename from x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_2/exception_item_card_header.test.tsx rename to x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_header.test.tsx diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_2/exception_item_card_header.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_header.tsx similarity index 100% rename from x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_2/exception_item_card_header.tsx rename to x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_header.tsx diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_2/exception_item_card_meta.test.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_meta.test.tsx similarity index 100% rename from x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_2/exception_item_card_meta.test.tsx rename to x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_meta.test.tsx diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_2/exception_item_card_meta.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_meta.tsx similarity index 100% rename from x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_2/exception_item_card_meta.tsx rename to x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_meta.tsx diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_2/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/index.test.tsx similarity index 100% rename from x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_2/index.test.tsx rename to x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/index.test.tsx diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_2/index.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/index.tsx similarity index 100% rename from x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_2/index.tsx rename to x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/index.tsx diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_2/translations.ts b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/translations.ts similarity index 95% rename from x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_2/translations.ts rename to x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/translations.ts index 070e26720238d..793bd8984a3eb 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_2/translations.ts +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/translations.ts @@ -96,10 +96,6 @@ export const CONDITION_OPERATOR_TYPE_LIST = i18n.translate( } ); -export const CONDITION_OS = i18n.translate('xpack.securitySolution.exceptions.exceptionItem.conditions.os', { - defaultMessage: 'OS', -}); - export const CONDITION_AND = i18n.translate('xpack.securitySolution.exceptions.exceptionItem.conditions.and', { defaultMessage: 'AND', }); diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exceptions_viewer_items.test.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exceptions_viewer_items.test.tsx index 90a06a732a283..dcbdc56cd547b 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exceptions_viewer_items.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exceptions_viewer_items.test.tsx @@ -32,7 +32,6 @@ describe('ExceptionsViewerItems', () => { disableActions={false} exceptions={[]} loadingItemIds={[]} - commentsAccordionId="comments-accordion-id" onDeleteException={jest.fn()} onEditExceptionItem={jest.fn()} /> @@ -58,7 +57,6 @@ describe('ExceptionsViewerItems', () => { disableActions={false} exceptions={[]} loadingItemIds={[]} - commentsAccordionId="comments-accordion-id" onDeleteException={jest.fn()} onEditExceptionItem={jest.fn()} /> @@ -83,7 +81,6 @@ describe('ExceptionsViewerItems', () => { disableActions={false} exceptions={[getExceptionListItemSchemaMock()]} loadingItemIds={[]} - commentsAccordionId="comments-accordion-id" onDeleteException={jest.fn()} onEditExceptionItem={jest.fn()} /> @@ -104,7 +101,6 @@ describe('ExceptionsViewerItems', () => { disableActions={false} exceptions={[]} loadingItemIds={[]} - commentsAccordionId="comments-accordion-id" onDeleteException={jest.fn()} onEditExceptionItem={jest.fn()} /> @@ -129,7 +125,6 @@ describe('ExceptionsViewerItems', () => { disableActions={false} exceptions={[exception1, exception2]} loadingItemIds={[]} - commentsAccordionId="comments-accordion-id" onDeleteException={jest.fn()} onEditExceptionItem={jest.fn()} /> @@ -155,7 +150,6 @@ describe('ExceptionsViewerItems', () => { disableActions={false} exceptions={[exception1, exception2]} loadingItemIds={[]} - commentsAccordionId="comments-accordion-id" onDeleteException={jest.fn()} onEditExceptionItem={jest.fn()} /> @@ -181,7 +175,6 @@ describe('ExceptionsViewerItems', () => { disableActions={false} exceptions={[getExceptionListItemSchemaMock()]} loadingItemIds={[]} - commentsAccordionId="comments-accordion-id" onDeleteException={mockOnDeleteException} onEditExceptionItem={jest.fn()} /> diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exceptions_viewer_items.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exceptions_viewer_items.tsx index f2fa7dd70bd82..b446ac723a8d2 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exceptions_viewer_items.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exceptions_viewer_items.tsx @@ -11,7 +11,7 @@ import styled from 'styled-components'; import type { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; import * as i18n from '../translations'; -import { ExceptionItem } from './exception_item_2'; +import { ExceptionItemCard } from './exception_item_card'; import type { ExceptionListItemIdentifiers } from '../types'; const MyFlexItem = styled(EuiFlexItem)` @@ -33,7 +33,6 @@ interface ExceptionsViewerItemsProps { disableActions: boolean; exceptions: ExceptionListItemSchema[]; loadingItemIds: ExceptionListItemIdentifiers[]; - commentsAccordionId: string; onDeleteException: (arg: ExceptionListItemIdentifiers) => void; onEditExceptionItem: (item: ExceptionListItemSchema) => void; } @@ -44,7 +43,6 @@ const ExceptionsViewerItemsComponent: React.FC = ({ isInitLoading, exceptions, loadingItemIds, - commentsAccordionId, onDeleteException, onEditExceptionItem, disableActions, @@ -80,10 +78,9 @@ const ExceptionsViewerItemsComponent: React.FC = ({ exceptions.length > 0 && exceptions.map((exception, index) => ( - { - beforeEach(() => { - moment.tz.setDefault('UTC'); - }); - - afterEach(() => { - moment.tz.setDefault('Browser'); - }); - - describe('#getFormattedEntries', () => { - test('it returns empty array if no entries passed', () => { - const result = getFormattedEntries([]); - - expect(result).toEqual([]); - }); - - test('it formats nested entries as expected', () => { - const payload = [getEntryMatchMock()]; - const result = getFormattedEntries(payload); - const expected: FormattedEntry[] = [ - { - fieldName: 'host.name', - isNested: false, - operator: 'is', - value: 'some host name', - }, - ]; - expect(result).toEqual(expected); - }); - - test('it formats "exists" entries as expected', () => { - const payload = [getEntryExistsMock()]; - const result = getFormattedEntries(payload); - const expected: FormattedEntry[] = [ - { - fieldName: 'host.name', - isNested: false, - operator: 'exists', - value: undefined, - }, - ]; - expect(result).toEqual(expected); - }); - - test('it formats non-nested entries as expected', () => { - const payload = [getEntryMatchAnyMock(), getEntryMatchMock()]; - const result = getFormattedEntries(payload); - const expected: FormattedEntry[] = [ - { - fieldName: 'host.name', - isNested: false, - operator: 'is one of', - value: ['some host name'], - }, - { - fieldName: 'host.name', - isNested: false, - operator: 'is', - value: 'some host name', - }, - ]; - expect(result).toEqual(expected); - }); - - test('it formats a mix of nested and non-nested entries as expected', () => { - const payload = getEntriesArrayMock(); - const result = getFormattedEntries(payload); - const expected: FormattedEntry[] = [ - { - fieldName: 'host.name', - isNested: false, - operator: 'is', - value: 'some host name', - }, - { - fieldName: 'host.name', - isNested: false, - operator: 'is one of', - value: ['some host name'], - }, - { - fieldName: 'host.name', - isNested: false, - operator: 'exists', - value: undefined, - }, - { - fieldName: 'parent.field', - isNested: false, - operator: undefined, - value: undefined, - }, - { - fieldName: 'host.name', - isNested: true, - operator: 'is', - value: 'some host name', - }, - { - fieldName: 'host.name', - isNested: true, - operator: 'is one of', - value: ['some host name'], - }, - ]; - expect(result).toEqual(expected); - }); - }); - - describe('#formatEntry', () => { - test('it formats an entry', () => { - const payload = getEntryMatchMock(); - const formattedEntry = formatEntry({ isNested: false, item: payload }); - const expected: FormattedEntry = { - fieldName: 'host.name', - isNested: false, - operator: 'is', - value: 'some host name', - }; - - expect(formattedEntry).toEqual(expected); - }); - - test('it formats as expected when "isNested" is "true"', () => { - const payload = getEntryMatchMock(); - const formattedEntry = formatEntry({ isNested: true, item: payload }); - const expected: FormattedEntry = { - fieldName: 'host.name', - isNested: true, - operator: 'is', - value: 'some host name', - }; - - expect(formattedEntry).toEqual(expected); - }); - }); - - describe('#getDescriptionListContent', () => { - test('it returns formatted description list with os if one is specified', () => { - const payload = getExceptionListItemSchemaMock({ os_types: ['linux'] }); - payload.description = ''; - const result = getDescriptionListContent(payload); - const os = result.find(({ title }) => title === 'OS'); - - expect(os).toMatchInlineSnapshot(` - Object { - "description": - - Linux - - , - "title": "OS", - } - `); - }); - - test('it returns formatted description list with a description if one specified', () => { - const payload = getExceptionListItemSchemaMock({ os_types: ['linux'] }); - payload.description = 'Im a description'; - const result = getDescriptionListContent(payload); - const description = result.find(({ title }) => title === 'Description'); - - expect(description).toMatchInlineSnapshot(` - Object { - "description": - - Im a description - - , - "title": "Description", - } - `); - }); - - test('it returns scrolling element when description is longer than 75 charachters', () => { - const payload = getExceptionListItemSchemaMock({ os_types: ['linux'] }); - payload.description = - 'Puppy kitty ipsum dolor sit good dog foot stick canary. Teeth Mittens grooming vaccine walk swimming nest good boy furry tongue heel furry treats fish. Cage run fast kitten dinnertime ball run foot park fleas throw house train licks stick dinnertime window. Yawn litter fish yawn toy pet gate throw Buddy kitty wag tail ball groom crate ferret heel wet nose Rover toys pet supplies. Bird Food treats tongue lick teeth ferret litter box slobbery litter box crate bird small animals yawn small animals shake slobber gimme five toys polydactyl meow. '; - const result = getDescriptionListContent(payload); - const description = result.find(({ title }) => title === 'Description'); - - expect(description).toMatchInlineSnapshot(` - Object { - "description": - - Puppy kitty ipsum dolor sit good dog foot stick canary. Teeth Mittens grooming vaccine walk swimming nest good boy furry tongue heel furry treats fish. Cage run fast kitten dinnertime ball run foot park fleas throw house train licks stick dinnertime window. Yawn litter fish yawn toy pet gate throw Buddy kitty wag tail ball groom crate ferret heel wet nose Rover toys pet supplies. Bird Food treats tongue lick teeth ferret litter box slobbery litter box crate bird small animals yawn small animals shake slobber gimme five toys polydactyl meow. - - , - "title": "Description", - } - `); - }); - - test('it returns just user and date created if no other fields specified', () => { - const payload = getExceptionListItemSchemaMock(); - payload.description = ''; - const result = getDescriptionListContent(payload); - expect(result).toMatchInlineSnapshot(` - Array [ - Object { - "description": - - April 20th 2020 @ 15:25:31 - - , - "title": "Date created", - }, - Object { - "description": - - some user - - , - "title": "Created by", - }, - ] - `); - }); - - test('it returns Modified By/On info when `includeModified` is true', () => { - const result = getDescriptionListContent( - getExceptionListItemSchemaMock({ os_types: ['linux'] }), - true - ); - const dateModified = result.find(({ title }) => title === 'Date modified'); - const modifiedBy = result.find(({ title }) => title === 'Modified by'); - expect(modifiedBy).toMatchInlineSnapshot(` - Object { - "description": - - some user - - , - "title": "Modified by", - } - `); - expect(dateModified).toMatchInlineSnapshot(` - Object { - "description": - - April 20th 2020 @ 15:25:31 - - , - "title": "Date modified", - } - `); - }); - - test('it returns Name when `includeName` is true', () => { - const result = getDescriptionListContent( - getExceptionListItemSchemaMock({ os_types: ['linux'] }), - false, - true - ); - const name = result.find(({ title }) => title === 'Name'); - expect(name).toMatchInlineSnapshot(` - Object { - "description": - - some name - - , - "title": "Name", - } - `); - }); - }); -}); diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/helpers.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/helpers.tsx deleted file mode 100644 index 37bfeb6166405..0000000000000 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/helpers.tsx +++ /dev/null @@ -1,166 +0,0 @@ -/* - * 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 moment from 'moment'; - -import { entriesNested, ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; -import { - getEntryValue, - getExceptionOperatorSelect, - BuilderEntry, -} from '@kbn/securitysolution-list-utils'; - -import React from 'react'; -import { EuiDescriptionListDescription, EuiText, EuiToolTip } from '@elastic/eui'; -import { formatOperatingSystems } from '../helpers'; -import type { FormattedEntry, DescriptionListItem } from '../types'; -import * as i18n from '../translations'; - -/** - * Helper method for `getFormattedEntries` - */ -export const formatEntry = ({ - isNested, - item, -}: { - isNested: boolean; - item: BuilderEntry; -}): FormattedEntry => { - const operator = getExceptionOperatorSelect(item); - const value = getEntryValue(item); - - return { - fieldName: item.field ?? '', - operator: operator.message, - value, - isNested, - }; -}; - -/** - * Formats ExceptionItem entries into simple field, operator, value - * for use in rendering items in table - * - * @param entries an ExceptionItem's entries - */ -export const getFormattedEntries = (entries: BuilderEntry[]): FormattedEntry[] => { - const formattedEntries = entries.map((item) => { - if (entriesNested.is(item)) { - const parent = { - fieldName: item.field, - operator: undefined, - value: undefined, - isNested: false, - }; - return item.entries.reduce( - (acc, nestedEntry) => { - const formattedEntry = formatEntry({ - isNested: true, - item: nestedEntry, - }); - return [...acc, { ...formattedEntry }]; - }, - [parent] - ); - } else { - return formatEntry({ isNested: false, item }); - } - }); - - return formattedEntries.flat(); -}; - -/** - * Formats ExceptionItem details for description list component - * - * @param exceptionItem an ExceptionItem - * @param includeModified if modified information should be included - * @param includeName if the Name should be included - */ -export const getDescriptionListContent = ( - exceptionItem: ExceptionListItemSchema, - includeModified: boolean = false, - includeName: boolean = false -): DescriptionListItem[] => { - const details = [ - ...(includeName - ? [ - { - title: i18n.NAME, - value: exceptionItem.name, - }, - ] - : []), - { - title: i18n.OPERATING_SYSTEM, - value: formatOperatingSystems(exceptionItem.os_types), - }, - { - title: i18n.DATE_CREATED, - value: moment(exceptionItem.created_at).format('MMMM Do YYYY @ HH:mm:ss'), - }, - { - title: i18n.CREATED_BY, - value: exceptionItem.created_by, - }, - ...(includeModified - ? [ - { - title: i18n.DATE_MODIFIED, - value: moment(exceptionItem.updated_at).format('MMMM Do YYYY @ HH:mm:ss'), - }, - { - title: i18n.MODIFIED_BY, - value: exceptionItem.updated_by, - }, - ] - : []), - { - title: i18n.DESCRIPTION, - value: exceptionItem.description, - }, - ]; - - return details.reduce((acc, { value, title }) => { - if (value != null && value.trim() !== '') { - const valueElement = ( - - - {value} - - - ); - if (title === i18n.DESCRIPTION) { - return [ - ...acc, - { - title, - description: - value.length > 75 ? ( - - - {value} - - - ) : ( - valueElement - ), - }, - ]; - } - return [...acc, { title, description: valueElement }]; - } else { - return acc; - } - }, []); -}; diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/index.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/index.tsx index 63093c06a9450..5b16d26b66408 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/index.tsx @@ -396,7 +396,6 @@ const ExceptionsViewerComponent = ({ isInitLoading={isInitLoading} exceptions={exceptions} loadingItemIds={loadingItemIds} - commentsAccordionId={commentsAccordionId} onDeleteException={handleDeleteException} onEditExceptionItem={handleEditException} /> From fb1f464b7e5a31206c7914108c6e90e7baa732bf Mon Sep 17 00:00:00 2001 From: Yara Tercero Date: Mon, 27 Jun 2022 13:47:18 -0700 Subject: [PATCH 05/16] update comments color --- .../exceptions/viewer/exception_item_card/index.tsx | 13 ++++++++++++- .../common/components/exceptions/viewer/index.tsx | 2 +- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/index.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/index.tsx index 972734687eadf..c65fca36e5b7d 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/index.tsx @@ -13,6 +13,7 @@ import { EuiAccordion, EuiFlexItem, EuiText, + useEuiTheme, } from '@elastic/eui'; import React, { useMemo, useCallback } from 'react'; import type { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; @@ -41,6 +42,8 @@ const ExceptionItemCardComponent = ({ onEditException, dataTestSubj, }: ExceptionItemProps): JSX.Element => { + const { euiTheme } = useEuiTheme(); + const handleDelete = useCallback((): void => { onDeleteException({ id: exceptionItem.id, @@ -98,7 +101,15 @@ const ExceptionItemCardComponent = ({ {i18n.exceptionItemCommentsAccordion(formattedComments.length)}} + buttonContent={( + + {i18n.exceptionItemCommentsAccordion(formattedComments.length)} + + )} + arrowDisplay="none" data-test-subj="exceptionsViewerCommentAccordion" > diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/index.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/index.tsx index 5b16d26b66408..5bcbc128bc81f 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/index.tsx @@ -260,7 +260,7 @@ const ExceptionsViewerComponent = ({ lists: exceptionListsMeta, exception, }); - + console.log({exception}) setCurrentModal('editException'); }, [setCurrentModal, exceptionListsMeta] From a6b10707978e59f1b17fd88ff3587589962f6d1c Mon Sep 17 00:00:00 2001 From: Yara Tercero Date: Mon, 27 Jun 2022 14:03:40 -0700 Subject: [PATCH 06/16] Remove console log --- .../public/common/components/exceptions/viewer/index.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/index.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/index.tsx index 5bcbc128bc81f..b4425e2e165c7 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/index.tsx @@ -260,7 +260,6 @@ const ExceptionsViewerComponent = ({ lists: exceptionListsMeta, exception, }); - console.log({exception}) setCurrentModal('editException'); }, [setCurrentModal, exceptionListsMeta] From 187278aa082c5bb89b44d2d939745a5e41815185 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Mon, 27 Jun 2022 21:55:40 +0000 Subject: [PATCH 07/16] [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix' --- .../exception_item_card_conditions.test.tsx | 32 +++---- .../exception_item_card_conditions.tsx | 87 ++++++++----------- .../exception_item_card_header.test.tsx | 78 +++++++++-------- .../exception_item_card_header.tsx | 23 +++-- .../exception_item_card_meta.tsx | 53 +++++------ .../viewer/exception_item_card/index.tsx | 22 ++--- .../exception_item_card/translations.ts | 56 ++++++++---- 7 files changed, 184 insertions(+), 167 deletions(-) diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_conditions.test.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_conditions.test.tsx index 91c73636654b0..fa770fd20cb6b 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_conditions.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_conditions.test.tsx @@ -18,34 +18,34 @@ describe('ExceptionItemCardConditions', () => { ); - + // Text is gonna look a bit off unformatted expect( wrapper.find('[data-test-subj="exceptionItemConditions-condition"]').at(0).text() diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_conditions.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_conditions.tsx index 7f5376e3a856b..4b884d2de4809 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_conditions.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_conditions.tsx @@ -6,15 +6,13 @@ */ import React, { memo, useCallback } from 'react'; -import { - EuiExpression, - EuiToken, - EuiFlexGroup, - EuiFlexItem, - EuiBadge, -} from '@elastic/eui'; +import { EuiExpression, EuiToken, EuiFlexGroup, EuiFlexItem, EuiBadge } from '@elastic/eui'; import styled from 'styled-components'; -import { ExceptionListItemSchema, ListOperatorTypeEnum, NonEmptyNestedEntriesArray } from '@kbn/securitysolution-io-ts-list-types'; +import { + ExceptionListItemSchema, + ListOperatorTypeEnum, + NonEmptyNestedEntriesArray, +} from '@kbn/securitysolution-io-ts-list-types'; import * as i18n from './translations'; @@ -65,40 +63,34 @@ export const ExceptionItemCardConditions = memo( const getNestedEntriesContent = useCallback( (type: string, nestedEntries: NonEmptyNestedEntriesArray) => { if (type === 'nested' && nestedEntries.length) { - return nestedEntries.map( - (entry) => { - const { - field: nestedField, - type: nestedType, - operator: nestedOperator, - } = entry; - const nestedValue = "value" in entry ? entry.value : ''; + return nestedEntries.map((entry) => { + const { field: nestedField, type: nestedType, operator: nestedOperator } = entry; + const nestedValue = 'value' in entry ? entry.value : ''; - return ( - - - - - - - - - - - - ); - } - ); + return ( + + + + + + + + + + + + ); + }); } }, [] @@ -107,13 +99,10 @@ export const ExceptionItemCardConditions = memo( return (
{entries.map((entry, index) => { - const { - field, - type - } = entry; - const value = "value" in entry ? entry.value : ''; - const nestedEntries = "entries" in entry ? entry.entries : []; - const operator = "operator" in entry ? entry.operator : ''; + const { field, type } = entry; + const value = 'value' in entry ? entry.value : ''; + const nestedEntries = 'entries' in entry ? entry.entries : []; + const operator = 'operator' in entry ? entry.operator : ''; return (
@@ -121,7 +110,7 @@ export const ExceptionItemCardConditions = memo( { key: 'edit', icon: 'pencil', label: i18n.EXCEPTION_ITEM_EDIT_BUTTON, - onClick: jest.fn() + onClick: jest.fn(), }, { key: 'delete', icon: 'trash', label: i18n.EXCEPTION_ITEM_DELETE_BUTTON, - onClick: jest.fn() + onClick: jest.fn(), }, ]} /> ); - expect( - wrapper.find('[data-test-subj="exceptionItemHeader-title"]').at(0).text() - ).toEqual('some name'); + expect(wrapper.find('[data-test-subj="exceptionItemHeader-title"]').at(0).text()).toEqual( + 'some name' + ); }); it('it displays actions', () => { @@ -59,29 +59,32 @@ describe('ExceptionItemCardHeader', () => { ); - + // click on popover - wrapper.find('button[data-test-subj="exceptionItemHeader-actionButton"]').at(0).simulate('click'); + wrapper + .find('button[data-test-subj="exceptionItemHeader-actionButton"]') + .at(0) + .simulate('click'); expect(wrapper.find('button[data-test-subj="exceptionItemHeader-actionItem"]')).toHaveLength(2); - + wrapper.find('button[data-test-subj="exceptionItemHeader-actionItem"]').at(0).simulate('click'); expect(handleEdit).toHaveBeenCalled(); @@ -96,26 +99,29 @@ describe('ExceptionItemCardHeader', () => { ); - - expect(wrapper.find('button[data-test-subj="exceptionItemHeader-actionButton"]').at(0).props().disabled).toBeTruthy(); + + expect( + wrapper.find('button[data-test-subj="exceptionItemHeader-actionButton"]').at(0).props() + .disabled + ).toBeTruthy(); }); }); diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_header.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_header.tsx index 7a93a0b9258ca..ea69fb36068be 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_header.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_header.tsx @@ -6,12 +6,21 @@ */ import React, { memo, useMemo, useState } from 'react'; -import { EuiButtonIcon, EuiContextMenuPanelProps, EuiContextMenuPanel, EuiFlexGroup, EuiFlexItem, EuiPopover, EuiTitle, EuiContextMenuItem } from '@elastic/eui'; +import { + EuiButtonIcon, + EuiContextMenuPanelProps, + EuiContextMenuPanel, + EuiFlexGroup, + EuiFlexItem, + EuiPopover, + EuiTitle, + EuiContextMenuItem, +} from '@elastic/eui'; import { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; export interface ExceptionItemCardHeaderProps { item: ExceptionListItemSchema; - actions: Array<{ key: string; icon: string; label: string; onClick: () => void; }>; + actions: Array<{ key: string; icon: string; label: string; onClick: () => void }>; disableActions?: boolean; dataTestSubj: string; } @@ -36,9 +45,9 @@ export const ExceptionItemCardHeader = memo( > {action.label} - )) + )); }, []); - + return ( @@ -48,7 +57,7 @@ export const ExceptionItemCardHeader = memo( ( onClick={onItemActionsClick} data-test-subj={`${dataTestSubj}-actionButton`} /> - )} + } panelPaddingSize="none" isOpen={isPopoverOpen} closePopover={onClosePopover} data-test-subj={`${dataTestSubj}-items`} > - + diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_meta.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_meta.tsx index 1153f49ae6088..8a1d8c00a1aec 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_meta.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_meta.tsx @@ -19,7 +19,6 @@ export interface ExceptionItemCardMetaInfoProps { export const ExceptionItemCardMetaInfo = memo( ({ item, dataTestSubj }) => { - return ( ( data-test-subj={dataTestSubj} > - - )} + + } value2={item.created_by} dataTestSubj={`${dataTestSubj}-createdBy`} /> @@ -43,13 +41,13 @@ export const ExceptionItemCardMetaInfo = memo( - )} + } value2={item.updated_by} dataTestSubj={`${dataTestSubj}-updatedBy`} /> @@ -70,36 +68,33 @@ interface MetaInfoDetailsProps { const MetaInfoDetails = memo(({ label, value1, value2, dataTestSubj }) => { return ( - + - + - {value1} + + {value1} + - {i18n.EXCEPTION_ITEM_META_BY} + + {i18n.EXCEPTION_ITEM_META_BY} + - - {value2} + + + {value2} + diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/index.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/index.tsx index c65fca36e5b7d..c520d4f59fb43 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/index.tsx @@ -66,7 +66,7 @@ const ExceptionItemCardComponent = ({ return ( - + @@ -95,20 +95,20 @@ const ExceptionItemCardComponent = ({ /> - + {formattedComments.length > 0 && ( + buttonContent={ + {i18n.exceptionItemCommentsAccordion(formattedComments.length)} - )} + } arrowDisplay="none" data-test-subj="exceptionsViewerCommentAccordion" > diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/translations.ts b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/translations.ts index 793bd8984a3eb..bbd5f2fbaa155 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/translations.ts +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/translations.ts @@ -7,25 +7,40 @@ import { i18n } from '@kbn/i18n'; -export const EXCEPTION_ITEM_EDIT_BUTTON = i18n.translate('xpack.securitySolution.exceptions.exceptionItem.editItemButton', { - defaultMessage: 'Edit item', -}); +export const EXCEPTION_ITEM_EDIT_BUTTON = i18n.translate( + 'xpack.securitySolution.exceptions.exceptionItem.editItemButton', + { + defaultMessage: 'Edit item', + } +); -export const EXCEPTION_ITEM_DELETE_BUTTON = i18n.translate('xpack.securitySolution.exceptions.exceptionItem.deleteItemButton', { - defaultMessage: 'Delete item', -}); +export const EXCEPTION_ITEM_DELETE_BUTTON = i18n.translate( + 'xpack.securitySolution.exceptions.exceptionItem.deleteItemButton', + { + defaultMessage: 'Delete item', + } +); -export const EXCEPTION_ITEM_CREATED_LABEL = i18n.translate('xpack.securitySolution.exceptions.exceptionItem.createdLabel', { - defaultMessage: 'Created', -}); +export const EXCEPTION_ITEM_CREATED_LABEL = i18n.translate( + 'xpack.securitySolution.exceptions.exceptionItem.createdLabel', + { + defaultMessage: 'Created', + } +); -export const EXCEPTION_ITEM_UPDATED_LABEL = i18n.translate('xpack.securitySolution.exceptions.exceptionItem.updatedLabel', { - defaultMessage: 'Updated', -}); +export const EXCEPTION_ITEM_UPDATED_LABEL = i18n.translate( + 'xpack.securitySolution.exceptions.exceptionItem.updatedLabel', + { + defaultMessage: 'Updated', + } +); -export const EXCEPTION_ITEM_META_BY = i18n.translate('xpack.securitySolution.exceptions.exceptionItem.metaDetailsBy', { - defaultMessage: 'by', -}); +export const EXCEPTION_ITEM_META_BY = i18n.translate( + 'xpack.securitySolution.exceptions.exceptionItem.metaDetailsBy', + { + defaultMessage: 'by', + } +); export const exceptionItemCommentsAccordion = (comments: number) => i18n.translate('xpack.securitySolution.exceptions.exceptionItem.showCommentsLabel', { @@ -39,7 +54,7 @@ export const DESCRIPTOR_WHEN = i18n.translate( defaultMessage: 'WHEN', } ); - + export const CONDITION_OPERATOR_TYPE_MATCH = i18n.translate( 'xpack.securitySolution.exceptions.exceptionItem.conditions.matchOperator', { @@ -96,6 +111,9 @@ export const CONDITION_OPERATOR_TYPE_LIST = i18n.translate( } ); -export const CONDITION_AND = i18n.translate('xpack.securitySolution.exceptions.exceptionItem.conditions.and', { - defaultMessage: 'AND', -}); +export const CONDITION_AND = i18n.translate( + 'xpack.securitySolution.exceptions.exceptionItem.conditions.and', + { + defaultMessage: 'AND', + } +); From 7718b62e52a59de7bca60f33b49a40a520e16448 Mon Sep 17 00:00:00 2001 From: Yara Tercero Date: Tue, 28 Jun 2022 14:50:30 -0700 Subject: [PATCH 08/16] fixing up lint and tests --- .../cypress/screens/rule_details.ts | 2 +- .../exception_item_card_conditions.tsx | 2 +- .../exception_item_card_header.test.tsx | 5 +- .../exception_item_card_header.tsx | 6 +- .../viewer/exception_item_card/index.test.tsx | 6 +- .../viewer/exceptions_viewer_items.test.tsx | 179 ++++++------------ .../viewer/exceptions_viewer_items.tsx | 2 +- 7 files changed, 65 insertions(+), 137 deletions(-) diff --git a/x-pack/plugins/security_solution/cypress/screens/rule_details.ts b/x-pack/plugins/security_solution/cypress/screens/rule_details.ts index c7726ac40e83d..afead8151d23d 100644 --- a/x-pack/plugins/security_solution/cypress/screens/rule_details.ts +++ b/x-pack/plugins/security_solution/cypress/screens/rule_details.ts @@ -74,7 +74,7 @@ export const RISK_SCORE_OVERRIDE_DETAILS = 'Risk score override'; export const REFERENCE_URLS_DETAILS = 'Reference URLs'; -export const REMOVE_EXCEPTION_BTN = '[data-test-subj="exceptionsViewerDeleteBtn"]'; +export const REMOVE_EXCEPTION_BTN = '[data-test-subj="exceptionItemCardHeader-actionItem-delete"]'; export const RULE_SWITCH = '[data-test-subj="ruleSwitch"]'; diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_conditions.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_conditions.tsx index 4b884d2de4809..5b37cb1a52a05 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_conditions.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_conditions.tsx @@ -93,7 +93,7 @@ export const ExceptionItemCardConditions = memo( }); } }, - [] + [dataTestSubj] ); return ( diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_header.test.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_header.test.tsx index d6fc3c8c76919..9703ff3e38ddd 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_header.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_header.test.tsx @@ -83,12 +83,11 @@ describe('ExceptionItemCardHeader', () => { .find('button[data-test-subj="exceptionItemHeader-actionButton"]') .at(0) .simulate('click'); - expect(wrapper.find('button[data-test-subj="exceptionItemHeader-actionItem"]')).toHaveLength(2); - wrapper.find('button[data-test-subj="exceptionItemHeader-actionItem"]').at(0).simulate('click'); + wrapper.find('button[data-test-subj="exceptionItemHeader-actionItem-edit"]').simulate('click'); expect(handleEdit).toHaveBeenCalled(); - wrapper.find('button[data-test-subj="exceptionItemHeader-actionItem"]').at(1).simulate('click'); + wrapper.find('button[data-test-subj="exceptionItemHeader-actionItem-delete"]').simulate('click'); expect(handleDelete).toHaveBeenCalled(); }); diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_header.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_header.tsx index ea69fb36068be..3389bd0cb29b9 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_header.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_header.tsx @@ -29,13 +29,13 @@ export const ExceptionItemCardHeader = memo( ({ item, actions, disableActions = false, dataTestSubj }) => { const [isPopoverOpen, setIsPopoverOpen] = useState(false); - const onItemActionsClick = () => setIsPopoverOpen((isPopoverOpen) => !isPopoverOpen); + const onItemActionsClick = () => setIsPopoverOpen((isOpen) => !isOpen); const onClosePopover = () => setIsPopoverOpen(false); const itemActions = useMemo((): EuiContextMenuPanelProps['items'] => { return actions.map((action) => ( { @@ -46,7 +46,7 @@ export const ExceptionItemCardHeader = memo( {action.label} )); - }, []); + }, [dataTestSubj, actions]); return ( diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/index.test.tsx index c22d405df62a2..46a0f74642c08 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/index.test.tsx @@ -116,8 +116,7 @@ describe('ExceptionItemCard', () => { .at(0) .simulate('click'); wrapper - .find('button[data-test-subj="exceptionItemCardHeader-actionItem"]') - .at(0) + .find('button[data-test-subj="exceptionItemCardHeader-actionItem-edit"]') .simulate('click'); expect(mockOnEditException).toHaveBeenCalledWith(getExceptionListItemSchemaMock()); @@ -146,8 +145,7 @@ describe('ExceptionItemCard', () => { .at(0) .simulate('click'); wrapper - .find('button[data-test-subj="exceptionItemCardHeader-actionItem"]') - .at(1) + .find('button[data-test-subj="exceptionItemCardHeader-actionItem-delete"]') .simulate('click'); expect(mockOnDeleteException).toHaveBeenCalledWith({ diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exceptions_viewer_items.test.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exceptions_viewer_items.test.tsx index dcbdc56cd547b..05e419d2e6b93 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exceptions_viewer_items.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exceptions_viewer_items.test.tsx @@ -13,6 +13,7 @@ import * as i18n from '../translations'; import { getExceptionListItemSchemaMock } from '@kbn/lists-plugin/common/schemas/response/exception_list_item_schema.mock'; import { ExceptionsViewerItems } from './exceptions_viewer_items'; import { getMockTheme } from '../../../lib/kibana/kibana_react.mock'; +import { TestProviders } from '../../../mock'; const mockTheme = getMockTheme({ eui: { @@ -25,16 +26,18 @@ const mockTheme = getMockTheme({ describe('ExceptionsViewerItems', () => { it('it renders empty prompt if "showEmpty" is "true"', () => { const wrapper = mount( - + + + ); expect(wrapper.find('[data-test-subj="exceptionsEmptyPrompt"]').exists()).toBeTruthy(); @@ -49,18 +52,20 @@ describe('ExceptionsViewerItems', () => { it('it renders no search results found prompt if "showNoResults" is "true"', () => { const wrapper = mount( - - - + + + + + ); expect(wrapper.find('[data-test-subj="exceptionsEmptyPrompt"]').exists()).toBeTruthy(); @@ -73,18 +78,20 @@ describe('ExceptionsViewerItems', () => { it('it renders exceptions if "showEmpty" and "isInitLoading" is "false", and exceptions exist', () => { const wrapper = mount( - - - + + + + + ); expect(wrapper.find('[data-test-subj="exceptionsContainer"]').exists()).toBeTruthy(); @@ -93,99 +100,23 @@ describe('ExceptionsViewerItems', () => { it('it does not render exceptions if "isInitLoading" is "true"', () => { const wrapper = mount( - - - + + + + + ); expect(wrapper.find('[data-test-subj="exceptionsContainer"]').exists()).toBeFalsy(); expect(wrapper.find('[data-test-subj="exceptionsEmptyPrompt"]').exists()).toBeTruthy(); }); - - it('it does not render or badge for first exception displayed', () => { - const exception1 = getExceptionListItemSchemaMock(); - const exception2 = getExceptionListItemSchemaMock(); - exception2.id = 'newId'; - - const wrapper = mount( - - - - ); - - const firstExceptionItem = wrapper.find('[data-test-subj="exceptionItemContainer"]').at(0); - - expect(firstExceptionItem.find('[data-test-subj="exceptionItemOrBadge"]').exists()).toBeFalsy(); - }); - - it('it does render or badge with exception displayed', () => { - const exception1 = getExceptionListItemSchemaMock(); - const exception2 = getExceptionListItemSchemaMock(); - exception2.id = 'newId'; - - const wrapper = mount( - - - - ); - - const notFirstExceptionItem = wrapper.find('[data-test-subj="exceptionItemContainer"]').at(1); - - expect( - notFirstExceptionItem.find('[data-test-subj="exceptionItemOrBadge"]').exists() - ).toBeFalsy(); - }); - - it('it invokes "onDeleteException" when delete button is clicked', () => { - const mockOnDeleteException = jest.fn(); - - const wrapper = mount( - - - - ); - - wrapper.find('[data-test-subj="exceptionsViewerDeleteBtn"] button').at(0).simulate('click'); - - expect(mockOnDeleteException).toHaveBeenCalledWith({ - id: '1', - namespaceType: 'single', - }); - }); }); diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exceptions_viewer_items.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exceptions_viewer_items.tsx index b446ac723a8d2..e1d91ed0a0580 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exceptions_viewer_items.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exceptions_viewer_items.tsx @@ -76,7 +76,7 @@ const ExceptionsViewerItemsComponent: React.FC = ({ > {!isInitLoading && exceptions.length > 0 && - exceptions.map((exception, index) => ( + exceptions.map((exception) => ( Date: Tue, 28 Jun 2022 22:43:59 +0000 Subject: [PATCH 09/16] [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix' --- .../exception_item_card/exception_item_card_header.test.tsx | 4 +++- .../exceptions/viewer/exceptions_viewer_items.test.tsx | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_header.test.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_header.test.tsx index 9703ff3e38ddd..fe8811152e2e1 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_header.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_header.test.tsx @@ -87,7 +87,9 @@ describe('ExceptionItemCardHeader', () => { wrapper.find('button[data-test-subj="exceptionItemHeader-actionItem-edit"]').simulate('click'); expect(handleEdit).toHaveBeenCalled(); - wrapper.find('button[data-test-subj="exceptionItemHeader-actionItem-delete"]').simulate('click'); + wrapper + .find('button[data-test-subj="exceptionItemHeader-actionItem-delete"]') + .simulate('click'); expect(handleDelete).toHaveBeenCalled(); }); diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exceptions_viewer_items.test.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exceptions_viewer_items.test.tsx index 05e419d2e6b93..22c6e7dbf8ecf 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exceptions_viewer_items.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exceptions_viewer_items.test.tsx @@ -53,7 +53,7 @@ describe('ExceptionsViewerItems', () => { it('it renders no search results found prompt if "showNoResults" is "true"', () => { const wrapper = mount( - + Date: Tue, 28 Jun 2022 21:04:38 -0700 Subject: [PATCH 10/16] fixing cypress tests --- .../exceptions/exceptions_flyout.spec.ts | 9 +++----- .../cypress/screens/exceptions.ts | 2 -- .../cypress/screens/rule_details.ts | 4 ++++ .../cypress/tasks/rule_details.ts | 22 ++++++++++++++++++- 4 files changed, 28 insertions(+), 9 deletions(-) diff --git a/x-pack/plugins/security_solution/cypress/integration/exceptions/exceptions_flyout.spec.ts b/x-pack/plugins/security_solution/cypress/integration/exceptions/exceptions_flyout.spec.ts index 75d7696140368..48a7819a898f2 100644 --- a/x-pack/plugins/security_solution/cypress/integration/exceptions/exceptions_flyout.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/exceptions/exceptions_flyout.spec.ts @@ -13,7 +13,7 @@ import { createCustomRule } from '../../tasks/api_calls/rules'; import { goToRuleDetails } from '../../tasks/alerts_detection_rules'; import { esArchiverLoad, esArchiverResetKibana, esArchiverUnload } from '../../tasks/es_archiver'; import { login, visitWithoutDateRange } from '../../tasks/login'; -import { openExceptionFlyoutFromRuleSettings, goToExceptionsTab } from '../../tasks/rule_details'; +import { openExceptionFlyoutFromRuleSettings, goToExceptionsTab, editException } from '../../tasks/rule_details'; import { addExceptionEntryFieldMatchAnyValue, addExceptionEntryFieldValue, @@ -32,7 +32,6 @@ import { EXCEPTION_ITEM_CONTAINER, ADD_EXCEPTIONS_BTN, EXCEPTION_FIELD_LIST, - EDIT_EXCEPTIONS_BTN, EXCEPTION_EDIT_FLYOUT_SAVE_BTN, EXCEPTION_FLYOUT_VERSION_CONFLICT, EXCEPTION_FLYOUT_LIST_DELETED_ERROR, @@ -302,8 +301,7 @@ describe('Exceptions flyout', () => { context('When updating an item with version conflict', () => { it('Displays version conflict error', () => { - cy.get(EDIT_EXCEPTIONS_BTN).should('be.visible'); - cy.get(EDIT_EXCEPTIONS_BTN).click({ force: true }); + editException(); // update exception item via api updateExceptionListItem('simple_list_item', { @@ -334,8 +332,7 @@ describe('Exceptions flyout', () => { context('When updating an item for a list that has since been deleted', () => { it('Displays missing exception list error', () => { - cy.get(EDIT_EXCEPTIONS_BTN).should('be.visible'); - cy.get(EDIT_EXCEPTIONS_BTN).click({ force: true }); + editException(); // delete exception list via api deleteExceptionList(getExceptionList().list_id, getExceptionList().namespace_type); diff --git a/x-pack/plugins/security_solution/cypress/screens/exceptions.ts b/x-pack/plugins/security_solution/cypress/screens/exceptions.ts index 58c070b194002..4a98d59bc5a0f 100644 --- a/x-pack/plugins/security_solution/cypress/screens/exceptions.ts +++ b/x-pack/plugins/security_solution/cypress/screens/exceptions.ts @@ -5,8 +5,6 @@ * 2.0. */ -export const EDIT_EXCEPTIONS_BTN = '[data-test-subj="exceptionsViewerEditBtn"]'; - export const ADD_EXCEPTIONS_BTN = '[data-test-subj="exceptionsHeaderAddExceptionBtn"]'; export const CLOSE_ALERTS_CHECKBOX = diff --git a/x-pack/plugins/security_solution/cypress/screens/rule_details.ts b/x-pack/plugins/security_solution/cypress/screens/rule_details.ts index afead8151d23d..52f6135bb68ee 100644 --- a/x-pack/plugins/security_solution/cypress/screens/rule_details.ts +++ b/x-pack/plugins/security_solution/cypress/screens/rule_details.ts @@ -74,8 +74,12 @@ export const RISK_SCORE_OVERRIDE_DETAILS = 'Risk score override'; export const REFERENCE_URLS_DETAILS = 'Reference URLs'; +export const EXCEPTION_ITEM_ACTIONS_BUTTON = 'button[data-test-subj="exceptionItemHeader-actionButton"]'; + export const REMOVE_EXCEPTION_BTN = '[data-test-subj="exceptionItemCardHeader-actionItem-delete"]'; +export const EDIT_EXCEPTION_BTN = '[data-test-subj="exceptionItemCardHeader-actionItem-edit"]'; + export const RULE_SWITCH = '[data-test-subj="ruleSwitch"]'; export const RULE_SWITCH_LOADER = '[data-test-subj="rule-switch-loader"]'; diff --git a/x-pack/plugins/security_solution/cypress/tasks/rule_details.ts b/x-pack/plugins/security_solution/cypress/tasks/rule_details.ts index 7fda21016205a..4c6921c9e4554 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/rule_details.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/rule_details.ts @@ -29,6 +29,8 @@ import { INDEX_PATTERNS_DETAILS, DETAILS_TITLE, DETAILS_DESCRIPTION, + EXCEPTION_ITEM_ACTIONS_BUTTON, + EDIT_EXCEPTION_BTN, } from '../screens/rule_details'; import { addsFields, closeFieldsBrowser, filterFieldsBrowser } from './fields_browser'; @@ -96,8 +98,26 @@ export const goToExceptionsTab = () => { .should('be.visible'); }; +export const editException = () => { + cy.root() + .pipe(($el) => { + $el.find(EXCEPTION_ITEM_ACTIONS_BUTTON).trigger('click'); + return $el.find(EDIT_EXCEPTION_BTN); + }) + .should('be.visible'); + + cy.get(EDIT_EXCEPTION_BTN).click(); +}; + export const removeException = () => { - cy.get(REMOVE_EXCEPTION_BTN).click(); + cy.root() + .pipe(($el) => { + $el.find(EXCEPTION_ITEM_ACTIONS_BUTTON).trigger('click'); + return $el.find(REMOVE_EXCEPTION_BTN); + }) + .should('be.visible'); + + cy.get(REMOVE_EXCEPTION_BTN).click(); }; export const waitForTheRuleToBeExecuted = () => { From 6b9f609986c94893b25e705e68a37242187cbfb5 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Wed, 29 Jun 2022 04:59:28 +0000 Subject: [PATCH 11/16] [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix' --- .../integration/exceptions/exceptions_flyout.spec.ts | 6 +++++- .../security_solution/cypress/screens/rule_details.ts | 3 ++- .../plugins/security_solution/cypress/tasks/rule_details.ts | 4 ++-- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/security_solution/cypress/integration/exceptions/exceptions_flyout.spec.ts b/x-pack/plugins/security_solution/cypress/integration/exceptions/exceptions_flyout.spec.ts index 48a7819a898f2..9e08313e7e73f 100644 --- a/x-pack/plugins/security_solution/cypress/integration/exceptions/exceptions_flyout.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/exceptions/exceptions_flyout.spec.ts @@ -13,7 +13,11 @@ import { createCustomRule } from '../../tasks/api_calls/rules'; import { goToRuleDetails } from '../../tasks/alerts_detection_rules'; import { esArchiverLoad, esArchiverResetKibana, esArchiverUnload } from '../../tasks/es_archiver'; import { login, visitWithoutDateRange } from '../../tasks/login'; -import { openExceptionFlyoutFromRuleSettings, goToExceptionsTab, editException } from '../../tasks/rule_details'; +import { + openExceptionFlyoutFromRuleSettings, + goToExceptionsTab, + editException, +} from '../../tasks/rule_details'; import { addExceptionEntryFieldMatchAnyValue, addExceptionEntryFieldValue, diff --git a/x-pack/plugins/security_solution/cypress/screens/rule_details.ts b/x-pack/plugins/security_solution/cypress/screens/rule_details.ts index 52f6135bb68ee..81cad4ffa0ede 100644 --- a/x-pack/plugins/security_solution/cypress/screens/rule_details.ts +++ b/x-pack/plugins/security_solution/cypress/screens/rule_details.ts @@ -74,7 +74,8 @@ export const RISK_SCORE_OVERRIDE_DETAILS = 'Risk score override'; export const REFERENCE_URLS_DETAILS = 'Reference URLs'; -export const EXCEPTION_ITEM_ACTIONS_BUTTON = 'button[data-test-subj="exceptionItemHeader-actionButton"]'; +export const EXCEPTION_ITEM_ACTIONS_BUTTON = + 'button[data-test-subj="exceptionItemHeader-actionButton"]'; export const REMOVE_EXCEPTION_BTN = '[data-test-subj="exceptionItemCardHeader-actionItem-delete"]'; diff --git a/x-pack/plugins/security_solution/cypress/tasks/rule_details.ts b/x-pack/plugins/security_solution/cypress/tasks/rule_details.ts index 4c6921c9e4554..8cebebbca75f4 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/rule_details.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/rule_details.ts @@ -106,7 +106,7 @@ export const editException = () => { }) .should('be.visible'); - cy.get(EDIT_EXCEPTION_BTN).click(); + cy.get(EDIT_EXCEPTION_BTN).click(); }; export const removeException = () => { @@ -117,7 +117,7 @@ export const removeException = () => { }) .should('be.visible'); - cy.get(REMOVE_EXCEPTION_BTN).click(); + cy.get(REMOVE_EXCEPTION_BTN).click(); }; export const waitForTheRuleToBeExecuted = () => { From 611cc254df810e5288bbf3dbe8be2d9c590e32df Mon Sep 17 00:00:00 2001 From: Yara Tercero Date: Thu, 7 Jul 2022 14:04:15 -0700 Subject: [PATCH 12/16] updating per PR feedback --- .../exception_item_card_conditions.test.tsx | 55 ++++++++++++++++++- .../exception_item_card_conditions.tsx | 36 +++++++++++- .../exception_item_card_meta.test.tsx | 2 +- .../exception_item_card_meta.tsx | 17 ++++-- .../viewer/exception_item_card/index.tsx | 5 +- .../exception_item_card/translations.ts | 23 +++++--- 6 files changed, 119 insertions(+), 19 deletions(-) diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_conditions.test.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_conditions.test.tsx index fa770fd20cb6b..c83bb7e43e527 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_conditions.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_conditions.test.tsx @@ -12,6 +12,56 @@ import { TestProviders } from '../../../../mock'; import { ExceptionItemCardConditions } from './exception_item_card_conditions'; describe('ExceptionItemCardConditions', () => { + it('it includes os condition if one exists', () => { + const wrapper = mount( + + + + ); + + // Text is gonna look a bit off unformatted + expect( + wrapper.find('[data-test-subj="exceptionItemConditions-os"]').at(0).text() + ).toEqual(' OSIS Linux'); + expect( + wrapper.find('[data-test-subj="exceptionItemConditions-condition"]').at(0).text() + ).toEqual(' host.nameIS host'); + expect( + wrapper.find('[data-test-subj="exceptionItemConditions-condition"]').at(1).text() + ).toEqual('AND threat.indicator.portexists '); + expect( + wrapper.find('[data-test-subj="exceptionItemConditions-condition"]').at(2).text() + ).toEqual('AND file.Ext.code_signature validIS true'); + }); + it('it renders item conditions', () => { const wrapper = mount( @@ -47,9 +97,12 @@ describe('ExceptionItemCardConditions', () => { ); // Text is gonna look a bit off unformatted + expect( + wrapper.find('[data-test-subj="exceptionItemConditions-os"]').exists() + ).toBeFalsy(); expect( wrapper.find('[data-test-subj="exceptionItemConditions-condition"]').at(0).text() - ).toEqual('WHEN host.nameIS host'); + ).toEqual(' host.nameIS host'); expect( wrapper.find('[data-test-subj="exceptionItemConditions-condition"]').at(1).text() ).toEqual('AND threat.indicator.portexists '); diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_conditions.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_conditions.tsx index 5b37cb1a52a05..e0b3f87fba48e 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_conditions.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_conditions.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { memo, useCallback } from 'react'; +import React, { memo, useMemo, useCallback } from 'react'; import { EuiExpression, EuiToken, EuiFlexGroup, EuiFlexItem, EuiBadge } from '@elastic/eui'; import styled from 'styled-components'; import { @@ -16,6 +16,13 @@ import { import * as i18n from './translations'; +const OS_LABELS = Object.freeze({ + linux: i18n.OS_LINUX, + mac: i18n.OS_MAC, + macos: i18n.OS_MAC, + windows: i18n.OS_WINDOWS, +}); + const OPERATOR_TYPE_LABELS_INCLUDED = Object.freeze({ [ListOperatorTypeEnum.NESTED]: i18n.CONDITION_OPERATOR_TYPE_NESTED, [ListOperatorTypeEnum.MATCH_ANY]: i18n.CONDITION_OPERATOR_TYPE_MATCH_ANY, @@ -39,13 +46,28 @@ const EuiFlexItemNested = styled(EuiFlexItem)` margin-top: 6px !important; `; +const StyledCondition = styled('span')` + margin-right: 6px; +`; + export interface CriteriaConditionsProps { entries: ExceptionListItemSchema['entries']; dataTestSubj: string; + os?: ExceptionListItemSchema['os_types']; } export const ExceptionItemCardConditions = memo( - ({ entries, dataTestSubj }) => { + ({ os, entries, dataTestSubj }) => { + const osLabel = useMemo(() => { + if (os != null && os.length > 0) { + return os + .map((osValue) => OS_LABELS[osValue as keyof typeof OS_LABELS] ?? osValue) + .join(', '); + } + + return null; + }, [os]); + const getEntryValue = (type: string, value: string | string[] | undefined) => { if (type === 'match_any' && Array.isArray(value)) { return value.map((currentValue) => {currentValue}); @@ -98,6 +120,14 @@ export const ExceptionItemCardConditions = memo( return (
+ {osLabel != null && ( +
+ + + + +
+ )} {entries.map((entry, index) => { const { field, type } = entry; const value = 'value' in entry ? entry.value : ''; @@ -108,7 +138,7 @@ export const ExceptionItemCardConditions = memo(
{i18n.CONDITION_AND}} value={field} color={index === 0 ? 'primary' : 'subdued'} /> diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_meta.test.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_meta.test.tsx index 5080e1074b7dd..b5a24ef3e472d 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_meta.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_meta.test.tsx @@ -25,7 +25,7 @@ describe('ExceptionItemCardMetaInfo', () => { expect( wrapper.find('[data-test-subj="exceptionItemMeta-createdBy-value1"]').at(0).text() - ).toEqual('04/20/20'); + ).toEqual('Apr 20, 2020 @ 15:25:31.830'); expect( wrapper.find('[data-test-subj="exceptionItemMeta-createdBy-value2"]').at(0).text() ).toEqual('some user'); diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_meta.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_meta.tsx index 8a1d8c00a1aec..004c6dca00a59 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_meta.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_meta.tsx @@ -6,12 +6,17 @@ */ import React, { memo } from 'react'; -import { EuiAvatar, EuiBetaBadge, EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; +import { EuiAvatar, EuiBadge, EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; import { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; +import styled from 'styled-components'; import * as i18n from './translations'; import { FormattedDate, FormattedRelativePreferenceDate } from '../../../formatted_date'; +const EuiFlexItemStyled = styled(EuiFlexItem)` + padding-top: 4px !important; +`; + export interface ExceptionItemCardMetaInfoProps { item: ExceptionListItemSchema; dataTestSubj: string; @@ -31,7 +36,7 @@ export const ExceptionItemCardMetaInfo = memo( fieldName="created_by" label={i18n.EXCEPTION_ITEM_CREATED_LABEL} value1={ - + } value2={item.created_by} dataTestSubj={`${dataTestSubj}-createdBy`} @@ -70,13 +75,15 @@ const MetaInfoDetails = memo(({ label, value1, value2, dat return ( - + + {label} + - + {value1} - + {i18n.EXCEPTION_ITEM_META_BY} diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/index.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/index.tsx index c520d4f59fb43..13e1d679a44f9 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/index.tsx @@ -60,8 +60,8 @@ const ExceptionItemCardComponent = ({ }, [exceptionItem.comments]); const disableItemActions = useMemo((): boolean => { - const foundItems = loadingItemIds.filter(({ id }) => id === exceptionItem.id); - return disableActions || foundItems.length > 0; + const foundItems = loadingItemIds.some(({ id }) => id === exceptionItem.id); + return disableActions || foundItems; }, [loadingItemIds, exceptionItem.id, disableActions]); return ( @@ -96,6 +96,7 @@ const ExceptionItemCardComponent = ({ diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/translations.ts b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/translations.ts index bbd5f2fbaa155..0947603bff59a 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/translations.ts +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/translations.ts @@ -48,13 +48,6 @@ export const exceptionItemCommentsAccordion = (comments: number) => defaultMessage: 'Show {comments, plural, =1 {comment} other {comments}} ({comments})', }); -export const DESCRIPTOR_WHEN = i18n.translate( - 'xpack.securitySolution.exceptions.exceptionItem.conditions.whenDescriptor', - { - defaultMessage: 'WHEN', - } -); - export const CONDITION_OPERATOR_TYPE_MATCH = i18n.translate( 'xpack.securitySolution.exceptions.exceptionItem.conditions.matchOperator', { @@ -117,3 +110,19 @@ export const CONDITION_AND = i18n.translate( defaultMessage: 'AND', } ); + +export const CONDITION_OS = i18n.translate('xpack.securitySolution.exceptions.exceptionItem.conditions.os', { + defaultMessage: 'OS', +}); + +export const OS_WINDOWS = i18n.translate('xpack.securitySolution.exceptions.exceptionItem.conditions.windows', { + defaultMessage: 'Windows', +}); + +export const OS_LINUX = i18n.translate('xpack.securitySolution.exceptions.exceptionItem.conditions.linux', { + defaultMessage: 'Linux', +}); + +export const OS_MAC = i18n.translate('xpack.securitySolution.exceptions.exceptionItem.conditions.macos', { + defaultMessage: 'Mac', +}); From 2cc3460b44633dee88e9d0852057dc1e0e16e236 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Thu, 7 Jul 2022 21:09:59 +0000 Subject: [PATCH 13/16] [CI] Auto-commit changed files from 'node scripts/precommit_hook.js --ref HEAD~1..HEAD --fix' --- .../exception_item_card_conditions.test.tsx | 10 +++--- .../exception_item_card_conditions.tsx | 6 ++-- .../exception_item_card_meta.tsx | 4 +-- .../exception_item_card/translations.ts | 36 ++++++++++++------- 4 files changed, 33 insertions(+), 23 deletions(-) diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_conditions.test.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_conditions.test.tsx index c83bb7e43e527..dd0249958949f 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_conditions.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_conditions.test.tsx @@ -48,9 +48,9 @@ describe('ExceptionItemCardConditions', () => { ); // Text is gonna look a bit off unformatted - expect( - wrapper.find('[data-test-subj="exceptionItemConditions-os"]').at(0).text() - ).toEqual(' OSIS Linux'); + expect(wrapper.find('[data-test-subj="exceptionItemConditions-os"]').at(0).text()).toEqual( + ' OSIS Linux' + ); expect( wrapper.find('[data-test-subj="exceptionItemConditions-condition"]').at(0).text() ).toEqual(' host.nameIS host'); @@ -97,9 +97,7 @@ describe('ExceptionItemCardConditions', () => { ); // Text is gonna look a bit off unformatted - expect( - wrapper.find('[data-test-subj="exceptionItemConditions-os"]').exists() - ).toBeFalsy(); + expect(wrapper.find('[data-test-subj="exceptionItemConditions-os"]').exists()).toBeFalsy(); expect( wrapper.find('[data-test-subj="exceptionItemConditions-condition"]').at(0).text() ).toEqual(' host.nameIS host'); diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_conditions.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_conditions.tsx index e0b3f87fba48e..24cbdd5061943 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_conditions.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_conditions.tsx @@ -64,7 +64,7 @@ export const ExceptionItemCardConditions = memo( .map((osValue) => OS_LABELS[osValue as keyof typeof OS_LABELS] ?? osValue) .join(', '); } - + return null; }, [os]); @@ -138,7 +138,9 @@ export const ExceptionItemCardConditions = memo(
{i18n.CONDITION_AND}} + description={ + index === 0 ? '' : {i18n.CONDITION_AND} + } value={field} color={index === 0 ? 'primary' : 'subdued'} /> diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_meta.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_meta.tsx index 004c6dca00a59..531e5deafeeca 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_meta.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_meta.tsx @@ -35,9 +35,7 @@ export const ExceptionItemCardMetaInfo = memo( - } + value1={} value2={item.created_by} dataTestSubj={`${dataTestSubj}-createdBy`} /> diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/translations.ts b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/translations.ts index 0947603bff59a..8d345c23fbf09 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/translations.ts +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/translations.ts @@ -111,18 +111,30 @@ export const CONDITION_AND = i18n.translate( } ); -export const CONDITION_OS = i18n.translate('xpack.securitySolution.exceptions.exceptionItem.conditions.os', { - defaultMessage: 'OS', -}); +export const CONDITION_OS = i18n.translate( + 'xpack.securitySolution.exceptions.exceptionItem.conditions.os', + { + defaultMessage: 'OS', + } +); -export const OS_WINDOWS = i18n.translate('xpack.securitySolution.exceptions.exceptionItem.conditions.windows', { - defaultMessage: 'Windows', -}); +export const OS_WINDOWS = i18n.translate( + 'xpack.securitySolution.exceptions.exceptionItem.conditions.windows', + { + defaultMessage: 'Windows', + } +); -export const OS_LINUX = i18n.translate('xpack.securitySolution.exceptions.exceptionItem.conditions.linux', { - defaultMessage: 'Linux', -}); +export const OS_LINUX = i18n.translate( + 'xpack.securitySolution.exceptions.exceptionItem.conditions.linux', + { + defaultMessage: 'Linux', + } +); -export const OS_MAC = i18n.translate('xpack.securitySolution.exceptions.exceptionItem.conditions.macos', { - defaultMessage: 'Mac', -}); +export const OS_MAC = i18n.translate( + 'xpack.securitySolution.exceptions.exceptionItem.conditions.macos', + { + defaultMessage: 'Mac', + } +); From be769ed7e4f1e88514de2606bf3cb90befb65db7 Mon Sep 17 00:00:00 2001 From: Yara Tercero Date: Thu, 7 Jul 2022 14:58:10 -0700 Subject: [PATCH 14/16] fix bug rendering initial item exception after creation --- .../exception_item_card_meta.tsx | 19 ++++++++++--------- .../components/exceptions/viewer/index.tsx | 2 +- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_meta.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_meta.tsx index 004c6dca00a59..93737ad6966b8 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_meta.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item_card/exception_item_card_meta.tsx @@ -13,10 +13,9 @@ import styled from 'styled-components'; import * as i18n from './translations'; import { FormattedDate, FormattedRelativePreferenceDate } from '../../../formatted_date'; -const EuiFlexItemStyled = styled(EuiFlexItem)` +const StyledCondition = styled('div')` padding-top: 4px !important; `; - export interface ExceptionItemCardMetaInfoProps { item: ExceptionListItemSchema; dataTestSubj: string; @@ -47,11 +46,13 @@ export const ExceptionItemCardMetaInfo = memo( fieldName="updated_by" label={i18n.EXCEPTION_ITEM_UPDATED_LABEL} value1={ - + + + } value2={item.updated_by} dataTestSubj={`${dataTestSubj}-updatedBy`} @@ -79,11 +80,11 @@ const MetaInfoDetails = memo(({ label, value1, value2, dat {label} - + {value1} - + {i18n.EXCEPTION_ITEM_META_BY} diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/index.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/index.tsx index b4425e2e165c7..aa16347d9133b 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/index.tsx @@ -328,7 +328,7 @@ const ExceptionsViewerComponent = ({ ); const showEmpty: boolean = - !isInitLoading && !loadingList && totalEndpointItems === 0 && totalDetectionsItems === 0; + !isInitLoading && !loadingList && exceptions.length === 0; const showNoResults: boolean = exceptions.length === 0 && (totalEndpointItems > 0 || totalDetectionsItems > 0); From f325a07e2390231dfaf30c86cd2dd28bbbd06482 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Thu, 7 Jul 2022 22:51:33 +0000 Subject: [PATCH 15/16] [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix' --- .../public/common/components/exceptions/viewer/index.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/index.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/index.tsx index aa16347d9133b..6e5d6a1c21fbd 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/index.tsx @@ -327,8 +327,7 @@ const ExceptionsViewerComponent = ({ `security/detections/rules/id/${encodeURI(ruleId)}/edit` ); - const showEmpty: boolean = - !isInitLoading && !loadingList && exceptions.length === 0; + const showEmpty: boolean = !isInitLoading && !loadingList && exceptions.length === 0; const showNoResults: boolean = exceptions.length === 0 && (totalEndpointItems > 0 || totalDetectionsItems > 0); From 99228394c1f18f916f71ab05185397b1de7b9bb0 Mon Sep 17 00:00:00 2001 From: Yara Tercero Date: Fri, 8 Jul 2022 12:28:31 -0700 Subject: [PATCH 16/16] fix cypress tests --- .../integration/exceptions/add_exception.spec.ts | 8 ++++---- .../cypress/screens/rule_details.ts | 2 +- .../cypress/tasks/rule_details.ts | 14 ++------------ 3 files changed, 7 insertions(+), 17 deletions(-) diff --git a/x-pack/plugins/security_solution/cypress/integration/exceptions/add_exception.spec.ts b/x-pack/plugins/security_solution/cypress/integration/exceptions/add_exception.spec.ts index d41e86fb9c96d..814f29622f51a 100644 --- a/x-pack/plugins/security_solution/cypress/integration/exceptions/add_exception.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/exceptions/add_exception.spec.ts @@ -14,7 +14,7 @@ import { addExceptionFromFirstAlert, goToClosedAlerts, goToOpenedAlerts } from ' import { createCustomRuleEnabled } from '../../tasks/api_calls/rules'; import { goToRuleDetails } from '../../tasks/alerts_detection_rules'; import { waitForAlertsToPopulate } from '../../tasks/create_new_rule'; -import { esArchiverLoad, esArchiverUnload } from '../../tasks/es_archiver'; +import { esArchiverLoad, esArchiverUnload, esArchiverResetKibana } from '../../tasks/es_archiver'; import { login, visitWithoutDateRange } from '../../tasks/login'; import { addsException, @@ -26,15 +26,15 @@ import { } from '../../tasks/rule_details'; import { DETECTIONS_RULE_MANAGEMENT_URL } from '../../urls/navigation'; -import { cleanKibana, deleteAlertsAndRules } from '../../tasks/common'; +import { deleteAlertsAndRules } from '../../tasks/common'; describe('Adds rule exception', () => { const NUMBER_OF_AUDITBEAT_EXCEPTIONS_ALERTS = '1 alert'; before(() => { - cleanKibana(); - login(); + esArchiverResetKibana(); esArchiverLoad('exceptions'); + login(); }); beforeEach(() => { diff --git a/x-pack/plugins/security_solution/cypress/screens/rule_details.ts b/x-pack/plugins/security_solution/cypress/screens/rule_details.ts index f047f69c44954..e8fd18c4449b1 100644 --- a/x-pack/plugins/security_solution/cypress/screens/rule_details.ts +++ b/x-pack/plugins/security_solution/cypress/screens/rule_details.ts @@ -79,7 +79,7 @@ export const RISK_SCORE_OVERRIDE_DETAILS = 'Risk score override'; export const REFERENCE_URLS_DETAILS = 'Reference URLs'; export const EXCEPTION_ITEM_ACTIONS_BUTTON = - 'button[data-test-subj="exceptionItemHeader-actionButton"]'; + 'button[data-test-subj="exceptionItemCardHeader-actionButton"]'; export const REMOVE_EXCEPTION_BTN = '[data-test-subj="exceptionItemCardHeader-actionItem-delete"]'; diff --git a/x-pack/plugins/security_solution/cypress/tasks/rule_details.ts b/x-pack/plugins/security_solution/cypress/tasks/rule_details.ts index 8cebebbca75f4..159f62778f74e 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/rule_details.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/rule_details.ts @@ -99,23 +99,13 @@ export const goToExceptionsTab = () => { }; export const editException = () => { - cy.root() - .pipe(($el) => { - $el.find(EXCEPTION_ITEM_ACTIONS_BUTTON).trigger('click'); - return $el.find(EDIT_EXCEPTION_BTN); - }) - .should('be.visible'); + cy.get(EXCEPTION_ITEM_ACTIONS_BUTTON).click(); cy.get(EDIT_EXCEPTION_BTN).click(); }; export const removeException = () => { - cy.root() - .pipe(($el) => { - $el.find(EXCEPTION_ITEM_ACTIONS_BUTTON).trigger('click'); - return $el.find(REMOVE_EXCEPTION_BTN); - }) - .should('be.visible'); + cy.get(EXCEPTION_ITEM_ACTIONS_BUTTON).click(); cy.get(REMOVE_EXCEPTION_BTN).click(); };