mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[ML] Add functional tests for Field statistics embeddable in Dashboard, check for filters (#116774)
* [ML] Add functional tests for dashboard embeddable, filters * [ML] Fix permissions * [ML] Update tests to use bulk api * [ML] Change to constants * [ML] Fix savedSearchTitle Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
735a286fac
commit
6fe005268a
11 changed files with 222 additions and 50 deletions
|
@ -48,7 +48,7 @@ export class DiscoverPageObject extends FtrService {
|
|||
await fieldSearch.clearValue();
|
||||
}
|
||||
|
||||
public async saveSearch(searchName: string) {
|
||||
public async saveSearch(searchName: string, saveAsNew?: boolean) {
|
||||
await this.clickSaveSearchButton();
|
||||
// preventing an occasional flakiness when the saved object wasn't set and the form can't be submitted
|
||||
await this.retry.waitFor(
|
||||
|
@ -59,6 +59,14 @@ export class DiscoverPageObject extends FtrService {
|
|||
return (await saveButton.getAttribute('disabled')) !== 'true';
|
||||
}
|
||||
);
|
||||
|
||||
if (saveAsNew !== undefined) {
|
||||
await this.retry.waitFor(`save as new switch is set`, async () => {
|
||||
await this.testSubjects.setEuiSwitch('saveAsNewCheckbox', saveAsNew ? 'check' : 'uncheck');
|
||||
return (await this.testSubjects.isEuiSwitchChecked('saveAsNewCheckbox')) === saveAsNew;
|
||||
});
|
||||
}
|
||||
|
||||
await this.testSubjects.click('confirmSaveSavedObjectButton');
|
||||
await this.header.waitUntilLoadingHasFinished();
|
||||
// LeeDr - this additional checking for the saved search name was an attempt
|
||||
|
|
|
@ -13,6 +13,7 @@ export default function ({ loadTestFile }: FtrProviderContext) {
|
|||
|
||||
loadTestFile(require.resolve('./index_data_visualizer'));
|
||||
loadTestFile(require.resolve('./index_data_visualizer_grid_in_discover'));
|
||||
loadTestFile(require.resolve('./index_data_visualizer_grid_in_dashboard'));
|
||||
loadTestFile(require.resolve('./index_data_visualizer_actions_panel'));
|
||||
loadTestFile(require.resolve('./index_data_visualizer_index_pattern_management'));
|
||||
loadTestFile(require.resolve('./file_data_visualizer'));
|
||||
|
|
|
@ -9,6 +9,7 @@ import { FtrProviderContext } from '../../../ftr_provider_context';
|
|||
import { TestData, MetricFieldVisConfig } from './types';
|
||||
import {
|
||||
farequoteDataViewTestData,
|
||||
farequoteKQLFiltersSearchTestData,
|
||||
farequoteKQLSearchTestData,
|
||||
farequoteLuceneSearchTestData,
|
||||
sampleLogTestData,
|
||||
|
@ -76,6 +77,13 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
);
|
||||
await ml.dataVisualizerIndexBased.assertTotalFieldsCount(testData.expected.totalFieldsCount);
|
||||
|
||||
if (testData.expected.filters) {
|
||||
await ml.testExecution.logTestStep('displays filters in filter bar correctly');
|
||||
for (const filter of testData.expected.filters!) {
|
||||
await ml.dataVisualizerIndexBased.assertFilterBarFilterContent(filter);
|
||||
}
|
||||
}
|
||||
|
||||
await ml.testExecution.logTestStep(
|
||||
'displays details for metric fields and non-metric fields correctly'
|
||||
);
|
||||
|
@ -96,7 +104,9 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
fieldRow.fieldName!,
|
||||
fieldRow.docCountFormatted,
|
||||
fieldRow.exampleCount,
|
||||
fieldRow.viewableInLens
|
||||
fieldRow.viewableInLens,
|
||||
false,
|
||||
fieldRow.exampleContent
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -150,6 +160,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
await ml.testResources.createIndexPatternIfNeeded('ft_module_sample_logs', '@timestamp');
|
||||
await ml.testResources.createSavedSearchFarequoteLuceneIfNeeded();
|
||||
await ml.testResources.createSavedSearchFarequoteKueryIfNeeded();
|
||||
await ml.testResources.createSavedSearchFarequoteFilterAndKueryIfNeeded();
|
||||
await ml.testResources.setKibanaTimeZoneToUTC();
|
||||
|
||||
await ml.securityUI.loginAsMlPowerUser();
|
||||
|
@ -182,6 +193,14 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
});
|
||||
|
||||
runTests(farequoteLuceneSearchTestData);
|
||||
|
||||
it(`${farequoteKQLFiltersSearchTestData.suiteTitle} loads the data visualizer selector page`, async () => {
|
||||
// Start navigation from the base of the ML app.
|
||||
await ml.navigation.navigateToMl();
|
||||
await ml.navigation.navigateToDataVisualizer();
|
||||
});
|
||||
|
||||
runTests(farequoteKQLFiltersSearchTestData);
|
||||
});
|
||||
|
||||
describe('with module_sample_logs ', function () {
|
||||
|
|
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { FtrProviderContext } from '../../../ftr_provider_context';
|
||||
import { TestData, MetricFieldVisConfig } from './types';
|
||||
import { farequoteLuceneFiltersSearchTestData } from './index_test_data';
|
||||
|
||||
const SHOW_FIELD_STATISTICS = 'discover:showFieldStatistics';
|
||||
export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
||||
const esArchiver = getService('esArchiver');
|
||||
const PageObjects = getPageObjects(['common', 'discover', 'timePicker', 'settings', 'dashboard']);
|
||||
const ml = getService('ml');
|
||||
const retry = getService('retry');
|
||||
const dashboardAddPanel = getService('dashboardAddPanel');
|
||||
|
||||
function runTests(testData: TestData) {
|
||||
const savedSearchTitle = `Field stats for ${testData.suiteTitle} ${Date.now()}`;
|
||||
const dashboardTitle = `Dashboard for ${testData.suiteTitle} ${Date.now()}`;
|
||||
const startTime = 'Jan 1, 2016 @ 00:00:00.000';
|
||||
const endTime = 'Nov 1, 2020 @ 00:00:00.000';
|
||||
|
||||
describe(`with ${testData.suiteTitle}`, function () {
|
||||
after(async function () {
|
||||
await ml.testResources.deleteSavedSearchByTitle(savedSearchTitle);
|
||||
await ml.testResources.deleteDashboardByTitle(dashboardTitle);
|
||||
});
|
||||
|
||||
it(`saves search with Field statistics table in Discover`, async function () {
|
||||
await ml.testResources.setAdvancedSettingProperty(SHOW_FIELD_STATISTICS, true);
|
||||
|
||||
await PageObjects.common.navigateToApp('discover');
|
||||
if (testData.isSavedSearch) {
|
||||
await retry.tryForTime(2 * 1000, async () => {
|
||||
await PageObjects.discover.loadSavedSearch(testData.sourceIndexOrSavedSearch);
|
||||
});
|
||||
} else {
|
||||
await ml.dashboardEmbeddables.selectDiscoverIndexPattern(
|
||||
testData.sourceIndexOrSavedSearch
|
||||
);
|
||||
}
|
||||
await PageObjects.timePicker.setAbsoluteRange(startTime, endTime);
|
||||
|
||||
await PageObjects.discover.assertViewModeToggleExists();
|
||||
await PageObjects.discover.clickViewModeFieldStatsButton();
|
||||
await ml.testExecution.logTestStep('saves as new search');
|
||||
await PageObjects.discover.saveSearch(savedSearchTitle, true);
|
||||
});
|
||||
|
||||
it(`displays Field statistics table in Dashboard when enabled`, async function () {
|
||||
await PageObjects.common.navigateToApp('dashboard');
|
||||
await PageObjects.dashboard.gotoDashboardLandingPage();
|
||||
await PageObjects.dashboard.clickNewDashboard();
|
||||
await dashboardAddPanel.addSavedSearch(savedSearchTitle);
|
||||
await PageObjects.dashboard.waitForRenderComplete();
|
||||
|
||||
await PageObjects.timePicker.setAbsoluteRange(startTime, endTime);
|
||||
await PageObjects.dashboard.waitForRenderComplete();
|
||||
|
||||
for (const fieldRow of testData.expected.metricFields as Array<
|
||||
Required<MetricFieldVisConfig>
|
||||
>) {
|
||||
await ml.dataVisualizerTable.assertNumberFieldContents(
|
||||
fieldRow.fieldName,
|
||||
fieldRow.docCountFormatted,
|
||||
fieldRow.topValuesCount,
|
||||
fieldRow.viewableInLens
|
||||
);
|
||||
}
|
||||
|
||||
for (const fieldRow of testData.expected.nonMetricFields!) {
|
||||
await ml.dataVisualizerTable.assertNonMetricFieldContents(
|
||||
fieldRow.type,
|
||||
fieldRow.fieldName!,
|
||||
fieldRow.docCountFormatted,
|
||||
fieldRow.exampleCount,
|
||||
fieldRow.viewableInLens,
|
||||
false,
|
||||
fieldRow.exampleContent
|
||||
);
|
||||
}
|
||||
|
||||
await PageObjects.dashboard.saveDashboard(dashboardTitle);
|
||||
});
|
||||
|
||||
it(`doesn't display Field statistics table in Dashboard when disabled`, async function () {
|
||||
await ml.testResources.setAdvancedSettingProperty(SHOW_FIELD_STATISTICS, false);
|
||||
|
||||
await PageObjects.common.navigateToApp('dashboard');
|
||||
await PageObjects.dashboard.gotoDashboardEditMode(dashboardTitle);
|
||||
await PageObjects.dashboard.waitForRenderComplete();
|
||||
|
||||
await dashboardAddPanel.addSavedSearch(savedSearchTitle);
|
||||
await PageObjects.dashboard.waitForRenderComplete();
|
||||
|
||||
await PageObjects.timePicker.setAbsoluteRange(startTime, endTime);
|
||||
await PageObjects.dashboard.waitForRenderComplete();
|
||||
|
||||
await PageObjects.discover.assertFieldStatsTableNotExists();
|
||||
await PageObjects.dashboard.saveDashboard(dashboardTitle);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
describe('field statistics in Dashboard', function () {
|
||||
before(async function () {
|
||||
await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote');
|
||||
await ml.testResources.createIndexPatternIfNeeded('ft_farequote', '@timestamp');
|
||||
await ml.testResources.createSavedSearchFarequoteFilterAndLuceneIfNeeded();
|
||||
await ml.securityUI.loginAsMlPowerUser();
|
||||
});
|
||||
|
||||
after(async function () {
|
||||
await ml.testResources.clearAdvancedSettingProperty(SHOW_FIELD_STATISTICS);
|
||||
});
|
||||
|
||||
runTests(farequoteLuceneFiltersSearchTestData);
|
||||
});
|
||||
}
|
|
@ -5,7 +5,6 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import expect from '@kbn/expect';
|
||||
import { FtrProviderContext } from '../../../ftr_provider_context';
|
||||
import { TestData, MetricFieldVisConfig } from './types';
|
||||
|
||||
|
@ -22,39 +21,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
const esArchiver = getService('esArchiver');
|
||||
const PageObjects = getPageObjects(['common', 'discover', 'timePicker', 'settings']);
|
||||
const ml = getService('ml');
|
||||
const testSubjects = getService('testSubjects');
|
||||
const retry = getService('retry');
|
||||
const toasts = getService('toasts');
|
||||
|
||||
const selectIndexPattern = async (indexPattern: string) => {
|
||||
await retry.tryForTime(2 * 1000, async () => {
|
||||
await PageObjects.discover.selectIndexPattern(indexPattern);
|
||||
const indexPatternTitle = await testSubjects.getVisibleText('indexPattern-switch-link');
|
||||
expect(indexPatternTitle).to.be(indexPattern);
|
||||
});
|
||||
};
|
||||
|
||||
const clearAdvancedSetting = async (propertyName: string) => {
|
||||
await retry.tryForTime(2 * 1000, async () => {
|
||||
await PageObjects.common.navigateToUrl('management', 'kibana/settings', {
|
||||
shouldUseHashForSubUrl: false,
|
||||
});
|
||||
if ((await PageObjects.settings.getAdvancedSettingCheckbox(propertyName)) === 'true') {
|
||||
await PageObjects.settings.clearAdvancedSettings(propertyName);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const setAdvancedSettingCheckbox = async (propertyName: string, checkedState: boolean) => {
|
||||
await retry.tryForTime(2 * 1000, async () => {
|
||||
await PageObjects.common.navigateToUrl('management', 'kibana/settings', {
|
||||
shouldUseHashForSubUrl: false,
|
||||
});
|
||||
await testSubjects.click('settings');
|
||||
await toasts.dismissAllToasts();
|
||||
await PageObjects.settings.toggleAdvancedSettingCheckbox(propertyName, checkedState);
|
||||
});
|
||||
};
|
||||
const startTime = 'Jan 1, 2016 @ 00:00:00.000';
|
||||
const endTime = 'Nov 1, 2020 @ 00:00:00.000';
|
||||
|
||||
function runTestsWhenDisabled(testData: TestData) {
|
||||
it('should not show view mode toggle or Field stats table', async function () {
|
||||
|
@ -64,13 +34,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
await PageObjects.discover.loadSavedSearch(testData.sourceIndexOrSavedSearch);
|
||||
});
|
||||
} else {
|
||||
await selectIndexPattern(testData.sourceIndexOrSavedSearch);
|
||||
await ml.dashboardEmbeddables.selectDiscoverIndexPattern(testData.sourceIndexOrSavedSearch);
|
||||
}
|
||||
|
||||
await PageObjects.timePicker.setAbsoluteRange(
|
||||
'Jan 1, 2016 @ 00:00:00.000',
|
||||
'Nov 1, 2020 @ 00:00:00.000'
|
||||
);
|
||||
await PageObjects.timePicker.setAbsoluteRange(startTime, endTime);
|
||||
|
||||
await PageObjects.discover.assertViewModeToggleNotExists();
|
||||
await PageObjects.discover.assertFieldStatsTableNotExists();
|
||||
|
@ -86,12 +53,11 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
await PageObjects.discover.loadSavedSearch(testData.sourceIndexOrSavedSearch);
|
||||
});
|
||||
} else {
|
||||
await selectIndexPattern(testData.sourceIndexOrSavedSearch);
|
||||
await ml.dashboardEmbeddables.selectDiscoverIndexPattern(
|
||||
testData.sourceIndexOrSavedSearch
|
||||
);
|
||||
}
|
||||
await PageObjects.timePicker.setAbsoluteRange(
|
||||
'Jan 1, 2016 @ 00:00:00.000',
|
||||
'Nov 1, 2020 @ 00:00:00.000'
|
||||
);
|
||||
await PageObjects.timePicker.setAbsoluteRange(startTime, endTime);
|
||||
|
||||
await PageObjects.discover.assertHitCount(testData.expected.totalDocCountFormatted);
|
||||
await PageObjects.discover.assertViewModeToggleExists();
|
||||
|
@ -140,16 +106,16 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
});
|
||||
|
||||
after(async function () {
|
||||
await clearAdvancedSetting(SHOW_FIELD_STATISTICS);
|
||||
await ml.testResources.clearAdvancedSettingProperty(SHOW_FIELD_STATISTICS);
|
||||
});
|
||||
|
||||
describe('when enabled', function () {
|
||||
before(async function () {
|
||||
await setAdvancedSettingCheckbox(SHOW_FIELD_STATISTICS, true);
|
||||
await ml.testResources.setAdvancedSettingProperty(SHOW_FIELD_STATISTICS, true);
|
||||
});
|
||||
|
||||
after(async function () {
|
||||
await clearAdvancedSetting(SHOW_FIELD_STATISTICS);
|
||||
await ml.testResources.clearAdvancedSettingProperty(SHOW_FIELD_STATISTICS);
|
||||
});
|
||||
|
||||
runTests(farequoteDataViewTestData);
|
||||
|
@ -163,7 +129,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
describe('when disabled', function () {
|
||||
before(async function () {
|
||||
// Ensure that the setting is set to default state which is false
|
||||
await setAdvancedSettingCheckbox(SHOW_FIELD_STATISTICS, false);
|
||||
await ml.testResources.setAdvancedSettingProperty(SHOW_FIELD_STATISTICS, false);
|
||||
});
|
||||
|
||||
runTestsWhenDisabled(farequoteDataViewTestData);
|
||||
|
|
|
@ -213,6 +213,7 @@ export const farequoteKQLFiltersSearchTestData: TestData = {
|
|||
{ size: 5000, expected: { field: '@timestamp', docCountFormatted: '5000 (100%)' } },
|
||||
],
|
||||
expected: {
|
||||
filters: [{ key: 'airline', value: 'ASA' }],
|
||||
totalDocCountFormatted: '5,674',
|
||||
metricFields: [
|
||||
{
|
||||
|
@ -408,6 +409,7 @@ export const farequoteLuceneFiltersSearchTestData: TestData = {
|
|||
{ size: 5000, expected: { field: '@timestamp', docCountFormatted: '5000 (100%)' } },
|
||||
],
|
||||
expected: {
|
||||
filters: [{ key: 'airline', value: 'ASA' }],
|
||||
totalDocCountFormatted: '5,673',
|
||||
metricFields: [
|
||||
{
|
||||
|
|
|
@ -33,6 +33,13 @@ export interface TestData {
|
|||
expected: { field: string; docCountFormatted: string };
|
||||
}>;
|
||||
expected: {
|
||||
filters?: Array<{
|
||||
key: string;
|
||||
value: string;
|
||||
enabled?: boolean;
|
||||
pinned?: boolean;
|
||||
negated?: boolean;
|
||||
}>;
|
||||
totalDocCountFormatted: string;
|
||||
metricFields?: MetricFieldVisConfig[];
|
||||
nonMetricFields?: NonMetricFieldVisConfig[];
|
||||
|
|
|
@ -10,13 +10,14 @@ import { FtrProviderContext } from '../../ftr_provider_context';
|
|||
import { MlDashboardJobSelectionTable } from './dashboard_job_selection_table';
|
||||
|
||||
export function MachineLearningDashboardEmbeddablesProvider(
|
||||
{ getService }: FtrProviderContext,
|
||||
{ getService, getPageObjects }: FtrProviderContext,
|
||||
mlDashboardJobSelectionTable: MlDashboardJobSelectionTable
|
||||
) {
|
||||
const retry = getService('retry');
|
||||
const testSubjects = getService('testSubjects');
|
||||
const find = getService('find');
|
||||
const dashboardAddPanel = getService('dashboardAddPanel');
|
||||
const PageObjects = getPageObjects(['discover']);
|
||||
|
||||
return {
|
||||
async assertAnomalyChartsEmbeddableInitializerExists() {
|
||||
|
@ -112,5 +113,13 @@ export function MachineLearningDashboardEmbeddablesProvider(
|
|||
await mlDashboardJobSelectionTable.assertJobSelectionTableExists();
|
||||
});
|
||||
},
|
||||
|
||||
async selectDiscoverIndexPattern(indexPattern: string) {
|
||||
await retry.tryForTime(2 * 1000, async () => {
|
||||
await PageObjects.discover.selectIndexPattern(indexPattern);
|
||||
const indexPatternTitle = await testSubjects.getVisibleText('indexPattern-switch-link');
|
||||
expect(indexPatternTitle).to.be(indexPattern);
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ export function MachineLearningDataVisualizerIndexBasedProvider({
|
|||
const retry = getService('retry');
|
||||
const PageObjects = getPageObjects(['discover']);
|
||||
const queryBar = getService('queryBar');
|
||||
const filterBar = getService('filterBar');
|
||||
|
||||
return {
|
||||
async assertTimeRangeSelectorSectionExists() {
|
||||
|
@ -208,5 +209,27 @@ export function MachineLearningDataVisualizerIndexBasedProvider({
|
|||
);
|
||||
});
|
||||
},
|
||||
|
||||
async assertFilterBarFilterContent(filter: {
|
||||
key: string;
|
||||
value: string;
|
||||
enabled?: boolean;
|
||||
pinned?: boolean;
|
||||
negated?: boolean;
|
||||
}) {
|
||||
await retry.waitForWithTimeout(
|
||||
`filter ${JSON.stringify(filter)} to exist`,
|
||||
2000,
|
||||
async () => {
|
||||
return await filterBar.hasFilter(
|
||||
filter.key,
|
||||
filter.value,
|
||||
filter.enabled,
|
||||
filter.pinned,
|
||||
filter.negated
|
||||
);
|
||||
}
|
||||
);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -144,7 +144,9 @@ export function MachineLearningSecurityCommonProvider({ getService }: FtrProvide
|
|||
// such as "View in Lens", "Add to Dashboard", and creating anomaly detection rules. These feature privileges are the minimal ones
|
||||
// necessary to satisfy all of those functional tests.
|
||||
feature: {
|
||||
discover: ['read'],
|
||||
// FIXME: We need permission to save search in Discover to test the data viz embeddable
|
||||
// change permission back to read once tests are moved out of ML
|
||||
discover: ['all'],
|
||||
visualize: ['read'],
|
||||
dashboard: ['all'],
|
||||
actions: ['all'],
|
||||
|
|
|
@ -509,5 +509,18 @@ export function MachineLearningTestResourcesProvider({ getService }: FtrProvider
|
|||
log.debug(` > found version '${packageVersion}'`);
|
||||
return packageVersion;
|
||||
},
|
||||
|
||||
async setAdvancedSettingProperty(
|
||||
propertyName: string,
|
||||
propertyValue: string | number | boolean
|
||||
) {
|
||||
await kibanaServer.uiSettings.update({
|
||||
[propertyName]: propertyValue,
|
||||
});
|
||||
},
|
||||
|
||||
async clearAdvancedSettingProperty(propertyName: string) {
|
||||
await kibanaServer.uiSettings.unset(propertyName);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue