Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Visualize] Add unit tests #70410

Merged
merged 39 commits into from
Jul 8, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
ba5ac21
Reactify visualize app
sulemanof Jun 12, 2020
727583a
Fix typescript failures after merging master
sulemanof Jun 12, 2020
fcc3508
Make sure refresh button works
sulemanof Jun 12, 2020
f2691e7
Subscribe filter manager fetches
sulemanof Jun 12, 2020
619e408
Use redirect to landing page
sulemanof Jun 12, 2020
e5f45a6
Update savedSearch type
sulemanof Jun 12, 2020
ef776c1
Add check for TSVB is loaded
sulemanof Jun 12, 2020
d3b5baa
Add unit tests for useSavedVisInstance effect
sulemanof Jun 16, 2020
3664bf3
Merge branch 'master' into np/reactify_visualize
sulemanof Jun 17, 2020
cd21384
Fix comments
sulemanof Jun 17, 2020
c030614
Fix uiState persistence on vis load
sulemanof Jun 17, 2020
22b451f
Merge branch 'np/reactify_visualize' into test/visualize
sulemanof Jun 17, 2020
a43752a
Remove extra div around TableListView
sulemanof Jun 17, 2020
6879ea7
Update DTS selectors
sulemanof Jun 18, 2020
6d51a08
Add error handling for embeddable
sulemanof Jun 18, 2020
8655f27
Add unit tests for createVisualizeAppState
sulemanof Jun 18, 2020
36a118b
Add unit tests for useChromeVisibility
sulemanof Jun 18, 2020
395c052
Add filter_manager.mock
sulemanof Jun 19, 2020
fc61ee3
Add unit tests for useVisualizeAppState
sulemanof Jun 19, 2020
c30a933
Use app state stub
sulemanof Jun 19, 2020
81928d4
Add unit tests for useLinkedSearchUpdates
sulemanof Jun 19, 2020
839cd3a
Add unit tests for useEditorUpdates
sulemanof Jun 19, 2020
cc84a37
Remove extra argument from useEditorUpdates effect
sulemanof Jun 19, 2020
5ff42f4
Update comments, fix typos
sulemanof Jun 19, 2020
fc15832
Remove extra div wrapper
sulemanof Jun 19, 2020
e90e0b6
Apply design suggestions
sulemanof Jun 19, 2020
3566a34
Merge remote-tracking branch 'upstream/master' into np/reactify_visua…
sulemanof Jun 19, 2020
03dec22
Revert accidental config changes
sulemanof Jun 19, 2020
17f1cb2
Merge branch 'np/reactify_visualize' into test/visualize
sulemanof Jun 19, 2020
3ba091f
Add unit tests for useEditorUpdates
sulemanof Jun 22, 2020
b112ba3
Use visualize services mock
sulemanof Jun 22, 2020
5ca55e3
Add unit tests for getVisualizationInstance
sulemanof Jun 23, 2020
cecb3f7
Merge branch 'master' into test/visualize
sulemanof Jul 1, 2020
613679f
Fix eslint warnings
sulemanof Jul 1, 2020
c5cfde3
Merge branch 'master' into test/visualize
elasticmachine Jul 6, 2020
6f0f5f6
Merge branch 'master' into test/visualize
elasticmachine Jul 6, 2020
6e5ca1f
Merge remote-tracking branch 'upstream/master' into test/visualize
sulemanof Jul 8, 2020
d5773ef
Merge branch 'test/visualize' of github.com:sulemanof/kibana into tes…
sulemanof Jul 8, 2020
8530eb7
Merge branch 'master' into test/visualize
elasticmachine Jul 8, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

import { Observable } from 'rxjs';
import { FilterManager } from './filter_manager';

export const createFilterManagerMock = () => {
const filterManager = ({
mergeIncomingFilters: jest.fn(),
handleStateUpdate: jest.fn(),
getFilters: jest.fn(),
getAppFilters: jest.fn(),
getGlobalFilters: jest.fn(),
getPartitionedFilters: jest.fn(),
getUpdates$: jest.fn(() => new Observable()),
getFetches$: jest.fn(() => new Observable()),
addFilters: jest.fn(),
setFilters: jest.fn(),
setGlobalFilters: jest.fn(),
setAppFilters: jest.fn(),
removeFilter: jest.fn(),
removeAll: jest.fn(),
} as unknown) as jest.Mocked<FilterManager>;

return filterManager;
};
5 changes: 3 additions & 2 deletions src/plugins/data/public/query/mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,13 @@
import { Observable } from 'rxjs';
import { QueryService, QuerySetup, QueryStart } from '.';
import { timefilterServiceMock } from './timefilter/timefilter_service.mock';
import { createFilterManagerMock } from './filter_manager/filter_manager.mock';

type QueryServiceClientContract = PublicMethodsOf<QueryService>;

