mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
This PR adds tests for ML single metric, multi metric and population job cloning.
This commit is contained in:
parent
3398b79043
commit
e5219d7044
17 changed files with 1314 additions and 715 deletions
|
@ -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 />}
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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={
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
}
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
}
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
}
|
|
@ -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'));
|
||||
});
|
||||
|
|
|
@ -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'));
|
||||
});
|
||||
}
|
||||
|
|
365
x-pack/test/functional/apps/machine_learning/multi_metric_job.ts
Normal file
365
x-pack/test/functional/apps/machine_learning/multi_metric_job.ts
Normal 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)
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
404
x-pack/test/functional/apps/machine_learning/population_job.ts
Normal file
404
x-pack/test/functional/apps/machine_learning/population_job.ts
Normal 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)
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
|
@ -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)
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
|
@ -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');
|
||||
}
|
||||
})();
|
||||
}
|
||||
|
|
|
@ -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');
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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')
|
||||
);
|
||||
},
|
||||
};
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue