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
new file mode 100644
index 0000000000000..b1c86da72b058
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/assignees.test.tsx
@@ -0,0 +1,123 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import React from 'react';
+import { render } from '@testing-library/react';
+import type { UserProfileWithAvatar } from '@kbn/user-profile-components';
+
+import {
+ ASSIGNEES_ADD_BUTTON_TEST_ID,
+ ASSIGNEES_COUNT_BADGE_TEST_ID,
+ ASSIGNEES_TITLE_TEST_ID,
+ ASSIGNEES_VALUE_TEST_ID,
+ ASSIGNEE_AVATAR_TEST_ID,
+} from './test_ids';
+import { Assignees } from './assignees';
+
+import { useGetUserProfiles } from '../../../../detections/containers/detection_engine/alerts/use_get_user_profiles';
+import { useSuggestUsers } from '../../../../detections/containers/detection_engine/alerts/use_suggest_users';
+import type { SetAlertAssigneesFunc } from '../../../../common/components/toolbar/bulk_actions/use_set_alert_assignees';
+import { useSetAlertAssignees } from '../../../../common/components/toolbar/bulk_actions/use_set_alert_assignees';
+import { TestProviders } from '../../../../common/mock';
+
+jest.mock('../../../../detections/containers/detection_engine/alerts/use_get_user_profiles');
+jest.mock('../../../../detections/containers/detection_engine/alerts/use_suggest_users');
+jest.mock('../../../../common/components/toolbar/bulk_actions/use_set_alert_assignees');
+
+const mockUserProfiles: UserProfileWithAvatar[] = [
+ { uid: 'user-id-1', enabled: true, user: { username: 'user1', full_name: 'User 1' }, data: {} },
+ { uid: 'user-id-2', enabled: true, user: { username: 'user2', full_name: 'User 2' }, data: {} },
+ { uid: 'user-id-3', enabled: true, user: { username: 'user3', full_name: 'User 3' }, data: {} },
+];
+
+const renderAssignees = (
+ eventId = 'event-1',
+ alertAssignees = ['user-id-1'],
+ onAssigneesUpdated = jest.fn()
+) =>
+ render(
+
+
+
+ );
+
+describe('', () => {
+ let setAlertAssigneesMock: jest.Mocked;
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+ (useGetUserProfiles as jest.Mock).mockReturnValue({
+ loading: false,
+ userProfiles: mockUserProfiles,
+ });
+ (useSuggestUsers as jest.Mock).mockReturnValue({
+ loading: false,
+ userProfiles: mockUserProfiles,
+ });
+
+ setAlertAssigneesMock = jest.fn().mockReturnValue(Promise.resolve());
+ (useSetAlertAssignees as jest.Mock).mockReturnValue(setAlertAssigneesMock);
+ });
+
+ it('should render component', () => {
+ const { getByTestId } = renderAssignees();
+
+ expect(getByTestId(ASSIGNEES_TITLE_TEST_ID)).toBeInTheDocument();
+ expect(getByTestId(ASSIGNEES_VALUE_TEST_ID)).toBeInTheDocument();
+ expect(getByTestId(ASSIGNEES_ADD_BUTTON_TEST_ID)).toBeInTheDocument();
+ });
+
+ it('should render assignees avatars', () => {
+ const assignees = ['user-id-1', 'user-id-2'];
+ const { getByTestId, queryByTestId } = renderAssignees('test-event', assignees);
+
+ expect(getByTestId(ASSIGNEE_AVATAR_TEST_ID('user1'))).toBeInTheDocument();
+ expect(getByTestId(ASSIGNEE_AVATAR_TEST_ID('user2'))).toBeInTheDocument();
+
+ expect(queryByTestId(ASSIGNEES_COUNT_BADGE_TEST_ID)).not.toBeInTheDocument();
+ });
+
+ it('should render badge with assignees count in case there are more than two users assigned to an alert', () => {
+ const assignees = ['user-id-1', 'user-id-2', 'user-id-3'];
+ const { getByTestId, queryByTestId } = renderAssignees('test-event', assignees);
+
+ const assigneesCountBadge = getByTestId(ASSIGNEES_COUNT_BADGE_TEST_ID);
+ expect(assigneesCountBadge).toBeInTheDocument();
+ expect(assigneesCountBadge).toHaveTextContent(`${assignees.length}`);
+
+ expect(queryByTestId(ASSIGNEE_AVATAR_TEST_ID('user1'))).not.toBeInTheDocument();
+ expect(queryByTestId(ASSIGNEE_AVATAR_TEST_ID('user2'))).not.toBeInTheDocument();
+ expect(queryByTestId(ASSIGNEE_AVATAR_TEST_ID('user3'))).not.toBeInTheDocument();
+ });
+
+ it('should call assignees update functionality with the right arguments', () => {
+ const assignees = ['user-id-1', 'user-id-2'];
+ const { getByTestId, getByText } = renderAssignees('test-event', assignees);
+
+ // Update assignees
+ getByTestId(ASSIGNEES_ADD_BUTTON_TEST_ID).click();
+ getByText('User 1').click();
+ getByText('User 3').click();
+
+ // Close assignees popover
+ getByTestId(ASSIGNEES_ADD_BUTTON_TEST_ID).click();
+
+ expect(setAlertAssigneesMock).toHaveBeenCalledWith(
+ {
+ assignees_to_add: ['user-id-3'],
+ assignees_to_remove: ['user-id-1'],
+ },
+ ['test-event'],
+ expect.anything(),
+ expect.anything()
+ );
+ });
+});
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 83f6aeb307530..3f22d13f82531 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
@@ -16,9 +16,15 @@ import {
} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n-react';
import { UserAvatar } from '@kbn/user-profile-components';
+import { noop } from 'lodash';
import { useGetUserProfiles } from '../../../../detections/containers/detection_engine/alerts/use_get_user_profiles';
import { useSetAlertAssignees } from '../../../../common/components/toolbar/bulk_actions/use_set_alert_assignees';
-import { ASSIGNEES_TITLE_TEST_ID, ASSIGNEES_VALUE_TEST_ID } from './test_ids';
+import {
+ ASSIGNEE_AVATAR_TEST_ID,
+ ASSIGNEES_TITLE_TEST_ID,
+ ASSIGNEES_VALUE_TEST_ID,
+ ASSIGNEES_COUNT_BADGE_TEST_ID,
+} from './test_ids';
import { AssigneesPopover } from './assignees_popover';
export interface AssigneesProps {
@@ -44,7 +50,6 @@ export const Assignees: FC = memo(
const onSuccess = useCallback(() => {
if (onAssigneesUpdated) onAssigneesUpdated();
}, [onAssigneesUpdated]);
- const setIsLoading = useCallback(() => {}, []);
const handleOnAlertAssigneesSubmit = useCallback(async () => {
if (setAlertAssignees && selectedAssignees) {
@@ -59,9 +64,9 @@ export const Assignees: FC = memo(
assignees_to_remove: assigneesToRemoveArray,
};
- await setAlertAssignees(assigneesToUpdate, [eventId], onSuccess, setIsLoading);
+ await setAlertAssignees(assigneesToUpdate, [eventId], onSuccess, noop);
}
- }, [alertAssignees, eventId, onSuccess, selectedAssignees, setAlertAssignees, setIsLoading]);
+ }, [alertAssignees, eventId, onSuccess, selectedAssignees, setAlertAssignees]);
const togglePopover = useCallback(() => {
setIsPopoverOpen((value) => !value);
@@ -111,11 +116,19 @@ export const Assignees: FC = memo(
))}
repositionOnScroll={true}
>
- {assignees.length}
+
+ {assignees.length}
+
) : (
assignees.map((user) => (
-
+
))
)}
diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/assignees_popover.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/assignees_popover.test.tsx
new file mode 100644
index 0000000000000..b9a5604657090
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/assignees_popover.test.tsx
@@ -0,0 +1,125 @@
+/*
+ * 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 type { UserProfileWithAvatar } from '@kbn/user-profile-components';
+
+import { ASSIGNEES_ADD_BUTTON_TEST_ID } from './test_ids';
+import { AssigneesPopover } from './assignees_popover';
+
+import { useSuggestUsers } from '../../../../detections/containers/detection_engine/alerts/use_suggest_users';
+import { TestProviders } from '../../../../common/mock';
+
+jest.mock('../../../../detections/containers/detection_engine/alerts/use_suggest_users');
+
+const mockUserProfiles: UserProfileWithAvatar[] = [
+ {
+ uid: 'user-id-1',
+ enabled: true,
+ user: { username: 'user1', full_name: 'User 1', email: 'user1@test.com' },
+ data: {},
+ },
+ {
+ uid: 'user-id-2',
+ enabled: true,
+ user: { username: 'user2', full_name: 'User 2', email: 'user2@test.com' },
+ data: {},
+ },
+ {
+ uid: 'user-id-3',
+ enabled: true,
+ user: { username: 'user3', full_name: 'User 3', email: 'user3@test.com' },
+ data: {},
+ },
+];
+
+const renderAssigneesPopover = (
+ alertAssignees: string[],
+ isPopoverOpen: boolean,
+ onUsersChange = jest.fn(),
+ togglePopover = jest.fn(),
+ onClosePopover = jest.fn()
+) =>
+ render(
+
+
+
+ );
+
+describe('', () => {
+ beforeEach(() => {
+ jest.clearAllMocks();
+ (useSuggestUsers as jest.Mock).mockReturnValue({
+ loading: false,
+ userProfiles: mockUserProfiles,
+ });
+ });
+
+ it('should render closed popover component', () => {
+ const { getByTestId, queryByTestId } = renderAssigneesPopover([], false);
+
+ expect(getByTestId(ASSIGNEES_ADD_BUTTON_TEST_ID)).toBeInTheDocument();
+ expect(queryByTestId('euiSelectableList')).not.toBeInTheDocument();
+ });
+
+ it('should render opened popover component', () => {
+ const { getByTestId } = renderAssigneesPopover([], true);
+
+ expect(getByTestId(ASSIGNEES_ADD_BUTTON_TEST_ID)).toBeInTheDocument();
+ expect(getByTestId('euiSelectableList')).toBeInTheDocument();
+ });
+
+ it('should render assignees', () => {
+ const { getByTestId } = renderAssigneesPopover([], true);
+
+ const assigneesList = getByTestId('euiSelectableList');
+ expect(assigneesList).toHaveTextContent('User 1');
+ expect(assigneesList).toHaveTextContent('user1@test.com');
+ expect(assigneesList).toHaveTextContent('User 2');
+ expect(assigneesList).toHaveTextContent('user2@test.com');
+ expect(assigneesList).toHaveTextContent('User 3');
+ expect(assigneesList).toHaveTextContent('user3@test.com');
+ });
+
+ it('should call onUsersChange on clsing the popover', () => {
+ const onUsersChangeMock = jest.fn();
+ const { getByText } = renderAssigneesPopover([], true, onUsersChangeMock);
+
+ getByText('User 1').click();
+ getByText('User 2').click();
+ getByText('User 3').click();
+ getByText('User 3').click();
+ getByText('User 2').click();
+ getByText('User 1').click();
+
+ expect(onUsersChangeMock).toHaveBeenCalledTimes(6);
+ expect(onUsersChangeMock.mock.calls).toEqual([
+ [['user-id-1']],
+ [['user-id-2', 'user-id-1']],
+ [['user-id-3', 'user-id-2', 'user-id-1']],
+ [['user-id-2', 'user-id-1']],
+ [['user-id-1']],
+ [[]],
+ ]);
+ });
+
+ it('should call togglePopover on add button click', () => {
+ const togglePopoverMock = jest.fn();
+ const { getByTestId } = renderAssigneesPopover([], false, jest.fn(), togglePopoverMock);
+
+ getByTestId(ASSIGNEES_ADD_BUTTON_TEST_ID).click();
+
+ expect(togglePopoverMock).toHaveBeenCalledTimes(1);
+ });
+});
diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/assignees_popover.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/assignees_popover.tsx
index 5dc05e6864e6e..d973001213c6b 100644
--- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/assignees_popover.tsx
+++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/assignees_popover.tsx
@@ -13,6 +13,7 @@ import { UserProfilesPopover } from '@kbn/user-profile-components';
import { EuiButtonIcon, EuiToolTip } from '@elastic/eui';
import { useSuggestUsers } from '../../../../detections/containers/detection_engine/alerts/use_suggest_users';
+import { ASSIGNEES_ADD_BUTTON_TEST_ID } from './test_ids';
const PopoverButton: React.FC<{ togglePopover: () => void; isDisabled: boolean }> = ({
togglePopover,
@@ -28,7 +29,8 @@ const PopoverButton: React.FC<{ togglePopover: () => void; isDisabled: boolean }
)}
>
`${PREFIX}AssigneeAvatar-${userName}`;
+export const ASSIGNEES_COUNT_BADGE_TEST_ID = `${PREFIX}AssigneesCountBadge`;