From c961b4b1f830169c6f76c9b06d3d4346c9771add Mon Sep 17 00:00:00 2001 From: Melissa Alvarez Date: Tue, 15 Feb 2022 12:58:11 -0700 Subject: [PATCH] [ML] Data Frame Analytics saved search creation functional tests (#125040) * create filter saved search test for each dfa job type * add search query test cases * temp comment of failing with insuff memory test case * remove problem case and update test text * Use downsampled farequote archive * remove unnecessary file Co-authored-by: Robert Oskamp --- .../classification_creation_saved_search.ts | 363 +++++++++++++++++ .../apps/ml/data_frame_analytics/index.ts | 3 + ...outlier_detection_creation_saved_search.ts | 377 ++++++++++++++++++ .../regression_creation_saved_search.ts | 333 ++++++++++++++++ x-pack/test/functional/apps/ml/index.ts | 1 + .../ml/farequote_small/data.json.gz | Bin 0 -> 63274 bytes .../ml/farequote_small/mappings.json | 48 +++ .../functional/services/ml/test_resources.ts | 34 +- .../services/ml/test_resources_data.ts | 5 - 9 files changed, 1147 insertions(+), 17 deletions(-) create mode 100644 x-pack/test/functional/apps/ml/data_frame_analytics/classification_creation_saved_search.ts create mode 100644 x-pack/test/functional/apps/ml/data_frame_analytics/outlier_detection_creation_saved_search.ts create mode 100644 x-pack/test/functional/apps/ml/data_frame_analytics/regression_creation_saved_search.ts create mode 100644 x-pack/test/functional/es_archives/ml/farequote_small/data.json.gz create mode 100644 x-pack/test/functional/es_archives/ml/farequote_small/mappings.json diff --git a/x-pack/test/functional/apps/ml/data_frame_analytics/classification_creation_saved_search.ts b/x-pack/test/functional/apps/ml/data_frame_analytics/classification_creation_saved_search.ts new file mode 100644 index 00000000000000..67550ae17a4b06 --- /dev/null +++ b/x-pack/test/functional/apps/ml/data_frame_analytics/classification_creation_saved_search.ts @@ -0,0 +1,363 @@ +/* + * 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 { AnalyticsTableRowDetails } from '../../../services/ml/data_frame_analytics_table'; +import { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function ({ getService }: FtrProviderContext) { + const esArchiver = getService('esArchiver'); + const ml = getService('ml'); + const editedDescription = 'Edited description'; + + describe('classification saved search creation', function () { + before(async () => { + await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote_small'); + await ml.testResources.createIndexPatternIfNeeded('ft_farequote_small', '@timestamp'); + await ml.testResources.createSavedSearchFarequoteLuceneIfNeeded('ft_farequote_small'); + await ml.testResources.createSavedSearchFarequoteKueryIfNeeded('ft_farequote_small'); + await ml.testResources.setKibanaTimeZoneToUTC(); + + await ml.securityUI.loginAsMlPowerUser(); + }); + + after(async () => { + await ml.api.cleanMlIndices(); + await ml.testResources.deleteSavedSearches(); + await ml.testResources.deleteIndexPatternByTitle('ft_farequote_small'); + }); + + const dateNow = Date.now(); + const testDataList = [ + { + suiteTitle: 'with lucene query', + jobType: 'classification', + jobId: `fq_saved_search_2_${dateNow}`, + jobDescription: 'Classification job based on a saved search with lucene query', + source: 'ft_farequote_lucene', + get destinationIndex(): string { + return `user-${this.jobId}`; + }, + runtimeFields: { + uppercase_airline: { + type: 'keyword', + script: 'emit(params._source.airline.toUpperCase())', + }, + }, + dependentVariable: 'airline', + trainingPercent: 20, + modelMemory: '20mb', + createIndexPattern: true, + expected: { + source: 'ft_farequote_small', + rocCurveColorState: [ + // tick/grid/axis + { color: '#DDDDDD', percentage: 38 }, + // line + { color: '#98A2B3', percentage: 7 }, + ], + runtimeFieldsEditorContent: ['{', ' "uppercase_airline": {', ' "type": "keyword",'], + row: { + memoryStatus: 'ok', + type: 'classification', + status: 'stopped', + progress: '100', + }, + rowDetails: { + jobDetails: [ + { + section: 'state', + expectedEntries: { + id: `fq_saved_search_2_${dateNow}`, + state: 'stopped', + data_counts: + '{"training_docs_count":320,"test_docs_count":1284,"skipped_docs_count":0}', + description: 'Classification job based on a saved search with lucene query', + }, + }, + { section: 'progress', expectedEntries: { Phase: '8/8' } }, + ], + } as AnalyticsTableRowDetails, + }, + }, + { + suiteTitle: 'with kuery query', + jobType: 'classification', + jobId: `fq_saved_search_3_${dateNow}`, + jobDescription: 'Classification job based on a saved search with kuery query', + source: 'ft_farequote_kuery', + get destinationIndex(): string { + return `user-${this.jobId}`; + }, + runtimeFields: { + uppercase_airline: { + type: 'keyword', + script: 'emit(params._source.airline.toUpperCase())', + }, + }, + dependentVariable: 'airline', + trainingPercent: 20, + modelMemory: '20mb', + createIndexPattern: true, + expected: { + source: 'ft_farequote_small', + rocCurveColorState: [ + // tick/grid/axis + { color: '#DDDDDD', percentage: 38 }, + // line + { color: '#98A2B3', percentage: 7 }, + ], + runtimeFieldsEditorContent: ['{', ' "uppercase_airline": {', ' "type": "keyword",'], + row: { + memoryStatus: 'ok', + type: 'classification', + status: 'stopped', + progress: '100', + }, + rowDetails: { + jobDetails: [ + { + section: 'state', + expectedEntries: { + id: `fq_saved_search_3_${dateNow}`, + state: 'stopped', + data_counts: + '{"training_docs_count":320,"test_docs_count":1283,"skipped_docs_count":0}', + description: 'Classification job based on a saved search with kuery query', + }, + }, + { section: 'progress', expectedEntries: { Phase: '8/8' } }, + ], + } as AnalyticsTableRowDetails, + }, + }, + ]; + + for (const testData of testDataList) { + describe(`${testData.suiteTitle}`, function () { + after(async () => { + await ml.api.deleteIndices(testData.destinationIndex); + await ml.testResources.deleteIndexPatternByTitle(testData.destinationIndex); + }); + + it('loads the data frame analytics wizard', async () => { + await ml.testExecution.logTestStep('loads the data frame analytics page'); + await ml.navigation.navigateToMl(); + await ml.navigation.navigateToDataFrameAnalytics(); + + await ml.testExecution.logTestStep('loads the source selection modal'); + + // Disable anti-aliasing to stabilize canvas image rendering assertions + await ml.commonUI.disableAntiAliasing(); + + await ml.dataFrameAnalytics.startAnalyticsCreation(); + + await ml.testExecution.logTestStep( + 'selects the source data and loads the job wizard page' + ); + await ml.jobSourceSelection.selectSourceForAnalyticsJob(testData.source); + await ml.dataFrameAnalyticsCreation.assertConfigurationStepActive(); + }); + + it('navigates through the wizard and sets all needed fields', async () => { + await ml.testExecution.logTestStep('selects the job type'); + await ml.dataFrameAnalyticsCreation.assertJobTypeSelectExists(); + await ml.dataFrameAnalyticsCreation.selectJobType(testData.jobType); + + await ml.testExecution.logTestStep('displays the runtime mappings editor switch'); + await ml.dataFrameAnalyticsCreation.assertRuntimeMappingSwitchExists(); + + await ml.testExecution.logTestStep('enables the runtime mappings editor'); + await ml.dataFrameAnalyticsCreation.toggleRuntimeMappingsEditorSwitch(true); + await ml.dataFrameAnalyticsCreation.assertRuntimeMappingsEditorContent(['']); + + await ml.testExecution.logTestStep('sets runtime mappings'); + await ml.dataFrameAnalyticsCreation.setRuntimeMappingsEditorContent( + JSON.stringify(testData.runtimeFields) + ); + await ml.dataFrameAnalyticsCreation.applyRuntimeMappings(); + await ml.dataFrameAnalyticsCreation.assertRuntimeMappingsEditorContent( + testData.expected.runtimeFieldsEditorContent + ); + + await ml.testExecution.logTestStep('inputs the dependent variable'); + await ml.dataFrameAnalyticsCreation.assertDependentVariableInputExists(); + await ml.dataFrameAnalyticsCreation.selectDependentVariable(testData.dependentVariable); + + await ml.testExecution.logTestStep('inputs the training percent'); + await ml.dataFrameAnalyticsCreation.assertTrainingPercentInputExists(); + await ml.dataFrameAnalyticsCreation.setTrainingPercent(testData.trainingPercent); + + await ml.testExecution.logTestStep('displays the source data preview'); + await ml.dataFrameAnalyticsCreation.assertSourceDataPreviewExists(); + + await ml.testExecution.logTestStep('displays the include fields selection'); + await ml.dataFrameAnalyticsCreation.assertIncludeFieldsSelectionExists(); + + await ml.testExecution.logTestStep('continues to the additional options step'); + await ml.dataFrameAnalyticsCreation.continueToAdditionalOptionsStep(); + + await ml.testExecution.logTestStep('accepts the suggested model memory limit'); + await ml.dataFrameAnalyticsCreation.assertModelMemoryInputExists(); + await ml.dataFrameAnalyticsCreation.assertModelMemoryInputPopulated(); + + await ml.testExecution.logTestStep('continues to the details step'); + await ml.dataFrameAnalyticsCreation.continueToDetailsStep(); + + await ml.testExecution.logTestStep('inputs the job id'); + await ml.dataFrameAnalyticsCreation.assertJobIdInputExists(); + await ml.dataFrameAnalyticsCreation.setJobId(testData.jobId); + + await ml.testExecution.logTestStep('inputs the job description'); + await ml.dataFrameAnalyticsCreation.assertJobDescriptionInputExists(); + await ml.dataFrameAnalyticsCreation.setJobDescription(testData.jobDescription); + + await ml.testExecution.logTestStep( + 'should default the set destination index to job id switch to true' + ); + await ml.dataFrameAnalyticsCreation.assertDestIndexSameAsIdSwitchExists(); + await ml.dataFrameAnalyticsCreation.assertDestIndexSameAsIdCheckState(true); + + await ml.testExecution.logTestStep('should input the destination index'); + await ml.dataFrameAnalyticsCreation.setDestIndexSameAsIdCheckState(false); + await ml.dataFrameAnalyticsCreation.assertDestIndexInputExists(); + await ml.dataFrameAnalyticsCreation.setDestIndex(testData.destinationIndex); + + await ml.testExecution.logTestStep('continues to the validation step'); + await ml.dataFrameAnalyticsCreation.continueToValidationStep(); + + await ml.testExecution.logTestStep('checks validation callouts exist'); + await ml.dataFrameAnalyticsCreation.assertValidationCalloutsExists(); + // Expect the follow callouts: + // - ✓ Dependent variable + // - ✓ Training percent + // - ✓ Top classes + // - ⚠ Analysis fields + await ml.dataFrameAnalyticsCreation.assertAllValidationCalloutsPresent(4); + + await ml.testExecution.logTestStep('continues to the create step'); + await ml.dataFrameAnalyticsCreation.continueToCreateStep(); + + await ml.testExecution.logTestStep('sets the create data view switch'); + await ml.dataFrameAnalyticsCreation.assertCreateIndexPatternSwitchExists(); + await ml.dataFrameAnalyticsCreation.setCreateIndexPatternSwitchState( + testData.createIndexPattern + ); + }); + + it('runs the analytics job and displays it correctly in the job list', async () => { + await ml.testExecution.logTestStep('creates and starts the analytics job'); + await ml.dataFrameAnalyticsCreation.assertCreateButtonExists(); + await ml.dataFrameAnalyticsCreation.assertStartJobCheckboxCheckState(true); + await ml.dataFrameAnalyticsCreation.createAnalyticsJob(testData.jobId); + + await ml.testExecution.logTestStep('finishes analytics processing'); + await ml.dataFrameAnalytics.waitForAnalyticsCompletion(testData.jobId); + + await ml.testExecution.logTestStep('displays the analytics table'); + await ml.dataFrameAnalyticsCreation.navigateToJobManagementPage(); + await ml.dataFrameAnalytics.assertAnalyticsTableExists(); + + await ml.testExecution.logTestStep('displays the stats bar'); + await ml.dataFrameAnalytics.assertAnalyticsStatsBarExists(); + + await ml.testExecution.logTestStep('displays the created job in the analytics table'); + await ml.dataFrameAnalyticsTable.refreshAnalyticsTable(); + await ml.dataFrameAnalyticsTable.filterWithSearchString(testData.jobId, 1); + + await ml.testExecution.logTestStep( + 'displays details for the created job in the analytics table' + ); + await ml.dataFrameAnalyticsTable.assertAnalyticsRowFields(testData.jobId, { + id: testData.jobId, + description: testData.jobDescription, + memoryStatus: testData.expected.row.memoryStatus, + sourceIndex: testData.expected.source, + destinationIndex: testData.destinationIndex, + type: testData.expected.row.type, + status: testData.expected.row.status, + progress: testData.expected.row.progress, + }); + + await ml.dataFrameAnalyticsTable.assertAnalyticsRowDetails( + testData.jobId, + testData.expected.rowDetails + ); + }); + + it('edits the analytics job and displays it correctly in the job list', async () => { + await ml.testExecution.logTestStep( + 'should open the edit form for the created job in the analytics table' + ); + await ml.dataFrameAnalyticsTable.openEditFlyout(testData.jobId); + + await ml.testExecution.logTestStep('should input the description in the edit form'); + await ml.dataFrameAnalyticsEdit.assertJobDescriptionEditInputExists(); + await ml.dataFrameAnalyticsEdit.setJobDescriptionEdit(editedDescription); + + await ml.testExecution.logTestStep( + 'should input the model memory limit in the edit form' + ); + await ml.dataFrameAnalyticsEdit.assertJobMmlEditInputExists(); + await ml.dataFrameAnalyticsEdit.setJobMmlEdit('21mb'); + + await ml.testExecution.logTestStep('should submit the edit job form'); + await ml.dataFrameAnalyticsEdit.updateAnalyticsJob(); + + await ml.testExecution.logTestStep( + 'displays details for the edited job in the analytics table' + ); + await ml.dataFrameAnalyticsTable.assertAnalyticsRowFields(testData.jobId, { + id: testData.jobId, + description: editedDescription, + memoryStatus: testData.expected.row.memoryStatus, + sourceIndex: testData.expected.source, + destinationIndex: testData.destinationIndex, + type: testData.expected.row.type, + status: testData.expected.row.status, + progress: testData.expected.row.progress, + }); + + await ml.testExecution.logTestStep( + 'creates the destination index and writes results to it' + ); + await ml.api.assertIndicesExist(testData.destinationIndex); + await ml.api.assertIndicesNotEmpty(testData.destinationIndex); + + await ml.testExecution.logTestStep('displays the results view for created job'); + await ml.dataFrameAnalyticsTable.openResultsView(testData.jobId); + await ml.dataFrameAnalyticsResults.assertClassificationEvaluatePanelElementsExists(); + await ml.dataFrameAnalyticsResults.assertClassificationTablePanelExists(); + await ml.dataFrameAnalyticsResults.assertResultsTableExists(); + await ml.dataFrameAnalyticsResults.assertResultsTableTrainingFiltersExist(); + await ml.dataFrameAnalyticsResults.assertResultsTableNotEmpty(); + + await ml.testExecution.logTestStep('displays the ROC curve chart'); + await ml.commonUI.assertColorsInCanvasElement( + 'mlDFAnalyticsClassificationExplorationRocCurveChart', + testData.expected.rocCurveColorState, + ['#000000'], + undefined, + undefined, + // increased tolerance for ROC curve chart up from 10 to 20 + // since the returned colors vary quite a bit on each run. + 20 + ); + + await ml.commonUI.resetAntiAliasing(); + }); + + it('displays the analytics job in the map view', async () => { + await ml.testExecution.logTestStep('should open the map view for created job'); + await ml.navigation.navigateToDataFrameAnalytics(); + await ml.dataFrameAnalyticsTable.openMapView(testData.jobId); + await ml.dataFrameAnalyticsMap.assertMapElementsExists(); + await ml.dataFrameAnalyticsMap.assertJobMapTitle(testData.jobId); + }); + }); + } + }); +} diff --git a/x-pack/test/functional/apps/ml/data_frame_analytics/index.ts b/x-pack/test/functional/apps/ml/data_frame_analytics/index.ts index e7b5df70c99a0b..908e45daf7105f 100644 --- a/x-pack/test/functional/apps/ml/data_frame_analytics/index.ts +++ b/x-pack/test/functional/apps/ml/data_frame_analytics/index.ts @@ -16,5 +16,8 @@ export default function ({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./classification_creation')); loadTestFile(require.resolve('./cloning')); loadTestFile(require.resolve('./feature_importance')); + loadTestFile(require.resolve('./regression_creation_saved_search')); + loadTestFile(require.resolve('./classification_creation_saved_search')); + loadTestFile(require.resolve('./outlier_detection_creation_saved_search')); }); } diff --git a/x-pack/test/functional/apps/ml/data_frame_analytics/outlier_detection_creation_saved_search.ts b/x-pack/test/functional/apps/ml/data_frame_analytics/outlier_detection_creation_saved_search.ts new file mode 100644 index 00000000000000..861be18591a118 --- /dev/null +++ b/x-pack/test/functional/apps/ml/data_frame_analytics/outlier_detection_creation_saved_search.ts @@ -0,0 +1,377 @@ +/* + * 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 { AnalyticsTableRowDetails } from '../../../services/ml/data_frame_analytics_table'; +import { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function ({ getService }: FtrProviderContext) { + const esArchiver = getService('esArchiver'); + const ml = getService('ml'); + const editedDescription = 'Edited description'; + + describe('outlier detection saved search creation', function () { + before(async () => { + await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote_small'); + await ml.testResources.createIndexPatternIfNeeded('ft_farequote_small', '@timestamp'); + await ml.testResources.createSavedSearchFarequoteLuceneIfNeeded('ft_farequote_small'); + await ml.testResources.createSavedSearchFarequoteKueryIfNeeded('ft_farequote_small'); + await ml.testResources.setKibanaTimeZoneToUTC(); + + await ml.securityUI.loginAsMlPowerUser(); + }); + + after(async () => { + await ml.api.cleanMlIndices(); + await ml.testResources.deleteSavedSearches(); + await ml.testResources.deleteIndexPatternByTitle('ft_farequote_small'); + }); + + const dateNow = Date.now(); + const testDataList = [ + { + suiteTitle: 'with lucene query', + jobType: 'outlier_detection', + jobId: `fq_saved_search_2_${dateNow}`, + jobDescription: 'Outlier detection job based on a saved search with lucene query', + source: 'ft_farequote_lucene', + get destinationIndex(): string { + return `user-${this.jobId}`; + }, + runtimeFields: { + uppercase_airline: { + type: 'keyword', + script: 'emit(params._source.airline.toUpperCase())', + }, + }, + modelMemory: '65mb', + createIndexPattern: true, + expected: { + source: 'ft_farequote_small', + histogramCharts: [ + { chartAvailable: true, id: 'uppercase_airline', legend: '5 categories' }, + { chartAvailable: true, id: 'responsetime', legend: '4.91 - 171.08' }, + { chartAvailable: true, id: 'airline', legend: '5 categories' }, + ], + scatterplotMatrixColorsWizard: [ + // markers + { color: '#52B398', percentage: 15 }, + // grey boilerplate + { color: '#6A717D', percentage: 13 }, + ], + scatterplotMatrixColorStatsResults: [ + // red markers + { color: '#D98071', percentage: 1 }, + // tick/grid/axis, grey markers + { color: '#6A717D', percentage: 12 }, + { color: '#D3DAE6', percentage: 8 }, + { color: '#98A1B3', percentage: 12 }, + // anti-aliasing + { color: '#F5F7FA', percentage: 30 }, + ], + runtimeFieldsEditorContent: ['{', ' "uppercase_airline": {', ' "type": "keyword",'], + row: { + memoryStatus: 'ok', + type: 'outlier_detection', + status: 'stopped', + progress: '100', + }, + rowDetails: { + jobDetails: [ + { + section: 'state', + expectedEntries: { + id: `fq_saved_search_2_${dateNow}`, + state: 'stopped', + data_counts: + '{"training_docs_count":1604,"test_docs_count":0,"skipped_docs_count":0}', + description: 'Outlier detection job based on a saved search with lucene query', + }, + }, + { section: 'progress', expectedEntries: { Phase: '4/4' } }, + ], + } as AnalyticsTableRowDetails, + }, + }, + { + suiteTitle: 'with kuery query', + jobType: 'outlier_detection', + jobId: `fq_saved_search_3_${dateNow}`, + jobDescription: 'Outlier detection job based on a saved search with kuery query', + source: 'ft_farequote_kuery', + get destinationIndex(): string { + return `user-${this.jobId}`; + }, + runtimeFields: { + uppercase_airline: { + type: 'keyword', + script: 'emit(params._source.airline.toUpperCase())', + }, + }, + modelMemory: '65mb', + createIndexPattern: true, + expected: { + source: 'ft_farequote_small', + histogramCharts: [ + { chartAvailable: true, id: 'uppercase_airline', legend: '5 categories' }, + { chartAvailable: true, id: 'responsetime', legend: '9.91 - 171.08' }, + { chartAvailable: true, id: 'airline', legend: '5 categories' }, + ], + scatterplotMatrixColorsWizard: [ + // markers + { color: '#52B398', percentage: 15 }, + // grey boilerplate + { color: '#6A717D', percentage: 13 }, + ], + scatterplotMatrixColorStatsResults: [ + // red markers + { color: '#D98071', percentage: 1 }, + // tick/grid/axis, grey markers + { color: '#6A717D', percentage: 12 }, + { color: '#D3DAE6', percentage: 8 }, + { color: '#98A1B3', percentage: 12 }, + // anti-aliasing + { color: '#F5F7FA', percentage: 30 }, + ], + runtimeFieldsEditorContent: ['{', ' "uppercase_airline": {', ' "type": "keyword",'], + row: { + memoryStatus: 'ok', + type: 'outlier_detection', + status: 'stopped', + progress: '100', + }, + rowDetails: { + jobDetails: [ + { + section: 'state', + expectedEntries: { + id: `fq_saved_search_3_${dateNow}`, + state: 'stopped', + data_counts: + '{"training_docs_count":1603,"test_docs_count":0,"skipped_docs_count":0}', + description: 'Outlier detection job based on a saved search with kuery query', + }, + }, + { section: 'progress', expectedEntries: { Phase: '4/4' } }, + ], + } as AnalyticsTableRowDetails, + }, + }, + ]; + + for (const testData of testDataList) { + describe(`${testData.suiteTitle}`, function () { + after(async () => { + await ml.api.deleteIndices(testData.destinationIndex); + await ml.testResources.deleteIndexPatternByTitle(testData.destinationIndex); + }); + + it('loads the data frame analytics wizard', async () => { + await ml.testExecution.logTestStep('loads the data frame analytics page'); + await ml.navigation.navigateToMl(); + await ml.navigation.navigateToDataFrameAnalytics(); + + await ml.testExecution.logTestStep('loads the source selection modal'); + + // Disable anti-aliasing to stabilize canvas image rendering assertions + await ml.commonUI.disableAntiAliasing(); + + await ml.dataFrameAnalytics.startAnalyticsCreation(); + + await ml.testExecution.logTestStep( + 'selects the source data and loads the job wizard page' + ); + await ml.jobSourceSelection.selectSourceForAnalyticsJob(testData.source); + await ml.dataFrameAnalyticsCreation.assertConfigurationStepActive(); + }); + + it('navigates through the wizard and sets all needed fields', async () => { + await ml.testExecution.logTestStep('selects the job type'); + await ml.dataFrameAnalyticsCreation.assertJobTypeSelectExists(); + await ml.dataFrameAnalyticsCreation.selectJobType(testData.jobType); + + await ml.testExecution.logTestStep('displays the runtime mappings editor switch'); + await ml.dataFrameAnalyticsCreation.assertRuntimeMappingSwitchExists(); + + await ml.testExecution.logTestStep('enables the runtime mappings editor'); + await ml.dataFrameAnalyticsCreation.toggleRuntimeMappingsEditorSwitch(true); + await ml.dataFrameAnalyticsCreation.assertRuntimeMappingsEditorContent(['']); + + await ml.testExecution.logTestStep('sets runtime mappings'); + await ml.dataFrameAnalyticsCreation.setRuntimeMappingsEditorContent( + JSON.stringify(testData.runtimeFields) + ); + await ml.dataFrameAnalyticsCreation.applyRuntimeMappings(); + await ml.dataFrameAnalyticsCreation.assertRuntimeMappingsEditorContent( + testData.expected.runtimeFieldsEditorContent + ); + + await ml.testExecution.logTestStep('does not display the dependent variable input'); + await ml.dataFrameAnalyticsCreation.assertDependentVariableInputMissing(); + + await ml.testExecution.logTestStep('does not display the training percent input'); + await ml.dataFrameAnalyticsCreation.assertTrainingPercentInputMissing(); + + await ml.testExecution.logTestStep('displays the source data preview'); + await ml.dataFrameAnalyticsCreation.assertSourceDataPreviewExists(); + + await ml.testExecution.logTestStep('enables the source data preview histogram charts'); + await ml.dataFrameAnalyticsCreation.enableSourceDataPreviewHistogramCharts(true); + + await ml.testExecution.logTestStep('displays the source data preview histogram charts'); + await ml.dataFrameAnalyticsCreation.assertSourceDataPreviewHistogramCharts( + testData.expected.histogramCharts + ); + + await ml.testExecution.logTestStep('displays the include fields selection'); + await ml.dataFrameAnalyticsCreation.assertIncludeFieldsSelectionExists(); + + await ml.testExecution.logTestStep('continues to the additional options step'); + await ml.dataFrameAnalyticsCreation.continueToAdditionalOptionsStep(); + + await ml.testExecution.logTestStep('accepts the suggested model memory limit'); + await ml.dataFrameAnalyticsCreation.assertModelMemoryInputExists(); + await ml.dataFrameAnalyticsCreation.assertModelMemoryInputPopulated(); + + await ml.testExecution.logTestStep('continues to the details step'); + await ml.dataFrameAnalyticsCreation.continueToDetailsStep(); + + await ml.testExecution.logTestStep('inputs the job id'); + await ml.dataFrameAnalyticsCreation.assertJobIdInputExists(); + await ml.dataFrameAnalyticsCreation.setJobId(testData.jobId); + + await ml.testExecution.logTestStep('inputs the job description'); + await ml.dataFrameAnalyticsCreation.assertJobDescriptionInputExists(); + await ml.dataFrameAnalyticsCreation.setJobDescription(testData.jobDescription); + + await ml.testExecution.logTestStep( + 'should default the set destination index to job id switch to true' + ); + await ml.dataFrameAnalyticsCreation.assertDestIndexSameAsIdSwitchExists(); + await ml.dataFrameAnalyticsCreation.assertDestIndexSameAsIdCheckState(true); + + await ml.testExecution.logTestStep('should input the destination index'); + await ml.dataFrameAnalyticsCreation.setDestIndexSameAsIdCheckState(false); + await ml.dataFrameAnalyticsCreation.assertDestIndexInputExists(); + await ml.dataFrameAnalyticsCreation.setDestIndex(testData.destinationIndex); + + await ml.testExecution.logTestStep('continues to the validation step'); + await ml.dataFrameAnalyticsCreation.continueToValidationStep(); + + await ml.testExecution.logTestStep('checks validation callouts exist'); + await ml.dataFrameAnalyticsCreation.assertValidationCalloutsExists(); + await ml.dataFrameAnalyticsCreation.assertAllValidationCalloutsPresent(1); + + await ml.testExecution.logTestStep('continues to the create step'); + await ml.dataFrameAnalyticsCreation.continueToCreateStep(); + + await ml.testExecution.logTestStep('sets the create data view switch'); + await ml.dataFrameAnalyticsCreation.assertCreateIndexPatternSwitchExists(); + await ml.dataFrameAnalyticsCreation.setCreateIndexPatternSwitchState( + testData.createIndexPattern + ); + }); + + it('runs the analytics job and displays it correctly in the job list', async () => { + await ml.testExecution.logTestStep('creates and starts the analytics job'); + await ml.dataFrameAnalyticsCreation.assertCreateButtonExists(); + await ml.dataFrameAnalyticsCreation.assertStartJobCheckboxCheckState(true); + await ml.dataFrameAnalyticsCreation.createAnalyticsJob(testData.jobId); + + await ml.testExecution.logTestStep('finishes analytics processing'); + await ml.dataFrameAnalytics.waitForAnalyticsCompletion(testData.jobId); + + await ml.testExecution.logTestStep('displays the analytics table'); + await ml.dataFrameAnalyticsCreation.navigateToJobManagementPage(); + await ml.dataFrameAnalytics.assertAnalyticsTableExists(); + + await ml.testExecution.logTestStep('displays the stats bar'); + await ml.dataFrameAnalytics.assertAnalyticsStatsBarExists(); + + await ml.testExecution.logTestStep('displays the created job in the analytics table'); + await ml.dataFrameAnalyticsTable.refreshAnalyticsTable(); + await ml.dataFrameAnalyticsTable.filterWithSearchString(testData.jobId, 1); + + await ml.testExecution.logTestStep( + 'displays details for the created job in the analytics table' + ); + await ml.dataFrameAnalyticsTable.assertAnalyticsRowFields(testData.jobId, { + id: testData.jobId, + description: testData.jobDescription, + memoryStatus: testData.expected.row.memoryStatus, + sourceIndex: testData.expected.source, + destinationIndex: testData.destinationIndex, + type: testData.expected.row.type, + status: testData.expected.row.status, + progress: testData.expected.row.progress, + }); + + await ml.dataFrameAnalyticsTable.assertAnalyticsRowDetails( + testData.jobId, + testData.expected.rowDetails + ); + }); + + it('edits the analytics job and displays it correctly in the job list', async () => { + await ml.testExecution.logTestStep( + 'should open the edit form for the created job in the analytics table' + ); + await ml.dataFrameAnalyticsTable.openEditFlyout(testData.jobId); + + await ml.testExecution.logTestStep('should input the description in the edit form'); + await ml.dataFrameAnalyticsEdit.assertJobDescriptionEditInputExists(); + await ml.dataFrameAnalyticsEdit.setJobDescriptionEdit(editedDescription); + + await ml.testExecution.logTestStep( + 'should input the model memory limit in the edit form' + ); + await ml.dataFrameAnalyticsEdit.assertJobMmlEditInputExists(); + await ml.dataFrameAnalyticsEdit.setJobMmlEdit('21mb'); + + await ml.testExecution.logTestStep('should submit the edit job form'); + await ml.dataFrameAnalyticsEdit.updateAnalyticsJob(); + + await ml.testExecution.logTestStep( + 'displays details for the edited job in the analytics table' + ); + await ml.dataFrameAnalyticsTable.assertAnalyticsRowFields(testData.jobId, { + id: testData.jobId, + description: editedDescription, + memoryStatus: testData.expected.row.memoryStatus, + sourceIndex: testData.expected.source, + destinationIndex: testData.destinationIndex, + type: testData.expected.row.type, + status: testData.expected.row.status, + progress: testData.expected.row.progress, + }); + + await ml.testExecution.logTestStep( + 'creates the destination index and writes results to it' + ); + await ml.api.assertIndicesExist(testData.destinationIndex); + await ml.api.assertIndicesNotEmpty(testData.destinationIndex); + + await ml.testExecution.logTestStep('displays the results view for created job'); + await ml.dataFrameAnalyticsTable.openResultsView(testData.jobId); + await ml.dataFrameAnalyticsResults.assertOutlierTablePanelExists(); + await ml.dataFrameAnalyticsResults.assertResultsTableExists(); + await ml.dataFrameAnalyticsResults.assertResultsTableNotEmpty(); + await ml.dataFrameAnalyticsResults.assertFeatureInfluenceCellNotEmpty(); + + await ml.commonUI.resetAntiAliasing(); + }); + + it('displays the analytics job in the map view', async () => { + await ml.testExecution.logTestStep('should open the map view for created job'); + await ml.navigation.navigateToDataFrameAnalytics(); + await ml.dataFrameAnalyticsTable.openMapView(testData.jobId); + await ml.dataFrameAnalyticsMap.assertMapElementsExists(); + await ml.dataFrameAnalyticsMap.assertJobMapTitle(testData.jobId); + }); + }); + } + }); +} diff --git a/x-pack/test/functional/apps/ml/data_frame_analytics/regression_creation_saved_search.ts b/x-pack/test/functional/apps/ml/data_frame_analytics/regression_creation_saved_search.ts new file mode 100644 index 00000000000000..e22c4908486d18 --- /dev/null +++ b/x-pack/test/functional/apps/ml/data_frame_analytics/regression_creation_saved_search.ts @@ -0,0 +1,333 @@ +/* + * 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 { AnalyticsTableRowDetails } from '../../../services/ml/data_frame_analytics_table'; +import { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function ({ getService }: FtrProviderContext) { + const esArchiver = getService('esArchiver'); + const ml = getService('ml'); + const editedDescription = 'Edited description'; + + describe('regression saved search creation', function () { + before(async () => { + await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote_small'); + await ml.testResources.createIndexPatternIfNeeded('ft_farequote_small', '@timestamp'); + await ml.testResources.createSavedSearchFarequoteLuceneIfNeeded('ft_farequote_small'); + await ml.testResources.createSavedSearchFarequoteKueryIfNeeded('ft_farequote_small'); + await ml.testResources.setKibanaTimeZoneToUTC(); + + await ml.securityUI.loginAsMlPowerUser(); + }); + + after(async () => { + await ml.api.cleanMlIndices(); + await ml.testResources.deleteSavedSearches(); + await ml.testResources.deleteIndexPatternByTitle('ft_farequote_small'); + }); + + const dateNow = Date.now(); + const testDataList = [ + { + suiteTitle: 'with lucene query', + jobType: 'regression', + jobId: `fq_saved_search_2_${dateNow}`, + jobDescription: 'Regression job based on a saved search with lucene query', + source: 'ft_farequote_lucene', + get destinationIndex(): string { + return `user-${this.jobId}`; + }, + runtimeFields: { + uppercase_airline: { + type: 'keyword', + script: 'emit(params._source.airline.toUpperCase())', + }, + }, + dependentVariable: 'responsetime', + trainingPercent: 20, + modelMemory: '20mb', + createIndexPattern: true, + expected: { + source: 'ft_farequote_small', + runtimeFieldsEditorContent: ['{', ' "uppercase_airline": {', ' "type": "keyword",'], + row: { + memoryStatus: 'ok', + type: 'regression', + status: 'stopped', + progress: '100', + }, + rowDetails: { + jobDetails: [ + { + section: 'state', + expectedEntries: { + id: `fq_saved_search_2_${dateNow}`, + state: 'stopped', + data_counts: + '{"training_docs_count":320,"test_docs_count":1284,"skipped_docs_count":0}', + description: 'Regression job based on a saved search with lucene query', + }, + }, + { section: 'progress', expectedEntries: { Phase: '8/8' } }, + ], + } as AnalyticsTableRowDetails, + }, + }, + { + suiteTitle: 'with kuery query', + jobType: 'regression', + jobId: `fq_saved_search_3_${dateNow}`, + jobDescription: 'Regression job based on a saved search with kuery query', + source: 'ft_farequote_kuery', + get destinationIndex(): string { + return `user-${this.jobId}`; + }, + runtimeFields: { + uppercase_airline: { + type: 'keyword', + script: 'emit(params._source.airline.toUpperCase())', + }, + }, + dependentVariable: 'responsetime', + trainingPercent: 20, + modelMemory: '20mb', + createIndexPattern: true, + expected: { + source: 'ft_farequote_small', + runtimeFieldsEditorContent: ['{', ' "uppercase_airline": {', ' "type": "keyword",'], + row: { + memoryStatus: 'ok', + type: 'regression', + status: 'stopped', + progress: '100', + }, + rowDetails: { + jobDetails: [ + { + section: 'state', + expectedEntries: { + id: `fq_saved_search_3_${dateNow}`, + state: 'stopped', + data_counts: + '{"training_docs_count":320,"test_docs_count":1283,"skipped_docs_count":0}', + description: 'Regression job based on a saved search with kuery query', + }, + }, + { section: 'progress', expectedEntries: { Phase: '8/8' } }, + ], + } as AnalyticsTableRowDetails, + }, + }, + ]; + + for (const testData of testDataList) { + describe(`${testData.suiteTitle}`, function () { + after(async () => { + await ml.api.deleteIndices(testData.destinationIndex); + await ml.testResources.deleteIndexPatternByTitle(testData.destinationIndex); + }); + + it('loads the data frame analytics wizard', async () => { + await ml.testExecution.logTestStep('loads the data frame analytics page'); + await ml.navigation.navigateToMl(); + await ml.navigation.navigateToDataFrameAnalytics(); + + await ml.testExecution.logTestStep('loads the source selection modal'); + + // Disable anti-aliasing to stabilize canvas image rendering assertions + await ml.commonUI.disableAntiAliasing(); + + await ml.dataFrameAnalytics.startAnalyticsCreation(); + + await ml.testExecution.logTestStep( + 'selects the source data and loads the job wizard page' + ); + await ml.jobSourceSelection.selectSourceForAnalyticsJob(testData.source); + await ml.dataFrameAnalyticsCreation.assertConfigurationStepActive(); + }); + + it('navigates through the wizard and sets all needed fields', async () => { + await ml.testExecution.logTestStep('selects the job type'); + await ml.dataFrameAnalyticsCreation.assertJobTypeSelectExists(); + await ml.dataFrameAnalyticsCreation.selectJobType(testData.jobType); + + await ml.testExecution.logTestStep('displays the runtime mappings editor switch'); + await ml.dataFrameAnalyticsCreation.assertRuntimeMappingSwitchExists(); + + await ml.testExecution.logTestStep('enables the runtime mappings editor'); + await ml.dataFrameAnalyticsCreation.toggleRuntimeMappingsEditorSwitch(true); + await ml.dataFrameAnalyticsCreation.assertRuntimeMappingsEditorContent(['']); + + await ml.testExecution.logTestStep('sets runtime mappings'); + await ml.dataFrameAnalyticsCreation.setRuntimeMappingsEditorContent( + JSON.stringify(testData.runtimeFields) + ); + await ml.dataFrameAnalyticsCreation.applyRuntimeMappings(); + await ml.dataFrameAnalyticsCreation.assertRuntimeMappingsEditorContent( + testData.expected.runtimeFieldsEditorContent + ); + + await ml.testExecution.logTestStep('inputs the dependent variable'); + await ml.dataFrameAnalyticsCreation.assertDependentVariableInputExists(); + await ml.dataFrameAnalyticsCreation.selectDependentVariable(testData.dependentVariable); + + await ml.testExecution.logTestStep('inputs the training percent'); + await ml.dataFrameAnalyticsCreation.assertTrainingPercentInputExists(); + await ml.dataFrameAnalyticsCreation.setTrainingPercent(testData.trainingPercent); + + await ml.testExecution.logTestStep('displays the source data preview'); + await ml.dataFrameAnalyticsCreation.assertSourceDataPreviewExists(); + + await ml.testExecution.logTestStep('displays the include fields selection'); + await ml.dataFrameAnalyticsCreation.assertIncludeFieldsSelectionExists(); + + await ml.testExecution.logTestStep('continues to the additional options step'); + await ml.dataFrameAnalyticsCreation.continueToAdditionalOptionsStep(); + + await ml.testExecution.logTestStep('accepts the suggested model memory limit'); + await ml.dataFrameAnalyticsCreation.assertModelMemoryInputExists(); + await ml.dataFrameAnalyticsCreation.assertModelMemoryInputPopulated(); + + await ml.testExecution.logTestStep('continues to the details step'); + await ml.dataFrameAnalyticsCreation.continueToDetailsStep(); + + await ml.testExecution.logTestStep('inputs the job id'); + await ml.dataFrameAnalyticsCreation.assertJobIdInputExists(); + await ml.dataFrameAnalyticsCreation.setJobId(testData.jobId); + + await ml.testExecution.logTestStep('inputs the job description'); + await ml.dataFrameAnalyticsCreation.assertJobDescriptionInputExists(); + await ml.dataFrameAnalyticsCreation.setJobDescription(testData.jobDescription); + + await ml.testExecution.logTestStep( + 'should default the set destination index to job id switch to true' + ); + await ml.dataFrameAnalyticsCreation.assertDestIndexSameAsIdSwitchExists(); + await ml.dataFrameAnalyticsCreation.assertDestIndexSameAsIdCheckState(true); + + await ml.testExecution.logTestStep('should input the destination index'); + await ml.dataFrameAnalyticsCreation.setDestIndexSameAsIdCheckState(false); + await ml.dataFrameAnalyticsCreation.assertDestIndexInputExists(); + await ml.dataFrameAnalyticsCreation.setDestIndex(testData.destinationIndex); + + await ml.testExecution.logTestStep('continues to the validation step'); + await ml.dataFrameAnalyticsCreation.continueToValidationStep(); + + await ml.testExecution.logTestStep('checks validation callouts exist'); + await ml.dataFrameAnalyticsCreation.assertValidationCalloutsExists(); + await ml.dataFrameAnalyticsCreation.assertAllValidationCalloutsPresent(3); + + await ml.testExecution.logTestStep('continues to the create step'); + await ml.dataFrameAnalyticsCreation.continueToCreateStep(); + + await ml.testExecution.logTestStep('sets the create data view switch'); + await ml.dataFrameAnalyticsCreation.assertCreateIndexPatternSwitchExists(); + await ml.dataFrameAnalyticsCreation.setCreateIndexPatternSwitchState( + testData.createIndexPattern + ); + }); + + it('runs the analytics job and displays it correctly in the job list', async () => { + await ml.testExecution.logTestStep('creates and starts the analytics job'); + await ml.dataFrameAnalyticsCreation.assertCreateButtonExists(); + await ml.dataFrameAnalyticsCreation.assertStartJobCheckboxCheckState(true); + await ml.dataFrameAnalyticsCreation.createAnalyticsJob(testData.jobId); + + await ml.testExecution.logTestStep('finishes analytics processing'); + await ml.dataFrameAnalytics.waitForAnalyticsCompletion(testData.jobId); + + await ml.testExecution.logTestStep('displays the analytics table'); + await ml.dataFrameAnalyticsCreation.navigateToJobManagementPage(); + await ml.dataFrameAnalytics.assertAnalyticsTableExists(); + + await ml.testExecution.logTestStep('displays the stats bar'); + await ml.dataFrameAnalytics.assertAnalyticsStatsBarExists(); + + await ml.testExecution.logTestStep('displays the created job in the analytics table'); + await ml.dataFrameAnalyticsTable.refreshAnalyticsTable(); + await ml.dataFrameAnalyticsTable.filterWithSearchString(testData.jobId, 1); + + await ml.testExecution.logTestStep( + 'displays details for the created job in the analytics table' + ); + await ml.dataFrameAnalyticsTable.assertAnalyticsRowFields(testData.jobId, { + id: testData.jobId, + description: testData.jobDescription, + memoryStatus: testData.expected.row.memoryStatus, + sourceIndex: testData.expected.source, + destinationIndex: testData.destinationIndex, + type: testData.expected.row.type, + status: testData.expected.row.status, + progress: testData.expected.row.progress, + }); + await ml.dataFrameAnalyticsTable.assertAnalyticsRowDetails( + testData.jobId, + testData.expected.rowDetails + ); + }); + + it('edits the analytics job and displays it correctly in the job list', async () => { + await ml.testExecution.logTestStep( + 'should open the edit form for the created job in the analytics table' + ); + await ml.dataFrameAnalyticsTable.openEditFlyout(testData.jobId); + + await ml.testExecution.logTestStep('should input the description in the edit form'); + await ml.dataFrameAnalyticsEdit.assertJobDescriptionEditInputExists(); + await ml.dataFrameAnalyticsEdit.setJobDescriptionEdit(editedDescription); + + await ml.testExecution.logTestStep( + 'should input the model memory limit in the edit form' + ); + await ml.dataFrameAnalyticsEdit.assertJobMmlEditInputExists(); + await ml.dataFrameAnalyticsEdit.setJobMmlEdit('21mb'); + + await ml.testExecution.logTestStep('should submit the edit job form'); + await ml.dataFrameAnalyticsEdit.updateAnalyticsJob(); + + await ml.testExecution.logTestStep( + 'displays details for the edited job in the analytics table' + ); + await ml.dataFrameAnalyticsTable.assertAnalyticsRowFields(testData.jobId, { + id: testData.jobId, + description: editedDescription, + memoryStatus: testData.expected.row.memoryStatus, + sourceIndex: testData.expected.source, + destinationIndex: testData.destinationIndex, + type: testData.expected.row.type, + status: testData.expected.row.status, + progress: testData.expected.row.progress, + }); + + await ml.testExecution.logTestStep( + 'creates the destination index and writes results to it' + ); + await ml.api.assertIndicesExist(testData.destinationIndex); + await ml.api.assertIndicesNotEmpty(testData.destinationIndex); + + await ml.testExecution.logTestStep('displays the results view for created job'); + await ml.dataFrameAnalyticsTable.openResultsView(testData.jobId); + await ml.dataFrameAnalyticsResults.assertRegressionEvaluatePanelElementsExists(); + await ml.dataFrameAnalyticsResults.assertRegressionTablePanelExists(); + await ml.dataFrameAnalyticsResults.assertResultsTableExists(); + await ml.dataFrameAnalyticsResults.assertResultsTableTrainingFiltersExist(); + await ml.dataFrameAnalyticsResults.assertResultsTableNotEmpty(); + + await ml.commonUI.resetAntiAliasing(); + }); + + it('displays the analytics job in the map view', async () => { + await ml.testExecution.logTestStep('should open the map view for created job'); + await ml.navigation.navigateToDataFrameAnalytics(); + await ml.dataFrameAnalyticsTable.openMapView(testData.jobId); + await ml.dataFrameAnalyticsMap.assertMapElementsExists(); + await ml.dataFrameAnalyticsMap.assertJobMapTitle(testData.jobId); + }); + }); + } + }); +} diff --git a/x-pack/test/functional/apps/ml/index.ts b/x-pack/test/functional/apps/ml/index.ts index eeae200f35ba77..c58b20e1c374bd 100644 --- a/x-pack/test/functional/apps/ml/index.ts +++ b/x-pack/test/functional/apps/ml/index.ts @@ -25,6 +25,7 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { await ml.securityCommon.cleanMlRoles(); await esArchiver.unload('x-pack/test/functional/es_archives/ml/farequote'); + await esArchiver.unload('x-pack/test/functional/es_archives/ml/farequote_small'); await esArchiver.unload('x-pack/test/functional/es_archives/ml/ecommerce'); await esArchiver.unload('x-pack/test/functional/es_archives/ml/categorization_small'); await esArchiver.unload('x-pack/test/functional/es_archives/ml/event_rate_nanos'); diff --git a/x-pack/test/functional/es_archives/ml/farequote_small/data.json.gz b/x-pack/test/functional/es_archives/ml/farequote_small/data.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..7c82fda817373e596e4e847ebfa642bba10ec5a3 GIT binary patch literal 63274 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

D1(>;(UVd#+hH2St0xi!<>AR;-L>+?EzSb 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�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 BA-w { + async createSavedSearchIfNeeded(savedSearch: any, indexPatternTitle: string): Promise { const title = savedSearch.requestBody.attributes.title; const savedSearchId = await this.getSavedSearchId(title); if (savedSearchId !== undefined) { @@ -195,7 +195,7 @@ export function MachineLearningTestResourcesProvider({ getService }: FtrProvider } else { const body = await this.updateSavedSearchRequestBody( savedSearch.requestBody, - savedSearch.indexPatternTitle + indexPatternTitle ); return await this.createSavedSearch(title, body); } @@ -226,8 +226,8 @@ export function MachineLearningTestResourcesProvider({ getService }: FtrProvider return updatedBody; }, - async createSavedSearchFarequoteFilterIfNeeded() { - await this.createSavedSearchIfNeeded(savedSearches.farequoteFilter); + async createSavedSearchFarequoteFilterIfNeeded(indexPatternTitle: string = 'ft_farequote') { + await this.createSavedSearchIfNeeded(savedSearches.farequoteFilter, indexPatternTitle); }, async createMLTestDashboardIfNeeded() { @@ -249,20 +249,30 @@ export function MachineLearningTestResourcesProvider({ getService }: FtrProvider } }, - async createSavedSearchFarequoteLuceneIfNeeded() { - await this.createSavedSearchIfNeeded(savedSearches.farequoteLucene); + async createSavedSearchFarequoteLuceneIfNeeded(indexPatternTitle: string = 'ft_farequote') { + await this.createSavedSearchIfNeeded(savedSearches.farequoteLucene, indexPatternTitle); }, - async createSavedSearchFarequoteKueryIfNeeded() { - await this.createSavedSearchIfNeeded(savedSearches.farequoteKuery); + async createSavedSearchFarequoteKueryIfNeeded(indexPatternTitle: string = 'ft_farequote') { + await this.createSavedSearchIfNeeded(savedSearches.farequoteKuery, indexPatternTitle); }, - async createSavedSearchFarequoteFilterAndLuceneIfNeeded() { - await this.createSavedSearchIfNeeded(savedSearches.farequoteFilterAndLucene); + async createSavedSearchFarequoteFilterAndLuceneIfNeeded( + indexPatternTitle: string = 'ft_farequote' + ) { + await this.createSavedSearchIfNeeded( + savedSearches.farequoteFilterAndLucene, + indexPatternTitle + ); }, - async createSavedSearchFarequoteFilterAndKueryIfNeeded() { - await this.createSavedSearchIfNeeded(savedSearches.farequoteFilterAndKuery); + async createSavedSearchFarequoteFilterAndKueryIfNeeded( + indexPatternTitle: string = 'ft_farequote' + ) { + await this.createSavedSearchIfNeeded( + savedSearches.farequoteFilterAndKuery, + indexPatternTitle + ); }, async deleteSavedObjectById(id: string, objectType: SavedObjectType, force: boolean = false) { diff --git a/x-pack/test/functional/services/ml/test_resources_data.ts b/x-pack/test/functional/services/ml/test_resources_data.ts index 7502968bd2bb46..aeacc51cecbc95 100644 --- a/x-pack/test/functional/services/ml/test_resources_data.ts +++ b/x-pack/test/functional/services/ml/test_resources_data.ts @@ -7,7 +7,6 @@ export const savedSearches = { farequoteFilter: { - indexPatternTitle: 'ft_farequote', requestBody: { attributes: { title: 'ft_farequote_filter', @@ -66,7 +65,6 @@ export const savedSearches = { }, }, farequoteLucene: { - indexPatternTitle: 'ft_farequote', requestBody: { attributes: { title: 'ft_farequote_lucene', @@ -98,7 +96,6 @@ export const savedSearches = { }, }, farequoteKuery: { - indexPatternTitle: 'ft_farequote', requestBody: { attributes: { title: 'ft_farequote_kuery', @@ -130,7 +127,6 @@ export const savedSearches = { }, }, farequoteFilterAndLucene: { - indexPatternTitle: 'ft_farequote', requestBody: { attributes: { title: 'ft_farequote_filter_and_lucene', @@ -189,7 +185,6 @@ export const savedSearches = { }, }, farequoteFilterAndKuery: { - indexPatternTitle: 'ft_farequote', requestBody: { attributes: { title: 'ft_farequote_filter_and_kuery',