Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 @@ -21,7 +21,7 @@ import { ReactWrapper } from 'enzyme';
const waitForPromises = () => new Promise(resolve => setTimeout(resolve));

describe('workspace_panel', () => {
let mockVisualization: Visualization;
let mockVisualization: jest.Mocked<Visualization>;
let mockDatasource: DatasourceMock;

let expressionRendererMock: jest.Mock<React.ReactElement, [ExpressionRendererProps]>;
Expand Down Expand Up @@ -274,4 +274,134 @@ Object {
expect(instance.find(expressionRendererMock).length).toBe(1);
});
});

describe('suggestions from dropping in workspace panel', () => {
let mockDispatch: jest.Mock;

beforeEach(() => {
mockDispatch = jest.fn();
instance = mount(
<WorkspacePanel
activeDatasource={mockDatasource}
datasourceState={{}}
activeVisualizationId={null}
visualizationMap={{
vis: mockVisualization,
}}
visualizationState={{}}
datasourcePublicAPI={mockDatasource.publicAPIMock}
dispatch={mockDispatch}
ExpressionRenderer={expressionRendererMock}
/>
);
});

it('should immediately transition if exactly one suggestion is returned', () => {
const expectedTable = {
datasourceSuggestionId: 0,
isMultiRow: true,
columns: [],
};
mockDatasource.getDatasourceSuggestionsForField.mockReturnValueOnce([
{
state: {},
table: expectedTable,
},
]);
mockVisualization.getSuggestions.mockReturnValueOnce([
{
score: 0.5,
title: 'my title',
state: {},
datasourceSuggestionId: 0,
},
]);

instance.childAt(0).prop('onDrop')({
name: '@timestamp',
type: 'date',
searchable: false,
aggregatable: false,
});

expect(mockDatasource.getDatasourceSuggestionsForField).toHaveBeenCalledTimes(1);
expect(mockVisualization.getSuggestions).toHaveBeenCalledWith(
expect.objectContaining({
tables: [expectedTable],
})
);
expect(mockDispatch).toHaveBeenCalledWith({
type: 'SWITCH_VISUALIZATION',
newVisualizationId: 'vis',
initialState: {},
datasourceState: {},
});
});

it('should immediately transition to the first suggestion if there are multiple', () => {
mockDatasource.getDatasourceSuggestionsForField.mockReturnValueOnce([
{
state: {},
table: {
datasourceSuggestionId: 0,
isMultiRow: true,
columns: [],
},
},
{
state: {},
table: {
datasourceSuggestionId: 1,
isMultiRow: true,
columns: [],
},
},
]);
mockVisualization.getSuggestions.mockReturnValueOnce([
{
score: 0.8,
title: 'first suggestion',
state: {
isFirst: true,
},
datasourceSuggestionId: 1,
},
{
score: 0.5,
title: 'second suggestion',
state: {},
datasourceSuggestionId: 0,
},
]);

instance.childAt(0).prop('onDrop')({
name: '@timestamp',
type: 'date',
searchable: false,
aggregatable: false,
});

expect(mockDispatch).toHaveBeenCalledWith({
type: 'SWITCH_VISUALIZATION',
newVisualizationId: 'vis',
initialState: {
isFirst: true,
},
datasourceState: {},
});
});

it("should do nothing when the visualization can't use the suggestions", () => {
instance.childAt(0).prop('onDrop')({
name: '@timestamp',
type: 'date',
searchable: false,
aggregatable: false,
});

expect(mockDatasource.getDatasourceSuggestionsForField).toHaveBeenCalledTimes(1);
expect(mockVisualization.getSuggestions).toHaveBeenCalledTimes(1);
expect(mockDispatch).not.toHaveBeenCalled();
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,10 @@ export function WorkspacePanel({
dispatch,
ExpressionRenderer: ExpressionRendererComponent,
}: WorkspacePanelProps) {
function onDrop() {
function onDrop(item: unknown) {
const datasourceSuggestions = activeDatasource.getDatasourceSuggestionsForField(
datasourceState
datasourceState,
item
);

const suggestions = getSuggestions(
Expand Down
2 changes: 1 addition & 1 deletion x-pack/plugins/lens/public/editor_frame_plugin/mocks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export function createMockDatasource(): DatasourceMock {
};

return {
getDatasourceSuggestionsForField: jest.fn(_state => []),
getDatasourceSuggestionsForField: jest.fn((_state, item) => []),
getDatasourceSuggestionsFromCurrentState: jest.fn(_state => []),
getPersistableState: jest.fn(),
getPublicAPI: jest.fn((_state, _setState) => publicAPIMock),
Expand Down
200 changes: 200 additions & 0 deletions x-pack/plugins/lens/public/indexpattern_plugin/indexpattern.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,206 @@ describe('IndexPattern Data Source', () => {
});
});

describe('#getDatasourceSuggestionsForField', () => {
describe('with no previous selections', () => {
let initialState: IndexPatternPrivateState;

beforeEach(async () => {
initialState = await indexPatternDatasource.initialize({
currentIndexPatternId: '1',
columnOrder: [],
columns: {},
});
});

it('should apply a bucketed aggregation for a string field', () => {
const suggestions = indexPatternDatasource.getDatasourceSuggestionsForField(initialState, {
name: 'source',
type: 'string',
aggregatable: true,
searchable: true,
});

expect(suggestions).toHaveLength(1);
expect(suggestions[0].state).toEqual(
expect.objectContaining({
columnOrder: ['col1', 'col2'],
columns: {
col1: expect.objectContaining({
operationType: 'terms',
sourceField: 'source',
}),
col2: expect.objectContaining({
operationType: 'count',
sourceField: 'documents',
}),
},
})
);
expect(suggestions[0].table).toEqual({
datasourceSuggestionId: 0,
isMultiRow: true,
columns: [
expect.objectContaining({
columnId: 'col1',
}),
expect.objectContaining({
columnId: 'col2',
}),
],
});
});

it('should apply a bucketed aggregation for a date field', () => {
const suggestions = indexPatternDatasource.getDatasourceSuggestionsForField(initialState, {
name: 'timestamp',
type: 'date',
aggregatable: true,
searchable: true,
});

expect(suggestions).toHaveLength(1);
expect(suggestions[0].state).toEqual(
expect.objectContaining({
columnOrder: ['col1', 'col2'],
columns: {
col1: expect.objectContaining({
operationType: 'date_histogram',
sourceField: 'timestamp',
}),
col2: expect.objectContaining({
operationType: 'count',
sourceField: 'documents',
}),
},
})
);
expect(suggestions[0].table).toEqual({
datasourceSuggestionId: 0,
isMultiRow: true,
columns: [
expect.objectContaining({
columnId: 'col1',
}),
expect.objectContaining({
columnId: 'col2',
}),
],
});
});

it('should select a metric for a number field', () => {
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: We could add a test for not having a timefield on the index

const suggestions = indexPatternDatasource.getDatasourceSuggestionsForField(initialState, {
name: 'bytes',
type: 'number',
aggregatable: true,
searchable: true,
});

expect(suggestions).toHaveLength(1);
expect(suggestions[0].state).toEqual(
expect.objectContaining({
columnOrder: ['col1', 'col2'],
columns: {
col1: expect.objectContaining({
sourceField: 'timestamp',
operationType: 'date_histogram',
}),
col2: expect.objectContaining({
sourceField: 'bytes',
operationType: 'sum',
}),
},
})
);
expect(suggestions[0].table).toEqual({
datasourceSuggestionId: 0,
isMultiRow: true,
columns: [
expect.objectContaining({
columnId: 'col1',
}),
expect.objectContaining({
columnId: 'col2',
}),
],
});
});

it('should not make any suggestions for a number without a time field', async () => {
const state: IndexPatternPrivateState = {
currentIndexPatternId: '1',
columnOrder: [],
columns: {},
indexPatterns: {
1: {
id: '1',
title: 'no timefield',
fields: [
{
name: 'bytes',
type: 'number',
aggregatable: true,
searchable: true,
},
],
},
},
};

const suggestions = indexPatternDatasource.getDatasourceSuggestionsForField(state, {
name: 'bytes',
type: 'number',
aggregatable: true,
searchable: true,
});

expect(suggestions).toHaveLength(0);
});
});

describe('with a prior column', () => {
let initialState: IndexPatternPrivateState;

beforeEach(async () => {
initialState = await indexPatternDatasource.initialize(persistedState);
});

it('should not suggest for string', () => {
expect(
indexPatternDatasource.getDatasourceSuggestionsForField(initialState, {
name: 'source',
type: 'string',
aggregatable: true,
searchable: true,
})
).toHaveLength(0);
});

it('should not suggest for date', () => {
expect(
indexPatternDatasource.getDatasourceSuggestionsForField(initialState, {
name: 'timestamp',
type: 'date',
aggregatable: true,
searchable: true,
})
).toHaveLength(0);
});

it('should not suggest for number', () => {
expect(
indexPatternDatasource.getDatasourceSuggestionsForField(initialState, {
name: 'bytes',
type: 'number',
aggregatable: true,
searchable: true,
})
).toHaveLength(0);
});
});
});

describe('#getPublicAPI', () => {
let publicAPI: DatasourcePublicAPI;

Expand Down
Loading