diff --git a/x-pack/legacy/plugins/ml/public/components/full_time_range_selector/__snapshots__/full_time_range_selector.test.tsx.snap b/x-pack/legacy/plugins/ml/public/components/full_time_range_selector/__snapshots__/full_time_range_selector.test.tsx.snap index 752e21f228263..dfdc40df32808 100644 --- a/x-pack/legacy/plugins/ml/public/components/full_time_range_selector/__snapshots__/full_time_range_selector.test.tsx.snap +++ b/x-pack/legacy/plugins/ml/public/components/full_time_range_selector/__snapshots__/full_time_range_selector.test.tsx.snap @@ -3,6 +3,7 @@ exports[`FullTimeRangeSelector renders the selector 1`] = ` = ({ indexPattern, query, disabled } } return ( - setRange(indexPattern, query)}> + setRange(indexPattern, query)} + data-test-subj="mlButtonUseFullData" + >
-
+

-
+

- +
- +
- +
diff --git a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/charts/anomaly_chart/anomaly_chart.tsx b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/charts/anomaly_chart/anomaly_chart.tsx index ae0cb502d56fd..f7b9d18a24358 100644 --- a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/charts/anomaly_chart/anomaly_chart.tsx +++ b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/charts/anomaly_chart/anomaly_chart.tsx @@ -44,7 +44,7 @@ export const AnomalyChart: FC = ({ const xDomain = getXRange(data); return ( -
+
diff --git a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/charts/event_rate_chart/event_rate_chart.tsx b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/charts/event_rate_chart/event_rate_chart.tsx index 011df2bc550a7..ad806f8af243c 100644 --- a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/charts/event_rate_chart/event_rate_chart.tsx +++ b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/charts/event_rate_chart/event_rate_chart.tsx @@ -22,7 +22,7 @@ const SPEC_ID = 'event_rate'; export const EventRateChart: FC = ({ eventRateChartData, height, width, showAxis }) => { return ( -
+
{showAxis === true && } diff --git a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/job_details_step/components/advanced_section/advanced_section.tsx b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/job_details_step/components/advanced_section/advanced_section.tsx index 2bc7a612e1f20..dc1f446b1a0a3 100644 --- a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/job_details_step/components/advanced_section/advanced_section.tsx +++ b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/job_details_step/components/advanced_section/advanced_section.tsx @@ -24,9 +24,14 @@ export const AdvancedSection: FC = ({ advancedExpanded, setAdvancedExpand buttonContent={ButtonContent} onToggle={setAdvancedExpanded} initialIsOpen={advancedExpanded} + data-test-subj="mlJobWizardToggleAdvancedSection" > - + diff --git a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/job_details_step/components/advanced_section/components/dedicated_index/dedicated_index_switch.tsx b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/job_details_step/components/advanced_section/components/dedicated_index/dedicated_index_switch.tsx index 7be4c5563355d..13c835952e251 100644 --- a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/job_details_step/components/advanced_section/components/dedicated_index/dedicated_index_switch.tsx +++ b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/job_details_step/components/advanced_section/components/dedicated_index/dedicated_index_switch.tsx @@ -24,7 +24,12 @@ export const DedicatedIndexSwitch: FC = () => { return ( - + ); }; diff --git a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/job_details_step/components/advanced_section/components/model_memory_limit/model_memory_limit_input.tsx b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/job_details_step/components/advanced_section/components/model_memory_limit/model_memory_limit_input.tsx index cb690cfcfbc53..d682f901ce368 100644 --- a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/job_details_step/components/advanced_section/components/model_memory_limit/model_memory_limit_input.tsx +++ b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/job_details_step/components/advanced_section/components/model_memory_limit/model_memory_limit_input.tsx @@ -33,6 +33,7 @@ export const ModelMemoryLimitInput: FC = () => { value={modelMemoryLimit} onChange={e => setModelMemoryLimit(e.target.value)} isInvalid={validation.valid === false} + data-test-subj="mlJobWizardInputModelMemoryLimit" /> ); diff --git a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/job_details_step/components/advanced_section/components/model_plot/model_plot_switch.tsx b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/job_details_step/components/advanced_section/components/model_plot/model_plot_switch.tsx index 0ae8016b7dce9..963730f4279b8 100644 --- a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/job_details_step/components/advanced_section/components/model_plot/model_plot_switch.tsx +++ b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/job_details_step/components/advanced_section/components/model_plot/model_plot_switch.tsx @@ -24,7 +24,12 @@ export const ModelPlotSwitch: FC = () => { return ( - + ); }; diff --git a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/job_details_step/components/groups/groups_input.tsx b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/job_details_step/components/groups/groups_input.tsx index d1eb1a45cf09b..39d46b8f7ffbf 100644 --- a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/job_details_step/components/groups/groups_input.tsx +++ b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/job_details_step/components/groups/groups_input.tsx @@ -77,6 +77,7 @@ export const GroupsInput: FC = () => { onCreateOption={onCreateGroup} isClearable={true} isInvalid={validation.valid === false} + data-test-subj="mlJobWizardComboBoxJobGroups" /> ); diff --git a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/job_details_step/components/job_description/job_description_input.tsx b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/job_details_step/components/job_description/job_description_input.tsx index f1e894f359082..d5e510a277a48 100644 --- a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/job_details_step/components/job_description/job_description_input.tsx +++ b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/job_details_step/components/job_description/job_description_input.tsx @@ -24,6 +24,7 @@ export const JobDescriptionInput: FC = () => { placeholder="Job description" value={jobDescription} onChange={e => setJobDescription(e.target.value)} + data-test-subj="mlJobWizardInputJobDescription" /> ); diff --git a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/job_details_step/components/job_id/job_id_input.tsx b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/job_details_step/components/job_id/job_id_input.tsx index 14a8b29c0cca4..58ce77e455ce2 100644 --- a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/job_details_step/components/job_id/job_id_input.tsx +++ b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/job_details_step/components/job_id/job_id_input.tsx @@ -36,6 +36,7 @@ export const JobIdInput: FC = () => { value={jobId} onChange={e => setJobId(e.target.value)} isInvalid={validation.valid === false} + data-test-subj="mlJobWizardInputJobId" /> ); diff --git a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/pick_fields_step/components/agg_select/agg_select.tsx b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/pick_fields_step/components/agg_select/agg_select.tsx index 938489feef7ee..34532fee93011 100644 --- a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/pick_fields_step/components/agg_select/agg_select.tsx +++ b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/pick_fields_step/components/agg_select/agg_select.tsx @@ -64,7 +64,11 @@ export const AggSelect: FC = ({ fields, changeHandler, selectedOptions, r }, [jobValidatorUpdated]); return ( - + = ({ bucketSpan, setBucketSpan, isInvali value={bucketSpan} onChange={e => setBucketSpan(e.target.value)} isInvalid={isInvalid} + data-test-subj="mlJobWizardInputBucketSpan" /> ); }; diff --git a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/summary_step/summary.tsx b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/summary_step/summary.tsx index a6f4361ee5475..6812935794baf 100644 --- a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/summary_step/summary.tsx +++ b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/summary_step/summary.tsx @@ -77,11 +77,21 @@ export const SummaryStep: FC = ({ setCurrentStep, isCurrentStep }) => {progress < 100 && ( - 0} disabled={isValid === false}> + 0} + disabled={isValid === false} + data-test-subj="mlJobWizardButtonCreateJob" + > Create job   - 0}> + 0} + data-test-subj="mlJobWizardButtonPreviewJobJson" + > Preview job JSON {showJsonFlyout && ( @@ -92,7 +102,9 @@ export const SummaryStep: FC = ({ setCurrentStep, isCurrentStep }) => )} {progress > 0 && ( - View results + + View results + )} diff --git a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/wizard_nav/wizard_nav.tsx b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/wizard_nav/wizard_nav.tsx index d1629e03f36d9..fd99cf90b238a 100644 --- a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/wizard_nav/wizard_nav.tsx +++ b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/wizard_nav/wizard_nav.tsx @@ -27,7 +27,13 @@ export const WizardNav: FC = ({ {previous && ( - + {i18n.translate('xpack.ml.newJob.wizard.previousStepButton', { defaultMessage: 'Previous', })} @@ -36,7 +42,13 @@ export const WizardNav: FC = ({ )} {next && ( - + {i18n.translate('xpack.ml.newJob.wizard.nextStepButton', { defaultMessage: 'Next', })} diff --git a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/new_job/page.tsx b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/new_job/page.tsx index 7397cba6953bd..b55ca355803b6 100644 --- a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/new_job/page.tsx +++ b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/new_job/page.tsx @@ -95,7 +95,7 @@ export const Page: FC = ({ existingJobsAndGroups, jobType }) => { return ( - + = ({ {currentStep === WIZARD_STEPS.TIME_RANGE && ( - Time range + Time range = ({ )} {currentStep === WIZARD_STEPS.PICK_FIELDS && ( - Pick fields + Pick fields = ({ )} {currentStep === WIZARD_STEPS.JOB_DETAILS && ( - Job details + Job details = ({ )} {currentStep === WIZARD_STEPS.VALIDATION && ( - Validation + Validation = ({ )} {currentStep === WIZARD_STEPS.SUMMARY && ( - Summary + Summary = ({ ); }; -const Title: FC = ({ children }) => { +const Title: FC<{ 'data-test-subj': string }> = ({ 'data-test-subj': dataTestSubj, children }) => { return ( -

{children}

+

{children}

diff --git a/x-pack/test/functional/apps/machine_learning/create_single_metric_job.ts b/x-pack/test/functional/apps/machine_learning/create_single_metric_job.ts new file mode 100644 index 0000000000000..3973f2aa38257 --- /dev/null +++ b/x-pack/test/functional/apps/machine_learning/create_single_metric_job.ts @@ -0,0 +1,146 @@ +/* + * 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 expect from '@kbn/expect'; + +import { FtrProviderContext } from '../../ftr_provider_context'; + +// eslint-disable-next-line import/no-default-export +export default function({ getService }: FtrProviderContext) { + const esArchiver = getService('esArchiver'); + const ml = getService('ml'); + + const jobId = `fq_single_1_${Date.now()}`; + + describe('single metric job creation', function() { + this.tags('smoke'); + before(async () => { + await esArchiver.loadIfNeeded('ml/farequote'); + }); + + after(async () => { + await esArchiver.unload('ml/farequote'); + await ml.api.cleanMlIndices(); + await ml.api.cleanDataframeIndices(); + }); + + it('loads the job management page', async () => { + await ml.navigation.navigateToMl(); + await ml.navigation.navigateToJobManagement(); + }); + + it('loads the new job source selection page', async () => { + await ml.jobManagement.navigateToNewJobSourceSelection(); + }); + + it('loads the job type selection page', async () => { + await ml.jobSourceSelection.selectSourceIndexPattern('farequote'); + }); + + it('loads the single metric job wizard page', async () => { + await ml.jobTypeSelection.selectSingleMetricJob(); + }); + + it('displays the time range step', async () => { + await ml.jobWizardCommon.assertTimeRangeSectionExists(); + }); + + it('displays the event rate chart', async () => { + await ml.jobWizardCommon.clickUseFullDataButton(); + await ml.jobWizardCommon.assertEventRateChartExists(); + }); + + it('displays the pick fields step', async () => { + await ml.jobWizardCommon.clickNextButton(); + await ml.jobWizardCommon.assertPickFieldsSectionExists(); + }); + + it('selects field and aggregation', async () => { + const identifier = 'Mean(responsetime)'; + await ml.jobWizardCommon.assertAggAndFieldInputExists(); + await ml.jobWizardCommon.selectAggAndField(identifier); + await ml.jobWizardCommon.assertAggAndFieldSelection(identifier); + }); + + it('inputs the bucket span', async () => { + const bucketSpan = '30m'; + await ml.jobWizardCommon.assertBucketSpanInputExists(); + await ml.jobWizardCommon.setBucketSpan(bucketSpan); + await ml.jobWizardCommon.assertBucketSpanValue(bucketSpan); + }); + + it('displays the job details step', async () => { + await ml.jobWizardCommon.clickNextButton(); + await ml.jobWizardCommon.assertJobDetailsSectionExists(); + }); + + it('inputs the job id', async () => { + await ml.jobWizardCommon.assertJobIdInputExists(); + await ml.jobWizardCommon.setJobId(jobId); + await ml.jobWizardCommon.assertJobIdValue(jobId); + }); + + it('inputs the job description', async () => { + const jobDescription = + 'Create single metric job based on the farequote dataset with 30m bucketspan and mean(responsetime)'; + await ml.jobWizardCommon.assertJobDescriptionInputExists(); + await ml.jobWizardCommon.setJobDescription(jobDescription); + await ml.jobWizardCommon.assertJobDescriptionValue(jobDescription); + }); + + it('inputs job groups', async () => { + const jobGroups = ['automated', 'farequote', 'single-metric']; + await ml.jobWizardCommon.assertJobGroupInputExists(); + for (const jobGroup of jobGroups) { + await ml.jobWizardCommon.addJobGroup(jobGroup); + } + await ml.jobWizardCommon.assertJobGroupSelection(jobGroups); + }); + + it('opens the advanced section', async () => { + await ml.jobWizardCommon.ensureAdvancedSectionOpen(); + }); + + it('displays the model plot switch', async () => { + await ml.jobWizardCommon.assertModelPlotSwitchExists(); + }); + + it('enables the dedicated index switch', async () => { + await ml.jobWizardCommon.assertDedicatedIndexSwitchExists(); + await ml.jobWizardCommon.activateDedicatedIndexSwitch(); + await ml.jobWizardCommon.assertDedicatedIndexSwitchCheckedState(true); + }); + + it('inputs the model memory limit', async () => { + const memoryLimit = '15MB'; + await ml.jobWizardCommon.assertModelMemoryLimitInputExists(); + await ml.jobWizardCommon.setModelMemoryLimit(memoryLimit); + await ml.jobWizardCommon.assertModelMemoryLimitValue(memoryLimit); + }); + + it('displays the validation step', async () => { + await ml.jobWizardCommon.clickNextButton(); + await ml.jobWizardCommon.assertValidationSectionExists(); + }); + + it('displays the summary step', async () => { + await ml.jobWizardCommon.clickNextButton(); + await ml.jobWizardCommon.assertSummarySectionExists(); + }); + + it('creates the job and finishes processing', async () => { + await ml.jobWizardCommon.assertCreateJobButtonExists(); + await ml.jobWizardCommon.createJobAndWaitForCompletion(); + }); + + it('displays the created job in the job list', async () => { + await ml.navigation.navigateToMl(); + await ml.navigation.navigateToJobManagement(); + await ml.jobManagement.filterJobsTable(jobId); + const jobRow = await ml.jobManagement.getJobRowByJobId(jobId); + expect(jobRow).to.not.be(null); + }); + }); +} diff --git a/x-pack/test/functional/apps/machine_learning/index.ts b/x-pack/test/functional/apps/machine_learning/index.ts index 3f0e6d0d58142..31f7d67717ae3 100644 --- a/x-pack/test/functional/apps/machine_learning/index.ts +++ b/x-pack/test/functional/apps/machine_learning/index.ts @@ -11,5 +11,6 @@ export default function({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./feature_controls')); loadTestFile(require.resolve('./pages')); + loadTestFile(require.resolve('./create_single_metric_job')); }); } diff --git a/x-pack/test/functional/apps/machine_learning/pages.ts b/x-pack/test/functional/apps/machine_learning/pages.ts index 4d6f6d6f94164..fb085f0661f8c 100644 --- a/x-pack/test/functional/apps/machine_learning/pages.ts +++ b/x-pack/test/functional/apps/machine_learning/pages.ts @@ -21,41 +21,41 @@ export default function({ getService }: FtrProviderContext) { }); it('loads the home page', async () => { - await ml.navigateTo(); + await ml.navigation.navigateToMl(); }); it('loads the job management page', async () => { - await ml.navigateToJobManagement(); - await ml.assertJobStatsBarExists(); - await ml.assertJobTableExists(); - await ml.assertCreateNewJobButtonExists(); + await ml.navigation.navigateToJobManagement(); + await ml.jobManagement.assertJobStatsBarExists(); + await ml.jobManagement.assertJobTableExists(); + await ml.jobManagement.assertCreateNewJobButtonExists(); }); it('loads the anomaly explorer page', async () => { - await ml.navigateToAnomalyExplorert(); - await ml.assertAnomalyExplorerEmptyListMessageExists(); + await ml.navigation.navigateToAnomalyExplorert(); + await ml.anomalyExplorer.assertAnomalyExplorerEmptyListMessageExists(); }); it('loads the single metric viewer page', async () => { - await ml.navigateToSingleMetricViewer(); - await ml.assertSingleMetricViewerEmptyListMessageExsist(); + await ml.navigation.navigateToSingleMetricViewer(); + await ml.singleMetricViewer.assertSingleMetricViewerEmptyListMessageExsist(); }); it('loads the data frame page', async () => { - await ml.navigateToDataFrames(); - await ml.assertDataFrameEmptyListMessageExists(); + await ml.navigation.navigateToDataFrames(); + await ml.dataFrames.assertDataFrameEmptyListMessageExists(); }); it('loads the data visualizer page', async () => { - await ml.navigateToDataVisualizer(); - await ml.assertDataVisualizerImportDataCardExists(); - await ml.assertDataVisualizerIndexDataCardExists(); + await ml.navigation.navigateToDataVisualizer(); + await ml.dataVisualizer.assertDataVisualizerImportDataCardExists(); + await ml.dataVisualizer.assertDataVisualizerIndexDataCardExists(); }); it('loads the settings page', async () => { - await ml.navigateToSettings(); - await ml.assertSettingsCalendarLinkExists(); - await ml.assertSettingsFilterlistLinkExists(); + await ml.navigation.navigateToSettings(); + await ml.settings.assertSettingsCalendarLinkExists(); + await ml.settings.assertSettingsFilterlistLinkExists(); }); }); } diff --git a/x-pack/test/functional/es_archives/ml/farequote/data.json.gz b/x-pack/test/functional/es_archives/ml/farequote/data.json.gz new file mode 100644 index 0000000000000..47e0d55672bfd Binary files /dev/null and b/x-pack/test/functional/es_archives/ml/farequote/data.json.gz differ diff --git a/x-pack/test/functional/es_archives/ml/farequote/mappings.json b/x-pack/test/functional/es_archives/ml/farequote/mappings.json new file mode 100644 index 0000000000000..b4c6be7655805 --- /dev/null +++ b/x-pack/test/functional/es_archives/ml/farequote/mappings.json @@ -0,0 +1,1105 @@ +{ + "type": "index", + "value": { + "aliases": { + }, + "index": "farequote", + "mappings": { + "properties": { + "@timestamp": { + "type": "date" + }, + "@version": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + }, + "airline": { + "type": "keyword" + }, + "host": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + }, + "path": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + }, + "responsetime": { + "type": "float" + }, + "sourcetype": { + "type": "keyword" + }, + "type": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + } + } + }, + "settings": { + "index": { + "number_of_replicas": "1", + "number_of_shards": "1" + } + } + } +} + +{ + "type": "index", + "value": { + "aliases": { + ".kibana": { + } + }, + "index": ".kibana_1", + "mappings": { + "_meta": { + "migrationMappingPropertyHashes": { + "action": "ecc01e367a369542bc2b15dae1fb1773", + "alert": "66fb7d877c9f102755357c30c7b98e02", + "apm-telemetry": "07ee1939fa4302c62ddc052ec03fed90", + "canvas-element": "7390014e1091044523666d97247392fc", + "canvas-workpad": "b0a1706d356228dbdcb4a17e6b9eb231", + "config": "87aca8fdb053154f11383fce3dbf3edf", + "dashboard": "d00f614b29a80360e1190193fd333bab", + "file-upload-telemetry": "0ed4d3e1983d1217a30982630897092e", + "graph-workspace": "cd7ba1330e6682e9cc00b78850874be1", + "index-pattern": "66eccb05066c5a89924f48a9e9736499", + "infrastructure-ui-source": "ddc0ecb18383f6b26101a2fadb2dab0c", + "kql-telemetry": "d12a98a6f19a2d273696597547e064ee", + "map": "23d7aa4a720d4938ccde3983f87bd58d", + "maps-telemetry": "a4229f8b16a6820c6d724b7e0c1f729d", + "migrationVersion": "4a1746014a75ade3a714e1db5763276f", + "ml-telemetry": "257fd1d4b4fdbb9cb4b8a3b27da201e9", + "namespace": "2f4316de49999235636386fe51dc06c1", + "references": "7997cf5a56cc02bdc9c93361bde732b0", + "sample-data-telemetry": "7d3cfeb915303c9641c59681967ffeb4", + "search": "181661168bbadd1eff5902361e2a0d5c", + "server": "ec97f1c5da1a19609a60874e5af1100c", + "siem-ui-timeline": "1f6f0860ad7bc0dba3e42467ca40470d", + "siem-ui-timeline-note": "8874706eedc49059d4cf0f5094559084", + "siem-ui-timeline-pinned-event": "20638091112f0e14f0e443d512301c29", + "space": "25de8c2deec044392922989cfcf24c54", + "telemetry": "e1c8bc94e443aefd9458932cc0697a4d", + "timelion-sheet": "9a2a2748877c7a7b582fef201ab1d4cf", + "type": "2f4316de49999235636386fe51dc06c1", + "ui-metric": "0d409297dc5ebe1e3a1da691c6ee32e3", + "updated_at": "00da57df13e94e9d98437d13ace4bfe0", + "upgrade-assistant-reindex-operation": "a53a20fe086b72c9a86da3cc12dad8a6", + "upgrade-assistant-telemetry": "56702cec857e0a9dacfb696655b4ff7b", + "url": "c7f66a0df8b1b52f17c28c4adb111105", + "visualization": "52d7a13ad68a150c4525b292d23e12cc" + } + }, + "dynamic": "strict", + "properties": { + "action": { + "properties": { + "actionTypeId": { + "type": "keyword" + }, + "config": { + "enabled": false, + "type": "object" + }, + "description": { + "type": "text" + }, + "secrets": { + "type": "binary" + } + } + }, + "alert": { + "properties": { + "actions": { + "properties": { + "actionRef": { + "type": "keyword" + }, + "group": { + "type": "keyword" + }, + "params": { + "enabled": false, + "type": "object" + } + }, + "type": "nested" + }, + "alertTypeId": { + "type": "keyword" + }, + "alertTypeParams": { + "enabled": false, + "type": "object" + }, + "enabled": { + "type": "boolean" + }, + "interval": { + "type": "keyword" + }, + "scheduledTaskId": { + "type": "keyword" + } + } + }, + "apm-telemetry": { + "properties": { + "has_any_services": { + "type": "boolean" + }, + "services_per_agent": { + "properties": { + "dotnet": { + "null_value": 0, + "type": "long" + }, + "go": { + "null_value": 0, + "type": "long" + }, + "java": { + "null_value": 0, + "type": "long" + }, + "js-base": { + "null_value": 0, + "type": "long" + }, + "nodejs": { + "null_value": 0, + "type": "long" + }, + "python": { + "null_value": 0, + "type": "long" + }, + "ruby": { + "null_value": 0, + "type": "long" + }, + "rum-js": { + "null_value": 0, + "type": "long" + } + } + } + } + }, + "canvas-element": { + "dynamic": "false", + "properties": { + "@created": { + "type": "date" + }, + "@timestamp": { + "type": "date" + }, + "content": { + "type": "text" + }, + "help": { + "type": "text" + }, + "image": { + "type": "text" + }, + "name": { + "fields": { + "keyword": { + "type": "keyword" + } + }, + "type": "text" + } + } + }, + "canvas-workpad": { + "dynamic": "false", + "properties": { + "@created": { + "type": "date" + }, + "@timestamp": { + "type": "date" + }, + "name": { + "fields": { + "keyword": { + "type": "keyword" + } + }, + "type": "text" + } + } + }, + "config": { + "dynamic": "true", + "properties": { + "buildNum": { + "type": "keyword" + }, + "dateFormat:tz": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + }, + "defaultIndex": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + } + } + }, + "dashboard": { + "properties": { + "description": { + "type": "text" + }, + "hits": { + "type": "integer" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "type": "text" + } + } + }, + "optionsJSON": { + "type": "text" + }, + "panelsJSON": { + "type": "text" + }, + "refreshInterval": { + "properties": { + "display": { + "type": "keyword" + }, + "pause": { + "type": "boolean" + }, + "section": { + "type": "integer" + }, + "value": { + "type": "integer" + } + } + }, + "timeFrom": { + "type": "keyword" + }, + "timeRestore": { + "type": "boolean" + }, + "timeTo": { + "type": "keyword" + }, + "title": { + "type": "text" + }, + "version": { + "type": "integer" + } + } + }, + "file-upload-telemetry": { + "properties": { + "filesUploadedTotalCount": { + "type": "long" + } + } + }, + "graph-workspace": { + "properties": { + "description": { + "type": "text" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "type": "text" + } + } + }, + "numLinks": { + "type": "integer" + }, + "numVertices": { + "type": "integer" + }, + "title": { + "type": "text" + }, + "version": { + "type": "integer" + }, + "wsState": { + "type": "text" + } + } + }, + "index-pattern": { + "properties": { + "fieldFormatMap": { + "type": "text" + }, + "fields": { + "type": "text" + }, + "intervalName": { + "type": "keyword" + }, + "notExpandable": { + "type": "boolean" + }, + "sourceFilters": { + "type": "text" + }, + "timeFieldName": { + "type": "keyword" + }, + "title": { + "type": "text" + }, + "type": { + "type": "keyword" + }, + "typeMeta": { + "type": "keyword" + } + } + }, + "infrastructure-ui-source": { + "properties": { + "description": { + "type": "text" + }, + "fields": { + "properties": { + "container": { + "type": "keyword" + }, + "host": { + "type": "keyword" + }, + "pod": { + "type": "keyword" + }, + "tiebreaker": { + "type": "keyword" + }, + "timestamp": { + "type": "keyword" + } + } + }, + "logAlias": { + "type": "keyword" + }, + "logColumns": { + "properties": { + "fieldColumn": { + "properties": { + "field": { + "type": "keyword" + }, + "id": { + "type": "keyword" + } + } + }, + "messageColumn": { + "properties": { + "id": { + "type": "keyword" + } + } + }, + "timestampColumn": { + "properties": { + "id": { + "type": "keyword" + } + } + } + }, + "type": "nested" + }, + "metricAlias": { + "type": "keyword" + }, + "name": { + "type": "text" + } + } + }, + "kql-telemetry": { + "properties": { + "optInCount": { + "type": "long" + }, + "optOutCount": { + "type": "long" + } + } + }, + "map": { + "properties": { + "bounds": { + "type": "geo_shape" + }, + "description": { + "type": "text" + }, + "layerListJSON": { + "type": "text" + }, + "mapStateJSON": { + "type": "text" + }, + "title": { + "type": "text" + }, + "uiStateJSON": { + "type": "text" + }, + "version": { + "type": "integer" + } + } + }, + "maps-telemetry": { + "properties": { + "attributesPerMap": { + "properties": { + "dataSourcesCount": { + "properties": { + "avg": { + "type": "long" + }, + "max": { + "type": "long" + }, + "min": { + "type": "long" + } + } + }, + "emsVectorLayersCount": { + "dynamic": "true", + "type": "object" + }, + "layerTypesCount": { + "dynamic": "true", + "type": "object" + }, + "layersCount": { + "properties": { + "avg": { + "type": "long" + }, + "max": { + "type": "long" + }, + "min": { + "type": "long" + } + } + } + } + }, + "mapsTotalCount": { + "type": "long" + }, + "timeCaptured": { + "type": "date" + } + } + }, + "migrationVersion": { + "dynamic": "true", + "properties": { + "index-pattern": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + }, + "space": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + } + } + }, + "ml-telemetry": { + "properties": { + "file_data_visualizer": { + "properties": { + "index_creation_count": { + "type": "long" + } + } + } + } + }, + "namespace": { + "type": "keyword" + }, + "references": { + "properties": { + "id": { + "type": "keyword" + }, + "name": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + }, + "type": "nested" + }, + "sample-data-telemetry": { + "properties": { + "installCount": { + "type": "long" + }, + "unInstallCount": { + "type": "long" + } + } + }, + "search": { + "properties": { + "columns": { + "type": "keyword" + }, + "description": { + "type": "text" + }, + "hits": { + "type": "integer" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "type": "text" + } + } + }, + "sort": { + "type": "keyword" + }, + "title": { + "type": "text" + }, + "version": { + "type": "integer" + } + } + }, + "server": { + "properties": { + "uuid": { + "type": "keyword" + } + } + }, + "siem-ui-timeline": { + "properties": { + "columns": { + "properties": { + "aggregatable": { + "type": "boolean" + }, + "category": { + "type": "keyword" + }, + "columnHeaderType": { + "type": "keyword" + }, + "description": { + "type": "text" + }, + "example": { + "type": "text" + }, + "id": { + "type": "keyword" + }, + "indexes": { + "type": "keyword" + }, + "name": { + "type": "text" + }, + "placeholder": { + "type": "text" + }, + "searchable": { + "type": "boolean" + }, + "type": { + "type": "keyword" + } + } + }, + "created": { + "type": "date" + }, + "createdBy": { + "type": "text" + }, + "dataProviders": { + "properties": { + "and": { + "properties": { + "enabled": { + "type": "boolean" + }, + "excluded": { + "type": "boolean" + }, + "id": { + "type": "keyword" + }, + "kqlQuery": { + "type": "text" + }, + "name": { + "type": "text" + }, + "queryMatch": { + "properties": { + "displayField": { + "type": "text" + }, + "displayValue": { + "type": "text" + }, + "field": { + "type": "text" + }, + "operator": { + "type": "text" + }, + "value": { + "type": "text" + } + } + } + } + }, + "enabled": { + "type": "boolean" + }, + "excluded": { + "type": "boolean" + }, + "id": { + "type": "keyword" + }, + "kqlQuery": { + "type": "text" + }, + "name": { + "type": "text" + }, + "queryMatch": { + "properties": { + "displayField": { + "type": "text" + }, + "displayValue": { + "type": "text" + }, + "field": { + "type": "text" + }, + "operator": { + "type": "text" + }, + "value": { + "type": "text" + } + } + } + } + }, + "dateRange": { + "properties": { + "end": { + "type": "date" + }, + "start": { + "type": "date" + } + } + }, + "description": { + "type": "text" + }, + "favorite": { + "properties": { + "favoriteDate": { + "type": "date" + }, + "fullName": { + "type": "text" + }, + "keySearch": { + "type": "text" + }, + "userName": { + "type": "text" + } + } + }, + "kqlMode": { + "type": "keyword" + }, + "kqlQuery": { + "properties": { + "filterQuery": { + "properties": { + "kuery": { + "properties": { + "expression": { + "type": "text" + }, + "kind": { + "type": "keyword" + } + } + }, + "serializedQuery": { + "type": "text" + } + } + } + } + }, + "sort": { + "properties": { + "columnId": { + "type": "keyword" + }, + "sortDirection": { + "type": "keyword" + } + } + }, + "title": { + "type": "text" + }, + "updated": { + "type": "date" + }, + "updatedBy": { + "type": "text" + } + } + }, + "siem-ui-timeline-note": { + "properties": { + "created": { + "type": "date" + }, + "createdBy": { + "type": "text" + }, + "eventId": { + "type": "keyword" + }, + "note": { + "type": "text" + }, + "timelineId": { + "type": "keyword" + }, + "updated": { + "type": "date" + }, + "updatedBy": { + "type": "text" + } + } + }, + "siem-ui-timeline-pinned-event": { + "properties": { + "created": { + "type": "date" + }, + "createdBy": { + "type": "text" + }, + "eventId": { + "type": "keyword" + }, + "timelineId": { + "type": "keyword" + }, + "updated": { + "type": "date" + }, + "updatedBy": { + "type": "text" + } + } + }, + "space": { + "properties": { + "_reserved": { + "type": "boolean" + }, + "color": { + "type": "keyword" + }, + "description": { + "type": "text" + }, + "disabledFeatures": { + "type": "keyword" + }, + "initials": { + "type": "keyword" + }, + "name": { + "fields": { + "keyword": { + "ignore_above": 2048, + "type": "keyword" + } + }, + "type": "text" + } + } + }, + "telemetry": { + "properties": { + "enabled": { + "type": "boolean" + } + } + }, + "timelion-sheet": { + "properties": { + "description": { + "type": "text" + }, + "hits": { + "type": "integer" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "type": "text" + } + } + }, + "timelion_chart_height": { + "type": "integer" + }, + "timelion_columns": { + "type": "integer" + }, + "timelion_interval": { + "type": "keyword" + }, + "timelion_other_interval": { + "type": "keyword" + }, + "timelion_rows": { + "type": "integer" + }, + "timelion_sheet": { + "type": "text" + }, + "title": { + "type": "text" + }, + "version": { + "type": "integer" + } + } + }, + "type": { + "type": "keyword" + }, + "ui-metric": { + "properties": { + "count": { + "type": "integer" + } + } + }, + "updated_at": { + "type": "date" + }, + "upgrade-assistant-reindex-operation": { + "dynamic": "true", + "properties": { + "indexName": { + "type": "keyword" + }, + "status": { + "type": "integer" + } + } + }, + "upgrade-assistant-telemetry": { + "properties": { + "features": { + "properties": { + "deprecation_logging": { + "properties": { + "enabled": { + "null_value": true, + "type": "boolean" + } + } + } + } + }, + "ui_open": { + "properties": { + "cluster": { + "null_value": 0, + "type": "long" + }, + "indices": { + "null_value": 0, + "type": "long" + }, + "overview": { + "null_value": 0, + "type": "long" + } + } + }, + "ui_reindex": { + "properties": { + "close": { + "null_value": 0, + "type": "long" + }, + "open": { + "null_value": 0, + "type": "long" + }, + "start": { + "null_value": 0, + "type": "long" + }, + "stop": { + "null_value": 0, + "type": "long" + } + } + } + } + }, + "url": { + "properties": { + "accessCount": { + "type": "long" + }, + "accessDate": { + "type": "date" + }, + "createDate": { + "type": "date" + }, + "url": { + "fields": { + "keyword": { + "ignore_above": 2048, + "type": "keyword" + } + }, + "type": "text" + } + } + }, + "visualization": { + "properties": { + "description": { + "type": "text" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "type": "text" + } + } + }, + "savedSearchRefName": { + "type": "keyword" + }, + "title": { + "type": "text" + }, + "uiStateJSON": { + "type": "text" + }, + "version": { + "type": "integer" + }, + "visState": { + "type": "text" + } + } + } + } + }, + "settings": { + "index": { + "auto_expand_replicas": "0-1", + "number_of_replicas": "0", + "number_of_shards": "1" + } + } + } +} \ No newline at end of file diff --git a/x-pack/test/functional/services/index.ts b/x-pack/test/functional/services/index.ts index 1f509315d7fdc..9a256b3bbb141 100644 --- a/x-pack/test/functional/services/index.ts +++ b/x-pack/test/functional/services/index.ts @@ -46,7 +46,7 @@ import { UserMenuProvider } from './user_menu'; import { UptimeProvider } from './uptime'; import { InfraSourceConfigurationFormProvider } from './infra_source_configuration_form'; import { InfraLogStreamProvider } from './infra_log_stream'; -import { MachineLearningProvider } from './machine_learning'; +import { MachineLearningProvider } from './ml'; import { SecurityServiceProvider, SpacesServiceProvider } from '../../common/services'; diff --git a/x-pack/test/functional/services/machine_learning.ts b/x-pack/test/functional/services/machine_learning.ts deleted file mode 100644 index 1e62d1bb61265..0000000000000 --- a/x-pack/test/functional/services/machine_learning.ts +++ /dev/null @@ -1,88 +0,0 @@ -/* - * 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 { FtrProviderContext } from '../ftr_provider_context'; - -export function MachineLearningProvider({ getService, getPageObjects }: FtrProviderContext) { - const testSubjects = getService('testSubjects'); - const PageObjects = getPageObjects(['common']); - - return { - async navigateTo() { - return await PageObjects.common.navigateToApp('ml'); - }, - - async navigateToJobManagement() { - await testSubjects.click('mlTabJobManagement'); - await testSubjects.exists('mlPageJobManagement'); - }, - - async navigateToAnomalyExplorert() { - await testSubjects.click('mlTabAnomalyExplorer'); - await testSubjects.exists('mlPageAnomalyExplorer'); - }, - - async navigateToSingleMetricViewer() { - await testSubjects.click('mlTabSingleMetricViewer'); - await testSubjects.exists('mlPageSingleMetricViewer'); - }, - - async navigateToDataFrames() { - await testSubjects.click('mlTabDataFrames'); - await testSubjects.exists('mlPageDataFrame'); - }, - - async navigateToDataVisualizer() { - await testSubjects.click('mlTabDataVisualizer'); - await testSubjects.exists('mlPageDataVisualizerSelector'); - }, - - async navigateToSettings() { - await testSubjects.click('mlTabSettings'); - await testSubjects.exists('mlPageSettings'); - }, - - async assertJobTableExists() { - await testSubjects.existOrFail('mlJobListTable'); - }, - - async assertCreateNewJobButtonExists() { - await testSubjects.existOrFail('mlCreateNewJobButton'); - }, - - async assertJobStatsBarExists() { - await testSubjects.existOrFail('mlJobStatsBar'); - }, - - async assertAnomalyExplorerEmptyListMessageExists() { - await testSubjects.existOrFail('mlNoJobsFound'); - }, - - async assertSingleMetricViewerEmptyListMessageExsist() { - await testSubjects.existOrFail('mlNoSingleMetricJobsFound'); - }, - - async assertDataFrameEmptyListMessageExists() { - await testSubjects.existOrFail('mlNoDataFrameTransformsFound'); - }, - - async assertDataVisualizerImportDataCardExists() { - await testSubjects.existOrFail('mlDataVisualizerCardImportData'); - }, - - async assertDataVisualizerIndexDataCardExists() { - await testSubjects.existOrFail('mlDataVisualizerCardIndexData'); - }, - - async assertSettingsCalendarLinkExists() { - await testSubjects.existOrFail('ml_calendar_mng_button'); - }, - - async assertSettingsFilterlistLinkExists() { - await testSubjects.existOrFail('ml_filter_lists_button'); - }, - }; -} diff --git a/x-pack/test/functional/services/machine_learning/anomaly_explorer.ts b/x-pack/test/functional/services/machine_learning/anomaly_explorer.ts new file mode 100644 index 0000000000000..18a185cbf253b --- /dev/null +++ b/x-pack/test/functional/services/machine_learning/anomaly_explorer.ts @@ -0,0 +1,17 @@ +/* + * 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 { FtrProviderContext } from '../../ftr_provider_context'; + +export function MachineLearningAnomalyExplorerProvider({ getService }: FtrProviderContext) { + const testSubjects = getService('testSubjects'); + + return { + async assertAnomalyExplorerEmptyListMessageExists() { + await testSubjects.existOrFail('mlNoJobsFound'); + }, + }; +} diff --git a/x-pack/test/functional/services/machine_learning/api.ts b/x-pack/test/functional/services/machine_learning/api.ts new file mode 100644 index 0000000000000..a82f37485dc4f --- /dev/null +++ b/x-pack/test/functional/services/machine_learning/api.ts @@ -0,0 +1,47 @@ +/* + * 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 expect from '@kbn/expect'; + +import { isEmpty } from 'lodash'; +import { FtrProviderContext } from '../../ftr_provider_context'; + +export function MachineLearningAPIProvider({ getService }: FtrProviderContext) { + const es = getService('es'); + const log = getService('log'); + const retry = getService('retry'); + + return { + async deleteIndices(indices: string) { + log.debug(`Deleting indices: '${indices}'...`); + const deleteResponse = await es.indices.delete({ + index: indices, + }); + expect(deleteResponse) + .to.have.property('acknowledged') + .eql(true); + + await retry.waitForWithTimeout(`'${indices}' indices to be deleted`, 30 * 1000, async () => { + const getRepsonse = await es.indices.get({ + index: indices, + }); + + if (isEmpty(getRepsonse)) { + return true; + } else { + throw new Error(`expected indices '${indices}' to be deleted`); + } + }); + }, + + async cleanMlIndices() { + await this.deleteIndices('.ml-*'); + }, + + async cleanDataframeIndices() { + await this.deleteIndices('.data-frame-*'); + }, + }; +} diff --git a/x-pack/test/functional/services/machine_learning/data_frames.ts b/x-pack/test/functional/services/machine_learning/data_frames.ts new file mode 100644 index 0000000000000..c0fe7eab65f3e --- /dev/null +++ b/x-pack/test/functional/services/machine_learning/data_frames.ts @@ -0,0 +1,17 @@ +/* + * 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 { FtrProviderContext } from '../../ftr_provider_context'; + +export function MachineLearningDataFramesProvider({ getService }: FtrProviderContext) { + const testSubjects = getService('testSubjects'); + + return { + async assertDataFrameEmptyListMessageExists() { + await testSubjects.existOrFail('mlNoDataFrameTransformsFound'); + }, + }; +} diff --git a/x-pack/test/functional/services/machine_learning/data_visualizer.ts b/x-pack/test/functional/services/machine_learning/data_visualizer.ts new file mode 100644 index 0000000000000..5eec624a985aa --- /dev/null +++ b/x-pack/test/functional/services/machine_learning/data_visualizer.ts @@ -0,0 +1,21 @@ +/* + * 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 { FtrProviderContext } from '../../ftr_provider_context'; + +export function MachineLearningDataVisualizerProvider({ getService }: FtrProviderContext) { + const testSubjects = getService('testSubjects'); + + return { + async assertDataVisualizerImportDataCardExists() { + await testSubjects.existOrFail('mlDataVisualizerCardImportData'); + }, + + async assertDataVisualizerIndexDataCardExists() { + await testSubjects.existOrFail('mlDataVisualizerCardIndexData'); + }, + }; +} diff --git a/x-pack/test/functional/services/machine_learning/index.ts b/x-pack/test/functional/services/machine_learning/index.ts new file mode 100644 index 0000000000000..d2e387d5ea1ba --- /dev/null +++ b/x-pack/test/functional/services/machine_learning/index.ts @@ -0,0 +1,17 @@ +/* + * 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 { MachineLearningAnomalyExplorerProvider } from './anomaly_explorer'; +export { MachineLearningAPIProvider } from './api'; +export { MachineLearningDataFramesProvider } from './data_frames'; +export { MachineLearningDataVisualizerProvider } from './data_visualizer'; +export { MachineLearningJobManagementProvider } from './job_management'; +export { MachineLearningJobSourceSelectionProvider } from './job_source_selection'; +export { MachineLearningJobTypeSelectionProvider } from './job_type_selection'; +export { MachineLearningJobWizardCommonProvider } from './job_wizard_common'; +export { MachineLearningNavigationProvider } from './navigation'; +export { MachineLearningSettingsProvider } from './settings'; +export { MachineLearningSingleMetricViewerProvider } from './single_metric_viewer'; diff --git a/x-pack/test/functional/services/machine_learning/job_management.ts b/x-pack/test/functional/services/machine_learning/job_management.ts new file mode 100644 index 0000000000000..dcec09def1221 --- /dev/null +++ b/x-pack/test/functional/services/machine_learning/job_management.ts @@ -0,0 +1,74 @@ +/* + * 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 { FtrProviderContext } from '../../ftr_provider_context'; +import { WebElementWrapper } from '../../../../../test/functional/services/lib/web_element_wrapper'; + +export function MachineLearningJobManagementProvider({ getService }: FtrProviderContext) { + const testSubjects = getService('testSubjects'); + const retry = getService('retry'); + + return { + async getJobsTable(): Promise { + const tableContainer = await testSubjects.find('mlJobListTable'); + return await tableContainer.findByTagName('table'); + }, + + async isJobsTableLoadingIndicatorDisplayed(): Promise { + const mlJobListTable = await testSubjects.find('mlJobListTable'); + const innerText = await mlJobListTable.getVisibleText(); + return innerText.includes('Loading jobs...'); + }, + + async isNoItemsFoundMessageDisplayed(): Promise { + const mlJobListTable = await testSubjects.find('mlJobListTable'); + const innerText = await mlJobListTable.getVisibleText(); + return innerText.includes('No jobs found'); + }, + + async navigateToNewJobSourceSelection() { + await testSubjects.clickWhenNotDisabled('mlCreateNewJobButton'); + await testSubjects.existOrFail('mlPageSourceSelection'); + }, + + async assertJobTableExists() { + await testSubjects.existOrFail('mlJobListTable'); + }, + + async assertCreateNewJobButtonExists() { + await testSubjects.existOrFail('mlCreateNewJobButton'); + }, + + async assertJobStatsBarExists() { + await testSubjects.existOrFail('mlJobStatsBar'); + }, + + async waitForJobsTableToLoad() { + await retry.waitFor( + 'jobs table to exist', + async () => await testSubjects.exists('mlJobListTable') + ); + + await retry.waitFor( + 'jobs table loading indicator to be invisible', + async () => (await this.isJobsTableLoadingIndicatorDisplayed()) === false + ); + }, + + async filterJobsTable(jobId: string) { + await this.waitForJobsTableToLoad(); + const searchBar = await testSubjects.find('mlJobListSearchBar'); + const searchBarInput = await searchBar.findByTagName('input'); + await searchBarInput.clearValueWithKeyboard(); + await searchBarInput.type(jobId); + }, + + async getJobRowByJobId(jobId: string): Promise { + const table = await this.getJobsTable(); + return await table.findByCssSelector(`[data-row-id=${jobId}]`); + }, + }; +} diff --git a/x-pack/test/functional/services/machine_learning/job_source_selection.ts b/x-pack/test/functional/services/machine_learning/job_source_selection.ts new file mode 100644 index 0000000000000..89fae06fd33ce --- /dev/null +++ b/x-pack/test/functional/services/machine_learning/job_source_selection.ts @@ -0,0 +1,19 @@ +/* + * 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 { FtrProviderContext } from '../../ftr_provider_context'; + +export function MachineLearningJobSourceSelectionProvider({ getService }: FtrProviderContext) { + const testSubjects = getService('testSubjects'); + + return { + async selectSourceIndexPattern(indexPattern: string) { + const subj = 'paginatedListItem-' + indexPattern; + await testSubjects.clickWhenNotDisabled(subj); + await testSubjects.existOrFail('mlPageJobTypeSelection'); + }, + }; +} diff --git a/x-pack/test/functional/services/machine_learning/job_type_selection.ts b/x-pack/test/functional/services/machine_learning/job_type_selection.ts new file mode 100644 index 0000000000000..b6a6e613d075c --- /dev/null +++ b/x-pack/test/functional/services/machine_learning/job_type_selection.ts @@ -0,0 +1,18 @@ +/* + * 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 { FtrProviderContext } from '../../ftr_provider_context'; + +export function MachineLearningJobTypeSelectionProvider({ getService }: FtrProviderContext) { + const testSubjects = getService('testSubjects'); + + return { + async selectSingleMetricJob() { + await testSubjects.clickWhenNotDisabled('mlJobTypeLinkSingleMetricJob'); + await testSubjects.existOrFail('mlPageJobWizard'); + }, + }; +} diff --git a/x-pack/test/functional/services/machine_learning/job_wizard_common.ts b/x-pack/test/functional/services/machine_learning/job_wizard_common.ts new file mode 100644 index 0000000000000..eea5e2a606d0c --- /dev/null +++ b/x-pack/test/functional/services/machine_learning/job_wizard_common.ts @@ -0,0 +1,202 @@ +/* + * 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 expect from '@kbn/expect'; + +import { FtrProviderContext } from '../../ftr_provider_context'; + +export function MachineLearningJobWizardCommonProvider({ getService }: FtrProviderContext) { + const comboBox = getService('comboBox'); + const retry = getService('retry'); + const testSubjects = getService('testSubjects'); + + return { + async assertTimeRangeSectionExists() { + await testSubjects.existOrFail('mlJobWizardStepTitleTimeRange'); + }, + + async assertPickFieldsSectionExists() { + await testSubjects.existOrFail('mlJobWizardStepTitlePickFields'); + }, + + async assertJobDetailsSectionExists() { + await testSubjects.existOrFail('mlJobWizardStepTitleJobDetails'); + }, + + async assertValidationSectionExists() { + await testSubjects.existOrFail('mlJobWizardStepTitleValidation'); + }, + + async assertSummarySectionExists() { + await testSubjects.existOrFail('mlJobWizardStepTitleSummary'); + }, + + async assertEventRateChartExists() { + await testSubjects.existOrFail('mlEventRateChart'); + }, + + async assertAggAndFieldInputExists() { + await testSubjects.existOrFail('mlJobWizardAggSelection comboBoxInput'); + }, + + async assertAggAndFieldSelection(identifier: string) { + const comboBoxSelectedOptions = await comboBox.getComboBoxSelectedOptions( + 'mlJobWizardAggSelection comboBoxInput' + ); + expect(comboBoxSelectedOptions.length).to.eql(1); + expect(comboBoxSelectedOptions[0]).to.eql(identifier); + }, + + async assertBucketSpanInputExists() { + await testSubjects.existOrFail('mlJobWizardInputBucketSpan'); + }, + + async assertBucketSpanValue(expectedValue: string) { + const actualBucketSpan = await testSubjects.getAttribute( + 'mlJobWizardInputBucketSpan', + 'value' + ); + expect(actualBucketSpan).to.eql(expectedValue); + }, + + async assertJobIdInputExists() { + await testSubjects.existOrFail('mlJobWizardInputJobId'); + }, + + async assertJobIdValue(expectedValue: string) { + const actualJobId = await testSubjects.getAttribute('mlJobWizardInputJobId', 'value'); + expect(actualJobId).to.eql(expectedValue); + }, + + async assertJobDescriptionInputExists() { + await testSubjects.existOrFail('mlJobWizardInputJobDescription'); + }, + + async assertJobDescriptionValue(expectedValue: string) { + const actualJobDescription = await testSubjects.getVisibleText( + 'mlJobWizardInputJobDescription' + ); + expect(actualJobDescription).to.eql(expectedValue); + }, + + async assertJobGroupInputExists() { + await testSubjects.existOrFail('mlJobWizardComboBoxJobGroups comboBoxInput'); + }, + + async assertJobGroupSelection(jobGroups: string[]) { + const comboBoxSelectedOptions = await comboBox.getComboBoxSelectedOptions( + 'mlJobWizardComboBoxJobGroups comboBoxInput' + ); + expect(comboBoxSelectedOptions.length).to.eql(jobGroups.length); + expect(comboBoxSelectedOptions).to.eql(jobGroups); + }, + + async assertModelPlotSwitchExists() { + await this.ensureAdvancedSectionOpen(); + await testSubjects.existOrFail('mlJobWizardAdvancedSection mlJobWizardSwitchModelPlot', { + allowHidden: true, + }); + }, + + async assertDedicatedIndexSwitchExists() { + await this.ensureAdvancedSectionOpen(); + await testSubjects.existOrFail( + 'mlJobWizardAdvancedSection mlJobWizardSwitchUseDedicatedIndex', + { allowHidden: true } + ); + }, + + async assertDedicatedIndexSwitchCheckedState(expectedValue: boolean) { + await this.ensureAdvancedSectionOpen(); + const actualCheckedState = await this.getDedicatedIndexSwitchCheckedState(); + expect(actualCheckedState).to.eql(expectedValue); + }, + + async assertCreateJobButtonExists() { + await testSubjects.existOrFail('mlJobWizardButtonCreateJob'); + }, + + async getDedicatedIndexSwitchCheckedState() { + await this.ensureAdvancedSectionOpen(); + return await testSubjects.isSelected( + 'mlJobWizardAdvancedSection mlJobWizardSwitchUseDedicatedIndex' + ); + }, + + async assertModelMemoryLimitInputExists() { + await this.ensureAdvancedSectionOpen(); + await testSubjects.existOrFail('mlJobWizardAdvancedSection mlJobWizardInputModelMemoryLimit'); + }, + + async assertModelMemoryLimitValue(expectedValue: string) { + await this.ensureAdvancedSectionOpen(); + const actualModelMemoryLimit = await testSubjects.getAttribute( + 'mlJobWizardAdvancedSection mlJobWizardInputModelMemoryLimit', + 'value' + ); + expect(actualModelMemoryLimit).to.eql(expectedValue); + }, + + async clickNextButton() { + await testSubjects.clickWhenNotDisabled('mlJobWizardNavButtonNext'); + }, + + async clickPreviousButton() { + await testSubjects.clickWhenNotDisabled('mlJobWizardNavButtonPrevious'); + }, + + async clickUseFullDataButton() { + await testSubjects.clickWhenNotDisabled('mlButtonUseFullData'); + }, + + async selectAggAndField(identifier: string) { + await comboBox.set('mlJobWizardAggSelection comboBoxInput', identifier); + }, + + async setBucketSpan(bucketSpan: string) { + await testSubjects.setValue('mlJobWizardInputBucketSpan', bucketSpan); + }, + + async setJobId(jobId: string) { + await testSubjects.setValue('mlJobWizardInputJobId', jobId); + }, + + async setJobDescription(jobDescription: string) { + await testSubjects.setValue('mlJobWizardInputJobDescription', jobDescription); + }, + + async addJobGroup(jobGroup: string) { + await comboBox.setCustom('mlJobWizardComboBoxJobGroups comboBoxInput', jobGroup); + }, + + async ensureAdvancedSectionOpen() { + await retry.try(async () => { + if ((await testSubjects.exists('mlJobWizardAdvancedSection')) === false) { + await testSubjects.click('mlJobWizardToggleAdvancedSection'); + await testSubjects.existOrFail('mlJobWizardAdvancedSection'); + } + }); + }, + + async activateDedicatedIndexSwitch() { + if ((await this.getDedicatedIndexSwitchCheckedState()) === false) { + await testSubjects.clickWhenNotDisabled('mlJobWizardSwitchUseDedicatedIndex'); + } + }, + + async setModelMemoryLimit(modelMemoryLimit: string) { + await testSubjects.setValue('mlJobWizardInputModelMemoryLimit', modelMemoryLimit); + }, + + async createJobAndWaitForCompletion() { + await testSubjects.clickWhenNotDisabled('mlJobWizardButtonCreateJob'); + await retry.waitForWithTimeout( + 'job processing to finish', + 5 * 60 * 1000, + async () => (await testSubjects.exists('mlJobWizardButtonCreateJob')) === false + ); + }, + }; +} diff --git a/x-pack/test/functional/services/machine_learning/navigation.ts b/x-pack/test/functional/services/machine_learning/navigation.ts new file mode 100644 index 0000000000000..12f97712ccb11 --- /dev/null +++ b/x-pack/test/functional/services/machine_learning/navigation.ts @@ -0,0 +1,55 @@ +/* + * 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 { FtrProviderContext } from '../../ftr_provider_context'; + +export function MachineLearningNavigationProvider({ + getService, + getPageObjects, +}: FtrProviderContext) { + const retry = getService('retry'); + const testSubjects = getService('testSubjects'); + const PageObjects = getPageObjects(['common']); + + return { + async navigateToMl() { + return await PageObjects.common.navigateToApp('ml'); + }, + + async navigateToArea(linkSubject: string, pageSubject: string) { + await retry.try(async () => { + if ((await testSubjects.exists(pageSubject)) === false) { + await testSubjects.click(linkSubject); + await testSubjects.existOrFail(pageSubject); + } + }); + }, + + async navigateToJobManagement() { + await this.navigateToArea('mlTabJobManagement', 'mlPageJobManagement'); + }, + + async navigateToAnomalyExplorert() { + await this.navigateToArea('mlTabAnomalyExplorer', 'mlPageAnomalyExplorer'); + }, + + async navigateToSingleMetricViewer() { + await this.navigateToArea('mlTabSingleMetricViewer', 'mlPageSingleMetricViewer'); + }, + + async navigateToDataFrames() { + await this.navigateToArea('mlTabDataFrames', 'mlPageDataFrame'); + }, + + async navigateToDataVisualizer() { + await this.navigateToArea('mlTabDataVisualizer', 'mlPageDataVisualizerSelector'); + }, + + async navigateToSettings() { + await this.navigateToArea('mlTabSettings', 'mlPageSettings'); + }, + }; +} diff --git a/x-pack/test/functional/services/machine_learning/settings.ts b/x-pack/test/functional/services/machine_learning/settings.ts new file mode 100644 index 0000000000000..15a3131e7c969 --- /dev/null +++ b/x-pack/test/functional/services/machine_learning/settings.ts @@ -0,0 +1,21 @@ +/* + * 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 { FtrProviderContext } from '../../ftr_provider_context'; + +export function MachineLearningSettingsProvider({ getService }: FtrProviderContext) { + const testSubjects = getService('testSubjects'); + + return { + async assertSettingsCalendarLinkExists() { + await testSubjects.existOrFail('ml_calendar_mng_button'); + }, + + async assertSettingsFilterlistLinkExists() { + await testSubjects.existOrFail('ml_filter_lists_button'); + }, + }; +} diff --git a/x-pack/test/functional/services/machine_learning/single_metric_viewer.ts b/x-pack/test/functional/services/machine_learning/single_metric_viewer.ts new file mode 100644 index 0000000000000..36abeecbddcea --- /dev/null +++ b/x-pack/test/functional/services/machine_learning/single_metric_viewer.ts @@ -0,0 +1,17 @@ +/* + * 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 { FtrProviderContext } from '../../ftr_provider_context'; + +export function MachineLearningSingleMetricViewerProvider({ getService }: FtrProviderContext) { + const testSubjects = getService('testSubjects'); + + return { + async assertSingleMetricViewerEmptyListMessageExsist() { + await testSubjects.existOrFail('mlNoSingleMetricJobsFound'); + }, + }; +} diff --git a/x-pack/test/functional/services/ml.ts b/x-pack/test/functional/services/ml.ts new file mode 100644 index 0000000000000..b93ad7c9121bc --- /dev/null +++ b/x-pack/test/functional/services/ml.ts @@ -0,0 +1,49 @@ +/* + * 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 { FtrProviderContext } from '../ftr_provider_context'; + +import { + MachineLearningAnomalyExplorerProvider, + MachineLearningAPIProvider, + MachineLearningDataFramesProvider, + MachineLearningDataVisualizerProvider, + MachineLearningJobManagementProvider, + MachineLearningJobSourceSelectionProvider, + MachineLearningJobTypeSelectionProvider, + MachineLearningJobWizardCommonProvider, + MachineLearningNavigationProvider, + MachineLearningSettingsProvider, + MachineLearningSingleMetricViewerProvider, +} from './machine_learning'; + +export function MachineLearningProvider(context: FtrProviderContext) { + const anomalyExplorer = MachineLearningAnomalyExplorerProvider(context); + const api = MachineLearningAPIProvider(context); + const dataFrames = MachineLearningDataFramesProvider(context); + const dataVisualizer = MachineLearningDataVisualizerProvider(context); + const jobManagement = MachineLearningJobManagementProvider(context); + const jobSourceSelection = MachineLearningJobSourceSelectionProvider(context); + const jobTypeSelection = MachineLearningJobTypeSelectionProvider(context); + const jobWizardCommon = MachineLearningJobWizardCommonProvider(context); + const navigation = MachineLearningNavigationProvider(context); + const settings = MachineLearningSettingsProvider(context); + const singleMetricViewer = MachineLearningSingleMetricViewerProvider(context); + + return { + anomalyExplorer, + api, + dataFrames, + dataVisualizer, + jobManagement, + jobSourceSelection, + jobTypeSelection, + jobWizardCommon, + navigation, + settings, + singleMetricViewer, + }; +}