mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[ML] Add functional tests for Anomaly detection job conversion (#160688)
This commit is contained in:
parent
d6d4c6495f
commit
e2ad90fbec
10 changed files with 1101 additions and 56 deletions
|
@ -42,7 +42,10 @@ export const SingleMetricSettings: FC<Props> = ({ setIsValid }) => {
|
|||
</EuiFlexGroup>
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButtonEmpty onClick={convertToMultiMetric}>
|
||||
<EuiButtonEmpty
|
||||
onClick={convertToMultiMetric}
|
||||
data-test-subj={'mlJobWizardButtonConvertToMultiMetric'}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.newJob.wizard.pickFieldsStep.singleMetricView.convertToMultiMetricButton"
|
||||
defaultMessage="Convert to multi-metric job"
|
||||
|
|
|
@ -210,7 +210,10 @@ export const SummaryStep: FC<StepProps> = ({ setCurrentStep, isCurrentStep }) =>
|
|||
</EuiFlexItem>
|
||||
{isAdvanced === false && (
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButtonEmpty onClick={convertToAdvanced}>
|
||||
<EuiButtonEmpty
|
||||
onClick={convertToAdvanced}
|
||||
data-test-subj="mlJobWizardButtonConvertToAdvancedJob"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.newJob.wizard.summaryStep.convertToAdvancedButton"
|
||||
defaultMessage="Convert to advanced job"
|
||||
|
|
|
@ -227,8 +227,10 @@ export const Page: FC<PageProps> = ({ existingJobsAndGroups, jobType }) => {
|
|||
return (
|
||||
<Fragment>
|
||||
<MlPageHeader>
|
||||
<FormattedMessage id="xpack.ml.newJob.page.createJob" defaultMessage="Create job" />:{' '}
|
||||
{jobCreatorTitle}
|
||||
<div data-test-subj={`mlPageJobWizardHeader-${jobCreator.type}`}>
|
||||
<FormattedMessage id="xpack.ml.newJob.page.createJob" defaultMessage="Create job" />:{' '}
|
||||
{jobCreatorTitle}
|
||||
</div>
|
||||
</MlPageHeader>
|
||||
|
||||
<div style={{ backgroundColor: 'inherit' }} data-test-subj={`mlPageJobWizard ${jobType}`}>
|
||||
|
|
|
@ -6,32 +6,7 @@
|
|||
*/
|
||||
|
||||
import { FtrProviderContext } from '../../../ftr_provider_context';
|
||||
|
||||
interface Detector {
|
||||
identifier: string;
|
||||
function: string;
|
||||
field?: string;
|
||||
byField?: string;
|
||||
overField?: string;
|
||||
partitionField?: string;
|
||||
excludeFrequent?: string;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
interface DatafeedConfig {
|
||||
queryDelay?: string;
|
||||
frequency?: string;
|
||||
scrollSize?: string;
|
||||
}
|
||||
|
||||
interface PickFieldsConfig {
|
||||
detectors: Detector[];
|
||||
influencers: string[];
|
||||
bucketSpan: string;
|
||||
memoryLimit: string;
|
||||
categorizationField?: string;
|
||||
summaryCountField?: string;
|
||||
}
|
||||
import type { PickFieldsConfig, DatafeedConfig, Detector } from './types';
|
||||
|
||||
export default function ({ getService }: FtrProviderContext) {
|
||||
const esArchiver = getService('esArchiver');
|
||||
|
|
|
@ -0,0 +1,796 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { CATEGORY_EXAMPLES_VALIDATION_STATUS } from '@kbn/ml-plugin/common/constants/categorization_job';
|
||||
import type { PickFieldsConfig, DatafeedConfig, Detector } from './types';
|
||||
import type { FtrProviderContext } from '../../../ftr_provider_context';
|
||||
|
||||
export default function ({ getService }: FtrProviderContext) {
|
||||
const esArchiver = getService('esArchiver');
|
||||
const ml = getService('ml');
|
||||
|
||||
const calendarId = `wizard-test-calendar_${Date.now()}`;
|
||||
|
||||
const assertConversionToAdvancedJobWizardRetainsSettingsAndRuns = async ({
|
||||
testSuite,
|
||||
testData,
|
||||
previousJobGroups,
|
||||
bucketSpan,
|
||||
previousDetectors,
|
||||
previousInfluencers,
|
||||
}: {
|
||||
testSuite: string;
|
||||
testData: {
|
||||
suiteTitle: string;
|
||||
jobSource: string;
|
||||
jobId: string;
|
||||
jobDescription: string;
|
||||
jobGroups: string[];
|
||||
pickFieldsConfig: PickFieldsConfig;
|
||||
datafeedConfig: DatafeedConfig;
|
||||
categorizationFieldIdentifier?: string;
|
||||
};
|
||||
previousJobGroups: string[];
|
||||
bucketSpan: string;
|
||||
previousDetectors: Array<{ advancedJobIdentifier: string }>;
|
||||
previousInfluencers: string[];
|
||||
}) => {
|
||||
const previousJobWizard = `${testSuite} job wizard`;
|
||||
|
||||
it(`${testSuite} converts to advanced job and retains previous settings`, async () => {
|
||||
await ml.testExecution.logTestStep(`${previousJobWizard} converts to advanced job creation`);
|
||||
await ml.jobWizardCommon.assertCreateJobButtonExists();
|
||||
await ml.jobWizardCommon.convertToAdvancedJobWizard();
|
||||
|
||||
await ml.testExecution.logTestStep('advanced job creation advances to the pick fields step');
|
||||
await ml.jobWizardCommon.advanceToPickFieldsSection();
|
||||
|
||||
await ml.testExecution.logTestStep('advanced job creation retains the categorization field');
|
||||
await ml.jobWizardAdvanced.assertCategorizationFieldSelection(
|
||||
testData.categorizationFieldIdentifier ? [testData.categorizationFieldIdentifier] : []
|
||||
);
|
||||
|
||||
await ml.testExecution.logTestStep(
|
||||
'advanced job creation retains or inputs the summary count field'
|
||||
);
|
||||
await ml.jobWizardAdvanced.assertSummaryCountFieldInputExists();
|
||||
if (testData.pickFieldsConfig.hasOwnProperty('summaryCountField')) {
|
||||
await ml.jobWizardAdvanced.selectSummaryCountField(
|
||||
testData.pickFieldsConfig.summaryCountField!
|
||||
);
|
||||
} else {
|
||||
await ml.jobWizardAdvanced.assertSummaryCountFieldSelection([]);
|
||||
}
|
||||
|
||||
await ml.testExecution.logTestStep(
|
||||
`advanced job creation retains detectors from ${previousJobWizard}`
|
||||
);
|
||||
for (const [index, detector] of previousDetectors.entries()) {
|
||||
await ml.jobWizardAdvanced.assertDetectorEntryExists(index, detector.advancedJobIdentifier);
|
||||
}
|
||||
|
||||
await ml.testExecution.logTestStep('advanced job creation adds additional detectors');
|
||||
for (const detector of testData.pickFieldsConfig.detectors) {
|
||||
await ml.jobWizardAdvanced.openCreateDetectorModal();
|
||||
await ml.jobWizardAdvanced.assertDetectorFunctionInputExists();
|
||||
await ml.jobWizardAdvanced.assertDetectorFunctionSelection([]);
|
||||
await ml.jobWizardAdvanced.assertDetectorFieldInputExists();
|
||||
await ml.jobWizardAdvanced.assertDetectorFieldSelection([]);
|
||||
await ml.jobWizardAdvanced.assertDetectorByFieldInputExists();
|
||||
await ml.jobWizardAdvanced.assertDetectorByFieldSelection([]);
|
||||
await ml.jobWizardAdvanced.assertDetectorOverFieldInputExists();
|
||||
await ml.jobWizardAdvanced.assertDetectorOverFieldSelection([]);
|
||||
await ml.jobWizardAdvanced.assertDetectorPartitionFieldInputExists();
|
||||
await ml.jobWizardAdvanced.assertDetectorPartitionFieldSelection([]);
|
||||
await ml.jobWizardAdvanced.assertDetectorExcludeFrequentInputExists();
|
||||
await ml.jobWizardAdvanced.assertDetectorExcludeFrequentSelection([]);
|
||||
await ml.jobWizardAdvanced.assertDetectorDescriptionInputExists();
|
||||
await ml.jobWizardAdvanced.assertDetectorDescriptionValue('');
|
||||
|
||||
await ml.jobWizardAdvanced.selectDetectorFunction(detector.function);
|
||||
if (detector.hasOwnProperty('field')) {
|
||||
await ml.jobWizardAdvanced.selectDetectorField(detector.field!);
|
||||
}
|
||||
if (detector.hasOwnProperty('byField')) {
|
||||
await ml.jobWizardAdvanced.selectDetectorByField(detector.byField!);
|
||||
}
|
||||
if (detector.hasOwnProperty('overField')) {
|
||||
await ml.jobWizardAdvanced.selectDetectorOverField(detector.overField!);
|
||||
}
|
||||
if (detector.hasOwnProperty('partitionField')) {
|
||||
await ml.jobWizardAdvanced.selectDetectorPartitionField(detector.partitionField!);
|
||||
}
|
||||
if (detector.hasOwnProperty('excludeFrequent')) {
|
||||
await ml.jobWizardAdvanced.selectDetectorExcludeFrequent(detector.excludeFrequent!);
|
||||
}
|
||||
if (detector.hasOwnProperty('description')) {
|
||||
await ml.jobWizardAdvanced.setDetectorDescription(detector.description!);
|
||||
}
|
||||
|
||||
await ml.jobWizardAdvanced.confirmAddDetectorModal();
|
||||
}
|
||||
|
||||
await ml.testExecution.logTestStep('advanced job creation displays detector entries');
|
||||
for (const [index, detector] of testData.pickFieldsConfig.detectors.entries()) {
|
||||
await ml.jobWizardAdvanced.assertDetectorEntryExists(
|
||||
index + previousDetectors.length,
|
||||
detector.identifier,
|
||||
detector.hasOwnProperty('description') ? detector.description! : undefined
|
||||
);
|
||||
}
|
||||
|
||||
await ml.testExecution.logTestStep('advanced job creation retains the bucket span');
|
||||
await ml.jobWizardCommon.assertBucketSpanInputExists();
|
||||
await ml.jobWizardCommon.assertBucketSpanValue(bucketSpan);
|
||||
|
||||
await ml.testExecution.logTestStep(
|
||||
`advanced job creation retains influencers from ${previousJobWizard}`
|
||||
);
|
||||
await ml.jobWizardCommon.assertInfluencerInputExists();
|
||||
await ml.jobWizardCommon.assertInfluencerSelection(previousInfluencers);
|
||||
for (const influencer of testData.pickFieldsConfig.influencers) {
|
||||
await ml.jobWizardCommon.addInfluencer(influencer);
|
||||
}
|
||||
|
||||
await ml.testExecution.logTestStep('advanced job creation inputs the model memory limit');
|
||||
await ml.jobWizardCommon.assertModelMemoryLimitInputExists({
|
||||
withAdvancedSection: false,
|
||||
});
|
||||
await ml.jobWizardCommon.setModelMemoryLimit(testData.pickFieldsConfig.memoryLimit, {
|
||||
withAdvancedSection: false,
|
||||
});
|
||||
|
||||
await ml.testExecution.logTestStep('advanced job creation displays the job details step');
|
||||
await ml.jobWizardCommon.advanceToJobDetailsSection();
|
||||
|
||||
await ml.testExecution.logTestStep(
|
||||
`advanced job creation retains the job id from ${previousJobWizard}`
|
||||
);
|
||||
await ml.jobWizardCommon.assertJobIdInputExists();
|
||||
await ml.jobWizardCommon.assertJobIdValue(testData.jobId);
|
||||
|
||||
await ml.testExecution.logTestStep(
|
||||
`advanced job creation retains the job description from ${previousJobWizard}`
|
||||
);
|
||||
await ml.jobWizardCommon.assertJobDescriptionInputExists();
|
||||
await ml.jobWizardCommon.assertJobDescriptionValue(testData.jobDescription);
|
||||
|
||||
await ml.testExecution.logTestStep(
|
||||
`advanced job creation retains job groups and inputs new groups from ${previousJobWizard}`
|
||||
);
|
||||
await ml.jobWizardCommon.assertJobGroupInputExists();
|
||||
for (const jobGroup of testData.jobGroups) {
|
||||
await ml.jobWizardCommon.addJobGroup(jobGroup);
|
||||
}
|
||||
await ml.jobWizardCommon.assertJobGroupSelection([
|
||||
...previousJobGroups,
|
||||
...testData.jobGroups,
|
||||
]);
|
||||
|
||||
await ml.testExecution.logTestStep(
|
||||
'advanced job creation opens the additional settings section'
|
||||
);
|
||||
await ml.jobWizardCommon.ensureAdditionalSettingsSectionOpen();
|
||||
|
||||
await ml.testExecution.logTestStep(
|
||||
`advanced job creation retains calendar and custom url from ${previousJobWizard}`
|
||||
);
|
||||
await ml.jobWizardCommon.assertCalendarsSelection([calendarId]);
|
||||
await ml.jobWizardCommon.assertCustomUrlLabel(0, { label: 'check-kibana-dashboard' });
|
||||
|
||||
await ml.testExecution.logTestStep('advanced job creation displays the validation step');
|
||||
await ml.jobWizardCommon.advanceToValidationSection();
|
||||
|
||||
await ml.testExecution.logTestStep('advanced job creation displays the summary step');
|
||||
await ml.jobWizardCommon.advanceToSummarySection();
|
||||
});
|
||||
|
||||
it('advanced job creation runs the job and displays it correctly in the job list', async () => {
|
||||
await ml.testExecution.logTestStep('advanced job creates the job and finishes processing');
|
||||
await ml.jobWizardCommon.assertCreateJobButtonExists();
|
||||
await ml.jobWizardAdvanced.createJob();
|
||||
await ml.jobManagement.assertStartDatafeedModalExists();
|
||||
await ml.jobManagement.confirmStartDatafeedModal();
|
||||
|
||||
await ml.testExecution.logTestStep(
|
||||
'advanced job creation displays the created job in the job list'
|
||||
);
|
||||
await ml.jobTable.filterWithSearchString(testData.jobId, 1);
|
||||
});
|
||||
};
|
||||
|
||||
describe('conversion to advanced job wizard', function () {
|
||||
this.tags(['ml']);
|
||||
|
||||
before(async () => {
|
||||
await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/categorization_small');
|
||||
await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/ecommerce');
|
||||
await ml.testResources.createIndexPatternIfNeeded('ft_ecommerce', 'order_date');
|
||||
await ml.testResources.createIndexPatternIfNeeded('ft_categorization_small', '@timestamp');
|
||||
|
||||
await ml.testResources.setKibanaTimeZoneToUTC();
|
||||
|
||||
await ml.api.createCalendar(calendarId);
|
||||
await ml.securityUI.loginAsMlPowerUser();
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await ml.api.cleanMlIndices();
|
||||
await ml.testResources.deleteIndexPatternByTitle('ft_ecommerce');
|
||||
await ml.testResources.deleteIndexPatternByTitle('ft_categorization_small');
|
||||
});
|
||||
|
||||
describe('from multi-metric job creation wizard', function () {
|
||||
const jobGroups = ['automated', 'farequote', 'multi-metric'];
|
||||
const splitField = 'customer_gender';
|
||||
const multiMetricDetectors = [
|
||||
{
|
||||
identifier: 'High count(Event rate)',
|
||||
advancedJobIdentifier: 'high_count partition_field_name=customer_gender',
|
||||
},
|
||||
{
|
||||
identifier: 'Low mean(products.base_price)',
|
||||
advancedJobIdentifier:
|
||||
'low_mean("products.base_price") partition_field_name=customer_gender',
|
||||
},
|
||||
];
|
||||
const multiMetricInfluencers = ['customer_gender'];
|
||||
|
||||
const bucketSpan = '2h';
|
||||
const memoryLimit = '8mb';
|
||||
|
||||
const testData = {
|
||||
suiteTitle: 'multi-metric job to advanced job wizard',
|
||||
jobSource: 'ft_ecommerce',
|
||||
jobId: `ec_multimetric_to_advanced_1_${Date.now()}`,
|
||||
jobDescription: 'advanced job from multi-metric wizard',
|
||||
jobGroups: ['advanced'],
|
||||
pickFieldsConfig: {
|
||||
detectors: [],
|
||||
influencers: ['geoip.region_name'],
|
||||
bucketSpan: '1h',
|
||||
memoryLimit: '10mb',
|
||||
} as PickFieldsConfig,
|
||||
datafeedConfig: {
|
||||
queryDelay: '55s',
|
||||
frequency: '350s',
|
||||
scrollSize: '999',
|
||||
} as DatafeedConfig,
|
||||
};
|
||||
|
||||
it('multi-metric job creation loads the multi-metric job wizard for the source data', async () => {
|
||||
await ml.testExecution.logTestStep('job creation loads the job management page');
|
||||
await ml.navigation.navigateToMl();
|
||||
await ml.navigation.navigateToJobManagement();
|
||||
|
||||
await ml.testExecution.logTestStep('job creation loads the new job source selection page');
|
||||
await ml.jobManagement.navigateToNewJobSourceSelection();
|
||||
|
||||
await ml.testExecution.logTestStep('job creation loads the job type selection page');
|
||||
await ml.jobSourceSelection.selectSourceForAnomalyDetectionJob('ft_ecommerce');
|
||||
|
||||
await ml.testExecution.logTestStep('job creation loads the multi-metric job wizard page');
|
||||
await ml.jobTypeSelection.selectMultiMetricJob();
|
||||
});
|
||||
|
||||
it('multi-metric job creation navigates through the multi-metric job wizard and sets all needed fields', async () => {
|
||||
await ml.testExecution.logTestStep('job creation displays the time range step');
|
||||
await ml.jobWizardCommon.assertTimeRangeSectionExists();
|
||||
|
||||
await ml.testExecution.logTestStep('job creation sets the time range');
|
||||
await ml.jobWizardCommon.clickUseFullDataButton(
|
||||
'Jun 12, 2019 @ 00:04:19.000',
|
||||
'Jul 12, 2019 @ 23:45:36.000'
|
||||
);
|
||||
|
||||
await ml.testExecution.logTestStep(
|
||||
'multi-metric job creation displays the event rate chart'
|
||||
);
|
||||
await ml.jobWizardCommon.assertEventRateChartExists();
|
||||
await ml.jobWizardCommon.assertEventRateChartHasData();
|
||||
|
||||
await ml.testExecution.logTestStep(
|
||||
'multi-metric job creation displays the pick fields step'
|
||||
);
|
||||
await ml.jobWizardCommon.advanceToPickFieldsSection();
|
||||
|
||||
for (const [
|
||||
index,
|
||||
{ identifier: aggAndFieldIdentifier },
|
||||
] of multiMetricDetectors.entries()) {
|
||||
await ml.jobWizardCommon.assertAggAndFieldInputExists();
|
||||
await ml.jobWizardCommon.selectAggAndField(aggAndFieldIdentifier, false);
|
||||
await ml.jobWizardCommon.assertDetectorPreviewExists(
|
||||
aggAndFieldIdentifier,
|
||||
index,
|
||||
'LINE'
|
||||
);
|
||||
}
|
||||
|
||||
await ml.testExecution.logTestStep(
|
||||
'multi-metric job creation inputs the split field and displays split cards'
|
||||
);
|
||||
await ml.jobWizardMultiMetric.assertSplitFieldInputExists();
|
||||
await ml.jobWizardMultiMetric.selectSplitField(splitField);
|
||||
|
||||
await ml.jobWizardMultiMetric.assertDetectorSplitExists(splitField);
|
||||
await ml.jobWizardMultiMetric.assertDetectorSplitFrontCardTitle('FEMALE');
|
||||
await ml.jobWizardMultiMetric.assertDetectorSplitNumberOfBackCards(1);
|
||||
|
||||
await ml.testExecution.logTestStep(
|
||||
'multi-metric job creation displays the influencer field'
|
||||
);
|
||||
await ml.jobWizardCommon.assertInfluencerInputExists();
|
||||
await ml.jobWizardCommon.assertInfluencerSelection(multiMetricInfluencers);
|
||||
|
||||
await ml.testExecution.logTestStep('multi-metric job creation inputs the bucket span');
|
||||
await ml.jobWizardCommon.assertBucketSpanInputExists();
|
||||
await ml.jobWizardCommon.setBucketSpan(bucketSpan);
|
||||
|
||||
await ml.testExecution.logTestStep(
|
||||
'multi-metric job creation displays the job details step'
|
||||
);
|
||||
await ml.jobWizardCommon.advanceToJobDetailsSection();
|
||||
|
||||
await ml.testExecution.logTestStep('multi-metric job creation inputs the job id');
|
||||
await ml.jobWizardCommon.assertJobIdInputExists();
|
||||
await ml.jobWizardCommon.setJobId(testData.jobId);
|
||||
|
||||
await ml.testExecution.logTestStep('multi-metric job creation inputs the job description');
|
||||
await ml.jobWizardCommon.assertJobDescriptionInputExists();
|
||||
await ml.jobWizardCommon.setJobDescription(testData.jobDescription);
|
||||
|
||||
await ml.testExecution.logTestStep('multi-metric job creation inputs job groups');
|
||||
await ml.jobWizardCommon.assertJobGroupInputExists();
|
||||
for (const jobGroup of jobGroups) {
|
||||
await ml.jobWizardCommon.addJobGroup(jobGroup);
|
||||
}
|
||||
await ml.jobWizardCommon.assertJobGroupSelection(jobGroups);
|
||||
|
||||
await ml.testExecution.logTestStep(
|
||||
'multi-metric job creation opens the additional settings section'
|
||||
);
|
||||
await ml.jobWizardCommon.ensureAdditionalSettingsSectionOpen();
|
||||
|
||||
await ml.testExecution.logTestStep('multi-metric job creation adds a new custom url');
|
||||
await ml.jobWizardCommon.addCustomUrl({ label: 'check-kibana-dashboard' });
|
||||
|
||||
await ml.testExecution.logTestStep('multi-metric job creation assigns calendars');
|
||||
await ml.jobWizardCommon.addCalendar(calendarId);
|
||||
|
||||
await ml.testExecution.logTestStep('multi-metric job creation opens the advanced section');
|
||||
await ml.jobWizardCommon.ensureAdvancedSectionOpen();
|
||||
|
||||
await ml.testExecution.logTestStep(
|
||||
'multi-metric job creation displays the model plot switch'
|
||||
);
|
||||
await ml.jobWizardCommon.assertModelPlotSwitchExists();
|
||||
|
||||
await ml.testExecution.logTestStep(
|
||||
'multi-metric job creation enables the dedicated index switch'
|
||||
);
|
||||
await ml.jobWizardCommon.assertDedicatedIndexSwitchExists();
|
||||
await ml.jobWizardCommon.activateDedicatedIndexSwitch();
|
||||
|
||||
await ml.testExecution.logTestStep(
|
||||
'multi-metric job creation inputs the model memory limit'
|
||||
);
|
||||
await ml.jobWizardCommon.assertModelMemoryLimitInputExists();
|
||||
await ml.jobWizardCommon.setModelMemoryLimit(memoryLimit);
|
||||
|
||||
await ml.testExecution.logTestStep(
|
||||
'multi-metric job creation displays the validation step'
|
||||
);
|
||||
await ml.jobWizardCommon.advanceToValidationSection();
|
||||
|
||||
await ml.testExecution.logTestStep('multi-metric job creation displays the summary step');
|
||||
await ml.jobWizardCommon.advanceToSummarySection();
|
||||
});
|
||||
|
||||
assertConversionToAdvancedJobWizardRetainsSettingsAndRuns({
|
||||
testSuite: 'multi-metric',
|
||||
testData,
|
||||
bucketSpan,
|
||||
previousInfluencers: multiMetricInfluencers,
|
||||
previousDetectors: multiMetricDetectors,
|
||||
previousJobGroups: jobGroups,
|
||||
});
|
||||
});
|
||||
|
||||
describe('from population job creation wizard', function () {
|
||||
const jobGroups = ['automated', 'ecommerce', 'population'];
|
||||
const populationField = 'customer_id';
|
||||
const populationDetectors = [
|
||||
{
|
||||
identifier: 'Mean(products.base_price)',
|
||||
splitField: 'customer_gender',
|
||||
frontCardTitle: 'FEMALE',
|
||||
numberOfBackCards: 1,
|
||||
advancedJobIdentifier: 'mean("products.base_price") by customer_gender over customer_id',
|
||||
},
|
||||
{
|
||||
identifier: 'Mean(products.quantity)',
|
||||
splitField: 'category.keyword',
|
||||
frontCardTitle: "Men's Clothing",
|
||||
numberOfBackCards: 5,
|
||||
advancedJobIdentifier: 'mean("products.quantity") by "category.keyword" over customer_id',
|
||||
},
|
||||
];
|
||||
const populationInfluencers = [populationField].concat(
|
||||
populationDetectors.map((detector) => detector.splitField)
|
||||
);
|
||||
|
||||
const bucketSpan = '2h';
|
||||
const memoryLimit = '8mb';
|
||||
|
||||
const testData = {
|
||||
suiteTitle: 'population job to advanced job wizard',
|
||||
jobSource: 'ft_ecommerce',
|
||||
jobId: `ec_population_to_advanced_1_${Date.now()}`,
|
||||
jobDescription: 'advanced job from population wizard',
|
||||
jobGroups: ['advanced'],
|
||||
pickFieldsConfig: {
|
||||
detectors: [
|
||||
{
|
||||
identifier: 'high_count',
|
||||
function: 'high_count',
|
||||
description: 'high_count detector without split',
|
||||
} as Detector,
|
||||
{
|
||||
identifier: 'mean("products.base_price") by "category.keyword"',
|
||||
function: 'mean',
|
||||
field: 'products.base_price',
|
||||
byField: 'category.keyword',
|
||||
} as Detector,
|
||||
{
|
||||
identifier: 'sum("products.discount_amount") over customer_id',
|
||||
function: 'sum',
|
||||
field: 'products.discount_amount',
|
||||
overField: 'customer_id',
|
||||
} as Detector,
|
||||
{
|
||||
identifier: 'median(total_quantity) partition_field_name=customer_gender',
|
||||
function: 'median',
|
||||
field: 'total_quantity',
|
||||
partitionField: 'customer_gender',
|
||||
} as Detector,
|
||||
{
|
||||
identifier:
|
||||
'max(total_quantity) by "geoip.continent_name" over customer_id partition_field_name=customer_gender',
|
||||
function: 'max',
|
||||
field: 'total_quantity',
|
||||
byField: 'geoip.continent_name',
|
||||
overField: 'customer_id',
|
||||
partitionField: 'customer_gender',
|
||||
} as Detector,
|
||||
],
|
||||
influencers: ['geoip.continent_name'],
|
||||
bucketSpan: '1h',
|
||||
memoryLimit: '10mb',
|
||||
} as PickFieldsConfig,
|
||||
datafeedConfig: {
|
||||
queryDelay: '55s',
|
||||
frequency: '350s',
|
||||
scrollSize: '999',
|
||||
} as DatafeedConfig,
|
||||
};
|
||||
|
||||
it('population job creation loads the population wizard for the source data', async () => {
|
||||
await ml.testExecution.logTestStep('job creation loads the job management page');
|
||||
await ml.navigation.navigateToMl();
|
||||
await ml.navigation.navigateToJobManagement();
|
||||
|
||||
await ml.testExecution.logTestStep('job creation loads the new job source selection page');
|
||||
await ml.jobManagement.navigateToNewJobSourceSelection();
|
||||
|
||||
await ml.testExecution.logTestStep('job creation loads the job type selection page');
|
||||
await ml.jobSourceSelection.selectSourceForAnomalyDetectionJob('ft_ecommerce');
|
||||
|
||||
await ml.testExecution.logTestStep('job creation loads the population job wizard page');
|
||||
await ml.jobTypeSelection.selectPopulationJob();
|
||||
});
|
||||
|
||||
it('population job creation navigates through the population wizard and sets all needed fields', async () => {
|
||||
await ml.testExecution.logTestStep('job creation displays the time range step');
|
||||
await ml.jobWizardCommon.assertTimeRangeSectionExists();
|
||||
|
||||
await ml.testExecution.logTestStep('job creation sets the time range');
|
||||
await ml.jobWizardCommon.clickUseFullDataButton(
|
||||
'Jun 12, 2019 @ 00:04:19.000',
|
||||
'Jul 12, 2019 @ 23:45:36.000'
|
||||
);
|
||||
|
||||
await ml.testExecution.logTestStep('population job creation displays the event rate chart');
|
||||
await ml.jobWizardCommon.assertEventRateChartExists();
|
||||
await ml.jobWizardCommon.assertEventRateChartHasData();
|
||||
|
||||
await ml.testExecution.logTestStep('population job creation displays the pick fields step');
|
||||
await ml.jobWizardCommon.advanceToPickFieldsSection();
|
||||
|
||||
await ml.testExecution.logTestStep('population job creation selects the population field');
|
||||
await ml.jobWizardPopulation.selectPopulationField(populationField);
|
||||
|
||||
await ml.testExecution.logTestStep(
|
||||
'population job creation selects populationDetectors and displays detector previews'
|
||||
);
|
||||
for (const [index, detector] of populationDetectors.entries()) {
|
||||
await ml.jobWizardCommon.assertAggAndFieldInputExists();
|
||||
await ml.jobWizardCommon.selectAggAndField(detector.identifier, false);
|
||||
await ml.jobWizardCommon.assertDetectorPreviewExists(
|
||||
detector.identifier,
|
||||
index,
|
||||
'SCATTER'
|
||||
);
|
||||
}
|
||||
|
||||
await ml.testExecution.logTestStep(
|
||||
'population job creation inputs detector split fields and displays split cards'
|
||||
);
|
||||
for (const [index, detector] of populationDetectors.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
|
||||
);
|
||||
}
|
||||
|
||||
await ml.testExecution.logTestStep('population job creation displays the influencer field');
|
||||
await ml.jobWizardCommon.assertInfluencerInputExists();
|
||||
await ml.jobWizardCommon.assertInfluencerSelection(populationInfluencers);
|
||||
|
||||
await ml.testExecution.logTestStep('population job creation inputs the bucket span');
|
||||
await ml.jobWizardCommon.assertBucketSpanInputExists();
|
||||
await ml.jobWizardCommon.setBucketSpan(bucketSpan);
|
||||
|
||||
await ml.testExecution.logTestStep('population job creation displays the job details step');
|
||||
await ml.jobWizardCommon.advanceToJobDetailsSection();
|
||||
|
||||
await ml.testExecution.logTestStep('population job creation inputs the job id');
|
||||
await ml.jobWizardCommon.assertJobIdInputExists();
|
||||
await ml.jobWizardCommon.setJobId(testData.jobId);
|
||||
|
||||
await ml.testExecution.logTestStep('population job creation inputs the job description');
|
||||
await ml.jobWizardCommon.assertJobDescriptionInputExists();
|
||||
await ml.jobWizardCommon.setJobDescription(testData.jobDescription);
|
||||
|
||||
await ml.testExecution.logTestStep('population job creation inputs job groups');
|
||||
await ml.jobWizardCommon.assertJobGroupInputExists();
|
||||
for (const jobGroup of jobGroups) {
|
||||
await ml.jobWizardCommon.addJobGroup(jobGroup);
|
||||
}
|
||||
await ml.jobWizardCommon.assertJobGroupSelection(jobGroups);
|
||||
|
||||
await ml.testExecution.logTestStep(
|
||||
'population job creation opens the additional settings section'
|
||||
);
|
||||
await ml.jobWizardCommon.ensureAdditionalSettingsSectionOpen();
|
||||
|
||||
await ml.testExecution.logTestStep('population job creation adds a new custom url');
|
||||
await ml.jobWizardCommon.addCustomUrl({ label: 'check-kibana-dashboard' });
|
||||
|
||||
await ml.testExecution.logTestStep('population job creation assigns calendars');
|
||||
await ml.jobWizardCommon.addCalendar(calendarId);
|
||||
|
||||
await ml.testExecution.logTestStep('population job creation opens the advanced section');
|
||||
await ml.jobWizardCommon.ensureAdvancedSectionOpen();
|
||||
|
||||
await ml.testExecution.logTestStep(
|
||||
'population job creation displays the model plot switch'
|
||||
);
|
||||
await ml.jobWizardCommon.assertModelPlotSwitchExists();
|
||||
|
||||
await ml.testExecution.logTestStep(
|
||||
'population job creation enables the dedicated index switch'
|
||||
);
|
||||
await ml.jobWizardCommon.assertDedicatedIndexSwitchExists();
|
||||
await ml.jobWizardCommon.activateDedicatedIndexSwitch();
|
||||
|
||||
await ml.testExecution.logTestStep('population job creation inputs the model memory limit');
|
||||
await ml.jobWizardCommon.assertModelMemoryLimitInputExists();
|
||||
await ml.jobWizardCommon.setModelMemoryLimit(memoryLimit);
|
||||
|
||||
await ml.testExecution.logTestStep('population job creation displays the validation step');
|
||||
await ml.jobWizardCommon.advanceToValidationSection();
|
||||
|
||||
await ml.testExecution.logTestStep('population job creation displays the summary step');
|
||||
await ml.jobWizardCommon.advanceToSummarySection();
|
||||
});
|
||||
|
||||
assertConversionToAdvancedJobWizardRetainsSettingsAndRuns({
|
||||
testSuite: 'population',
|
||||
testData,
|
||||
bucketSpan,
|
||||
previousInfluencers: populationInfluencers,
|
||||
previousDetectors: populationDetectors,
|
||||
previousJobGroups: jobGroups,
|
||||
});
|
||||
});
|
||||
|
||||
describe('from categorization job creation wizard', function () {
|
||||
const jobGroups = ['automated', 'categorization'];
|
||||
const detectorTypeIdentifier = 'Rare';
|
||||
const categorizationFieldIdentifier = 'field1';
|
||||
const categorizationExampleCount = 5;
|
||||
const bucketSpan = '1d';
|
||||
const memoryLimit = '15MB';
|
||||
const categorizationInfluencers = ['mlcategory'];
|
||||
const testData = {
|
||||
suiteTitle: 'categorization job to advanced job wizard',
|
||||
jobSource: 'ft_ecommerce',
|
||||
jobId: `categorization_to_advanced_${Date.now()}`,
|
||||
jobDescription: 'advanced job from categorization wizard',
|
||||
jobGroups: ['advanced'],
|
||||
categorizationFieldIdentifier,
|
||||
pickFieldsConfig: {
|
||||
categorizationField: 'field1',
|
||||
detectors: [],
|
||||
influencers: [],
|
||||
bucketSpan: '1h',
|
||||
memoryLimit: '10mb',
|
||||
} as PickFieldsConfig,
|
||||
datafeedConfig: {
|
||||
queryDelay: '55s',
|
||||
frequency: '350s',
|
||||
scrollSize: '999',
|
||||
} as DatafeedConfig,
|
||||
};
|
||||
const categorizationDetectors = [{ advancedJobIdentifier: 'rare by mlcategory' }];
|
||||
|
||||
it('categorization job creation loads the categorization wizard for the source data', async () => {
|
||||
await ml.testExecution.logTestStep(
|
||||
'categorization job creation loads the job management page'
|
||||
);
|
||||
await ml.testExecution.logTestStep('');
|
||||
await ml.navigation.navigateToMl();
|
||||
await ml.navigation.navigateToJobManagement();
|
||||
|
||||
await ml.testExecution.logTestStep(
|
||||
'categorization job creation loads the new job source selection page'
|
||||
);
|
||||
await ml.jobManagement.navigateToNewJobSourceSelection();
|
||||
|
||||
await ml.testExecution.logTestStep(
|
||||
'categorization job creation loads the job type selection page'
|
||||
);
|
||||
await ml.jobSourceSelection.selectSourceForAnomalyDetectionJob('ft_categorization_small');
|
||||
|
||||
await ml.testExecution.logTestStep(
|
||||
'categorization job creation loads the categorization job wizard page'
|
||||
);
|
||||
await ml.jobTypeSelection.selectCategorizationJob();
|
||||
});
|
||||
|
||||
it('categorization job creation navigates through the categorization wizard and sets all needed fields', async () => {
|
||||
await ml.testExecution.logTestStep(
|
||||
'categorization job creation displays the time range step'
|
||||
);
|
||||
await ml.jobWizardCommon.assertTimeRangeSectionExists();
|
||||
|
||||
await ml.testExecution.logTestStep('categorization job creation sets the time range');
|
||||
await ml.jobWizardCommon.clickUseFullDataButton(
|
||||
'Apr 5, 2019 @ 11:25:35.770',
|
||||
'Nov 21, 2019 @ 00:01:13.923'
|
||||
);
|
||||
|
||||
await ml.testExecution.logTestStep(
|
||||
'categorization job creation displays the event rate chart'
|
||||
);
|
||||
await ml.jobWizardCommon.assertEventRateChartExists();
|
||||
await ml.jobWizardCommon.assertEventRateChartHasData();
|
||||
|
||||
await ml.testExecution.logTestStep(
|
||||
'categorization job creation displays the pick fields step'
|
||||
);
|
||||
await ml.jobWizardCommon.advanceToPickFieldsSection();
|
||||
|
||||
await ml.testExecution.logTestStep(
|
||||
`categorization job creation selects ${detectorTypeIdentifier} detector type`
|
||||
);
|
||||
await ml.jobWizardCategorization.assertCategorizationDetectorTypeSelectionExists();
|
||||
await ml.jobWizardCategorization.selectCategorizationDetectorType(detectorTypeIdentifier);
|
||||
|
||||
await ml.testExecution.logTestStep(
|
||||
`categorization job creation selects the categorization field`
|
||||
);
|
||||
await ml.jobWizardCategorization.selectCategorizationField(
|
||||
testData.categorizationFieldIdentifier
|
||||
);
|
||||
await ml.jobWizardCategorization.assertCategorizationExamplesCallout(
|
||||
CATEGORY_EXAMPLES_VALIDATION_STATUS.VALID
|
||||
);
|
||||
await ml.jobWizardCategorization.assertCategorizationExamplesTable(
|
||||
categorizationExampleCount
|
||||
);
|
||||
|
||||
await ml.testExecution.logTestStep('categorization job creation inputs the bucket span');
|
||||
await ml.jobWizardCommon.assertBucketSpanInputExists();
|
||||
await ml.jobWizardCommon.setBucketSpan(bucketSpan);
|
||||
|
||||
await ml.testExecution.logTestStep(
|
||||
'categorization job creation displays the job details step'
|
||||
);
|
||||
await ml.jobWizardCommon.advanceToJobDetailsSection();
|
||||
|
||||
await ml.testExecution.logTestStep('categorization job creation inputs the job id');
|
||||
await ml.jobWizardCommon.assertJobIdInputExists();
|
||||
await ml.jobWizardCommon.setJobId(testData.jobId);
|
||||
|
||||
await ml.testExecution.logTestStep(
|
||||
'categorization job creation inputs the job description'
|
||||
);
|
||||
await ml.jobWizardCommon.assertJobDescriptionInputExists();
|
||||
await ml.jobWizardCommon.setJobDescription(testData.jobDescription);
|
||||
|
||||
await ml.testExecution.logTestStep('categorization job creation inputs job groups');
|
||||
await ml.jobWizardCommon.assertJobGroupInputExists();
|
||||
for (const jobGroup of jobGroups) {
|
||||
await ml.jobWizardCommon.addJobGroup(jobGroup);
|
||||
}
|
||||
await ml.jobWizardCommon.assertJobGroupSelection(jobGroups);
|
||||
|
||||
await ml.testExecution.logTestStep(
|
||||
'categorization job creation opens the additional settings section'
|
||||
);
|
||||
await ml.jobWizardCommon.ensureAdditionalSettingsSectionOpen();
|
||||
|
||||
await ml.testExecution.logTestStep('categorization job creation adds a new custom url');
|
||||
await ml.jobWizardCommon.addCustomUrl({ label: 'check-kibana-dashboard' });
|
||||
|
||||
await ml.testExecution.logTestStep('categorization job creation assigns calendars');
|
||||
await ml.jobWizardCommon.addCalendar(calendarId);
|
||||
|
||||
await ml.testExecution.logTestStep(
|
||||
'categorization job creation opens the advanced section'
|
||||
);
|
||||
await ml.jobWizardCommon.ensureAdvancedSectionOpen();
|
||||
|
||||
await ml.testExecution.logTestStep(
|
||||
'categorization job creation displays the model plot switch'
|
||||
);
|
||||
await ml.jobWizardCommon.assertModelPlotSwitchExists();
|
||||
await ml.jobWizardCommon.assertModelPlotSwitchEnabled(false);
|
||||
await ml.jobWizardCommon.assertModelPlotSwitchCheckedState(false);
|
||||
|
||||
await ml.testExecution.logTestStep(
|
||||
'categorization job creation enables the dedicated index switch'
|
||||
);
|
||||
await ml.jobWizardCommon.assertDedicatedIndexSwitchExists();
|
||||
await ml.jobWizardCommon.activateDedicatedIndexSwitch();
|
||||
|
||||
await ml.testExecution.logTestStep(
|
||||
'categorization job creation inputs the model memory limit'
|
||||
);
|
||||
await ml.jobWizardCommon.assertModelMemoryLimitInputExists();
|
||||
await ml.jobWizardCommon.setModelMemoryLimit(memoryLimit);
|
||||
|
||||
await ml.testExecution.logTestStep(
|
||||
'categorization job creation displays the validation step'
|
||||
);
|
||||
await ml.jobWizardCommon.advanceToValidationSection();
|
||||
|
||||
await ml.testExecution.logTestStep('categorization job creation displays the summary step');
|
||||
await ml.jobWizardCommon.advanceToSummarySection();
|
||||
});
|
||||
|
||||
assertConversionToAdvancedJobWizardRetainsSettingsAndRuns({
|
||||
testSuite: 'categorization',
|
||||
testData,
|
||||
bucketSpan,
|
||||
previousInfluencers: categorizationInfluencers,
|
||||
previousDetectors: categorizationDetectors,
|
||||
previousJobGroups: jobGroups,
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
|
@ -0,0 +1,213 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { FtrProviderContext } from '../../../ftr_provider_context';
|
||||
|
||||
export default function ({ getService }: FtrProviderContext) {
|
||||
const config = getService('config');
|
||||
const esNode = config.get('esTestCluster.ccs')
|
||||
? getService('remoteEsArchiver' as 'esArchiver')
|
||||
: getService('esArchiver');
|
||||
const ml = getService('ml');
|
||||
|
||||
const calendarId = `wizard-test-calendar_${Date.now()}`;
|
||||
const remoteName = 'ftr-remote:';
|
||||
const indexPatternName = 'ft_farequote';
|
||||
const indexPatternString = config.get('esTestCluster.ccs')
|
||||
? remoteName + indexPatternName
|
||||
: indexPatternName;
|
||||
|
||||
describe('single metric job conversion to multi-metric job', function () {
|
||||
this.tags(['ml']);
|
||||
before(async () => {
|
||||
await esNode.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote');
|
||||
await ml.testResources.createIndexPatternIfNeeded(indexPatternString, '@timestamp');
|
||||
await ml.testResources.setKibanaTimeZoneToUTC();
|
||||
|
||||
await ml.api.createCalendar(calendarId);
|
||||
await ml.securityUI.loginAsMlPowerUser();
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await ml.api.cleanMlIndices();
|
||||
await ml.testResources.deleteIndexPatternByTitle(indexPatternString);
|
||||
});
|
||||
|
||||
const jobId = `fq_single_to_multi_${Date.now()}`;
|
||||
const jobDescription = 'Create multi metric job from single metric job';
|
||||
const jobGroups = ['automated', 'farequote', 'multi-metric'];
|
||||
const smAggAndFieldIdentifier = 'Mean(responsetime)';
|
||||
const bucketSpan = '30m';
|
||||
|
||||
const mmAggAndFieldIdentifiers = [
|
||||
'Min(responsetime)',
|
||||
'Max(responsetime)',
|
||||
'High mean(responsetime)',
|
||||
];
|
||||
const splitField = 'airline';
|
||||
|
||||
it('loads the single metric wizard for the source data', async () => {
|
||||
await ml.testExecution.logTestStep('loads the job management page');
|
||||
await ml.navigation.navigateToMl();
|
||||
await ml.navigation.navigateToJobManagement();
|
||||
|
||||
await ml.testExecution.logTestStep('loads the new job source selection page');
|
||||
await ml.jobManagement.navigateToNewJobSourceSelection();
|
||||
|
||||
await ml.testExecution.logTestStep('loads the job type selection page');
|
||||
await ml.jobSourceSelection.selectSourceForAnomalyDetectionJob(indexPatternString);
|
||||
|
||||
await ml.testExecution.logTestStep('loads the single metric job wizard page');
|
||||
await ml.jobTypeSelection.selectSingleMetricJob();
|
||||
});
|
||||
|
||||
it('navigates through the single metric wizard and sets all needed fields', async () => {
|
||||
await ml.testExecution.logTestStep('displays the time range step');
|
||||
await ml.jobWizardCommon.assertTimeRangeSectionExists();
|
||||
|
||||
await ml.testExecution.logTestStep('sets the time range');
|
||||
await ml.jobWizardCommon.clickUseFullDataButton(
|
||||
'Feb 7, 2016 @ 00:00:00.000',
|
||||
'Feb 11, 2016 @ 23:59:54.000'
|
||||
);
|
||||
|
||||
await ml.testExecution.logTestStep('displays the event rate chart');
|
||||
await ml.jobWizardCommon.assertEventRateChartExists();
|
||||
await ml.jobWizardCommon.assertEventRateChartHasData();
|
||||
|
||||
await ml.testExecution.logTestStep('displays the pick fields step');
|
||||
await ml.jobWizardCommon.advanceToPickFieldsSection();
|
||||
|
||||
await ml.testExecution.logTestStep('selects field and aggregation');
|
||||
await ml.jobWizardCommon.selectAggAndField(smAggAndFieldIdentifier, true);
|
||||
await ml.jobWizardCommon.assertAnomalyChartExists('LINE');
|
||||
|
||||
await ml.testExecution.logTestStep('inputs the bucket span');
|
||||
await ml.jobWizardCommon.assertBucketSpanInputExists();
|
||||
await ml.jobWizardCommon.setBucketSpan(bucketSpan);
|
||||
|
||||
await ml.testExecution.logTestStep('displays the job details step');
|
||||
await ml.jobWizardCommon.advanceToJobDetailsSection();
|
||||
|
||||
await ml.testExecution.logTestStep('inputs the job id');
|
||||
await ml.jobWizardCommon.assertJobIdInputExists();
|
||||
await ml.jobWizardCommon.setJobId(jobId);
|
||||
|
||||
await ml.testExecution.logTestStep('inputs the job description');
|
||||
await ml.jobWizardCommon.assertJobDescriptionInputExists();
|
||||
await ml.jobWizardCommon.setJobDescription(jobDescription);
|
||||
|
||||
await ml.testExecution.logTestStep('inputs job groups');
|
||||
await ml.jobWizardCommon.assertJobGroupInputExists();
|
||||
for (const jobGroup of jobGroups) {
|
||||
await ml.jobWizardCommon.addJobGroup(jobGroup);
|
||||
}
|
||||
await ml.jobWizardCommon.assertJobGroupSelection(jobGroups);
|
||||
|
||||
await ml.testExecution.logTestStep('opens the additional settings section');
|
||||
await ml.jobWizardCommon.ensureAdditionalSettingsSectionOpen();
|
||||
|
||||
await ml.testExecution.logTestStep('adds a new custom url');
|
||||
await ml.jobWizardCommon.addCustomUrl({ label: 'check-kibana-dashboard' });
|
||||
|
||||
await ml.testExecution.logTestStep('assigns calendars');
|
||||
await ml.jobWizardCommon.addCalendar(calendarId);
|
||||
});
|
||||
|
||||
it('converts to multi-metric job creation wizard and retains all previously set fields', async () => {
|
||||
await ml.testExecution.logTestStep(
|
||||
'navigates to previous page and converts to multi-metric job wizard'
|
||||
);
|
||||
await ml.jobWizardCommon.navigateToPreviousJobWizardPage(
|
||||
'mlJobWizardButtonConvertToMultiMetric'
|
||||
);
|
||||
await ml.jobWizardCommon.convertToMultiMetricJobWizard();
|
||||
await ml.jobWizardCommon.assertPickFieldsSectionExists();
|
||||
|
||||
await ml.testExecution.logTestStep(
|
||||
'multi-metric job wizard selects detectors and displays detector previews'
|
||||
);
|
||||
for (const [index, aggAndFieldIdentifier] of mmAggAndFieldIdentifiers.entries()) {
|
||||
await ml.jobWizardCommon.assertAggAndFieldInputExists();
|
||||
await ml.jobWizardCommon.selectAggAndField(aggAndFieldIdentifier, false);
|
||||
await ml.jobWizardCommon.assertDetectorPreviewExists(
|
||||
aggAndFieldIdentifier,
|
||||
// +1 to account for the one detector set from single metric job wizard
|
||||
index + 1,
|
||||
'LINE'
|
||||
);
|
||||
}
|
||||
|
||||
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]);
|
||||
|
||||
await ml.testExecution.logTestStep('multi-metric job wizard retains the bucket span');
|
||||
await ml.jobWizardCommon.assertBucketSpanInputExists();
|
||||
await ml.jobWizardCommon.assertBucketSpanValue(bucketSpan);
|
||||
|
||||
await ml.testExecution.logTestStep('multi-metric job wizard displays the job details step');
|
||||
await ml.jobWizardCommon.advanceToJobDetailsSection();
|
||||
|
||||
await ml.testExecution.logTestStep('multi-metric job wizard retains job id');
|
||||
await ml.jobWizardCommon.assertJobIdInputExists();
|
||||
await ml.jobWizardCommon.assertJobIdValue(jobId);
|
||||
|
||||
await ml.testExecution.logTestStep('multi-metric job wizard retains the job description');
|
||||
await ml.jobWizardCommon.assertJobDescriptionInputExists();
|
||||
await ml.jobWizardCommon.assertJobDescriptionValue(jobDescription);
|
||||
|
||||
await ml.testExecution.logTestStep('multi-metric job wizard retains job groups');
|
||||
await ml.jobWizardCommon.assertJobGroupInputExists();
|
||||
await ml.jobWizardCommon.assertJobGroupSelection(jobGroups);
|
||||
|
||||
await ml.testExecution.logTestStep(
|
||||
'multi-metric job wizard opens the additional settings section'
|
||||
);
|
||||
await ml.jobWizardCommon.ensureAdditionalSettingsSectionOpen();
|
||||
|
||||
await ml.testExecution.logTestStep('multi-metric job wizard retains calendar and custom url');
|
||||
await ml.jobWizardCommon.assertCalendarsSelection([calendarId]);
|
||||
await ml.jobWizardCommon.assertCustomUrlLabel(0, { label: 'check-kibana-dashboard' });
|
||||
|
||||
await ml.testExecution.logTestStep('multi-metric job wizard displays the validation step');
|
||||
await ml.jobWizardCommon.advanceToValidationSection();
|
||||
|
||||
await ml.testExecution.logTestStep('multi-metric job wizard displays the summary step');
|
||||
await ml.jobWizardCommon.advanceToSummarySection();
|
||||
});
|
||||
|
||||
it('runs the converted job and displays it correctly in the job list', async () => {
|
||||
await ml.testExecution.logTestStep(
|
||||
'multi-metric job wizard creates the job and finishes processing'
|
||||
);
|
||||
await ml.jobWizardCommon.assertCreateJobButtonExists();
|
||||
await ml.jobWizardCommon.createJobAndWaitForCompletion();
|
||||
|
||||
await ml.testExecution.logTestStep(
|
||||
'multi-metric job wizard displays the created job in the job list'
|
||||
);
|
||||
await ml.navigation.navigateToMl();
|
||||
await ml.navigation.navigateToJobManagement();
|
||||
|
||||
await ml.jobTable.filterWithSearchString(jobId, 1);
|
||||
|
||||
await ml.testExecution.logTestStep(
|
||||
'job list displays details for the created job in the job list'
|
||||
);
|
||||
|
||||
await ml.testExecution.logTestStep('job has detector results');
|
||||
for (let i = 0; i < mmAggAndFieldIdentifiers.length; i++) {
|
||||
await ml.api.assertDetectorResultsExist(jobId, i);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
|
@ -6,31 +6,7 @@
|
|||
*/
|
||||
|
||||
import { FtrProviderContext } from '../../../ftr_provider_context';
|
||||
|
||||
interface Detector {
|
||||
identifier: string;
|
||||
function: string;
|
||||
field?: string;
|
||||
byField?: string;
|
||||
overField?: string;
|
||||
partitionField?: string;
|
||||
excludeFrequent?: string;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
interface DatafeedConfig {
|
||||
queryDelay?: string;
|
||||
frequency?: string;
|
||||
scrollSize?: string;
|
||||
}
|
||||
|
||||
interface PickFieldsConfig {
|
||||
detectors: Detector[];
|
||||
influencers: string[];
|
||||
bucketSpan: string;
|
||||
memoryLimit: string;
|
||||
summaryCountField?: string;
|
||||
}
|
||||
import type { PickFieldsConfig, DatafeedConfig, Detector } from './types';
|
||||
|
||||
export default function ({ getService }: FtrProviderContext) {
|
||||
const esArchiver = getService('esArchiver');
|
||||
|
|
|
@ -50,6 +50,8 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) {
|
|||
loadTestFile(require.resolve('./date_nanos_job'));
|
||||
loadTestFile(require.resolve('./custom_urls'));
|
||||
loadTestFile(require.resolve('./delete_job_and_delete_annotations'));
|
||||
loadTestFile(require.resolve('./convert_single_metric_job_to_multi_metric'));
|
||||
loadTestFile(require.resolve('./convert_jobs_to_advanced_job'));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
export interface Detector {
|
||||
identifier: string;
|
||||
function: string;
|
||||
field?: string;
|
||||
byField?: string;
|
||||
overField?: string;
|
||||
partitionField?: string;
|
||||
excludeFrequent?: string;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
export interface DatafeedConfig {
|
||||
queryDelay?: string;
|
||||
frequency?: string;
|
||||
scrollSize?: string;
|
||||
}
|
||||
|
||||
export interface PickFieldsConfig {
|
||||
detectors: Detector[];
|
||||
influencers: string[];
|
||||
bucketSpan: string;
|
||||
memoryLimit: string;
|
||||
categorizationField?: string;
|
||||
summaryCountField?: string;
|
||||
}
|
|
@ -17,7 +17,7 @@ export interface SectionOptions {
|
|||
}
|
||||
|
||||
export function MachineLearningJobWizardCommonProvider(
|
||||
{ getService }: FtrProviderContext,
|
||||
{ getPageObject, getService }: FtrProviderContext,
|
||||
mlCommonUI: MlCommonUI,
|
||||
customUrls: MlCustomUrls,
|
||||
mlCommonFieldStatsFlyout: MlCommonFieldStatsFlyout
|
||||
|
@ -25,6 +25,7 @@ export function MachineLearningJobWizardCommonProvider(
|
|||
const comboBox = getService('comboBox');
|
||||
const retry = getService('retry');
|
||||
const testSubjects = getService('testSubjects');
|
||||
const headerPage = getPageObject('header');
|
||||
|
||||
function advancedSectionSelector(subSelector?: string) {
|
||||
const subj = 'mlJobWizardAdvancedSection';
|
||||
|
@ -492,6 +493,20 @@ export function MachineLearningJobWizardCommonProvider(
|
|||
await testSubjects.existOrFail('mlJobWizardButtonCreateJob');
|
||||
},
|
||||
|
||||
async assertConvertToAdvancedJobExists() {
|
||||
await testSubjects.existOrFail('mlJobWizardButtonConvertToAdvancedJob');
|
||||
},
|
||||
|
||||
async convertToAdvancedJobWizard() {
|
||||
await this.assertConvertToAdvancedJobExists();
|
||||
|
||||
await retry.tryForTime(5000, async () => {
|
||||
await testSubjects.click('mlJobWizardButtonConvertToAdvancedJob');
|
||||
await headerPage.waitUntilLoadingHasFinished();
|
||||
await testSubjects.existOrFail('mlPageJobWizardHeader-advanced');
|
||||
});
|
||||
},
|
||||
|
||||
async assertDateRangeSelectionExists() {
|
||||
await testSubjects.existOrFail('mlJobWizardDateRange');
|
||||
},
|
||||
|
@ -558,6 +573,12 @@ export function MachineLearningJobWizardCommonProvider(
|
|||
await customUrls.assertCustomUrlLabel(expectedIndex, customUrl.label);
|
||||
},
|
||||
|
||||
async assertCustomUrlLabel(expectedIndex: number, customUrl: { label: string }) {
|
||||
await this.ensureAdditionalSettingsSectionOpen();
|
||||
|
||||
await customUrls.assertCustomUrlLabel(expectedIndex, customUrl.label);
|
||||
},
|
||||
|
||||
async ensureAdvancedSectionOpen() {
|
||||
await retry.tryForTime(5000, async () => {
|
||||
if ((await testSubjects.exists(advancedSectionSelector())) === false) {
|
||||
|
@ -576,5 +597,27 @@ export function MachineLearningJobWizardCommonProvider(
|
|||
await testSubjects.clickWhenNotDisabledWithoutRetry('mlJobWizardButtonCreateJob');
|
||||
await testSubjects.existOrFail('mlPageJobManagement');
|
||||
},
|
||||
|
||||
async assertConvertToMultiMetricButtonExist(bucketSpan: string) {
|
||||
await testSubjects.existOrFail('mlJobWizardButtonConvertToMultiMetric');
|
||||
},
|
||||
|
||||
async convertToMultiMetricJobWizard() {
|
||||
await retry.tryForTime(5 * 1000, async () => {
|
||||
await testSubjects.click('mlJobWizardButtonConvertToMultiMetric');
|
||||
await headerPage.waitUntilLoadingHasFinished();
|
||||
|
||||
await testSubjects.existOrFail('mlPageJobWizardHeader-multi_metric');
|
||||
});
|
||||
},
|
||||
|
||||
async navigateToPreviousJobWizardPage(expectedSelector: string) {
|
||||
await retry.tryForTime(5 * 1000, async () => {
|
||||
await testSubjects.click('mlJobWizardNavButtonPrevious');
|
||||
await headerPage.waitUntilLoadingHasFinished();
|
||||
|
||||
await testSubjects.existOrFail(expectedSelector);
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue