[Workplace Search] Add source logic and sources logic unit tests (#89247)
* Move shared data to mock * Change name of mock Everywhere else we don’t use the “mock” prefix for mocked data so I’m changing here to match. Also added missing “size” prop from mock. * Remove unused actions These were missed on the migration to the new add_source_logic file. All of that logic lives there now * Add tests for source logic * REmove resetFlashMessages This is no longer used as Kibana resets its own. This was removed from the component already. * Export items for use in tests * Remove unnecessary condition It’s literally not possible for this function to receive an empty contentSources parameter. Not sure why this was added. Even if the server sends response with no privateContentSources, the reducer falls back to an empty array. * Add tests for sources logic * Fix typo
This commit is contained in:
parent
a4c884b92b
commit
110e880fbf
|
@ -323,3 +323,14 @@ export const mostRecentIndexJob = {
|
|||
activeReindexJobId: '123',
|
||||
numDocumentsWithErrors: 1,
|
||||
};
|
||||
|
||||
export const contentItems = [
|
||||
{
|
||||
id: '1234',
|
||||
last_updated: '2021-01-21',
|
||||
},
|
||||
{
|
||||
id: '1235',
|
||||
last_updated: '2021-01-20',
|
||||
},
|
||||
];
|
||||
|
|
|
@ -6,10 +6,11 @@
|
|||
|
||||
import { DEFAULT_META } from '../../shared/constants';
|
||||
|
||||
export const mockMeta = {
|
||||
export const meta = {
|
||||
...DEFAULT_META,
|
||||
page: {
|
||||
current: 1,
|
||||
size: 5,
|
||||
total_results: 50,
|
||||
total_pages: 5,
|
||||
},
|
||||
|
|
|
@ -20,8 +20,8 @@ import {
|
|||
EuiLink,
|
||||
} from '@elastic/eui';
|
||||
|
||||
import { mockMeta } from '../../../__mocks__/meta.mock';
|
||||
import { fullContentSources } from '../../../__mocks__/content_sources.mock';
|
||||
import { meta } from '../../../__mocks__/meta.mock';
|
||||
import { fullContentSources, contentItems } from '../../../__mocks__/content_sources.mock';
|
||||
|
||||
import { DEFAULT_META } from '../../../../shared/constants';
|
||||
import { ComponentLoader } from '../../../components/shared/component_loader';
|
||||
|
@ -38,17 +38,8 @@ describe('SourceContent', () => {
|
|||
|
||||
const mockValues = {
|
||||
contentSource: fullContentSources[0],
|
||||
contentMeta: mockMeta,
|
||||
contentItems: [
|
||||
{
|
||||
id: '1234',
|
||||
last_updated: '2021-01-21',
|
||||
},
|
||||
{
|
||||
id: '1235',
|
||||
last_updated: '2021-01-20',
|
||||
},
|
||||
],
|
||||
contentMeta: meta,
|
||||
contentItems,
|
||||
contentFilterValue: '',
|
||||
dataLoading: false,
|
||||
sectionLoading: false,
|
||||
|
|
|
@ -0,0 +1,444 @@
|
|||
/*
|
||||
* 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 {
|
||||
LogicMounter,
|
||||
mockFlashMessageHelpers,
|
||||
mockHttpValues,
|
||||
mockKibanaValues,
|
||||
expectedAsyncError,
|
||||
} from '../../../__mocks__';
|
||||
|
||||
import { AppLogic } from '../../app_logic';
|
||||
jest.mock('../../app_logic', () => ({
|
||||
AppLogic: { values: { isOrganization: true } },
|
||||
}));
|
||||
|
||||
import {
|
||||
fullContentSources,
|
||||
sourceConfigData,
|
||||
contentItems,
|
||||
} from '../../__mocks__/content_sources.mock';
|
||||
import { meta } from '../../__mocks__/meta.mock';
|
||||
|
||||
import { DEFAULT_META } from '../../../shared/constants';
|
||||
import { NOT_FOUND_PATH } from '../../routes';
|
||||
|
||||
import { SourceLogic } from './source_logic';
|
||||
|
||||
describe('SourceLogic', () => {
|
||||
const { http } = mockHttpValues;
|
||||
const {
|
||||
clearFlashMessages,
|
||||
flashAPIErrors,
|
||||
setSuccessMessage,
|
||||
setQueuedSuccessMessage,
|
||||
} = mockFlashMessageHelpers;
|
||||
const { navigateToUrl } = mockKibanaValues;
|
||||
const { mount, getListeners } = new LogicMounter(SourceLogic);
|
||||
|
||||
const contentSource = fullContentSources[0];
|
||||
|
||||
const defaultValues = {
|
||||
contentSource: {},
|
||||
contentItems: [],
|
||||
sourceConfigData: {},
|
||||
dataLoading: true,
|
||||
sectionLoading: true,
|
||||
buttonLoading: false,
|
||||
contentMeta: DEFAULT_META,
|
||||
contentFilterValue: '',
|
||||
};
|
||||
|
||||
const searchServerResponse = {
|
||||
results: contentItems,
|
||||
meta,
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
mount();
|
||||
});
|
||||
|
||||
it('has expected default values', () => {
|
||||
expect(SourceLogic.values).toEqual(defaultValues);
|
||||
});
|
||||
|
||||
describe('actions', () => {
|
||||
it('onInitializeSource', () => {
|
||||
SourceLogic.actions.onInitializeSource(contentSource);
|
||||
|
||||
expect(SourceLogic.values.contentSource).toEqual(contentSource);
|
||||
expect(SourceLogic.values.dataLoading).toEqual(false);
|
||||
});
|
||||
|
||||
it('onUpdateSourceName', () => {
|
||||
const NAME = 'foo';
|
||||
SourceLogic.actions.onInitializeSource(contentSource);
|
||||
SourceLogic.actions.onUpdateSourceName(NAME);
|
||||
|
||||
expect(SourceLogic.values.contentSource).toEqual({
|
||||
...contentSource,
|
||||
name: NAME,
|
||||
});
|
||||
expect(setSuccessMessage).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('setSourceConfigData', () => {
|
||||
SourceLogic.actions.setSourceConfigData(sourceConfigData);
|
||||
|
||||
expect(SourceLogic.values.sourceConfigData).toEqual(sourceConfigData);
|
||||
expect(SourceLogic.values.dataLoading).toEqual(false);
|
||||
});
|
||||
|
||||
it('setSearchResults', () => {
|
||||
SourceLogic.actions.setSearchResults(searchServerResponse);
|
||||
|
||||
expect(SourceLogic.values.contentItems).toEqual(contentItems);
|
||||
expect(SourceLogic.values.contentMeta).toEqual(meta);
|
||||
expect(SourceLogic.values.sectionLoading).toEqual(false);
|
||||
});
|
||||
|
||||
it('setContentFilterValue', () => {
|
||||
const VALUE = 'bar';
|
||||
SourceLogic.actions.setSearchResults(searchServerResponse);
|
||||
SourceLogic.actions.onInitializeSource(contentSource);
|
||||
SourceLogic.actions.setContentFilterValue(VALUE);
|
||||
|
||||
expect(SourceLogic.values.contentMeta).toEqual({
|
||||
...meta,
|
||||
page: {
|
||||
...meta.page,
|
||||
current: DEFAULT_META.page.current,
|
||||
},
|
||||
});
|
||||
expect(SourceLogic.values.contentFilterValue).toEqual(VALUE);
|
||||
});
|
||||
|
||||
it('setActivePage', () => {
|
||||
const PAGE = 2;
|
||||
SourceLogic.actions.setSearchResults(searchServerResponse);
|
||||
SourceLogic.actions.setActivePage(PAGE);
|
||||
|
||||
expect(SourceLogic.values.contentMeta).toEqual({
|
||||
...meta,
|
||||
page: {
|
||||
...meta.page,
|
||||
current: PAGE,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('setButtonNotLoading', () => {
|
||||
// Set button state to loading
|
||||
SourceLogic.actions.removeContentSource(contentSource.id);
|
||||
SourceLogic.actions.setButtonNotLoading();
|
||||
|
||||
expect(SourceLogic.values.buttonLoading).toEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('listeners', () => {
|
||||
describe('initializeSource', () => {
|
||||
it('calls API and sets values (org)', async () => {
|
||||
const onInitializeSourceSpy = jest.spyOn(SourceLogic.actions, 'onInitializeSource');
|
||||
const promise = Promise.resolve(contentSource);
|
||||
http.get.mockReturnValue(promise);
|
||||
SourceLogic.actions.initializeSource(contentSource.id);
|
||||
|
||||
expect(http.get).toHaveBeenCalledWith('/api/workplace_search/org/sources/123');
|
||||
await promise;
|
||||
expect(onInitializeSourceSpy).toHaveBeenCalledWith(contentSource);
|
||||
});
|
||||
|
||||
it('calls API and sets values (account)', async () => {
|
||||
AppLogic.values.isOrganization = false;
|
||||
|
||||
const onInitializeSourceSpy = jest.spyOn(SourceLogic.actions, 'onInitializeSource');
|
||||
const promise = Promise.resolve(contentSource);
|
||||
http.get.mockReturnValue(promise);
|
||||
SourceLogic.actions.initializeSource(contentSource.id);
|
||||
|
||||
expect(http.get).toHaveBeenCalledWith('/api/workplace_search/account/sources/123');
|
||||
await promise;
|
||||
expect(onInitializeSourceSpy).toHaveBeenCalledWith(contentSource);
|
||||
});
|
||||
|
||||
it('handles federated source', async () => {
|
||||
AppLogic.values.isOrganization = false;
|
||||
|
||||
const initializeFederatedSummarySpy = jest.spyOn(
|
||||
SourceLogic.actions,
|
||||
'initializeFederatedSummary'
|
||||
);
|
||||
const promise = Promise.resolve({
|
||||
...contentSource,
|
||||
isFederatedSource: true,
|
||||
});
|
||||
http.get.mockReturnValue(promise);
|
||||
SourceLogic.actions.initializeSource(contentSource.id);
|
||||
|
||||
expect(http.get).toHaveBeenCalledWith('/api/workplace_search/account/sources/123');
|
||||
await promise;
|
||||
expect(initializeFederatedSummarySpy).toHaveBeenCalledWith(contentSource.id);
|
||||
});
|
||||
|
||||
it('handles error', async () => {
|
||||
const error = {
|
||||
response: {
|
||||
error: 'this is an error',
|
||||
status: 400,
|
||||
},
|
||||
};
|
||||
const promise = Promise.reject(error);
|
||||
http.get.mockReturnValue(promise);
|
||||
SourceLogic.actions.initializeSource(contentSource.id);
|
||||
await expectedAsyncError(promise);
|
||||
|
||||
expect(flashAPIErrors).toHaveBeenCalledWith(error);
|
||||
});
|
||||
|
||||
it('handles not found state', async () => {
|
||||
const error = {
|
||||
response: {
|
||||
error: 'this is an error',
|
||||
status: 404,
|
||||
},
|
||||
};
|
||||
const promise = Promise.reject(error);
|
||||
http.get.mockReturnValue(promise);
|
||||
SourceLogic.actions.initializeSource(contentSource.id);
|
||||
await expectedAsyncError(promise);
|
||||
|
||||
expect(navigateToUrl).toHaveBeenCalledWith(NOT_FOUND_PATH);
|
||||
});
|
||||
});
|
||||
|
||||
describe('initializeFederatedSummary', () => {
|
||||
it('calls API and sets values', async () => {
|
||||
const onUpdateSummarySpy = jest.spyOn(SourceLogic.actions, 'onUpdateSummary');
|
||||
const promise = Promise.resolve(contentSource);
|
||||
http.get.mockReturnValue(promise);
|
||||
SourceLogic.actions.initializeFederatedSummary(contentSource.id);
|
||||
|
||||
expect(http.get).toHaveBeenCalledWith(
|
||||
'/api/workplace_search/org/sources/123/federated_summary'
|
||||
);
|
||||
await promise;
|
||||
expect(onUpdateSummarySpy).toHaveBeenCalledWith(contentSource.summary);
|
||||
});
|
||||
|
||||
it('handles error', async () => {
|
||||
const error = {
|
||||
response: {
|
||||
error: 'this is an error',
|
||||
status: 400,
|
||||
},
|
||||
};
|
||||
const promise = Promise.reject(error);
|
||||
http.get.mockReturnValue(promise);
|
||||
SourceLogic.actions.initializeFederatedSummary(contentSource.id);
|
||||
await expectedAsyncError(promise);
|
||||
|
||||
expect(flashAPIErrors).toHaveBeenCalledWith(error);
|
||||
});
|
||||
});
|
||||
|
||||
describe('searchContentSourceDocuments', () => {
|
||||
const mockBreakpoint = jest.fn();
|
||||
const values = { contentMeta: meta, contentFilterValue: '' };
|
||||
const actions = { setSearchResults: jest.fn() };
|
||||
const { searchContentSourceDocuments } = getListeners({
|
||||
values,
|
||||
actions,
|
||||
});
|
||||
|
||||
it('calls API and sets values (org)', async () => {
|
||||
AppLogic.values.isOrganization = true;
|
||||
const promise = Promise.resolve(searchServerResponse);
|
||||
http.post.mockReturnValue(promise);
|
||||
|
||||
await searchContentSourceDocuments({ sourceId: contentSource.id }, mockBreakpoint);
|
||||
expect(http.post).toHaveBeenCalledWith('/api/workplace_search/org/sources/123/documents', {
|
||||
body: JSON.stringify({ query: '', page: meta.page }),
|
||||
});
|
||||
|
||||
await promise;
|
||||
expect(actions.setSearchResults).toHaveBeenCalledWith(searchServerResponse);
|
||||
});
|
||||
|
||||
it('calls API and sets values (account)', async () => {
|
||||
AppLogic.values.isOrganization = false;
|
||||
const promise = Promise.resolve(searchServerResponse);
|
||||
http.post.mockReturnValue(promise);
|
||||
|
||||
SourceLogic.actions.searchContentSourceDocuments(contentSource.id);
|
||||
await searchContentSourceDocuments({ sourceId: contentSource.id }, mockBreakpoint);
|
||||
expect(http.post).toHaveBeenCalledWith(
|
||||
'/api/workplace_search/account/sources/123/documents',
|
||||
{
|
||||
body: JSON.stringify({ query: '', page: meta.page }),
|
||||
}
|
||||
);
|
||||
|
||||
await promise;
|
||||
expect(actions.setSearchResults).toHaveBeenCalledWith(searchServerResponse);
|
||||
});
|
||||
|
||||
it('handles error', async () => {
|
||||
const error = {
|
||||
response: {
|
||||
error: 'this is an error',
|
||||
status: 400,
|
||||
},
|
||||
};
|
||||
const promise = Promise.reject(error);
|
||||
http.post.mockReturnValue(promise);
|
||||
|
||||
await searchContentSourceDocuments({ sourceId: contentSource.id }, mockBreakpoint);
|
||||
await expectedAsyncError(promise);
|
||||
|
||||
expect(flashAPIErrors).toHaveBeenCalledWith(error);
|
||||
});
|
||||
});
|
||||
|
||||
describe('updateContentSource', () => {
|
||||
it('calls API and sets values (org)', async () => {
|
||||
AppLogic.values.isOrganization = true;
|
||||
|
||||
const onUpdateSourceNameSpy = jest.spyOn(SourceLogic.actions, 'onUpdateSourceName');
|
||||
const promise = Promise.resolve(contentSource);
|
||||
http.patch.mockReturnValue(promise);
|
||||
SourceLogic.actions.updateContentSource(contentSource.id, contentSource);
|
||||
|
||||
expect(http.patch).toHaveBeenCalledWith('/api/workplace_search/org/sources/123/settings', {
|
||||
body: JSON.stringify({ content_source: contentSource }),
|
||||
});
|
||||
await promise;
|
||||
expect(onUpdateSourceNameSpy).toHaveBeenCalledWith(contentSource.name);
|
||||
});
|
||||
|
||||
it('calls API and sets values (account)', async () => {
|
||||
AppLogic.values.isOrganization = false;
|
||||
|
||||
const onUpdateSourceNameSpy = jest.spyOn(SourceLogic.actions, 'onUpdateSourceName');
|
||||
const promise = Promise.resolve(contentSource);
|
||||
http.patch.mockReturnValue(promise);
|
||||
SourceLogic.actions.updateContentSource(contentSource.id, contentSource);
|
||||
|
||||
expect(http.patch).toHaveBeenCalledWith(
|
||||
'/api/workplace_search/account/sources/123/settings',
|
||||
{
|
||||
body: JSON.stringify({ content_source: contentSource }),
|
||||
}
|
||||
);
|
||||
await promise;
|
||||
expect(onUpdateSourceNameSpy).toHaveBeenCalledWith(contentSource.name);
|
||||
});
|
||||
|
||||
it('handles error', async () => {
|
||||
const error = {
|
||||
response: {
|
||||
error: 'this is an error',
|
||||
status: 400,
|
||||
},
|
||||
};
|
||||
const promise = Promise.reject(error);
|
||||
http.patch.mockReturnValue(promise);
|
||||
SourceLogic.actions.updateContentSource(contentSource.id, contentSource);
|
||||
await expectedAsyncError(promise);
|
||||
|
||||
expect(flashAPIErrors).toHaveBeenCalledWith(error);
|
||||
});
|
||||
});
|
||||
|
||||
describe('removeContentSource', () => {
|
||||
it('calls API and sets values (org)', async () => {
|
||||
AppLogic.values.isOrganization = true;
|
||||
|
||||
const setButtonNotLoadingSpy = jest.spyOn(SourceLogic.actions, 'setButtonNotLoading');
|
||||
const promise = Promise.resolve(contentSource);
|
||||
http.delete.mockReturnValue(promise);
|
||||
SourceLogic.actions.removeContentSource(contentSource.id);
|
||||
|
||||
expect(clearFlashMessages).toHaveBeenCalled();
|
||||
expect(http.delete).toHaveBeenCalledWith('/api/workplace_search/org/sources/123');
|
||||
await promise;
|
||||
expect(setQueuedSuccessMessage).toHaveBeenCalled();
|
||||
expect(setButtonNotLoadingSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('calls API and sets values (account)', async () => {
|
||||
AppLogic.values.isOrganization = false;
|
||||
|
||||
const setButtonNotLoadingSpy = jest.spyOn(SourceLogic.actions, 'setButtonNotLoading');
|
||||
const promise = Promise.resolve(contentSource);
|
||||
http.delete.mockReturnValue(promise);
|
||||
SourceLogic.actions.removeContentSource(contentSource.id);
|
||||
|
||||
expect(clearFlashMessages).toHaveBeenCalled();
|
||||
expect(http.delete).toHaveBeenCalledWith('/api/workplace_search/account/sources/123');
|
||||
await promise;
|
||||
expect(setButtonNotLoadingSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('handles error', async () => {
|
||||
const error = {
|
||||
response: {
|
||||
error: 'this is an error',
|
||||
status: 400,
|
||||
},
|
||||
};
|
||||
const promise = Promise.reject(error);
|
||||
http.delete.mockReturnValue(promise);
|
||||
SourceLogic.actions.removeContentSource(contentSource.id);
|
||||
await expectedAsyncError(promise);
|
||||
|
||||
expect(flashAPIErrors).toHaveBeenCalledWith(error);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getSourceConfigData', () => {
|
||||
const serviceType = 'github';
|
||||
|
||||
it('calls API and sets values', async () => {
|
||||
AppLogic.values.isOrganization = true;
|
||||
|
||||
const setSourceConfigDataSpy = jest.spyOn(SourceLogic.actions, 'setSourceConfigData');
|
||||
const promise = Promise.resolve(contentSource);
|
||||
http.get.mockReturnValue(promise);
|
||||
SourceLogic.actions.getSourceConfigData(serviceType);
|
||||
|
||||
expect(http.get).toHaveBeenCalledWith(
|
||||
`/api/workplace_search/org/settings/connectors/${serviceType}`
|
||||
);
|
||||
await promise;
|
||||
expect(setSourceConfigDataSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('handles error', async () => {
|
||||
const error = {
|
||||
response: {
|
||||
error: 'this is an error',
|
||||
status: 400,
|
||||
},
|
||||
};
|
||||
const promise = Promise.reject(error);
|
||||
http.get.mockReturnValue(promise);
|
||||
SourceLogic.actions.getSourceConfigData(serviceType);
|
||||
await expectedAsyncError(promise);
|
||||
|
||||
expect(flashAPIErrors).toHaveBeenCalledWith(error);
|
||||
});
|
||||
});
|
||||
|
||||
it('resetSourceState', () => {
|
||||
SourceLogic.actions.resetSourceState();
|
||||
|
||||
expect(clearFlashMessages).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -126,29 +126,22 @@ export const SourceLogic = kea<MakeLogicType<SourceValues, SourceActions>>({
|
|||
onInitializeSource: () => false,
|
||||
setSourceConfigData: () => false,
|
||||
resetSourceState: () => false,
|
||||
setPreContentSourceConfigData: () => false,
|
||||
},
|
||||
],
|
||||
buttonLoading: [
|
||||
false,
|
||||
{
|
||||
setButtonNotLoading: () => false,
|
||||
setSourceConnectData: () => false,
|
||||
setSourceConfigData: () => false,
|
||||
resetSourceState: () => false,
|
||||
removeContentSource: () => true,
|
||||
saveSourceConfig: () => true,
|
||||
getSourceConnectData: () => true,
|
||||
createContentSource: () => true,
|
||||
},
|
||||
],
|
||||
sectionLoading: [
|
||||
true,
|
||||
{
|
||||
searchContentSourceDocuments: () => true,
|
||||
getPreContentSourceConfigData: () => true,
|
||||
setSearchResults: () => false,
|
||||
setPreContentSourceConfigData: () => false,
|
||||
},
|
||||
],
|
||||
contentItems: [
|
||||
|
|
|
@ -0,0 +1,319 @@
|
|||
/*
|
||||
* 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 {
|
||||
LogicMounter,
|
||||
mockFlashMessageHelpers,
|
||||
mockHttpValues,
|
||||
expectedAsyncError,
|
||||
} from '../../../__mocks__';
|
||||
|
||||
import { AppLogic } from '../../app_logic';
|
||||
jest.mock('../../app_logic', () => ({
|
||||
AppLogic: { values: { isOrganization: true } },
|
||||
}));
|
||||
|
||||
import { configuredSources, contentSources } from '../../__mocks__/content_sources.mock';
|
||||
|
||||
import { SourcesLogic, fetchSourceStatuses, POLLING_INTERVAL } from './sources_logic';
|
||||
|
||||
describe('SourcesLogic', () => {
|
||||
const { http } = mockHttpValues;
|
||||
const { flashAPIErrors, setQueuedSuccessMessage } = mockFlashMessageHelpers;
|
||||
const { mount, unmount } = new LogicMounter(SourcesLogic);
|
||||
|
||||
const contentSource = contentSources[0];
|
||||
|
||||
const defaultValues = {
|
||||
contentSources: [],
|
||||
privateContentSources: [],
|
||||
sourceData: [],
|
||||
availableSources: [],
|
||||
configuredSources: [],
|
||||
serviceTypes: [],
|
||||
permissionsModal: null,
|
||||
dataLoading: true,
|
||||
serverStatuses: null,
|
||||
};
|
||||
|
||||
const serverStatuses = [
|
||||
{
|
||||
id: '123',
|
||||
name: 'my source',
|
||||
service_type: 'github',
|
||||
status: {
|
||||
status: 'this is a thing',
|
||||
synced_at: '2021-01-25',
|
||||
error_reason: 1,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const serverResponse = {
|
||||
contentSources,
|
||||
privateContentSources: contentSources,
|
||||
serviceTypes: configuredSources,
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
jest.useFakeTimers();
|
||||
jest.clearAllMocks();
|
||||
mount();
|
||||
});
|
||||
|
||||
it('has expected default values', () => {
|
||||
expect(SourcesLogic.values).toEqual(defaultValues);
|
||||
});
|
||||
|
||||
it('handles unmounting', async () => {
|
||||
unmount();
|
||||
expect(clearInterval).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe('actions', () => {
|
||||
describe('onInitializeSources', () => {
|
||||
it('sets values', () => {
|
||||
SourcesLogic.actions.onInitializeSources(serverResponse);
|
||||
|
||||
expect(SourcesLogic.values.contentSources).toEqual(contentSources);
|
||||
expect(SourcesLogic.values.privateContentSources).toEqual(contentSources);
|
||||
expect(SourcesLogic.values.serviceTypes).toEqual(configuredSources);
|
||||
expect(SourcesLogic.values.dataLoading).toEqual(false);
|
||||
});
|
||||
|
||||
it('fallbacks', () => {
|
||||
SourcesLogic.actions.onInitializeSources({
|
||||
contentSources,
|
||||
serviceTypes: undefined as any,
|
||||
});
|
||||
|
||||
expect(SourcesLogic.values.serviceTypes).toEqual([]);
|
||||
expect(SourcesLogic.values.privateContentSources).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
it('setServerSourceStatuses', () => {
|
||||
SourcesLogic.actions.setServerSourceStatuses(serverStatuses);
|
||||
const source = serverStatuses[0];
|
||||
|
||||
expect(SourcesLogic.values.serverStatuses).toEqual({
|
||||
[source.id]: source.status.status,
|
||||
});
|
||||
});
|
||||
|
||||
it('onSetSearchability', () => {
|
||||
const id = contentSources[0].id;
|
||||
const updatedSources = [...contentSources];
|
||||
updatedSources[0].searchable = false;
|
||||
SourcesLogic.actions.onInitializeSources(serverResponse);
|
||||
SourcesLogic.actions.onSetSearchability(id, false);
|
||||
|
||||
expect(SourcesLogic.values.contentSources).toEqual(updatedSources);
|
||||
expect(SourcesLogic.values.privateContentSources).toEqual(updatedSources);
|
||||
});
|
||||
|
||||
describe('setAddedSource', () => {
|
||||
it('configured', () => {
|
||||
const name = contentSources[0].name;
|
||||
SourcesLogic.actions.setAddedSource(name, false, 'custom');
|
||||
|
||||
expect(SourcesLogic.values.permissionsModal).toEqual({
|
||||
addedSourceName: name,
|
||||
additionalConfiguration: false,
|
||||
serviceType: 'custom',
|
||||
});
|
||||
expect(setQueuedSuccessMessage).toHaveBeenCalledWith('Successfully connected source. ');
|
||||
});
|
||||
|
||||
it('unconfigured', () => {
|
||||
const name = contentSources[0].name;
|
||||
SourcesLogic.actions.setAddedSource(name, true, 'custom');
|
||||
|
||||
expect(SourcesLogic.values.permissionsModal).toEqual({
|
||||
addedSourceName: name,
|
||||
additionalConfiguration: true,
|
||||
serviceType: 'custom',
|
||||
});
|
||||
expect(setQueuedSuccessMessage).toHaveBeenCalledWith(
|
||||
'Successfully connected source. This source requires additional configuration.'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('resetPermissionsModal', () => {
|
||||
SourcesLogic.actions.resetPermissionsModal();
|
||||
|
||||
expect(SourcesLogic.values.permissionsModal).toEqual(null);
|
||||
});
|
||||
});
|
||||
|
||||
describe('listeners', () => {
|
||||
describe('initializeSources', () => {
|
||||
it('calls API and sets values (org)', async () => {
|
||||
AppLogic.values.isOrganization = true;
|
||||
const pollForSourceStatusChangesSpy = jest.spyOn(
|
||||
SourcesLogic.actions,
|
||||
'pollForSourceStatusChanges'
|
||||
);
|
||||
const onInitializeSourcesSpy = jest.spyOn(SourcesLogic.actions, 'onInitializeSources');
|
||||
const promise = Promise.resolve(contentSources);
|
||||
http.get.mockReturnValue(promise);
|
||||
SourcesLogic.actions.initializeSources();
|
||||
|
||||
expect(http.get).toHaveBeenCalledWith('/api/workplace_search/org/sources');
|
||||
await promise;
|
||||
expect(pollForSourceStatusChangesSpy).toHaveBeenCalled();
|
||||
expect(onInitializeSourcesSpy).toHaveBeenCalledWith(contentSources);
|
||||
});
|
||||
|
||||
it('calls API (account)', async () => {
|
||||
AppLogic.values.isOrganization = false;
|
||||
const promise = Promise.resolve(contentSource);
|
||||
http.get.mockReturnValue(promise);
|
||||
SourcesLogic.actions.initializeSources();
|
||||
|
||||
expect(http.get).toHaveBeenCalledWith('/api/workplace_search/account/sources');
|
||||
});
|
||||
|
||||
it('handles error', async () => {
|
||||
const error = {
|
||||
response: {
|
||||
error: 'this is an error',
|
||||
status: 400,
|
||||
},
|
||||
};
|
||||
const promise = Promise.reject(error);
|
||||
http.get.mockReturnValue(promise);
|
||||
SourcesLogic.actions.initializeSources();
|
||||
await expectedAsyncError(promise);
|
||||
|
||||
expect(flashAPIErrors).toHaveBeenCalledWith(error);
|
||||
});
|
||||
});
|
||||
|
||||
describe('setSourceSearchability', () => {
|
||||
const id = contentSources[0].id;
|
||||
|
||||
it('calls API and sets values (org)', async () => {
|
||||
AppLogic.values.isOrganization = true;
|
||||
const onSetSearchability = jest.spyOn(SourcesLogic.actions, 'onSetSearchability');
|
||||
const promise = Promise.resolve(contentSources);
|
||||
http.put.mockReturnValue(promise);
|
||||
SourcesLogic.actions.setSourceSearchability(id, true);
|
||||
|
||||
expect(http.put).toHaveBeenCalledWith('/api/workplace_search/org/sources/123/searchable', {
|
||||
body: JSON.stringify({ searchable: true }),
|
||||
});
|
||||
await promise;
|
||||
expect(onSetSearchability).toHaveBeenCalledWith(id, true);
|
||||
});
|
||||
|
||||
it('calls API (account)', async () => {
|
||||
AppLogic.values.isOrganization = false;
|
||||
const promise = Promise.resolve(contentSource);
|
||||
http.put.mockReturnValue(promise);
|
||||
SourcesLogic.actions.setSourceSearchability(id, true);
|
||||
|
||||
expect(http.put).toHaveBeenCalledWith(
|
||||
'/api/workplace_search/account/sources/123/searchable',
|
||||
{
|
||||
body: JSON.stringify({ searchable: true }),
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('handles error', async () => {
|
||||
const error = {
|
||||
response: {
|
||||
error: 'this is an error',
|
||||
status: 400,
|
||||
},
|
||||
};
|
||||
const promise = Promise.reject(error);
|
||||
http.put.mockReturnValue(promise);
|
||||
SourcesLogic.actions.setSourceSearchability(id, true);
|
||||
await expectedAsyncError(promise);
|
||||
|
||||
expect(flashAPIErrors).toHaveBeenCalledWith(error);
|
||||
});
|
||||
});
|
||||
|
||||
describe('pollForSourceStatusChanges', () => {
|
||||
it('calls API and sets values', async () => {
|
||||
AppLogic.values.isOrganization = true;
|
||||
SourcesLogic.actions.setServerSourceStatuses(serverStatuses);
|
||||
|
||||
const setServerSourceStatusesSpy = jest.spyOn(
|
||||
SourcesLogic.actions,
|
||||
'setServerSourceStatuses'
|
||||
);
|
||||
const promise = Promise.resolve(contentSources);
|
||||
http.get.mockReturnValue(promise);
|
||||
SourcesLogic.actions.pollForSourceStatusChanges();
|
||||
|
||||
jest.advanceTimersByTime(POLLING_INTERVAL);
|
||||
|
||||
expect(http.get).toHaveBeenCalledWith('/api/workplace_search/org/sources/status');
|
||||
await promise;
|
||||
expect(setServerSourceStatusesSpy).toHaveBeenCalledWith(contentSources);
|
||||
});
|
||||
});
|
||||
|
||||
it('resetSourcesState', () => {
|
||||
SourcesLogic.actions.resetSourcesState();
|
||||
|
||||
expect(clearInterval).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('selectors', () => {
|
||||
it('availableSources & configuredSources have correct length', () => {
|
||||
SourcesLogic.actions.onInitializeSources(serverResponse);
|
||||
|
||||
expect(SourcesLogic.values.availableSources).toHaveLength(1);
|
||||
expect(SourcesLogic.values.configuredSources).toHaveLength(5);
|
||||
});
|
||||
});
|
||||
|
||||
describe('fetchSourceStatuses', () => {
|
||||
it('calls API and sets values (org)', async () => {
|
||||
const setServerSourceStatusesSpy = jest.spyOn(
|
||||
SourcesLogic.actions,
|
||||
'setServerSourceStatuses'
|
||||
);
|
||||
const promise = Promise.resolve(contentSources);
|
||||
http.get.mockReturnValue(promise);
|
||||
fetchSourceStatuses(true);
|
||||
|
||||
expect(http.get).toHaveBeenCalledWith('/api/workplace_search/org/sources/status');
|
||||
await promise;
|
||||
expect(setServerSourceStatusesSpy).toHaveBeenCalledWith(contentSources);
|
||||
});
|
||||
|
||||
it('calls API (account)', async () => {
|
||||
const promise = Promise.resolve(contentSource);
|
||||
http.get.mockReturnValue(promise);
|
||||
fetchSourceStatuses(false);
|
||||
|
||||
expect(http.get).toHaveBeenCalledWith('/api/workplace_search/account/sources/status');
|
||||
});
|
||||
|
||||
it('handles error', async () => {
|
||||
const error = {
|
||||
response: {
|
||||
error: 'this is an error',
|
||||
status: 400,
|
||||
},
|
||||
};
|
||||
const promise = Promise.reject(error);
|
||||
http.get.mockReturnValue(promise);
|
||||
fetchSourceStatuses(true);
|
||||
await expectedAsyncError(promise);
|
||||
|
||||
expect(flashAPIErrors).toHaveBeenCalledWith(error);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -12,11 +12,7 @@ import { i18n } from '@kbn/i18n';
|
|||
|
||||
import { HttpLogic } from '../../../shared/http';
|
||||
|
||||
import {
|
||||
flashAPIErrors,
|
||||
setQueuedSuccessMessage,
|
||||
clearFlashMessages,
|
||||
} from '../../../shared/flash_messages';
|
||||
import { flashAPIErrors, setQueuedSuccessMessage } from '../../../shared/flash_messages';
|
||||
|
||||
import { Connector, ContentSourceDetails, ContentSourceStatus, SourceDataItem } from '../../types';
|
||||
|
||||
|
@ -40,7 +36,6 @@ export interface ISourcesActions {
|
|||
additionalConfiguration: boolean,
|
||||
serviceType: string
|
||||
): { addedSourceName: string; additionalConfiguration: boolean; serviceType: string };
|
||||
resetFlashMessages(): void;
|
||||
resetPermissionsModal(): void;
|
||||
resetSourcesState(): void;
|
||||
initializeSources(): void;
|
||||
|
@ -78,7 +73,7 @@ interface ISourcesServerResponse {
|
|||
}
|
||||
|
||||
let pollingInterval: number;
|
||||
const POLLING_INTERVAL = 10000;
|
||||
export const POLLING_INTERVAL = 10000;
|
||||
|
||||
export const SourcesLogic = kea<MakeLogicType<ISourcesValues, ISourcesActions>>({
|
||||
path: ['enterprise_search', 'workplace_search', 'sources_logic'],
|
||||
|
@ -91,7 +86,6 @@ export const SourcesLogic = kea<MakeLogicType<ISourcesValues, ISourcesActions>>(
|
|||
additionalConfiguration: boolean,
|
||||
serviceType: string
|
||||
) => ({ addedSourceName, additionalConfiguration, serviceType }),
|
||||
resetFlashMessages: () => true,
|
||||
resetPermissionsModal: () => true,
|
||||
resetSourcesState: () => true,
|
||||
initializeSources: () => true,
|
||||
|
@ -238,9 +232,6 @@ export const SourcesLogic = kea<MakeLogicType<ISourcesValues, ISourcesActions>>(
|
|||
].join(' ')
|
||||
);
|
||||
},
|
||||
resetFlashMessages: () => {
|
||||
clearFlashMessages();
|
||||
},
|
||||
resetSourcesState: () => {
|
||||
clearInterval(pollingInterval);
|
||||
},
|
||||
|
@ -252,7 +243,7 @@ export const SourcesLogic = kea<MakeLogicType<ISourcesValues, ISourcesActions>>(
|
|||
}),
|
||||
});
|
||||
|
||||
const fetchSourceStatuses = async (isOrganization: boolean) => {
|
||||
export const fetchSourceStatuses = async (isOrganization: boolean) => {
|
||||
const route = isOrganization
|
||||
? '/api/workplace_search/org/sources/status'
|
||||
: '/api/workplace_search/account/sources/status';
|
||||
|
@ -273,7 +264,6 @@ const updateSourcesOnToggle = (
|
|||
sourceId: string,
|
||||
searchable: boolean
|
||||
): ContentSourceDetails[] => {
|
||||
if (!contentSources) return [];
|
||||
const sources = cloneDeep(contentSources) as ContentSourceDetails[];
|
||||
const index = findIndex(sources, ({ id }) => id === sourceId);
|
||||
const updatedSource = sources[index];
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
import '../../../__mocks__/shallow_useeffect.mock';
|
||||
import { setMockActions, setMockValues } from '../../../__mocks__';
|
||||
import { groups } from '../../__mocks__/groups.mock';
|
||||
import { mockMeta } from '../../__mocks__/meta.mock';
|
||||
import { meta } from '../../__mocks__/meta.mock';
|
||||
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
|
@ -46,7 +46,7 @@ const mockValues = {
|
|||
newGroup: null,
|
||||
groupListLoading: false,
|
||||
hasFiltersSet: false,
|
||||
groupsMeta: mockMeta,
|
||||
groupsMeta: meta,
|
||||
filteredSources: [],
|
||||
filteredUsers: [],
|
||||
filterValue: '',
|
||||
|
|
Loading…
Reference in a new issue