From 107f66e2a19b1b2cd77a0b1c8b2ac813976bb2a4 Mon Sep 17 00:00:00 2001 From: scottybollinger Date: Mon, 19 Oct 2020 10:25:43 -0400 Subject: [PATCH 01/16] Update id of second sources mock to be unique MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both were originally set to ‘123’ --- .../workplace_search/__mocks__/content_sources.mock.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/__mocks__/content_sources.mock.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/__mocks__/content_sources.mock.ts index 7789d0caba345..c305ae9d5f7a9 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/__mocks__/content_sources.mock.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/__mocks__/content_sources.mock.ts @@ -20,7 +20,7 @@ export const contentSources = [ boost: 1, }, { - id: '123', + id: '124', serviceType: 'jira', searchable: true, supportedByLicense: true, From c00afa028417a79dbbd3a5a8297cf2b0596565ef Mon Sep 17 00:00:00 2001 From: scottybollinger Date: Mon, 19 Oct 2020 10:33:31 -0400 Subject: [PATCH 02/16] Remove unused types from interface These were never used and were only uncovered while testing. These both exist on `group` but not at the top level. --- .../applications/workplace_search/views/groups/group_logic.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/group_logic.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/group_logic.ts index 1ce0fe53726d4..4171e9bb27987 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/group_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/group_logic.ts @@ -56,8 +56,6 @@ export interface IGroupActions { } export interface IGroupValues { - contentSources: IContentSourceDetails[]; - users: IUser[]; group: IGroupDetails; dataLoading: boolean; manageUsersModalVisible: boolean; From 364a0bc45e9ba727fb378c503b9b87c4ed0d267f Mon Sep 17 00:00:00 2001 From: scottybollinger Date: Mon, 19 Oct 2020 12:38:41 -0400 Subject: [PATCH 03/16] Add group mock --- .../groups/__mocks__/group_logic.mock.ts | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/__mocks__/group_logic.mock.ts diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/__mocks__/group_logic.mock.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/__mocks__/group_logic.mock.ts new file mode 100644 index 0000000000000..a3c7b0fee5496 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/__mocks__/group_logic.mock.ts @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { IGroupValues } from '../group_logic'; + +import { IGroupDetails, ISourcePriority } from '../../../types'; + +export const mockGroupValues = { + group: {} as IGroupDetails, + dataLoading: true, + manageUsersModalVisible: false, + managerModalFormErrors: [], + sharedSourcesModalModalVisible: false, + confirmDeleteModalVisible: false, + groupNameInputValue: '', + selectedGroupSources: [], + selectedGroupUsers: [], + groupPrioritiesUnchanged: true, + activeSourcePriorities: {} as ISourcePriority, + cachedSourcePriorities: {} as ISourcePriority, +} as IGroupValues; From 6e965a8b6a36244ac88d71505020772889d64667 Mon Sep 17 00:00:00 2001 From: scottybollinger Date: Mon, 19 Oct 2020 12:46:46 -0400 Subject: [PATCH 04/16] Fix typo in reducer name --- .../views/groups/__mocks__/group_logic.mock.ts | 2 +- .../applications/workplace_search/views/groups/group_logic.ts | 4 ++-- .../workplace_search/views/groups/group_router.tsx | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/__mocks__/group_logic.mock.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/__mocks__/group_logic.mock.ts index a3c7b0fee5496..18e7851485222 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/__mocks__/group_logic.mock.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/__mocks__/group_logic.mock.ts @@ -13,7 +13,7 @@ export const mockGroupValues = { dataLoading: true, manageUsersModalVisible: false, managerModalFormErrors: [], - sharedSourcesModalModalVisible: false, + sharedSourcesModalVisible: false, confirmDeleteModalVisible: false, groupNameInputValue: '', selectedGroupSources: [], diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/group_logic.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/group_logic.ts index 4171e9bb27987..be941a231ac96 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/group_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/group_logic.ts @@ -60,7 +60,7 @@ export interface IGroupValues { dataLoading: boolean; manageUsersModalVisible: boolean; managerModalFormErrors: string[]; - sharedSourcesModalModalVisible: boolean; + sharedSourcesModalVisible: boolean; confirmDeleteModalVisible: boolean; groupNameInputValue: string; selectedGroupSources: string[]; @@ -136,7 +136,7 @@ export const GroupLogic = kea>({ hideManageUsersModal: () => [], }, ], - sharedSourcesModalModalVisible: [ + sharedSourcesModalVisible: [ false, { showSharedSourcesModal: () => true, diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/group_router.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/group_router.tsx index 0a637497a5b05..73795f789f2bb 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/group_router.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/group_router.tsx @@ -29,7 +29,7 @@ export const GroupRouter: React.FC = () => { const { messages } = useValues(FlashMessagesLogic); const { initializeGroup, resetGroup } = useActions(GroupLogic); const { - sharedSourcesModalModalVisible, + sharedSourcesModalVisible, manageUsersModalVisible, group: { name }, } = useValues(GroupLogic); @@ -55,7 +55,7 @@ export const GroupRouter: React.FC = () => { - {sharedSourcesModalModalVisible && } + {sharedSourcesModalVisible && } {manageUsersModalVisible && } ); From ea2b26c401c9aaac1f08fe1cac1af73846ba09db Mon Sep 17 00:00:00 2001 From: scottybollinger Date: Mon, 19 Oct 2020 16:34:56 -0400 Subject: [PATCH 05/16] Add tests for group_logic --- .../views/groups/group_logic.test.ts | 509 ++++++++++++++++++ .../views/groups/group_logic.ts | 7 +- 2 files changed, 512 insertions(+), 4 deletions(-) create mode 100644 x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/group_logic.test.ts diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/group_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/group_logic.test.ts new file mode 100644 index 0000000000000..b4724c8ed3f0e --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/group_logic.test.ts @@ -0,0 +1,509 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { resetContext } from 'kea'; + +jest.mock('../../../shared/http', () => ({ + HttpLogic: { + values: { http: { get: jest.fn(), post: jest.fn(), put: jest.fn(), delete: jest.fn() } }, + }, +})); +import { HttpLogic } from '../../../shared/http'; + +jest.mock('../../../shared/flash_messages', () => ({ + FlashMessagesLogic: { actions: { clearFlashMessages: jest.fn(), setQueuedMessages: jest.fn() } }, + flashAPIErrors: jest.fn(), + setSuccessMessage: jest.fn(), + setQueuedSuccessMessage: jest.fn(), +})); +import { + FlashMessagesLogic, + flashAPIErrors, + setSuccessMessage, + setQueuedSuccessMessage, +} from '../../../shared/flash_messages'; + +jest.mock('../../../shared/kibana', () => ({ + KibanaLogic: { values: { navigateToUrl: jest.fn() } }, +})); +import { KibanaLogic } from '../../../shared/kibana'; + +import { groups } from '../../__mocks__/groups.mock'; +import { mockGroupValues } from './__mocks__/group_logic.mock'; +import { GroupLogic } from './group_logic'; + +import { GROUPS_PATH } from '../../routes'; + +describe('GroupLogic', () => { + const group = groups[0]; + const sourceIds = ['123', '124']; + const userIds = ['1z1z']; + const sourcePriorities = { [sourceIds[0]]: 1, [sourceIds[1]]: 0.5 }; + const clearFlashMessagesSpy = jest.spyOn(FlashMessagesLogic.actions, 'clearFlashMessages'); + + beforeEach(() => { + jest.clearAllMocks(); + resetContext({}); + GroupLogic.mount(); + }); + + it('has expected default values', () => { + expect(GroupLogic.values).toEqual(mockGroupValues); + }); + + describe('actions', () => { + describe('onInitializeGroup', () => { + it('sets reducers', () => { + GroupLogic.actions.onInitializeGroup(group); + + expect(GroupLogic.values.group).toEqual(group); + expect(GroupLogic.values.dataLoading).toEqual(false); + expect(GroupLogic.values.groupNameInputValue).toEqual(group.name); + expect(GroupLogic.values.selectedGroupSources).toEqual(sourceIds); + expect(GroupLogic.values.selectedGroupUsers).toEqual(userIds); + expect(GroupLogic.values.cachedSourcePriorities).toEqual(sourcePriorities); + expect(GroupLogic.values.activeSourcePriorities).toEqual(sourcePriorities); + expect(GroupLogic.values.groupPrioritiesUnchanged).toEqual(true); + }); + }); + + describe('onGroupNameChanged', () => { + it('sets reducers', () => { + const renamedGroup = { + ...group, + name: 'changed', + }; + GroupLogic.actions.onGroupNameChanged(renamedGroup); + + expect(GroupLogic.values.group).toEqual(renamedGroup); + expect(GroupLogic.values.groupNameInputValue).toEqual(renamedGroup.name); + }); + }); + + describe('onGroupPrioritiesChanged', () => { + it('sets reducers', () => { + GroupLogic.actions.onGroupPrioritiesChanged(group); + + expect(GroupLogic.values.dataLoading).toEqual(false); + expect(GroupLogic.values.cachedSourcePriorities).toEqual(sourcePriorities); + expect(GroupLogic.values.activeSourcePriorities).toEqual(sourcePriorities); + }); + }); + + describe('onGroupNameInputChange', () => { + it('sets reducers', () => { + const name = 'new name'; + GroupLogic.actions.onGroupNameInputChange(name); + + expect(GroupLogic.values.groupNameInputValue).toEqual(name); + }); + }); + + describe('addGroupSource', () => { + it('sets reducer', () => { + GroupLogic.actions.addGroupSource(sourceIds[0]); + + expect(GroupLogic.values.selectedGroupSources).toEqual([sourceIds[0]]); + }); + }); + + describe('removeGroupSource', () => { + it('sets reducers', () => { + GroupLogic.actions.addGroupSource(sourceIds[0]); + GroupLogic.actions.addGroupSource(sourceIds[1]); + GroupLogic.actions.removeGroupSource(sourceIds[0]); + + expect(GroupLogic.values.selectedGroupSources).toEqual([sourceIds[1]]); + }); + }); + + describe('addGroupUser', () => { + it('sets reducer', () => { + GroupLogic.actions.addGroupUser(sourceIds[0]); + + expect(GroupLogic.values.selectedGroupUsers).toEqual([sourceIds[0]]); + }); + }); + + describe('removeGroupUser', () => { + it('sets reducers', () => { + GroupLogic.actions.addGroupUser(sourceIds[0]); + GroupLogic.actions.addGroupUser(sourceIds[1]); + GroupLogic.actions.removeGroupUser(sourceIds[0]); + + expect(GroupLogic.values.selectedGroupUsers).toEqual([sourceIds[1]]); + }); + }); + + describe('onGroupSourcesSaved', () => { + it('sets reducers', () => { + GroupLogic.actions.onGroupSourcesSaved(group); + + expect(GroupLogic.values.group).toEqual(group); + expect(GroupLogic.values.sharedSourcesModalVisible).toEqual(false); + expect(GroupLogic.values.selectedGroupSources).toEqual(sourceIds); + expect(GroupLogic.values.cachedSourcePriorities).toEqual(sourcePriorities); + expect(GroupLogic.values.activeSourcePriorities).toEqual(sourcePriorities); + }); + }); + + describe('onGroupUsersSaved', () => { + it('sets reducers', () => { + GroupLogic.actions.onGroupUsersSaved(group); + + expect(GroupLogic.values.group).toEqual(group); + expect(GroupLogic.values.manageUsersModalVisible).toEqual(false); + expect(GroupLogic.values.selectedGroupUsers).toEqual(userIds); + }); + }); + + describe('setGroupModalErrors', () => { + it('sets reducers', () => { + const errors = ['this is an error']; + GroupLogic.actions.setGroupModalErrors(errors); + + expect(GroupLogic.values.managerModalFormErrors).toEqual(errors); + }); + }); + + describe('hideSharedSourcesModal', () => { + it('sets reducers', () => { + GroupLogic.actions.hideSharedSourcesModal(group); + + expect(GroupLogic.values.sharedSourcesModalVisible).toEqual(false); + expect(GroupLogic.values.selectedGroupSources).toEqual(sourceIds); + }); + }); + + describe('hideManageUsersModal', () => { + it('sets reducers', () => { + GroupLogic.actions.hideManageUsersModal(group); + + expect(GroupLogic.values.manageUsersModalVisible).toEqual(false); + expect(GroupLogic.values.managerModalFormErrors).toEqual([]); + expect(GroupLogic.values.selectedGroupUsers).toEqual(userIds); + }); + }); + + describe('selectAllSources', () => { + it('sets reducers', () => { + GroupLogic.actions.selectAllSources(group.contentSources); + + expect(GroupLogic.values.selectedGroupSources).toEqual(sourceIds); + }); + }); + + describe('selectAllUsers', () => { + it('sets reducers', () => { + GroupLogic.actions.selectAllUsers(group.users); + + expect(GroupLogic.values.selectedGroupUsers).toEqual(userIds); + }); + }); + + describe('updatePriority', () => { + it('sets reducers', () => { + const PRIORITY_VALUE = 4; + GroupLogic.actions.updatePriority(sourceIds[0], PRIORITY_VALUE); + + expect(GroupLogic.values.activeSourcePriorities).toEqual({ + [sourceIds[0]]: PRIORITY_VALUE, + }); + expect(GroupLogic.values.groupPrioritiesUnchanged).toEqual(false); + }); + }); + + describe('resetGroup', () => { + it('sets reducers', () => { + GroupLogic.actions.resetGroup(); + + expect(GroupLogic.values.group).toEqual({}); + expect(GroupLogic.values.dataLoading).toEqual(true); + }); + }); + + describe('hideConfirmDeleteModal', () => { + it('sets reducer', () => { + GroupLogic.actions.showConfirmDeleteModal(); + GroupLogic.actions.hideConfirmDeleteModal(); + + expect(GroupLogic.values.confirmDeleteModalVisible).toEqual(false); + }); + }); + }); + + describe('listeners', () => { + describe('initializeGroup', () => { + it('calls API and sets values', async () => { + const onInitializeGroupSpy = jest.spyOn(GroupLogic.actions, 'onInitializeGroup'); + const promise = Promise.resolve(group); + (HttpLogic.values.http.get as jest.Mock).mockReturnValue(promise); + + GroupLogic.actions.initializeGroup(sourceIds[0]); + expect(HttpLogic.values.http.get).toHaveBeenCalledWith('/api/workplace_search/groups/123'); + await promise; + expect(onInitializeGroupSpy).toHaveBeenCalledWith(group); + }); + + it('handles 404 error', async () => { + const promise = Promise.reject({ response: { status: 404 } }); + (HttpLogic.values.http.get as jest.Mock).mockReturnValue(promise); + + GroupLogic.actions.initializeGroup(sourceIds[0]); + + try { + await promise; + } catch { + expect(KibanaLogic.values.navigateToUrl).toHaveBeenCalledWith(GROUPS_PATH); + expect(FlashMessagesLogic.actions.setQueuedMessages).toHaveBeenCalledWith({ + type: 'error', + message: 'Unable to find group with ID: "123".', + }); + } + }); + + it('handles non-404 error', async () => { + const promise = Promise.reject('this is an error'); + (HttpLogic.values.http.get as jest.Mock).mockReturnValue(promise); + + GroupLogic.actions.initializeGroup(sourceIds[0]); + + try { + await promise; + } catch { + expect(KibanaLogic.values.navigateToUrl).toHaveBeenCalledWith(GROUPS_PATH); + expect(FlashMessagesLogic.actions.setQueuedMessages).toHaveBeenCalledWith({ + type: 'error', + message: 'this is an error', + }); + } + }); + }); + + describe('deleteGroup', () => { + beforeEach(() => { + GroupLogic.actions.onInitializeGroup(group); + }); + it('deletes a group', async () => { + const promise = Promise.resolve(true); + (HttpLogic.values.http.delete as jest.Mock).mockReturnValue(promise); + + GroupLogic.actions.deleteGroup(); + expect(HttpLogic.values.http.delete).toHaveBeenCalledWith( + '/api/workplace_search/groups/123' + ); + + await promise; + expect(KibanaLogic.values.navigateToUrl).toHaveBeenCalledWith(GROUPS_PATH); + expect(setQueuedSuccessMessage).toHaveBeenCalledWith( + 'Group "group" was successfully deleted.' + ); + }); + + it('handles error', async () => { + const promise = Promise.reject('this is an error'); + (HttpLogic.values.http.delete as jest.Mock).mockReturnValue(promise); + + GroupLogic.actions.deleteGroup(); + try { + await promise; + } catch { + expect(flashAPIErrors).toHaveBeenCalledWith('this is an error'); + } + }); + }); + + describe('updateGroupName', () => { + beforeEach(() => { + GroupLogic.actions.onInitializeGroup(group); + GroupLogic.actions.onGroupNameInputChange('new name'); + }); + it('updates name', async () => { + const onGroupNameChangedSpy = jest.spyOn(GroupLogic.actions, 'onGroupNameChanged'); + const promise = Promise.resolve(group); + (HttpLogic.values.http.put as jest.Mock).mockReturnValue(promise); + + GroupLogic.actions.updateGroupName(); + expect(HttpLogic.values.http.put).toHaveBeenCalledWith('/api/workplace_search/groups/123', { + body: JSON.stringify({ group: { name: 'new name' } }), + }); + + await promise; + expect(onGroupNameChangedSpy).toHaveBeenCalledWith(group); + expect(setSuccessMessage).toHaveBeenCalledWith( + 'Successfully renamed this group to "group".' + ); + }); + + it('handles error', async () => { + const promise = Promise.reject('this is an error'); + (HttpLogic.values.http.put as jest.Mock).mockReturnValue(promise); + + GroupLogic.actions.updateGroupName(); + try { + await promise; + } catch { + expect(flashAPIErrors).toHaveBeenCalledWith('this is an error'); + } + }); + }); + + describe('saveGroupSources', () => { + beforeEach(() => { + GroupLogic.actions.onInitializeGroup(group); + GroupLogic.actions.selectAllSources(group.contentSources); + }); + it('updates name', async () => { + const onGroupSourcesSavedSpy = jest.spyOn(GroupLogic.actions, 'onGroupSourcesSaved'); + const promise = Promise.resolve(group); + (HttpLogic.values.http.post as jest.Mock).mockReturnValue(promise); + + GroupLogic.actions.saveGroupSources(); + expect(HttpLogic.values.http.post).toHaveBeenCalledWith( + '/api/workplace_search/groups/123/share', + { + body: JSON.stringify({ content_source_ids: sourceIds }), + } + ); + + await promise; + expect(onGroupSourcesSavedSpy).toHaveBeenCalledWith(group); + expect(setSuccessMessage).toHaveBeenCalledWith( + 'Successfully updated shared content sources.' + ); + }); + + it('handles error', async () => { + const promise = Promise.reject('this is an error'); + (HttpLogic.values.http.post as jest.Mock).mockReturnValue(promise); + + GroupLogic.actions.saveGroupSources(); + try { + await promise; + } catch { + expect(flashAPIErrors).toHaveBeenCalledWith('this is an error'); + } + }); + }); + + describe('saveGroupUsers', () => { + beforeEach(() => { + GroupLogic.actions.onInitializeGroup(group); + }); + it('updates name', async () => { + const onGroupUsersSavedSpy = jest.spyOn(GroupLogic.actions, 'onGroupUsersSaved'); + const promise = Promise.resolve(group); + (HttpLogic.values.http.post as jest.Mock).mockReturnValue(promise); + + GroupLogic.actions.saveGroupUsers(); + expect(HttpLogic.values.http.post).toHaveBeenCalledWith( + '/api/workplace_search/groups/123/assign', + { + body: JSON.stringify({ user_ids: userIds }), + } + ); + + await promise; + expect(onGroupUsersSavedSpy).toHaveBeenCalledWith(group); + expect(setSuccessMessage).toHaveBeenCalledWith( + 'Successfully updated the users of this group.' + ); + }); + + it('handles error', async () => { + const promise = Promise.reject('this is an error'); + (HttpLogic.values.http.post as jest.Mock).mockReturnValue(promise); + + GroupLogic.actions.saveGroupUsers(); + try { + await promise; + } catch { + expect(flashAPIErrors).toHaveBeenCalledWith('this is an error'); + } + }); + }); + + describe('saveGroupSourcePrioritization', () => { + beforeEach(() => { + GroupLogic.actions.onInitializeGroup(group); + }); + it('updates name', async () => { + const onGroupPrioritiesChangedSpy = jest.spyOn( + GroupLogic.actions, + 'onGroupPrioritiesChanged' + ); + const promise = Promise.resolve(group); + (HttpLogic.values.http.put as jest.Mock).mockReturnValue(promise); + + GroupLogic.actions.saveGroupSourcePrioritization(); + expect(HttpLogic.values.http.put).toHaveBeenCalledWith( + '/api/workplace_search/groups/123/boosts', + { + body: JSON.stringify({ + content_source_boosts: [ + [sourceIds[0], 1], + [sourceIds[1], 0.5], + ], + }), + } + ); + + await promise; + expect(setSuccessMessage).toHaveBeenCalledWith( + 'Successfully updated shared source prioritization.' + ); + expect(onGroupPrioritiesChangedSpy).toHaveBeenCalledWith(group); + }); + + it('handles error', async () => { + const promise = Promise.reject('this is an error'); + (HttpLogic.values.http.put as jest.Mock).mockReturnValue(promise); + + GroupLogic.actions.saveGroupSourcePrioritization(); + try { + await promise; + } catch { + expect(flashAPIErrors).toHaveBeenCalledWith('this is an error'); + } + }); + }); + + describe('showConfirmDeleteModal', () => { + it('sets reducer and clears flash messages', () => { + GroupLogic.actions.showConfirmDeleteModal(); + + expect(GroupLogic.values.confirmDeleteModalVisible).toEqual(true); + expect(clearFlashMessagesSpy).toHaveBeenCalled(); + }); + }); + + describe('showSharedSourcesModal', () => { + it('sets reducer and clears flash messages', () => { + GroupLogic.actions.showSharedSourcesModal(); + + expect(GroupLogic.values.sharedSourcesModalVisible).toEqual(true); + expect(clearFlashMessagesSpy).toHaveBeenCalled(); + }); + }); + + describe('showManageUsersModal', () => { + it('sets reducer and clears flash messages', () => { + GroupLogic.actions.showManageUsersModal(); + + expect(GroupLogic.values.manageUsersModalVisible).toEqual(true); + expect(clearFlashMessagesSpy).toHaveBeenCalled(); + }); + }); + + describe('resetFlashMessages', () => { + it('clears flash messages', () => { + GroupLogic.actions.resetFlashMessages(); + + expect(clearFlashMessagesSpy).toHaveBeenCalled(); + }); + }); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/group_logic.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/group_logic.ts index be941a231ac96..b895200d3fc22 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/group_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/group_logic.ts @@ -223,8 +223,7 @@ export const GroupLogic = kea>({ } ); - const error = e.response.status === 404 ? NOT_FOUND_MESSAGE : e; - + const error = e.response?.status === 404 ? NOT_FOUND_MESSAGE : e; FlashMessagesLogic.actions.setQueuedMessages({ type: 'error', message: error, @@ -319,7 +318,7 @@ export const GroupLogic = kea>({ const GROUP_USERS_UPDATED_MESSAGE = i18n.translate( 'xpack.enterpriseSearch.workplaceSearch.groups.groupUsersUpdated', { - defaultMessage: 'Successfully updated the users of this group', + defaultMessage: 'Successfully updated the users of this group.', } ); setSuccessMessage(GROUP_USERS_UPDATED_MESSAGE); @@ -351,7 +350,7 @@ export const GroupLogic = kea>({ const GROUP_PRIORITIZATION_UPDATED_MESSAGE = i18n.translate( 'xpack.enterpriseSearch.workplaceSearch.groups.groupPrioritizationUpdated', { - defaultMessage: 'Successfully updated shared source prioritization', + defaultMessage: 'Successfully updated shared source prioritization.', } ); From 01bb5ede3015c8b4bee9c42afd0747380196da72 Mon Sep 17 00:00:00 2001 From: scottybollinger Date: Mon, 19 Oct 2020 16:37:35 -0400 Subject: [PATCH 06/16] Remove redundant messages check This is not needed with global flash messages, as the component will not render with no messages. --- .../workplace_search/views/groups/group_router.tsx | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/group_router.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/group_router.tsx index 73795f789f2bb..1b6f0c4c49a05 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/group_router.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/group_router.tsx @@ -9,7 +9,7 @@ import React, { useEffect } from 'react'; import { useActions, useValues } from 'kea'; import { Route, Switch, useParams } from 'react-router-dom'; -import { FlashMessages, FlashMessagesLogic } from '../../../shared/flash_messages'; +import { FlashMessages } from '../../../shared/flash_messages'; import { SetWorkplaceSearchChrome as SetPageChrome } from '../../../shared/kibana_chrome'; import { SendWorkplaceSearchTelemetry as SendTelemetry } from '../../../shared/telemetry'; @@ -26,7 +26,6 @@ import { GroupSourcePrioritization } from './components/group_source_prioritizat export const GroupRouter: React.FC = () => { const { groupId } = useParams() as { groupId: string }; - const { messages } = useValues(FlashMessagesLogic); const { initializeGroup, resetGroup } = useActions(GroupLogic); const { sharedSourcesModalVisible, @@ -34,8 +33,6 @@ export const GroupRouter: React.FC = () => { group: { name }, } = useValues(GroupLogic); - const hasMessages = messages.length > 0; - useEffect(() => { initializeGroup(groupId); return resetGroup; @@ -43,7 +40,7 @@ export const GroupRouter: React.FC = () => { return ( <> - {hasMessages && } + From 8e6af203df90fe4bc6d1cc80b51afcacc050ed9a Mon Sep 17 00:00:00 2001 From: scottybollinger Date: Mon, 19 Oct 2020 17:36:08 -0400 Subject: [PATCH 07/16] Add tests for group_router --- .../__mocks__/react_router_history.mock.ts | 1 + .../views/groups/group_router.test.tsx | 87 +++++++++++++++++++ 2 files changed, 88 insertions(+) create mode 100644 x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/group_router.test.tsx diff --git a/x-pack/plugins/enterprise_search/public/applications/__mocks__/react_router_history.mock.ts b/x-pack/plugins/enterprise_search/public/applications/__mocks__/react_router_history.mock.ts index 2c833bcfeaf4c..1516aa9096eca 100644 --- a/x-pack/plugins/enterprise_search/public/applications/__mocks__/react_router_history.mock.ts +++ b/x-pack/plugins/enterprise_search/public/applications/__mocks__/react_router_history.mock.ts @@ -28,6 +28,7 @@ jest.mock('react-router-dom', () => ({ ...(jest.requireActual('react-router-dom') as object), useHistory: jest.fn(() => mockHistory), useLocation: jest.fn(() => mockLocation), + useParams: jest.fn(() => ({})), })); /** diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/group_router.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/group_router.test.tsx new file mode 100644 index 0000000000000..6f293920fa387 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/group_router.test.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import '../../../__mocks__/kea.mock'; +import '../../../__mocks__/shallow_useeffect.mock'; + +import { setMockValues, setMockActions } from '../../../__mocks__'; + +import React from 'react'; +import { shallow } from 'enzyme'; + +import { Route, Switch } from 'react-router-dom'; + +import { groups } from '../../__mocks__/groups.mock'; + +import { SetWorkplaceSearchChrome as SetPageChrome } from '../../../shared/kibana_chrome'; + +import { GroupOverview } from './components/group_overview'; +import { GroupSourcePrioritization } from './components/group_source_prioritization'; + +import { GroupRouter } from './group_router'; + +import { FlashMessages } from '../../../shared/flash_messages'; + +import { ManageUsersModal } from './components/manage_users_modal'; +import { SharedSourcesModal } from './components/shared_sources_modal'; + +describe('GroupRouter', () => { + const initializeGroup = jest.fn(); + const resetGroup = jest.fn(); + + beforeEach(() => { + setMockValues({ + sharedSourcesModalVisible: false, + manageUsersModalVisible: false, + group: groups[0], + }); + + setMockActions({ + initializeGroup, + resetGroup, + }); + }); + it('renders', () => { + const wrapper = shallow(); + + expect(wrapper.find(FlashMessages)).toHaveLength(1); + expect(wrapper.find(Switch)).toHaveLength(1); + expect(wrapper.find(Route)).toHaveLength(2); + expect(wrapper.find(GroupOverview)).toHaveLength(1); + expect(wrapper.find(GroupSourcePrioritization)).toHaveLength(1); + }); + + it('renders modals', () => { + setMockValues({ + sharedSourcesModalVisible: true, + manageUsersModalVisible: true, + group: groups[0], + }); + + const wrapper = shallow(); + + expect(wrapper.find(ManageUsersModal)).toHaveLength(1); + expect(wrapper.find(SharedSourcesModal)).toHaveLength(1); + }); + + it('handles breadcrumbs while loading', () => { + setMockValues({ + sharedSourcesModalVisible: false, + manageUsersModalVisible: false, + group: {}, + }); + + const loadingBreadcrumbs = ['Groups', '...']; + + const wrapper = shallow(); + + const firstBreadCrumb = wrapper.find(SetPageChrome).first(); + const lastBreadCrumb = wrapper.find(SetPageChrome).last(); + + expect(firstBreadCrumb.prop('trail')).toEqual([...loadingBreadcrumbs, 'Source Prioritization']); + expect(lastBreadCrumb.prop('trail')).toEqual(loadingBreadcrumbs); + }); +}); From 468e44426dae1f96dde96085187138fce6330db7 Mon Sep 17 00:00:00 2001 From: scottybollinger Date: Mon, 19 Oct 2020 19:45:10 -0400 Subject: [PATCH 08/16] Add group mock --- .../groups/__mocks__/groups_logic.mock.ts | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/__mocks__/groups_logic.mock.ts diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/__mocks__/groups_logic.mock.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/__mocks__/groups_logic.mock.ts new file mode 100644 index 0000000000000..f284725da3eca --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/__mocks__/groups_logic.mock.ts @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { IGroupsValues } from '../group_logic'; + +import { IContentSource, IUser, IGroup } from '../../../types'; + +import { DEFAULT_META } from '../../../../shared/constants'; + +export const mockGroupsValues = { + groups: [] as IGroup[], + contentSources: [] as IContentSource[], + users: [] as IUser[], + groupsDataLoading: true, + groupListLoading: true, + newGroupModalOpen: false, + newGroupName: '', + hasFiltersSet: false, + newGroup: null, + newGroupNameErrors: [], + filterSourcesDropdownOpen: false, + filteredSources: [], + filterUsersDropdownOpen: false, + filteredUsers: [], + allGroupUsersLoading: false, + allGroupUsers: [], + filterValue: '', + groupsMeta: DEFAULT_META, +} as IGroupsValues; From b0c218c02e0d01e87c1dbf08bbb0228aad7ad118 Mon Sep 17 00:00:00 2001 From: scottybollinger Date: Mon, 19 Oct 2020 19:45:55 -0400 Subject: [PATCH 09/16] Add tests for groups_logic --- .../views/groups/groups_logic.test.ts | 437 ++++++++++++++++++ 1 file changed, 437 insertions(+) create mode 100644 x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups_logic.test.ts diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups_logic.test.ts new file mode 100644 index 0000000000000..1d128b30fad50 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups_logic.test.ts @@ -0,0 +1,437 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { resetContext } from 'kea'; + +jest.mock('../../../shared/http', () => ({ + HttpLogic: { + values: { http: { get: jest.fn(), post: jest.fn() } }, + }, +})); +import { HttpLogic } from '../../../shared/http'; + +jest.mock('../../../shared/flash_messages', () => ({ + FlashMessagesLogic: { actions: { clearFlashMessages: jest.fn(), setQueuedMessages: jest.fn() } }, + flashAPIErrors: jest.fn(), + setSuccessMessage: jest.fn(), + setQueuedSuccessMessage: jest.fn(), +})); +import { + FlashMessagesLogic, + flashAPIErrors, + setSuccessMessage, +} from '../../../shared/flash_messages'; + +import { DEFAULT_META } from '../../../shared/constants'; +import { JSON_HEADER as headers } from '../../../../../common/constants'; + +import { groups } from '../../__mocks__/groups.mock'; +import { contentSources } from '../../__mocks__/content_sources.mock'; +import { users } from '../../__mocks__/users.mock'; +import { mockGroupsValues } from './__mocks__/groups_logic.mock'; +import { GroupsLogic } from './groups_logic'; + +// We need to mock out the debounced functionality +const TIMEOUT = 400; +const delay = () => new Promise((resolve) => setTimeout(resolve, TIMEOUT)); + +describe('GroupsLogic', () => { + const clearFlashMessagesSpy = jest.spyOn(FlashMessagesLogic.actions, 'clearFlashMessages'); + const groupsResponse = { + results: groups, + meta: DEFAULT_META, + }; + + beforeEach(() => { + jest.clearAllMocks(); + resetContext({}); + GroupsLogic.mount(); + }); + + it('has expected default values', () => { + expect(GroupsLogic.values).toEqual(mockGroupsValues); + }); + + describe('actions', () => { + describe('onInitializeGroups', () => { + it('sets reducers', () => { + GroupsLogic.actions.onInitializeGroups({ contentSources, users }); + + expect(GroupsLogic.values.groupsDataLoading).toEqual(false); + expect(GroupsLogic.values.contentSources).toEqual(contentSources); + expect(GroupsLogic.values.users).toEqual(users); + }); + }); + + describe('setSearchResults', () => { + it('sets reducers', () => { + GroupsLogic.actions.setSearchResults(groupsResponse); + + expect(GroupsLogic.values.groups).toEqual(groups); + expect(GroupsLogic.values.groupListLoading).toEqual(false); + expect(GroupsLogic.values.newGroupName).toEqual(''); + expect(GroupsLogic.values.groupsMeta).toEqual(DEFAULT_META); + }); + }); + + describe('addFilteredSource', () => { + it('sets reducers', () => { + GroupsLogic.actions.addFilteredSource('foo'); + GroupsLogic.actions.addFilteredSource('bar'); + GroupsLogic.actions.addFilteredSource('baz'); + + expect(GroupsLogic.values.filteredSources).toEqual(['bar', 'baz', 'foo']); + }); + }); + + describe('removeFilteredSource', () => { + it('sets reducers', () => { + GroupsLogic.actions.addFilteredSource('foo'); + GroupsLogic.actions.addFilteredSource('bar'); + GroupsLogic.actions.addFilteredSource('baz'); + GroupsLogic.actions.removeFilteredSource('foo'); + + expect(GroupsLogic.values.filteredSources).toEqual(['bar', 'baz']); + }); + }); + + describe('addFilteredUser', () => { + it('sets reducers', () => { + GroupsLogic.actions.addFilteredUser('foo'); + GroupsLogic.actions.addFilteredUser('bar'); + GroupsLogic.actions.addFilteredUser('baz'); + + expect(GroupsLogic.values.filteredUsers).toEqual(['bar', 'baz', 'foo']); + }); + }); + + describe('removeFilteredUser', () => { + it('sets reducers', () => { + GroupsLogic.actions.addFilteredUser('foo'); + GroupsLogic.actions.addFilteredUser('bar'); + GroupsLogic.actions.addFilteredUser('baz'); + GroupsLogic.actions.removeFilteredUser('foo'); + + expect(GroupsLogic.values.filteredUsers).toEqual(['bar', 'baz']); + }); + }); + + describe('setGroupUsers', () => { + it('sets reducers', () => { + GroupsLogic.actions.setGroupUsers(users); + + expect(GroupsLogic.values.allGroupUsersLoading).toEqual(false); + expect(GroupsLogic.values.allGroupUsers).toEqual(users); + }); + }); + + describe('setAllGroupLoading', () => { + it('sets reducer', () => { + GroupsLogic.actions.setAllGroupLoading(true); + + expect(GroupsLogic.values.allGroupUsersLoading).toEqual(true); + expect(GroupsLogic.values.allGroupUsers).toEqual([]); + }); + }); + + describe('setFilterValue', () => { + it('sets reducer', () => { + GroupsLogic.actions.setFilterValue('foo'); + + expect(GroupsLogic.values.filterValue).toEqual('foo'); + }); + }); + + describe('setActivePage', () => { + it('sets reducer', () => { + const activePage = 3; + GroupsLogic.actions.setActivePage(activePage); + + expect(GroupsLogic.values.groupsMeta).toEqual({ + ...DEFAULT_META, + page: { + ...DEFAULT_META.page, + current: activePage, + }, + }); + }); + }); + + describe('setNewGroupName', () => { + it('sets reducer', () => { + const NEW_NAME = 'new name'; + GroupsLogic.actions.setNewGroupName(NEW_NAME); + + expect(GroupsLogic.values.newGroupName).toEqual(NEW_NAME); + expect(GroupsLogic.values.newGroupNameErrors).toEqual([]); + }); + }); + + describe('setNewGroup', () => { + it('sets reducer', () => { + GroupsLogic.actions.setNewGroup(groups[0]); + + expect(GroupsLogic.values.newGroupModalOpen).toEqual(false); + expect(GroupsLogic.values.newGroup).toEqual(groups[0]); + expect(GroupsLogic.values.newGroupNameErrors).toEqual([]); + expect(GroupsLogic.values.filteredSources).toEqual([]); + expect(GroupsLogic.values.filteredUsers).toEqual([]); + expect(GroupsLogic.values.groupsMeta).toEqual(DEFAULT_META); + }); + }); + + describe('setNewGroupFormErrors', () => { + it('sets reducer', () => { + const errors = ['this is an error']; + GroupsLogic.actions.setNewGroupFormErrors(errors); + + expect(GroupsLogic.values.newGroupNameErrors).toEqual(errors); + }); + }); + + describe('closeNewGroupModal', () => { + it('sets reducer', () => { + GroupsLogic.actions.closeNewGroupModal(); + + expect(GroupsLogic.values.newGroupModalOpen).toEqual(false); + expect(GroupsLogic.values.newGroupName).toEqual(''); + expect(GroupsLogic.values.newGroupNameErrors).toEqual([]); + }); + }); + + describe('closeFilterSourcesDropdown', () => { + it('sets reducer', () => { + // Open dropdown first + GroupsLogic.actions.toggleFilterSourcesDropdown(); + GroupsLogic.actions.closeFilterSourcesDropdown(); + + expect(GroupsLogic.values.filterSourcesDropdownOpen).toEqual(false); + }); + }); + + describe('closeFilterUsersDropdown', () => { + it('sets reducer', () => { + // Open dropdown first + GroupsLogic.actions.toggleFilterUsersDropdown(); + GroupsLogic.actions.closeFilterUsersDropdown(); + + expect(GroupsLogic.values.filterUsersDropdownOpen).toEqual(false); + }); + }); + + describe('setGroupsLoading', () => { + it('sets reducer', () => { + // Set to false first + GroupsLogic.actions.setSearchResults(groupsResponse); + GroupsLogic.actions.setGroupsLoading(); + + expect(GroupsLogic.values.groupListLoading).toEqual(true); + }); + }); + + describe('resetGroups', () => { + it('sets reducer', () => { + GroupsLogic.actions.resetGroups(); + + expect(GroupsLogic.values.newGroup).toEqual(null); + }); + }); + }); + + describe('listeners', () => { + describe('initializeGroups', () => { + it('calls API and sets values', async () => { + const onInitializeGroupsSpy = jest.spyOn(GroupsLogic.actions, 'onInitializeGroups'); + const promise = Promise.resolve(groupsResponse); + (HttpLogic.values.http.get as jest.Mock).mockReturnValue(promise); + + GroupsLogic.actions.initializeGroups(); + expect(HttpLogic.values.http.get).toHaveBeenCalledWith('/api/workplace_search/groups'); + await promise; + expect(onInitializeGroupsSpy).toHaveBeenCalledWith(groupsResponse); + }); + + it('handles error', async () => { + const promise = Promise.reject('this is an error'); + (HttpLogic.values.http.get as jest.Mock).mockReturnValue(promise); + + GroupsLogic.actions.initializeGroups(); + try { + await promise; + } catch { + expect(flashAPIErrors).toHaveBeenCalledWith('this is an error'); + } + }); + }); + + describe('getSearchResults', () => { + const search = { + query: '', + content_source_ids: [], + user_ids: [], + }; + + const payload = { + body: JSON.stringify({ + page: { + current: 1, + size: 10, + }, + search, + }), + headers, + }; + + it('calls API and sets values', async () => { + const setSearchResultsSpy = jest.spyOn(GroupsLogic.actions, 'setSearchResults'); + const promise = Promise.resolve(groups); + (HttpLogic.values.http.post as jest.Mock).mockReturnValue(promise); + + GroupsLogic.actions.getSearchResults(); + await delay(); + expect(HttpLogic.values.http.post).toHaveBeenCalledWith( + '/api/workplace_search/groups/search', + payload + ); + await promise; + expect(setSearchResultsSpy).toHaveBeenCalledWith(groups); + }); + + it('calls API and resets pagination', async () => { + // Set active page to 2 to confirm resetting sends the `payload` value of 1 for the current page. + GroupsLogic.actions.setActivePage(2); + const setSearchResultsSpy = jest.spyOn(GroupsLogic.actions, 'setSearchResults'); + const promise = Promise.resolve(groups); + (HttpLogic.values.http.post as jest.Mock).mockReturnValue(promise); + + GroupsLogic.actions.getSearchResults(true); + await delay(); + expect(HttpLogic.values.http.post).toHaveBeenCalledWith( + '/api/workplace_search/groups/search', + payload + ); + await promise; + expect(setSearchResultsSpy).toHaveBeenCalledWith(groups); + }); + + it('handles error', async () => { + /** + * TODO: There is some sort of issue with Kea's `breakpoint` functionality that causes the + * catch to break the tests. Because this functionality is tested elsewhere successfully, + * without the breakpoint, I'm leaving this in now for 100% coverage and will reach out + * to the plugin author for help with the issue with the test breaking. + */ + const promise = Promise.resolve(undefined); + (HttpLogic.values.http.get as jest.Mock).mockReturnValue(undefined); + + GroupsLogic.actions.getSearchResults(); + await delay(); + try { + await promise; + } catch { + expect(flashAPIErrors).toHaveBeenCalled(); + } + }); + }); + + describe('fetchGroupUsers', () => { + it('calls API and sets values', async () => { + const setGroupUsersSpy = jest.spyOn(GroupsLogic.actions, 'setGroupUsers'); + const promise = Promise.resolve(users); + (HttpLogic.values.http.get as jest.Mock).mockReturnValue(promise); + + GroupsLogic.actions.fetchGroupUsers('123'); + expect(HttpLogic.values.http.get).toHaveBeenCalledWith( + '/api/workplace_search/groups/123/group_users' + ); + await promise; + expect(setGroupUsersSpy).toHaveBeenCalledWith(users); + }); + + it('handles error', async () => { + const promise = Promise.reject('this is an error'); + (HttpLogic.values.http.get as jest.Mock).mockReturnValue(promise); + + GroupsLogic.actions.fetchGroupUsers('123'); + try { + await promise; + } catch { + expect(flashAPIErrors).toHaveBeenCalledWith('this is an error'); + } + }); + }); + + describe('saveNewGroup', () => { + it('calls API and sets values', async () => { + const GROUP_NAME = 'new group'; + GroupsLogic.actions.setNewGroupName(GROUP_NAME); + const setNewGroupSpy = jest.spyOn(GroupsLogic.actions, 'setNewGroup'); + const promise = Promise.resolve(groups[0]); + (HttpLogic.values.http.post as jest.Mock).mockReturnValue(promise); + + GroupsLogic.actions.saveNewGroup(); + expect(HttpLogic.values.http.post).toHaveBeenCalledWith('/api/workplace_search/groups', { + body: JSON.stringify({ group_name: GROUP_NAME }), + headers, + }); + await promise; + expect(setNewGroupSpy).toHaveBeenCalledWith(groups[0]); + }); + + it('handles error', async () => { + const promise = Promise.reject('this is an error'); + (HttpLogic.values.http.post as jest.Mock).mockReturnValue(promise); + + GroupsLogic.actions.saveNewGroup(); + try { + await promise; + } catch { + expect(flashAPIErrors).toHaveBeenCalledWith('this is an error'); + } + }); + }); + + describe('openNewGroupModal', () => { + it('sets reducer and clears flash messages', () => { + GroupsLogic.actions.openNewGroupModal(); + + expect(GroupsLogic.values.newGroupModalOpen).toEqual(true); + expect(GroupsLogic.values.newGroup).toEqual(null); + expect(clearFlashMessagesSpy).toHaveBeenCalled(); + }); + }); + + describe('resetGroupsFilters', () => { + it('sets reducer and clears flash messages', () => { + GroupsLogic.actions.resetGroupsFilters(); + + expect(GroupsLogic.values.filteredSources).toEqual([]); + expect(GroupsLogic.values.filteredUsers).toEqual([]); + expect(GroupsLogic.values.filterValue).toEqual(''); + expect(GroupsLogic.values.groupsMeta).toEqual(DEFAULT_META); + expect(clearFlashMessagesSpy).toHaveBeenCalled(); + }); + }); + + describe('toggleFilterSourcesDropdown', () => { + it('sets reducer and clears flash messages', () => { + GroupsLogic.actions.toggleFilterSourcesDropdown(); + + expect(GroupsLogic.values.filterSourcesDropdownOpen).toEqual(true); + expect(clearFlashMessagesSpy).toHaveBeenCalled(); + }); + }); + + describe('toggleFilterUsersDropdown', () => { + it('sets reducer and clears flash messages', () => { + GroupsLogic.actions.toggleFilterUsersDropdown(); + + expect(GroupsLogic.values.filterUsersDropdownOpen).toEqual(true); + expect(clearFlashMessagesSpy).toHaveBeenCalled(); + }); + }); + }); +}); From 65f35ca3c435e8ac737335a3c306adda1e25a14c Mon Sep 17 00:00:00 2001 From: scottybollinger Date: Mon, 19 Oct 2020 19:52:25 -0400 Subject: [PATCH 10/16] Convert groups_router to use children This allow for testing the component visiblity and aligns with other usage of React Router --- .../workplace_search/views/groups/groups_router.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups_router.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups_router.tsx index adfa10d37c524..a4fe472065d90 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups_router.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups_router.tsx @@ -37,7 +37,9 @@ export const GroupsRouter: React.FC = () => { - + + + ); }; From 40c493649522c9b3bdd8ed1f79488ffc9da3c6b4 Mon Sep 17 00:00:00 2001 From: scottybollinger Date: Mon, 19 Oct 2020 19:54:16 -0400 Subject: [PATCH 11/16] Add tests for groups_router --- .../views/groups/groups_router.test.tsx | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups_router.test.tsx diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups_router.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups_router.test.tsx new file mode 100644 index 0000000000000..0b2b1ad05dfd7 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups_router.test.tsx @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import '../../../__mocks__/kea.mock'; +import '../../../__mocks__/shallow_useeffect.mock'; + +import { setMockActions } from '../../../__mocks__'; + +import React from 'react'; +import { shallow } from 'enzyme'; + +import { Route, Switch } from 'react-router-dom'; + +import { GroupsRouter } from './groups_router'; + +import { GroupRouter } from './group_router'; +import { Groups } from './groups'; + +describe('GroupsRouter', () => { + const initializeGroups = jest.fn(); + + beforeEach(() => { + setMockActions({ initializeGroups }); + }); + + it('renders', () => { + const wrapper = shallow(); + + expect(wrapper.find(Switch)).toHaveLength(1); + expect(wrapper.find(Route)).toHaveLength(2); + expect(wrapper.find(GroupRouter)).toHaveLength(1); + expect(wrapper.find(Groups)).toHaveLength(1); + }); +}); From 99a630cc4808498c8f07aaa38acd476a5dca5ba3 Mon Sep 17 00:00:00 2001 From: scottybollinger Date: Tue, 20 Oct 2020 12:56:34 -0400 Subject: [PATCH 12/16] Refactor pagination logic This commit removes the useDidUpdateEffect custom hook and moves the logic to fetch the search results to a listener inside of Kea. --- .../shared/use_did_update_effect/index.ts | 7 ---- .../use_did_update_effect.test.tsx | 33 ------------------- .../use_did_update_effect.tsx | 23 ------------- .../workplace_search/views/groups/groups.tsx | 8 +---- .../views/groups/groups_logic.test.ts | 33 ++++++++++--------- .../views/groups/groups_logic.ts | 3 ++ 6 files changed, 22 insertions(+), 85 deletions(-) delete mode 100644 x-pack/plugins/enterprise_search/public/applications/shared/use_did_update_effect/index.ts delete mode 100644 x-pack/plugins/enterprise_search/public/applications/shared/use_did_update_effect/use_did_update_effect.test.tsx delete mode 100644 x-pack/plugins/enterprise_search/public/applications/shared/use_did_update_effect/use_did_update_effect.tsx diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/use_did_update_effect/index.ts b/x-pack/plugins/enterprise_search/public/applications/shared/use_did_update_effect/index.ts deleted file mode 100644 index 05c60ebced088..0000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/shared/use_did_update_effect/index.ts +++ /dev/null @@ -1,7 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -export { useDidUpdateEffect } from './use_did_update_effect'; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/use_did_update_effect/use_did_update_effect.test.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/use_did_update_effect/use_did_update_effect.test.tsx deleted file mode 100644 index e3d2ffb44f01e..0000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/shared/use_did_update_effect/use_did_update_effect.test.tsx +++ /dev/null @@ -1,33 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import React, { useState } from 'react'; -import { mount } from 'enzyme'; - -import { EuiLink } from '@elastic/eui'; - -import { useDidUpdateEffect } from './use_did_update_effect'; - -const fn = jest.fn(); - -const TestHook = ({ value }: { value: number }) => { - const [inputValue, setValue] = useState(value); - useDidUpdateEffect(fn, [inputValue]); - return setValue(2)} />; -}; - -const wrapper = mount(); - -describe('useDidUpdateEffect', () => { - it('should not fire function when value unchanged', () => { - expect(fn).not.toHaveBeenCalled(); - }); - - it('should fire function when value changed', () => { - wrapper.find(EuiLink).simulate('click'); - expect(fn).toHaveBeenCalled(); - }); -}); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/use_did_update_effect/use_did_update_effect.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/use_did_update_effect/use_did_update_effect.tsx deleted file mode 100644 index 4c3e10fc84b84..0000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/shared/use_did_update_effect/use_did_update_effect.tsx +++ /dev/null @@ -1,23 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -/* - * Sometimes we don't want to fire the initial useEffect call. - * This custom Hook only fires after the intial render has completed. - */ -import { useEffect, useRef, DependencyList } from 'react'; - -export const useDidUpdateEffect = (fn: Function, inputs: DependencyList) => { - const didMountRef = useRef(false); - - useEffect(() => { - if (didMountRef.current) { - fn(); - } else { - didMountRef.current = true; - } - }, inputs); -}; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups.tsx index 34a66282a312d..4d15b3ae0119e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups.tsx @@ -19,7 +19,6 @@ import { ViewContentHeader } from '../../components/shared/view_content_header'; import { getGroupPath, USERS_PATH } from '../../routes'; -import { useDidUpdateEffect } from '../../../shared/use_did_update_effect'; import { FlashMessages, FlashMessagesLogic } from '../../../shared/flash_messages'; import { GroupsLogic } from './groups_logic'; @@ -40,7 +39,7 @@ export const Groups: React.FC = () => { groupListLoading, hasFiltersSet, groupsMeta: { - page: { current: activePage, total_results: numGroups }, + page: { total_results: numGroups }, }, filteredSources, filteredUsers, @@ -56,11 +55,6 @@ export const Groups: React.FC = () => { return resetGroups; }, [filteredSources, filteredUsers, filterValue]); - // Because the initial search happens above, we want to skip the initial search and use the custom hook to do so. - useDidUpdateEffect(() => { - getSearchResults(); - }, [activePage]); - if (groupsDataLoading) { return ; } diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups_logic.test.ts index 1d128b30fad50..f76d9b1f33509 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups_logic.test.ts @@ -145,21 +145,6 @@ describe('GroupsLogic', () => { }); }); - describe('setActivePage', () => { - it('sets reducer', () => { - const activePage = 3; - GroupsLogic.actions.setActivePage(activePage); - - expect(GroupsLogic.values.groupsMeta).toEqual({ - ...DEFAULT_META, - page: { - ...DEFAULT_META.page, - current: activePage, - }, - }); - }); - }); - describe('setNewGroupName', () => { it('sets reducer', () => { const NEW_NAME = 'new name'; @@ -394,6 +379,24 @@ describe('GroupsLogic', () => { }); }); + describe('setActivePage', () => { + it('sets reducer', () => { + const getSearchResultsSpy = jest.spyOn(GroupsLogic.actions, 'getSearchResults'); + const activePage = 3; + GroupsLogic.actions.setActivePage(activePage); + + expect(GroupsLogic.values.groupsMeta).toEqual({ + ...DEFAULT_META, + page: { + ...DEFAULT_META.page, + current: activePage, + }, + }); + + expect(getSearchResultsSpy).toHaveBeenCalled(); + }); + }); + describe('openNewGroupModal', () => { it('sets reducer and clears flash messages', () => { GroupsLogic.actions.openNewGroupModal(); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups_logic.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups_logic.ts index 35d4387b4cf3d..685a2651cb24a 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups_logic.ts @@ -335,6 +335,9 @@ export const GroupsLogic = kea>({ flashAPIErrors(e); } }, + setActivePage: () => { + actions.getSearchResults(); + }, openNewGroupModal: () => { FlashMessagesLogic.actions.clearFlashMessages(); }, From f16d3c5d100009c659c0f262a9c1e69b435fd359 Mon Sep 17 00:00:00 2001 From: scottybollinger Date: Tue, 20 Oct 2020 13:30:02 -0400 Subject: [PATCH 13/16] dd tests for main groups container --- .../views/groups/groups.test.tsx | 180 ++++++++++++++++++ .../workplace_search/views/groups/groups.tsx | 6 +- 2 files changed, 185 insertions(+), 1 deletion(-) create mode 100644 x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups.test.tsx diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups.test.tsx new file mode 100644 index 0000000000000..98f40acb96469 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups.test.tsx @@ -0,0 +1,180 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import '../../../__mocks__/kea.mock'; +import '../../../__mocks__/shallow_useeffect.mock'; + +import { setMockActions, setMockValues } from '../../../__mocks__'; +import { groups } from '../../__mocks__/groups.mock'; + +import React from 'react'; +import { shallow } from 'enzyme'; + +import { Groups } from './groups'; + +import { ViewContentHeader } from '../../components/shared/view_content_header'; +import { Loading } from '../../components/shared/loading'; +import { FlashMessages } from '../../../shared/flash_messages'; + +import { AddGroupModal } from './components/add_group_modal'; +import { ClearFiltersLink } from './components/clear_filters_link'; +import { GroupsTable } from './components/groups_table'; +import { TableFilters } from './components/table_filters'; + +import { DEFAULT_META } from '../../../shared/constants'; + +import { EuiFieldSearch, EuiLoadingSpinner } from '@elastic/eui'; +import { EuiButton as EuiLinkButton } from '../../../shared/react_router_helpers'; + +const getSearchResults = jest.fn(); +const openNewGroupModal = jest.fn(); +const resetGroups = jest.fn(); +const setFilterValue = jest.fn(); +const setActivePage = jest.fn(); + +const mockMeta = { + ...DEFAULT_META, + page: { + current: 1, + total_results: 50, + total_pages: 5, + }, +}; + +const mockSuccessMessage = { + type: 'success', + message: 'group added', +}; + +const mockValues = { + groups, + groupsDataLoading: false, + newGroupModalOpen: false, + newGroup: null, + groupListLoading: false, + hasFiltersSet: false, + groupsMeta: mockMeta, + filteredSources: [], + filteredUsers: [], + filterValue: '', + isFederatedAuth: false, +}; + +describe('GroupOverview', () => { + beforeEach(() => { + setMockActions({ + getSearchResults, + openNewGroupModal, + resetGroups, + setFilterValue, + setActivePage, + }); + setMockValues(mockValues); + }); + + it('renders', () => { + const wrapper = shallow(); + + expect(wrapper.find(ViewContentHeader)).toHaveLength(1); + expect(wrapper.find(GroupsTable)).toHaveLength(1); + expect(wrapper.find(TableFilters)).toHaveLength(1); + }); + + it('returns loading when loading', () => { + setMockValues({ ...mockValues, groupsDataLoading: true }); + const wrapper = shallow(); + + expect(wrapper.find(Loading)).toHaveLength(1); + }); + + it('gets search results when filters changed', () => { + const wrapper = shallow(); + + const filters = wrapper.find(TableFilters).dive().shallow(); + const input = filters.find(EuiFieldSearch); + + input.simulate('change', { target: { value: 'Query' } }); + + expect(getSearchResults).toHaveBeenCalledWith(true); + }); + + it('renders manage button when new group added', () => { + setMockValues({ + ...mockValues, + newGroup: { name: 'group', id: '123' }, + messages: [mockSuccessMessage], + }); + const wrapper = shallow(); + const flashMessages = wrapper.find(FlashMessages).dive().shallow(); + + expect(flashMessages.find('[data-test-subj="NewGroupManageButton"]')).toHaveLength(1); + }); + + it('renders ClearFiltersLink when filters set', () => { + setMockValues({ + ...mockValues, + hasFiltersSet: true, + groupsMeta: DEFAULT_META, + }); + + const wrapper = shallow(); + + expect(wrapper.find(ClearFiltersLink)).toHaveLength(1); + }); + + it('renders inviteUsersButton when not federated auth', () => { + setMockValues({ + ...mockValues, + isFederatedAuth: false, + }); + + const wrapper = shallow(); + + const Action: React.FC = () => + wrapper.find(ViewContentHeader).props().action as React.ReactElement | null; + const action = shallow(); + + expect(action.find('[data-test-subj="InviteUsersButton"]')).toHaveLength(1); + expect(action.find(EuiLinkButton)).toHaveLength(1); + }); + + it('does not render inviteUsersButton when federated auth', () => { + setMockValues({ + ...mockValues, + isFederatedAuth: true, + }); + + const wrapper = shallow(); + + const Action: React.FC = () => + wrapper.find(ViewContentHeader).props().action as React.ReactElement | null; + const action = shallow(); + + expect(action.find('[data-test-subj="InviteUsersButton"]')).toHaveLength(0); + }); + + it('renders EuiLoadingSpinner when loading', () => { + setMockValues({ + ...mockValues, + groupListLoading: true, + }); + + const wrapper = shallow(); + + expect(wrapper.find(EuiLoadingSpinner)).toHaveLength(1); + }); + + it('renders AddGroupModal', () => { + setMockValues({ + ...mockValues, + newGroupModalOpen: true, + }); + + const wrapper = shallow(); + + expect(wrapper.find(AddGroupModal)).toHaveLength(1); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups.tsx index 4d15b3ae0119e..ae87ef735bb9f 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups.tsx @@ -61,7 +61,11 @@ export const Groups: React.FC = () => { if (newGroup && hasMessages) { messages[0].description = ( - + {i18n.translate('xpack.enterpriseSearch.workplaceSearch.groups.newGroup.action', { defaultMessage: 'Manage Group', })} From b0974792f1dc7f02f27e58aad409ef8e64570c94 Mon Sep 17 00:00:00 2001 From: scottybollinger Date: Tue, 20 Oct 2020 13:34:14 -0400 Subject: [PATCH 14/16] Lint fixes --- .../views/groups/__mocks__/groups_logic.mock.ts | 2 +- .../workplace_search/views/groups/groups_logic.test.ts | 6 +----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/__mocks__/groups_logic.mock.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/__mocks__/groups_logic.mock.ts index f284725da3eca..0483799b73093 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/__mocks__/groups_logic.mock.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/__mocks__/groups_logic.mock.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { IGroupsValues } from '../group_logic'; +import { IGroupsValues } from '../groups_logic'; import { IContentSource, IUser, IGroup } from '../../../types'; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups_logic.test.ts index f76d9b1f33509..fef2577cfa255 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups_logic.test.ts @@ -19,11 +19,7 @@ jest.mock('../../../shared/flash_messages', () => ({ setSuccessMessage: jest.fn(), setQueuedSuccessMessage: jest.fn(), })); -import { - FlashMessagesLogic, - flashAPIErrors, - setSuccessMessage, -} from '../../../shared/flash_messages'; +import { FlashMessagesLogic, flashAPIErrors } from '../../../shared/flash_messages'; import { DEFAULT_META } from '../../../shared/constants'; import { JSON_HEADER as headers } from '../../../../../common/constants'; From 234c63b4014a4793faf307329443ce304dd522f4 Mon Sep 17 00:00:00 2001 From: scottybollinger Date: Wed, 21 Oct 2020 10:45:16 -0400 Subject: [PATCH 15/16] Fix broken test and remove comment The issue was that the delay needed to be in the catch block to properly execute. --- .../views/groups/groups_logic.test.ts | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups_logic.test.ts index fef2577cfa255..f7ac91b1648d1 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups_logic.test.ts @@ -299,20 +299,14 @@ describe('GroupsLogic', () => { }); it('handles error', async () => { - /** - * TODO: There is some sort of issue with Kea's `breakpoint` functionality that causes the - * catch to break the tests. Because this functionality is tested elsewhere successfully, - * without the breakpoint, I'm leaving this in now for 100% coverage and will reach out - * to the plugin author for help with the issue with the test breaking. - */ - const promise = Promise.resolve(undefined); - (HttpLogic.values.http.get as jest.Mock).mockReturnValue(undefined); + const promise = Promise.reject('this is an error'); + (HttpLogic.values.http.post as jest.Mock).mockReturnValue(promise); GroupsLogic.actions.getSearchResults(); - await delay(); try { await promise; } catch { + await delay(); expect(flashAPIErrors).toHaveBeenCalled(); } }); From 45713bf364a88ba00c7073f7674ed6a103288f1c Mon Sep 17 00:00:00 2001 From: scottybollinger Date: Wed, 21 Oct 2020 10:55:45 -0400 Subject: [PATCH 16/16] Add comments and make test explicit In other places in the tests, we explicitly test for an error string in the catch block. Changing this to match. --- .../workplace_search/views/groups/groups_logic.test.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups_logic.test.ts index f7ac91b1648d1..18206573a3978 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups_logic.test.ts @@ -289,6 +289,7 @@ describe('GroupsLogic', () => { (HttpLogic.values.http.post as jest.Mock).mockReturnValue(promise); GroupsLogic.actions.getSearchResults(true); + // Account for `breakpoint` that debounces filter value. await delay(); expect(HttpLogic.values.http.post).toHaveBeenCalledWith( '/api/workplace_search/groups/search', @@ -306,8 +307,9 @@ describe('GroupsLogic', () => { try { await promise; } catch { + // Account for `breakpoint` that debounces filter value. await delay(); - expect(flashAPIErrors).toHaveBeenCalled(); + expect(flashAPIErrors).toHaveBeenCalledWith('this is an error'); } }); });