From e9c40ce0f862e6773e7b34b7fd1a96630beab34e Mon Sep 17 00:00:00 2001 From: Luke Elmers Date: Wed, 6 Mar 2019 23:36:12 -0700 Subject: [PATCH] Initial unit tests for embedded_visualize_handler (#31324) --- .../embedded_visualize_handler.test.ts.snap | 28 ++ .../loader/embedded_visualize_handler.test.ts | 290 ++++++++++++++++++ 2 files changed, 318 insertions(+) create mode 100644 src/legacy/ui/public/visualize/loader/__snapshots__/embedded_visualize_handler.test.ts.snap create mode 100644 src/legacy/ui/public/visualize/loader/embedded_visualize_handler.test.ts diff --git a/src/legacy/ui/public/visualize/loader/__snapshots__/embedded_visualize_handler.test.ts.snap b/src/legacy/ui/public/visualize/loader/__snapshots__/embedded_visualize_handler.test.ts.snap new file mode 100644 index 0000000000000..6e9ab0a1aa223 --- /dev/null +++ b/src/legacy/ui/public/visualize/loader/__snapshots__/embedded_visualize_handler.test.ts.snap @@ -0,0 +1,28 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`EmbeddedVisualizeHandler data$ observable can be used to get response data in the correct format 1`] = ` +Object { + "params": Object {}, + "visConfig": Object {}, + "visData": Object {}, + "visType": "histogram", +} +`; + +exports[`EmbeddedVisualizeHandler update should add provided data- attributes to the html element 1`] = ` +
+`; + +exports[`EmbeddedVisualizeHandler update should remove null data- attributes from the html element 1`] = ` +
+`; diff --git a/src/legacy/ui/public/visualize/loader/embedded_visualize_handler.test.ts b/src/legacy/ui/public/visualize/loader/embedded_visualize_handler.test.ts new file mode 100644 index 0000000000000..02a00687c1dad --- /dev/null +++ b/src/legacy/ui/public/visualize/loader/embedded_visualize_handler.test.ts @@ -0,0 +1,290 @@ +/* + * 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. + */ + +jest.useFakeTimers(); + +import { EventEmitter } from 'events'; + +// @ts-ignore +import MockState from '../../../../../fixtures/mock_state'; +import { Adapters } from '../../inspector/types'; +import { RequestHandlerParams, Vis } from '../../vis'; +import { VisResponseData } from './types'; + +jest.mock('./utils', () => ({ + queryGeohashBounds: jest.fn(), +})); + +jest.mock('./pipeline_helpers/utilities', () => ({ + getFormat: jest.fn(), + getTableAggs: jest.fn(), +})); + +const timefilter = new EventEmitter(); +jest.mock('../../timefilter', () => ({ timefilter })); + +const mockInspectorIsAvailable = jest.fn(); +const mockInspectorOpen = jest.fn(); +jest.mock('../../inspector', () => ({ + Inspector: { + open: (adapters: Adapters, opts: any) => mockInspectorOpen(adapters, opts), + isAvailable: (adapters: Adapters) => mockInspectorIsAvailable(adapters), + }, +})); + +const mockDataLoaderFetch = jest.fn().mockReturnValue({ + as: 'visualization', + value: { + visType: 'histogram', + visData: {}, + visConfig: {}, + params: {}, + }, +}); +const MockDataLoader = class { + public async fetch(data: any) { + return await mockDataLoaderFetch(data); + } +}; + +jest.mock('./pipeline_data_loader', () => ({ + PipelineDataLoader: MockDataLoader, +})); +jest.mock('./visualize_data_loader', () => ({ + VisualizeDataLoader: MockDataLoader, +})); + +import { EmbeddedVisualizeHandler } from './embedded_visualize_handler'; + +describe('EmbeddedVisualizeHandler', () => { + let handler: any; + let div: HTMLElement; + let dataLoaderParams: RequestHandlerParams; + const mockVis: Vis = { + title: 'My Vis', + // @ts-ignore + type: 'foo', + getAggConfig: () => [], + _setUiState: () => ({}), + getUiState: () => new MockState(), + on: () => ({}), + off: () => ({}), + removeListener: jest.fn(), + API: {}, + }; + + beforeEach(() => { + mockDataLoaderFetch.mockClear(); + mockInspectorOpen.mockClear(); + mockInspectorIsAvailable.mockClear(); + + dataLoaderParams = { + aggs: [], + filters: undefined, + forceFetch: false, + inspectorAdapters: {}, + query: undefined, + queryFilter: null, + searchSource: undefined, + timeRange: undefined, + uiState: undefined, + }; + + div = document.createElement('div'); + handler = new EmbeddedVisualizeHandler( + div, + { + vis: mockVis, + title: 'My Vis', + searchSource: undefined, + destroy: () => ({}), + }, + { + autoFetch: true, + Private: (provider: () => T) => provider(), + queryFilter: null, + } + ); + }); + + describe('autoFetch', () => { + it('should trigger a reload when autoFetch=true and auto refresh happens', () => { + const spy = jest.spyOn(handler, 'fetchAndRender'); + timefilter.emit('autoRefreshFetch'); + jest.runAllTimers(); + expect(spy).toHaveBeenCalledTimes(1); + expect(spy).toHaveBeenCalledWith(true); + }); + + it('should not trigger a reload when autoFetch=false and auto refresh happens', () => { + handler = new EmbeddedVisualizeHandler( + div, + { + vis: mockVis, + title: 'My Vis', + searchSource: undefined, + destroy: () => ({}), + }, + { + autoFetch: false, + Private: (provider: () => T) => provider(), + queryFilter: null, + } + ); + const spy = jest.spyOn(handler, 'fetchAndRender'); + timefilter.emit('autoRefreshFetch'); + jest.runAllTimers(); + expect(spy).not.toHaveBeenCalled(); + }); + }); + + describe('getElement', () => { + it('should return the provided html element', () => { + expect(handler.getElement()).toBe(div); + }); + }); + + describe('update', () => { + it('should add provided data- attributes to the html element', () => { + const spy = jest.spyOn(handler, 'fetchAndRender'); + const params = { + dataAttrs: { foo: 'bar' }, + }; + handler.update(params); + expect(spy).not.toHaveBeenCalled(); + expect(handler.getElement()).toMatchSnapshot(); + }); + + it('should remove null data- attributes from the html element', () => { + const spy = jest.spyOn(handler, 'fetchAndRender'); + handler.update({ + dataAttrs: { foo: 'bar' }, + }); + const params = { + dataAttrs: { + foo: null, + baz: 'qux', + }, + }; + handler.update(params); + expect(spy).not.toHaveBeenCalled(); + expect(handler.getElement()).toMatchSnapshot(); + }); + + it('should call dataLoader.render with updated timeRange', () => { + const params = { timeRange: { foo: 'bar' } }; + handler.update(params); + jest.runAllTimers(); + expect(mockDataLoaderFetch).toHaveBeenCalledWith({ ...dataLoaderParams, ...params }); + }); + + it('should call dataLoader.render with updated filters', () => { + const params = { filters: [{ foo: 'bar' }] }; + handler.update(params); + jest.runAllTimers(); + expect(mockDataLoaderFetch).toHaveBeenCalledWith({ ...dataLoaderParams, ...params }); + }); + + it('should call dataLoader.render with updated query', () => { + const params = { query: { foo: 'bar' } }; + handler.update(params); + jest.runAllTimers(); + expect(mockDataLoaderFetch).toHaveBeenCalledWith({ ...dataLoaderParams, ...params }); + }); + }); + + describe('destroy', () => { + it('should remove vis event listeners', () => { + const spy = jest.spyOn(mockVis, 'removeListener'); + handler.destroy(); + expect(spy).toHaveBeenCalledTimes(2); + expect(spy.mock.calls[0][0]).toBe('reload'); + expect(spy.mock.calls[1][0]).toBe('update'); + }); + + it('should remove element event listeners', () => { + const spy = jest.spyOn(handler.getElement(), 'removeEventListener'); + handler.destroy(); + expect(spy).toHaveBeenCalled(); + }); + + it('should prevent subsequent renders', () => { + const spy = jest.spyOn(handler, 'fetchAndRender'); + handler.destroy(); + expect(spy).not.toHaveBeenCalled(); + }); + + it('should cancel debounced fetchAndRender', () => { + const spy = jest.spyOn(handler.debouncedFetchAndRender, 'cancel'); + handler.destroy(); + expect(spy).toHaveBeenCalledTimes(1); + }); + }); + + describe('openInspector', () => { + it('calls Inspector.open()', () => { + handler.openInspector(); + expect(mockInspectorOpen).toHaveBeenCalledTimes(1); + expect(mockInspectorOpen).toHaveBeenCalledWith({}, { title: 'My Vis' }); + }); + }); + + describe('hasInspector', () => { + it('calls Inspector.isAvailable()', () => { + handler.hasInspector(); + expect(mockInspectorIsAvailable).toHaveBeenCalledTimes(1); + expect(mockInspectorIsAvailable).toHaveBeenCalledWith({}); + }); + }); + + describe('reload', () => { + it('should force fetch and render', () => { + const spy = jest.spyOn(handler, 'fetchAndRender'); + handler.reload(); + expect(spy).toHaveBeenCalledTimes(1); + expect(spy).toHaveBeenCalledWith(true); + }); + }); + + describe('data$', () => { + it('observable can be used to get response data in the correct format', async () => { + let response; + handler.data$.subscribe((data: VisResponseData) => (response = data)); + await handler.fetch(true); + jest.runAllTimers(); + expect(response).toMatchSnapshot(); + }); + }); + + describe('render', () => { + // TODO + }); + + describe('whenFirstRenderComplete', () => { + // TODO + }); + + describe('addRenderCompleteListener', () => { + // TODO + }); + + describe('removeRenderCompleteListener', () => { + // TODO + }); +});