mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
* Update id of second sources mock to be unique Both were originally set to ‘123’ * 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. * Add group mock * Fix typo in reducer name * Add tests for group_logic * Remove redundant messages check This is not needed with global flash messages, as the component will not render with no messages. * Add tests for group_router * Add group mock * Add tests for groups_logic * Convert groups_router to use children This allow for testing the component visiblity and aligns with other usage of React Router * Add tests for groups_router * 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. * dd tests for main groups container * Lint fixes * Fix broken test and remove comment The issue was that the delay needed to be in the catch block to properly execute. * 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. Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
eb05a569d0
commit
837f464c62
17 changed files with 1324 additions and 88 deletions
|
@ -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(() => ({})),
|
||||
}));
|
||||
|
||||
/**
|
||||
|
|
|
@ -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';
|
|
@ -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 <EuiLink onClick={() => setValue(2)} />;
|
||||
};
|
||||
|
||||
const wrapper = mount(<TestHook value={1} />);
|
||||
|
||||
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();
|
||||
});
|
||||
});
|
|
@ -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);
|
||||
};
|
|
@ -20,7 +20,7 @@ export const contentSources = [
|
|||
boost: 1,
|
||||
},
|
||||
{
|
||||
id: '123',
|
||||
id: '124',
|
||||
serviceType: 'jira',
|
||||
searchable: true,
|
||||
supportedByLicense: true,
|
||||
|
|
|
@ -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: [],
|
||||
sharedSourcesModalVisible: false,
|
||||
confirmDeleteModalVisible: false,
|
||||
groupNameInputValue: '',
|
||||
selectedGroupSources: [],
|
||||
selectedGroupUsers: [],
|
||||
groupPrioritiesUnchanged: true,
|
||||
activeSourcePriorities: {} as ISourcePriority,
|
||||
cachedSourcePriorities: {} as ISourcePriority,
|
||||
} as IGroupValues;
|
|
@ -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 '../groups_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;
|
|
@ -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();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -56,13 +56,11 @@ export interface IGroupActions {
|
|||
}
|
||||
|
||||
export interface IGroupValues {
|
||||
contentSources: IContentSourceDetails[];
|
||||
users: IUser[];
|
||||
group: IGroupDetails;
|
||||
dataLoading: boolean;
|
||||
manageUsersModalVisible: boolean;
|
||||
managerModalFormErrors: string[];
|
||||
sharedSourcesModalModalVisible: boolean;
|
||||
sharedSourcesModalVisible: boolean;
|
||||
confirmDeleteModalVisible: boolean;
|
||||
groupNameInputValue: string;
|
||||
selectedGroupSources: string[];
|
||||
|
@ -138,7 +136,7 @@ export const GroupLogic = kea<MakeLogicType<IGroupValues, IGroupActions>>({
|
|||
hideManageUsersModal: () => [],
|
||||
},
|
||||
],
|
||||
sharedSourcesModalModalVisible: [
|
||||
sharedSourcesModalVisible: [
|
||||
false,
|
||||
{
|
||||
showSharedSourcesModal: () => true,
|
||||
|
@ -225,8 +223,7 @@ export const GroupLogic = kea<MakeLogicType<IGroupValues, IGroupActions>>({
|
|||
}
|
||||
);
|
||||
|
||||
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,
|
||||
|
@ -321,7 +318,7 @@ export const GroupLogic = kea<MakeLogicType<IGroupValues, IGroupActions>>({
|
|||
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);
|
||||
|
@ -353,7 +350,7 @@ export const GroupLogic = kea<MakeLogicType<IGroupValues, IGroupActions>>({
|
|||
const GROUP_PRIORITIZATION_UPDATED_MESSAGE = i18n.translate(
|
||||
'xpack.enterpriseSearch.workplaceSearch.groups.groupPrioritizationUpdated',
|
||||
{
|
||||
defaultMessage: 'Successfully updated shared source prioritization',
|
||||
defaultMessage: 'Successfully updated shared source prioritization.',
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
@ -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(<GroupRouter />);
|
||||
|
||||
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(<GroupRouter />);
|
||||
|
||||
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(<GroupRouter />);
|
||||
|
||||
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);
|
||||
});
|
||||
});
|
|
@ -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,16 +26,13 @@ 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 {
|
||||
sharedSourcesModalModalVisible,
|
||||
sharedSourcesModalVisible,
|
||||
manageUsersModalVisible,
|
||||
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 && <FlashMessages />}
|
||||
<FlashMessages />
|
||||
<Switch>
|
||||
<Route path={GROUP_SOURCE_PRIORITIZATION_PATH}>
|
||||
<SetPageChrome trail={[NAV.GROUPS, name || '...', NAV.SOURCE_PRIORITIZATION]} />
|
||||
|
@ -55,7 +52,7 @@ export const GroupRouter: React.FC = () => {
|
|||
<GroupOverview />
|
||||
</Route>
|
||||
</Switch>
|
||||
{sharedSourcesModalModalVisible && <SharedSourcesModal />}
|
||||
{sharedSourcesModalVisible && <SharedSourcesModal />}
|
||||
{manageUsersModalVisible && <ManageUsersModal />}
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -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(<Groups />);
|
||||
|
||||
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(<Groups />);
|
||||
|
||||
expect(wrapper.find(Loading)).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('gets search results when filters changed', () => {
|
||||
const wrapper = shallow(<Groups />);
|
||||
|
||||
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(<Groups />);
|
||||
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(<Groups />);
|
||||
|
||||
expect(wrapper.find(ClearFiltersLink)).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('renders inviteUsersButton when not federated auth', () => {
|
||||
setMockValues({
|
||||
...mockValues,
|
||||
isFederatedAuth: false,
|
||||
});
|
||||
|
||||
const wrapper = shallow(<Groups />);
|
||||
|
||||
const Action: React.FC = () =>
|
||||
wrapper.find(ViewContentHeader).props().action as React.ReactElement<any, any> | null;
|
||||
const action = shallow(<Action />);
|
||||
|
||||
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(<Groups />);
|
||||
|
||||
const Action: React.FC = () =>
|
||||
wrapper.find(ViewContentHeader).props().action as React.ReactElement<any, any> | null;
|
||||
const action = shallow(<Action />);
|
||||
|
||||
expect(action.find('[data-test-subj="InviteUsersButton"]')).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('renders EuiLoadingSpinner when loading', () => {
|
||||
setMockValues({
|
||||
...mockValues,
|
||||
groupListLoading: true,
|
||||
});
|
||||
|
||||
const wrapper = shallow(<Groups />);
|
||||
|
||||
expect(wrapper.find(EuiLoadingSpinner)).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('renders AddGroupModal', () => {
|
||||
setMockValues({
|
||||
...mockValues,
|
||||
newGroupModalOpen: true,
|
||||
});
|
||||
|
||||
const wrapper = shallow(<Groups />);
|
||||
|
||||
expect(wrapper.find(AddGroupModal)).toHaveLength(1);
|
||||
});
|
||||
});
|
|
@ -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,18 +55,17 @@ 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 <Loading />;
|
||||
}
|
||||
|
||||
if (newGroup && hasMessages) {
|
||||
messages[0].description = (
|
||||
<EuiLinkButton to={getGroupPath(newGroup.id)} color="primary">
|
||||
<EuiLinkButton
|
||||
to={getGroupPath(newGroup.id)}
|
||||
color="primary"
|
||||
data-test-subj="NewGroupManageButton"
|
||||
>
|
||||
{i18n.translate('xpack.enterpriseSearch.workplaceSearch.groups.newGroup.action', {
|
||||
defaultMessage: 'Manage Group',
|
||||
})}
|
||||
|
|
|
@ -0,0 +1,432 @@
|
|||
/*
|
||||
* 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 } 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('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);
|
||||
// Account for `breakpoint` that debounces filter value.
|
||||
await delay();
|
||||
expect(HttpLogic.values.http.post).toHaveBeenCalledWith(
|
||||
'/api/workplace_search/groups/search',
|
||||
payload
|
||||
);
|
||||
await promise;
|
||||
expect(setSearchResultsSpy).toHaveBeenCalledWith(groups);
|
||||
});
|
||||
|
||||
it('handles error', async () => {
|
||||
const promise = Promise.reject('this is an error');
|
||||
(HttpLogic.values.http.post as jest.Mock).mockReturnValue(promise);
|
||||
|
||||
GroupsLogic.actions.getSearchResults();
|
||||
try {
|
||||
await promise;
|
||||
} catch {
|
||||
// Account for `breakpoint` that debounces filter value.
|
||||
await delay();
|
||||
expect(flashAPIErrors).toHaveBeenCalledWith('this is an error');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
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('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();
|
||||
|
||||
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();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -335,6 +335,9 @@ export const GroupsLogic = kea<MakeLogicType<IGroupsValues, IGroupsActions>>({
|
|||
flashAPIErrors(e);
|
||||
}
|
||||
},
|
||||
setActivePage: () => {
|
||||
actions.getSearchResults();
|
||||
},
|
||||
openNewGroupModal: () => {
|
||||
FlashMessagesLogic.actions.clearFlashMessages();
|
||||
},
|
||||
|
|
|
@ -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(<GroupsRouter />);
|
||||
|
||||
expect(wrapper.find(Switch)).toHaveLength(1);
|
||||
expect(wrapper.find(Route)).toHaveLength(2);
|
||||
expect(wrapper.find(GroupRouter)).toHaveLength(1);
|
||||
expect(wrapper.find(Groups)).toHaveLength(1);
|
||||
});
|
||||
});
|
|
@ -37,7 +37,9 @@ export const GroupsRouter: React.FC = () => {
|
|||
<SendTelemetry action="viewed" metric="groups" />
|
||||
<Groups />
|
||||
</Route>
|
||||
<Route path={GROUP_PATH} component={GroupRouter} />
|
||||
<Route path={GROUP_PATH}>
|
||||
<GroupRouter />
|
||||
</Route>
|
||||
</Switch>
|
||||
);
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue