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

[Lens] Editor state 2 #36513

Merged
merged 60 commits into from
May 27, 2019
Merged
Show file tree
Hide file tree
Changes from 57 commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
6d6f304
add nativerenderer component
flash1293 May 7, 2019
44894df
use native renderer in app and editor frame
flash1293 May 7, 2019
943fca8
remove prop types
flash1293 May 7, 2019
731e264
add unit test and fix linting issues
flash1293 May 8, 2019
cc1a0db
rename test suite
flash1293 May 8, 2019
18f9d83
rename prop and adjust shallow comparison function
flash1293 May 9, 2019
60cd891
add documentation
flash1293 May 9, 2019
0eb1f31
move native renderer to top level directory
flash1293 May 9, 2019
0f652c3
Merge remote-tracking branch 'upstream/feature/lens' into lens/native…
flash1293 May 10, 2019
a21b0ea
add test, fix linting error and improve shallow equal comparison
flash1293 May 10, 2019
34e9328
Merge remote-tracking branch 'upstream/feature/lens' into lens/native…
flash1293 May 10, 2019
b124f88
Merge branch 'feature/lens' into lens/native-renderer
flash1293 May 10, 2019
36de942
Implement basic editor frame state handling
wylieconlon May 3, 2019
7445372
Merge remote-tracking branch 'upstream/feature/lens' into lens/native…
flash1293 May 10, 2019
0395e9a
Merge branch 'feature/lens' into lens/editor-state
flash1293 May 10, 2019
8147aaa
fix linting errors
flash1293 May 10, 2019
4baf6b1
fix native renderer tests
flash1293 May 10, 2019
d33576e
Merge branch 'lens/native-renderer' into lens/editor-state
flash1293 May 10, 2019
033a632
re structure editor frame
flash1293 May 10, 2019
345e20f
fix tests
flash1293 May 10, 2019
48203dd
move layout to a separate component
flash1293 May 10, 2019
dece47f
Merge remote-tracking branch 'upstream/feature/lens' into lens/native…
flash1293 May 12, 2019
7469315
Merge branch 'lens/native-renderer' into lens/editor-state
flash1293 May 12, 2019
ee6fdbc
clean up and improve typings
flash1293 May 12, 2019
396cf1c
add tests for plugin itself
flash1293 May 12, 2019
5a21a43
Merge branch 'feature/lens' into lens/native-renderer
flash1293 May 12, 2019
c383c3e
Merge branch 'lens/native-renderer' into lens/editor-state
flash1293 May 12, 2019
6f8c295
start implementing suggestion control flow
flash1293 May 12, 2019
b61008c
refactor control flow and add visualization switch action dispatch
flash1293 May 12, 2019
cae9fe0
move mock builder into separate file and fix tests
flash1293 May 12, 2019
3f48c98
remove unused import
flash1293 May 13, 2019
7cdea68
add tests for suggestions
flash1293 May 13, 2019
d63d2f9
correct typo
flash1293 May 13, 2019
52cad4f
rework suggestion stuff
flash1293 May 14, 2019
d776b54
Merge branch 'feature/lens' into lens/editor-state
flash1293 May 20, 2019
3221830
Merge branch 'lens/editor-state' into lens/editor-state-2
flash1293 May 20, 2019
f05024b
resolve conflicts
flash1293 May 20, 2019
1c24cb5
Merge branch 'lens/editor-state' into lens/editor-state-2
flash1293 May 20, 2019
bc0c85a
start implementing drop fiel control flow
flash1293 May 20, 2019
fe1356e
Merge branch 'feature/lens' into lens/editor-state
flash1293 May 21, 2019
a562914
Merge branch 'feature/lens' into lens/editor-state
flash1293 May 21, 2019
3d055ee
adress review and restructure state
flash1293 May 21, 2019
7c3a2c8
align naming
flash1293 May 21, 2019
849f3a1
add comment
flash1293 May 21, 2019
ff5092b
Merge branch 'lens/editor-state' into lens/editor-state-2
flash1293 May 21, 2019
7540916
fix merge conflict
flash1293 May 21, 2019
7b8c21a
refactor and add tests
flash1293 May 21, 2019
0c721a7
more refactoring
flash1293 May 21, 2019
21c8756
only save single visualization state
flash1293 May 21, 2019
b31032b
Merge branch 'lens/editor-state' into lens/editor-state-2
flash1293 May 21, 2019
fc47428
add comment
flash1293 May 21, 2019
eb8f2e8
start adding tests for suggestion helpers and improve mock typing
flash1293 May 21, 2019
e0be753
improve getSuggestions tests
flash1293 May 22, 2019
e464ce2
add i18n
flash1293 May 22, 2019
7f1d66d
use jest types
flash1293 May 22, 2019
0bfb2d3
Merge remote-tracking branch 'upstream/feature/lens' into lens/editor…
flash1293 May 22, 2019
0e6fd91
Merge branch 'feature/lens' into lens/editor-state-2
flash1293 May 23, 2019
1b1c9b7
review fixes
flash1293 May 23, 2019
8652dd2
remove roles from API
flash1293 May 23, 2019
082a2d3
simplify API and fix i18n context in tests
flash1293 May 24, 2019
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
Expand Up @@ -9,43 +9,25 @@ import { mount, ReactWrapper } from 'enzyme';
import { EditorFrame } from './editor_frame';
import { Visualization, Datasource, DatasourcePublicAPI } from '../../types';
import { act } from 'react-dom/test-utils';
import { createMockVisualization, createMockDatasource } from '../mock_extensions';

// calling this function will wait for all pending Promises from mock
// datasources to be processed by its callers.
const waitForPromises = () => new Promise(resolve => setImmediate(resolve));
const waitForPromises = () => new Promise(resolve => setTimeout(resolve));

describe('editor_frame', () => {
const getMockVisualization = () => ({
getMappingOfTableToRoles: jest.fn(),
getPersistableState: jest.fn(),
getSuggestions: jest.fn(),
initialize: jest.fn(),
renderConfigPanel: jest.fn(),
toExpression: jest.fn(),
});

const getMockDatasource = () => ({
getDatasourceSuggestionsForField: jest.fn(),
getDatasourceSuggestionsFromCurrentState: jest.fn(),
getPersistableState: jest.fn(),
getPublicAPI: jest.fn(),
initialize: jest.fn(() => Promise.resolve()),
renderDataPanel: jest.fn(),
toExpression: jest.fn(),
});

let mockVisualization: Visualization;
let mockDatasource: Datasource;

let mockVisualization2: Visualization;
let mockDatasource2: Datasource;

beforeEach(() => {
mockVisualization = getMockVisualization();
mockVisualization2 = getMockVisualization();
mockVisualization = createMockVisualization();
mockVisualization2 = createMockVisualization();

mockDatasource = getMockDatasource();
mockDatasource2 = getMockDatasource();
mockDatasource = createMockDatasource();
mockDatasource2 = createMockDatasource();
});

describe('initialization', () => {
Expand Down Expand Up @@ -439,4 +421,284 @@ describe('editor_frame', () => {
);
});
});

describe('suggestions', () => {
it('should fetch suggestions of currently active datasource', async () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This says that it's fetching suggestions on first render, without user interaction. Why would we want that?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test loads the editor with an initial datasource and an initial visualization. This means the datasource will be able to provide suggestions for datatables and all visualizations can particiapte in creating suggestions. This is done to fill the bottom right suggestions:
Untitled drawing

mount(
<EditorFrame
visualizationMap={{
testVis: mockVisualization,
}}
datasourceMap={{
testDatasource: mockDatasource,
testDatasource2: mockDatasource2,
}}
initialDatasourceId="testDatasource"
initialVisualizationId="testVis"
/>
);

await waitForPromises();

expect(mockDatasource.getDatasourceSuggestionsFromCurrentState).toHaveBeenCalled();
expect(mockDatasource2.getDatasourceSuggestionsFromCurrentState).not.toHaveBeenCalled();
});

it('should fetch suggestions of all visualizations', async () => {
mount(
<EditorFrame
visualizationMap={{
testVis: mockVisualization,
testVis2: mockVisualization2,
}}
datasourceMap={{
testDatasource: mockDatasource,
testDatasource2: mockDatasource2,
}}
initialDatasourceId="testDatasource"
initialVisualizationId="testVis"
/>
);

await waitForPromises();

expect(mockVisualization.getSuggestions).toHaveBeenCalled();
expect(mockVisualization2.getSuggestions).toHaveBeenCalled();
});

it('should display suggestions in descending order', async () => {
const instance = mount(
<EditorFrame
visualizationMap={{
testVis: {
...mockVisualization,
getSuggestions: () => [
{
datasourceSuggestionId: 0,
score: 0.5,
state: {},
title: 'Suggestion2',
},
{
datasourceSuggestionId: 0,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're reusing suggestion IDs here, I would expect a uniqueness error.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the implementation of getSuggestions of the visualization, not the datasource. These ids are referencing the datatable used for this visualization suggestion. A single visualization can provide multiple suggestions based on a single datatable, this is not an error. Because this concept is pretty misleading, I changed the naming (see your other comment down below)

score: 0.8,
state: {},
title: 'Suggestion1',
},
],
},
testVis2: {
...mockVisualization,
getSuggestions: () => [
{
datasourceSuggestionId: 0,
score: 0.4,
state: {},
title: 'Suggestion4',
},
{
datasourceSuggestionId: 0,
score: 0.45,
state: {},
title: 'Suggestion3',
},
],
},
}}
datasourceMap={{
testDatasource: {
...mockDatasource,
getDatasourceSuggestionsFromCurrentState: () => [{ state: {}, tableColumns: [] }],
},
}}
initialDatasourceId="testDatasource"
initialVisualizationId="testVis"
/>
);

await waitForPromises();

// TODO why is this necessary?
instance.update();
const suggestions = instance.find('[data-test-subj="suggestion"]');
expect(suggestions.map(el => el.text())).toEqual([
'Suggestion1',
'Suggestion2',
'Suggestion3',
'Suggestion4',
]);
});

it('should switch to suggested visualization', async () => {
const newDatasourceState = {};
const suggestionVisState = {};
const instance = mount(
<EditorFrame
visualizationMap={{
testVis: {
...mockVisualization,
getSuggestions: () => [
{
datasourceSuggestionId: 0,
score: 0.8,
state: suggestionVisState,
title: 'Suggestion1',
},
],
},
testVis2: mockVisualization2,
}}
datasourceMap={{
testDatasource: {
...mockDatasource,
getDatasourceSuggestionsFromCurrentState: () => [
{ state: newDatasourceState, tableColumns: [] },
],
},
}}
initialDatasourceId="testDatasource"
initialVisualizationId="testVis2"
/>
);

await waitForPromises();

// TODO why is this necessary?
instance.update();

act(() => {
instance.find('[data-test-subj="suggestion"]').simulate('click');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would expect a suggestion click to happen inside a modal, is that what this is doing? If not in a modal, what's this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the bottom right suggestions which are always displayed.
Untitled drawing

The modal makes sense when the user drops a field in the workspace. I didn't add the modal yet, atm it is always picking the first suggestion (see https://github.com/elastic/kibana/pull/36513/files#diff-3bc17690d1f0177c324498f168ff529dR63 )

});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does this need an act() call? I thought enzyme was smart enough to know how to trigger these changes with simulate

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should, but it currently doesn't - this is probably fixed when we upgrade enzyme and enzyme react adapter (should also get rid of the "you did something outside of act" error which get logged to console while running the tests currently)


expect(mockVisualization.renderConfigPanel).toHaveBeenCalledTimes(1);
expect(mockVisualization.renderConfigPanel).toHaveBeenCalledWith(
expect.any(Element),
expect.objectContaining({
state: suggestionVisState,
})
);
expect(mockDatasource.renderDataPanel).toHaveBeenLastCalledWith(
expect.any(Element),
expect.objectContaining({
state: newDatasourceState,
})
);
});

it('should switch to best suggested visualization on field drop', async () => {
const suggestionVisState = {};
const instance = mount(
<EditorFrame
visualizationMap={{
testVis: {
...mockVisualization,
getSuggestions: () => [
{
datasourceSuggestionId: 0,
score: 0.2,
state: {},
title: 'Suggestion1',
},
{
datasourceSuggestionId: 0,
score: 0.8,
state: suggestionVisState,
title: 'Suggestion2',
},
],
},
testVis2: mockVisualization2,
}}
datasourceMap={{
testDatasource: {
...mockDatasource,
getDatasourceSuggestionsForField: () => [{ state: {}, tableColumns: [] }],
getDatasourceSuggestionsFromCurrentState: () => [{ state: {}, tableColumns: [] }],
},
}}
initialDatasourceId="testDatasource"
initialVisualizationId="testVis"
/>
);

await waitForPromises();

// TODO why is this necessary?
instance.update();

act(() => {
instance.find('[data-test-subj="lnsDragDrop"]').simulate('drop');
});

expect(mockVisualization.renderConfigPanel).toHaveBeenCalledWith(
expect.any(Element),
expect.objectContaining({
state: suggestionVisState,
})
);
});

it('should switch to best suggested visualization regardless extension on field drop', async () => {
const suggestionVisState = {};
const instance = mount(
<EditorFrame
visualizationMap={{
testVis: {
...mockVisualization,
getSuggestions: () => [
{
datasourceSuggestionId: 0,
score: 0.2,
state: {},
title: 'Suggestion1',
},
{
datasourceSuggestionId: 0,
score: 0.6,
state: {},
title: 'Suggestion2',
},
],
},
testVis2: {
...mockVisualization2,
getSuggestions: () => [
{
datasourceSuggestionId: 0,
score: 0.8,
state: suggestionVisState,
title: 'Suggestion3',
},
],
},
}}
datasourceMap={{
testDatasource: {
...mockDatasource,
getDatasourceSuggestionsForField: () => [{ state: {}, tableColumns: [] }],
getDatasourceSuggestionsFromCurrentState: () => [{ state: {}, tableColumns: [] }],
},
}}
initialDatasourceId="testDatasource"
initialVisualizationId="testVis"
/>
);

await waitForPromises();

// TODO why is this necessary?
instance.update();

act(() => {
instance.find('[data-test-subj="lnsDragDrop"]').simulate('drop');
});

expect(mockVisualization2.renderConfigPanel).toHaveBeenCalledWith(
expect.any(Element),
expect.objectContaining({
state: suggestionVisState,
})
);
});
});
});
Loading