mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
This PR adds tests for the ML advanced wizard.
This commit is contained in:
parent
d2b85636c2
commit
b1f497fd78
23 changed files with 1496 additions and 115 deletions
|
@ -136,6 +136,7 @@ export class StartDatafeedModal extends Component {
|
|||
onClose={this.closeModal}
|
||||
style={{ width: '850px' }}
|
||||
maxWidth={false}
|
||||
data-test-subj="mlStartDatafeedModal"
|
||||
>
|
||||
<EuiModalHeader>
|
||||
<EuiModalHeaderTitle>
|
||||
|
@ -178,6 +179,7 @@ export class StartDatafeedModal extends Component {
|
|||
<EuiModalFooter>
|
||||
<EuiButtonEmpty
|
||||
onClick={this.closeModal}
|
||||
data-test-subj="mlStartDatafeedModalCancelButton"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.jobsList.startDatafeedModal.cancelButtonLabel"
|
||||
|
@ -189,6 +191,7 @@ export class StartDatafeedModal extends Component {
|
|||
onClick={this.save}
|
||||
isDisabled={startDisabled}
|
||||
fill
|
||||
data-test-subj="mlStartDatafeedModalStartButton"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.jobsList.startDatafeedModal.startButtonLabel"
|
||||
|
|
|
@ -37,7 +37,7 @@ export const DatafeedStep: FC<StepProps> = ({ setCurrentStep, isCurrentStep }) =
|
|||
{isCurrentStep && (
|
||||
<Fragment>
|
||||
<EuiFlexGroup gutterSize="xl">
|
||||
<EuiFlexItem>
|
||||
<EuiFlexItem data-test-subj="mlAdvancedDatafeedQueryEditor">
|
||||
<QueryInput setIsValidQuery={setIsValidQuery} />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
|
|
|
@ -213,7 +213,7 @@ export const AdvancedDetectorModal: FC<Props> = ({
|
|||
<ModalWrapper onCreateClick={onCreateClick} closeModal={closeModal} saveEnabled={saveEnabled()}>
|
||||
<Fragment>
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem>
|
||||
<EuiFlexItem data-test-subj="mlAdvancedFunctionSelect">
|
||||
<AggDescription>
|
||||
<EuiComboBox
|
||||
singleSelection={{ asPlainText: true }}
|
||||
|
@ -224,7 +224,7 @@ export const AdvancedDetectorModal: FC<Props> = ({
|
|||
/>
|
||||
</AggDescription>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiFlexItem data-test-subj="mlAdvancedFieldSelect">
|
||||
<FieldDescription>
|
||||
<EuiComboBox
|
||||
singleSelection={{ asPlainText: true }}
|
||||
|
@ -239,7 +239,7 @@ export const AdvancedDetectorModal: FC<Props> = ({
|
|||
</EuiFlexGroup>
|
||||
<EuiHorizontalRule margin="l" />
|
||||
<EuiFlexGrid columns={2}>
|
||||
<EuiFlexItem>
|
||||
<EuiFlexItem data-test-subj="mlAdvancedByFieldSelect">
|
||||
<ByFieldDescription>
|
||||
<EuiComboBox
|
||||
singleSelection={{ asPlainText: true }}
|
||||
|
@ -251,7 +251,7 @@ export const AdvancedDetectorModal: FC<Props> = ({
|
|||
/>
|
||||
</ByFieldDescription>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiFlexItem data-test-subj="mlAdvancedOverFieldSelect">
|
||||
<OverFieldDescription>
|
||||
<EuiComboBox
|
||||
singleSelection={{ asPlainText: true }}
|
||||
|
@ -263,7 +263,7 @@ export const AdvancedDetectorModal: FC<Props> = ({
|
|||
/>
|
||||
</OverFieldDescription>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiFlexItem data-test-subj="mlAdvancedPartitionFieldSelect">
|
||||
<PartitionFieldDescription>
|
||||
<EuiComboBox
|
||||
singleSelection={{ asPlainText: true }}
|
||||
|
@ -275,7 +275,7 @@ export const AdvancedDetectorModal: FC<Props> = ({
|
|||
/>
|
||||
</PartitionFieldDescription>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiFlexItem data-test-subj="mlAdvancedExcludeFrequentSelect">
|
||||
<ExcludeFrequentDescription>
|
||||
<EuiComboBox
|
||||
singleSelection={{ asPlainText: true }}
|
||||
|
@ -301,6 +301,7 @@ export const AdvancedDetectorModal: FC<Props> = ({
|
|||
placeholder={descriptionPlaceholder}
|
||||
value={descriptionOption}
|
||||
onChange={e => setDescriptionOption(e.target.value)}
|
||||
data-test-subj="mlAdvancedDetectorDescriptionInput"
|
||||
/>
|
||||
</DescriptionDescription>
|
||||
</EuiFlexItem>
|
||||
|
|
|
@ -28,7 +28,11 @@ interface Props {
|
|||
export const ModalWrapper: FC<Props> = ({ onCreateClick, closeModal, saveEnabled, children }) => {
|
||||
return (
|
||||
<EuiOverlayMask>
|
||||
<EuiModal onClose={closeModal} maxWidth={MAX_MODAL_WIDTH}>
|
||||
<EuiModal
|
||||
onClose={closeModal}
|
||||
maxWidth={MAX_MODAL_WIDTH}
|
||||
data-test-subj="mlCreateDetectorModal"
|
||||
>
|
||||
<EuiModalHeader>
|
||||
<EuiModalHeaderTitle>
|
||||
<FormattedMessage
|
||||
|
@ -41,14 +45,19 @@ export const ModalWrapper: FC<Props> = ({ onCreateClick, closeModal, saveEnabled
|
|||
<EuiModalBody>{children}</EuiModalBody>
|
||||
|
||||
<EuiModalFooter>
|
||||
<EuiButtonEmpty onClick={closeModal}>
|
||||
<EuiButtonEmpty onClick={closeModal} data-test-subj="mlCreateDetectorModalCancelButton">
|
||||
<FormattedMessage
|
||||
id="xpack.ml.newJob.wizard.pickFieldsStep.advancedDetectorModal.cancelButton"
|
||||
defaultMessage="Cancel"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
|
||||
<EuiButton onClick={onCreateClick} isDisabled={saveEnabled === false} fill>
|
||||
<EuiButton
|
||||
onClick={onCreateClick}
|
||||
isDisabled={saveEnabled === false}
|
||||
fill
|
||||
data-test-subj="mlCreateDetectorModalSaveButton"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.newJob.wizard.pickFieldsStep.advancedDetectorModal.saveButton"
|
||||
defaultMessage="Save"
|
||||
|
|
|
@ -25,6 +25,7 @@ import { JobCreatorContext } from '../../../job_creator_context';
|
|||
import { AdvancedJobCreator } from '../../../../../common/job_creator';
|
||||
import { Validation } from '../../../../../common/job_validator';
|
||||
import { detectorToString } from '../../../../../../../util/string_utils';
|
||||
import { Detector } from '../../../../../common/job_creator/configs';
|
||||
|
||||
interface Props {
|
||||
isActive: boolean;
|
||||
|
@ -62,6 +63,7 @@ export const DetectorList: FC<Props> = ({ isActive, onEditJob, onDeleteJob }) =>
|
|||
defaultMessage: 'Edit',
|
||||
}
|
||||
)}
|
||||
data-test-subj="mlAdvancedDetectorEditButton"
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
|
@ -75,6 +77,7 @@ export const DetectorList: FC<Props> = ({ isActive, onEditJob, onDeleteJob }) =>
|
|||
defaultMessage: 'Delete',
|
||||
}
|
||||
)}
|
||||
data-test-subj="mlAdvancedDetectorDeleteButton"
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
|
@ -98,14 +101,16 @@ export const DetectorList: FC<Props> = ({ isActive, onEditJob, onDeleteJob }) =>
|
|||
|
||||
<EuiFlexGrid columns={3}>
|
||||
{detectors.map((d, i) => (
|
||||
<EuiFlexItem key={i}>
|
||||
<EuiFlexItem key={i} data-test-subj={`mlAdvancedDetector ${i}`}>
|
||||
<EuiPanel paddingSize="m">
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem>
|
||||
{d.detector_description !== undefined ? (
|
||||
<div style={{ fontWeight: 'bold' }}>{d.detector_description}</div>
|
||||
<div style={{ fontWeight: 'bold' }} data-test-subj="mlDetectorDescription">
|
||||
{d.detector_description}
|
||||
</div>
|
||||
) : (
|
||||
detectorToString(d)
|
||||
<StringifiedDetector detector={d} />
|
||||
)}
|
||||
</EuiFlexItem>
|
||||
{isActive && (
|
||||
|
@ -117,7 +122,7 @@ export const DetectorList: FC<Props> = ({ isActive, onEditJob, onDeleteJob }) =>
|
|||
{d.detector_description !== undefined && (
|
||||
<Fragment>
|
||||
<EuiHorizontalRule margin="s" />
|
||||
{detectorToString(d)}
|
||||
<StringifiedDetector detector={d} />
|
||||
</Fragment>
|
||||
)}
|
||||
</EuiPanel>
|
||||
|
@ -142,6 +147,7 @@ const NoDetectorsWarning: FC<{ show: boolean }> = ({ show }) => {
|
|||
defaultMessage: 'No detectors',
|
||||
})}
|
||||
iconType="alert"
|
||||
data-test-subj="mlAdvancedNoDetectorsMessage"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.newJob.wizard.pickFieldsStep.noDetectorsCallout.message"
|
||||
|
@ -166,3 +172,7 @@ const DuplicateDetectorsWarning: FC<{ validation: Validation }> = ({ validation
|
|||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
const StringifiedDetector: FC<{ detector: Detector }> = ({ detector }) => {
|
||||
return <div data-test-subj="mlDetectorIdentifier">{detectorToString(detector)}</div>;
|
||||
};
|
||||
|
|
|
@ -35,7 +35,7 @@ export const MetricSelector: FC<Props> = ({
|
|||
<EuiFlexGroup style={{ maxWidth: MAX_WIDTH }}>
|
||||
<EuiFlexItem>
|
||||
<EuiFormRow>
|
||||
<EuiButton onClick={showModal}>
|
||||
<EuiButton onClick={showModal} data-test-subj="mlAddDetectorButton">
|
||||
<FormattedMessage
|
||||
id="xpack.ml.newJob.wizard.pickFieldsStep.addDetectorButton"
|
||||
defaultMessage="Add detector"
|
||||
|
|
|
@ -0,0 +1,805 @@
|
|||
/*
|
||||
* 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';
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// type guards
|
||||
// Detector
|
||||
const isDetectorWithField = (arg: any): arg is Required<Pick<Detector, 'field'>> => {
|
||||
return arg.hasOwnProperty('field');
|
||||
};
|
||||
const isDetectorWithByField = (arg: any): arg is Required<Pick<Detector, 'byField'>> => {
|
||||
return arg.hasOwnProperty('byField');
|
||||
};
|
||||
const isDetectorWithOverField = (arg: any): arg is Required<Pick<Detector, 'overField'>> => {
|
||||
return arg.hasOwnProperty('overField');
|
||||
};
|
||||
const isDetectorWithPartitionField = (
|
||||
arg: any
|
||||
): arg is Required<Pick<Detector, 'partitionField'>> => {
|
||||
return arg.hasOwnProperty('partitionField');
|
||||
};
|
||||
const isDetectorWithExcludeFrequent = (
|
||||
arg: any
|
||||
): arg is Required<Pick<Detector, 'excludeFrequent'>> => {
|
||||
return arg.hasOwnProperty('excludeFrequent');
|
||||
};
|
||||
const isDetectorWithDescription = (arg: any): arg is Required<Pick<Detector, 'description'>> => {
|
||||
return arg.hasOwnProperty('description');
|
||||
};
|
||||
|
||||
// DatafeedConfig
|
||||
const isDatafeedConfigWithQueryDelay = (
|
||||
arg: any
|
||||
): arg is Required<Pick<DatafeedConfig, 'queryDelay'>> => {
|
||||
return arg.hasOwnProperty('queryDelay');
|
||||
};
|
||||
const isDatafeedConfigWithFrequency = (
|
||||
arg: any
|
||||
): arg is Required<Pick<DatafeedConfig, 'frequency'>> => {
|
||||
return arg.hasOwnProperty('frequency');
|
||||
};
|
||||
const isDatafeedConfigWithScrollSize = (
|
||||
arg: any
|
||||
): arg is Required<Pick<DatafeedConfig, 'scrollSize'>> => {
|
||||
return arg.hasOwnProperty('scrollSize');
|
||||
};
|
||||
|
||||
// PickFieldsConfig
|
||||
const isPickFieldsConfigWithCategorizationField = (
|
||||
arg: any
|
||||
): arg is Required<Pick<PickFieldsConfig, 'categorizationField'>> => {
|
||||
return arg.hasOwnProperty('categorizationField');
|
||||
};
|
||||
const isPickFieldsConfigWithSummaryCountField = (
|
||||
arg: any
|
||||
): arg is Required<Pick<PickFieldsConfig, 'summaryCountField'>> => {
|
||||
return arg.hasOwnProperty('summaryCountField');
|
||||
};
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default function({ getService }: FtrProviderContext) {
|
||||
const esArchiver = getService('esArchiver');
|
||||
const ml = getService('ml');
|
||||
|
||||
const defaultValues = {
|
||||
datafeedQuery: `{
|
||||
"bool": {
|
||||
"must": [
|
||||
{
|
||||
"match_all": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
}`,
|
||||
queryDelay: '60s',
|
||||
frequency: '450s',
|
||||
scrollSize: '1000',
|
||||
};
|
||||
|
||||
const testDataList = [
|
||||
{
|
||||
suiteTitle: 'with multiple metric detectors and custom datafeed settings',
|
||||
jobSource: 'ecommerce',
|
||||
jobId: `ec_advanced_1_${Date.now()}`,
|
||||
get jobIdClone(): string {
|
||||
return `${this.jobId}_clone`;
|
||||
},
|
||||
jobDescription:
|
||||
'Create advanced job from ecommerce dataset with multiple metric detectors and custom datafeed settings',
|
||||
jobGroups: ['automated', 'ecommerce', 'advanced'],
|
||||
get jobGroupsClone(): string[] {
|
||||
return [...this.jobGroups, 'clone'];
|
||||
},
|
||||
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: ['customer_id', 'category.keyword', 'geoip.continent_name', 'customer_gender'],
|
||||
bucketSpan: '1h',
|
||||
memoryLimit: '10mb',
|
||||
} as PickFieldsConfig,
|
||||
datafeedConfig: {
|
||||
queryDelay: '55s',
|
||||
frequency: '350s',
|
||||
scrollSize: '999',
|
||||
} as DatafeedConfig,
|
||||
expected: {
|
||||
wizard: {
|
||||
timeField: 'order_date',
|
||||
},
|
||||
row: {
|
||||
recordCount: '4,675',
|
||||
memoryStatus: 'ok',
|
||||
jobState: 'closed',
|
||||
datafeedState: 'stopped',
|
||||
latestTimestamp: '2019-07-12 23:45:36',
|
||||
},
|
||||
counts: {
|
||||
processed_record_count: '4,675',
|
||||
processed_field_count: '32,725',
|
||||
input_bytes: '1.1 MB',
|
||||
input_field_count: '32,725',
|
||||
invalid_date_count: '0',
|
||||
missing_field_count: '0',
|
||||
out_of_order_timestamp_count: '0',
|
||||
empty_bucket_count: '0',
|
||||
sparse_bucket_count: '0',
|
||||
bucket_count: '743',
|
||||
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 23:00:00',
|
||||
},
|
||||
modelSizeStats: {
|
||||
result_type: 'model_size_stats',
|
||||
model_bytes_exceeded: '0',
|
||||
model_bytes_memory_limit: '10485760',
|
||||
total_by_field_count: '37',
|
||||
total_over_field_count: '92',
|
||||
total_partition_field_count: '8',
|
||||
bucket_allocation_failures_count: '0',
|
||||
memory_status: 'ok',
|
||||
timestamp: '2019-07-12 22:00:00',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
suiteTitle: 'with categorization detector and default datafeed settings',
|
||||
jobSource: 'ecommerce',
|
||||
jobId: `ec_advanced_2_${Date.now()}`,
|
||||
get jobIdClone(): string {
|
||||
return `${this.jobId}_clone`;
|
||||
},
|
||||
jobDescription:
|
||||
'Create advanced job from ecommerce dataset with a categorization detector and default datafeed settings',
|
||||
jobGroups: ['automated', 'ecommerce', 'advanced'],
|
||||
get jobGroupsClone(): string[] {
|
||||
return [...this.jobGroups, 'clone'];
|
||||
},
|
||||
pickFieldsConfig: {
|
||||
categorizationField: 'products.product_name',
|
||||
detectors: [
|
||||
{
|
||||
identifier: 'count by mlcategory',
|
||||
function: 'count',
|
||||
byField: 'mlcategory',
|
||||
} as Detector,
|
||||
],
|
||||
influencers: ['mlcategory'],
|
||||
bucketSpan: '12h',
|
||||
memoryLimit: '100mb',
|
||||
} as PickFieldsConfig,
|
||||
datafeedConfig: {} as DatafeedConfig,
|
||||
expected: {
|
||||
wizard: {
|
||||
timeField: 'order_date',
|
||||
},
|
||||
row: {
|
||||
recordCount: '4,675',
|
||||
memoryStatus: 'ok',
|
||||
jobState: 'closed',
|
||||
datafeedState: 'stopped',
|
||||
latestTimestamp: '2019-07-12 23:45:36',
|
||||
},
|
||||
counts: {
|
||||
processed_record_count: '4,675',
|
||||
processed_field_count: '4,588',
|
||||
input_bytes: '4.4 MB',
|
||||
input_field_count: '6,154',
|
||||
invalid_date_count: '0',
|
||||
missing_field_count: '87',
|
||||
out_of_order_timestamp_count: '0',
|
||||
empty_bucket_count: '0',
|
||||
sparse_bucket_count: '0',
|
||||
bucket_count: '61',
|
||||
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 12:00:00',
|
||||
},
|
||||
modelSizeStats: {
|
||||
result_type: 'model_size_stats',
|
||||
model_bytes_exceeded: '0',
|
||||
model_bytes_memory_limit: '104857600',
|
||||
total_by_field_count: '3,787',
|
||||
total_over_field_count: '0',
|
||||
total_partition_field_count: '2',
|
||||
bucket_allocation_failures_count: '0',
|
||||
memory_status: 'ok',
|
||||
timestamp: '2019-07-12 00:00:00',
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
describe('advanced job', function() {
|
||||
this.tags(['smoke', 'mlqa']);
|
||||
before(async () => {
|
||||
await esArchiver.load('ml/ecommerce');
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await esArchiver.unload('ml/ecommerce');
|
||||
await ml.api.cleanMlIndices();
|
||||
});
|
||||
|
||||
for (const testData of testDataList) {
|
||||
describe(`${testData.suiteTitle}`, function() {
|
||||
it('job creation loads the job management page', async () => {
|
||||
await ml.navigation.navigateToMl();
|
||||
await ml.navigation.navigateToJobManagement();
|
||||
});
|
||||
|
||||
it('job creation loads the new job source selection page', async () => {
|
||||
await ml.jobManagement.navigateToNewJobSourceSelection();
|
||||
});
|
||||
|
||||
it('job creation loads the job type selection page', async () => {
|
||||
await ml.jobSourceSelection.selectSource(testData.jobSource);
|
||||
});
|
||||
|
||||
it('job creation loads the advanced job wizard page', async () => {
|
||||
await ml.jobTypeSelection.selectAdvancedJob();
|
||||
});
|
||||
|
||||
it('job creation displays the configure datafeed step', async () => {
|
||||
await ml.jobWizardCommon.assertConfigureDatafeedSectionExists();
|
||||
});
|
||||
|
||||
it('job creation pre-fills the datafeed query editor', async () => {
|
||||
await ml.jobWizardAdvanced.assertDatafeedQueryEditorExists();
|
||||
await ml.jobWizardAdvanced.assertDatafeedQueryEditorValue(defaultValues.datafeedQuery);
|
||||
});
|
||||
|
||||
it('job creation inputs the query delay', async () => {
|
||||
await ml.jobWizardAdvanced.assertQueryDelayInputExists();
|
||||
await ml.jobWizardAdvanced.assertQueryDelayValue(defaultValues.queryDelay);
|
||||
if (isDatafeedConfigWithQueryDelay(testData.datafeedConfig)) {
|
||||
await ml.jobWizardAdvanced.setQueryDelay(testData.datafeedConfig.queryDelay);
|
||||
}
|
||||
});
|
||||
|
||||
it('job creation inputs the frequency', async () => {
|
||||
await ml.jobWizardAdvanced.assertFrequencyInputExists();
|
||||
await ml.jobWizardAdvanced.assertFrequencyValue(defaultValues.frequency);
|
||||
if (isDatafeedConfigWithFrequency(testData.datafeedConfig)) {
|
||||
await ml.jobWizardAdvanced.setFrequency(testData.datafeedConfig.frequency);
|
||||
}
|
||||
});
|
||||
|
||||
it('job creation inputs the scroll size', async () => {
|
||||
await ml.jobWizardAdvanced.assertScrollSizeInputExists();
|
||||
await ml.jobWizardAdvanced.assertScrollSizeValue(defaultValues.scrollSize);
|
||||
if (isDatafeedConfigWithScrollSize(testData.datafeedConfig)) {
|
||||
await ml.jobWizardAdvanced.setScrollSize(testData.datafeedConfig.scrollSize);
|
||||
}
|
||||
});
|
||||
|
||||
it('job creation pre-fills the time field', async () => {
|
||||
await ml.jobWizardAdvanced.assertTimeFieldInputExists();
|
||||
await ml.jobWizardAdvanced.assertTimeFieldSelection([testData.expected.wizard.timeField]);
|
||||
});
|
||||
|
||||
it('job creation displays the pick fields step', async () => {
|
||||
await ml.jobWizardCommon.advanceToPickFieldsSection();
|
||||
});
|
||||
|
||||
it('job creation selects the categorization field', async () => {
|
||||
await ml.jobWizardAdvanced.assertCategorizationFieldInputExists();
|
||||
if (isPickFieldsConfigWithCategorizationField(testData.pickFieldsConfig)) {
|
||||
await ml.jobWizardAdvanced.selectCategorizationField(
|
||||
testData.pickFieldsConfig.categorizationField
|
||||
);
|
||||
} else {
|
||||
await ml.jobWizardAdvanced.assertCategorizationFieldSelection([]);
|
||||
}
|
||||
});
|
||||
|
||||
it('job creation selects the summary count field', async () => {
|
||||
await ml.jobWizardAdvanced.assertSummaryCountFieldInputExists();
|
||||
if (isPickFieldsConfigWithSummaryCountField(testData.pickFieldsConfig)) {
|
||||
await ml.jobWizardAdvanced.selectSummaryCountField(
|
||||
testData.pickFieldsConfig.summaryCountField
|
||||
);
|
||||
} else {
|
||||
await ml.jobWizardAdvanced.assertSummaryCountFieldSelection([]);
|
||||
}
|
||||
});
|
||||
|
||||
it('job creation adds detectors', async () => {
|
||||
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 (isDetectorWithField(detector)) {
|
||||
await ml.jobWizardAdvanced.selectDetectorField(detector.field);
|
||||
}
|
||||
if (isDetectorWithByField(detector)) {
|
||||
await ml.jobWizardAdvanced.selectDetectorByField(detector.byField);
|
||||
}
|
||||
if (isDetectorWithOverField(detector)) {
|
||||
await ml.jobWizardAdvanced.selectDetectorOverField(detector.overField);
|
||||
}
|
||||
if (isDetectorWithPartitionField(detector)) {
|
||||
await ml.jobWizardAdvanced.selectDetectorPartitionField(detector.partitionField);
|
||||
}
|
||||
if (isDetectorWithExcludeFrequent(detector)) {
|
||||
await ml.jobWizardAdvanced.selectDetectorExcludeFrequent(detector.excludeFrequent);
|
||||
}
|
||||
if (isDetectorWithDescription(detector)) {
|
||||
await ml.jobWizardAdvanced.setDetectorDescription(detector.description);
|
||||
}
|
||||
|
||||
await ml.jobWizardAdvanced.confirmAddDetectorModal();
|
||||
}
|
||||
});
|
||||
|
||||
it('job creation displays detector entries', async () => {
|
||||
for (const [index, detector] of testData.pickFieldsConfig.detectors.entries()) {
|
||||
await ml.jobWizardAdvanced.assertDetectorEntryExists(
|
||||
index,
|
||||
detector.identifier,
|
||||
isDetectorWithDescription(detector) ? detector.description : undefined
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it('job creation inputs the bucket span', async () => {
|
||||
await ml.jobWizardCommon.assertBucketSpanInputExists();
|
||||
await ml.jobWizardCommon.setBucketSpan(testData.pickFieldsConfig.bucketSpan);
|
||||
});
|
||||
|
||||
it('job creation inputs influencers', async () => {
|
||||
await ml.jobWizardCommon.assertInfluencerInputExists();
|
||||
await ml.jobWizardCommon.assertInfluencerSelection([]);
|
||||
for (const influencer of testData.pickFieldsConfig.influencers) {
|
||||
await ml.jobWizardCommon.addInfluencer(influencer);
|
||||
}
|
||||
});
|
||||
|
||||
it('job creation inputs the model memory limit', async () => {
|
||||
await ml.jobWizardCommon.assertModelMemoryLimitInputExists({
|
||||
withAdvancedSection: false,
|
||||
});
|
||||
await ml.jobWizardCommon.setModelMemoryLimit(testData.pickFieldsConfig.memoryLimit, {
|
||||
withAdvancedSection: false,
|
||||
});
|
||||
});
|
||||
|
||||
it('job creation displays the job details step', async () => {
|
||||
await ml.jobWizardCommon.advanceToJobDetailsSection();
|
||||
});
|
||||
|
||||
it('job creation inputs the job id', async () => {
|
||||
await ml.jobWizardCommon.assertJobIdInputExists();
|
||||
await ml.jobWizardCommon.setJobId(testData.jobId);
|
||||
});
|
||||
|
||||
it('job creation inputs the job description', async () => {
|
||||
await ml.jobWizardCommon.assertJobDescriptionInputExists();
|
||||
await ml.jobWizardCommon.setJobDescription(testData.jobDescription);
|
||||
});
|
||||
|
||||
it('job creation inputs job groups', async () => {
|
||||
await ml.jobWizardCommon.assertJobGroupInputExists();
|
||||
for (const jobGroup of testData.jobGroups) {
|
||||
await ml.jobWizardCommon.addJobGroup(jobGroup);
|
||||
}
|
||||
await ml.jobWizardCommon.assertJobGroupSelection(testData.jobGroups);
|
||||
});
|
||||
|
||||
it('job creation displays the model plot switch', async () => {
|
||||
await ml.jobWizardCommon.assertModelPlotSwitchExists({ withAdvancedSection: false });
|
||||
});
|
||||
|
||||
it('job creation enables the dedicated index switch', async () => {
|
||||
await ml.jobWizardCommon.assertDedicatedIndexSwitchExists({ withAdvancedSection: false });
|
||||
await ml.jobWizardCommon.activateDedicatedIndexSwitch({ withAdvancedSection: false });
|
||||
});
|
||||
|
||||
it('job creation displays the validation step', async () => {
|
||||
await ml.jobWizardCommon.advanceToValidationSection();
|
||||
});
|
||||
|
||||
it('job creation displays the summary step', async () => {
|
||||
await ml.jobWizardCommon.advanceToSummarySection();
|
||||
});
|
||||
|
||||
it('job creation creates the job and finishes processing', async () => {
|
||||
await ml.jobWizardCommon.assertCreateJobButtonExists();
|
||||
await ml.jobWizardAdvanced.createJob();
|
||||
await ml.jobManagement.assertStartDatafeedModalExists();
|
||||
await ml.jobManagement.confirmStartDatafeedModal();
|
||||
await ml.jobManagement.waitForJobCompletion(testData.jobId);
|
||||
});
|
||||
|
||||
it('job creation displays the created job in the job list', async () => {
|
||||
await ml.jobTable.refreshJobList();
|
||||
await ml.jobTable.filterWithSearchString(testData.jobId);
|
||||
const rows = await ml.jobTable.parseJobTable();
|
||||
expect(rows.filter(row => row.id === testData.jobId)).to.have.length(1);
|
||||
});
|
||||
|
||||
it('job creation displays details for the created job in the job list', async () => {
|
||||
await ml.jobTable.assertJobRowFields(testData.jobId, {
|
||||
id: testData.jobId,
|
||||
description: testData.jobDescription,
|
||||
jobGroups: [...new Set(testData.jobGroups)].sort(),
|
||||
recordCount: testData.expected.row.recordCount,
|
||||
memoryStatus: testData.expected.row.memoryStatus,
|
||||
jobState: testData.expected.row.jobState,
|
||||
datafeedState: testData.expected.row.datafeedState,
|
||||
latestTimestamp: testData.expected.row.latestTimestamp,
|
||||
});
|
||||
|
||||
await ml.jobTable.assertJobRowDetailsCounts(
|
||||
testData.jobId,
|
||||
{
|
||||
job_id: testData.jobId,
|
||||
processed_record_count: testData.expected.counts.processed_record_count,
|
||||
processed_field_count: testData.expected.counts.processed_field_count,
|
||||
input_bytes: testData.expected.counts.input_bytes,
|
||||
input_field_count: testData.expected.counts.input_field_count,
|
||||
invalid_date_count: testData.expected.counts.invalid_date_count,
|
||||
missing_field_count: testData.expected.counts.missing_field_count,
|
||||
out_of_order_timestamp_count: testData.expected.counts.out_of_order_timestamp_count,
|
||||
empty_bucket_count: testData.expected.counts.empty_bucket_count,
|
||||
sparse_bucket_count: testData.expected.counts.sparse_bucket_count,
|
||||
bucket_count: testData.expected.counts.bucket_count,
|
||||
earliest_record_timestamp: testData.expected.counts.earliest_record_timestamp,
|
||||
latest_record_timestamp: testData.expected.counts.latest_record_timestamp,
|
||||
input_record_count: testData.expected.counts.input_record_count,
|
||||
latest_bucket_timestamp: testData.expected.counts.latest_bucket_timestamp,
|
||||
},
|
||||
{
|
||||
job_id: testData.jobId,
|
||||
result_type: testData.expected.modelSizeStats.result_type,
|
||||
model_bytes_exceeded: testData.expected.modelSizeStats.model_bytes_exceeded,
|
||||
model_bytes_memory_limit: testData.expected.modelSizeStats.model_bytes_memory_limit,
|
||||
total_by_field_count: testData.expected.modelSizeStats.total_by_field_count,
|
||||
total_over_field_count: testData.expected.modelSizeStats.total_over_field_count,
|
||||
total_partition_field_count:
|
||||
testData.expected.modelSizeStats.total_partition_field_count,
|
||||
bucket_allocation_failures_count:
|
||||
testData.expected.modelSizeStats.bucket_allocation_failures_count,
|
||||
memory_status: testData.expected.modelSizeStats.memory_status,
|
||||
timestamp: testData.expected.modelSizeStats.timestamp,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('job creation has detector results', async () => {
|
||||
for (let i = 0; i < testData.pickFieldsConfig.detectors.length; i++) {
|
||||
await ml.api.assertDetectorResultsExist(testData.jobId, i);
|
||||
}
|
||||
});
|
||||
|
||||
it('job cloning clicks the clone action and loads the advanced wizard', async () => {
|
||||
await ml.jobTable.clickCloneJobAction(testData.jobId);
|
||||
await ml.jobTypeSelection.assertAdvancedJobWizardOpen();
|
||||
});
|
||||
|
||||
it('job cloning displays the configure datafeed step', async () => {
|
||||
await ml.jobWizardCommon.assertConfigureDatafeedSectionExists();
|
||||
});
|
||||
|
||||
it('job cloning pre-fills the datafeed query editor', async () => {
|
||||
await ml.jobWizardAdvanced.assertDatafeedQueryEditorExists();
|
||||
await ml.jobWizardAdvanced.assertDatafeedQueryEditorValue(defaultValues.datafeedQuery);
|
||||
});
|
||||
|
||||
it('job cloning pre-fills the query delay', async () => {
|
||||
await ml.jobWizardAdvanced.assertQueryDelayInputExists();
|
||||
if (isDatafeedConfigWithQueryDelay(testData.datafeedConfig)) {
|
||||
await ml.jobWizardAdvanced.assertQueryDelayValue(testData.datafeedConfig.queryDelay);
|
||||
}
|
||||
});
|
||||
|
||||
it('job cloning pre-fills the frequency', async () => {
|
||||
await ml.jobWizardAdvanced.assertFrequencyInputExists();
|
||||
if (isDatafeedConfigWithFrequency(testData.datafeedConfig)) {
|
||||
await ml.jobWizardAdvanced.assertFrequencyValue(testData.datafeedConfig.frequency);
|
||||
}
|
||||
});
|
||||
|
||||
it('job cloning pre-fills the scroll size', async () => {
|
||||
await ml.jobWizardAdvanced.assertScrollSizeInputExists();
|
||||
await ml.jobWizardAdvanced.assertScrollSizeValue(
|
||||
isDatafeedConfigWithScrollSize(testData.datafeedConfig)
|
||||
? testData.datafeedConfig.scrollSize
|
||||
: defaultValues.scrollSize
|
||||
);
|
||||
});
|
||||
|
||||
it('job creation pre-fills the time field', async () => {
|
||||
await ml.jobWizardAdvanced.assertTimeFieldInputExists();
|
||||
await ml.jobWizardAdvanced.assertTimeFieldSelection([testData.expected.wizard.timeField]);
|
||||
});
|
||||
|
||||
it('job cloning displays the pick fields step', async () => {
|
||||
await ml.jobWizardCommon.advanceToPickFieldsSection();
|
||||
});
|
||||
|
||||
it('job cloning pre-fills the categorization field', async () => {
|
||||
await ml.jobWizardAdvanced.assertCategorizationFieldInputExists();
|
||||
await ml.jobWizardAdvanced.assertCategorizationFieldSelection(
|
||||
isPickFieldsConfigWithCategorizationField(testData.pickFieldsConfig)
|
||||
? [testData.pickFieldsConfig.categorizationField]
|
||||
: []
|
||||
);
|
||||
});
|
||||
|
||||
it('job cloning pre-fills the summary count field', async () => {
|
||||
await ml.jobWizardAdvanced.assertSummaryCountFieldInputExists();
|
||||
await ml.jobWizardAdvanced.assertSummaryCountFieldSelection(
|
||||
isPickFieldsConfigWithSummaryCountField(testData.pickFieldsConfig)
|
||||
? [testData.pickFieldsConfig.summaryCountField]
|
||||
: []
|
||||
);
|
||||
});
|
||||
|
||||
it('job cloning pre-fills detectors', async () => {
|
||||
for (const [index, detector] of testData.pickFieldsConfig.detectors.entries()) {
|
||||
await ml.jobWizardAdvanced.assertDetectorEntryExists(
|
||||
index,
|
||||
detector.identifier,
|
||||
isDetectorWithDescription(detector) ? detector.description : undefined
|
||||
);
|
||||
await ml.jobWizardAdvanced.clickEditDetector(index);
|
||||
|
||||
await ml.jobWizardAdvanced.assertDetectorFunctionInputExists();
|
||||
await ml.jobWizardAdvanced.assertDetectorFieldInputExists();
|
||||
await ml.jobWizardAdvanced.assertDetectorByFieldInputExists();
|
||||
await ml.jobWizardAdvanced.assertDetectorOverFieldInputExists();
|
||||
await ml.jobWizardAdvanced.assertDetectorPartitionFieldInputExists();
|
||||
await ml.jobWizardAdvanced.assertDetectorExcludeFrequentInputExists();
|
||||
await ml.jobWizardAdvanced.assertDetectorDescriptionInputExists();
|
||||
|
||||
await ml.jobWizardAdvanced.assertDetectorFunctionSelection([detector.function]);
|
||||
await ml.jobWizardAdvanced.assertDetectorFieldSelection(
|
||||
isDetectorWithField(detector) ? [detector.field] : []
|
||||
);
|
||||
await ml.jobWizardAdvanced.assertDetectorByFieldSelection(
|
||||
isDetectorWithByField(detector) ? [detector.byField] : []
|
||||
);
|
||||
await ml.jobWizardAdvanced.assertDetectorOverFieldSelection(
|
||||
isDetectorWithOverField(detector) ? [detector.overField] : []
|
||||
);
|
||||
await ml.jobWizardAdvanced.assertDetectorPartitionFieldSelection(
|
||||
isDetectorWithPartitionField(detector) ? [detector.partitionField] : []
|
||||
);
|
||||
await ml.jobWizardAdvanced.assertDetectorExcludeFrequentSelection(
|
||||
isDetectorWithExcludeFrequent(detector) ? [detector.excludeFrequent] : []
|
||||
);
|
||||
// Currently, a description different form the identifier is generated for detectors with partition field
|
||||
await ml.jobWizardAdvanced.assertDetectorDescriptionValue(
|
||||
isDetectorWithDescription(detector)
|
||||
? detector.description
|
||||
: detector.identifier.replace('partition_field_name', 'partitionfield')
|
||||
);
|
||||
|
||||
await ml.jobWizardAdvanced.cancelAddDetectorModal();
|
||||
}
|
||||
});
|
||||
|
||||
it('job cloning pre-fills the bucket span', async () => {
|
||||
await ml.jobWizardCommon.assertBucketSpanInputExists();
|
||||
await ml.jobWizardCommon.assertBucketSpanValue(testData.pickFieldsConfig.bucketSpan);
|
||||
});
|
||||
|
||||
it('job cloning pre-fills influencers', async () => {
|
||||
await ml.jobWizardCommon.assertInfluencerInputExists();
|
||||
await ml.jobWizardCommon.assertInfluencerSelection(testData.pickFieldsConfig.influencers);
|
||||
});
|
||||
|
||||
it('job cloning pre-fills the model memory limit', async () => {
|
||||
await ml.jobWizardCommon.assertModelMemoryLimitInputExists({
|
||||
withAdvancedSection: false,
|
||||
});
|
||||
await ml.jobWizardCommon.assertModelMemoryLimitValue(
|
||||
testData.pickFieldsConfig.memoryLimit,
|
||||
{
|
||||
withAdvancedSection: false,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('job cloning displays the job details step', async () => {
|
||||
await ml.jobWizardCommon.advanceToJobDetailsSection();
|
||||
});
|
||||
|
||||
it('job cloning does not pre-fill the job id', async () => {
|
||||
await ml.jobWizardCommon.assertJobIdInputExists();
|
||||
await ml.jobWizardCommon.assertJobIdValue('');
|
||||
});
|
||||
|
||||
it('job cloning inputs the clone job id', async () => {
|
||||
await ml.jobWizardCommon.setJobId(testData.jobIdClone);
|
||||
});
|
||||
|
||||
it('job cloning pre-fills the job description', async () => {
|
||||
await ml.jobWizardCommon.assertJobDescriptionInputExists();
|
||||
await ml.jobWizardCommon.assertJobDescriptionValue(testData.jobDescription);
|
||||
});
|
||||
|
||||
it('job cloning pre-fills job groups', async () => {
|
||||
await ml.jobWizardCommon.assertJobGroupInputExists();
|
||||
await ml.jobWizardCommon.assertJobGroupSelection(testData.jobGroups);
|
||||
});
|
||||
|
||||
it('job cloning inputs the clone job group', async () => {
|
||||
await ml.jobWizardCommon.assertJobGroupInputExists();
|
||||
await ml.jobWizardCommon.addJobGroup('clone');
|
||||
await ml.jobWizardCommon.assertJobGroupSelection(testData.jobGroupsClone);
|
||||
});
|
||||
|
||||
it('job cloning pre-fills the model plot switch', async () => {
|
||||
await ml.jobWizardCommon.assertModelPlotSwitchExists({ withAdvancedSection: false });
|
||||
await ml.jobWizardCommon.assertModelPlotSwitchCheckedState(false, {
|
||||
withAdvancedSection: false,
|
||||
});
|
||||
});
|
||||
|
||||
it('job cloning pre-fills the dedicated index switch', async () => {
|
||||
await ml.jobWizardCommon.assertDedicatedIndexSwitchExists({ withAdvancedSection: false });
|
||||
await ml.jobWizardCommon.assertDedicatedIndexSwitchCheckedState(true, {
|
||||
withAdvancedSection: false,
|
||||
});
|
||||
});
|
||||
|
||||
it('job cloning displays the validation step', async () => {
|
||||
await ml.jobWizardCommon.advanceToValidationSection();
|
||||
});
|
||||
|
||||
it('job cloning displays the summary step', async () => {
|
||||
await ml.jobWizardCommon.advanceToSummarySection();
|
||||
});
|
||||
|
||||
it('job cloning creates the job and finishes processing', async () => {
|
||||
await ml.jobWizardCommon.assertCreateJobButtonExists();
|
||||
await ml.jobWizardAdvanced.createJob();
|
||||
await ml.jobManagement.assertStartDatafeedModalExists();
|
||||
await ml.jobManagement.confirmStartDatafeedModal();
|
||||
await ml.jobManagement.waitForJobCompletion(testData.jobIdClone);
|
||||
});
|
||||
|
||||
it('job cloning displays the created job in the job list', async () => {
|
||||
await ml.jobTable.refreshJobList();
|
||||
await ml.jobTable.filterWithSearchString(testData.jobIdClone);
|
||||
const rows = await ml.jobTable.parseJobTable();
|
||||
expect(rows.filter(row => row.id === testData.jobIdClone)).to.have.length(1);
|
||||
});
|
||||
|
||||
it('job creation displays details for the created job in the job list', async () => {
|
||||
await ml.jobTable.assertJobRowFields(testData.jobIdClone, {
|
||||
id: testData.jobIdClone,
|
||||
description: testData.jobDescription,
|
||||
jobGroups: [...new Set(testData.jobGroupsClone)].sort(),
|
||||
recordCount: testData.expected.row.recordCount,
|
||||
memoryStatus: testData.expected.row.memoryStatus,
|
||||
jobState: testData.expected.row.jobState,
|
||||
datafeedState: testData.expected.row.datafeedState,
|
||||
latestTimestamp: testData.expected.row.latestTimestamp,
|
||||
});
|
||||
|
||||
await ml.jobTable.assertJobRowDetailsCounts(
|
||||
testData.jobIdClone,
|
||||
{
|
||||
job_id: testData.jobIdClone,
|
||||
processed_record_count: testData.expected.counts.processed_record_count,
|
||||
processed_field_count: testData.expected.counts.processed_field_count,
|
||||
input_bytes: testData.expected.counts.input_bytes,
|
||||
input_field_count: testData.expected.counts.input_field_count,
|
||||
invalid_date_count: testData.expected.counts.invalid_date_count,
|
||||
missing_field_count: testData.expected.counts.missing_field_count,
|
||||
out_of_order_timestamp_count: testData.expected.counts.out_of_order_timestamp_count,
|
||||
empty_bucket_count: testData.expected.counts.empty_bucket_count,
|
||||
sparse_bucket_count: testData.expected.counts.sparse_bucket_count,
|
||||
bucket_count: testData.expected.counts.bucket_count,
|
||||
earliest_record_timestamp: testData.expected.counts.earliest_record_timestamp,
|
||||
latest_record_timestamp: testData.expected.counts.latest_record_timestamp,
|
||||
input_record_count: testData.expected.counts.input_record_count,
|
||||
latest_bucket_timestamp: testData.expected.counts.latest_bucket_timestamp,
|
||||
},
|
||||
{
|
||||
job_id: testData.jobIdClone,
|
||||
result_type: testData.expected.modelSizeStats.result_type,
|
||||
model_bytes_exceeded: testData.expected.modelSizeStats.model_bytes_exceeded,
|
||||
model_bytes_memory_limit: testData.expected.modelSizeStats.model_bytes_memory_limit,
|
||||
total_by_field_count: testData.expected.modelSizeStats.total_by_field_count,
|
||||
total_over_field_count: testData.expected.modelSizeStats.total_over_field_count,
|
||||
total_partition_field_count:
|
||||
testData.expected.modelSizeStats.total_partition_field_count,
|
||||
bucket_allocation_failures_count:
|
||||
testData.expected.modelSizeStats.bucket_allocation_failures_count,
|
||||
memory_status: testData.expected.modelSizeStats.memory_status,
|
||||
timestamp: testData.expected.modelSizeStats.timestamp,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('job creation has detector results', async () => {
|
||||
for (let i = 0; i < testData.pickFieldsConfig.detectors.length; i++) {
|
||||
await ml.api.assertDetectorResultsExist(testData.jobIdClone, i);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
|
@ -11,5 +11,6 @@ export default function({ loadTestFile }: FtrProviderContext) {
|
|||
loadTestFile(require.resolve('./multi_metric_job'));
|
||||
loadTestFile(require.resolve('./population_job'));
|
||||
loadTestFile(require.resolve('./saved_search_job'));
|
||||
loadTestFile(require.resolve('./advanced_job'));
|
||||
});
|
||||
}
|
||||
|
|
|
@ -221,6 +221,12 @@ export default function({ getService }: FtrProviderContext) {
|
|||
);
|
||||
});
|
||||
|
||||
it('job creation has detector results', async () => {
|
||||
for (let i = 0; i < aggAndFieldIdentifiers.length; i++) {
|
||||
await ml.api.assertDetectorResultsExist(jobId, i);
|
||||
}
|
||||
});
|
||||
|
||||
it('job cloning clicks the clone action and loads the multi metric wizard', async () => {
|
||||
await ml.jobTable.clickCloneJobAction(jobId);
|
||||
await ml.jobTypeSelection.assertMultiMetricJobWizardOpen();
|
||||
|
@ -258,7 +264,7 @@ export default function({ getService }: FtrProviderContext) {
|
|||
|
||||
it('job cloning pre-fills the split field', async () => {
|
||||
await ml.jobWizardMultiMetric.assertSplitFieldInputExists();
|
||||
await ml.jobWizardMultiMetric.assertSplitFieldSelection(splitField);
|
||||
await ml.jobWizardMultiMetric.assertSplitFieldSelection([splitField]);
|
||||
});
|
||||
|
||||
it('job cloning pre-fills influencers', async () => {
|
||||
|
@ -351,5 +357,11 @@ export default function({ getService }: FtrProviderContext) {
|
|||
getExpectedModelSizeStats(jobIdClone)
|
||||
);
|
||||
});
|
||||
|
||||
it('job cloning has detector results', async () => {
|
||||
for (let i = 0; i < aggAndFieldIdentifiers.length; i++) {
|
||||
await ml.api.assertDetectorResultsExist(jobId, i);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -248,6 +248,12 @@ export default function({ getService }: FtrProviderContext) {
|
|||
);
|
||||
});
|
||||
|
||||
it('job creation has detector results', async () => {
|
||||
for (let i = 0; i < detectors.length; i++) {
|
||||
await ml.api.assertDetectorResultsExist(jobId, i);
|
||||
}
|
||||
});
|
||||
|
||||
it('job cloning clicks the clone action and loads the population wizard', async () => {
|
||||
await ml.jobTable.clickCloneJobAction(jobId);
|
||||
await ml.jobTypeSelection.assertPopulationJobWizardOpen();
|
||||
|
@ -275,14 +281,16 @@ export default function({ getService }: FtrProviderContext) {
|
|||
|
||||
it('job cloning pre-fills the population field', async () => {
|
||||
await ml.jobWizardPopulation.assertPopulationFieldInputExists();
|
||||
await ml.jobWizardPopulation.assertPopulationFieldSelection(populationField);
|
||||
await ml.jobWizardPopulation.assertPopulationFieldSelection([populationField]);
|
||||
});
|
||||
|
||||
it('job cloning 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.assertDetectorSplitFieldSelection(index, [
|
||||
detector.splitField,
|
||||
]);
|
||||
await ml.jobWizardPopulation.assertDetectorSplitExists(index);
|
||||
await ml.jobWizardPopulation.assertDetectorSplitFrontCardTitle(
|
||||
index,
|
||||
|
@ -387,5 +395,11 @@ export default function({ getService }: FtrProviderContext) {
|
|||
getExpectedModelSizeStats(jobIdClone)
|
||||
);
|
||||
});
|
||||
|
||||
it('job cloning has detector results', async () => {
|
||||
for (let i = 0; i < detectors.length; i++) {
|
||||
await ml.api.assertDetectorResultsExist(jobId, i);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -468,6 +468,12 @@ export default function({ getService }: FtrProviderContext) {
|
|||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('has detector results', async () => {
|
||||
for (let i = 0; i < testData.aggAndFieldIdentifiers.length; i++) {
|
||||
await ml.api.assertDetectorResultsExist(testData.jobId, i);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
|
@ -202,6 +202,10 @@ export default function({ getService }: FtrProviderContext) {
|
|||
);
|
||||
});
|
||||
|
||||
it('job creation has detector results', async () => {
|
||||
await ml.api.assertDetectorResultsExist(jobId, 0);
|
||||
});
|
||||
|
||||
it('job cloning clicks the clone action and loads the single metric wizard', async () => {
|
||||
await ml.jobTable.clickCloneJobAction(jobId);
|
||||
await ml.jobTypeSelection.assertSingleMetricJobWizardOpen();
|
||||
|
@ -319,6 +323,10 @@ export default function({ getService }: FtrProviderContext) {
|
|||
);
|
||||
});
|
||||
|
||||
it('job cloning has detector results', async () => {
|
||||
await ml.api.assertDetectorResultsExist(jobId, 0);
|
||||
});
|
||||
|
||||
it('job deletion has results for the job before deletion', async () => {
|
||||
await ml.api.assertJobResultsExist(jobIdClone);
|
||||
});
|
||||
|
|
|
@ -21,24 +21,6 @@
|
|||
"airline": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"host": {
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"ignore_above": 256,
|
||||
"type": "keyword"
|
||||
}
|
||||
},
|
||||
"type": "text"
|
||||
},
|
||||
"path": {
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"ignore_above": 256,
|
||||
"type": "keyword"
|
||||
}
|
||||
},
|
||||
"type": "text"
|
||||
},
|
||||
"responsetime": {
|
||||
"type": "float"
|
||||
},
|
||||
|
@ -1102,4 +1084,4 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,10 +8,13 @@ import expect from '@kbn/expect';
|
|||
import { isEmpty } from 'lodash';
|
||||
import { FtrProviderContext } from '../../ftr_provider_context';
|
||||
|
||||
import { JOB_STATE, DATAFEED_STATE } from '../../../../legacy/plugins/ml/common/constants/states';
|
||||
|
||||
export function MachineLearningAPIProvider({ getService }: FtrProviderContext) {
|
||||
const es = getService('es');
|
||||
const log = getService('log');
|
||||
const retry = getService('retry');
|
||||
const esSupertest = getService('esSupertest');
|
||||
|
||||
return {
|
||||
async hasJobResults(jobId: string): Promise<boolean> {
|
||||
|
@ -54,6 +57,54 @@ export function MachineLearningAPIProvider({ getService }: FtrProviderContext) {
|
|||
);
|
||||
},
|
||||
|
||||
async hasDetectorResults(jobId: string, detectorIndex: number): Promise<boolean> {
|
||||
const response = await es.search({
|
||||
index: '.ml-anomalies-*',
|
||||
body: {
|
||||
size: 1,
|
||||
query: {
|
||||
bool: {
|
||||
must: [
|
||||
{
|
||||
match: {
|
||||
job_id: jobId,
|
||||
},
|
||||
},
|
||||
{
|
||||
match: {
|
||||
result_type: 'record',
|
||||
},
|
||||
},
|
||||
{
|
||||
match: {
|
||||
detector_index: detectorIndex,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return response.hits.hits.length > 0;
|
||||
},
|
||||
|
||||
async assertDetectorResultsExist(jobId: string, detectorIndex: number) {
|
||||
await retry.waitForWithTimeout(
|
||||
`results for detector ${detectorIndex} on job ${jobId} to exist`,
|
||||
30 * 1000,
|
||||
async () => {
|
||||
if ((await this.hasDetectorResults(jobId, detectorIndex)) === true) {
|
||||
return true;
|
||||
} else {
|
||||
throw new Error(
|
||||
`expected results for detector ${detectorIndex} on job '${jobId}' to exist`
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
async deleteIndices(indices: string) {
|
||||
log.debug(`Deleting indices: '${indices}'...`);
|
||||
const deleteResponse = await es.indices.delete({
|
||||
|
@ -79,5 +130,61 @@ export function MachineLearningAPIProvider({ getService }: FtrProviderContext) {
|
|||
async cleanMlIndices() {
|
||||
await this.deleteIndices('.ml-*');
|
||||
},
|
||||
|
||||
async getJobState(jobId: string): Promise<JOB_STATE> {
|
||||
log.debug(`Fetching job state for job ${jobId}`);
|
||||
const jobStats = await esSupertest
|
||||
.get(`/_ml/anomaly_detectors/${jobId}/_stats`)
|
||||
.expect(200)
|
||||
.then((res: any) => res.body);
|
||||
|
||||
expect(jobStats.jobs).to.have.length(1);
|
||||
const state: JOB_STATE = jobStats.jobs[0].state;
|
||||
|
||||
return state;
|
||||
},
|
||||
|
||||
async waitForJobState(jobId: string, expectedJobState: JOB_STATE) {
|
||||
await retry.waitForWithTimeout(
|
||||
`job state to be ${expectedJobState}`,
|
||||
2 * 60 * 1000,
|
||||
async () => {
|
||||
const state = await this.getJobState(jobId);
|
||||
if (state === expectedJobState) {
|
||||
return true;
|
||||
} else {
|
||||
throw new Error(`expected job state to be ${expectedJobState} but got ${state}`);
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
async getDatafeedState(datafeedId: string): Promise<DATAFEED_STATE> {
|
||||
log.debug(`Fetching datafeed state for datafeed ${datafeedId}`);
|
||||
const datafeedStats = await esSupertest
|
||||
.get(`/_ml/datafeeds/${datafeedId}/_stats`)
|
||||
.expect(200)
|
||||
.then((res: any) => res.body);
|
||||
|
||||
expect(datafeedStats.datafeeds).to.have.length(1);
|
||||
const state: DATAFEED_STATE = datafeedStats.datafeeds[0].state;
|
||||
|
||||
return state;
|
||||
},
|
||||
|
||||
async waitForDatafeedState(datafeedId: string, expectedDatafeedState: DATAFEED_STATE) {
|
||||
await retry.waitForWithTimeout(
|
||||
`datafeed state to be ${expectedDatafeedState}`,
|
||||
2 * 60 * 1000,
|
||||
async () => {
|
||||
const state = await this.getDatafeedState(datafeedId);
|
||||
if (state === expectedDatafeedState) {
|
||||
return true;
|
||||
} else {
|
||||
throw new Error(`expected job state to be ${expectedDatafeedState} but got ${state}`);
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ export { MachineLearningJobManagementProvider } from './job_management';
|
|||
export { MachineLearningJobSourceSelectionProvider } from './job_source_selection';
|
||||
export { MachineLearningJobTableProvider } from './job_table';
|
||||
export { MachineLearningJobTypeSelectionProvider } from './job_type_selection';
|
||||
export { MachineLearningJobWizardAdvancedProvider } from './job_wizard_advanced';
|
||||
export { MachineLearningJobWizardCommonProvider } from './job_wizard_common';
|
||||
export { MachineLearningJobWizardMultiMetricProvider } from './job_wizard_multi_metric';
|
||||
export { MachineLearningJobWizardPopulationProvider } from './job_wizard_population';
|
||||
|
|
|
@ -3,11 +3,19 @@
|
|||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
import { ProvidedType } from '@kbn/test/types/ftr';
|
||||
|
||||
import { FtrProviderContext } from '../../ftr_provider_context';
|
||||
import { MachineLearningAPIProvider } from './api';
|
||||
|
||||
export function MachineLearningJobManagementProvider({ getService }: FtrProviderContext) {
|
||||
import { JOB_STATE, DATAFEED_STATE } from '../../../../legacy/plugins/ml/common/constants/states';
|
||||
|
||||
export function MachineLearningJobManagementProvider(
|
||||
{ getService }: FtrProviderContext,
|
||||
mlApi: ProvidedType<typeof MachineLearningAPIProvider>
|
||||
) {
|
||||
const testSubjects = getService('testSubjects');
|
||||
const retry = getService('retry');
|
||||
|
||||
return {
|
||||
async navigateToNewJobSourceSelection() {
|
||||
|
@ -26,5 +34,22 @@ export function MachineLearningJobManagementProvider({ getService }: FtrProvider
|
|||
async assertJobStatsBarExists() {
|
||||
await testSubjects.existOrFail('~mlJobStatsBar');
|
||||
},
|
||||
|
||||
async assertStartDatafeedModalExists() {
|
||||
// this retry can be removed as soon as #48734 is merged
|
||||
await retry.tryForTime(5000, async () => {
|
||||
await testSubjects.existOrFail('mlStartDatafeedModal');
|
||||
});
|
||||
},
|
||||
|
||||
async confirmStartDatafeedModal() {
|
||||
await testSubjects.click('mlStartDatafeedModalStartButton');
|
||||
await testSubjects.missingOrFail('mlStartDatafeedModal');
|
||||
},
|
||||
|
||||
async waitForJobCompletion(jobId: string) {
|
||||
await mlApi.waitForDatafeedState(`datafeed-${jobId}`, DATAFEED_STATE.STOPPED);
|
||||
await mlApi.waitForJobState(jobId, JOB_STATE.CLOSED);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -157,6 +157,11 @@ export function MachineLearningJobTableProvider({ getService }: FtrProviderConte
|
|||
});
|
||||
}
|
||||
|
||||
public async refreshJobList() {
|
||||
await testSubjects.click('mlRefreshJobListButton');
|
||||
await this.waitForJobsToLoad();
|
||||
}
|
||||
|
||||
public async waitForJobsToLoad() {
|
||||
await testSubjects.existOrFail('~mlJobListTable', { timeout: 60 * 1000 });
|
||||
await testSubjects.existOrFail('mlJobListTable loaded', { timeout: 30 * 1000 });
|
||||
|
@ -206,7 +211,7 @@ export function MachineLearningJobTableProvider({ getService }: FtrProviderConte
|
|||
}
|
||||
|
||||
public async clickActionsMenu(jobId: string) {
|
||||
retry.tryForTime(30 * 1000, async () => {
|
||||
await retry.tryForTime(30 * 1000, async () => {
|
||||
if (!(await testSubjects.exists('mlActionButtonDeleteJob'))) {
|
||||
await testSubjects.click(this.rowSelector(jobId, 'euiCollapsedItemActionsButton'));
|
||||
await testSubjects.existOrFail('mlActionButtonDeleteJob', { timeout: 5000 });
|
||||
|
|
|
@ -36,5 +36,14 @@ export function MachineLearningJobTypeSelectionProvider({ getService }: FtrProvi
|
|||
async assertPopulationJobWizardOpen() {
|
||||
await testSubjects.existOrFail('mlPageJobWizard population');
|
||||
},
|
||||
|
||||
async selectAdvancedJob() {
|
||||
await testSubjects.clickWhenNotDisabled('mlJobTypeLinkAdvancedJob');
|
||||
await this.assertAdvancedJobWizardOpen();
|
||||
},
|
||||
|
||||
async assertAdvancedJobWizardOpen() {
|
||||
await testSubjects.existOrFail('mlPageJobWizard advanced');
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -0,0 +1,315 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
import expect from '@kbn/expect';
|
||||
|
||||
import { FtrProviderContext } from '../../ftr_provider_context';
|
||||
|
||||
export function MachineLearningJobWizardAdvancedProvider({
|
||||
getService,
|
||||
getPageObjects,
|
||||
}: FtrProviderContext) {
|
||||
const comboBox = getService('comboBox');
|
||||
const testSubjects = getService('testSubjects');
|
||||
const retry = getService('retry');
|
||||
const aceEditor = getService('aceEditor');
|
||||
|
||||
return {
|
||||
async getValueOrPlaceholder(inputLocator: string): Promise<string> {
|
||||
const value = await testSubjects.getAttribute(inputLocator, 'value');
|
||||
if (value !== '') {
|
||||
return value;
|
||||
} else {
|
||||
return await testSubjects.getAttribute(inputLocator, 'placeholder');
|
||||
}
|
||||
},
|
||||
|
||||
async assertDatafeedQueryEditorExists() {
|
||||
await testSubjects.existOrFail('mlAdvancedDatafeedQueryEditor > codeEditorContainer');
|
||||
},
|
||||
|
||||
async assertDatafeedQueryEditorValue(expectedValue: string) {
|
||||
const actualValue = await aceEditor.getValue(
|
||||
'mlAdvancedDatafeedQueryEditor > codeEditorContainer'
|
||||
);
|
||||
expect(actualValue).to.eql(expectedValue);
|
||||
},
|
||||
|
||||
async assertQueryDelayInputExists() {
|
||||
await testSubjects.existOrFail('mlJobWizardInputQueryDelay');
|
||||
},
|
||||
|
||||
async assertQueryDelayValue(expectedValue: string) {
|
||||
const actualQueryDelay = await this.getValueOrPlaceholder('mlJobWizardInputQueryDelay');
|
||||
expect(actualQueryDelay).to.eql(expectedValue);
|
||||
},
|
||||
|
||||
async setQueryDelay(queryDelay: string) {
|
||||
await testSubjects.setValue('mlJobWizardInputQueryDelay', queryDelay, {
|
||||
clearWithKeyboard: true,
|
||||
typeCharByChar: true,
|
||||
});
|
||||
await this.assertQueryDelayValue(queryDelay);
|
||||
},
|
||||
|
||||
async assertFrequencyInputExists() {
|
||||
await testSubjects.existOrFail('mlJobWizardInputFrequency');
|
||||
},
|
||||
|
||||
async assertFrequencyValue(expectedValue: string) {
|
||||
const actualFrequency = await this.getValueOrPlaceholder('mlJobWizardInputFrequency');
|
||||
expect(actualFrequency).to.eql(expectedValue);
|
||||
},
|
||||
|
||||
async setFrequency(frequency: string) {
|
||||
await testSubjects.setValue('mlJobWizardInputFrequency', frequency, {
|
||||
clearWithKeyboard: true,
|
||||
typeCharByChar: true,
|
||||
});
|
||||
await this.assertFrequencyValue(frequency);
|
||||
},
|
||||
|
||||
async assertScrollSizeInputExists() {
|
||||
await testSubjects.existOrFail('mlJobWizardInputScrollSize');
|
||||
},
|
||||
|
||||
async assertScrollSizeValue(expectedValue: string) {
|
||||
const actualScrollSize = await this.getValueOrPlaceholder('mlJobWizardInputScrollSize');
|
||||
expect(actualScrollSize).to.eql(expectedValue);
|
||||
},
|
||||
|
||||
async setScrollSize(scrollSize: string) {
|
||||
await testSubjects.setValue('mlJobWizardInputScrollSize', scrollSize, {
|
||||
clearWithKeyboard: true,
|
||||
typeCharByChar: true,
|
||||
});
|
||||
await this.assertScrollSizeValue(scrollSize);
|
||||
},
|
||||
|
||||
async assertTimeFieldInputExists() {
|
||||
await testSubjects.existOrFail('mlTimeFieldNameSelect > comboBoxInput');
|
||||
},
|
||||
|
||||
async assertTimeFieldSelection(expectedIdentifier: string[]) {
|
||||
const comboBoxSelectedOptions = await comboBox.getComboBoxSelectedOptions(
|
||||
'mlTimeFieldNameSelect > comboBoxInput'
|
||||
);
|
||||
expect(comboBoxSelectedOptions).to.eql(expectedIdentifier);
|
||||
},
|
||||
|
||||
async selectTimeField(identifier: string) {
|
||||
await comboBox.set('mlTimeFieldNameSelect > comboBoxInput', identifier);
|
||||
await this.assertTimeFieldSelection([identifier]);
|
||||
},
|
||||
|
||||
async assertCategorizationFieldInputExists() {
|
||||
await testSubjects.existOrFail('mlCategorizationFieldNameSelect > comboBoxInput');
|
||||
},
|
||||
|
||||
async assertCategorizationFieldSelection(expectedIdentifier: string[]) {
|
||||
const comboBoxSelectedOptions = await comboBox.getComboBoxSelectedOptions(
|
||||
'mlCategorizationFieldNameSelect > comboBoxInput'
|
||||
);
|
||||
expect(comboBoxSelectedOptions).to.eql(expectedIdentifier);
|
||||
},
|
||||
|
||||
async selectCategorizationField(identifier: string) {
|
||||
await comboBox.set('mlCategorizationFieldNameSelect > comboBoxInput', identifier);
|
||||
await this.assertCategorizationFieldSelection([identifier]);
|
||||
},
|
||||
|
||||
async assertSummaryCountFieldInputExists() {
|
||||
await testSubjects.existOrFail('mlSummaryCountFieldNameSelect > comboBoxInput');
|
||||
},
|
||||
|
||||
async assertSummaryCountFieldSelection(expectedIdentifier: string[]) {
|
||||
const comboBoxSelectedOptions = await comboBox.getComboBoxSelectedOptions(
|
||||
'mlSummaryCountFieldNameSelect > comboBoxInput'
|
||||
);
|
||||
expect(comboBoxSelectedOptions).to.eql(expectedIdentifier);
|
||||
},
|
||||
|
||||
async selectSummaryCountField(identifier: string) {
|
||||
await comboBox.set('mlSummaryCountFieldNameSelect > comboBoxInput', identifier);
|
||||
await this.assertSummaryCountFieldSelection([identifier]);
|
||||
},
|
||||
|
||||
async assertAddDetectorButtonExists() {
|
||||
await testSubjects.existOrFail('mlAddDetectorButton');
|
||||
},
|
||||
|
||||
async openCreateDetectorModal() {
|
||||
await testSubjects.click('mlAddDetectorButton');
|
||||
await this.assertCreateDetectorModalExists();
|
||||
},
|
||||
|
||||
async assertCreateDetectorModalExists() {
|
||||
// this retry can be removed as soon as #48734 is merged
|
||||
await retry.tryForTime(5000, async () => {
|
||||
await testSubjects.existOrFail('mlCreateDetectorModal');
|
||||
});
|
||||
},
|
||||
|
||||
async assertDetectorFunctionInputExists() {
|
||||
await testSubjects.existOrFail('mlAdvancedFunctionSelect > comboBoxInput');
|
||||
},
|
||||
|
||||
async assertDetectorFunctionSelection(expectedIdentifier: string[]) {
|
||||
const comboBoxSelectedOptions = await comboBox.getComboBoxSelectedOptions(
|
||||
'mlAdvancedFunctionSelect > comboBoxInput'
|
||||
);
|
||||
expect(comboBoxSelectedOptions).to.eql(expectedIdentifier);
|
||||
},
|
||||
|
||||
async selectDetectorFunction(identifier: string) {
|
||||
await comboBox.set('mlAdvancedFunctionSelect > comboBoxInput', identifier);
|
||||
await this.assertDetectorFunctionSelection([identifier]);
|
||||
},
|
||||
|
||||
async assertDetectorFieldInputExists() {
|
||||
await testSubjects.existOrFail('mlAdvancedFieldSelect > comboBoxInput');
|
||||
},
|
||||
|
||||
async assertDetectorFieldSelection(expectedIdentifier: string[]) {
|
||||
const comboBoxSelectedOptions = await comboBox.getComboBoxSelectedOptions(
|
||||
'mlAdvancedFieldSelect > comboBoxInput'
|
||||
);
|
||||
expect(comboBoxSelectedOptions).to.eql(expectedIdentifier);
|
||||
},
|
||||
|
||||
async selectDetectorField(identifier: string) {
|
||||
await comboBox.set('mlAdvancedFieldSelect > comboBoxInput', identifier);
|
||||
await this.assertDetectorFieldSelection([identifier]);
|
||||
},
|
||||
|
||||
async assertDetectorByFieldInputExists() {
|
||||
await testSubjects.existOrFail('mlAdvancedByFieldSelect > comboBoxInput');
|
||||
},
|
||||
|
||||
async assertDetectorByFieldSelection(expectedIdentifier: string[]) {
|
||||
const comboBoxSelectedOptions = await comboBox.getComboBoxSelectedOptions(
|
||||
'mlAdvancedByFieldSelect > comboBoxInput'
|
||||
);
|
||||
expect(comboBoxSelectedOptions).to.eql(expectedIdentifier);
|
||||
},
|
||||
|
||||
async selectDetectorByField(identifier: string) {
|
||||
await comboBox.set('mlAdvancedByFieldSelect > comboBoxInput', identifier);
|
||||
await this.assertDetectorByFieldSelection([identifier]);
|
||||
},
|
||||
|
||||
async assertDetectorOverFieldInputExists() {
|
||||
await testSubjects.existOrFail('mlAdvancedOverFieldSelect > comboBoxInput');
|
||||
},
|
||||
|
||||
async assertDetectorOverFieldSelection(expectedIdentifier: string[]) {
|
||||
const comboBoxSelectedOptions = await comboBox.getComboBoxSelectedOptions(
|
||||
'mlAdvancedOverFieldSelect > comboBoxInput'
|
||||
);
|
||||
expect(comboBoxSelectedOptions).to.eql(expectedIdentifier);
|
||||
},
|
||||
|
||||
async selectDetectorOverField(identifier: string) {
|
||||
await comboBox.set('mlAdvancedOverFieldSelect > comboBoxInput', identifier);
|
||||
await this.assertDetectorOverFieldSelection([identifier]);
|
||||
},
|
||||
|
||||
async assertDetectorPartitionFieldInputExists() {
|
||||
await testSubjects.existOrFail('mlAdvancedPartitionFieldSelect > comboBoxInput');
|
||||
},
|
||||
|
||||
async assertDetectorPartitionFieldSelection(expectedIdentifier: string[]) {
|
||||
const comboBoxSelectedOptions = await comboBox.getComboBoxSelectedOptions(
|
||||
'mlAdvancedPartitionFieldSelect > comboBoxInput'
|
||||
);
|
||||
expect(comboBoxSelectedOptions).to.eql(expectedIdentifier);
|
||||
},
|
||||
|
||||
async selectDetectorPartitionField(identifier: string) {
|
||||
await comboBox.set('mlAdvancedPartitionFieldSelect > comboBoxInput', identifier);
|
||||
await this.assertDetectorPartitionFieldSelection([identifier]);
|
||||
},
|
||||
|
||||
async assertDetectorExcludeFrequentInputExists() {
|
||||
await testSubjects.existOrFail('mlAdvancedExcludeFrequentSelect > comboBoxInput');
|
||||
},
|
||||
|
||||
async assertDetectorExcludeFrequentSelection(expectedIdentifier: string[]) {
|
||||
const comboBoxSelectedOptions = await comboBox.getComboBoxSelectedOptions(
|
||||
'mlAdvancedExcludeFrequentSelect > comboBoxInput'
|
||||
);
|
||||
expect(comboBoxSelectedOptions).to.eql(expectedIdentifier);
|
||||
},
|
||||
|
||||
async selectDetectorExcludeFrequent(identifier: string) {
|
||||
await comboBox.set('mlAdvancedExcludeFrequentSelect > comboBoxInput', identifier);
|
||||
await this.assertDetectorExcludeFrequentSelection([identifier]);
|
||||
},
|
||||
|
||||
async assertDetectorDescriptionInputExists() {
|
||||
await testSubjects.existOrFail('mlAdvancedDetectorDescriptionInput');
|
||||
},
|
||||
|
||||
async assertDetectorDescriptionValue(expectedValue: string) {
|
||||
const actualDetectorDescription = await testSubjects.getAttribute(
|
||||
'mlAdvancedDetectorDescriptionInput',
|
||||
'value'
|
||||
);
|
||||
expect(actualDetectorDescription).to.eql(expectedValue);
|
||||
},
|
||||
|
||||
async setDetectorDescription(description: string) {
|
||||
await testSubjects.setValue('mlAdvancedDetectorDescriptionInput', description, {
|
||||
clearWithKeyboard: true,
|
||||
});
|
||||
await this.assertDetectorDescriptionValue(description);
|
||||
},
|
||||
|
||||
async confirmAddDetectorModal() {
|
||||
await testSubjects.clickWhenNotDisabled('mlCreateDetectorModalSaveButton');
|
||||
await testSubjects.missingOrFail('mlCreateDetectorModal');
|
||||
},
|
||||
|
||||
async cancelAddDetectorModal() {
|
||||
await testSubjects.clickWhenNotDisabled('mlCreateDetectorModalCancelButton');
|
||||
await testSubjects.missingOrFail('mlCreateDetectorModal');
|
||||
},
|
||||
|
||||
async assertDetectorEntryExists(
|
||||
detectorIndex: number,
|
||||
expectedDetectorName: string,
|
||||
expectedDetectorDescription?: string
|
||||
) {
|
||||
await testSubjects.existOrFail(`mlAdvancedDetector ${detectorIndex}`);
|
||||
|
||||
const actualDetectorIdentifier = await testSubjects.getVisibleText(
|
||||
`mlAdvancedDetector ${detectorIndex} > mlDetectorIdentifier`
|
||||
);
|
||||
expect(actualDetectorIdentifier).to.eql(expectedDetectorName);
|
||||
|
||||
if (expectedDetectorDescription !== undefined) {
|
||||
const actualDetectorDescription = await testSubjects.getVisibleText(
|
||||
`mlAdvancedDetector ${detectorIndex} > mlDetectorDescription`
|
||||
);
|
||||
expect(actualDetectorDescription).to.eql(expectedDetectorDescription);
|
||||
}
|
||||
},
|
||||
|
||||
async clickEditDetector(detectorIndex: number) {
|
||||
await testSubjects.click(
|
||||
`mlAdvancedDetector ${detectorIndex} > mlAdvancedDetectorEditButton`
|
||||
);
|
||||
await this.assertCreateDetectorModalExists();
|
||||
},
|
||||
|
||||
async createJob() {
|
||||
await testSubjects.clickWhenNotDisabled('mlJobWizardButtonCreateJob');
|
||||
// this retry can be removed as soon as #48734 is merged
|
||||
await retry.tryForTime(5000, async () => {
|
||||
await testSubjects.existOrFail('mlStartDatafeedModal');
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
|
@ -12,6 +12,15 @@ export function MachineLearningJobWizardCommonProvider({ getService }: FtrProvid
|
|||
const retry = getService('retry');
|
||||
const testSubjects = getService('testSubjects');
|
||||
|
||||
interface SectionOptions {
|
||||
withAdvancedSection: boolean;
|
||||
}
|
||||
|
||||
function advancedSectionSelector(subSelector?: string) {
|
||||
const subj = 'mlJobWizardAdvancedSection';
|
||||
return !subSelector ? subj : `${subj} > ${subSelector}`;
|
||||
}
|
||||
|
||||
return {
|
||||
async clickNextButton() {
|
||||
await testSubjects.existOrFail('mlJobWizardNavButtonNext');
|
||||
|
@ -38,6 +47,10 @@ export function MachineLearningJobWizardCommonProvider({ getService }: FtrProvid
|
|||
await testSubjects.existOrFail('mlJobWizardStepTitleSummary');
|
||||
},
|
||||
|
||||
async assertConfigureDatafeedSectionExists() {
|
||||
await testSubjects.existOrFail('mlJobWizardStepTitleConfigureDatafeed');
|
||||
},
|
||||
|
||||
async advanceToPickFieldsSection() {
|
||||
await this.clickNextButton();
|
||||
await this.assertPickFieldsSectionExists();
|
||||
|
@ -153,75 +166,127 @@ export function MachineLearningJobWizardCommonProvider({ getService }: FtrProvid
|
|||
expect(await this.getSelectedJobGroups()).to.contain(jobGroup);
|
||||
},
|
||||
|
||||
async assertModelPlotSwitchExists() {
|
||||
await this.ensureAdvancedSectionOpen();
|
||||
await testSubjects.existOrFail('mlJobWizardAdvancedSection > mlJobWizardSwitchModelPlot', {
|
||||
allowHidden: true,
|
||||
async assertModelPlotSwitchExists(
|
||||
sectionOptions: SectionOptions = { withAdvancedSection: true }
|
||||
) {
|
||||
let subj = 'mlJobWizardSwitchModelPlot';
|
||||
if (sectionOptions.withAdvancedSection === true) {
|
||||
await this.ensureAdvancedSectionOpen();
|
||||
subj = advancedSectionSelector(subj);
|
||||
}
|
||||
await testSubjects.existOrFail(subj, { allowHidden: true });
|
||||
},
|
||||
|
||||
async getModelPlotSwitchCheckedState(
|
||||
sectionOptions: SectionOptions = { withAdvancedSection: true }
|
||||
): Promise<boolean> {
|
||||
let subj = 'mlJobWizardSwitchModelPlot';
|
||||
if (sectionOptions.withAdvancedSection === true) {
|
||||
await this.ensureAdvancedSectionOpen();
|
||||
subj = advancedSectionSelector(subj);
|
||||
}
|
||||
return await testSubjects.isSelected(subj);
|
||||
},
|
||||
|
||||
async assertModelPlotSwitchCheckedState(
|
||||
expectedValue: boolean,
|
||||
sectionOptions: SectionOptions = { withAdvancedSection: true }
|
||||
) {
|
||||
const actualCheckedState = await this.getModelPlotSwitchCheckedState({
|
||||
withAdvancedSection: sectionOptions.withAdvancedSection,
|
||||
});
|
||||
expect(actualCheckedState).to.eql(expectedValue);
|
||||
},
|
||||
|
||||
async assertDedicatedIndexSwitchExists(
|
||||
sectionOptions: SectionOptions = { withAdvancedSection: true }
|
||||
) {
|
||||
let subj = 'mlJobWizardSwitchUseDedicatedIndex';
|
||||
if (sectionOptions.withAdvancedSection === true) {
|
||||
await this.ensureAdvancedSectionOpen();
|
||||
subj = advancedSectionSelector(subj);
|
||||
}
|
||||
await testSubjects.existOrFail(subj, { allowHidden: true });
|
||||
},
|
||||
|
||||
async getDedicatedIndexSwitchCheckedState(
|
||||
sectionOptions: SectionOptions = { withAdvancedSection: true }
|
||||
): Promise<boolean> {
|
||||
let subj = 'mlJobWizardSwitchUseDedicatedIndex';
|
||||
if (sectionOptions.withAdvancedSection === true) {
|
||||
await this.ensureAdvancedSectionOpen();
|
||||
subj = advancedSectionSelector(subj);
|
||||
}
|
||||
return await testSubjects.isSelected(subj);
|
||||
},
|
||||
|
||||
async assertDedicatedIndexSwitchCheckedState(
|
||||
expectedValue: boolean,
|
||||
sectionOptions: SectionOptions = { withAdvancedSection: true }
|
||||
) {
|
||||
const actualCheckedState = await this.getDedicatedIndexSwitchCheckedState({
|
||||
withAdvancedSection: sectionOptions.withAdvancedSection,
|
||||
});
|
||||
expect(actualCheckedState).to.eql(expectedValue);
|
||||
},
|
||||
|
||||
async activateDedicatedIndexSwitch(
|
||||
sectionOptions: SectionOptions = { withAdvancedSection: true }
|
||||
) {
|
||||
let subj = 'mlJobWizardSwitchUseDedicatedIndex';
|
||||
if (sectionOptions.withAdvancedSection === true) {
|
||||
await this.ensureAdvancedSectionOpen();
|
||||
subj = advancedSectionSelector(subj);
|
||||
}
|
||||
if (
|
||||
(await this.getDedicatedIndexSwitchCheckedState({
|
||||
withAdvancedSection: sectionOptions.withAdvancedSection,
|
||||
})) === false
|
||||
) {
|
||||
await testSubjects.clickWhenNotDisabled(subj);
|
||||
}
|
||||
await this.assertDedicatedIndexSwitchCheckedState(true, {
|
||||
withAdvancedSection: sectionOptions.withAdvancedSection,
|
||||
});
|
||||
},
|
||||
|
||||
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(
|
||||
'mlJobWizardAdvancedSection > mlJobWizardSwitchUseDedicatedIndex',
|
||||
{ allowHidden: true }
|
||||
);
|
||||
},
|
||||
|
||||
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 activateDedicatedIndexSwitch() {
|
||||
if ((await this.getDedicatedIndexSwitchCheckedState()) === false) {
|
||||
await testSubjects.clickWhenNotDisabled('mlJobWizardSwitchUseDedicatedIndex');
|
||||
async assertModelMemoryLimitInputExists(
|
||||
sectionOptions: SectionOptions = { withAdvancedSection: true }
|
||||
) {
|
||||
let subj = 'mlJobWizardInputModelMemoryLimit';
|
||||
if (sectionOptions.withAdvancedSection === true) {
|
||||
await this.ensureAdvancedSectionOpen();
|
||||
subj = advancedSectionSelector(subj);
|
||||
}
|
||||
await this.assertDedicatedIndexSwitchCheckedState(true);
|
||||
await testSubjects.existOrFail(subj);
|
||||
},
|
||||
|
||||
async assertModelMemoryLimitInputExists() {
|
||||
await this.ensureAdvancedSectionOpen();
|
||||
await testSubjects.existOrFail(
|
||||
'mlJobWizardAdvancedSection > mlJobWizardInputModelMemoryLimit'
|
||||
);
|
||||
},
|
||||
|
||||
async assertModelMemoryLimitValue(expectedValue: string) {
|
||||
await this.ensureAdvancedSectionOpen();
|
||||
const actualModelMemoryLimit = await testSubjects.getAttribute(
|
||||
'mlJobWizardAdvancedSection > mlJobWizardInputModelMemoryLimit',
|
||||
'value'
|
||||
);
|
||||
async assertModelMemoryLimitValue(
|
||||
expectedValue: string,
|
||||
sectionOptions: SectionOptions = { withAdvancedSection: true }
|
||||
) {
|
||||
let subj = 'mlJobWizardInputModelMemoryLimit';
|
||||
if (sectionOptions.withAdvancedSection === true) {
|
||||
await this.ensureAdvancedSectionOpen();
|
||||
subj = advancedSectionSelector(subj);
|
||||
}
|
||||
const actualModelMemoryLimit = await testSubjects.getAttribute(subj, 'value');
|
||||
expect(actualModelMemoryLimit).to.eql(expectedValue);
|
||||
},
|
||||
|
||||
async setModelMemoryLimit(modelMemoryLimit: string) {
|
||||
await testSubjects.setValue('mlJobWizardInputModelMemoryLimit', modelMemoryLimit, {
|
||||
clearWithKeyboard: true,
|
||||
async setModelMemoryLimit(
|
||||
modelMemoryLimit: string,
|
||||
sectionOptions: SectionOptions = { withAdvancedSection: true }
|
||||
) {
|
||||
let subj = 'mlJobWizardInputModelMemoryLimit';
|
||||
if (sectionOptions.withAdvancedSection === true) {
|
||||
await this.ensureAdvancedSectionOpen();
|
||||
subj = advancedSectionSelector(subj);
|
||||
}
|
||||
await testSubjects.setValue(subj, modelMemoryLimit, { clearWithKeyboard: true });
|
||||
await this.assertModelMemoryLimitValue(modelMemoryLimit, {
|
||||
withAdvancedSection: sectionOptions.withAdvancedSection,
|
||||
});
|
||||
await this.assertModelMemoryLimitValue(modelMemoryLimit);
|
||||
},
|
||||
|
||||
async assertInfluencerInputExists() {
|
||||
|
@ -237,7 +302,7 @@ export function MachineLearningJobWizardCommonProvider({ getService }: FtrProvid
|
|||
},
|
||||
|
||||
async addInfluencer(influencer: string) {
|
||||
await comboBox.setCustom('mlInfluencerSelect > comboBoxInput', influencer);
|
||||
await comboBox.set('mlInfluencerSelect > comboBoxInput', influencer);
|
||||
expect(await this.getSelectedInfluencers()).to.contain(influencer);
|
||||
},
|
||||
|
||||
|
@ -293,16 +358,16 @@ export function MachineLearningJobWizardCommonProvider({ getService }: FtrProvid
|
|||
|
||||
async ensureAdvancedSectionOpen() {
|
||||
await retry.tryForTime(5000, async () => {
|
||||
if ((await testSubjects.exists('mlJobWizardAdvancedSection')) === false) {
|
||||
if ((await testSubjects.exists(advancedSectionSelector())) === false) {
|
||||
await testSubjects.click('mlJobWizardToggleAdvancedSection');
|
||||
await testSubjects.existOrFail('mlJobWizardAdvancedSection', { timeout: 1000 });
|
||||
await testSubjects.existOrFail(advancedSectionSelector(), { timeout: 1000 });
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
async createJobAndWaitForCompletion() {
|
||||
await testSubjects.clickWhenNotDisabled('mlJobWizardButtonCreateJob');
|
||||
await testSubjects.existOrFail('mlJobWizardButtonRunInRealTime', { timeout: 5 * 60 * 1000 });
|
||||
await testSubjects.existOrFail('mlJobWizardButtonRunInRealTime', { timeout: 2 * 60 * 1000 });
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -16,17 +16,16 @@ export function MachineLearningJobWizardMultiMetricProvider({ getService }: FtrP
|
|||
await testSubjects.existOrFail('mlMultiMetricSplitFieldSelect > comboBoxInput');
|
||||
},
|
||||
|
||||
async assertSplitFieldSelection(identifier: string) {
|
||||
async assertSplitFieldSelection(expectedIdentifier: string[]) {
|
||||
const comboBoxSelectedOptions = await comboBox.getComboBoxSelectedOptions(
|
||||
'mlMultiMetricSplitFieldSelect > comboBoxInput'
|
||||
);
|
||||
expect(comboBoxSelectedOptions.length).to.eql(1);
|
||||
expect(comboBoxSelectedOptions[0]).to.eql(identifier);
|
||||
expect(comboBoxSelectedOptions).to.eql(expectedIdentifier);
|
||||
},
|
||||
|
||||
async selectSplitField(identifier: string) {
|
||||
await comboBox.set('mlMultiMetricSplitFieldSelect > comboBoxInput', identifier);
|
||||
await this.assertSplitFieldSelection(identifier);
|
||||
await this.assertSplitFieldSelection([identifier]);
|
||||
},
|
||||
|
||||
async assertDetectorSplitExists(splitField: string) {
|
||||
|
|
|
@ -16,17 +16,16 @@ export function MachineLearningJobWizardPopulationProvider({ getService }: FtrPr
|
|||
await testSubjects.existOrFail('mlPopulationSplitFieldSelect > comboBoxInput');
|
||||
},
|
||||
|
||||
async assertPopulationFieldSelection(identifier: string) {
|
||||
async assertPopulationFieldSelection(expectedIdentifier: string[]) {
|
||||
const comboBoxSelectedOptions = await comboBox.getComboBoxSelectedOptions(
|
||||
'mlPopulationSplitFieldSelect > comboBoxInput'
|
||||
);
|
||||
expect(comboBoxSelectedOptions.length).to.eql(1);
|
||||
expect(comboBoxSelectedOptions[0]).to.eql(identifier);
|
||||
expect(comboBoxSelectedOptions).to.eql(expectedIdentifier);
|
||||
},
|
||||
|
||||
async selectPopulationField(identifier: string) {
|
||||
await comboBox.set('mlPopulationSplitFieldSelect > comboBoxInput', identifier);
|
||||
await this.assertPopulationFieldSelection(identifier);
|
||||
await this.assertPopulationFieldSelection([identifier]);
|
||||
},
|
||||
|
||||
async assertDetectorSplitFieldInputExists(detectorPosition: number) {
|
||||
|
@ -35,12 +34,14 @@ export function MachineLearningJobWizardPopulationProvider({ getService }: FtrPr
|
|||
);
|
||||
},
|
||||
|
||||
async assertDetectorSplitFieldSelection(detectorPosition: number, identifier: string) {
|
||||
async assertDetectorSplitFieldSelection(
|
||||
detectorPosition: number,
|
||||
expectedIdentifier: string[]
|
||||
) {
|
||||
const comboBoxSelectedOptions = await comboBox.getComboBoxSelectedOptions(
|
||||
`mlDetector ${detectorPosition} > mlByFieldSelect > comboBoxInput`
|
||||
);
|
||||
expect(comboBoxSelectedOptions.length).to.eql(1);
|
||||
expect(comboBoxSelectedOptions[0]).to.eql(identifier);
|
||||
expect(comboBoxSelectedOptions).to.eql(expectedIdentifier);
|
||||
},
|
||||
|
||||
async selectDetectorSplitField(detectorPosition: number, identifier: string) {
|
||||
|
@ -48,7 +49,7 @@ export function MachineLearningJobWizardPopulationProvider({ getService }: FtrPr
|
|||
`mlDetector ${detectorPosition} > mlByFieldSelect > comboBoxInput`,
|
||||
identifier
|
||||
);
|
||||
await this.assertDetectorSplitFieldSelection(detectorPosition, identifier);
|
||||
await this.assertDetectorSplitFieldSelection(detectorPosition, [identifier]);
|
||||
},
|
||||
|
||||
async assertDetectorSplitExists(detectorPosition: number) {
|
||||
|
|
|
@ -15,6 +15,7 @@ import {
|
|||
MachineLearningJobSourceSelectionProvider,
|
||||
MachineLearningJobTableProvider,
|
||||
MachineLearningJobTypeSelectionProvider,
|
||||
MachineLearningJobWizardAdvancedProvider,
|
||||
MachineLearningJobWizardCommonProvider,
|
||||
MachineLearningJobWizardMultiMetricProvider,
|
||||
MachineLearningJobWizardPopulationProvider,
|
||||
|
@ -28,10 +29,11 @@ export function MachineLearningProvider(context: FtrProviderContext) {
|
|||
const api = MachineLearningAPIProvider(context);
|
||||
const dataFrameAnalytics = MachineLearningDataFrameAnalyticsProvider(context);
|
||||
const dataVisualizer = MachineLearningDataVisualizerProvider(context);
|
||||
const jobManagement = MachineLearningJobManagementProvider(context);
|
||||
const jobManagement = MachineLearningJobManagementProvider(context, api);
|
||||
const jobSourceSelection = MachineLearningJobSourceSelectionProvider(context);
|
||||
const jobTable = MachineLearningJobTableProvider(context);
|
||||
const jobTypeSelection = MachineLearningJobTypeSelectionProvider(context);
|
||||
const jobWizardAdvanced = MachineLearningJobWizardAdvancedProvider(context);
|
||||
const jobWizardCommon = MachineLearningJobWizardCommonProvider(context);
|
||||
const jobWizardMultiMetric = MachineLearningJobWizardMultiMetricProvider(context);
|
||||
const jobWizardPopulation = MachineLearningJobWizardPopulationProvider(context);
|
||||
|
@ -48,6 +50,7 @@ export function MachineLearningProvider(context: FtrProviderContext) {
|
|||
jobSourceSelection,
|
||||
jobTable,
|
||||
jobTypeSelection,
|
||||
jobWizardAdvanced,
|
||||
jobWizardCommon,
|
||||
jobWizardMultiMetric,
|
||||
jobWizardPopulation,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue