[Workplace Search] Add top-level tests for Groups (#81215) (#81425)

* 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:
Scotty Bollinger 2020-10-21 23:24:12 -04:00 committed by GitHub
parent eb05a569d0
commit 837f464c62
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 1324 additions and 88 deletions

View file

@ -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(() => ({})),
}));
/**

View file

@ -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';

View file

@ -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();
});
});

View file

@ -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);
};

View file

@ -20,7 +20,7 @@ export const contentSources = [
boost: 1,
},
{
id: '123',
id: '124',
serviceType: 'jira',
searchable: true,
supportedByLicense: true,

View file

@ -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;

View file

@ -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;

View file

@ -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();
});
});
});
});

View file

@ -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.',
}
);

View file

@ -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);
});
});

View file

@ -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 />}
</>
);

View file

@ -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);
});
});

View file

@ -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',
})}

View file

@ -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();
});
});
});
});

View file

@ -335,6 +335,9 @@ export const GroupsLogic = kea<MakeLogicType<IGroupsValues, IGroupsActions>>({
flashAPIErrors(e);
}
},
setActivePage: () => {
actions.getSearchResults();
},
openNewGroupModal: () => {
FlashMessagesLogic.actions.clearFlashMessages();
},

View file

@ -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);
});
});

View file

@ -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>
);
};