diff --git a/x-pack/legacy/plugins/lens/public/app_plugin/app.test.tsx b/x-pack/legacy/plugins/lens/public/app_plugin/app.test.tsx index 374e3270b3d4..d6312005a6c2 100644 --- a/x-pack/legacy/plugins/lens/public/app_plugin/app.test.tsx +++ b/x-pack/legacy/plugins/lens/public/app_plugin/app.test.tsx @@ -38,11 +38,6 @@ jest const { TopNavMenu } = npStart.plugins.navigation.ui; -const waitForPromises = async () => - act(async () => { - await new Promise(resolve => setTimeout(resolve)); - }); - function createMockFrame(): jest.Mocked { return { mount: jest.fn((el, props) => {}), @@ -86,6 +81,7 @@ function createMockFilterManager() { describe('Lens App', () => { let frame: jest.Mocked; let core: ReturnType; + let instance: ReactWrapper; function makeDefaultArgs(): jest.Mocked<{ editorFrame: EditorFrameInstance; @@ -205,7 +201,7 @@ describe('Lens App', () => { it('sets breadcrumbs when the document title changes', async () => { const defaultArgs = makeDefaultArgs(); - const instance = mount(); + instance = mount(); expect(core.chrome.setBreadcrumbs).toHaveBeenCalledWith([ { text: 'Visualize', href: '/testbasepath/app/kibana#/visualize' }, @@ -221,10 +217,9 @@ describe('Lens App', () => { datasourceMetaData: { filterableIndexPatterns: [{ id: '1', title: 'saved' }] }, }, }); - - instance.setProps({ docId: '1234' }); - - await waitForPromises(); + await act(async () => { + instance.setProps({ docId: '1234' }); + }); expect(defaultArgs.core.chrome.setBreadcrumbs).toHaveBeenCalledWith([ { text: 'Visualize', href: '/testbasepath/app/kibana#/visualize' }, @@ -254,10 +249,11 @@ describe('Lens App', () => { }, }); - const instance = mount(); + instance = mount(); - instance.setProps({ docId: '1234' }); - await waitForPromises(); + await act(async () => { + instance.setProps({ docId: '1234' }); + }); expect(args.docStorage.load).toHaveBeenCalledWith('1234'); expect(args.data.indexPatterns.get).toHaveBeenCalledWith('1'); @@ -292,17 +288,20 @@ describe('Lens App', () => { args.editorFrame = frame; (args.docStorage.load as jest.Mock).mockResolvedValue({ id: '1234' }); - const instance = mount(); + instance = mount(); + await act(async () => { + instance.setProps({ docId: '1234' }); + }); - instance.setProps({ docId: '1234' }); - await waitForPromises(); - instance.setProps({ docId: '1234' }); - await waitForPromises(); + await act(async () => { + instance.setProps({ docId: '1234' }); + }); expect(args.docStorage.load).toHaveBeenCalledTimes(1); - instance.setProps({ docId: '9876' }); - await waitForPromises(); + await act(async () => { + instance.setProps({ docId: '9876' }); + }); expect(args.docStorage.load).toHaveBeenCalledTimes(2); }); @@ -312,10 +311,11 @@ describe('Lens App', () => { args.editorFrame = frame; (args.docStorage.load as jest.Mock).mockRejectedValue('failed to load'); - const instance = mount(); + instance = mount(); - instance.setProps({ docId: '1234' }); - await waitForPromises(); + await act(async () => { + instance.setProps({ docId: '1234' }); + }); expect(args.docStorage.load).toHaveBeenCalledWith('1234'); expect(args.core.notifications.toasts.addDanger).toHaveBeenCalled(); @@ -353,22 +353,20 @@ describe('Lens App', () => { } as jest.ResolvedValue); }); - function getButton(instance: ReactWrapper): TopNavMenuData { - return (instance + function getButton(inst: ReactWrapper): TopNavMenuData { + return (inst .find('[data-test-subj="lnsApp_topNav"]') .prop('config') as TopNavMenuData[]).find( button => button.testId === 'lnsApp_saveButton' )!; } - function testSave(instance: ReactWrapper, saveProps: SaveProps) { - act(() => { - getButton(instance).run(instance.getDOMNode()); - }); + async function testSave(inst: ReactWrapper, saveProps: SaveProps) { + await getButton(inst).run(inst.getDOMNode()); - instance.update(); + inst.update(); - const handler = instance.findWhere(el => el.prop('onSave')).prop('onSave') as ( + const handler = inst.findWhere(el => el.prop('onSave')).prop('onSave') as ( p: unknown ) => void; handler(saveProps); @@ -406,9 +404,9 @@ describe('Lens App', () => { expression: 'kibana 2', })); - const instance = mount(); - - await waitForPromises(); + await act(async () => { + instance = mount(); + }); if (initialDocId) { expect(args.docStorage.load).toHaveBeenCalledTimes(1); @@ -427,9 +425,10 @@ describe('Lens App', () => { instance.update(); expect(getButton(instance).disableButton).toEqual(false); - testSave(instance, saveProps); - await waitForPromises(); + await act(async () => { + testSave(instance, saveProps); + }); return { args, instance }; } @@ -445,7 +444,7 @@ describe('Lens App', () => { }; args.editorFrame = frame; - const instance = mount(); + instance = mount(); expect(getButton(instance).disableButton).toEqual(true); @@ -469,7 +468,7 @@ describe('Lens App', () => { } as jest.ResolvedValue); args.editorFrame = frame; - const instance = mount(); + instance = mount(); expect(getButton(instance).disableButton).toEqual(true); const onChange = frame.mount.mock.calls[0][1].onChange; @@ -488,7 +487,7 @@ describe('Lens App', () => { const args = defaultArgs; args.editorFrame = frame; - const instance = mount(); + instance = mount(); expect(getButton(instance).disableButton).toEqual(true); @@ -505,7 +504,7 @@ describe('Lens App', () => { }); it('saves new docs', async () => { - const { args, instance } = await save({ + const { args, instance: inst } = await save({ initialDocId: undefined, newCopyOnSave: false, newTitle: 'hello there', @@ -519,13 +518,13 @@ describe('Lens App', () => { expect(args.redirectTo).toHaveBeenCalledWith('aaa'); - instance.setProps({ docId: 'aaa' }); + inst.setProps({ docId: 'aaa' }); expect(args.docStorage.load).not.toHaveBeenCalled(); }); it('saves the latest doc as a copy', async () => { - const { args, instance } = await save({ + const { args, instance: inst } = await save({ initialDocId: '1234', newCopyOnSave: true, newTitle: 'hello there', @@ -539,13 +538,13 @@ describe('Lens App', () => { expect(args.redirectTo).toHaveBeenCalledWith('aaa'); - instance.setProps({ docId: 'aaa' }); + inst.setProps({ docId: 'aaa' }); expect(args.docStorage.load).toHaveBeenCalledTimes(1); }); it('saves existing docs', async () => { - const { args, instance } = await save({ + const { args, instance: inst } = await save({ initialDocId: '1234', newCopyOnSave: false, newTitle: 'hello there', @@ -559,7 +558,7 @@ describe('Lens App', () => { expect(args.redirectTo).not.toHaveBeenCalled(); - instance.setProps({ docId: '1234' }); + inst.setProps({ docId: '1234' }); expect(args.docStorage.load).toHaveBeenCalledTimes(1); }); @@ -569,7 +568,7 @@ describe('Lens App', () => { args.editorFrame = frame; (args.docStorage.save as jest.Mock).mockRejectedValue({ message: 'failed' }); - const instance = mount(); + instance = mount(); const onChange = frame.mount.mock.calls[0][1].onChange; act(() => @@ -581,13 +580,12 @@ describe('Lens App', () => { instance.update(); - testSave(instance, { newCopyOnSave: false, newTitle: 'hello there' }); - - await waitForPromises(); + await act(async () => { + testSave(instance, { newCopyOnSave: false, newTitle: 'hello there' }); + }); expect(args.core.notifications.toasts.addDanger).toHaveBeenCalled(); expect(args.redirectTo).not.toHaveBeenCalled(); - await waitForPromises(); expect(getButton(instance).disableButton).toEqual(false); }); @@ -616,8 +614,10 @@ describe('Lens App', () => { const unpinned = esFilters.buildExistsFilter(field, indexPattern); const pinned = esFilters.buildExistsFilter(pinnedField, indexPattern); - FilterManager.setFiltersStore([pinned], esFilters.FilterStateStore.GLOBAL_STATE); - await waitForPromises(); + + await act(async () => { + FilterManager.setFiltersStore([pinned], esFilters.FilterStateStore.GLOBAL_STATE); + }); const { args } = await save({ initialDocId: '1234', @@ -695,7 +695,7 @@ describe('Lens App', () => { const args = defaultArgs; args.editorFrame = frame; - const instance = mount(); + instance = mount(); expect(TopNavMenu).toHaveBeenCalledWith( expect.objectContaining({ @@ -705,14 +705,14 @@ describe('Lens App', () => { ); const onChange = frame.mount.mock.calls[0][1].onChange; - act(() => + + await act(async () => { onChange({ filterableIndexPatterns: [{ id: '1', title: 'newIndex' }], doc: ({ id: undefined, expression: 'valid expression' } as unknown) as Document, - }) - ); + }); + }); - await waitForPromises(); instance.update(); expect(TopNavMenu).toHaveBeenCalledWith( @@ -723,14 +723,13 @@ describe('Lens App', () => { ); // Do it again to verify that the dirty checking is done right - act(() => + + await act(async () => { onChange({ filterableIndexPatterns: [{ id: '2', title: 'second index' }], doc: ({ id: undefined, expression: 'valid expression' } as unknown) as Document, - }) - ); - - await waitForPromises(); + }); + }); instance.update(); @@ -745,7 +744,7 @@ describe('Lens App', () => { const args = defaultArgs; args.editorFrame = frame; - const instance = mount(); + instance = mount(); act(() => instance.find(TopNavMenu).prop('onQuerySubmit')!({ @@ -777,7 +776,7 @@ describe('Lens App', () => { const args = defaultArgs; args.editorFrame = frame; - const instance = mount(); + instance = mount(); const indexPattern = ({ id: 'index1' } as unknown) as IIndexPattern; const field = ({ name: 'myfield' } as unknown) as IFieldType; @@ -819,7 +818,7 @@ describe('Lens App', () => { const args = makeDefaultArgs(); args.editorFrame = frame; - const instance = mount(); + instance = mount(); expect(TopNavMenu).toHaveBeenCalledWith( expect.objectContaining({ @@ -862,7 +861,7 @@ describe('Lens App', () => { const args = makeDefaultArgs(); args.editorFrame = frame; - const instance = mount(); + instance = mount(); act(() => { instance.find(TopNavMenu).prop('onSaved')!({ @@ -905,7 +904,7 @@ describe('Lens App', () => { const args = makeDefaultArgs(); args.editorFrame = frame; - const instance = mount(); + instance = mount(); act(() => instance.find(TopNavMenu).prop('onQuerySubmit')!({ @@ -941,7 +940,7 @@ describe('Lens App', () => { const args = makeDefaultArgs(); args.editorFrame = frame; - const instance = mount(); + instance = mount(); const onError = frame.mount.mock.calls[0][1].onError; onError({ message: 'error' }); diff --git a/x-pack/legacy/plugins/lens/public/debounced_component/debounced_component.test.tsx b/x-pack/legacy/plugins/lens/public/debounced_component/debounced_component.test.tsx index 26e9c18e00e9..447d9118c293 100644 --- a/x-pack/legacy/plugins/lens/public/debounced_component/debounced_component.test.tsx +++ b/x-pack/legacy/plugins/lens/public/debounced_component/debounced_component.test.tsx @@ -7,6 +7,7 @@ import React from 'react'; import { mountWithIntl as mount } from 'test_utils/enzyme_helpers'; import { debouncedComponent } from './debounced_component'; +import { act } from 'react-dom/test-utils'; describe('debouncedComponent', () => { test('immediately renders', () => { @@ -23,7 +24,9 @@ describe('debouncedComponent', () => { const component = mount(); component.setProps({ title: 'yall' }); expect(component.text()).toEqual('there'); - await new Promise(r => setTimeout(r, 1)); + await act(async () => { + await new Promise(r => setTimeout(r, 1)); + }); expect(component.text()).toEqual('yall'); }); }); diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.test.tsx b/x-pack/legacy/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.test.tsx index 4736dd75831e..dd591b3992fe 100644 --- a/x-pack/legacy/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.test.tsx +++ b/x-pack/legacy/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.test.tsx @@ -22,13 +22,6 @@ import { ReactExpressionRendererType } from 'src/plugins/expressions/public'; import { DragDrop } from '../../drag_drop'; import { FrameLayout } from './frame_layout'; -// calling this function will wait for all pending Promises from mock -// datasources to be processed by its callers. -const waitForPromises = async () => - act(async () => { - await new Promise(resolve => setTimeout(resolve)); - }); - function generateSuggestion(state = {}): DatasourceSuggestion { return { state, @@ -102,7 +95,7 @@ describe('editor_frame', () => { describe('initialization', () => { it('should initialize initial datasource', async () => { - act(() => { + await act(async () => { mount( { /> ); }); - await waitForPromises(); expect(mockDatasource.initialize).toHaveBeenCalled(); }); @@ -150,7 +142,7 @@ describe('editor_frame', () => { const datasource1State = { datasource1: '' }; const datasource2State = { datasource2: '' }; - act(() => { + await act(async () => { mount( { /> ); }); - await waitForPromises(); expect(mockDatasource.initialize).toHaveBeenCalledWith(datasource1State); expect(mockDatasource2.initialize).toHaveBeenCalledWith(datasource2State); expect(mockDatasource3.initialize).not.toHaveBeenCalled(); }); it('should not render something before all datasources are initialized', async () => { - act(() => { + await act(async () => { mount( { ExpressionRenderer={expressionRendererMock} /> ); + expect(mockVisualization.renderLayerConfigPanel).not.toHaveBeenCalled(); + expect(mockDatasource.renderDataPanel).not.toHaveBeenCalled(); }); - - expect(mockVisualization.renderLayerConfigPanel).not.toHaveBeenCalled(); - expect(mockDatasource.renderDataPanel).not.toHaveBeenCalled(); - await waitForPromises(); }); it('should not initialize visualization before datasource is initialized', async () => { - act(() => { + await act(async () => { mount( { ExpressionRenderer={expressionRendererMock} /> ); + expect(mockVisualization.initialize).not.toHaveBeenCalled(); }); - expect(mockVisualization.initialize).not.toHaveBeenCalled(); - - await waitForPromises(); - expect(mockVisualization.initialize).toHaveBeenCalled(); }); it('should pass the public frame api into visualization initialize', async () => { - act(() => { + await act(async () => { mount( { dateRange={{ fromDate: 'now-7d', toDate: 'now' }} /> ); + expect(mockVisualization.initialize).not.toHaveBeenCalled(); }); - expect(mockVisualization.initialize).not.toHaveBeenCalled(); - - await waitForPromises(); - expect(mockVisualization.initialize).toHaveBeenCalledWith({ datasourceLayers: {}, addNewLayer: expect.any(Function), @@ -275,7 +258,7 @@ describe('editor_frame', () => { it('should add new layer on active datasource on frame api call', async () => { const initialState = { datasource2: '' }; mockDatasource2.initialize.mockReturnValue(Promise.resolve(initialState)); - act(() => { + await act(async () => { mount( { ); }); - await waitForPromises(); - act(() => { mockVisualization.initialize.mock.calls[0][0].addNewLayer(); }); @@ -308,7 +289,7 @@ describe('editor_frame', () => { mockDatasource2.initialize.mockReturnValue(Promise.resolve(initialState)); mockDatasource2.getLayers.mockReturnValue(['abc', 'def']); mockDatasource2.removeLayer.mockReturnValue({ removed: true }); - act(() => { + await act(async () => { mount( { ); }); - await waitForPromises(); - act(() => { mockVisualization.initialize.mock.calls[0][0].removeLayers(['abc', 'def']); }); @@ -340,7 +319,7 @@ describe('editor_frame', () => { const initialState = {}; let databaseInitialized: ({}) => void; - act(() => { + await act(async () => { mount( { /> ); }); - - databaseInitialized!(initialState); - - await waitForPromises(); + await act(async () => { + databaseInitialized!(initialState); + }); expect(mockDatasource.renderDataPanel).toHaveBeenCalledWith( expect.any(Element), expect.objectContaining({ state: initialState }) @@ -375,25 +353,26 @@ describe('editor_frame', () => { it('should initialize visualization state and render config panel', async () => { const initialState = {}; mockDatasource.getLayers.mockReturnValue(['first']); - mount( - initialState }, - }} - datasourceMap={{ - testDatasource: { - ...mockDatasource, - initialize: () => Promise.resolve(), - }, - }} - initialDatasourceId="testDatasource" - initialVisualizationId="testVis" - ExpressionRenderer={expressionRendererMock} - /> - ); - await waitForPromises(); + await act(async () => { + mount( + initialState }, + }} + datasourceMap={{ + testDatasource: { + ...mockDatasource, + initialize: () => Promise.resolve(), + }, + }} + initialDatasourceId="testDatasource" + initialVisualizationId="testVis" + ExpressionRenderer={expressionRendererMock} + /> + ); + }); expect(mockVisualization.renderLayerConfigPanel).toHaveBeenCalledWith( expect.any(Element), @@ -401,28 +380,30 @@ describe('editor_frame', () => { ); }); + let instance: ReactWrapper; + it('should render the resulting expression using the expression renderer', async () => { mockDatasource.getLayers.mockReturnValue(['first']); - const instance = mount( - 'vis' }, - }} - datasourceMap={{ - testDatasource: { - ...mockDatasource, - toExpression: () => 'datasource', - }, - }} - initialDatasourceId="testDatasource" - initialVisualizationId="testVis" - ExpressionRenderer={expressionRendererMock} - /> - ); - - await waitForPromises(); + await act(async () => { + instance = mount( + 'vis' }, + }} + datasourceMap={{ + testDatasource: { + ...mockDatasource, + toExpression: () => 'datasource', + }, + }} + initialDatasourceId="testDatasource" + initialVisualizationId="testVis" + ExpressionRenderer={expressionRendererMock} + /> + ); + }); instance.update(); @@ -489,40 +470,40 @@ describe('editor_frame', () => { mockDatasource2.initialize.mockImplementation(initialState => Promise.resolve(initialState)); mockDatasource2.getLayers.mockReturnValue(['second', 'third']); - const instance = mount( - 'vis' }, - }} - datasourceMap={{ - testDatasource: mockDatasource, - testDatasource2: mockDatasource2, - }} - initialDatasourceId="testDatasource" - initialVisualizationId="testVis" - ExpressionRenderer={expressionRendererMock} - doc={{ - visualizationType: 'testVis', - title: '', - expression: '', - state: { - datasourceStates: { - testDatasource: {}, - testDatasource2: {}, + await act(async () => { + instance = mount( + 'vis' }, + }} + datasourceMap={{ + testDatasource: mockDatasource, + testDatasource2: mockDatasource2, + }} + initialDatasourceId="testDatasource" + initialVisualizationId="testVis" + ExpressionRenderer={expressionRendererMock} + doc={{ + visualizationType: 'testVis', + title: '', + expression: '', + state: { + datasourceStates: { + testDatasource: {}, + testDatasource2: {}, + }, + visualization: {}, + datasourceMetaData: { + filterableIndexPatterns: [], + }, + query: { query: '', language: 'lucene' }, + filters: [], }, - visualization: {}, - datasourceMetaData: { - filterableIndexPatterns: [], - }, - query: { query: '', language: 'lucene' }, - filters: [], - }, - }} - /> - ); - - await waitForPromises(); + }} + /> + ); + }); instance.update(); @@ -615,23 +596,23 @@ describe('editor_frame', () => { describe('state update', () => { it('should re-render config panel after state update', async () => { mockDatasource.getLayers.mockReturnValue(['first']); - mount( - - ); - - await waitForPromises(); + await act(async () => { + mount( + + ); + }); const updatedState = {}; const setVisualizationState = (mockVisualization.renderLayerConfigPanel as jest.Mock).mock .calls[0][1].setState; @@ -650,22 +631,23 @@ describe('editor_frame', () => { it('should re-render data panel after state update', async () => { mockDatasource.getLayers.mockReturnValue(['first']); - mount( - - ); - await waitForPromises(); + await act(async () => { + mount( + + ); + }); const updatedState = { title: 'shazm', @@ -687,22 +669,23 @@ describe('editor_frame', () => { it('should re-render config panel with updated datasource api after datasource state update', async () => { mockDatasource.getLayers.mockReturnValue(['first']); - mount( - - ); - await waitForPromises(); + await act(async () => { + mount( + + ); + }); const updatedPublicAPI: DatasourcePublicAPI = { renderLayerPanel: jest.fn(), @@ -737,40 +720,40 @@ describe('editor_frame', () => { mockDatasource.getLayers.mockReturnValue(['first']); mockDatasource2.getLayers.mockReturnValue(['second', 'third']); - mount( - { + mount( + - ); - - await waitForPromises(); + }} + /> + ); + }); expect(mockVisualization.renderLayerConfigPanel).toHaveBeenCalled(); @@ -790,40 +773,40 @@ describe('editor_frame', () => { const datasource1State = { datasource1: '' }; const datasource2State = { datasource2: '' }; - mount( - { + mount( + - ); - - await waitForPromises(); + }} + /> + ); + }); expect(mockDatasource.getPublicAPI).toHaveBeenCalledWith( expect.objectContaining({ @@ -854,23 +837,23 @@ describe('editor_frame', () => { mockDatasource.initialize.mockResolvedValue(datasourceState); mockDatasource.getLayers.mockReturnValue(['first']); - mount( - - ); - - await waitForPromises(); + await act(async () => { + mount( + + ); + }); expect(mockDatasource.getPublicAPI).toHaveBeenCalledWith({ dateRange, @@ -882,22 +865,23 @@ describe('editor_frame', () => { it('should re-create the public api after state has been set', async () => { mockDatasource.getLayers.mockReturnValue(['first']); - mount( - - ); - await waitForPromises(); + await act(async () => { + mount( + + ); + }); const updatedState = {}; const setDatasourceState = mockDatasource.getPublicAPI.mock.calls[0][0].setState; @@ -951,23 +935,24 @@ describe('editor_frame', () => { }, ]); - instance = mount( - - ); - await waitForPromises(); + await act(async () => { + instance = mount( + + ); + }); // necessary to flush elements to dom synchronously instance.update(); @@ -986,15 +971,14 @@ describe('editor_frame', () => { }); it('should initialize other datasource on switch', async () => { - act(() => { + await act(async () => { instance.find('button[data-test-subj="datasource-switch"]').simulate('click'); }); - act(() => { + await act(async () => { (document.querySelector( '[data-test-subj="datasource-switch-testDatasource2"]' ) as HTMLButtonElement).click(); }); - await waitForPromises(); expect(mockDatasource2.initialize).toHaveBeenCalled(); }); @@ -1004,11 +988,11 @@ describe('editor_frame', () => { instance.find('button[data-test-subj="datasource-switch"]').simulate('click'); - (document.querySelector( - '[data-test-subj="datasource-switch-testDatasource2"]' - ) as HTMLButtonElement).click(); - - await waitForPromises(); + await act(async () => { + (document.querySelector( + '[data-test-subj="datasource-switch-testDatasource2"]' + ) as HTMLButtonElement).click(); + }); expect(mockDatasource2.renderDataPanel).toHaveBeenCalledWith( expect.any(Element), @@ -1064,23 +1048,23 @@ describe('editor_frame', () => { describe('suggestions', () => { it('should fetch suggestions of currently active datasource', async () => { - mount( - - ); - - await waitForPromises(); + await act(async () => { + mount( + + ); + }); expect(mockDatasource.getDatasourceSuggestionsFromCurrentState).toHaveBeenCalled(); expect(mockDatasource2.getDatasourceSuggestionsFromCurrentState).not.toHaveBeenCalled(); @@ -1099,95 +1083,98 @@ describe('editor_frame', () => { keptLayerIds: [], }, ]); - mount( - - ); - await waitForPromises(); + await act(async () => { + mount( + + ); + }); expect(mockVisualization.getSuggestions).toHaveBeenCalled(); expect(mockVisualization2.getSuggestions).toHaveBeenCalled(); }); + let instance: ReactWrapper; it('should display top 5 suggestions in descending order', async () => { mockDatasource.getLayers.mockReturnValue(['first']); - const instance = mount( - [ - { - score: 0.1, - state: {}, - title: 'Suggestion6', - previewIcon: 'empty', - }, - { - score: 0.5, - state: {}, - title: 'Suggestion3', - previewIcon: 'empty', - }, - { - score: 0.7, - state: {}, - title: 'Suggestion2', - previewIcon: 'empty', - }, - { - score: 0.8, - state: {}, - title: 'Suggestion1', - previewIcon: 'empty', - }, - ], - }, - testVis2: { - ...mockVisualization, - getSuggestions: () => [ - { - score: 0.4, - state: {}, - title: 'Suggestion5', - previewIcon: 'empty', - }, - { - score: 0.45, - state: {}, - title: 'Suggestion4', - previewIcon: 'empty', - }, - ], - }, - }} - datasourceMap={{ - testDatasource: { - ...mockDatasource, - getDatasourceSuggestionsFromCurrentState: () => [generateSuggestion()], - }, - }} - initialDatasourceId="testDatasource" - initialVisualizationId="testVis" - ExpressionRenderer={expressionRendererMock} - /> - ); - await waitForPromises(); + await act(async () => { + instance = mount( + [ + { + score: 0.1, + state: {}, + title: 'Suggestion6', + previewIcon: 'empty', + }, + { + score: 0.5, + state: {}, + title: 'Suggestion3', + previewIcon: 'empty', + }, + { + score: 0.7, + state: {}, + title: 'Suggestion2', + previewIcon: 'empty', + }, + { + score: 0.8, + state: {}, + title: 'Suggestion1', + previewIcon: 'empty', + }, + ], + }, + testVis2: { + ...mockVisualization, + getSuggestions: () => [ + { + score: 0.4, + state: {}, + title: 'Suggestion5', + previewIcon: 'empty', + }, + { + score: 0.45, + state: {}, + title: 'Suggestion4', + previewIcon: 'empty', + }, + ], + }, + }} + datasourceMap={{ + testDatasource: { + ...mockDatasource, + getDatasourceSuggestionsFromCurrentState: () => [generateSuggestion()], + }, + }} + initialDatasourceId="testDatasource" + initialVisualizationId="testVis" + ExpressionRenderer={expressionRendererMock} + /> + ); + }); // TODO why is this necessary? instance.update(); @@ -1210,36 +1197,37 @@ describe('editor_frame', () => { mockDatasource.getLayers.mockReturnValue(['first', 'second', 'third']); const newDatasourceState = {}; const suggestionVisState = {}; - const instance = mount( - [ - { - score: 0.8, - state: suggestionVisState, - title: 'Suggestion1', - previewIcon: 'empty', - }, - ], - }, - testVis2: mockVisualization2, - }} - datasourceMap={{ - testDatasource: { - ...mockDatasource, - getDatasourceSuggestionsFromCurrentState: () => [generateSuggestion()], - }, - }} - initialDatasourceId="testDatasource" - initialVisualizationId="testVis2" - ExpressionRenderer={expressionRendererMock} - /> - ); - await waitForPromises(); + await act(async () => { + instance = mount( + [ + { + score: 0.8, + state: suggestionVisState, + title: 'Suggestion1', + previewIcon: 'empty', + }, + ], + }, + testVis2: mockVisualization2, + }} + datasourceMap={{ + testDatasource: { + ...mockDatasource, + getDatasourceSuggestionsFromCurrentState: () => [generateSuggestion()], + }, + }} + initialDatasourceId="testDatasource" + initialVisualizationId="testVis2" + ExpressionRenderer={expressionRendererMock} + /> + ); + }); // TODO why is this necessary? instance.update(); @@ -1269,43 +1257,44 @@ describe('editor_frame', () => { it('should switch to best suggested visualization on field drop', async () => { mockDatasource.getLayers.mockReturnValue(['first']); const suggestionVisState = {}; - const instance = mount( - [ - { - score: 0.2, - state: {}, - title: 'Suggestion1', - previewIcon: 'empty', - }, - { - score: 0.8, - state: suggestionVisState, - title: 'Suggestion2', - previewIcon: 'empty', - }, - ], - }, - testVis2: mockVisualization2, - }} - datasourceMap={{ - testDatasource: { - ...mockDatasource, - getDatasourceSuggestionsForField: () => [generateSuggestion()], - getDatasourceSuggestionsFromCurrentState: () => [generateSuggestion()], - }, - }} - initialDatasourceId="testDatasource" - initialVisualizationId="testVis" - ExpressionRenderer={expressionRendererMock} - /> - ); - await waitForPromises(); + await act(async () => { + instance = mount( + [ + { + score: 0.2, + state: {}, + title: 'Suggestion1', + previewIcon: 'empty', + }, + { + score: 0.8, + state: suggestionVisState, + title: 'Suggestion2', + previewIcon: 'empty', + }, + ], + }, + testVis2: mockVisualization2, + }} + datasourceMap={{ + testDatasource: { + ...mockDatasource, + getDatasourceSuggestionsForField: () => [generateSuggestion()], + getDatasourceSuggestionsFromCurrentState: () => [generateSuggestion()], + }, + }} + initialDatasourceId="testDatasource" + initialVisualizationId="testVis" + ExpressionRenderer={expressionRendererMock} + /> + ); + }); // TODO why is this necessary? instance.update(); @@ -1328,58 +1317,59 @@ describe('editor_frame', () => { it('should use the currently selected visualization if possible on field drop', async () => { mockDatasource.getLayers.mockReturnValue(['first', 'second', 'third']); const suggestionVisState = {}; - const instance = mount( - [ - { - score: 0.2, - state: {}, - title: 'Suggestion1', - previewIcon: 'empty', - }, - { - score: 0.6, - state: {}, - title: 'Suggestion2', - previewIcon: 'empty', - }, - ], - }, - testVis2: { - ...mockVisualization2, - getSuggestions: () => [ - { - score: 0.8, - state: suggestionVisState, - title: 'Suggestion3', - previewIcon: 'empty', - }, - ], - }, - }} - datasourceMap={{ - testDatasource: { - ...mockDatasource, - getDatasourceSuggestionsForField: () => [generateSuggestion()], - getDatasourceSuggestionsFromCurrentState: () => [generateSuggestion()], - renderDataPanel: (_element, { dragDropContext: { setDragging, dragging } }) => { - if (dragging !== 'draggedField') { - setDragging('draggedField'); - } - }, - }, - }} - initialDatasourceId="testDatasource" - initialVisualizationId="testVis2" - ExpressionRenderer={expressionRendererMock} - /> - ); - await waitForPromises(); + await act(async () => { + instance = mount( + [ + { + score: 0.2, + state: {}, + title: 'Suggestion1', + previewIcon: 'empty', + }, + { + score: 0.6, + state: {}, + title: 'Suggestion2', + previewIcon: 'empty', + }, + ], + }, + testVis2: { + ...mockVisualization2, + getSuggestions: () => [ + { + score: 0.8, + state: suggestionVisState, + title: 'Suggestion3', + previewIcon: 'empty', + }, + ], + }, + }} + datasourceMap={{ + testDatasource: { + ...mockDatasource, + getDatasourceSuggestionsForField: () => [generateSuggestion()], + getDatasourceSuggestionsFromCurrentState: () => [generateSuggestion()], + renderDataPanel: (_element, { dragDropContext: { setDragging, dragging } }) => { + if (dragging !== 'draggedField') { + setDragging('draggedField'); + } + }, + }, + }} + initialDatasourceId="testDatasource" + initialVisualizationId="testVis2" + ExpressionRenderer={expressionRendererMock} + /> + ); + }); // TODO why is this necessary? instance.update(); @@ -1428,54 +1418,55 @@ describe('editor_frame', () => { }, ], }; - const instance = mount( - [ - { - score: 0.2, - state: {}, - title: 'Suggestion1', - previewIcon: 'empty', - }, - { - score: 0.6, - state: {}, - title: 'Suggestion2', - previewIcon: 'empty', - }, - ], - }, - testVis2: { - ...mockVisualization2, - getSuggestions: () => [], - }, - testVis3: { - ...mockVisualization3, - }, - }} - datasourceMap={{ - testDatasource: { - ...mockDatasource, - getDatasourceSuggestionsForField: () => [generateSuggestion()], - getDatasourceSuggestionsFromCurrentState: () => [generateSuggestion()], - renderDataPanel: (_element, { dragDropContext: { setDragging, dragging } }) => { - if (dragging !== 'draggedField') { - setDragging('draggedField'); - } - }, - }, - }} - initialDatasourceId="testDatasource" - initialVisualizationId="testVis2" - ExpressionRenderer={expressionRendererMock} - /> - ); - await waitForPromises(); + await act(async () => { + instance = mount( + [ + { + score: 0.2, + state: {}, + title: 'Suggestion1', + previewIcon: 'empty', + }, + { + score: 0.6, + state: {}, + title: 'Suggestion2', + previewIcon: 'empty', + }, + ], + }, + testVis2: { + ...mockVisualization2, + getSuggestions: () => [], + }, + testVis3: { + ...mockVisualization3, + }, + }} + datasourceMap={{ + testDatasource: { + ...mockDatasource, + getDatasourceSuggestionsForField: () => [generateSuggestion()], + getDatasourceSuggestionsFromCurrentState: () => [generateSuggestion()], + renderDataPanel: (_element, { dragDropContext: { setDragging, dragging } }) => { + if (dragging !== 'draggedField') { + setDragging('draggedField'); + } + }, + }, + }} + initialDatasourceId="testDatasource" + initialVisualizationId="testVis2" + ExpressionRenderer={expressionRendererMock} + /> + ); + }); // TODO why is this necessary? instance.update(); @@ -1514,8 +1505,8 @@ describe('editor_frame', () => { }); mockVisualization.initialize.mockReturnValue({ initialState: true }); - act(() => { - instance = mount( + await act(async () => { + mount( { onChange={onChange} /> ); + expect(onChange).toHaveBeenCalledTimes(0); + resolver({}); }); - expect(onChange).toHaveBeenCalledTimes(0); - - resolver({}); - await waitForPromises(); - expect(onChange).toHaveBeenCalledTimes(2); expect(onChange).toHaveBeenNthCalledWith(1, { filterableIndexPatterns: [{ id: '1', title: 'resolved' }], @@ -1585,7 +1573,7 @@ describe('editor_frame', () => { mockDatasource.getLayers.mockReturnValue(['first']); mockVisualization.initialize.mockReturnValue({ initialState: true }); - act(() => { + await act(async () => { instance = mount( { ); }); - await waitForPromises(); - expect(onChange).toHaveBeenCalledTimes(2); mockDatasource.toExpression.mockReturnValue('data expression'); @@ -1612,7 +1598,6 @@ describe('editor_frame', () => { instance.setProps({ query: { query: 'new query', language: 'lucene' } }); instance.update(); - await waitForPromises(); expect(onChange).toHaveBeenCalledTimes(3); expect(onChange).toHaveBeenNthCalledWith(3, { filterableIndexPatterns: [], @@ -1643,7 +1628,7 @@ describe('editor_frame', () => { }); mockVisualization.initialize.mockReturnValue({ initialState: true }); - act(() => { + await act(async () => { instance = mount( { ); }); - await waitForPromises(); expect(onChange).toHaveBeenCalledTimes(2); - act(() => { + await act(async () => { (instance.find(FrameLayout).prop('dataPanel') as ReactElement)!.props.dispatch({ type: 'UPDATE_DATASOURCE_STATE', updater: () => ({ @@ -1670,8 +1654,6 @@ describe('editor_frame', () => { }); }); - await waitForPromises(); - expect(onChange).toHaveBeenCalledTimes(3); }); }); diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel.test.tsx b/x-pack/legacy/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel.test.tsx index 92a14963ff0b..a51091d39f84 100644 --- a/x-pack/legacy/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel.test.tsx +++ b/x-pack/legacy/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel.test.tsx @@ -23,11 +23,6 @@ import { Ast } from '@kbn/interpreter/common'; import { coreMock } from 'src/core/public/mocks'; import { esFilters, IFieldType, IIndexPattern } from '../../../../../../../src/plugins/data/public'; -const waitForPromises = async () => - act(async () => { - await new Promise(resolve => setTimeout(resolve)); - }); - describe('workspace_panel', () => { let mockVisualization: jest.Mocked; let mockVisualization2: jest.Mocked; @@ -299,41 +294,43 @@ describe('workspace_panel', () => { expressionRendererMock = jest.fn(_arg => ); - instance = mount( - 'vis' }, - }} - visualizationState={{}} - dispatch={() => {}} - ExpressionRenderer={expressionRendererMock} - core={coreMock.createSetup()} - /> - ); - - // "wait" for the expression to execute - await waitForPromises(); + await act(async () => { + instance = mount( + 'vis' }, + }} + visualizationState={{}} + dispatch={() => {}} + ExpressionRenderer={expressionRendererMock} + core={coreMock.createSetup()} + /> + ); + }); instance.update(); expect(expressionRendererMock).toHaveBeenCalledTimes(1); - instance.setProps({ - framePublicAPI: { ...framePublicAPI, dateRange: { fromDate: 'now-90d', toDate: 'now-30d' } }, + await act(async () => { + instance.setProps({ + framePublicAPI: { + ...framePublicAPI, + dateRange: { fromDate: 'now-90d', toDate: 'now-30d' }, + }, + }); }); - - await waitForPromises(); instance.update(); expect(expressionRendererMock).toHaveBeenCalledTimes(2); @@ -351,33 +348,32 @@ describe('workspace_panel', () => { .mockReturnValueOnce('datasource second'); expressionRendererMock = jest.fn(_arg => ); + await act(async () => { + instance = mount( + 'vis' }, + }} + visualizationState={{}} + dispatch={() => {}} + ExpressionRenderer={expressionRendererMock} + core={coreMock.createSetup()} + /> + ); + }); - instance = mount( - 'vis' }, - }} - visualizationState={{}} - dispatch={() => {}} - ExpressionRenderer={expressionRendererMock} - core={coreMock.createSetup()} - /> - ); - - // "wait" for the expression to execute - await waitForPromises(); instance.update(); expect(expressionRendererMock).toHaveBeenCalledTimes(1); @@ -385,14 +381,15 @@ describe('workspace_panel', () => { const indexPattern = ({ id: 'index1' } as unknown) as IIndexPattern; const field = ({ name: 'myfield' } as unknown) as IFieldType; - instance.setProps({ - framePublicAPI: { - ...framePublicAPI, - filters: [esFilters.buildExistsFilter(field, indexPattern)], - }, + await act(async () => { + instance.setProps({ + framePublicAPI: { + ...framePublicAPI, + filters: [esFilters.buildExistsFilter(field, indexPattern)], + }, + }); }); - await waitForPromises(); instance.update(); expect(expressionRendererMock).toHaveBeenCalledTimes(2); @@ -442,32 +439,31 @@ describe('workspace_panel', () => { first: mockDatasource.publicAPIMock, }; - instance = mount( - 'vis' }, - }} - visualizationState={{}} - dispatch={() => {}} - ExpressionRenderer={expressionRendererMock} - core={coreMock.createSetup()} - /> - ); - - // "wait" for the expression to execute - await waitForPromises(); + await act(async () => { + instance = mount( + 'vis' }, + }} + visualizationState={{}} + dispatch={() => {}} + ExpressionRenderer={expressionRendererMock} + core={coreMock.createSetup()} + /> + ); + }); instance.update(); @@ -486,32 +482,31 @@ describe('workspace_panel', () => { first: mockDatasource.publicAPIMock, }; - instance = mount( - 'vis' }, - }} - visualizationState={{}} - dispatch={() => {}} - ExpressionRenderer={expressionRendererMock} - core={coreMock.createSetup()} - /> - ); - - // "wait" for the expression to execute - await waitForPromises(); + await act(async () => { + instance = mount( + 'vis' }, + }} + visualizationState={{}} + dispatch={() => {}} + ExpressionRenderer={expressionRendererMock} + core={coreMock.createSetup()} + /> + ); + }); instance.update(); diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_datasource/datapanel.test.tsx b/x-pack/legacy/plugins/lens/public/indexpattern_datasource/datapanel.test.tsx index 7c1db7ed4357..c669369e6e1d 100644 --- a/x-pack/legacy/plugins/lens/public/indexpattern_datasource/datapanel.test.tsx +++ b/x-pack/legacy/plugins/lens/public/indexpattern_datasource/datapanel.test.tsx @@ -269,13 +269,6 @@ describe('IndexPattern Data Panel', () => { }); describe('loading existence data', () => { - function waitForPromises() { - return Promise.resolve() - .catch(() => {}) - .then(() => {}) - .then(() => {}); - } - function testProps() { const setState = jest.fn(); core.http.get.mockImplementation(async ({ path }) => { @@ -318,14 +311,12 @@ describe('IndexPattern Data Panel', () => { const props = testProps(); const inst = mountWithIntl(); - act(() => { + await act(async () => { inst.update(); }); - await waitForPromises(); - if (stateChanges || propChanges) { - act(() => { + await act(async () => { ((inst.setProps as unknown) as (props: unknown) => {})({ ...props, ...((propChanges as object) || {}), @@ -336,7 +327,6 @@ describe('IndexPattern Data Panel', () => { }); inst.update(); }); - await waitForPromises(); } return props.setState; @@ -473,13 +463,11 @@ describe('IndexPattern Data Panel', () => { }); it('shows a loading indicator when loading', async () => { + const load = async () => {}; const inst = mountWithIntl(); - expect(inst.find(EuiProgress).length).toEqual(1); - - await waitForPromises(); + await act(load); inst.update(); - expect(inst.find(EuiProgress).length).toEqual(0); }); @@ -520,7 +508,7 @@ describe('IndexPattern Data Panel', () => { inst.update(); }); - act(() => { + await act(async () => { ((inst.setProps as unknown) as (props: unknown) => {})({ ...props, dateRange: { fromDate: '2019-01-01', toDate: '2020-01-03' }, @@ -528,8 +516,6 @@ describe('IndexPattern Data Panel', () => { inst.update(); }); - await waitForPromises(); - expect(core.http.get).toHaveBeenCalledTimes(2); expect(overlapCount).toEqual(0); }); diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_datasource/field_item.test.tsx b/x-pack/legacy/plugins/lens/public/indexpattern_datasource/field_item.test.tsx index 1a38ffa44f6f..77f7f8cdab35 100644 --- a/x-pack/legacy/plugins/lens/public/indexpattern_datasource/field_item.test.tsx +++ b/x-pack/legacy/plugins/lens/public/indexpattern_datasource/field_item.test.tsx @@ -16,11 +16,6 @@ import { IndexPattern } from './types'; jest.mock('ui/new_platform'); -const waitForPromises = async () => - act(async () => { - await new Promise(resolve => setTimeout(resolve)); - }); - describe('IndexPattern Field Item', () => { let defaultProps: FieldItemProps; let indexPattern: IndexPattern; @@ -99,7 +94,10 @@ describe('IndexPattern Field Item', () => { return Promise.resolve({}); }); const wrapper = mountWithIntl(); - wrapper.find('[data-test-subj="lnsFieldListPanelField-bytes"]').simulate('click'); + + await act(async () => { + wrapper.find('[data-test-subj="lnsFieldListPanelField-bytes"]').simulate('click'); + }); expect(core.http.post).toHaveBeenCalledWith( '/api/lens/index_stats/my-fake-index-pattern/field', @@ -153,19 +151,20 @@ describe('IndexPattern Field Item', () => { expect(wrapper.find(EuiLoadingSpinner)).toHaveLength(1); - resolveFunction!({ - totalDocuments: 4633, - sampledDocuments: 4633, - sampledValues: 4633, - histogram: { - buckets: [{ count: 705, key: 0 }], - }, - topValues: { - buckets: [{ count: 147, key: 0 }], - }, + await act(async () => { + resolveFunction!({ + totalDocuments: 4633, + sampledDocuments: 4633, + sampledValues: 4633, + histogram: { + buckets: [{ count: 705, key: 0 }], + }, + topValues: { + buckets: [{ count: 147, key: 0 }], + }, + }); }); - await waitForPromises(); wrapper.update(); expect(wrapper.find(EuiLoadingSpinner)).toHaveLength(0); diff --git a/x-pack/legacy/plugins/lens/public/loader.test.tsx b/x-pack/legacy/plugins/lens/public/loader.test.tsx index f448849d1043..445b4a07597e 100644 --- a/x-pack/legacy/plugins/lens/public/loader.test.tsx +++ b/x-pack/legacy/plugins/lens/public/loader.test.tsx @@ -8,27 +8,25 @@ import React from 'react'; import { EuiProgress } from '@elastic/eui'; import { Loader } from './loader'; import { mount } from 'enzyme'; +import { act } from 'react-dom/test-utils'; describe('loader', () => { it('shows a loading indicator when loading', async () => { const load = jest.fn(() => Promise.resolve()); const inst = mount(); - expect(inst.find(EuiProgress).length).toEqual(1); - await load(); + await act(async () => load()); inst.update(); - expect(inst.find(EuiProgress).length).toEqual(0); }); it('hides loading indicator when failed', async () => { const load = jest.fn(() => Promise.reject()); const inst = mount(); - expect(inst.find(EuiProgress).length).toEqual(1); - await Promise.resolve(); + await act(async () => Promise.resolve()); inst.update(); expect(inst.find(EuiProgress).length).toEqual(0); @@ -45,7 +43,11 @@ describe('loader', () => { return Promise.resolve().then(() => --count); }); const inst = mount(); - inst.setProps({ loadDeps: ['foo'] }); + + await act(async () => { + inst.setProps({ loadDeps: ['foo'] }); + }); + inst.update(); await Promise.resolve();