const createSetupContractMock = () => {
const setupContract: jest.Mocked<QuerySetup> = {
filterManager: jest.fn() as any,
filterManager: createFilterManagerMock(),
timefilter: timefilterServiceMock.createSetupContract(),
state$: new Observable(),
};
Expand All @@ -36,7 +37,7 @@ const createSetupContractMock = () => {
const createStartContractMock = () => {
const startContract: jest.Mocked<QueryStart> = {
addToQueryLog: jest.fn(),
filterManager: jest.fn() as any,
filterManager: createFilterManagerMock(),
savedQueries: jest.fn() as any,
state$: new Observable(),
timefilter: timefilterServiceMock.createStartContract(),
Expand Down
4 changes: 3 additions & 1 deletion src/plugins/visualizations/public/mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@ const createStartContract = (): VisualizationsStart => ({
get: jest.fn(),
all: jest.fn(),
getAliases: jest.fn(),
savedVisualizationsLoader: {} as any,
savedVisualizationsLoader: {
get: jest.fn(),
} as any,
showNewVisModal: jest.fn(),
createVis: jest.fn(),
convertFromSerializedVis: jest.fn(),
Expand Down
2 changes: 1 addition & 1 deletion src/plugins/visualize/public/application/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export type PureVisState = SavedVisState;

export interface VisualizeAppState {
filters: Filter[];
uiState: PersistedState;
uiState: Record<string, unknown>;
vis: PureVisState;
query: Query;
savedQuery?: string;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

import { IKbnUrlStateStorage } from 'src/plugins/kibana_utils/public';
import { createVisualizeAppState } from './create_visualize_app_state';
import { migrateAppState } from './migrate_app_state';
import { visualizeAppStateStub } from './stubs';

const mockStartStateSync = jest.fn();
const mockStopStateSync = jest.fn();

jest.mock('../../../../kibana_utils/public', () => ({
createStateContainer: jest.fn(() => 'stateContainer'),
syncState: jest.fn(() => ({
start: mockStartStateSync,
stop: mockStopStateSync,
})),
}));
jest.mock('./migrate_app_state', () => ({
migrateAppState: jest.fn(() => 'migratedAppState'),
}));

const { createStateContainer, syncState } = jest.requireMock('../../../../kibana_utils/public');

describe('createVisualizeAppState', () => {
const kbnUrlStateStorage = ({
set: jest.fn(),
get: jest.fn(() => ({ linked: false })),
} as unknown) as IKbnUrlStateStorage;

const { stateContainer, stopStateSync } = createVisualizeAppState({
stateDefaults: visualizeAppStateStub,
kbnUrlStateStorage,
});
const transitions = createStateContainer.mock.calls[0][1];

test('should initialize visualize app state', () => {
expect(kbnUrlStateStorage.get).toHaveBeenCalledWith('_a');
expect(migrateAppState).toHaveBeenCalledWith({
...visualizeAppStateStub,
linked: false,
});
expect(kbnUrlStateStorage.set).toHaveBeenCalledWith('_a', 'migratedAppState', {
replace: true,
});
expect(createStateContainer).toHaveBeenCalled();
expect(syncState).toHaveBeenCalled();
expect(mockStartStateSync).toHaveBeenCalled();
});

test('should return the stateContainer and stopStateSync', () => {
expect(stateContainer).toBe('stateContainer');
stopStateSync();
expect(stopStateSync).toHaveBeenCalledTimes(1);
});

describe('stateContainer transitions', () => {
test('set', () => {
const newQuery = { query: '', language: '' };
expect(transitions.set(visualizeAppStateStub)('query', newQuery)).toEqual({
...visualizeAppStateStub,
query: newQuery,
});
});

test('setVis', () => {
const newVis = { data: 'data' };
expect(transitions.setVis(visualizeAppStateStub)(newVis)).toEqual({
...visualizeAppStateStub,
vis: {
...visualizeAppStateStub.vis,
...newVis,
},
});
});

test('unlinkSavedSearch', () => {
const params = {
query: { query: '', language: '' },
parentFilters: [{ test: 'filter2' }],
};
expect(transitions.unlinkSavedSearch(visualizeAppStateStub)(params)).toEqual({
...visualizeAppStateStub,
query: params.query,
filters: [...visualizeAppStateStub.filters, { test: 'filter2' }],
linked: false,
});
});

test('updateVisState: should not include resctricted param types', () => {
const newVisState = {
a: 1,
_b: 2,
$c: 3,
d: () => {},
};
expect(transitions.updateVisState(visualizeAppStateStub)(newVisState)).toEqual({
...visualizeAppStateStub,
vis: { a: 1 },
});
});

test('updateSavedQuery: add savedQuery', () => {
const savedQueryId = '123test';
expect(transitions.updateSavedQuery(visualizeAppStateStub)(savedQueryId)).toEqual({
...visualizeAppStateStub,
savedQuery: savedQueryId,
});
});

test('updateSavedQuery: remove savedQuery from state', () => {
const savedQueryId = '123test';
expect(
transitions.updateSavedQuery({ ...visualizeAppStateStub, savedQuery: savedQueryId })()
).toEqual(visualizeAppStateStub);
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

import { createSavedSearchesLoader } from '../../../../discover/public';
import { getVisualizationInstance } from './get_visualization_instance';
import { createVisualizeServicesMock } from './mocks';
import { VisualizeServices } from '../types';
import { BehaviorSubject } from 'rxjs';

const mockSavedSearchObj = {};
const mockGetSavedSearch = jest.fn(() => mockSavedSearchObj);

jest.mock('../../../../discover/public', () => ({
createSavedSearchesLoader: jest.fn(() => ({
get: mockGetSavedSearch,
})),
}));

describe('getVisualizationInstance', () => {
const serializedVisMock = {
type: 'area',
};
let savedVisMock: any;
let visMock: any;
let mockServices: jest.Mocked<VisualizeServices>;
let subj: BehaviorSubject<any>;

beforeEach(() => {
mockServices = createVisualizeServicesMock();
subj = new BehaviorSubject({});
visMock = {
type: {},
data: {},
};
savedVisMock = {};
// @ts-expect-error
mockServices.savedVisualizations.get.mockImplementation(() => savedVisMock);
// @ts-expect-error
mockServices.visualizations.convertToSerializedVis.mockImplementation(() => serializedVisMock);
// @ts-expect-error
mockServices.visualizations.createVis.mockImplementation(() => visMock);
// @ts-expect-error
mockServices.createVisEmbeddableFromObject.mockImplementation(() => ({
getOutput$: jest.fn(() => subj.asObservable()),
}));
});

test('should create new instances of savedVis, vis and embeddableHandler', async () => {
const opts = {
type: 'area',
indexPattern: 'my_index_pattern',
};
const { savedVis, savedSearch, vis, embeddableHandler } = await getVisualizationInstance(
mockServices,
opts
);

expect(mockServices.savedVisualizations.get).toHaveBeenCalledWith(opts);
expect(savedVisMock.searchSourceFields).toEqual({
index: opts.indexPattern,
});
expect(mockServices.visualizations.convertToSerializedVis).toHaveBeenCalledWith(savedVisMock);
expect(mockServices.visualizations.createVis).toHaveBeenCalledWith(
serializedVisMock.type,
serializedVisMock
);
expect(mockServices.createVisEmbeddableFromObject).toHaveBeenCalledWith(visMock, {
timeRange: undefined,
filters: undefined,
id: '',
});

expect(vis).toBe(visMock);
expect(savedVis).toBe(savedVisMock);
expect(embeddableHandler).toBeDefined();
expect(savedSearch).toBeUndefined();
});

test('should load existing vis by id and call vis type setup if exists', async () => {
const newVisObj = { data: {} };
visMock.type.setup = jest.fn(() => newVisObj);
const { vis } = await getVisualizationInstance(mockServices, 'saved_vis_id');

expect(mockServices.savedVisualizations.get).toHaveBeenCalledWith('saved_vis_id');
expect(savedVisMock.searchSourceFields).toBeUndefined();
expect(visMock.type.setup).toHaveBeenCalledWith(visMock);
expect(vis).toBe(newVisObj);
});

test('should create saved search instance if vis based on saved search id', async () => {
visMock.data.savedSearchId = 'saved_search_id';
const { savedSearch } = await getVisualizationInstance(mockServices, 'saved_vis_id');

expect(createSavedSearchesLoader).toHaveBeenCalled();
expect(mockGetSavedSearch).toHaveBeenCalledWith(visMock.data.savedSearchId);
expect(savedSearch).toBe(mockSavedSearchObj);
});

test('should subscribe on embeddable handler updates and send toasts on errors', async () => {
await getVisualizationInstance(mockServices, 'saved_vis_id');

subj.next({
error: 'error',
});

expect(mockServices.toastNotifications.addError).toHaveBeenCalled();
});
});
43 changes: 43 additions & 0 deletions src/plugins/visualize/public/application/utils/mocks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

import { coreMock } from '../../../../../core/public/mocks';
import { dataPluginMock } from '../../../../data/public/mocks';
import { visualizationsPluginMock } from '../../../../visualizations/public/mocks';
import { VisualizeServices } from '../types';

export const createVisualizeServicesMock = () => {
const coreStartMock = coreMock.createStart();
const dataStartMock = dataPluginMock.createStartContract();
const toastNotifications = coreStartMock.notifications.toasts;
const visualizations = visualizationsPluginMock.createStartContract();

return ({
...coreStartMock,
data: dataStartMock,
toastNotifications,
history: {
replace: jest.fn(),
location: { pathname: '' },
},
visualizations,
savedVisualizations: visualizations.savedVisualizationsLoader,
createVisEmbeddableFromObject: visualizations.__LEGACY.createVisEmbeddableFromObject,
} as unknown) as jest.Mocked<VisualizeServices>;
};
Loading