[ML] Add clone tests for single metric, multi metric and population jobs (#46894) (#46955)

This PR adds tests for ML single metric, multi metric and population job cloning.
This commit is contained in:
Robert Oskamp 2019-09-30 21:02:04 +02:00 committed by GitHub
parent 3398b79043
commit e5219d7044
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 1314 additions and 715 deletions

View file

@ -30,7 +30,10 @@ export const EventRateChart: FC<Props> = ({
loading = false,
}) => {
return (
<div style={{ width, height }} data-test-subj="mlEventRateChart">
<div
style={{ width, height }}
data-test-subj={`mlEventRateChart ${eventRateChartData.length ? 'withData' : 'empty'}`}
>
<LoadingWrapper height={height} hasData={eventRateChartData.length > 0} loading={loading}>
<Chart>
{showAxis === true && <Axes />}

View file

@ -79,7 +79,7 @@ export const PostSaveOptions: FC<Props> = ({ jobRunner }) => {
datafeedState === DATAFEED_STATE.STARTING || datafeedState === DATAFEED_STATE.STARTED
}
onClick={startJobInRealTime}
data-test-subj="mlButtonUseFullData3"
data-test-subj="mlJobWizardButtonRunInRealTime"
>
<FormattedMessage
id="xpack.ml.newJob.wizard.summaryStep.postSaveOptions.startJobInRealTime"
@ -95,7 +95,7 @@ export const PostSaveOptions: FC<Props> = ({ jobRunner }) => {
watchCreated === true
}
onClick={() => setWatchFlyoutVisible(true)}
data-test-subj="mlButtonUseFullData"
data-test-subj="mlJobWizardButtonCreateWatch"
>
<FormattedMessage
id="xpack.ml.newJob.wizard.summaryStep.postSaveOptions.createWatch"

View file

@ -56,7 +56,7 @@ export const TimeRangePicker: FC<Props> = ({ setTimeRange, timeRange }) => {
return (
<Fragment>
<div style={{ minWidth: WIDTH }}>
<div style={{ minWidth: WIDTH }} data-test-subj={`jobWizardDateRange`}>
<EuiDatePickerRange
fullWidth={true}
startDateControl={

View file

@ -123,7 +123,7 @@ export const Page: FC<PageProps> = ({ existingJobsAndGroups, jobType }) => {
return (
<Fragment>
<EuiPage style={{ backgroundColor: 'inherit' }} data-test-subj="mlPageJobWizard">
<EuiPage style={{ backgroundColor: 'inherit' }} data-test-subj={`mlPageJobWizard ${jobType}`}>
<EuiPageBody>
<EuiPageContentBody>
<Wizard

View file

@ -1,213 +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 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_multi_1_${Date.now()}`;
const jobDescription =
'Create multi metric job based on the farequote dataset with 15m bucketspan and min/max/mean(responsetime) split by airline';
const jobGroups = ['automated', 'farequote', 'multi-metric'];
const aggAndFieldIdentifiers = ['Min(responsetime)', 'Max(responsetime)', 'Mean(responsetime)'];
const splitField = 'airline';
const bucketSpan = '15m';
const memoryLimit = '20MB';
describe('multi metric job creation', function() {
this.tags(['smoke', 'mlqa']);
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 multi metric job wizard page', async () => {
await ml.jobTypeSelection.selectMultiMetricJob();
});
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 detectors and displays detector previews', async () => {
for (const [index, aggAndFieldIdentifier] of aggAndFieldIdentifiers.entries()) {
await ml.jobWizardCommon.assertAggAndFieldInputExists();
await ml.jobWizardCommon.selectAggAndField(aggAndFieldIdentifier);
await ml.jobWizardCommon.assertDetectorPreviewExists(aggAndFieldIdentifier, index, 'LINE');
}
});
it('inputs the split field and displays split cards', async () => {
await ml.jobWizardMultiMetric.assertSplitFieldInputExists();
await ml.jobWizardMultiMetric.selectSplitField(splitField);
await ml.jobWizardMultiMetric.assertSplitFieldSelection(splitField);
await ml.jobWizardMultiMetric.assertDetectorSplitExists(splitField);
await ml.jobWizardMultiMetric.assertDetectorSplitFrontCardTitle('AAL');
await ml.jobWizardMultiMetric.assertDetectorSplitNumberOfBackCards(9);
await ml.jobWizardCommon.assertInfluencerSelection([splitField]);
});
it('displays the influencer field', async () => {
await ml.jobWizardCommon.assertInfluencerInputExists();
await ml.jobWizardCommon.assertInfluencerSelection([splitField]);
});
it('inputs the bucket span', async () => {
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 () => {
await ml.jobWizardCommon.assertJobDescriptionInputExists();
await ml.jobWizardCommon.setJobDescription(jobDescription);
await ml.jobWizardCommon.assertJobDescriptionValue(jobDescription);
});
it('inputs job groups', async () => {
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 () => {
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.jobTable.waitForJobsToLoad();
await ml.jobTable.filterWithSearchString(jobId);
const rows = await ml.jobTable.parseJobTable();
expect(rows.filter(row => row.id === jobId)).to.have.length(1);
});
it('displays details for the created job in the job list', async () => {
const expectedRow = {
id: jobId,
description: jobDescription,
jobGroups,
recordCount: '86,274',
memoryStatus: 'ok',
jobState: 'closed',
datafeedState: 'stopped',
latestTimestamp: '2016-02-11 23:59:54',
};
await ml.jobTable.assertJobRowFields(jobId, expectedRow);
const expectedCounts = {
job_id: jobId,
processed_record_count: '86,274',
processed_field_count: '172,548',
input_bytes: '6.4 MB',
input_field_count: '172,548',
invalid_date_count: '0',
missing_field_count: '0',
out_of_order_timestamp_count: '0',
empty_bucket_count: '0',
sparse_bucket_count: '0',
bucket_count: '479',
earliest_record_timestamp: '2016-02-07 00:00:00',
latest_record_timestamp: '2016-02-11 23:59:54',
input_record_count: '86,274',
latest_bucket_timestamp: '2016-02-11 23:45:00',
};
const expectedModelSizeStats = {
job_id: jobId,
result_type: 'model_size_stats',
model_bytes_exceeded: '0',
model_bytes_memory_limit: '20971520',
total_by_field_count: '59',
total_over_field_count: '0',
total_partition_field_count: '58',
bucket_allocation_failures_count: '0',
memory_status: 'ok',
timestamp: '2016-02-11 23:30:00',
};
await ml.jobTable.assertJobRowDetailsCounts(jobId, expectedCounts, expectedModelSizeStats);
});
});
}

View file

@ -1,243 +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 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 log = getService('log');
const jobId = `ec_population_1_${Date.now()}`;
const jobDescription =
'Create population job based on the ecommerce sample dataset with 2h bucketspan over customer_id' +
' - detectors: (Mean(products.base_price) by customer_gender), (Mean(products.quantity) by category.leyword)';
const jobGroups = ['automated', 'ecommerce', 'population'];
const populationField = 'customer_id';
const detectors = [
{
identifier: 'Mean(products.base_price)',
splitField: 'customer_gender',
frontCardTitle: 'FEMALE',
numberOfBackCards: 1,
},
{
identifier: 'Mean(products.quantity)',
splitField: 'category.keyword',
frontCardTitle: "Men's Clothing",
numberOfBackCards: 5,
},
];
const bucketSpan = '2h';
const memoryLimit = '8MB';
describe('population job creation', function() {
this.tags(['smoke', 'mlqa']);
before(async () => {
await esArchiver.loadIfNeeded('ml/ecommerce');
});
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('ecommerce');
});
it('loads the population job wizard page', async () => {
await ml.jobTypeSelection.selectPopulationJob();
});
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 the population field', async () => {
await ml.jobWizardPopulation.assertPopulationFieldInputExists();
await ml.jobWizardPopulation.selectPopulationField(populationField);
await ml.jobWizardPopulation.assertPopulationFieldSelection(populationField);
});
it('selects detectors and displays detector previews', async () => {
for (const [index, detector] of detectors.entries()) {
await ml.jobWizardCommon.assertAggAndFieldInputExists();
await ml.jobWizardCommon.selectAggAndField(detector.identifier);
await ml.jobWizardCommon.assertDetectorPreviewExists(detector.identifier, index, 'SCATTER');
}
});
it('inputs detector split fields and displays split cards', async () => {
for (const [index, detector] of detectors.entries()) {
log.debug(detector);
await ml.jobWizardPopulation.assertDetectorSplitFieldInputExists(index);
await ml.jobWizardPopulation.selectDetectorSplitField(index, detector.splitField);
await ml.jobWizardPopulation.assertDetectorSplitFieldSelection(index, detector.splitField);
await ml.jobWizardPopulation.assertDetectorSplitExists(index);
await ml.jobWizardPopulation.assertDetectorSplitFrontCardTitle(
index,
detector.frontCardTitle
);
await ml.jobWizardPopulation.assertDetectorSplitNumberOfBackCards(
index,
detector.numberOfBackCards
);
}
});
it('displays the influencer field', async () => {
await ml.jobWizardCommon.assertInfluencerInputExists();
await ml.jobWizardCommon.assertInfluencerSelection(
[populationField].concat(detectors.map(detector => detector.splitField))
);
});
it('inputs the bucket span', async () => {
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 () => {
await ml.jobWizardCommon.assertJobDescriptionInputExists();
await ml.jobWizardCommon.setJobDescription(jobDescription);
await ml.jobWizardCommon.assertJobDescriptionValue(jobDescription);
});
it('inputs job groups', async () => {
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 () => {
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.jobTable.waitForJobsToLoad();
await ml.jobTable.filterWithSearchString(jobId);
const rows = await ml.jobTable.parseJobTable();
expect(rows.filter(row => row.id === jobId)).to.have.length(1);
});
it('displays details for the created job in the job list', async () => {
const expectedRow = {
id: jobId,
description: jobDescription,
jobGroups,
recordCount: '4,675',
memoryStatus: 'ok',
jobState: 'closed',
datafeedState: 'stopped',
latestTimestamp: '2019-07-12 23:45:36',
};
await ml.jobTable.assertJobRowFields(jobId, expectedRow);
const expectedCounts = {
job_id: jobId,
processed_record_count: '4,675',
processed_field_count: '23,375',
input_bytes: '867.7 KB',
input_field_count: '23,375',
invalid_date_count: '0',
missing_field_count: '0',
out_of_order_timestamp_count: '0',
empty_bucket_count: '0',
sparse_bucket_count: '0',
bucket_count: '371',
earliest_record_timestamp: '2019-06-12 00:04:19',
latest_record_timestamp: '2019-07-12 23:45:36',
input_record_count: '4,675',
latest_bucket_timestamp: '2019-07-12 22:00:00',
};
const expectedModelSizeStats = {
job_id: jobId,
result_type: 'model_size_stats',
model_bytes_exceeded: '0',
model_bytes_memory_limit: '8388608',
total_by_field_count: '25',
total_over_field_count: '92',
total_partition_field_count: '3',
bucket_allocation_failures_count: '0',
memory_status: 'ok',
timestamp: '2019-07-12 20:00:00',
};
await ml.jobTable.assertJobRowDetailsCounts(jobId, expectedCounts, expectedModelSizeStats);
});
});
}

View file

@ -1,193 +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 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()}`;
const jobDescription =
'Create single metric job based on the farequote dataset with 30m bucketspan and mean(responsetime)';
const jobGroups = ['automated', 'farequote', 'single-metric'];
const aggAndFieldIdentifier = 'Mean(responsetime)';
const bucketSpan = '30m';
const memoryLimit = '15MB';
describe('single metric job creation', function() {
this.tags(['smoke', 'mlqa']);
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 () => {
await ml.jobWizardCommon.assertAggAndFieldInputExists();
await ml.jobWizardCommon.selectAggAndField(aggAndFieldIdentifier);
await ml.jobWizardCommon.assertAggAndFieldSelection(aggAndFieldIdentifier);
});
it('inputs the bucket span', async () => {
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 () => {
await ml.jobWizardCommon.assertJobDescriptionInputExists();
await ml.jobWizardCommon.setJobDescription(jobDescription);
await ml.jobWizardCommon.assertJobDescriptionValue(jobDescription);
});
it('inputs job groups', async () => {
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 () => {
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.jobTable.waitForJobsToLoad();
await ml.jobTable.filterWithSearchString(jobId);
const rows = await ml.jobTable.parseJobTable();
expect(rows.filter(row => row.id === jobId)).to.have.length(1);
});
it('displays details for the created job in the job list', async () => {
const expectedRow = {
id: jobId,
description: jobDescription,
jobGroups,
recordCount: '2,399',
memoryStatus: 'ok',
jobState: 'closed',
datafeedState: 'stopped',
latestTimestamp: '2016-02-11 23:56:59',
};
await ml.jobTable.assertJobRowFields(jobId, expectedRow);
const expectedCounts = {
job_id: jobId,
processed_record_count: '2,399',
processed_field_count: '4,798',
input_bytes: '180.6 KB',
input_field_count: '4,798',
invalid_date_count: '0',
missing_field_count: '0',
out_of_order_timestamp_count: '0',
empty_bucket_count: '0',
sparse_bucket_count: '0',
bucket_count: '239',
earliest_record_timestamp: '2016-02-07 00:02:50',
latest_record_timestamp: '2016-02-11 23:56:59',
input_record_count: '2,399',
latest_bucket_timestamp: '2016-02-11 23:30:00',
};
const expectedModelSizeStats = {
job_id: jobId,
result_type: 'model_size_stats',
model_bytes_exceeded: '0',
model_bytes_memory_limit: '15728640',
total_by_field_count: '3',
total_over_field_count: '0',
total_partition_field_count: '2',
bucket_allocation_failures_count: '0',
memory_status: 'ok',
timestamp: '2016-02-11 23:00:00',
};
await ml.jobTable.assertJobRowDetailsCounts(jobId, expectedCounts, expectedModelSizeStats);
});
});
}

View file

@ -7,7 +7,7 @@ import { FtrProviderContext } from '../../../ftr_provider_context';
export default function({ loadTestFile }: FtrProviderContext) {
describe('feature controls', function() {
this.tags('skipFirefox');
this.tags(['skipFirefox', 'mlqa']);
loadTestFile(require.resolve('./ml_security'));
loadTestFile(require.resolve('./ml_spaces'));
});

View file

@ -11,8 +11,8 @@ export default function({ loadTestFile }: FtrProviderContext) {
loadTestFile(require.resolve('./feature_controls'));
loadTestFile(require.resolve('./pages'));
loadTestFile(require.resolve('./create_single_metric_job'));
loadTestFile(require.resolve('./create_multi_metric_job'));
loadTestFile(require.resolve('./create_population_job'));
loadTestFile(require.resolve('./single_metric_job'));
loadTestFile(require.resolve('./multi_metric_job'));
loadTestFile(require.resolve('./population_job'));
});
}

View file

@ -0,0 +1,365 @@
/*
* 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_multi_1_${Date.now()}`;
const jobIdClone = `${jobId}_clone`;
const jobDescription =
'Create multi metric job based on the farequote dataset with 15m bucketspan and min/max/mean(responsetime) split by airline';
const jobGroups = ['automated', 'farequote', 'multi-metric'];
const jobGroupsClone = [...jobGroups, 'clone'];
const aggAndFieldIdentifiers = ['Min(responsetime)', 'Max(responsetime)', 'Mean(responsetime)'];
const splitField = 'airline';
const bucketSpan = '15m';
const memoryLimit = '20mb';
function getExpectedRow(expectedJobId: string, expectedJobGroups: string[]) {
return {
id: expectedJobId,
description: jobDescription,
jobGroups: [...new Set(expectedJobGroups)].sort(),
recordCount: '86,274',
memoryStatus: 'ok',
jobState: 'closed',
datafeedState: 'stopped',
latestTimestamp: '2016-02-11 23:59:54',
};
}
function getExpectedCounts(expectedJobId: string) {
return {
job_id: expectedJobId,
processed_record_count: '86,274',
processed_field_count: '172,548',
input_bytes: '6.4 MB',
input_field_count: '172,548',
invalid_date_count: '0',
missing_field_count: '0',
out_of_order_timestamp_count: '0',
empty_bucket_count: '0',
sparse_bucket_count: '0',
bucket_count: '479',
earliest_record_timestamp: '2016-02-07 00:00:00',
latest_record_timestamp: '2016-02-11 23:59:54',
input_record_count: '86,274',
latest_bucket_timestamp: '2016-02-11 23:45:00',
};
}
function getExpectedModelSizeStats(expectedJobId: string) {
return {
job_id: expectedJobId,
result_type: 'model_size_stats',
model_bytes_exceeded: '0',
model_bytes_memory_limit: '20971520',
total_by_field_count: '59',
total_over_field_count: '0',
total_partition_field_count: '58',
bucket_allocation_failures_count: '0',
memory_status: 'ok',
timestamp: '2016-02-11 23:30:00',
};
}
describe('multi metric', function() {
this.tags(['smoke', 'mlqa']);
before(async () => {
await esArchiver.loadIfNeeded('ml/farequote');
});
after(async () => {
await esArchiver.unload('ml/farequote');
await ml.api.cleanMlIndices();
await ml.api.cleanDataframeIndices();
});
describe('job creation', function() {
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 multi metric job wizard page', async () => {
await ml.jobTypeSelection.selectMultiMetricJob();
});
it('displays the time range step', async () => {
await ml.jobWizardCommon.assertTimeRangeSectionExists();
});
it('displays the event rate chart', async () => {
await ml.jobWizardCommon.clickUseFullDataButton(
'Feb 7, 2016 @ 00:00:00.000',
'Feb 11, 2016 @ 23:59:54.000'
);
await ml.jobWizardCommon.assertEventRateChartExists();
await ml.jobWizardCommon.assertEventRateChartHasData();
});
it('displays the pick fields step', async () => {
await ml.jobWizardCommon.advanceToPickFieldsSection();
});
it('selects detectors and displays detector previews', async () => {
for (const [index, aggAndFieldIdentifier] of aggAndFieldIdentifiers.entries()) {
await ml.jobWizardCommon.assertAggAndFieldInputExists();
await ml.jobWizardCommon.selectAggAndField(aggAndFieldIdentifier, false);
await ml.jobWizardCommon.assertDetectorPreviewExists(
aggAndFieldIdentifier,
index,
'LINE'
);
}
});
it('inputs the split field and displays split cards', async () => {
await ml.jobWizardMultiMetric.assertSplitFieldInputExists();
await ml.jobWizardMultiMetric.selectSplitField(splitField);
await ml.jobWizardMultiMetric.assertDetectorSplitExists(splitField);
await ml.jobWizardMultiMetric.assertDetectorSplitFrontCardTitle('AAL');
await ml.jobWizardMultiMetric.assertDetectorSplitNumberOfBackCards(9);
await ml.jobWizardCommon.assertInfluencerSelection([splitField]);
});
it('displays the influencer field', async () => {
await ml.jobWizardCommon.assertInfluencerInputExists();
await ml.jobWizardCommon.assertInfluencerSelection([splitField]);
});
it('inputs the bucket span', async () => {
await ml.jobWizardCommon.assertBucketSpanInputExists();
await ml.jobWizardCommon.setBucketSpan(bucketSpan);
});
it('displays the job details step', async () => {
await ml.jobWizardCommon.advanceToJobDetailsSection();
});
it('inputs the job id', async () => {
await ml.jobWizardCommon.assertJobIdInputExists();
await ml.jobWizardCommon.setJobId(jobId);
});
it('inputs the job description', async () => {
await ml.jobWizardCommon.assertJobDescriptionInputExists();
await ml.jobWizardCommon.setJobDescription(jobDescription);
});
it('inputs job groups', async () => {
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();
});
it('inputs the model memory limit', async () => {
await ml.jobWizardCommon.assertModelMemoryLimitInputExists();
await ml.jobWizardCommon.setModelMemoryLimit(memoryLimit);
});
it('displays the validation step', async () => {
await ml.jobWizardCommon.advanceToValidationSection();
});
it('displays the summary step', async () => {
await ml.jobWizardCommon.advanceToSummarySection();
});
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.jobTable.waitForJobsToLoad();
await ml.jobTable.filterWithSearchString(jobId);
const rows = await ml.jobTable.parseJobTable();
expect(rows.filter(row => row.id === jobId)).to.have.length(1);
});
it('displays details for the created job in the job list', async () => {
await ml.jobTable.assertJobRowFields(jobId, getExpectedRow(jobId, jobGroups));
await ml.jobTable.assertJobRowDetailsCounts(
jobId,
getExpectedCounts(jobId),
getExpectedModelSizeStats(jobId)
);
});
});
describe('job cloning', function() {
it('clicks the clone action and loads the multi metric wizard', async () => {
await ml.jobTable.clickCloneJobAction(jobId);
await ml.jobTypeSelection.assertMultiMetricJobWizardOpen();
});
it('displays the time range step', async () => {
await ml.jobWizardCommon.assertTimeRangeSectionExists();
});
it('displays the event rate chart', async () => {
await ml.jobWizardCommon.clickUseFullDataButton(
'Feb 7, 2016 @ 00:00:00.000',
'Feb 11, 2016 @ 23:59:54.000'
);
await ml.jobWizardCommon.assertEventRateChartExists();
await ml.jobWizardCommon.assertEventRateChartHasData();
});
it('displays the pick fields step', async () => {
await ml.jobWizardCommon.advanceToPickFieldsSection();
});
it('pre-fills detectors and shows preview with split cards', async () => {
for (const [index, aggAndFieldIdentifier] of aggAndFieldIdentifiers.entries()) {
await ml.jobWizardCommon.assertDetectorPreviewExists(
aggAndFieldIdentifier,
index,
'LINE'
);
}
await ml.jobWizardMultiMetric.assertDetectorSplitExists(splitField);
await ml.jobWizardMultiMetric.assertDetectorSplitFrontCardTitle('AAL');
await ml.jobWizardMultiMetric.assertDetectorSplitNumberOfBackCards(9);
});
it('pre-fills the split field', async () => {
await ml.jobWizardMultiMetric.assertSplitFieldInputExists();
await ml.jobWizardMultiMetric.assertSplitFieldSelection(splitField);
});
it('pre-fills influencers', async () => {
await ml.jobWizardCommon.assertInfluencerInputExists();
await ml.jobWizardCommon.assertInfluencerSelection([splitField]);
});
it('pre-fills the bucket span', async () => {
await ml.jobWizardCommon.assertBucketSpanInputExists();
await ml.jobWizardCommon.assertBucketSpanValue(bucketSpan);
});
it('displays the job details step', async () => {
await ml.jobWizardCommon.advanceToJobDetailsSection();
});
it('does not pre-fill the job id', async () => {
await ml.jobWizardCommon.assertJobIdInputExists();
await ml.jobWizardCommon.assertJobIdValue('');
});
it('inputs the clone job id', async () => {
await ml.jobWizardCommon.setJobId(jobIdClone);
});
it('pre-fills the job description', async () => {
await ml.jobWizardCommon.assertJobDescriptionInputExists();
await ml.jobWizardCommon.assertJobDescriptionValue(jobDescription);
});
it('pre-fills job groups', async () => {
await ml.jobWizardCommon.assertJobGroupInputExists();
await ml.jobWizardCommon.assertJobGroupSelection(jobGroups);
});
it('inputs the clone job group', async () => {
await ml.jobWizardCommon.assertJobGroupInputExists();
await ml.jobWizardCommon.addJobGroup('clone');
await ml.jobWizardCommon.assertJobGroupSelection(jobGroupsClone);
});
it('opens the advanced section', async () => {
await ml.jobWizardCommon.ensureAdvancedSectionOpen();
});
it('pre-fills the model plot switch', async () => {
await ml.jobWizardCommon.assertModelPlotSwitchExists();
await ml.jobWizardCommon.assertModelPlotSwitchCheckedState(false);
});
it('pre-fills the dedicated index switch', async () => {
await ml.jobWizardCommon.assertDedicatedIndexSwitchExists();
await ml.jobWizardCommon.assertDedicatedIndexSwitchCheckedState(true);
});
it('pre-fills the model memory limit', async () => {
await ml.jobWizardCommon.assertModelMemoryLimitInputExists();
await ml.jobWizardCommon.assertModelMemoryLimitValue(memoryLimit);
});
it('displays the validation step', async () => {
await ml.jobWizardCommon.advanceToValidationSection();
});
it('displays the summary step', async () => {
await ml.jobWizardCommon.advanceToSummarySection();
});
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.jobTable.waitForJobsToLoad();
await ml.jobTable.filterWithSearchString(jobIdClone);
const rows = await ml.jobTable.parseJobTable();
expect(rows.filter(row => row.id === jobIdClone)).to.have.length(1);
});
it('displays details for the created job in the job list', async () => {
await ml.jobTable.assertJobRowFields(
jobIdClone,
getExpectedRow(jobIdClone, jobGroupsClone)
);
await ml.jobTable.assertJobRowDetailsCounts(
jobIdClone,
getExpectedCounts(jobIdClone),
getExpectedModelSizeStats(jobIdClone)
);
});
});
});
}

View file

@ -0,0 +1,404 @@
/*
* 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 = `ec_population_1_${Date.now()}`;
const jobIdClone = `${jobId}_clone`;
const jobDescription =
'Create population job based on the ecommerce sample dataset with 2h bucketspan over customer_id' +
' - detectors: (Mean(products.base_price) by customer_gender), (Mean(products.quantity) by category.leyword)';
const jobGroups = ['automated', 'ecommerce', 'population'];
const jobGroupsClone = [...jobGroups, 'clone'];
const populationField = 'customer_id';
const detectors = [
{
identifier: 'Mean(products.base_price)',
splitField: 'customer_gender',
frontCardTitle: 'FEMALE',
numberOfBackCards: 1,
},
{
identifier: 'Mean(products.quantity)',
splitField: 'category.keyword',
frontCardTitle: "Men's Clothing",
numberOfBackCards: 5,
},
];
const bucketSpan = '2h';
const memoryLimit = '8mb';
function getExpectedRow(expectedJobId: string, expectedJobGroups: string[]) {
return {
id: expectedJobId,
description: jobDescription,
jobGroups: [...new Set(expectedJobGroups)].sort(),
recordCount: '4,675',
memoryStatus: 'ok',
jobState: 'closed',
datafeedState: 'stopped',
latestTimestamp: '2019-07-12 23:45:36',
};
}
function getExpectedCounts(expectedJobId: string) {
return {
job_id: expectedJobId,
processed_record_count: '4,675',
processed_field_count: '23,375',
input_bytes: '867.7 KB',
input_field_count: '23,375',
invalid_date_count: '0',
missing_field_count: '0',
out_of_order_timestamp_count: '0',
empty_bucket_count: '0',
sparse_bucket_count: '0',
bucket_count: '371',
earliest_record_timestamp: '2019-06-12 00:04:19',
latest_record_timestamp: '2019-07-12 23:45:36',
input_record_count: '4,675',
latest_bucket_timestamp: '2019-07-12 22:00:00',
};
}
function getExpectedModelSizeStats(expectedJobId: string) {
return {
job_id: expectedJobId,
result_type: 'model_size_stats',
model_bytes_exceeded: '0',
model_bytes_memory_limit: '8388608',
total_by_field_count: '25',
total_over_field_count: '92',
total_partition_field_count: '3',
bucket_allocation_failures_count: '0',
memory_status: 'ok',
timestamp: '2019-07-12 20:00:00',
};
}
describe('population', function() {
this.tags(['smoke', 'mlqa']);
before(async () => {
await esArchiver.loadIfNeeded('ml/ecommerce');
});
after(async () => {
await esArchiver.unload('ml/farequote');
await ml.api.cleanMlIndices();
await ml.api.cleanDataframeIndices();
});
describe('job creation', function() {
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('ecommerce');
});
it('loads the population job wizard page', async () => {
await ml.jobTypeSelection.selectPopulationJob();
});
it('displays the time range step', async () => {
await ml.jobWizardCommon.assertTimeRangeSectionExists();
});
it('displays the event rate chart', async () => {
await ml.jobWizardCommon.clickUseFullDataButton(
'Jun 12, 2019 @ 00:04:19.000',
'Jul 12, 2019 @ 23:45:36.000'
);
await ml.jobWizardCommon.assertEventRateChartExists();
await ml.jobWizardCommon.assertEventRateChartHasData();
});
it('displays the pick fields step', async () => {
await ml.jobWizardCommon.advanceToPickFieldsSection();
});
it('selects the population field', async () => {
await ml.jobWizardPopulation.assertPopulationFieldInputExists();
await ml.jobWizardPopulation.selectPopulationField(populationField);
});
it('selects detectors and displays detector previews', async () => {
for (const [index, detector] of detectors.entries()) {
await ml.jobWizardCommon.assertAggAndFieldInputExists();
await ml.jobWizardCommon.selectAggAndField(detector.identifier, false);
await ml.jobWizardCommon.assertDetectorPreviewExists(
detector.identifier,
index,
'SCATTER'
);
}
});
it('inputs detector split fields and displays split cards', async () => {
for (const [index, detector] of detectors.entries()) {
await ml.jobWizardPopulation.assertDetectorSplitFieldInputExists(index);
await ml.jobWizardPopulation.selectDetectorSplitField(index, detector.splitField);
await ml.jobWizardPopulation.assertDetectorSplitExists(index);
await ml.jobWizardPopulation.assertDetectorSplitFrontCardTitle(
index,
detector.frontCardTitle
);
await ml.jobWizardPopulation.assertDetectorSplitNumberOfBackCards(
index,
detector.numberOfBackCards
);
}
});
it('displays the influencer field', async () => {
await ml.jobWizardCommon.assertInfluencerInputExists();
await ml.jobWizardCommon.assertInfluencerSelection(
[populationField].concat(detectors.map(detector => detector.splitField))
);
});
it('inputs the bucket span', async () => {
await ml.jobWizardCommon.assertBucketSpanInputExists();
await ml.jobWizardCommon.setBucketSpan(bucketSpan);
});
it('displays the job details step', async () => {
await ml.jobWizardCommon.advanceToJobDetailsSection();
});
it('inputs the job id', async () => {
await ml.jobWizardCommon.assertJobIdInputExists();
await ml.jobWizardCommon.setJobId(jobId);
});
it('inputs the job description', async () => {
await ml.jobWizardCommon.assertJobDescriptionInputExists();
await ml.jobWizardCommon.setJobDescription(jobDescription);
});
it('inputs job groups', async () => {
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();
});
it('inputs the model memory limit', async () => {
await ml.jobWizardCommon.assertModelMemoryLimitInputExists();
await ml.jobWizardCommon.setModelMemoryLimit(memoryLimit);
});
it('displays the validation step', async () => {
await ml.jobWizardCommon.advanceToValidationSection();
});
it('displays the summary step', async () => {
await ml.jobWizardCommon.advanceToSummarySection();
});
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.jobTable.waitForJobsToLoad();
await ml.jobTable.filterWithSearchString(jobId);
const rows = await ml.jobTable.parseJobTable();
expect(rows.filter(row => row.id === jobId)).to.have.length(1);
});
it('displays details for the created job in the job list', async () => {
await ml.jobTable.assertJobRowFields(jobId, getExpectedRow(jobId, jobGroups));
await ml.jobTable.assertJobRowDetailsCounts(
jobId,
getExpectedCounts(jobId),
getExpectedModelSizeStats(jobId)
);
});
});
describe('job cloning', function() {
it('clicks the clone action and loads the population wizard', async () => {
await ml.jobTable.clickCloneJobAction(jobId);
await ml.jobTypeSelection.assertPopulationJobWizardOpen();
});
it('displays the time range step', async () => {
await ml.jobWizardCommon.assertTimeRangeSectionExists();
});
it('displays the event rate chart', async () => {
await ml.jobWizardCommon.clickUseFullDataButton(
'Jun 12, 2019 @ 00:04:19.000',
'Jul 12, 2019 @ 23:45:36.000'
);
await ml.jobWizardCommon.assertEventRateChartExists();
await ml.jobWizardCommon.assertEventRateChartHasData();
});
it('displays the pick fields step', async () => {
await ml.jobWizardCommon.advanceToPickFieldsSection();
});
it('pre-fills the population field', async () => {
await ml.jobWizardPopulation.assertPopulationFieldInputExists();
await ml.jobWizardPopulation.assertPopulationFieldSelection(populationField);
});
it('pre-fills detectors and shows preview with split cards', async () => {
for (const [index, detector] of detectors.entries()) {
await ml.jobWizardCommon.assertDetectorPreviewExists(
detector.identifier,
index,
'SCATTER'
);
await ml.jobWizardPopulation.assertDetectorSplitFieldSelection(
index,
detector.splitField
);
await ml.jobWizardPopulation.assertDetectorSplitExists(index);
await ml.jobWizardPopulation.assertDetectorSplitFrontCardTitle(
index,
detector.frontCardTitle
);
await ml.jobWizardPopulation.assertDetectorSplitNumberOfBackCards(
index,
detector.numberOfBackCards
);
}
});
it('pre-fills influencers', async () => {
await ml.jobWizardCommon.assertInfluencerInputExists();
await ml.jobWizardCommon.assertInfluencerSelection(
[populationField].concat(detectors.map(detector => detector.splitField))
);
});
it('pre-fills the bucket span', async () => {
await ml.jobWizardCommon.assertBucketSpanInputExists();
await ml.jobWizardCommon.assertBucketSpanValue(bucketSpan);
});
it('displays the job details step', async () => {
await ml.jobWizardCommon.advanceToJobDetailsSection();
});
it('does not pre-fill the job id', async () => {
await ml.jobWizardCommon.assertJobIdInputExists();
await ml.jobWizardCommon.assertJobIdValue('');
});
it('inputs the clone job id', async () => {
await ml.jobWizardCommon.setJobId(jobIdClone);
});
it('pre-fills the job description', async () => {
await ml.jobWizardCommon.assertJobDescriptionInputExists();
await ml.jobWizardCommon.assertJobDescriptionValue(jobDescription);
});
it('pre-fills job groups', async () => {
await ml.jobWizardCommon.assertJobGroupInputExists();
await ml.jobWizardCommon.assertJobGroupSelection(jobGroups);
});
it('inputs the clone job group', async () => {
await ml.jobWizardCommon.assertJobGroupInputExists();
await ml.jobWizardCommon.addJobGroup('clone');
await ml.jobWizardCommon.assertJobGroupSelection(jobGroupsClone);
});
it('opens the advanced section', async () => {
await ml.jobWizardCommon.ensureAdvancedSectionOpen();
});
it('pre-fills the model plot switch', async () => {
await ml.jobWizardCommon.assertModelPlotSwitchExists();
await ml.jobWizardCommon.assertModelPlotSwitchCheckedState(false);
});
it('pre-fills the dedicated index switch', async () => {
await ml.jobWizardCommon.assertDedicatedIndexSwitchExists();
await ml.jobWizardCommon.assertDedicatedIndexSwitchCheckedState(true);
});
it('pre-fills the model memory limit', async () => {
await ml.jobWizardCommon.assertModelMemoryLimitInputExists();
await ml.jobWizardCommon.assertModelMemoryLimitValue(memoryLimit);
});
it('displays the validation step', async () => {
await ml.jobWizardCommon.advanceToValidationSection();
});
it('displays the summary step', async () => {
await ml.jobWizardCommon.advanceToSummarySection();
});
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.jobTable.waitForJobsToLoad();
await ml.jobTable.filterWithSearchString(jobIdClone);
const rows = await ml.jobTable.parseJobTable();
expect(rows.filter(row => row.id === jobIdClone)).to.have.length(1);
});
it('displays details for the created job in the job list', async () => {
await ml.jobTable.assertJobRowFields(
jobIdClone,
getExpectedRow(jobIdClone, jobGroupsClone)
);
await ml.jobTable.assertJobRowDetailsCounts(
jobIdClone,
getExpectedCounts(jobIdClone),
getExpectedModelSizeStats(jobIdClone)
);
});
});
});
}

View file

@ -0,0 +1,324 @@
/*
* 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()}`;
const jobIdClone = `${jobId}_clone`;
const jobDescription =
'Create single metric job based on the farequote dataset with 30m bucketspan and mean(responsetime)';
const jobGroups = ['automated', 'farequote', 'single-metric'];
const jobGroupsClone = [...jobGroups, 'clone'];
const aggAndFieldIdentifier = 'Mean(responsetime)';
const bucketSpan = '30m';
const memoryLimit = '15mb';
function getExpectedRow(expectedJobId: string, expectedJobGroups: string[]) {
return {
id: expectedJobId,
description: jobDescription,
jobGroups: [...new Set(expectedJobGroups)].sort(),
recordCount: '2,399',
memoryStatus: 'ok',
jobState: 'closed',
datafeedState: 'stopped',
latestTimestamp: '2016-02-11 23:56:59',
};
}
function getExpectedCounts(expectedJobId: string) {
return {
job_id: expectedJobId,
processed_record_count: '2,399',
processed_field_count: '4,798',
input_bytes: '180.6 KB',
input_field_count: '4,798',
invalid_date_count: '0',
missing_field_count: '0',
out_of_order_timestamp_count: '0',
empty_bucket_count: '0',
sparse_bucket_count: '0',
bucket_count: '239',
earliest_record_timestamp: '2016-02-07 00:02:50',
latest_record_timestamp: '2016-02-11 23:56:59',
input_record_count: '2,399',
latest_bucket_timestamp: '2016-02-11 23:30:00',
};
}
function getExpectedModelSizeStats(expectedJobId: string) {
return {
job_id: expectedJobId,
result_type: 'model_size_stats',
model_bytes_exceeded: '0',
model_bytes_memory_limit: '15728640',
total_by_field_count: '3',
total_over_field_count: '0',
total_partition_field_count: '2',
bucket_allocation_failures_count: '0',
memory_status: 'ok',
timestamp: '2016-02-11 23:00:00',
};
}
describe('single metric', function() {
this.tags(['smoke', 'mlqa']);
before(async () => {
await esArchiver.loadIfNeeded('ml/farequote');
});
after(async () => {
await esArchiver.unload('ml/farequote');
await ml.api.cleanMlIndices();
await ml.api.cleanDataframeIndices();
});
describe('job creation', function() {
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(
'Feb 7, 2016 @ 00:00:00.000',
'Feb 11, 2016 @ 23:59:54.000'
);
await ml.jobWizardCommon.assertEventRateChartExists();
await ml.jobWizardCommon.assertEventRateChartHasData();
});
it('displays the pick fields step', async () => {
await ml.jobWizardCommon.advanceToPickFieldsSection();
});
it('selects field and aggregation', async () => {
await ml.jobWizardCommon.assertAggAndFieldInputExists();
await ml.jobWizardCommon.selectAggAndField(aggAndFieldIdentifier, true);
await ml.jobWizardCommon.assertAnomalyChartExists('LINE');
});
it('inputs the bucket span', async () => {
await ml.jobWizardCommon.assertBucketSpanInputExists();
await ml.jobWizardCommon.setBucketSpan(bucketSpan);
});
it('displays the job details step', async () => {
await ml.jobWizardCommon.advanceToJobDetailsSection();
});
it('inputs the job id', async () => {
await ml.jobWizardCommon.assertJobIdInputExists();
await ml.jobWizardCommon.setJobId(jobId);
});
it('inputs the job description', async () => {
await ml.jobWizardCommon.assertJobDescriptionInputExists();
await ml.jobWizardCommon.setJobDescription(jobDescription);
});
it('inputs job groups', async () => {
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();
});
it('inputs the model memory limit', async () => {
await ml.jobWizardCommon.assertModelMemoryLimitInputExists();
await ml.jobWizardCommon.setModelMemoryLimit(memoryLimit);
});
it('displays the validation step', async () => {
await ml.jobWizardCommon.advanceToValidationSection();
});
it('displays the summary step', async () => {
await ml.jobWizardCommon.advanceToSummarySection();
});
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.jobTable.waitForJobsToLoad();
await ml.jobTable.filterWithSearchString(jobId);
const rows = await ml.jobTable.parseJobTable();
expect(rows.filter(row => row.id === jobId)).to.have.length(1);
});
it('displays details for the created job in the job list', async () => {
await ml.jobTable.assertJobRowFields(jobId, getExpectedRow(jobId, jobGroups));
await ml.jobTable.assertJobRowDetailsCounts(
jobId,
getExpectedCounts(jobId),
getExpectedModelSizeStats(jobId)
);
});
});
describe('job cloning', function() {
it('clicks the clone action and loads the single metric wizard', async () => {
await ml.jobTable.clickCloneJobAction(jobId);
await ml.jobTypeSelection.assertSingleMetricJobWizardOpen();
});
it('displays the time range step', async () => {
await ml.jobWizardCommon.assertTimeRangeSectionExists();
});
it('displays the event rate chart', async () => {
await ml.jobWizardCommon.clickUseFullDataButton(
'Feb 7, 2016 @ 00:00:00.000',
'Feb 11, 2016 @ 23:59:54.000'
);
await ml.jobWizardCommon.assertEventRateChartExists();
await ml.jobWizardCommon.assertEventRateChartHasData();
});
it('displays the pick fields step', async () => {
await ml.jobWizardCommon.advanceToPickFieldsSection();
});
it('pre-fills field and aggregation', async () => {
await ml.jobWizardCommon.assertAggAndFieldInputExists();
await ml.jobWizardCommon.assertAggAndFieldSelection(aggAndFieldIdentifier);
await ml.jobWizardCommon.assertAnomalyChartExists('LINE');
});
it('pre-fills the bucket span', async () => {
await ml.jobWizardCommon.assertBucketSpanInputExists();
await ml.jobWizardCommon.assertBucketSpanValue(bucketSpan);
});
it('displays the job details step', async () => {
await ml.jobWizardCommon.advanceToJobDetailsSection();
});
it('does not pre-fill the job id', async () => {
await ml.jobWizardCommon.assertJobIdInputExists();
await ml.jobWizardCommon.assertJobIdValue('');
});
it('inputs the clone job id', async () => {
await ml.jobWizardCommon.setJobId(jobIdClone);
});
it('pre-fills the job description', async () => {
await ml.jobWizardCommon.assertJobDescriptionInputExists();
await ml.jobWizardCommon.assertJobDescriptionValue(jobDescription);
});
it('pre-fills job groups', async () => {
await ml.jobWizardCommon.assertJobGroupInputExists();
await ml.jobWizardCommon.assertJobGroupSelection(jobGroups);
});
it('inputs the clone job group', async () => {
await ml.jobWizardCommon.assertJobGroupInputExists();
await ml.jobWizardCommon.addJobGroup('clone');
await ml.jobWizardCommon.assertJobGroupSelection(jobGroupsClone);
});
it('opens the advanced section', async () => {
await ml.jobWizardCommon.ensureAdvancedSectionOpen();
});
it('pre-fills the model plot switch', async () => {
await ml.jobWizardCommon.assertModelPlotSwitchExists();
await ml.jobWizardCommon.assertModelPlotSwitchCheckedState(true);
});
it('pre-fills the dedicated index switch', async () => {
await ml.jobWizardCommon.assertDedicatedIndexSwitchExists();
await ml.jobWizardCommon.assertDedicatedIndexSwitchCheckedState(true);
});
it('pre-fills the model memory limit', async () => {
await ml.jobWizardCommon.assertModelMemoryLimitInputExists();
await ml.jobWizardCommon.assertModelMemoryLimitValue(memoryLimit);
});
it('displays the validation step', async () => {
await ml.jobWizardCommon.advanceToValidationSection();
});
it('displays the summary step', async () => {
await ml.jobWizardCommon.advanceToSummarySection();
});
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.jobTable.waitForJobsToLoad();
await ml.jobTable.filterWithSearchString(jobIdClone);
const rows = await ml.jobTable.parseJobTable();
expect(rows.filter(row => row.id === jobIdClone)).to.have.length(1);
});
it('displays details for the created job in the job list', async () => {
await ml.jobTable.assertJobRowFields(
jobIdClone,
getExpectedRow(jobIdClone, jobGroupsClone)
);
await ml.jobTable.assertJobRowDetailsCounts(
jobIdClone,
getExpectedCounts(jobIdClone),
getExpectedModelSizeStats(jobIdClone)
);
});
});
});
}

View file

@ -10,6 +10,8 @@ import { FtrProviderContext } from '../../ftr_provider_context';
export function MachineLearningJobTableProvider({ getService }: FtrProviderContext) {
const testSubjects = getService('testSubjects');
const retry = getService('retry');
const find = getService('find');
const log = getService('log');
return new (class MlJobTable {
public async parseJobTable() {
@ -210,5 +212,42 @@ export function MachineLearningJobTableProvider({ getService }: FtrProviderConte
expect(modelSizeStats).to.eql(expectedModelSizeStats);
}
public async clickActionsMenu(jobId: string) {
const jobRow = await testSubjects.find(this.rowSelector(jobId));
const actionsCell = await jobRow.findByCssSelector(`[id=${jobId}-actions]`);
const actionsMenuButton = await actionsCell.findByTagName('button');
log.debug(`Clicking actions menu button for job id ${jobId}`);
await actionsMenuButton.click();
if (!(await find.existsByDisplayedByCssSelector('[class~=euiContextMenuPanel]'))) {
throw new Error(`expected euiContextMenuPanel to exist`);
}
}
public async clickActionsMenuEntry(jobId: string, entryText: string) {
await this.clickActionsMenu(jobId);
const actionsMenuPanel = await find.byCssSelector('[class~=euiContextMenuPanel]');
const actionButtons = await actionsMenuPanel.findAllByTagName('button');
const filteredButtons = [];
for (const button of actionButtons) {
if ((await button.getVisibleText()) === entryText) {
filteredButtons.push(button);
}
}
if (!(filteredButtons.length === 1)) {
throw new Error(
`expected action button ${entryText} to exist exactly once, but found ${filteredButtons.length} matching buttons`
);
}
log.debug(`Clicking action button ${entryText} for job id ${jobId}`);
await filteredButtons[0].click();
}
public async clickCloneJobAction(jobId: string) {
await this.clickActionsMenuEntry(jobId, 'Clone job');
}
})();
}

View file

@ -12,17 +12,29 @@ export function MachineLearningJobTypeSelectionProvider({ getService }: FtrProvi
return {
async selectSingleMetricJob() {
await testSubjects.clickWhenNotDisabled('mlJobTypeLinkSingleMetricJob');
await testSubjects.existOrFail('mlPageJobWizard');
await this.assertSingleMetricJobWizardOpen();
},
async assertSingleMetricJobWizardOpen() {
await testSubjects.existOrFail('mlPageJobWizard single_metric');
},
async selectMultiMetricJob() {
await testSubjects.clickWhenNotDisabled('mlJobTypeLinkMultiMetricJob');
await testSubjects.existOrFail('mlPageJobWizard');
await this.assertMultiMetricJobWizardOpen();
},
async assertMultiMetricJobWizardOpen() {
await testSubjects.existOrFail('mlPageJobWizard multi_metric');
},
async selectPopulationJob() {
await testSubjects.clickWhenNotDisabled('mlJobTypeLinkPopulationJob');
await testSubjects.existOrFail('mlPageJobWizard');
await this.assertPopulationJobWizardOpen();
},
async assertPopulationJobWizardOpen() {
await testSubjects.existOrFail('mlPageJobWizard population');
},
};
}

View file

@ -13,6 +13,18 @@ export function MachineLearningJobWizardCommonProvider({ getService }: FtrProvid
const testSubjects = getService('testSubjects');
return {
async waitForNextButtonVisible() {
await retry.waitFor(
'next button to be visible',
async () => await testSubjects.isDisplayed('mlJobWizardNavButtonNext')
);
},
async clickNextButton() {
await this.waitForNextButtonVisible();
await testSubjects.clickWhenNotDisabled('mlJobWizardNavButtonNext');
},
async assertTimeRangeSectionExists() {
await testSubjects.existOrFail('mlJobWizardStepTitleTimeRange');
},
@ -33,20 +45,49 @@ export function MachineLearningJobWizardCommonProvider({ getService }: FtrProvid
await testSubjects.existOrFail('mlJobWizardStepTitleSummary');
},
async advanceToPickFieldsSection() {
await this.clickNextButton();
await this.assertPickFieldsSectionExists();
},
async advanceToJobDetailsSection() {
await this.clickNextButton();
await this.assertJobDetailsSectionExists();
},
async advanceToValidationSection() {
await this.clickNextButton();
await this.assertValidationSectionExists();
},
async advanceToSummarySection() {
await this.clickNextButton();
await this.assertSummarySectionExists();
},
async assertEventRateChartExists() {
await testSubjects.existOrFail('mlEventRateChart');
await testSubjects.existOrFail('~mlEventRateChart');
},
async assertEventRateChartHasData() {
await testSubjects.existOrFail('mlEventRateChart withData');
},
async assertAggAndFieldInputExists() {
await testSubjects.existOrFail('mlJobWizardAggSelection > comboBoxInput');
},
async assertAggAndFieldSelection(identifier: string) {
async assertAggAndFieldSelection(expectedIdentifier: string) {
const comboBoxSelectedOptions = await comboBox.getComboBoxSelectedOptions(
'mlJobWizardAggSelection > comboBoxInput'
);
expect(comboBoxSelectedOptions.length).to.eql(1);
expect(comboBoxSelectedOptions[0]).to.eql(identifier);
expect(comboBoxSelectedOptions[0]).to.eql(expectedIdentifier);
},
async selectAggAndField(identifier: string, isIdentifierKeptInField: boolean) {
await comboBox.set('mlJobWizardAggSelection > comboBoxInput', identifier);
await this.assertAggAndFieldSelection(isIdentifierKeptInField ? identifier : '');
},
async assertBucketSpanInputExists() {
@ -61,6 +102,11 @@ export function MachineLearningJobWizardCommonProvider({ getService }: FtrProvid
expect(actualBucketSpan).to.eql(expectedValue);
},
async setBucketSpan(bucketSpan: string) {
await testSubjects.setValue('mlJobWizardInputBucketSpan', bucketSpan, { withKeyboard: true });
await this.assertBucketSpanValue(bucketSpan);
},
async assertJobIdInputExists() {
await testSubjects.existOrFail('mlJobWizardInputJobId');
},
@ -70,6 +116,11 @@ export function MachineLearningJobWizardCommonProvider({ getService }: FtrProvid
expect(actualJobId).to.eql(expectedValue);
},
async setJobId(jobId: string) {
await testSubjects.setValue('mlJobWizardInputJobId', jobId, { withKeyboard: true });
await this.assertJobIdValue(jobId);
},
async assertJobDescriptionInputExists() {
await testSubjects.existOrFail('mlJobWizardInputJobDescription');
},
@ -81,15 +132,30 @@ export function MachineLearningJobWizardCommonProvider({ getService }: FtrProvid
expect(actualJobDescription).to.eql(expectedValue);
},
async setJobDescription(jobDescription: string) {
await testSubjects.setValue('mlJobWizardInputJobDescription', jobDescription, {
withKeyboard: true,
});
await this.assertJobDescriptionValue(jobDescription);
},
async assertJobGroupInputExists() {
await testSubjects.existOrFail('mlJobWizardComboBoxJobGroups > comboBoxInput');
},
async assertJobGroupSelection(jobGroups: string[]) {
const comboBoxSelectedOptions = await comboBox.getComboBoxSelectedOptions(
async getSelectedJobGroups(): Promise<string[]> {
return await comboBox.getComboBoxSelectedOptions(
'mlJobWizardComboBoxJobGroups > comboBoxInput'
);
expect(comboBoxSelectedOptions).to.eql(jobGroups);
},
async assertJobGroupSelection(jobGroups: string[]) {
expect(await this.getSelectedJobGroups()).to.eql(jobGroups);
},
async addJobGroup(jobGroup: string) {
await comboBox.setCustom('mlJobWizardComboBoxJobGroups > comboBoxInput', jobGroup);
expect(await this.getSelectedJobGroups()).to.contain(jobGroup);
},
async assertModelPlotSwitchExists() {
@ -99,6 +165,19 @@ export function MachineLearningJobWizardCommonProvider({ getService }: FtrProvid
});
},
async getModelPlotSwitchCheckedState(): Promise<boolean> {
await this.ensureAdvancedSectionOpen();
return await testSubjects.isSelected(
'mlJobWizardAdvancedSection > mlJobWizardSwitchModelPlot'
);
},
async assertModelPlotSwitchCheckedState(expectedValue: boolean) {
await this.ensureAdvancedSectionOpen();
const actualCheckedState = await this.getModelPlotSwitchCheckedState();
expect(actualCheckedState).to.eql(expectedValue);
},
async assertDedicatedIndexSwitchExists() {
await this.ensureAdvancedSectionOpen();
await testSubjects.existOrFail(
@ -107,21 +186,24 @@ export function MachineLearningJobWizardCommonProvider({ getService }: FtrProvid
);
},
async getDedicatedIndexSwitchCheckedState(): Promise<boolean> {
await this.ensureAdvancedSectionOpen();
return await testSubjects.isSelected(
'mlJobWizardAdvancedSection > mlJobWizardSwitchUseDedicatedIndex'
);
},
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 activateDedicatedIndexSwitch() {
if ((await this.getDedicatedIndexSwitchCheckedState()) === false) {
await testSubjects.clickWhenNotDisabled('mlJobWizardSwitchUseDedicatedIndex');
}
await this.assertDedicatedIndexSwitchCheckedState(true);
},
async assertModelMemoryLimitInputExists() {
@ -140,15 +222,33 @@ export function MachineLearningJobWizardCommonProvider({ getService }: FtrProvid
expect(actualModelMemoryLimit).to.eql(expectedValue);
},
async setModelMemoryLimit(modelMemoryLimit: string) {
await testSubjects.setValue('mlJobWizardInputModelMemoryLimit', modelMemoryLimit, {
withKeyboard: true,
});
await this.assertModelMemoryLimitValue(modelMemoryLimit);
},
async assertInfluencerInputExists() {
await testSubjects.existOrFail('influencerSelect > comboBoxInput');
},
async getSelectedInfluencers(): Promise<string[]> {
return await comboBox.getComboBoxSelectedOptions('influencerSelect > comboBoxInput');
},
async assertInfluencerSelection(influencers: string[]) {
const comboBoxSelectedOptions = await comboBox.getComboBoxSelectedOptions(
'influencerSelect > comboBoxInput'
);
expect(comboBoxSelectedOptions).to.eql(influencers);
expect(await this.getSelectedInfluencers()).to.eql(influencers);
},
async addInfluencer(influencer: string) {
await comboBox.setCustom('influencerSelect > comboBoxInput', influencer);
expect(await this.getSelectedInfluencers()).to.contain(influencer);
},
async assertAnomalyChartExists(chartType: string, preSelector?: string) {
let chartSelector = `mlAnomalyChart ${chartType}`;
chartSelector = !preSelector ? chartSelector : `${preSelector} > ${chartSelector}`;
},
async assertDetectorPreviewExists(
@ -161,45 +261,55 @@ export function MachineLearningJobWizardCommonProvider({ getService }: FtrProvid
expect(
await testSubjects.getVisibleText(`detector ${detectorPosition} > detectorTitle`)
).to.eql(aggAndFieldIdentifier);
await testSubjects.existOrFail(`detector ${detectorPosition} > mlAnomalyChart ${chartType}`);
await this.assertAnomalyChartExists(chartType, `detector ${detectorPosition}`);
},
async clickNextButton() {
await testSubjects.clickWhenNotDisabled('mlJobWizardNavButtonNext');
async assertDetectorSplitExists(splitField: string) {
await testSubjects.existOrFail(`dataSplit > dataSplitTitle ${splitField}`);
await testSubjects.existOrFail(`dataSplit > splitCard front`);
await testSubjects.existOrFail(`dataSplit > splitCard back`);
},
async clickPreviousButton() {
await testSubjects.clickWhenNotDisabled('mlJobWizardNavButtonPrevious');
async assertDetectorSplitFrontCardTitle(frontCardTitle: string) {
expect(
await testSubjects.getVisibleText(`dataSplit > splitCard front > splitCardTitle`)
).to.eql(frontCardTitle);
},
async clickUseFullDataButton() {
await testSubjects.clickWhenNotDisabled('mlButtonUseFullData');
async assertDetectorSplitNumberOfBackCards(numberOfBackCards: number) {
expect(await testSubjects.findAll(`dataSplit > splitCard back`)).to.have.length(
numberOfBackCards
);
},
async selectAggAndField(identifier: string) {
await comboBox.set('mlJobWizardAggSelection > comboBoxInput', identifier);
async assertCreateJobButtonExists() {
await testSubjects.existOrFail('mlJobWizardButtonCreateJob');
},
async setBucketSpan(bucketSpan: string) {
await testSubjects.setValue('mlJobWizardInputBucketSpan', bucketSpan, { withKeyboard: true });
async assertDateRangeSelectionExists() {
await testSubjects.existOrFail('jobWizardDateRange');
},
async setJobId(jobId: string) {
await testSubjects.setValue('mlJobWizardInputJobId', jobId, { withKeyboard: true });
async getSelectedDateRange() {
const dateRange = await testSubjects.find('jobWizardDateRange');
const [startPicker, endPicker] = await dateRange.findAllByClassName('euiFieldText');
return {
startDate: await startPicker.getAttribute('value'),
endDate: await endPicker.getAttribute('value'),
};
},
async setJobDescription(jobDescription: string) {
await testSubjects.setValue('mlJobWizardInputJobDescription', jobDescription, {
withKeyboard: true,
async assertDateRangeSelection(expectedStartDate: string, expectedEndDate: string) {
expect(await this.getSelectedDateRange()).to.eql({
startDate: expectedStartDate,
endDate: expectedEndDate,
});
},
async addJobGroup(jobGroup: string) {
await comboBox.setCustom('mlJobWizardComboBoxJobGroups > comboBoxInput', jobGroup);
},
async addInfluencer(influencer: string) {
await comboBox.setCustom('influencerSelect > comboBoxInput', influencer);
async clickUseFullDataButton(expectedStartDate: string, expectedEndDate: string) {
await testSubjects.clickWhenNotDisabled('mlButtonUseFullData');
await this.assertDateRangeSelection(expectedStartDate, expectedEndDate);
},
async ensureAdvancedSectionOpen() {
@ -211,24 +321,12 @@ export function MachineLearningJobWizardCommonProvider({ getService }: FtrProvid
});
},
async activateDedicatedIndexSwitch() {
if ((await this.getDedicatedIndexSwitchCheckedState()) === false) {
await testSubjects.clickWhenNotDisabled('mlJobWizardSwitchUseDedicatedIndex');
}
},
async setModelMemoryLimit(modelMemoryLimit: string) {
await testSubjects.setValue('mlJobWizardInputModelMemoryLimit', modelMemoryLimit, {
withKeyboard: true,
});
},
async createJobAndWaitForCompletion() {
await testSubjects.clickWhenNotDisabled('mlJobWizardButtonCreateJob');
await retry.waitForWithTimeout(
'job processing to finish',
5 * 60 * 1000,
async () => (await testSubjects.exists('mlJobWizardButtonCreateJob')) === false
async () => await testSubjects.exists('mlJobWizardButtonRunInRealTime')
);
},
};

View file

@ -26,6 +26,7 @@ export function MachineLearningJobWizardMultiMetricProvider({ getService }: FtrP
async selectSplitField(identifier: string) {
await comboBox.set('multiMetricSplitFieldSelect > comboBoxInput', identifier);
await this.assertSplitFieldSelection(identifier);
},
async assertDetectorSplitExists(splitField: string) {

View file

@ -26,6 +26,7 @@ export function MachineLearningJobWizardPopulationProvider({ getService }: FtrPr
async selectPopulationField(identifier: string) {
await comboBox.set('populationSplitFieldSelect > comboBoxInput', identifier);
await this.assertPopulationFieldSelection(identifier);
},
async assertDetectorSplitFieldInputExists(detectorPosition: number) {
@ -47,6 +48,7 @@ export function MachineLearningJobWizardPopulationProvider({ getService }: FtrPr
`detector ${detectorPosition} > byFieldSelect > comboBoxInput`,
identifier
);
await this.assertDetectorSplitFieldSelection(detectorPosition, identifier);
},
async assertDetectorSplitExists(detectorPosition: number) {