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

Explore underlying data #68496

Merged
merged 31 commits into from
Jun 17, 2020
Merged
Show file tree
Hide file tree
Changes from 30 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
0d41009
feat: 🎸 stub discover_enhanced plugin
streamich Apr 16, 2020
fefceff
feat: 🎸 improve view in discover action
streamich Apr 16, 2020
bfb7ad0
feat: 🎸 add URL generator to "View in Discover" action
streamich Apr 30, 2020
cc1a89f
feat: 🎸 implement navigation and getHref in view raw logs actio
streamich Apr 30, 2020
4800348
fix: 🐛 disable action in "edit" mode
streamich Apr 30, 2020
f4788c0
refactor: 💡 renamce context menu view in discover action
streamich Apr 30, 2020
3f63154
feat: 🎸 rename action to "explore data"
streamich Jun 8, 2020
008a26c
fix: 🐛 correctly generate action path
streamich Jun 8, 2020
e37b515
feat: 🎸 add internationalization to "explore action"
streamich Jun 8, 2020
9fd7dea
Merge remote-tracking branch 'upstream/master' into explore-underlyin…
streamich Jun 9, 2020
0b78149
Merge remote-tracking branch 'upstream/master' into explore-underlyin…
streamich Jun 10, 2020
9852115
fix: 🐛 correctly parse generated Discover URL path
streamich Jun 10, 2020
787c5d0
test: 💍 setup basic functional tests
streamich Jun 10, 2020
ea402fc
refactor: 💡 modularize url generation logic
streamich Jun 11, 2020
51884c9
Merge remote-tracking branch 'upstream/master' into explore-underlyin…
streamich Jun 11, 2020
a7dc770
Merge remote-tracking branch 'upstream/master' into explore-underlyin…
streamich Jun 11, 2020
76812f0
feat: 🎸 export CommonlyUsed type
streamich Jun 12, 2020
39a9925
test: 💍 add test subjects to panel custom time range modal
streamich Jun 12, 2020
595998f
test: 💍 add index patterna and time range functional tests
streamich Jun 12, 2020
ad8015f
Merge remote-tracking branch 'upstream/master' into explore-underlyin…
streamich Jun 12, 2020
97fde72
refactor: 💡 rename action file
streamich Jun 12, 2020
783a78a
refactor: 💡 use URL generator from Discover plugin's contract
streamich Jun 12, 2020
5bddd7e
test: 💍 add "Explore raw data" action unit tests
streamich Jun 12, 2020
ce45079
fix: 🐛 import share plugin to check if it is enabled
streamich Jun 12, 2020
e801fcf
Merge branch 'master' into explore-underlying-data
elasticmachine Jun 15, 2020
3216757
Update x-pack/plugins/discover_enhanced/public/actions/view_in_discov…
streamich Jun 15, 2020
8eb1c2c
Merge remote-tracking branch 'upstream/master' into explore-underlyin…
streamich Jun 16, 2020
a69b5f1
Merge remote-tracking branch 'origin/explore-underlying-data' into ex…
streamich Jun 16, 2020
a6098b0
chore: 🤖 add discover_enhanced to KibanaApp codeowners
streamich Jun 16, 2020
e928068
test: 💍 improve "Explore underlying data" functional tests
streamich Jun 16, 2020
691b776
test: 💍 improve <a> link assertion
streamich Jun 16, 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
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

# App
/x-pack/plugins/dashboard_enhanced/ @elastic/kibana-app
/x-pack/plugins/discover_enhanced/ @elastic/kibana-app
/x-pack/plugins/lens/ @elastic/kibana-app
/x-pack/plugins/graph/ @elastic/kibana-app
/src/legacy/core_plugins/kibana/public/local_application_service/ @elastic/kibana-app
Expand Down
17 changes: 17 additions & 0 deletions test/functional/page_objects/discover_page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,23 @@ export function DiscoverPageProvider({ getService, getPageObjects }: FtrProvider
const nr = await el.getAttribute('data-fetch-counter');
return Number(nr);
}

/**
* Check if Discover app is currently rendered on the screen.
*/
public async isDiscoverAppOnScreen(): Promise<boolean> {
const result = await find.allByCssSelector('discover-app');
return result.length === 1;
}

