[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:
Quynh Nguyen 2021-11-05 16:47:06 -05:00 committed by GitHub
parent 735a286fac
commit 6fe005268a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 222 additions and 50 deletions

View file

@ -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

View file

@ -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'));

View file

@ -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 () {

View file

@ -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);
});
}

View file

@ -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);

View file

@ -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: [
{

View file

@ -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[];

View file

@ -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);
});
},
};
}

View file

@ -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
);
}
);
},
};
}

View file

@ -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'],

View file

@ -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);
},
};
}