[ML] Adds a11y tests for categorization and recognizer job wizards (#162126)

## Summary

Adds accessibility tests for the categorization and data recognizer
anomaly detection job wizards.

As part of this PR I have split out the tests for the anomaly detection
pages into a separate file as the original `ml.ts` file was getting very
large.

Part of https://github.com/elastic/kibana/issues/160712
Part of #88496

### Checklist

- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
This commit is contained in:
Pete Harverson 2023-07-19 14:03:40 +01:00 committed by GitHub
parent 8fd0577a72
commit 46403f1c1b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 710 additions and 496 deletions

View file

@ -154,6 +154,7 @@ export const JobSettingsForm: FC<JobSettingsFormProps> = ({
value={jobPrefix}
onChange={({ target: { value } }) => setJobPrefix(value)}
isInvalid={!!validationResult.jobPrefix}
data-test-subj="mlJobRecognizerWizardInputJobIdPrefix"
/>
</EuiFormRow>
</EuiDescribedFormGroup>
@ -210,6 +211,7 @@ export const JobSettingsForm: FC<JobSettingsFormProps> = ({
/>
}
paddingSize="l"
data-test-subj="mlJobWizardToggleAdvancedSection"
>
<EuiDescribedFormGroup
title={
@ -227,7 +229,10 @@ export const JobSettingsForm: FC<JobSettingsFormProps> = ({
/>
}
>
<EuiFormRow describedByIds={['ml_aria_label_new_job_dedicated_index']}>
<EuiFormRow
describedByIds={['ml_aria_label_new_job_dedicated_index']}
data-test-subj="mlJobWizardAdvancedSection"
>
<EuiSwitch
id="useDedicatedIndex"
name="useDedicatedIndex"

View file

@ -319,7 +319,7 @@ export const Page: FC<PageProps> = ({ moduleId, existingGroupIds }) => {
{jobsAwaitingNodeCount > 0 && <JobsAwaitingNodeWarning jobCount={jobsAwaitingNodeCount} />}
<EuiFlexGroup wrap={true} gutterSize="m">
<EuiFlexGroup wrap={true} gutterSize="m" data-test-subj="mlPageJobWizard recognizer">
<EuiFlexItem grow={1}>
<EuiPanel grow={false} hasShadow={false} hasBorder>
<EuiTitle size="s">

View file

@ -7,22 +7,11 @@
import { FtrProviderContext } from '../ftr_provider_context';
interface Detector {
identifier: string;
function: string;
field?: string;
byField?: string;
overField?: string;
partitionField?: string;
excludeFrequent?: string;
description?: string;
}
export default function ({ getService }: FtrProviderContext) {
const a11y = getService('a11y');
const ml = getService('ml');
describe('ml Accessibility', () => {
describe('ml Accessibility', function () {
const esArchiver = getService('esArchiver');
before(async () => {
@ -53,44 +42,20 @@ export default function ({ getService }: FtrProviderContext) {
await a11y.testAppSnapshot();
});
it('anomaly detection page', async () => {
await ml.navigation.navigateToAnomalyDetection();
await a11y.testAppSnapshot();
});
it('data frame analytics page', async () => {
await ml.navigation.navigateToDataFrameAnalytics();
await a11y.testAppSnapshot();
});
it('settings page', async () => {
await ml.navigation.navigateToSettings();
await a11y.testAppSnapshot();
});
});
describe('with data loaded', function () {
const adJobId = 'fq_single_a11y';
const dfaOutlierResultsJobId = 'iph_outlier_a11y';
const calendarId = 'calendar_a11y';
const eventDescription = 'calendar_event_a11y';
const filterId = 'filter_a11y';
const filterItems = ['filter_item_a11y'];
const fqIndexPattern = 'ft_farequote';
const ecIndexPattern = 'ft_module_sample_ecommerce';
const ihpIndexPattern = 'ft_ihp_outlier';
const egsIndexPattern = 'ft_egs_regression';
const bmIndexPattern = 'ft_bank_marketing';
const ecExpectedTotalCount = '287';
const adJobAggAndFieldIdentifier = 'Mean(responsetime)';
const adJobBucketSpan = '30m';
const adSingleMetricJobId = `fq_single_a11y_${Date.now()}`;
const adMultiSplitField = 'airline';
const adMultiMetricJobId = `fq_multi_a11y_${Date.now()}`;
const adMultiMetricJobDescription =
'Multi metric job based on the farequote dataset with 30m bucketspan and mean(responsetime) split by airline';
const dfaOutlierJobType = 'outlier_detection';
const dfaOutlierJobId = `ihp_outlier_ally_${Date.now()}`;
const dfaRegressionJobType = 'regression';
@ -106,169 +71,32 @@ export default function ({ getService }: FtrProviderContext) {
'../../functional/apps/ml/data_visualizer/files_to_import/artificial_server_log'
);
const advancedJobTestData = {
suiteTitle: 'with multiple metric detectors and custom datafeed settings',
jobSource: ecIndexPattern,
jobId: `ec_advanced_1_${Date.now()}`,
get jobIdClone(): string {
return `${this.jobId}_clone`;
},
jobDescription: `Create advanced job from ${ecIndexPattern} 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',
},
datafeedConfig: {
queryDelay: '55s',
frequency: '350s',
scrollSize: '999',
},
};
const populationJobTestData = {
suiteTitle: 'population job',
jobSource: ecIndexPattern,
jobId: `ec_population_1_${Date.now()}`,
get jobIdClone(): string {
return `${this.jobId}_clone`;
},
jobDescription:
'Create population job based on the ecommerce sample dataset with 2h bucketspan over customer_id' +
' - detectors: (Mean(products.base_price) by customer_gender), (Mean(products.quantity) by category.leyword)',
jobGroups: ['automated', 'ecommerce', 'population'],
get jobGroupsClone(): string[] {
return [...this.jobGroups, 'clone'];
},
populationField: 'customer_id',
pickFieldsConfig: {
detectors: [
{
identifier: 'Mean(products.base_price)',
splitField: 'customer_gender',
frontCardTitle: 'FEMALE',
numberOfBackCards: 1,
},
{
identifier: 'Mean(products.quantity)',
splitField: 'category.keyword',
frontCardTitle: "Men's Clothing",
numberOfBackCards: 5,
},
],
influencers: [
'customer_id',
'category.keyword',
'geoip.continent_name',
'customer_gender',
],
bucketSpan: '2h',
memoryLimit: '8mb',
},
datafeedConfig: {
queryDelay: '55s',
frequency: '350s',
scrollSize: '999',
},
};
before(async () => {
await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote');
await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/ihp_outlier');
await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/egs_regression');
await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/bm_classification');
await esArchiver.loadIfNeeded(
'x-pack/test/functional/es_archives/ml/module_sample_ecommerce'
);
await ml.testResources.createIndexPatternIfNeeded(fqIndexPattern, '@timestamp');
await ml.testResources.createIndexPatternIfNeeded(ihpIndexPattern);
await ml.testResources.createIndexPatternIfNeeded(egsIndexPattern);
await ml.testResources.createIndexPatternIfNeeded(bmIndexPattern);
await ml.testResources.createIndexPatternIfNeeded(ecIndexPattern, 'order_date');
await ml.testResources.setKibanaTimeZoneToUTC();
await ml.api.createAndRunAnomalyDetectionLookbackJob(
ml.commonConfig.getADFqMultiMetricJobConfig(adJobId),
ml.commonConfig.getADFqDatafeedConfig(adJobId)
);
await ml.api.createAndRunDFAJob(
ml.commonConfig.getDFAIhpOutlierDetectionJobConfig(dfaOutlierResultsJobId)
);
await ml.api.createCalendar(calendarId, {
calendar_id: calendarId,
job_ids: [],
description: 'Test calendar',
});
await ml.api.createCalendarEvents(calendarId, [
{
description: eventDescription,
start_time: '1513641600000',
end_time: '1513728000000',
},
]);
await ml.api.createFilter(filterId, {
description: 'Test filter list',
items: filterItems,
});
});
after(async () => {
await ml.api.cleanMlIndices();
await ml.api.deleteIndices(`user-${dfaOutlierResultsJobId}`);
await ml.api.deleteCalendar(calendarId);
await ml.api.deleteFilter(filterId);
await ml.testResources.deleteIndexPatternByTitle(fqIndexPattern);
await ml.testResources.deleteIndexPatternByTitle(ihpIndexPattern);
await ml.testResources.deleteIndexPatternByTitle(egsIndexPattern);
await ml.testResources.deleteIndexPatternByTitle(bmIndexPattern);
await ml.testResources.deleteIndexPatternByTitle(ecIndexPattern);
await esArchiver.unload('x-pack/test/functional/es_archives/ml/farequote');
await esArchiver.unload('x-pack/test/functional/es_archives/ml/ihp_outlier');
await esArchiver.unload('x-pack/test/functional/es_archives/ml/egs_regression');
await esArchiver.unload('x-pack/test/functional/es_archives/ml/bm_classification');
@ -282,299 +110,6 @@ export default function ({ getService }: FtrProviderContext) {
await a11y.testAppSnapshot();
});
it('anomaly detection jobs list page', async () => {
await ml.navigation.navigateToAnomalyDetection();
await a11y.testAppSnapshot();
});
it('anomaly detection create job select index pattern page', async () => {
await ml.jobManagement.navigateToNewJobSourceSelection();
await a11y.testAppSnapshot();
});
it('anomaly detection create job select type page', async () => {
await ml.jobSourceSelection.selectSourceForAnomalyDetectionJob(fqIndexPattern);
await a11y.testAppSnapshot();
});
it('anomaly detection create single metric job time range step', async () => {
await ml.jobTypeSelection.selectSingleMetricJob();
await ml.testExecution.logTestStep('job creation set the time range');
await ml.jobWizardCommon.clickUseFullDataButton(
'Feb 7, 2016 @ 00:00:00.000',
'Feb 11, 2016 @ 23:59:54.000'
);
await a11y.testAppSnapshot();
});
it('anomaly detection create single metric job pick fields step', async () => {
await ml.jobWizardCommon.advanceToPickFieldsSection();
await ml.testExecution.logTestStep('job creation selects field and aggregation');
await ml.jobWizardCommon.selectAggAndField(adJobAggAndFieldIdentifier, true);
await ml.testExecution.logTestStep('job creation inputs the bucket span');
await ml.jobWizardCommon.setBucketSpan(adJobBucketSpan);
await a11y.testAppSnapshot();
});
it('anomaly detection create single metric job details step', async () => {
await ml.jobWizardCommon.advanceToJobDetailsSection();
await ml.testExecution.logTestStep('job creation inputs the job id');
await ml.jobWizardCommon.setJobId(adSingleMetricJobId);
await ml.testExecution.logTestStep('job creation opens the additional settings section');
await ml.jobWizardCommon.ensureAdditionalSettingsSectionOpen();
await ml.testExecution.logTestStep('job creation opens the advanced section');
await ml.jobWizardCommon.ensureAdvancedSectionOpen();
await a11y.testAppSnapshot();
});
it('anomaly detection create single metric job validation step', async () => {
await ml.jobWizardCommon.advanceToValidationSection();
await a11y.testAppSnapshot();
});
it('anomaly detection create single metric job summary step', async () => {
await ml.jobWizardCommon.advanceToSummarySection();
await a11y.testAppSnapshot();
});
it('anomaly detection create multi metric job and move to time range step', async () => {
// Proceed all the way to the step for selecting the time range
// as the other steps have already been tested for the single metric job
await ml.navigation.navigateToAnomalyDetection();
await ml.jobManagement.navigateToNewJobSourceSelection();
await ml.jobSourceSelection.selectSourceForAnomalyDetectionJob(fqIndexPattern);
await ml.jobTypeSelection.selectMultiMetricJob();
await ml.testExecution.logTestStep('job creation set the time range');
await ml.jobWizardCommon.clickUseFullDataButton(
'Feb 7, 2016 @ 00:00:00.000',
'Feb 11, 2016 @ 23:59:54.000'
);
await a11y.testAppSnapshot();
});
it('anomaly detection create multi metric job pick fields step', async () => {
await ml.jobWizardCommon.advanceToPickFieldsSection();
await ml.testExecution.logTestStep('job creation selects field and aggregation');
await ml.jobWizardCommon.selectAggAndField(adJobAggAndFieldIdentifier, false);
await ml.testExecution.logTestStep('job creation selects split field');
await ml.jobWizardMultiMetric.selectSplitField(adMultiSplitField);
await ml.testExecution.logTestStep('job creation inputs the bucket span');
await ml.jobWizardCommon.setBucketSpan(adJobBucketSpan);
await a11y.testAppSnapshot();
});
it('anomaly detection create multi metric job details step', async () => {
await ml.jobWizardCommon.advanceToJobDetailsSection();
await ml.testExecution.logTestStep('job creation inputs the job id');
await ml.jobWizardCommon.setJobId(adMultiMetricJobId);
await ml.testExecution.logTestStep('job creation inputs the job description');
await ml.jobWizardCommon.setJobDescription(adMultiMetricJobDescription);
await ml.testExecution.logTestStep('job creation opens the additional settings section');
await ml.jobWizardCommon.ensureAdditionalSettingsSectionOpen();
await ml.testExecution.logTestStep('job creation opens the advanced section');
await ml.jobWizardCommon.ensureAdvancedSectionOpen();
await a11y.testAppSnapshot();
});
it('anomaly detection create multi metric job validation step', async () => {
await ml.jobWizardCommon.advanceToValidationSection();
await a11y.testAppSnapshot();
});
it('anomaly detection create multi metric job summary step', async () => {
await ml.jobWizardCommon.advanceToSummarySection();
await a11y.testAppSnapshot();
});
it('anomaly detection create advanced job open wizard', async () => {
await ml.navigation.navigateToMl();
await ml.navigation.navigateToJobManagement();
await ml.jobManagement.navigateToNewJobSourceSelection();
await ml.jobSourceSelection.selectSourceForAnomalyDetectionJob(
advancedJobTestData.jobSource
);
await ml.jobTypeSelection.selectAdvancedJob();
await a11y.testAppSnapshot();
});
it('anomaly detection create advanced job pick fields step', async () => {
await ml.jobWizardCommon.advanceToPickFieldsSection();
await a11y.testAppSnapshot();
for (const detector of advancedJobTestData.pickFieldsConfig.detectors) {
await ml.jobWizardAdvanced.openCreateDetectorModal();
await a11y.testAppSnapshot();
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('job creation inputs the bucket span');
await ml.jobWizardCommon.setBucketSpan(advancedJobTestData.pickFieldsConfig.bucketSpan);
await ml.testExecution.logTestStep('job creation inputs influencers');
for (const influencer of advancedJobTestData.pickFieldsConfig.influencers) {
await ml.jobWizardCommon.addInfluencer(influencer);
}
await ml.testExecution.logTestStep('job creation inputs the model memory limit');
await ml.jobWizardCommon.setModelMemoryLimit(
advancedJobTestData.pickFieldsConfig.memoryLimit,
{
withAdvancedSection: false,
}
);
await a11y.testAppSnapshot();
});
it('anomaly detection create advanced job job details step', async () => {
await ml.jobWizardCommon.advanceToJobDetailsSection();
await ml.jobWizardCommon.setJobId(advancedJobTestData.jobId);
await ml.jobWizardCommon.setJobDescription(advancedJobTestData.jobDescription);
for (const jobGroup of advancedJobTestData.jobGroups) {
await ml.jobWizardCommon.addJobGroup(jobGroup);
}
await ml.jobWizardCommon.ensureAdditionalSettingsSectionOpen();
await ml.jobWizardCommon.addCustomUrl({ label: 'check-kibana-dashboard' });
await ml.jobWizardCommon.addCalendar(calendarId);
await a11y.testAppSnapshot();
});
it('anomaly detection create advanced job validation step', async () => {
await ml.jobWizardCommon.advanceToValidationSection();
await a11y.testAppSnapshot();
});
it('anomaly detection create advanced job job summary step', async () => {
await ml.jobWizardCommon.advanceToSummarySection();
await a11y.testAppSnapshot();
});
it('anomaly detection create population job open wizard', async () => {
await ml.navigation.navigateToJobManagement();
await ml.jobManagement.navigateToNewJobSourceSelection();
await ml.jobSourceSelection.selectSourceForAnomalyDetectionJob(ecIndexPattern);
await ml.testExecution.logTestStep('job creation loads the population job wizard page');
await ml.jobTypeSelection.selectPopulationJob();
await a11y.testAppSnapshot();
});
it('anomaly detection create population job pick fields step', async () => {
await ml.jobWizardCommon.advanceToPickFieldsSection();
await ml.jobWizardPopulation.selectPopulationField(populationJobTestData.populationField);
for (const [
index,
detector,
] of populationJobTestData.pickFieldsConfig.detectors.entries()) {
await ml.jobWizardCommon.selectAggAndField(detector.identifier, false);
await ml.jobWizardCommon.assertDetectorPreviewExists(
detector.identifier,
index,
'SCATTER'
);
}
for (const [
index,
detector,
] of populationJobTestData.pickFieldsConfig.detectors.entries()) {
await ml.jobWizardPopulation.assertDetectorSplitFieldInputExists(index);
await ml.jobWizardPopulation.selectDetectorSplitField(index, detector.splitField);
}
await ml.jobWizardCommon.setBucketSpan(populationJobTestData.pickFieldsConfig.bucketSpan);
await a11y.testAppSnapshot();
});
it('anomaly detection create population job details step', async () => {
await ml.jobWizardCommon.advanceToJobDetailsSection();
await ml.jobWizardCommon.setJobId(populationJobTestData.jobId);
await ml.jobWizardCommon.setJobDescription(populationJobTestData.jobDescription);
for (const jobGroup of populationJobTestData.jobGroups) {
await ml.jobWizardCommon.addJobGroup(jobGroup);
}
await ml.jobWizardCommon.ensureAdditionalSettingsSectionOpen();
await ml.jobWizardCommon.addCustomUrl({ label: 'check-kibana-dashboard' });
await ml.jobWizardCommon.addCalendar(calendarId);
await ml.testExecution.logTestStep('job creation inputs the model memory limit');
await ml.jobWizardCommon.setModelMemoryLimit(
populationJobTestData.pickFieldsConfig.memoryLimit
);
await a11y.testAppSnapshot();
});
it('anomaly detection create population job validation step', async () => {
await ml.jobWizardCommon.advanceToValidationSection();
await a11y.testAppSnapshot();
});
it('anomaly detection create population job summary step', async () => {
await ml.jobWizardCommon.advanceToSummarySection();
await a11y.testAppSnapshot();
});
it('anomaly detection Single Metric Viewer page', async () => {
await ml.navigation.navigateToMl();
await ml.navigation.navigateToAnomalyDetection();
await ml.jobTable.clickOpenJobInSingleMetricViewerButton(adJobId);
await ml.commonUI.waitForMlLoadingIndicatorToDisappear();
await ml.testExecution.logTestStep('should input the airline entity value');
await ml.singleMetricViewer.assertEntityInputExist('airline');
await ml.singleMetricViewer.assertEntityInputSelection('airline', []);
await ml.singleMetricViewer.selectEntityValue('airline', 'AAL');
await a11y.testAppSnapshot();
});
it('anomaly detection forecasting from Single Metric Viewer page', async () => {
await ml.testExecution.logTestStep('opens the forecasting modal showing no forecasts');
await ml.forecast.openForecastModal();
await a11y.testAppSnapshot();
await ml.testExecution.logTestStep('run the forecast and close the modal');
await ml.forecast.clickForecastModalRunButton();
await ml.testExecution.logTestStep('opens the forecasting modal showing a forecast');
await ml.forecast.openForecastModal();
await a11y.testAppSnapshot();
await ml.testExecution.logTestStep('closes the forecasting modal');
await ml.forecast.closeForecastModal();
});
it('anomaly detection Anomaly Explorer page', async () => {
await ml.singleMetricViewer.openAnomalyExplorer();
await ml.commonUI.waitForMlLoadingIndicatorToDisappear();
await a11y.testAppSnapshot();
});
it('data frame analytics page', async () => {
await ml.navigation.navigateToDataFrameAnalytics();
await a11y.testAppSnapshot();
@ -734,33 +269,6 @@ export default function ({ getService }: FtrProviderContext) {
await a11y.testAppSnapshot();
});
it('settings page', async () => {
await ml.navigation.navigateToMl();
await ml.navigation.navigateToSettings();
await a11y.testAppSnapshot();
});
it('calendar management page', async () => {
await ml.settings.navigateToCalendarManagement();
await a11y.testAppSnapshot();
});
it('edit calendar page', async () => {
await ml.settingsCalendar.openCalendarEditForm(calendarId);
await a11y.testAppSnapshot();
});
it('filter list management page', async () => {
await ml.navigation.navigateToSettings();
await ml.settings.navigateToFilterListsManagement();
await a11y.testAppSnapshot();
});
it('edit filter list page', async () => {
await ml.settingsFilterList.openFilterListEditForm(filterId);
await a11y.testAppSnapshot();
});
it('data visualizer selector page', async () => {
await ml.navigation.navigateToDataVisualizer();
await a11y.testAppSnapshot();

View file

@ -0,0 +1,650 @@
/*
* 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 { FtrProviderContext } from '../ftr_provider_context';
interface Detector {
identifier: string;
function: string;
field?: string;
byField?: string;
overField?: string;
partitionField?: string;
excludeFrequent?: string;
description?: string;
}
export default function ({ getService }: FtrProviderContext) {
const a11y = getService('a11y');
const ml = getService('ml');
describe('machine learning anomaly detection Accessibility', function () {
const esArchiver = getService('esArchiver');
before(async () => {
await ml.securityCommon.createMlRoles();
await ml.securityCommon.createMlUsers();
});
after(async () => {
await ml.securityCommon.cleanMlUsers();
await ml.securityCommon.cleanMlRoles();
});
describe('for user with full ML access', function () {
before(async () => {
await ml.securityUI.loginAsMlPowerUser();
await ml.api.cleanMlIndices();
});
after(async () => {
// NOTE: Logout needs to happen before anything else to avoid flaky behavior
await ml.securityUI.logout();
});
describe('with no data loaded', function () {
it('anomaly detection page', async () => {
await ml.navigation.navigateToMl();
await ml.navigation.navigateToAnomalyDetection();
await a11y.testAppSnapshot();
});
it('anomaly detection settings page', async () => {
await ml.navigation.navigateToSettings();
await a11y.testAppSnapshot();
});
});
describe('with data loaded', function () {
const adJobId = 'fq_single_a11y';
const calendarId = 'calendar_a11y';
const eventDescription = 'calendar_event_a11y';
const filterId = 'filter_a11y';
const filterItems = ['filter_item_a11y'];
const fqIndexPattern = 'ft_farequote';
const ecIndexPattern = 'ft_module_sample_ecommerce';
const categorizationIndexPattern = 'ft_categorization_small';
const adJobAggAndFieldIdentifier = 'Mean(responsetime)';
const adJobBucketSpan = '30m';
const adSingleMetricJobId = `fq_single_a11y_${Date.now()}`;
const adMultiSplitField = 'airline';
const adMultiMetricJobId = `fq_multi_a11y_${Date.now()}`;
const adMultiMetricJobDescription =
'Multi metric job based on the farequote dataset with 30m bucketspan and mean(responsetime) split by airline';
const adCategorizationDetectorType = 'Rare';
const adCategorizationFieldIdentifier = 'field1';
const adCategorizationJobId = `categorization_a11y_${Date.now()}`;
const adCategorizationJobDescription =
'categorization job based on the ft_categorization dataset looking for rare field1 values';
const adRecognizerJobModuleId = 'sample_data_ecommerce';
const adRecognizerJobIdPrefix = 'ally_';
const advancedJobTestData = {
suiteTitle: 'with multiple metric detectors and custom datafeed settings',
jobSource: ecIndexPattern,
jobId: `ec_advanced_1_${Date.now()}`,
get jobIdClone(): string {
return `${this.jobId}_clone`;
},
jobDescription: `Create advanced job from ${ecIndexPattern} 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',
},
datafeedConfig: {
queryDelay: '55s',
frequency: '350s',
scrollSize: '999',
},
};
const populationJobTestData = {
suiteTitle: 'population job',
jobSource: ecIndexPattern,
jobId: `ec_population_1_${Date.now()}`,
get jobIdClone(): string {
return `${this.jobId}_clone`;
},
jobDescription:
'Create population job based on the ecommerce sample dataset with 2h bucketspan over customer_id' +
' - detectors: (Mean(products.base_price) by customer_gender), (Mean(products.quantity) by category.leyword)',
jobGroups: ['automated', 'ecommerce', 'population'],
get jobGroupsClone(): string[] {
return [...this.jobGroups, 'clone'];
},
populationField: 'customer_id',
pickFieldsConfig: {
detectors: [
{
identifier: 'Mean(products.base_price)',
splitField: 'customer_gender',
frontCardTitle: 'FEMALE',
numberOfBackCards: 1,
},
{
identifier: 'Mean(products.quantity)',
splitField: 'category.keyword',
frontCardTitle: "Men's Clothing",
numberOfBackCards: 5,
},
],
influencers: [
'customer_id',
'category.keyword',
'geoip.continent_name',
'customer_gender',
],
bucketSpan: '2h',
memoryLimit: '8mb',
},
datafeedConfig: {
queryDelay: '55s',
frequency: '350s',
scrollSize: '999',
},
};
before(async () => {
await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote');
await esArchiver.loadIfNeeded(
'x-pack/test/functional/es_archives/ml/module_sample_ecommerce'
);
await esArchiver.loadIfNeeded(
'x-pack/test/functional/es_archives/ml/categorization_small'
);
await ml.testResources.createIndexPatternIfNeeded(fqIndexPattern, '@timestamp');
await ml.testResources.createIndexPatternIfNeeded(ecIndexPattern, 'order_date');
await ml.testResources.createIndexPatternIfNeeded(
'ft_categorization_small',
'@timestamp'
);
await ml.testResources.setKibanaTimeZoneToUTC();
await ml.api.createAndRunAnomalyDetectionLookbackJob(
ml.commonConfig.getADFqMultiMetricJobConfig(adJobId),
ml.commonConfig.getADFqDatafeedConfig(adJobId)
);
await ml.api.createCalendar(calendarId, {
calendar_id: calendarId,
job_ids: [],
description: 'Test calendar',
});
await ml.api.createCalendarEvents(calendarId, [
{
description: eventDescription,
start_time: '1513641600000',
end_time: '1513728000000',
},
]);
await ml.api.createFilter(filterId, {
description: 'Test filter list',
items: filterItems,
});
});
after(async () => {
await ml.api.cleanMlIndices();
await ml.api.deleteCalendar(calendarId);
await ml.api.deleteFilter(filterId);
await ml.testResources.deleteIndexPatternByTitle(fqIndexPattern);
await ml.testResources.deleteIndexPatternByTitle(ecIndexPattern);
await ml.testResources.deleteIndexPatternByTitle(categorizationIndexPattern);
await esArchiver.unload('x-pack/test/functional/es_archives/ml/farequote');
await esArchiver.unload('x-pack/test/functional/es_archives/ml/module_sample_ecommerce');
await esArchiver.unload('x-pack/test/functional/es_archives/ml/categorization_small');
await ml.testResources.resetKibanaTimeZone();
});
it('anomaly detection jobs list page', async () => {
await ml.navigation.navigateToMl();
await ml.navigation.navigateToAnomalyDetection();
await a11y.testAppSnapshot();
});
it('anomaly detection create job select index pattern page', async () => {
await ml.jobManagement.navigateToNewJobSourceSelection();
await a11y.testAppSnapshot();
});
it('anomaly detection create job select type page', async () => {
await ml.jobSourceSelection.selectSourceForAnomalyDetectionJob(fqIndexPattern);
await a11y.testAppSnapshot();
});
it('anomaly detection create single metric job time range step', async () => {
await ml.jobTypeSelection.selectSingleMetricJob();
await ml.testExecution.logTestStep('job creation set the time range');
await ml.jobWizardCommon.clickUseFullDataButton(
'Feb 7, 2016 @ 00:00:00.000',
'Feb 11, 2016 @ 23:59:54.000'
);
await a11y.testAppSnapshot();
});
it('anomaly detection create single metric job pick fields step', async () => {
await ml.jobWizardCommon.advanceToPickFieldsSection();
await ml.testExecution.logTestStep('job creation selects field and aggregation');
await ml.jobWizardCommon.selectAggAndField(adJobAggAndFieldIdentifier, true);
await ml.testExecution.logTestStep('job creation inputs the bucket span');
await ml.jobWizardCommon.setBucketSpan(adJobBucketSpan);
await a11y.testAppSnapshot();
});
it('anomaly detection create single metric job details step', async () => {
await ml.jobWizardCommon.advanceToJobDetailsSection();
await ml.testExecution.logTestStep('job creation inputs the job id');
await ml.jobWizardCommon.setJobId(adSingleMetricJobId);
await ml.testExecution.logTestStep('job creation opens the additional settings section');
await ml.jobWizardCommon.ensureAdditionalSettingsSectionOpen();
await ml.testExecution.logTestStep('job creation opens the advanced section');
await ml.jobWizardCommon.ensureAdvancedSectionOpen();
await a11y.testAppSnapshot();
});
it('anomaly detection create single metric job validation step', async () => {
await ml.jobWizardCommon.advanceToValidationSection();
await a11y.testAppSnapshot();
});
it('anomaly detection create single metric job summary step', async () => {
await ml.jobWizardCommon.advanceToSummarySection();
await a11y.testAppSnapshot();
});
it('anomaly detection create multi metric job and move to time range step', async () => {
// Proceed all the way to the step for selecting the time range
// as the other steps have already been tested for the single metric job
await ml.navigation.navigateToAnomalyDetection();
await ml.jobManagement.navigateToNewJobSourceSelection();
await ml.jobSourceSelection.selectSourceForAnomalyDetectionJob(fqIndexPattern);
await ml.jobTypeSelection.selectMultiMetricJob();
await ml.testExecution.logTestStep('job creation set the time range');
await ml.jobWizardCommon.clickUseFullDataButton(
'Feb 7, 2016 @ 00:00:00.000',
'Feb 11, 2016 @ 23:59:54.000'
);
await a11y.testAppSnapshot();
});
it('anomaly detection create multi metric job pick fields step', async () => {
await ml.jobWizardCommon.advanceToPickFieldsSection();
await ml.testExecution.logTestStep('job creation selects field and aggregation');
await ml.jobWizardCommon.selectAggAndField(adJobAggAndFieldIdentifier, false);
await ml.testExecution.logTestStep('job creation selects split field');
await ml.jobWizardMultiMetric.selectSplitField(adMultiSplitField);
await ml.testExecution.logTestStep('job creation inputs the bucket span');
await ml.jobWizardCommon.setBucketSpan(adJobBucketSpan);
await a11y.testAppSnapshot();
});
it('anomaly detection create multi metric job details step', async () => {
await ml.jobWizardCommon.advanceToJobDetailsSection();
await ml.testExecution.logTestStep('job creation inputs the job id');
await ml.jobWizardCommon.setJobId(adMultiMetricJobId);
await ml.testExecution.logTestStep('job creation inputs the job description');
await ml.jobWizardCommon.setJobDescription(adMultiMetricJobDescription);
await ml.testExecution.logTestStep('job creation opens the additional settings section');
await ml.jobWizardCommon.ensureAdditionalSettingsSectionOpen();
await ml.testExecution.logTestStep('job creation opens the advanced section');
await ml.jobWizardCommon.ensureAdvancedSectionOpen();
await a11y.testAppSnapshot();
});
it('anomaly detection create multi metric job validation step', async () => {
await ml.jobWizardCommon.advanceToValidationSection();
await a11y.testAppSnapshot();
});
it('anomaly detection create multi metric job summary step', async () => {
await ml.jobWizardCommon.advanceToSummarySection();
await a11y.testAppSnapshot();
});
it('anomaly detection create advanced job open wizard', async () => {
await ml.navigation.navigateToMl();
await ml.navigation.navigateToJobManagement();
await ml.jobManagement.navigateToNewJobSourceSelection();
await ml.jobSourceSelection.selectSourceForAnomalyDetectionJob(
advancedJobTestData.jobSource
);
await ml.jobTypeSelection.selectAdvancedJob();
await a11y.testAppSnapshot();
});
it('anomaly detection create advanced job pick fields step', async () => {
await ml.jobWizardCommon.advanceToPickFieldsSection();
await a11y.testAppSnapshot();
for (const detector of advancedJobTestData.pickFieldsConfig.detectors) {
await ml.jobWizardAdvanced.openCreateDetectorModal();
await a11y.testAppSnapshot();
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('job creation inputs the bucket span');
await ml.jobWizardCommon.setBucketSpan(advancedJobTestData.pickFieldsConfig.bucketSpan);
await ml.testExecution.logTestStep('job creation inputs influencers');
for (const influencer of advancedJobTestData.pickFieldsConfig.influencers) {
await ml.jobWizardCommon.addInfluencer(influencer);
}
await ml.testExecution.logTestStep('job creation inputs the model memory limit');
await ml.jobWizardCommon.setModelMemoryLimit(
advancedJobTestData.pickFieldsConfig.memoryLimit,
{
withAdvancedSection: false,
}
);
await a11y.testAppSnapshot();
});
it('anomaly detection create advanced job job details step', async () => {
await ml.jobWizardCommon.advanceToJobDetailsSection();
await ml.jobWizardCommon.setJobId(advancedJobTestData.jobId);
await ml.jobWizardCommon.setJobDescription(advancedJobTestData.jobDescription);
for (const jobGroup of advancedJobTestData.jobGroups) {
await ml.jobWizardCommon.addJobGroup(jobGroup);
}
await ml.jobWizardCommon.ensureAdditionalSettingsSectionOpen();
await ml.jobWizardCommon.addCustomUrl({ label: 'check-kibana-dashboard' });
await ml.jobWizardCommon.addCalendar(calendarId);
await a11y.testAppSnapshot();
});
it('anomaly detection create advanced job validation step', async () => {
await ml.jobWizardCommon.advanceToValidationSection();
await a11y.testAppSnapshot();
});
it('anomaly detection create advanced job job summary step', async () => {
await ml.jobWizardCommon.advanceToSummarySection();
await a11y.testAppSnapshot();
});
it('anomaly detection create population job open wizard', async () => {
await ml.navigation.navigateToJobManagement();
await ml.jobManagement.navigateToNewJobSourceSelection();
await ml.jobSourceSelection.selectSourceForAnomalyDetectionJob(ecIndexPattern);
await ml.testExecution.logTestStep('job creation loads the population job wizard page');
await ml.jobTypeSelection.selectPopulationJob();
await a11y.testAppSnapshot();
});
it('anomaly detection create population job pick fields step', async () => {
await ml.jobWizardCommon.advanceToPickFieldsSection();
await ml.jobWizardPopulation.selectPopulationField(populationJobTestData.populationField);
for (const [
index,
detector,
] of populationJobTestData.pickFieldsConfig.detectors.entries()) {
await ml.jobWizardCommon.selectAggAndField(detector.identifier, false);
await ml.jobWizardCommon.assertDetectorPreviewExists(
detector.identifier,
index,
'SCATTER'
);
}
for (const [
index,
detector,
] of populationJobTestData.pickFieldsConfig.detectors.entries()) {
await ml.jobWizardPopulation.assertDetectorSplitFieldInputExists(index);
await ml.jobWizardPopulation.selectDetectorSplitField(index, detector.splitField);
}
await ml.jobWizardCommon.setBucketSpan(populationJobTestData.pickFieldsConfig.bucketSpan);
await a11y.testAppSnapshot();
});
it('anomaly detection create population job details step', async () => {
await ml.jobWizardCommon.advanceToJobDetailsSection();
await ml.jobWizardCommon.setJobId(populationJobTestData.jobId);
await ml.jobWizardCommon.setJobDescription(populationJobTestData.jobDescription);
for (const jobGroup of populationJobTestData.jobGroups) {
await ml.jobWizardCommon.addJobGroup(jobGroup);
}
await ml.jobWizardCommon.ensureAdditionalSettingsSectionOpen();
await ml.jobWizardCommon.addCustomUrl({ label: 'check-kibana-dashboard' });
await ml.jobWizardCommon.addCalendar(calendarId);
await ml.testExecution.logTestStep('job creation inputs the model memory limit');
await ml.jobWizardCommon.setModelMemoryLimit(
populationJobTestData.pickFieldsConfig.memoryLimit
);
await a11y.testAppSnapshot();
});
it('anomaly detection create population job validation step', async () => {
await ml.jobWizardCommon.advanceToValidationSection();
await a11y.testAppSnapshot();
});
it('anomaly detection create population job summary step', async () => {
await ml.jobWizardCommon.advanceToSummarySection();
await a11y.testAppSnapshot();
});
it('anomaly detection create categorization job and move to time range step', async () => {
await ml.navigation.navigateToJobManagement();
await ml.jobManagement.navigateToNewJobSourceSelection();
await ml.jobSourceSelection.selectSourceForAnomalyDetectionJob(
categorizationIndexPattern
);
await ml.testExecution.logTestStep(
'job creation loads the categorization job wizard page'
);
await ml.jobTypeSelection.selectCategorizationJob();
await ml.testExecution.logTestStep('job creation set the time range');
await ml.jobWizardCommon.clickUseFullDataButton(
'Apr 5, 2019 @ 11:25:35.770',
'Nov 21, 2019 @ 00:01:13.923'
);
await a11y.testAppSnapshot();
});
it('anomaly detection create categorization job pick fields step', async () => {
await ml.jobWizardCommon.advanceToPickFieldsSection();
await ml.testExecution.logTestStep(
`job creation selects ${adCategorizationDetectorType} detector type`
);
await ml.jobWizardCategorization.selectCategorizationDetectorType(
adCategorizationDetectorType
);
await ml.testExecution.logTestStep(`job creation selects the categorization field`);
await ml.jobWizardCategorization.selectCategorizationField(
adCategorizationFieldIdentifier
);
await a11y.testAppSnapshot();
});
it('anomaly detection create categorization job details step', async () => {
await ml.jobWizardCommon.advanceToJobDetailsSection();
await ml.testExecution.logTestStep('job creation inputs the job id');
await ml.jobWizardCommon.setJobId(adCategorizationJobId);
await ml.testExecution.logTestStep('job creation inputs the job description');
await ml.jobWizardCommon.setJobDescription(adCategorizationJobDescription);
await ml.testExecution.logTestStep('job creation opens the additional settings section');
await ml.jobWizardCommon.ensureAdditionalSettingsSectionOpen();
await ml.testExecution.logTestStep('job creation opens the advanced section');
await ml.jobWizardCommon.ensureAdvancedSectionOpen();
await a11y.testAppSnapshot();
});
it('anomaly detection create categorization job validation step', async () => {
await ml.jobWizardCommon.advanceToValidationSection();
await a11y.testAppSnapshot();
});
it('anomaly detection create categorization job summary step', async () => {
await ml.jobWizardCommon.advanceToSummarySection();
await a11y.testAppSnapshot();
});
it('anomaly detection create job from data recognizer module open wizard', async () => {
await ml.navigation.navigateToJobManagement();
await ml.jobManagement.navigateToNewJobSourceSelection();
await ml.jobSourceSelection.selectSourceForAnomalyDetectionJob(ecIndexPattern);
await ml.testExecution.logTestStep(
`job creation loads the data recognizer job wizard page for the ${adRecognizerJobModuleId} module`
);
await ml.jobTypeSelection.selectRecognizerJob(adRecognizerJobModuleId);
await a11y.testAppSnapshot();
});
it('anomaly detection create data recognizer job details step', async () => {
await ml.testExecution.logTestStep('job creation inputs the job id prefix');
await ml.jobWizardRecognizer.setJobIdPrefix(adRecognizerJobIdPrefix);
await ml.testExecution.logTestStep('job creation opens the advanced section');
await ml.jobWizardCommon.ensureAdvancedSectionOpen();
await a11y.testAppSnapshot();
});
it('anomaly detection Single Metric Viewer page', async () => {
await ml.navigation.navigateToMl();
await ml.navigation.navigateToAnomalyDetection();
await ml.jobTable.clickOpenJobInSingleMetricViewerButton(adJobId);
await ml.commonUI.waitForMlLoadingIndicatorToDisappear();
await ml.testExecution.logTestStep('should input the airline entity value');
await ml.singleMetricViewer.assertEntityInputExist('airline');
await ml.singleMetricViewer.assertEntityInputSelection('airline', []);
await ml.singleMetricViewer.selectEntityValue('airline', 'AAL');
await a11y.testAppSnapshot();
});
it('anomaly detection forecasting from Single Metric Viewer page', async () => {
await ml.testExecution.logTestStep('opens the forecasting modal showing no forecasts');
await ml.forecast.openForecastModal();
await a11y.testAppSnapshot();
await ml.testExecution.logTestStep('run the forecast and close the modal');
await ml.forecast.clickForecastModalRunButton();
await ml.testExecution.logTestStep('opens the forecasting modal showing a forecast');
await ml.forecast.openForecastModal();
await a11y.testAppSnapshot();
await ml.testExecution.logTestStep('closes the forecasting modal');
await ml.forecast.closeForecastModal();
});
it('anomaly detection Anomaly Explorer page', async () => {
await ml.singleMetricViewer.openAnomalyExplorer();
await ml.commonUI.waitForMlLoadingIndicatorToDisappear();
await a11y.testAppSnapshot();
});
it('anomaly detection settings page', async () => {
await ml.navigation.navigateToMl();
await ml.navigation.navigateToSettings();
await a11y.testAppSnapshot();
});
it('anomaly detection calendar management page', async () => {
await ml.settings.navigateToCalendarManagement();
await a11y.testAppSnapshot();
});
it('anomaly detection edit calendar page', async () => {
await ml.settingsCalendar.openCalendarEditForm(calendarId);
await a11y.testAppSnapshot();
});
it('anomaly detection filter list management page', async () => {
await ml.navigation.navigateToSettings();
await ml.settings.navigateToFilterListsManagement();
await a11y.testAppSnapshot();
});
it('anomaly detection edit filter list page', async () => {
await ml.settingsFilterList.openFilterListEditForm(filterId);
await a11y.testAppSnapshot();
});
});
});
});
}

View file

@ -34,6 +34,7 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) {
require.resolve('./apps/ingest_node_pipelines'),
require.resolve('./apps/index_lifecycle_management'),
require.resolve('./apps/ml'),
require.resolve('./apps/ml_anomaly_detection'),
require.resolve('./apps/transform'),
require.resolve('./apps/lens'),
require.resolve('./apps/upgrade_assistant'),

View file

@ -77,7 +77,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await ml.commonUI.waitForMlLoadingIndicatorToDisappear();
await ml.testExecution.logTestStep('Exploror page loaded');
await ml.testExecution.logTestStep('Explorer page loaded');
await ml.lensVisualizations.anomalyExplorerPageLoaded();
await ml.testExecution.logTestStep('pre-fills the job selection');

View file

@ -36,6 +36,7 @@ import { MachineLearningJobWizardCommonProvider } from './job_wizard_common';
import { MachineLearningJobWizardCategorizationProvider } from './job_wizard_categorization';
import { MachineLearningJobWizardMultiMetricProvider } from './job_wizard_multi_metric';
import { MachineLearningJobWizardPopulationProvider } from './job_wizard_population';
import { MachineLearningJobWizardRecognizerProvider } from './job_wizard_recognizer';
import { MachineLearningJobWizardGeoProvider } from './job_wizard_geo';
import { MachineLearningLensVisualizationsProvider } from './lens_visualizations';
import { MachineLearningNavigationProvider } from './navigation';
@ -123,6 +124,7 @@ export function MachineLearningProvider(context: FtrProviderContext) {
context,
commonFieldStatsFlyout
);
const jobWizardRecognizer = MachineLearningJobWizardRecognizerProvider(context, commonUI);
const jobWizardCommon = MachineLearningJobWizardCommonProvider(
context,
commonUI,
@ -200,6 +202,7 @@ export function MachineLearningProvider(context: FtrProviderContext) {
jobWizardGeo,
jobWizardMultiMetric,
jobWizardPopulation,
jobWizardRecognizer,
lensVisualizations,
mlNodesPanel,
navigation,

View file

@ -64,5 +64,14 @@ export function MachineLearningJobTypeSelectionProvider({ getService }: FtrProvi
async assertCategorizationJobWizardOpen() {
await testSubjects.existOrFail('mlPageJobWizard categorization');
},
async selectRecognizerJob(moduleId: string) {
await testSubjects.clickWhenNotDisabledWithoutRetry(`mlRecognizerCard ${moduleId}`);
await this.assertRecognizerJobWizardOpen();
},
async assertRecognizerJobWizardOpen() {
await testSubjects.existOrFail('mlPageJobWizard recognizer');
},
};
}

View file

@ -0,0 +1,38 @@
/*
* 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 expect from '@kbn/expect';
import type { FtrProviderContext } from '../../ftr_provider_context';
import type { MlCommonUI } from './common_ui';
export function MachineLearningJobWizardRecognizerProvider(
{ getService }: FtrProviderContext,
mlCommonUI: MlCommonUI
) {
const testSubjects = getService('testSubjects');
return {
async assertJobIdValue(expectedValue: string) {
const actualJobPrefixId = await testSubjects.getAttribute(
'mlJobRecognizerWizardInputJobIdPrefix',
'value'
);
expect(actualJobPrefixId).to.eql(
expectedValue,
`Expected job id prefix value to be '${expectedValue}' (got '${actualJobPrefixId}')`
);
},
async setJobIdPrefix(prefix: string) {
await mlCommonUI.setValueWithChecks('mlJobRecognizerWizardInputJobIdPrefix', prefix, {
clearWithKeyboard: true,
});
await this.assertJobIdValue(prefix);
},
};
}