diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/alert_header_block.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/alert_header_block.test.tsx new file mode 100644 index 00000000000000..ebe68d31f0e603 --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/alert_header_block.test.tsx @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { render } from '@testing-library/react'; +import { AlertHeaderBlock } from './alert_header_block'; + +const title =
{'title'}
; +const children =
{'children'}
; +const dataTestSubj = 'TITLE_TEST_ID'; + +describe('', () => { + it('should render component', () => { + const { getByTestId } = render( + + {children} + + ); + + expect(getByTestId('TITLE_TEST_ID')).toHaveTextContent('title'); + expect(getByTestId('CHILDREN_TEST_ID')).toBeInTheDocument(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/alert_header_block.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/alert_header_block.tsx new file mode 100644 index 00000000000000..84259ffcf0eb62 --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/alert_header_block.tsx @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { ReactElement, ReactNode, VFC } from 'react'; +import React, { memo } from 'react'; +import { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiTitle } from '@elastic/eui'; + +export interface AlertHeaderBlockProps { + /** + * React component to render as the title + */ + title: ReactElement; + /** + * React component to render as the value + */ + children: ReactNode; + /** + * data-test-subj to use for the title + */ + ['data-test-subj']?: string; +} + +/** + * Reusable component for rendering a block with rounded edges, show a title and value below one another + */ +export const AlertHeaderBlock: VFC = memo( + ({ title, children, 'data-test-subj': dataTestSubj }) => ( + + + + +

{title}

+
+
+ {children} +
+
+ ) +); + +AlertHeaderBlock.displayName = 'AlertHeaderBlock'; diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/alert_header_title.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/alert_header_title.test.tsx index 4a0267a8dade84..6d72ca9e58dfac 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/alert_header_title.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/alert_header_title.test.tsx @@ -13,8 +13,10 @@ import { SEVERITY_VALUE_TEST_ID, FLYOUT_ALERT_HEADER_TITLE_TEST_ID, STATUS_BUTTON_TEST_ID, - ASSIGNEES_HEADER_TEST_ID, ALERT_SUMMARY_PANEL_TEST_ID, + ASSIGNEES_TEST_ID, + ASSIGNEES_EMPTY_TEST_ID, + NOTES_TITLE_TEST_ID, } from './test_ids'; import { AlertHeaderTitle } from './alert_header_title'; import moment from 'moment-timezone'; @@ -22,8 +24,10 @@ import { useDateFormat, useTimeZone } from '../../../../common/lib/kibana'; import { mockGetFieldsData } from '../../shared/mocks/mock_get_fields_data'; import { mockDataFormattedForFieldBrowser } from '../../shared/mocks/mock_data_formatted_for_field_browser'; import { TestProvidersComponent } from '../../../../common/mock'; +import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features'; jest.mock('../../../../common/lib/kibana'); +jest.mock('../../../../common/hooks/use_experimental_features'); moment.suppressDeprecationWarnings = true; moment.tz.setDefault('UTC'); @@ -51,7 +55,7 @@ describe('', () => { }); it('should render component', () => { - const { getByTestId } = renderHeader(mockContextValue); + const { getByTestId, queryByTestId } = renderHeader(mockContextValue); expect(getByTestId(HEADER_TEXT_TEST_ID)).toHaveTextContent('rule-name'); expect(getByTestId(SEVERITY_VALUE_TEST_ID)).toBeInTheDocument(); @@ -59,7 +63,8 @@ describe('', () => { expect(getByTestId(RISK_SCORE_VALUE_TEST_ID)).toBeInTheDocument(); expect(getByTestId(STATUS_BUTTON_TEST_ID)).toBeInTheDocument(); - expect(getByTestId(ASSIGNEES_HEADER_TEST_ID)).toBeInTheDocument(); + expect(getByTestId(ASSIGNEES_TEST_ID)).toBeInTheDocument(); + expect(queryByTestId(ASSIGNEES_EMPTY_TEST_ID)).not.toBeInTheDocument(); }); it('should render title correctly if flyout is in preview', () => { @@ -68,7 +73,15 @@ describe('', () => { expect(getByTestId(RISK_SCORE_VALUE_TEST_ID)).toBeInTheDocument(); expect(queryByTestId(STATUS_BUTTON_TEST_ID)).not.toBeInTheDocument(); - expect(getByTestId(ASSIGNEES_HEADER_TEST_ID)).toHaveTextContent('Assignees—'); + expect(getByTestId(ASSIGNEES_EMPTY_TEST_ID)).toBeInTheDocument(); + expect(queryByTestId(ASSIGNEES_TEST_ID)).not.toBeInTheDocument(); + }); + + it('should render notes section if experimental flag is enabled', () => { + (useIsExperimentalFeatureEnabled as jest.Mock).mockReturnValue(true); + + const { getByTestId } = renderHeader(mockContextValue); + expect(getByTestId(NOTES_TITLE_TEST_ID)).toBeInTheDocument(); }); it('should render fall back values if document is not alert', () => { diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/alert_header_title.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/alert_header_title.tsx index a54a3a027fad18..f128fada5fb290 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/alert_header_title.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/alert_header_title.tsx @@ -6,11 +6,12 @@ */ import React, { memo, useCallback, useMemo } from 'react'; -import { EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiPanel, useEuiTheme, EuiLink } from '@elastic/eui'; -import { css } from '@emotion/css'; +import { EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiLink } from '@elastic/eui'; import { ALERT_WORKFLOW_ASSIGNEE_IDS } from '@kbn/rule-data-utils'; import { i18n } from '@kbn/i18n'; import { FlyoutTitle } from '@kbn/security-solution-common'; +import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features'; +import { Notes } from './notes'; import { useRuleDetailsLink } from '../../shared/hooks/use_rule_details_link'; import { DocumentStatus } from './status'; import { DocumentSeverity } from './severity'; @@ -22,6 +23,11 @@ import { PreferenceFormattedDate } from '../../../../common/components/formatted import { FLYOUT_ALERT_HEADER_TITLE_TEST_ID, ALERT_SUMMARY_PANEL_TEST_ID } from './test_ids'; import { Assignees } from './assignees'; +// minWidth for each block, allows to switch for a 1 row 4 blocks to 2 rows with 2 block each +const blockStyles = { + minWidth: 280, +}; + /** * Alert details flyout right section header */ @@ -34,10 +40,13 @@ export const AlertHeaderTitle = memo(() => { refetchFlyoutData, getFieldsData, } = useDocumentDetailsContext(); + const securitySolutionNotesEnabled = useIsExperimentalFeatureEnabled( + 'securitySolutionNotesEnabled' + ); + const { isAlert, ruleName, timestamp, ruleId } = useBasicDataFromDetailsData( dataFormattedForFieldBrowser ); - const { euiTheme } = useEuiTheme(); const href = useRuleDetailsLink({ ruleId: !isPreview ? ruleId : null }); const ruleTitle = useMemo( @@ -89,27 +98,52 @@ export const AlertHeaderTitle = memo(() => { /> )} - - - + {securitySolutionNotesEnabled ? ( + + + + + + + + + + + + + + + + + + + + + + + ) : ( + + - + @@ -121,7 +155,7 @@ export const AlertHeaderTitle = memo(() => { /> - + )} ); }); diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/assignees.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/assignees.test.tsx index 161fc4db117de4..3c27165f8cc115 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/assignees.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/assignees.test.tsx @@ -10,8 +10,8 @@ import { render } from '@testing-library/react'; import { ASSIGNEES_ADD_BUTTON_TEST_ID, + ASSIGNEES_EMPTY_TEST_ID, ASSIGNEES_TITLE_TEST_ID, - ASSIGNEES_HEADER_TEST_ID, } from './test_ids'; import { Assignees } from './assignees'; @@ -180,6 +180,6 @@ describe('', () => { ); expect(queryByTestId(ASSIGNEES_ADD_BUTTON_TEST_ID)).not.toBeInTheDocument(); - expect(getByTestId(ASSIGNEES_HEADER_TEST_ID)).toHaveTextContent('Assignees—'); + expect(getByTestId(ASSIGNEES_EMPTY_TEST_ID)).toHaveTextContent('—'); }); }); diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/assignees.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/assignees.tsx index 140a7baab04ab9..aa8360c30d292d 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/assignees.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/assignees.tsx @@ -14,12 +14,12 @@ import { EuiFlexGroup, EuiFlexItem, EuiPopover, - EuiTitle, EuiToolTip, useGeneratedHtmlId, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; +import { AlertHeaderBlock } from './alert_header_block'; import { useSetAlertAssignees } from '../../../../common/components/toolbar/bulk_actions/use_set_alert_assignees'; import { getEmptyTagValue } from '../../../../common/components/empty_value'; import { ASSIGNEES_PANEL_WIDTH } from '../../../../common/components/assignees/constants'; @@ -32,7 +32,8 @@ import { useBulkGetUserProfiles } from '../../../../common/components/user_profi import { UsersAvatarsPanel } from '../../../../common/components/user_profiles/users_avatars_panel'; import { ASSIGNEES_ADD_BUTTON_TEST_ID, - ASSIGNEES_HEADER_TEST_ID, + ASSIGNEES_EMPTY_TEST_ID, + ASSIGNEES_TEST_ID, ASSIGNEES_TITLE_TEST_ID, } from './test_ids'; @@ -158,26 +159,19 @@ export const Assignees: FC = memo( ]); return ( - + } + data-test-subj={ASSIGNEES_TITLE_TEST_ID} > - - -

- -

-
-
{isPreview ? ( - getEmptyTagValue() +
{getEmptyTagValue()}
) : ( - + {assignedUsers && ( @@ -186,7 +180,7 @@ export const Assignees: FC = memo( {updateAssigneesPopover} )} - +
); } ); diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/notes.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/notes.test.tsx new file mode 100644 index 00000000000000..437fb5d9e21b24 --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/notes.test.tsx @@ -0,0 +1,154 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { render } from '@testing-library/react'; +import { DocumentDetailsContext } from '../../shared/context'; +import { NOTES_COUNT_TEST_ID, NOTES_LOADING_TEST_ID, NOTES_TITLE_TEST_ID } from './test_ids'; +import { FETCH_NOTES_ERROR, Notes } from './notes'; +import { mockContextValue } from '../../shared/mocks/mock_context'; +import { createMockStore, mockGlobalState, TestProviders } from '../../../../common/mock'; +import { ReqStatus } from '../../../../notes'; +import type { Note } from '../../../../../common/api/timeline'; + +const mockAddError = jest.fn(); +jest.mock('../../../../common/hooks/use_app_toasts', () => ({ + useAppToasts: () => ({ + addError: mockAddError, + }), +})); + +const mockDispatch = jest.fn(); +jest.mock('react-redux', () => { + const original = jest.requireActual('react-redux'); + return { + ...original, + useDispatch: () => mockDispatch, + }; +}); + +describe('', () => { + it('should render loading spinner', () => { + const store = createMockStore({ + ...mockGlobalState, + notes: { + ...mockGlobalState.notes, + status: { + ...mockGlobalState.notes.status, + fetchNotesByDocumentIds: ReqStatus.Loading, + }, + }, + }); + + const { getByTestId } = render( + + + + + + ); + + expect(mockDispatch).toHaveBeenCalled(); + expect(getByTestId(NOTES_TITLE_TEST_ID)).toBeInTheDocument(); + expect(getByTestId(NOTES_TITLE_TEST_ID)).toHaveTextContent('Notes'); + expect(getByTestId(NOTES_LOADING_TEST_ID)).toBeInTheDocument(); + }); + + it('should render number of notes', () => { + const contextValue = { + ...mockContextValue, + eventId: '1', + }; + + const { getByTestId } = render( + + + + + + ); + + expect(getByTestId(NOTES_TITLE_TEST_ID)).toBeInTheDocument(); + expect(getByTestId(NOTES_TITLE_TEST_ID)).toHaveTextContent('Notes'); + expect(getByTestId(NOTES_COUNT_TEST_ID)).toBeInTheDocument(); + expect(getByTestId(NOTES_COUNT_TEST_ID)).toHaveTextContent('1'); + }); + + it('should render number of notes in scientific notation for big numbers', () => { + const createMockNote = (noteId: string): Note => ({ + eventId: '1', // should be a valid id based on mockTimelineData + noteId, + note: 'note-1', + timelineId: 'timeline-1', + created: 1663882629000, + createdBy: 'elastic', + updated: 1663882629000, + updatedBy: 'elastic', + version: 'version', + }); + const mockEntities = [...Array(1000).keys()] + .map((i: number) => createMockNote(i.toString())) + .reduce((acc, entity) => { + // @ts-ignore + acc[entity.noteId] = entity; + return acc; + }, {}); + const mockIds = Object.keys(mockEntities); + + const store = createMockStore({ + ...mockGlobalState, + notes: { + ...mockGlobalState.notes, + entities: mockEntities, + ids: mockIds, + }, + }); + const contextValue = { + ...mockContextValue, + eventId: '1', + }; + + const { getByTestId } = render( + + + + + + ); + + expect(getByTestId(NOTES_COUNT_TEST_ID)).toHaveTextContent('1k'); + }); + + it('should render toast error', () => { + const store = createMockStore({ + ...mockGlobalState, + notes: { + ...mockGlobalState.notes, + status: { + ...mockGlobalState.notes.status, + fetchNotesByDocumentIds: ReqStatus.Failed, + }, + error: { + ...mockGlobalState.notes.error, + fetchNotesByDocumentIds: { type: 'http', status: 500 }, + }, + }, + }); + + render( + + + + + + ); + + expect(mockAddError).toHaveBeenCalledWith(null, { + title: FETCH_NOTES_ERROR, + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/notes.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/notes.tsx new file mode 100644 index 00000000000000..41f96c90f89ec4 --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/notes.tsx @@ -0,0 +1,87 @@ +/* + * 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, useEffect } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { i18n } from '@kbn/i18n'; +import { EuiLoadingSpinner } from '@elastic/eui'; +import { FormattedCount } from '../../../../common/components/formatted_number'; +import { useDocumentDetailsContext } from '../../shared/context'; +import { NOTES_COUNT_TEST_ID, NOTES_LOADING_TEST_ID, NOTES_TITLE_TEST_ID } from './test_ids'; +import type { State } from '../../../../common/store'; +import type { Note } from '../../../../../common/api/timeline'; +import { + fetchNotesByDocumentIds, + ReqStatus, + selectFetchNotesByDocumentIdsError, + selectFetchNotesByDocumentIdsStatus, + selectSortedNotesByDocumentId, +} from '../../../../notes/store/notes.slice'; +import { useAppToasts } from '../../../../common/hooks/use_app_toasts'; +import { AlertHeaderBlock } from './alert_header_block'; + +export const FETCH_NOTES_ERROR = i18n.translate( + 'xpack.securitySolution.notes.fetchNotesErrorLabel', + { + defaultMessage: 'Error fetching notes', + } +); + +/** + * Renders a block with the number of notes for the event + */ +export const Notes = memo(() => { + const dispatch = useDispatch(); + const { eventId } = useDocumentDetailsContext(); + const { addError: addErrorToast } = useAppToasts(); + + useEffect(() => { + dispatch(fetchNotesByDocumentIds({ documentIds: [eventId] })); + }, [dispatch, eventId]); + + const fetchStatus = useSelector((state: State) => selectFetchNotesByDocumentIdsStatus(state)); + const fetchError = useSelector((state: State) => selectFetchNotesByDocumentIdsError(state)); + + const notes: Note[] = useSelector((state: State) => + selectSortedNotesByDocumentId(state, { + documentId: eventId, + sort: { field: 'created', direction: 'desc' }, + }) + ); + + // show a toast if the fetch notes call fails + useEffect(() => { + if (fetchStatus === ReqStatus.Failed && fetchError) { + addErrorToast(null, { + title: FETCH_NOTES_ERROR, + }); + } + }, [addErrorToast, fetchError, fetchStatus]); + + return ( + + } + data-test-subj={NOTES_TITLE_TEST_ID} + > + {fetchStatus === ReqStatus.Loading ? ( + + ) : ( +
+ +
+ )} +
+ ); +}); + +Notes.displayName = 'Notes'; diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/risk_score.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/risk_score.tsx index dfa03cabd53e66..fba4343e974998 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/risk_score.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/risk_score.tsx @@ -6,9 +6,9 @@ */ import React, { memo } from 'react'; -import { EuiFlexGroup, EuiFlexItem, EuiTitle } from '@elastic/eui'; import { ALERT_RISK_SCORE } from '@kbn/rule-data-utils'; import { FormattedMessage } from '@kbn/i18n-react'; +import { AlertHeaderBlock } from './alert_header_block'; import { RISK_SCORE_TITLE_TEST_ID, RISK_SCORE_VALUE_TEST_ID } from './test_ids'; import { useDocumentDetailsContext } from '../../shared/context'; @@ -33,21 +33,17 @@ export const RiskScore = memo(() => { } return ( - - - -

- -

-
-
- - {alertRiskScore} - -
+ + } + data-test-subj={RISK_SCORE_TITLE_TEST_ID} + > + {alertRiskScore} + ); }); diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/status.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/status.tsx index 420eb7feafa4a3..601201f2e58e81 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/status.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/status.tsx @@ -9,7 +9,7 @@ import type { FC } from 'react'; import React, { useMemo } from 'react'; import { find } from 'lodash/fp'; import { FormattedMessage } from '@kbn/i18n-react'; -import { EuiFlexGroup, EuiFlexItem, EuiTitle } from '@elastic/eui'; +import { AlertHeaderBlock } from './alert_header_block'; import { getEmptyTagValue } from '../../../../common/components/empty_value'; import { SIGNAL_STATUS_FIELD_NAME } from '../../../../timelines/components/timeline/body/renderers/constants'; import { StatusPopoverButton } from './status_popover_button'; @@ -51,32 +51,28 @@ export const DocumentStatus: FC = () => { }, [browserFields, dataFormattedForFieldBrowser, eventId, scopeId]); return ( - - - -

- -

-
-
- - {!statusData || !hasData(statusData) || isPreview ? ( - getEmptyTagValue() - ) : ( - - - - )} - -
+ + } + data-test-subj={STATUS_TITLE_TEST_ID} + > + {!statusData || !hasData(statusData) || isPreview ? ( + getEmptyTagValue() + ) : ( + + + + )} + ); }; diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/test_ids.ts b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/test_ids.ts index d215f161724148..83f833a4b24ace 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/test_ids.ts +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/test_ids.ts @@ -33,7 +33,12 @@ export const RISK_SCORE_VALUE_TEST_ID = `${FLYOUT_HEADER_TEST_ID}RiskScoreValue` export const SHARE_BUTTON_TEST_ID = `${FLYOUT_HEADER_TEST_ID}ShareButton` as const; export const CHAT_BUTTON_TEST_ID = 'newChatByTitle' as const; -export const ASSIGNEES_HEADER_TEST_ID = `${FLYOUT_HEADER_TEST_ID}AssigneesHeader` as const; +export const NOTES_TITLE_TEST_ID = `${FLYOUT_HEADER_TEST_ID}NotesTitle` as const; +export const NOTES_COUNT_TEST_ID = `${FLYOUT_HEADER_TEST_ID}NotesCount` as const; +export const NOTES_LOADING_TEST_ID = `${FLYOUT_HEADER_TEST_ID}NotesLoading` as const; + +export const ASSIGNEES_EMPTY_TEST_ID = `${FLYOUT_HEADER_TEST_ID}AssigneesEmpty` as const; +export const ASSIGNEES_TEST_ID = `${FLYOUT_HEADER_TEST_ID}Assignees` as const; export const ASSIGNEES_TITLE_TEST_ID = `${FLYOUT_HEADER_TEST_ID}AssigneesTitle` as const; export const ASSIGNEES_ADD_BUTTON_TEST_ID = `${FLYOUT_HEADER_TEST_ID}AssigneesAddButton` as const; diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/expandable_flyout/alert_details_right_panel.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/expandable_flyout/alert_details_right_panel.cy.ts index 2d0d80af9c2426..ab8f5557a5fafc 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/expandable_flyout/alert_details_right_panel.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/expandable_flyout/alert_details_right_panel.cy.ts @@ -34,7 +34,6 @@ import { DOCUMENT_DETAILS_FLYOUT_HEADER_LINK_ICON, DOCUMENT_DETAILS_FLYOUT_HEADER_RISK_SCORE, DOCUMENT_DETAILS_FLYOUT_HEADER_RISK_SCORE_VALUE, - DOCUMENT_DETAILS_FLYOUT_HEADER_ASSIGNEES, DOCUMENT_DETAILS_FLYOUT_HEADER_SEVERITY_VALUE, DOCUMENT_DETAILS_FLYOUT_HEADER_STATUS, DOCUMENT_DETAILS_FLYOUT_HEADER_TITLE, @@ -42,6 +41,7 @@ import { DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB, DOCUMENT_DETAILS_FLYOUT_TABLE_TAB, DOCUMENT_DETAILS_FLYOUT_FOOTER_ISOLATE_HOST, + DOCUMENT_DETAILS_FLYOUT_HEADER_ASSIGNEES_TITLE, } from '../../../../screens/expandable_flyout/alert_details_right_panel'; import { closeFlyout, @@ -93,7 +93,11 @@ describe('Alert details expandable flyout right panel', { tags: ['@ess', '@serve .should('be.visible') .and('have.text', rule.risk_score); - cy.get(DOCUMENT_DETAILS_FLYOUT_HEADER_ASSIGNEES).should('have.text', 'Assignees'); + cy.get(DOCUMENT_DETAILS_FLYOUT_HEADER_ASSIGNEES_TITLE).should('have.text', 'Assignees'); + + // TODO uncomment when the securitySolutionNotesEnabled feature flag is removed + // cy.get(DOCUMENT_DETAILS_FLYOUT_HEADER_NOTES_TITLE).should('have.text', 'Notes'); + // cy.get(DOCUMENT_DETAILS_FLYOUT_HEADER_NOTES_VALUE).should('have.text', '0'); cy.get(DOCUMENT_DETAILS_FLYOUT_HEADER_SEVERITY_VALUE) .should('be.visible') diff --git a/x-pack/test/security_solution_cypress/cypress/screens/expandable_flyout/alert_details_right_panel.ts b/x-pack/test/security_solution_cypress/cypress/screens/expandable_flyout/alert_details_right_panel.ts index 41cd41eb83890e..e84a70cbea621c 100644 --- a/x-pack/test/security_solution_cypress/cypress/screens/expandable_flyout/alert_details_right_panel.ts +++ b/x-pack/test/security_solution_cypress/cypress/screens/expandable_flyout/alert_details_right_panel.ts @@ -48,8 +48,17 @@ export const DOCUMENT_DETAILS_FLYOUT_HEADER_RISK_SCORE_VALUE = getDataTestSubjec 'securitySolutionFlyoutHeaderRiskScoreValue' ); export const DOCUMENT_DETAILS_FLYOUT_HEADER_SEVERITY_VALUE = getDataTestSubjectSelector('severity'); -export const DOCUMENT_DETAILS_FLYOUT_HEADER_ASSIGNEES = getDataTestSubjectSelector( - 'securitySolutionFlyoutHeaderAssigneesHeader' +export const DOCUMENT_DETAILS_FLYOUT_HEADER_ASSIGNEES_TITLE = getDataTestSubjectSelector( + 'securitySolutionFlyoutHeaderAssigneesTitle' +); +export const DOCUMENT_DETAILS_FLYOUT_HEADER_ASSIGNEES_VALUE = getDataTestSubjectSelector( + 'securitySolutionFlyoutHeaderAssignees' +); +export const DOCUMENT_DETAILS_FLYOUT_HEADER_NOTES_TITLE = getDataTestSubjectSelector( + 'securitySolutionFlyoutHeaderAssigneesTitle' +); +export const DOCUMENT_DETAILS_FLYOUT_HEADER_NOTES_VALUE = getDataTestSubjectSelector( + 'securitySolutionFlyoutHeaderAssigneesValue' ); /* Footer */ diff --git a/x-pack/test/security_solution_cypress/cypress/tasks/alert_assignments.ts b/x-pack/test/security_solution_cypress/cypress/tasks/alert_assignments.ts index 460b9bf14ea331..eb2f81b84071af 100644 --- a/x-pack/test/security_solution_cypress/cypress/tasks/alert_assignments.ts +++ b/x-pack/test/security_solution_cypress/cypress/tasks/alert_assignments.ts @@ -27,7 +27,7 @@ import { ALERT_ASSIGNEES_SELECTABLE_OPTIONS, } from '../screens/alerts'; import { PAGE_TITLE } from '../screens/common/page'; -import { DOCUMENT_DETAILS_FLYOUT_HEADER_ASSIGNEES } from '../screens/expandable_flyout/alert_details_right_panel'; +import { DOCUMENT_DETAILS_FLYOUT_HEADER_ASSIGNEES_VALUE } from '../screens/expandable_flyout/alert_details_right_panel'; import { expandFirstAlertActions, selectFirstPageAlerts } from './alerts'; import { login } from './login'; import { visitWithTimeRange } from './navigation'; @@ -85,7 +85,7 @@ export const checkEmptyAssigneesStateInAlertsTable = () => { }; export const checkEmptyAssigneesStateInAlertDetailsFlyout = () => { - cy.get(DOCUMENT_DETAILS_FLYOUT_HEADER_ASSIGNEES).within(() => { + cy.get(DOCUMENT_DETAILS_FLYOUT_HEADER_ASSIGNEES_VALUE).within(() => { cy.get(ALERT_AVATARS_PANEL).children().should('have.length', 0); }); }; @@ -137,13 +137,13 @@ export const alertsTableShowsAssigneesBadgeForFirstAlert = (users: string[]) => }; export const alertDetailsFlyoutShowsAssignees = (users: string[]) => { - cy.get(DOCUMENT_DETAILS_FLYOUT_HEADER_ASSIGNEES).within(() => { + cy.get(DOCUMENT_DETAILS_FLYOUT_HEADER_ASSIGNEES_VALUE).within(() => { users.forEach((user) => cy.get(`.euiAvatar${ALERT_USER_AVATAR(user)}`).should('exist')); }); }; export const alertDetailsFlyoutShowsAssigneesBadge = (users: string[]) => { - cy.get(DOCUMENT_DETAILS_FLYOUT_HEADER_ASSIGNEES).within(() => { + cy.get(DOCUMENT_DETAILS_FLYOUT_HEADER_ASSIGNEES_VALUE).within(() => { cy.get(ALERT_ASSIGNEES_COUNT_BADGE).contains(users.length); users.forEach((user) => cy.get(`.euiAvatar${ALERT_USER_AVATAR(user)}`).should('not.exist')); });