diff --git a/x-pack/solutions/security/plugins/security_solution/public/attack_discovery/pages/results/take_action/index.test.tsx b/x-pack/solutions/security/plugins/security_solution/public/attack_discovery/pages/results/take_action/index.test.tsx index aeddb01a07b70..f8070edd43997 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/attack_discovery/pages/results/take_action/index.test.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/attack_discovery/pages/results/take_action/index.test.tsx @@ -5,37 +5,61 @@ * 2.0. */ -import { fireEvent, render, screen } from '@testing-library/react'; +import { fireEvent, render, screen, waitFor } from '@testing-library/react'; import React from 'react'; -import { TestProviders } from '../../../../common/mock/test_providers'; +import { useKibana } from '../../../../common/lib/kibana'; +import { TestProviders } from '../../../../common/mock'; import { mockAttackDiscovery } from '../../mock/mock_attack_discovery'; +import { getMockAttackDiscoveryAlerts } from '../../mock/mock_attack_discovery_alerts'; +import { useAssistantAvailability } from '../../../../assistant/use_assistant_availability'; import { TakeAction } from '.'; -// Mocks for hooks and dependencies -jest.mock('../../use_kibana_feature_flags', () => ({ - useKibanaFeatureFlags: () => ({ attackDiscoveryAlertsEnabled: true }), +const mockMutateAsyncBulk = jest.fn().mockResolvedValue({}); +const mockMutateAsyncStatus = jest.fn().mockResolvedValue({}); + +jest.mock('../../../../assistant/use_assistant_availability', () => ({ + useAssistantAvailability: jest.fn(), })); + +const mockUseAssistantAvailability = useAssistantAvailability as jest.Mock; + +jest.mock('../../../../common/lib/kibana', () => ({ + useKibana: jest.fn(), +})); + jest.mock('../../use_attack_discovery_bulk', () => ({ - useAttackDiscoveryBulk: () => ({ mutateAsync: jest.fn().mockResolvedValue({}) }), + useAttackDiscoveryBulk: jest.fn(() => ({ mutateAsync: mockMutateAsyncBulk })), })); -jest.mock('./use_update_alerts_status', () => ({ - useUpdateAlertsStatus: () => ({ mutateAsync: jest.fn().mockResolvedValue({}) }), + +jest.mock('../../use_kibana_feature_flags', () => ({ + useKibanaFeatureFlags: jest.fn(() => ({ attackDiscoveryAlertsEnabled: true })), })); + jest.mock('./use_add_to_case', () => ({ - useAddToNewCase: () => ({ disabled: false, onAddToNewCase: jest.fn() }), + useAddToNewCase: jest.fn(() => ({ disabled: false, onAddToNewCase: jest.fn() })), })); + jest.mock('./use_add_to_existing_case', () => ({ - useAddToExistingCase: () => ({ onAddToExistingCase: jest.fn() }), + useAddToExistingCase: jest.fn(() => ({ onAddToExistingCase: jest.fn() })), })); + jest.mock('../attack_discovery_panel/view_in_ai_assistant/use_view_in_ai_assistant', () => ({ - useViewInAiAssistant: () => ({ showAssistantOverlay: jest.fn(), disabled: false }), + useViewInAiAssistant: jest.fn(() => ({ showAssistantOverlay: jest.fn(), disabled: false })), +})); + +jest.mock('./use_update_alerts_status', () => ({ + useUpdateAlertsStatus: jest.fn(() => ({ mutateAsync: mockMutateAsyncStatus })), })); + jest.mock('../../utils/is_attack_discovery_alert', () => ({ isAttackDiscoveryAlert: (ad: { alertWorkflowStatus?: string }) => - ad && ad.alertWorkflowStatus !== undefined, + ad?.alertWorkflowStatus !== undefined, })); +/** helper function to open the popover */ +const openPopover = () => fireEvent.click(screen.getAllByTestId('takeActionPopoverButton')[0]); + const defaultProps = { attackDiscoveries: [mockAttackDiscovery], setSelectedAttackDiscoveries: jest.fn(), @@ -44,6 +68,47 @@ const defaultProps = { describe('TakeAction', () => { beforeEach(() => { jest.clearAllMocks(); + + (useKibana as jest.Mock).mockReturnValue({ + services: { + application: { + capabilities: { + assistant: { + show: true, + save: true, + }, + }, + }, + cases: { + helpers: { + canUseCases: jest.fn().mockReturnValue({ + all: true, + connectors: true, + create: true, + delete: true, + push: true, + read: true, + settings: true, + update: true, + createComment: true, + }), + }, + hooks: { + useCasesAddToExistingCase: jest.fn(), + useCasesAddToExistingCaseModal: jest.fn().mockReturnValue({ open: jest.fn() }), + useCasesAddToNewCaseFlyout: jest.fn(), + }, + ui: {}, + }, + featureFlags: { + getBooleanValue: jest.fn().mockReturnValue(true), + }, + }, + }); + + mockUseAssistantAvailability.mockReturnValue({ + hasSearchAILakeConfigurations: false, // AI for SOC is not configured + }); }); it('renders the Add to new case action', () => { @@ -52,7 +117,9 @@ describe('TakeAction', () => { ); - fireEvent.click(screen.getAllByTestId('takeActionPopoverButton')[0]); + + openPopover(); + expect(screen.getByTestId('addToCase')).toBeInTheDocument(); }); @@ -62,7 +129,9 @@ describe('TakeAction', () => { ); - fireEvent.click(screen.getAllByTestId('takeActionPopoverButton')[0]); + + openPopover(); + expect(screen.getByTestId('addToExistingCase')).toBeInTheDocument(); }); @@ -72,11 +141,13 @@ describe('TakeAction', () => { ); - fireEvent.click(screen.getAllByTestId('takeActionPopoverButton')[0]); + + openPopover(); + expect(screen.getByTestId('viewInAiAssistant')).toBeInTheDocument(); }); - it('does not render View in AI Assistant when multiple discoveries', () => { + it('does NOT render View in AI Assistant when multiple discoveries are selected', () => { render( { /> ); - fireEvent.click(screen.getAllByTestId('takeActionPopoverButton')[0]); - expect(screen.queryByTestId('viewInAiAssistant')).toBeNull(); - }); - it('renders mark as open/acknowledged/closed actions when alertWorkflowStatus is set', () => { - const alert = { ...mockAttackDiscovery, alertWorkflowStatus: 'acknowledged' }; - render( - - - - ); - fireEvent.click(screen.getAllByTestId('takeActionPopoverButton')[0]); - expect(screen.getByTestId('markAsOpen')).toBeInTheDocument(); - expect(screen.getByTestId('markAsClosed')).toBeInTheDocument(); + openPopover(); + + expect(screen.queryByTestId('viewInAiAssistant')).toBeNull(); }); - it('shows UpdateAlertsModal when mark as closed is clicked', async () => { + it('shows the UpdateAlertsModal when mark as closed is clicked', async () => { const alert = { ...mockAttackDiscovery, alertWorkflowStatus: 'open', id: 'id1' }; + render( ); - fireEvent.click(screen.getAllByTestId('takeActionPopoverButton')[0]); + + openPopover(); fireEvent.click(screen.getByTestId('markAsClosed')); + expect(await screen.findByTestId('confirmModal')).toBeInTheDocument(); }); - it('calls setSelectedAttackDiscoveries and closes modal on confirm', async () => { + it('calls setSelectedAttackDiscoveries and closes the modal on confirm', async () => { const alert = { ...mockAttackDiscovery, alertWorkflowStatus: 'open', id: 'id1' }; const setSelectedAttackDiscoveries = jest.fn(); render( @@ -125,28 +189,434 @@ describe('TakeAction', () => { /> ); - fireEvent.click(screen.getAllByTestId('takeActionPopoverButton')[0]); + + openPopover(); fireEvent.click(screen.getByTestId('markAsClosed')); expect(await screen.findByTestId('confirmModal')).toBeInTheDocument(); + fireEvent.click(screen.getByTestId('markDiscoveriesOnly')); // Wait for setSelectedAttackDiscoveries to be called await screen.findByTestId('takeActionPopoverButton'); expect(setSelectedAttackDiscoveries).toHaveBeenCalledWith({}); }); - it('closes modal on cancel', async () => { + it('closes the modal on cancel', async () => { const alert = { ...mockAttackDiscovery, alertWorkflowStatus: 'open', id: 'id1' }; render( ); - fireEvent.click(screen.getAllByTestId('takeActionPopoverButton')[0]); + + openPopover(); fireEvent.click(screen.getByTestId('markAsClosed')); expect(await screen.findByTestId('confirmModal')).toBeInTheDocument(); + fireEvent.click(screen.getByTestId('cancel')); // Wait for modal to close await screen.findByTestId('takeActionPopoverButton'); + expect(screen.queryByTestId('confirmModal')).toBeNull(); }); + + describe('actions when a single alert is selected', () => { + const workflowStatuses = [ + { + status: 'open', + expected: { + markAsOpen: false, + markAsAcknowledged: true, + markAsClosed: true, + }, + }, + { + status: 'acknowledged', + expected: { + markAsOpen: true, + markAsAcknowledged: false, + markAsClosed: true, + }, + }, + { + status: 'closed', + expected: { + markAsOpen: true, + markAsAcknowledged: true, + markAsClosed: false, + }, + }, + ]; + + it.each(workflowStatuses)( + 'renders correct actions for status $status (single alert selection)', + ({ status, expected }) => { + const alert = { ...mockAttackDiscovery, alertWorkflowStatus: status }; + + render( + + + + ); + openPopover(); + + if (expected.markAsOpen) { + expect(screen.getByTestId('markAsOpen')).toBeInTheDocument(); + } else { + expect(screen.queryByTestId('markAsOpen')).toBeNull(); + } + + if (expected.markAsAcknowledged) { + expect(screen.getByTestId('markAsAcknowledged')).toBeInTheDocument(); + } else { + expect(screen.queryByTestId('markAsAcknowledged')).toBeNull(); + } + + if (expected.markAsClosed) { + expect(screen.getByTestId('markAsClosed')).toBeInTheDocument(); + } else { + expect(screen.queryByTestId('markAsClosed')).toBeNull(); + } + } + ); + }); + + describe('actions when multiple alerts are selected', () => { + const alerts = getMockAttackDiscoveryAlerts(); // <-- multiple alerts + const testCases = [ + { + testId: 'markAsAcknowledged', + description: 'renders mark as acknowledged', + }, + { + testId: 'markAsClosed', + description: 'renders mark as closed', + }, + { + testId: 'markAsOpen', + description: 'renders mark as open', + }, + ]; + + beforeEach(() => { + render( + + + + ); + + openPopover(); + }); + + it.each(testCases)('$description', ({ testId }) => { + expect(screen.getByTestId(testId)).toBeInTheDocument(); + }); + }); + + describe('when AI for SOC is the configured project', () => { + let alert: ReturnType[0]; + let setSelectedAttackDiscoveries: jest.Mock; + + beforeEach(() => { + alert = getMockAttackDiscoveryAlerts()[0]; + setSelectedAttackDiscoveries = jest.fn(); + (useKibana as jest.Mock).mockReturnValue({ + services: { + cases: { helpers: { canUseCases: () => ({ createComment: true, read: true }) } }, + }, + }); + + mockUseAssistantAvailability.mockReturnValue({ + hasSearchAILakeConfigurations: true, // AI for SOC IS configured + }); + }); + + it('renders mark as closed action and takes action immediately (no modal)', async () => { + render( + + + + ); + + openPopover(); + expect(screen.getByTestId('markAsClosed')).toBeInTheDocument(); + fireEvent.click(screen.getByTestId('markAsClosed')); + + // Modal should NOT appear + expect(screen.queryByTestId('confirmModal')).toBeNull(); + + // Wait for async action + await waitFor(() => { + expect(mockMutateAsyncBulk).toHaveBeenCalledWith( + expect.objectContaining({ + ids: [alert.id], + kibanaAlertWorkflowStatus: 'closed', + }) + ); + }); + + expect(mockMutateAsyncStatus).not.toHaveBeenCalled(); + expect(setSelectedAttackDiscoveries).toHaveBeenCalledWith({}); + }); + + it('renders mark as acknowledged action and takes action immediately (no modal)', async () => { + alert = { ...alert, alertWorkflowStatus: 'open' }; + render( + + + + ); + + openPopover(); + expect(screen.getByTestId('markAsAcknowledged')).toBeInTheDocument(); + + fireEvent.click(screen.getByTestId('markAsAcknowledged')); + + expect(screen.queryByTestId('confirmModal')).toBeNull(); + + await waitFor(() => { + expect(mockMutateAsyncBulk).toHaveBeenCalledWith( + expect.objectContaining({ + ids: [alert.id], + kibanaAlertWorkflowStatus: 'acknowledged', + }) + ); + }); + + expect(mockMutateAsyncStatus).not.toHaveBeenCalled(); + expect(setSelectedAttackDiscoveries).toHaveBeenCalledWith({}); + }); + + it('renders mark as open action and takes action immediately (no modal)', async () => { + alert = { ...alert, alertWorkflowStatus: 'closed' }; + render( + + + + ); + + openPopover(); + expect(screen.getByTestId('markAsOpen')).toBeInTheDocument(); + + fireEvent.click(screen.getByTestId('markAsOpen')); + + expect(screen.queryByTestId('confirmModal')).toBeNull(); + + await waitFor(() => { + expect(mockMutateAsyncBulk).toHaveBeenCalledWith( + expect.objectContaining({ + ids: [alert.id], + kibanaAlertWorkflowStatus: 'open', + }) + ); + }); + + expect(mockMutateAsyncStatus).not.toHaveBeenCalled(); + expect(setSelectedAttackDiscoveries).toHaveBeenCalledWith({}); + }); + }); + + describe('when attackDiscoveryAlertsEnabled is disabled', () => { + beforeEach(() => { + // Mock useKibanaFeatureFlags to return false + const { useKibanaFeatureFlags } = jest.requireMock('../../use_kibana_feature_flags'); + useKibanaFeatureFlags.mockReturnValue({ attackDiscoveryAlertsEnabled: false }); + }); + + it('does not render workflow status actions', () => { + const alert = { ...getMockAttackDiscoveryAlerts()[0], alertWorkflowStatus: 'open' }; + + render( + + + + ); + + openPopover(); + + expect(screen.queryByTestId('markAsOpen')).toBeNull(); + expect(screen.queryByTestId('markAsAcknowledged')).toBeNull(); + expect(screen.queryByTestId('markAsClosed')).toBeNull(); + }); + + it('renders case actions and view in AI assistant', () => { + render( + + + + ); + + openPopover(); + + expect(screen.getByTestId('addToCase')).toBeInTheDocument(); + expect(screen.getByTestId('addToExistingCase')).toBeInTheDocument(); + expect(screen.getByTestId('viewInAiAssistant')).toBeInTheDocument(); + }); + }); + + describe('case interactions', () => { + const mockOnAddToNewCase = jest.fn(); + const mockOnAddToExistingCase = jest.fn(); + + beforeEach(() => { + const { useAddToNewCase } = jest.requireMock('./use_add_to_case'); + const { useAddToExistingCase } = jest.requireMock('./use_add_to_existing_case'); + + useAddToNewCase.mockReturnValue({ + disabled: false, + onAddToNewCase: mockOnAddToNewCase, + }); + + useAddToExistingCase.mockReturnValue({ + onAddToExistingCase: mockOnAddToExistingCase, + }); + }); + + it('calls onAddToNewCase when clicking add to new case', async () => { + render( + + + + ); + + openPopover(); + fireEvent.click(screen.getByTestId('addToCase')); + + await waitFor(() => { + expect(mockOnAddToNewCase).toHaveBeenCalledWith({ + alertIds: expect.any(Array), + markdownComments: expect.any(Array), + replacements: undefined, + }); + }); + }); + + it('calls onAddToExistingCase when clicking add to existing case', () => { + render( + + + + ); + + openPopover(); + fireEvent.click(screen.getByTestId('addToExistingCase')); + + expect(mockOnAddToExistingCase).toHaveBeenCalledWith({ + alertIds: expect.any(Array), + markdownComments: expect.any(Array), + replacements: undefined, + }); + }); + }); + + describe('when case permissions are disabled', () => { + beforeEach(() => { + (useKibana as jest.Mock).mockReturnValue({ + services: { + cases: { + helpers: { + canUseCases: jest.fn().mockReturnValue({ + all: false, + connectors: false, + create: false, + delete: false, + push: false, + read: false, + settings: false, + update: false, + createComment: false, + }), + }, + hooks: { + useCasesAddToExistingCase: jest.fn(), + useCasesAddToExistingCaseModal: jest.fn().mockReturnValue({ open: jest.fn() }), + useCasesAddToNewCaseFlyout: jest.fn(), + }, + ui: {}, + }, + featureFlags: { + getBooleanValue: jest.fn().mockReturnValue(true), + }, + application: { + capabilities: { + assistant: { + show: true, + save: true, + }, + }, + }, + }, + }); + + const { useAddToNewCase } = jest.requireMock('./use_add_to_case'); + useAddToNewCase.mockReturnValue({ + disabled: true, + onAddToNewCase: jest.fn(), + }); + + const { useAddToExistingCase } = jest.requireMock('./use_add_to_existing_case'); + useAddToExistingCase.mockReturnValue({ + onAddToExistingCase: jest.fn(), + }); + }); + + it('disables case actions when the user lacks permissions', () => { + render( + + + + ); + + openPopover(); + + const addToCaseButton = screen.getByTestId('addToCase'); + const addToExistingCaseButton = screen.getByTestId('addToExistingCase'); + + expect(addToCaseButton).toBeDisabled(); + expect(addToExistingCaseButton).toBeDisabled(); + }); + }); + + describe('AI Assistant interactions', () => { + const mockShowAssistantOverlay = jest.fn(); + + beforeEach(() => { + const { useViewInAiAssistant } = jest.requireMock( + '../attack_discovery_panel/view_in_ai_assistant/use_view_in_ai_assistant' + ); + useViewInAiAssistant.mockReturnValue({ + showAssistantOverlay: mockShowAssistantOverlay, + disabled: false, + }); + }); + + it('disables view in AI assistant when disabled', () => { + const { useViewInAiAssistant } = jest.requireMock( + '../attack_discovery_panel/view_in_ai_assistant/use_view_in_ai_assistant' + ); + useViewInAiAssistant.mockReturnValue({ + showAssistantOverlay: mockShowAssistantOverlay, + disabled: true, + }); + + render( + + + + ); + + openPopover(); + const viewInAiAssistantButton = screen.getByTestId('viewInAiAssistant'); + + expect(viewInAiAssistantButton).toBeDisabled(); + }); + }); }); diff --git a/x-pack/solutions/security/plugins/security_solution/public/attack_discovery/pages/results/take_action/index.tsx b/x-pack/solutions/security/plugins/security_solution/public/attack_discovery/pages/results/take_action/index.tsx index 7aa8b8fc74b2f..31276492948ce 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/attack_discovery/pages/results/take_action/index.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/attack_discovery/pages/results/take_action/index.tsx @@ -21,6 +21,7 @@ import { } from '@elastic/eui'; import React, { useCallback, useMemo, useState } from 'react'; +import { useAssistantAvailability } from '../../../../assistant/use_assistant_availability'; import { useAddToNewCase } from './use_add_to_case'; import { useAddToExistingCase } from './use_add_to_existing_case'; import { useViewInAiAssistant } from '../attack_discovery_panel/view_in_ai_assistant/use_view_in_ai_assistant'; @@ -57,6 +58,8 @@ const TakeActionComponent: React.FC = ({ const { services: { cases }, } = useKibana(); + const { hasSearchAILakeConfigurations } = useAssistantAvailability(); + const { attackDiscoveryAlertsEnabled } = useKibanaFeatureFlags(); const userCasesPermissions = cases.helpers.canUseCases([APP_ID]); @@ -110,24 +113,63 @@ const TakeActionComponent: React.FC = ({ const { mutateAsync: attackDiscoveryBulk } = useAttackDiscoveryBulk(); const { mutateAsync: updateAlertStatus } = useUpdateAlertsStatus(); - // click handlers for the popover actions: - const onClickMarkAsAcknowledged = useCallback(async () => { - closePopover(); - - setPendingAction('acknowledged'); - }, [closePopover]); + /** + * Called by the modal when the user confirms the action, + * or directly when the user selects an action in AI for SOC. + */ + const onConfirm = useCallback( + async ({ + updateAlerts, + workflowStatus, + }: { + updateAlerts: boolean; + workflowStatus: 'open' | 'acknowledged' | 'closed'; + }) => { + setPendingAction(null); + + await attackDiscoveryBulk({ + attackDiscoveryAlertsEnabled, + ids: attackDiscoveryIds, + kibanaAlertWorkflowStatus: workflowStatus, + }); + + if (updateAlerts && alertIds.length > 0) { + const originalAlertIds = getOriginalAlertIds({ alertIds, replacements }); + + await updateAlertStatus({ + ids: originalAlertIds, + kibanaAlertWorkflowStatus: workflowStatus, + }); + } - const onClickMarkAsClosed = useCallback(async () => { - closePopover(); + setSelectedAttackDiscoveries({}); + refetchFindAttackDiscoveries?.(); + }, + [ + alertIds, + attackDiscoveryAlertsEnabled, + attackDiscoveryBulk, + attackDiscoveryIds, + refetchFindAttackDiscoveries, + replacements, + setSelectedAttackDiscoveries, + updateAlertStatus, + ] + ); - setPendingAction('closed'); - }, [closePopover]); + const onUpdateWorkflowStatus = useCallback( + async (workflowStatus: 'open' | 'acknowledged' | 'closed') => { + closePopover(); - const onClickMarkAsOpen = useCallback(async () => { - closePopover(); + setPendingAction(workflowStatus); - setPendingAction('open'); - }, [closePopover]); + if (hasSearchAILakeConfigurations) { + // there's no modal for AI for SOC, so we call onConfirm directly + onConfirm({ updateAlerts: false, workflowStatus }); + } + }, + [closePopover, hasSearchAILakeConfigurations, onConfirm] + ); const onClickAddToNewCase = useCallback(async () => { closePopover(); @@ -247,7 +289,7 @@ const TakeActionComponent: React.FC = ({ onUpdateWorkflowStatus('open')} > {i18n.MARK_AS_OPEN} , @@ -259,7 +301,7 @@ const TakeActionComponent: React.FC = ({ onUpdateWorkflowStatus('acknowledged')} > {i18n.MARK_AS_ACKNOWLEDGED} , @@ -271,7 +313,7 @@ const TakeActionComponent: React.FC = ({ onUpdateWorkflowStatus('closed')} > {i18n.MARK_AS_CLOSED} , @@ -279,50 +321,7 @@ const TakeActionComponent: React.FC = ({ : []; return [...markAsOpenItem, ...markAsAcknowledgedItem, ...markAsClosedItem, ...items].flat(); - }, [ - attackDiscoveries, - attackDiscoveryAlertsEnabled, - items, - onClickMarkAsAcknowledged, - onClickMarkAsClosed, - onClickMarkAsOpen, - ]); - - const onConfirm = useCallback( - async (updateAlerts: boolean) => { - if (pendingAction !== null) { - setPendingAction(null); - - await attackDiscoveryBulk({ - attackDiscoveryAlertsEnabled, - ids: attackDiscoveryIds, - kibanaAlertWorkflowStatus: pendingAction, - }); - - if (updateAlerts && alertIds.length > 0) { - const originalAlertIds = getOriginalAlertIds({ alertIds, replacements }); - await updateAlertStatus({ - ids: originalAlertIds, - kibanaAlertWorkflowStatus: pendingAction, - }); - } - - setSelectedAttackDiscoveries({}); - refetchFindAttackDiscoveries?.(); - } - }, - [ - alertIds, - attackDiscoveryAlertsEnabled, - attackDiscoveryBulk, - attackDiscoveryIds, - pendingAction, - refetchFindAttackDiscoveries, - replacements, - setSelectedAttackDiscoveries, - updateAlertStatus, - ] - ); + }, [attackDiscoveries, attackDiscoveryAlertsEnabled, items, onUpdateWorkflowStatus]); const onCloseOrCancel = useCallback(() => { setPendingAction(null); @@ -342,7 +341,7 @@ const TakeActionComponent: React.FC = ({ - {pendingAction != null && ( + {pendingAction != null && !hasSearchAILakeConfigurations && ( { expect(defaultProps.onCancel).toHaveBeenCalled(); }); - it('calls onConfirm(false) when markDiscoveriesOnly is clicked', () => { + it('calls onConfirm with updateAlerts: false when markDiscoveriesOnly is clicked', () => { render(); fireEvent.click(screen.getByTestId('markDiscoveriesOnly')); - expect(defaultProps.onConfirm).toHaveBeenCalledWith(false); + expect(defaultProps.onConfirm).toHaveBeenCalledWith({ + updateAlerts: false, + workflowStatus: 'acknowledged', + }); }); - it('calls onConfirm(true) when markAlertsAndDiscoveries is clicked', () => { + it('calls onConfirm with updateAlerts: true when markAlertsAndDiscoveries is clicked', () => { render(); fireEvent.click(screen.getByTestId('markAlertsAndDiscoveries')); - expect(defaultProps.onConfirm).toHaveBeenCalledWith(true); + expect(defaultProps.onConfirm).toHaveBeenCalledWith({ + updateAlerts: true, + workflowStatus: 'acknowledged', + }); }); it.each([ diff --git a/x-pack/solutions/security/plugins/security_solution/public/attack_discovery/pages/results/take_action/update_alerts_modal/index.tsx b/x-pack/solutions/security/plugins/security_solution/public/attack_discovery/pages/results/take_action/update_alerts_modal/index.tsx index ed6111bb6dce5..4eaac94a8d256 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/attack_discovery/pages/results/take_action/update_alerts_modal/index.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/attack_discovery/pages/results/take_action/update_alerts_modal/index.tsx @@ -28,7 +28,13 @@ interface Props { attackDiscoveriesCount: number; onCancel: () => void; onClose: () => void; - onConfirm: (updateAlerts: boolean) => void; + onConfirm: ({ + updateAlerts, + workflowStatus, + }: { + updateAlerts: boolean; + workflowStatus: 'open' | 'acknowledged' | 'closed'; + }) => Promise; workflowStatus: 'open' | 'acknowledged' | 'closed'; } @@ -45,12 +51,12 @@ const UpdateAlertsModalComponent: React.FC = ({ const titleId = useGeneratedHtmlId(); const markDiscoveriesOnly = useCallback(() => { - onConfirm(false); - }, [onConfirm]); + onConfirm({ updateAlerts: false, workflowStatus }); + }, [onConfirm, workflowStatus]); const markAlertsAndDiscoveries = useCallback(() => { - onConfirm(true); - }, [onConfirm]); + onConfirm({ updateAlerts: true, workflowStatus }); + }, [onConfirm, workflowStatus]); const confirmButtons = useMemo( () => (