/**
* Wait until Discover app is rendered on the screen.
*/
public async waitForDiscoverAppOnScreen() {
await retry.waitFor('Discover app on screen', async () => {
return await this.isDiscoverAppOnScreen();
});
}
}

return new DiscoverPage();
Expand Down
30 changes: 18 additions & 12 deletions test/functional/page_objects/time_picker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,18 @@ import moment from 'moment';
import { FtrProviderContext } from '../ftr_provider_context.d';
import { WebElementWrapper } from '../services/lib/web_element_wrapper';

export type CommonlyUsed =
| 'Today'
| 'This_week'
| 'Last_15 minutes'
| 'Last_30 minutes'
| 'Last_1 hour'
| 'Last_24 hours'
| 'Last_7 days'
| 'Last_30 days'
| 'Last_90 days'
| 'Last_1 year';

export function TimePickerProvider({ getService, getPageObjects }: FtrProviderContext) {
const log = getService('log');
const retry = getService('retry');
Expand All @@ -30,18 +42,6 @@ export function TimePickerProvider({ getService, getPageObjects }: FtrProviderCo
const { header, common } = getPageObjects(['header', 'common']);
const kibanaServer = getService('kibanaServer');

type CommonlyUsed =
| 'Today'
| 'This_week'
| 'Last_15 minutes'
| 'Last_30 minutes'
| 'Last_1 hour'
| 'Last_24 hours'
| 'Last_7 days'
| 'Last_30 days'
| 'Last_90 days'
| 'Last_1 year';

class TimePicker {
defaultStartTime = 'Sep 19, 2015 @ 06:31:44.000';
defaultEndTime = 'Sep 23, 2015 @ 18:31:44.000';
Expand Down Expand Up @@ -227,6 +227,12 @@ export function TimePickerProvider({ getService, getPageObjects }: FtrProviderCo
};
}

public async getShowDatesButtonText() {
const button = await testSubjects.find('superDatePickerShowDatesButton');
const text = await button.getVisibleText();
return text;
}

public async getTimeDurationForSharing() {
return await testSubjects.getAttribute(
'dataSharedTimefilterDuration',
Expand Down
1 change: 1 addition & 0 deletions x-pack/.i18nrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"xpack.beatsManagement": ["legacy/plugins/beats_management", "plugins/beats_management"],
"xpack.canvas": "plugins/canvas",
"xpack.dashboard": "plugins/dashboard_enhanced",
"xpack.discover": "plugins/discover_enhanced",
"xpack.crossClusterReplication": "plugins/cross_cluster_replication",
"xpack.dashboardMode": "legacy/plugins/dashboard_mode",
"xpack.data": "plugins/data_enhanced",
Expand Down
9 changes: 9 additions & 0 deletions x-pack/plugins/discover_enhanced/kibana.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"id": "discoverEnhanced",
"version": "8.0.0",
"kibanaVersion": "kibana",
"server": false,
"ui": true,
"requiredPlugins": ["uiActions", "embeddable", "discover"],
"optionalPlugins": ["share"]
}
streamich marked this conversation as resolved.
Show resolved Hide resolved
7 changes: 7 additions & 0 deletions x-pack/plugins/discover_enhanced/public/actions/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

export * from './view_in_discover';
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import {
ExploreDataContextMenuAction,
ACTION_EXPLORE_DATA,
Params,
PluginDeps,
} from './explore_data_context_menu_action';
import { coreMock } from '../../../../../../src/core/public/mocks';
import { UrlGeneratorContract } from '../../../../../../src/plugins/share/public';
import { i18n } from '@kbn/i18n';
import {
VisualizeEmbeddableContract,
VISUALIZE_EMBEDDABLE_TYPE,
} from '../../../../../../src/plugins/visualizations/public';
import { ViewMode } from '../../../../../../src/plugins/embeddable/public';

const i18nTranslateSpy = (i18n.translate as unknown) as jest.SpyInstance;

jest.mock('@kbn/i18n', () => ({
i18n: {
translate: jest.fn((key, options) => options.defaultMessage),
},
}));

afterEach(() => {
i18nTranslateSpy.mockClear();
});

const setup = () => {
type UrlGenerator = UrlGeneratorContract<'DISCOVER_APP_URL_GENERATOR'>;

const core = coreMock.createStart();

const urlGenerator: UrlGenerator = ({
id: ACTION_EXPLORE_DATA,
createUrl: jest.fn(() => Promise.resolve('/xyz/app/discover/foo#bar')),
} as unknown) as UrlGenerator;

const plugins: PluginDeps = {
discover: {
urlGenerator,
},
};

const params: Params = {
start: () => ({
plugins,
self: {},
core,
}),
};
const action = new ExploreDataContextMenuAction(params);

const input = {
viewMode: ViewMode.VIEW,
};

const output = {
indexPatterns: [
{
id: 'index-ptr-foo',
},
],
};

const embeddable: VisualizeEmbeddableContract = ({
type: VISUALIZE_EMBEDDABLE_TYPE,
getInput: () => input,
getOutput: () => output,
} as unknown) as VisualizeEmbeddableContract;

const context = {
embeddable,
};

return { core, plugins, urlGenerator, params, action, input, output, embeddable, context };
};

describe('"Explore underlying data" panel action', () => {
test('action has Discover icon', () => {
const { action } = setup();
expect(action.getIconType()).toBe('discoverApp');
});

test('title is "Explore underlying data"', () => {
const { action } = setup();
expect(action.getDisplayName()).toBe('Explore underlying data');
});

test('translates title', () => {
expect(i18nTranslateSpy).toHaveBeenCalledTimes(0);

setup().action.getDisplayName();

expect(i18nTranslateSpy).toHaveBeenCalledTimes(1);
expect(i18nTranslateSpy.mock.calls[0][0]).toBe(
'xpack.discover.FlyoutCreateDrilldownAction.displayName'
);
});

describe('isCompatible()', () => {
test('returns true when all conditions are met', async () => {
const { action, context } = setup();

const isCompatible = await action.isCompatible(context);

expect(isCompatible).toBe(true);
});

test('returns false when URL generator is not present', async () => {
const { action, plugins, context } = setup();
(plugins.discover as any).urlGenerator = undefined;

const isCompatible = await action.isCompatible(context);

expect(isCompatible).toBe(false);
});

test('returns false if embeddable is not Visualize embeddable', async () => {
const { action, embeddable, context } = setup();
(embeddable as any).type = 'NOT_VISUALIZE_EMBEDDABLE';

const isCompatible = await action.isCompatible(context);

expect(isCompatible).toBe(false);
});

test('returns false if embeddable does not have index patterns', async () => {
const { action, output, context } = setup();
delete output.indexPatterns;

const isCompatible = await action.isCompatible(context);

expect(isCompatible).toBe(false);
});

test('returns false if embeddable index patterns are empty', async () => {
const { action, output, context } = setup();
output.indexPatterns = [];

const isCompatible = await action.isCompatible(context);

expect(isCompatible).toBe(false);
});

test('returns false if dashboard is in edit mode', async () => {
const { action, input, context } = setup();
input.viewMode = ViewMode.EDIT;

const isCompatible = await action.isCompatible(context);

expect(isCompatible).toBe(false);
});
});

describe('getHref()', () => {
test('returns URL path generated by URL generator', async () => {
const { action, context } = setup();

const href = await action.getHref(context);

expect(href).toBe('/xyz/app/discover/foo#bar');
});

test('calls URL generator with right arguments', async () => {
const { action, urlGenerator, context } = setup();

expect(urlGenerator.createUrl).toHaveBeenCalledTimes(0);

await action.getHref(context);

expect(urlGenerator.createUrl).toHaveBeenCalledTimes(1);
expect(urlGenerator.createUrl).toHaveBeenCalledWith({
indexPatternId: 'index-ptr-foo',
});
});
});

describe('execute()', () => {
test('calls platform SPA navigation method', async () => {
const { action, context, core } = setup();

expect(core.application.navigateToApp).toHaveBeenCalledTimes(0);

await action.execute(context);

expect(core.application.navigateToApp).toHaveBeenCalledTimes(1);
});

test('calls platform SPA navigation method with right arguments', async () => {
const { action, context, core } = setup();

await action.execute(context);

expect(core.application.navigateToApp).toHaveBeenCalledTimes(1);
expect(core.application.navigateToApp.mock.calls[0]).toEqual([
'discover',
{
path: '/foo#bar',
},
]);
});
});
});
Loading