mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
This PR adds functional UI tests to create a machine learning job using the single metric wizard.
This commit is contained in:
parent
d6b89fe76f
commit
c76b0c18e1
39 changed files with 1923 additions and 131 deletions
|
@ -3,6 +3,7 @@
|
|||
exports[`FullTimeRangeSelector renders the selector 1`] = `
|
||||
<EuiButton
|
||||
color="primary"
|
||||
data-test-subj="mlButtonUseFullData"
|
||||
fill={true}
|
||||
iconSide="left"
|
||||
isDisabled={false}
|
||||
|
|
|
@ -30,7 +30,12 @@ export const FullTimeRangeSelector: FC<Props> = ({ indexPattern, query, disabled
|
|||
}
|
||||
}
|
||||
return (
|
||||
<EuiButton fill isDisabled={disabled} onClick={() => setRange(indexPattern, query)}>
|
||||
<EuiButton
|
||||
fill
|
||||
isDisabled={disabled}
|
||||
onClick={() => setRange(indexPattern, query)}
|
||||
data-test-subj="mlButtonUseFullData"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.fullTimeRangeSelector.useFullDataButtonLabel"
|
||||
defaultMessage="Use full {indexPatternTitle} data"
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<ml-new-job class="index-or-saved-search-selection">
|
||||
<ml-message-bar></ml-message-bar>
|
||||
<div class='kuiViewContent kuiViewContent--constrainedWidth kuiViewContentItem' >
|
||||
<div ng-controller="MlNewJobStepIndexOrSearch" class="visWizard">
|
||||
<div ng-controller="MlNewJobStepIndexOrSearch" class="visWizard" data-test-subj="mlPageSourceSelection">
|
||||
<div class="visWizard__column visWizard__column--small">
|
||||
<h3
|
||||
class="kuiTitle kuiVerticalRhythm"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<ml-nav-menu name="new_job" />
|
||||
<ml-new-job class="job-type-gallery">
|
||||
<ml-message-bar></ml-message-bar>
|
||||
<div ng-controller="MlNewJobStepJobType">
|
||||
<div ng-controller="MlNewJobStepJobType" data-test-subj="mlPageJobTypeSelection">
|
||||
<!-- Presents the various options for creating a job -->
|
||||
<div class="job-types-content">
|
||||
<h1
|
||||
|
@ -214,7 +214,7 @@
|
|||
</a>
|
||||
</div>
|
||||
<div class="euiFlexItem" ng-class='{disabled: isTimeBasedIndex===false}'>
|
||||
<a ng-href="{{getUrl('#jobs/new_job/new_new_job/single_metric')}}" ng-class='{disabled: isTimeBasedIndex===false}' class="euiLink synopsis">
|
||||
<a ng-href="{{getUrl('#jobs/new_job/new_new_job/single_metric')}}" ng-class='{disabled: isTimeBasedIndex===false}' class="euiLink synopsis" data-test-subj="mlJobTypeLinkSingleMetricJob">
|
||||
<div class="euiPanel euiPanel--paddingMedium synopsisPanel">
|
||||
<div class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--responsive">
|
||||
<div class="euiFlexItem euiFlexItem--flexGrowZero">
|
||||
|
@ -246,7 +246,7 @@
|
|||
</a>
|
||||
</div>
|
||||
<div class="euiFlexItem" ng-class='{disabled: isTimeBasedIndex===false}'>
|
||||
<a ng-href="{{getUrl('#jobs/new_job/new_new_job/multi_metric')}}" ng-class='{disabled: isTimeBasedIndex===false}' class="euiLink synopsis">
|
||||
<a ng-href="{{getUrl('#jobs/new_job/new_new_job/multi_metric')}}" ng-class='{disabled: isTimeBasedIndex===false}' class="euiLink synopsis" data-test-subj="mlJobTypeLinkMultiMetricJob">
|
||||
<div class="euiPanel euiPanel--paddingMedium synopsisPanel">
|
||||
<div class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--responsive">
|
||||
<div class="euiFlexItem euiFlexItem--flexGrowZero">
|
||||
|
@ -278,7 +278,7 @@
|
|||
</a>
|
||||
</div>
|
||||
<div class="euiFlexItem" ng-class='{disabled: isTimeBasedIndex===false}'>
|
||||
<a ng-href="{{getUrl('#jobs/new_job/new_new_job/population')}}" ng-class='{disabled: isTimeBasedIndex===false}' class="euiLink synopsis">
|
||||
<a ng-href="{{getUrl('#jobs/new_job/new_new_job/population')}}" ng-class='{disabled: isTimeBasedIndex===false}' class="euiLink synopsis" data-test-subj="mlJobTypeLinkPopulationJob">
|
||||
<div class="euiPanel euiPanel--paddingMedium synopsisPanel">
|
||||
<div class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--responsive">
|
||||
<div class="euiFlexItem euiFlexItem--flexGrowZero">
|
||||
|
|
|
@ -44,7 +44,7 @@ export const AnomalyChart: FC<Props> = ({
|
|||
|
||||
const xDomain = getXRange(data);
|
||||
return (
|
||||
<div style={{ width, height }}>
|
||||
<div style={{ width, height }} data-test-subj="mlAnomalyChart">
|
||||
<Chart>
|
||||
<Settings xDomain={xDomain} tooltip={TooltipType.None} />
|
||||
<Axes chartData={data} />
|
||||
|
|
|
@ -22,7 +22,7 @@ const SPEC_ID = 'event_rate';
|
|||
|
||||
export const EventRateChart: FC<Props> = ({ eventRateChartData, height, width, showAxis }) => {
|
||||
return (
|
||||
<div style={{ width, height }}>
|
||||
<div style={{ width, height }} data-test-subj="mlEventRateChart">
|
||||
<Chart>
|
||||
{showAxis === true && <Axes />}
|
||||
|
||||
|
|
|
@ -24,9 +24,14 @@ export const AdvancedSection: FC<Props> = ({ advancedExpanded, setAdvancedExpand
|
|||
buttonContent={ButtonContent}
|
||||
onToggle={setAdvancedExpanded}
|
||||
initialIsOpen={advancedExpanded}
|
||||
data-test-subj="mlJobWizardToggleAdvancedSection"
|
||||
>
|
||||
<EuiSpacer />
|
||||
<EuiFlexGroup gutterSize="xl" style={{ marginLeft: '0px', marginRight: '0px' }}>
|
||||
<EuiFlexGroup
|
||||
gutterSize="xl"
|
||||
style={{ marginLeft: '0px', marginRight: '0px' }}
|
||||
data-test-subj="mlJobWizardAdvancedSection"
|
||||
>
|
||||
<EuiFlexItem>
|
||||
<ModelPlotSwitch />
|
||||
<ModelMemoryLimitInput />
|
||||
|
|
|
@ -24,7 +24,12 @@ export const DedicatedIndexSwitch: FC = () => {
|
|||
|
||||
return (
|
||||
<Description>
|
||||
<EuiSwitch name="switch" checked={useDedicatedIndex} onChange={toggleModelPlot} />
|
||||
<EuiSwitch
|
||||
name="switch"
|
||||
checked={useDedicatedIndex}
|
||||
onChange={toggleModelPlot}
|
||||
data-test-subj="mlJobWizardSwitchUseDedicatedIndex"
|
||||
/>
|
||||
</Description>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -33,6 +33,7 @@ export const ModelMemoryLimitInput: FC = () => {
|
|||
value={modelMemoryLimit}
|
||||
onChange={e => setModelMemoryLimit(e.target.value)}
|
||||
isInvalid={validation.valid === false}
|
||||
data-test-subj="mlJobWizardInputModelMemoryLimit"
|
||||
/>
|
||||
</Description>
|
||||
);
|
||||
|
|
|
@ -24,7 +24,12 @@ export const ModelPlotSwitch: FC = () => {
|
|||
|
||||
return (
|
||||
<Description>
|
||||
<EuiSwitch name="switch" checked={modelPlotEnabled} onChange={toggleModelPlot} />
|
||||
<EuiSwitch
|
||||
name="switch"
|
||||
checked={modelPlotEnabled}
|
||||
onChange={toggleModelPlot}
|
||||
data-test-subj="mlJobWizardSwitchModelPlot"
|
||||
/>
|
||||
</Description>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -77,6 +77,7 @@ export const GroupsInput: FC = () => {
|
|||
onCreateOption={onCreateGroup}
|
||||
isClearable={true}
|
||||
isInvalid={validation.valid === false}
|
||||
data-test-subj="mlJobWizardComboBoxJobGroups"
|
||||
/>
|
||||
</Description>
|
||||
);
|
||||
|
|
|
@ -24,6 +24,7 @@ export const JobDescriptionInput: FC = () => {
|
|||
placeholder="Job description"
|
||||
value={jobDescription}
|
||||
onChange={e => setJobDescription(e.target.value)}
|
||||
data-test-subj="mlJobWizardInputJobDescription"
|
||||
/>
|
||||
</Description>
|
||||
);
|
||||
|
|
|
@ -36,6 +36,7 @@ export const JobIdInput: FC = () => {
|
|||
value={jobId}
|
||||
onChange={e => setJobId(e.target.value)}
|
||||
isInvalid={validation.valid === false}
|
||||
data-test-subj="mlJobWizardInputJobId"
|
||||
/>
|
||||
</Description>
|
||||
);
|
||||
|
|
|
@ -64,7 +64,11 @@ export const AggSelect: FC<Props> = ({ fields, changeHandler, selectedOptions, r
|
|||
}, [jobValidatorUpdated]);
|
||||
|
||||
return (
|
||||
<EuiFormRow error={validation.message} isInvalid={validation.valid === false}>
|
||||
<EuiFormRow
|
||||
error={validation.message}
|
||||
isInvalid={validation.valid === false}
|
||||
data-test-subj="mlJobWizardAggSelection"
|
||||
>
|
||||
<EuiComboBox
|
||||
singleSelection={{ asPlainText: true }}
|
||||
options={options}
|
||||
|
|
|
@ -20,6 +20,7 @@ export const BucketSpanInput: FC<Props> = ({ bucketSpan, setBucketSpan, isInvali
|
|||
value={bucketSpan}
|
||||
onChange={e => setBucketSpan(e.target.value)}
|
||||
isInvalid={isInvalid}
|
||||
data-test-subj="mlJobWizardInputBucketSpan"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -77,11 +77,21 @@ export const SummaryStep: FC<StepProps> = ({ setCurrentStep, isCurrentStep }) =>
|
|||
<EuiHorizontalRule />
|
||||
{progress < 100 && (
|
||||
<Fragment>
|
||||
<EuiButton onClick={start} isDisabled={progress > 0} disabled={isValid === false}>
|
||||
<EuiButton
|
||||
onClick={start}
|
||||
isDisabled={progress > 0}
|
||||
disabled={isValid === false}
|
||||
data-test-subj="mlJobWizardButtonCreateJob"
|
||||
>
|
||||
Create job
|
||||
</EuiButton>
|
||||
 
|
||||
<EuiButtonEmpty size="s" onClick={toggleJsonFlyout} isDisabled={progress > 0}>
|
||||
<EuiButtonEmpty
|
||||
size="s"
|
||||
onClick={toggleJsonFlyout}
|
||||
isDisabled={progress > 0}
|
||||
data-test-subj="mlJobWizardButtonPreviewJobJson"
|
||||
>
|
||||
Preview job JSON
|
||||
</EuiButtonEmpty>
|
||||
{showJsonFlyout && (
|
||||
|
@ -92,7 +102,9 @@ export const SummaryStep: FC<StepProps> = ({ setCurrentStep, isCurrentStep }) =>
|
|||
)}
|
||||
{progress > 0 && (
|
||||
<Fragment>
|
||||
<EuiButton onClick={viewResults}>View results</EuiButton>
|
||||
<EuiButton onClick={viewResults} data-test-subj="mlJobWizardButtonViewResults">
|
||||
View results
|
||||
</EuiButton>
|
||||
</Fragment>
|
||||
)}
|
||||
</Fragment>
|
||||
|
|
|
@ -27,7 +27,13 @@ export const WizardNav: FC<StepsNavProps> = ({
|
|||
<EuiFlexItem />
|
||||
{previous && (
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButton disabled={!previousActive} onClick={previous} iconType="arrowLeft" size="s">
|
||||
<EuiButton
|
||||
disabled={!previousActive}
|
||||
onClick={previous}
|
||||
iconType="arrowLeft"
|
||||
size="s"
|
||||
data-test-subj="mlJobWizardNavButtonPrevious"
|
||||
>
|
||||
{i18n.translate('xpack.ml.newJob.wizard.previousStepButton', {
|
||||
defaultMessage: 'Previous',
|
||||
})}
|
||||
|
@ -36,7 +42,13 @@ export const WizardNav: FC<StepsNavProps> = ({
|
|||
)}
|
||||
{next && (
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButton disabled={!nextActive} onClick={next} iconType="arrowRight" size="s">
|
||||
<EuiButton
|
||||
disabled={!nextActive}
|
||||
onClick={next}
|
||||
iconType="arrowRight"
|
||||
size="s"
|
||||
data-test-subj="mlJobWizardNavButtonNext"
|
||||
>
|
||||
{i18n.translate('xpack.ml.newJob.wizard.nextStepButton', {
|
||||
defaultMessage: 'Next',
|
||||
})}
|
||||
|
|
|
@ -95,7 +95,7 @@ export const Page: FC<PageProps> = ({ existingJobsAndGroups, jobType }) => {
|
|||
|
||||
return (
|
||||
<Fragment>
|
||||
<EuiPage style={{ backgroundColor: '#FFF' }}>
|
||||
<EuiPage style={{ backgroundColor: '#FFF' }} data-test-subj="mlPageJobWizard">
|
||||
<EuiPageBody>
|
||||
<EuiPageContentBody>
|
||||
<Wizard
|
||||
|
|
|
@ -176,7 +176,7 @@ export const Wizard: FC<Props> = ({
|
|||
|
||||
{currentStep === WIZARD_STEPS.TIME_RANGE && (
|
||||
<Fragment>
|
||||
<Title>Time range</Title>
|
||||
<Title data-test-subj="mlJobWizardStepTitleTimeRange">Time range</Title>
|
||||
<TimeRangeStep
|
||||
isCurrentStep={currentStep === WIZARD_STEPS.TIME_RANGE}
|
||||
setCurrentStep={setCurrentStep}
|
||||
|
@ -185,7 +185,7 @@ export const Wizard: FC<Props> = ({
|
|||
)}
|
||||
{currentStep === WIZARD_STEPS.PICK_FIELDS && (
|
||||
<Fragment>
|
||||
<Title>Pick fields</Title>
|
||||
<Title data-test-subj="mlJobWizardStepTitlePickFields">Pick fields</Title>
|
||||
<PickFieldsStep
|
||||
isCurrentStep={currentStep === WIZARD_STEPS.PICK_FIELDS}
|
||||
setCurrentStep={setCurrentStep}
|
||||
|
@ -194,7 +194,7 @@ export const Wizard: FC<Props> = ({
|
|||
)}
|
||||
{currentStep === WIZARD_STEPS.JOB_DETAILS && (
|
||||
<Fragment>
|
||||
<Title>Job details</Title>
|
||||
<Title data-test-subj="mlJobWizardStepTitleJobDetails">Job details</Title>
|
||||
<JobDetailsStep
|
||||
isCurrentStep={currentStep === WIZARD_STEPS.JOB_DETAILS}
|
||||
setCurrentStep={setCurrentStep}
|
||||
|
@ -207,7 +207,7 @@ export const Wizard: FC<Props> = ({
|
|||
)}
|
||||
{currentStep === WIZARD_STEPS.VALIDATION && (
|
||||
<Fragment>
|
||||
<Title>Validation</Title>
|
||||
<Title data-test-subj="mlJobWizardStepTitleValidation">Validation</Title>
|
||||
<ValidationStep
|
||||
isCurrentStep={currentStep === WIZARD_STEPS.VALIDATION}
|
||||
setCurrentStep={setCurrentStep}
|
||||
|
@ -216,7 +216,7 @@ export const Wizard: FC<Props> = ({
|
|||
)}
|
||||
{currentStep === WIZARD_STEPS.SUMMARY && (
|
||||
<Fragment>
|
||||
<Title>Summary</Title>
|
||||
<Title data-test-subj="mlJobWizardStepTitleSummary">Summary</Title>
|
||||
<SummaryStep
|
||||
isCurrentStep={currentStep === WIZARD_STEPS.SUMMARY}
|
||||
setCurrentStep={setCurrentStep}
|
||||
|
@ -227,11 +227,11 @@ export const Wizard: FC<Props> = ({
|
|||
);
|
||||
};
|
||||
|
||||
const Title: FC = ({ children }) => {
|
||||
const Title: FC<{ 'data-test-subj': string }> = ({ 'data-test-subj': dataTestSubj, children }) => {
|
||||
return (
|
||||
<Fragment>
|
||||
<EuiTitle>
|
||||
<h2>{children}</h2>
|
||||
<h2 data-test-subj={dataTestSubj}>{children}</h2>
|
||||
</EuiTitle>
|
||||
<EuiSpacer />
|
||||
</Fragment>
|
||||
|
|
|
@ -0,0 +1,146 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
import expect from '@kbn/expect';
|
||||
|
||||
import { FtrProviderContext } from '../../ftr_provider_context';
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default function({ getService }: FtrProviderContext) {
|
||||
const esArchiver = getService('esArchiver');
|
||||
const ml = getService('ml');
|
||||
|
||||
const jobId = `fq_single_1_${Date.now()}`;
|
||||
|
||||
describe('single metric job creation', function() {
|
||||
this.tags('smoke');
|
||||
before(async () => {
|
||||
await esArchiver.loadIfNeeded('ml/farequote');
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await esArchiver.unload('ml/farequote');
|
||||
await ml.api.cleanMlIndices();
|
||||
await ml.api.cleanDataframeIndices();
|
||||
});
|
||||
|
||||
it('loads the job management page', async () => {
|
||||
await ml.navigation.navigateToMl();
|
||||
await ml.navigation.navigateToJobManagement();
|
||||
});
|
||||
|
||||
it('loads the new job source selection page', async () => {
|
||||
await ml.jobManagement.navigateToNewJobSourceSelection();
|
||||
});
|
||||
|
||||
it('loads the job type selection page', async () => {
|
||||
await ml.jobSourceSelection.selectSourceIndexPattern('farequote');
|
||||
});
|
||||
|
||||
it('loads the single metric job wizard page', async () => {
|
||||
await ml.jobTypeSelection.selectSingleMetricJob();
|
||||
});
|
||||
|
||||
it('displays the time range step', async () => {
|
||||
await ml.jobWizardCommon.assertTimeRangeSectionExists();
|
||||
});
|
||||
|
||||
it('displays the event rate chart', async () => {
|
||||
await ml.jobWizardCommon.clickUseFullDataButton();
|
||||
await ml.jobWizardCommon.assertEventRateChartExists();
|
||||
});
|
||||
|
||||
it('displays the pick fields step', async () => {
|
||||
await ml.jobWizardCommon.clickNextButton();
|
||||
await ml.jobWizardCommon.assertPickFieldsSectionExists();
|
||||
});
|
||||
|
||||
it('selects field and aggregation', async () => {
|
||||
const identifier = 'Mean(responsetime)';
|
||||
await ml.jobWizardCommon.assertAggAndFieldInputExists();
|
||||
await ml.jobWizardCommon.selectAggAndField(identifier);
|
||||
await ml.jobWizardCommon.assertAggAndFieldSelection(identifier);
|
||||
});
|
||||
|
||||
it('inputs the bucket span', async () => {
|
||||
const bucketSpan = '30m';
|
||||
await ml.jobWizardCommon.assertBucketSpanInputExists();
|
||||
await ml.jobWizardCommon.setBucketSpan(bucketSpan);
|
||||
await ml.jobWizardCommon.assertBucketSpanValue(bucketSpan);
|
||||
});
|
||||
|
||||
it('displays the job details step', async () => {
|
||||
await ml.jobWizardCommon.clickNextButton();
|
||||
await ml.jobWizardCommon.assertJobDetailsSectionExists();
|
||||
});
|
||||
|
||||
it('inputs the job id', async () => {
|
||||
await ml.jobWizardCommon.assertJobIdInputExists();
|
||||
await ml.jobWizardCommon.setJobId(jobId);
|
||||
await ml.jobWizardCommon.assertJobIdValue(jobId);
|
||||
});
|
||||
|
||||
it('inputs the job description', async () => {
|
||||
const jobDescription =
|
||||
'Create single metric job based on the farequote dataset with 30m bucketspan and mean(responsetime)';
|
||||
await ml.jobWizardCommon.assertJobDescriptionInputExists();
|
||||
await ml.jobWizardCommon.setJobDescription(jobDescription);
|
||||
await ml.jobWizardCommon.assertJobDescriptionValue(jobDescription);
|
||||
});
|
||||
|
||||
it('inputs job groups', async () => {
|
||||
const jobGroups = ['automated', 'farequote', 'single-metric'];
|
||||
await ml.jobWizardCommon.assertJobGroupInputExists();
|
||||
for (const jobGroup of jobGroups) {
|
||||
await ml.jobWizardCommon.addJobGroup(jobGroup);
|
||||
}
|
||||
await ml.jobWizardCommon.assertJobGroupSelection(jobGroups);
|
||||
});
|
||||
|
||||
it('opens the advanced section', async () => {
|
||||
await ml.jobWizardCommon.ensureAdvancedSectionOpen();
|
||||
});
|
||||
|
||||
it('displays the model plot switch', async () => {
|
||||
await ml.jobWizardCommon.assertModelPlotSwitchExists();
|
||||
});
|
||||
|
||||
it('enables the dedicated index switch', async () => {
|
||||
await ml.jobWizardCommon.assertDedicatedIndexSwitchExists();
|
||||
await ml.jobWizardCommon.activateDedicatedIndexSwitch();
|
||||
await ml.jobWizardCommon.assertDedicatedIndexSwitchCheckedState(true);
|
||||
});
|
||||
|
||||
it('inputs the model memory limit', async () => {
|
||||
const memoryLimit = '15MB';
|
||||
await ml.jobWizardCommon.assertModelMemoryLimitInputExists();
|
||||
await ml.jobWizardCommon.setModelMemoryLimit(memoryLimit);
|
||||
await ml.jobWizardCommon.assertModelMemoryLimitValue(memoryLimit);
|
||||
});
|
||||
|
||||
it('displays the validation step', async () => {
|
||||
await ml.jobWizardCommon.clickNextButton();
|
||||
await ml.jobWizardCommon.assertValidationSectionExists();
|
||||
});
|
||||
|
||||
it('displays the summary step', async () => {
|
||||
await ml.jobWizardCommon.clickNextButton();
|
||||
await ml.jobWizardCommon.assertSummarySectionExists();
|
||||
});
|
||||
|
||||
it('creates the job and finishes processing', async () => {
|
||||
await ml.jobWizardCommon.assertCreateJobButtonExists();
|
||||
await ml.jobWizardCommon.createJobAndWaitForCompletion();
|
||||
});
|
||||
|
||||
it('displays the created job in the job list', async () => {
|
||||
await ml.navigation.navigateToMl();
|
||||
await ml.navigation.navigateToJobManagement();
|
||||
await ml.jobManagement.filterJobsTable(jobId);
|
||||
const jobRow = await ml.jobManagement.getJobRowByJobId(jobId);
|
||||
expect(jobRow).to.not.be(null);
|
||||
});
|
||||
});
|
||||
}
|
|
@ -11,5 +11,6 @@ export default function({ loadTestFile }: FtrProviderContext) {
|
|||
|
||||
loadTestFile(require.resolve('./feature_controls'));
|
||||
loadTestFile(require.resolve('./pages'));
|
||||
loadTestFile(require.resolve('./create_single_metric_job'));
|
||||
});
|
||||
}
|
||||
|
|
|
@ -21,41 +21,41 @@ export default function({ getService }: FtrProviderContext) {
|
|||
});
|
||||
|
||||
it('loads the home page', async () => {
|
||||
await ml.navigateTo();
|
||||
await ml.navigation.navigateToMl();
|
||||
});
|
||||
|
||||
it('loads the job management page', async () => {
|
||||
await ml.navigateToJobManagement();
|
||||
await ml.assertJobStatsBarExists();
|
||||
await ml.assertJobTableExists();
|
||||
await ml.assertCreateNewJobButtonExists();
|
||||
await ml.navigation.navigateToJobManagement();
|
||||
await ml.jobManagement.assertJobStatsBarExists();
|
||||
await ml.jobManagement.assertJobTableExists();
|
||||
await ml.jobManagement.assertCreateNewJobButtonExists();
|
||||
});
|
||||
|
||||
it('loads the anomaly explorer page', async () => {
|
||||
await ml.navigateToAnomalyExplorert();
|
||||
await ml.assertAnomalyExplorerEmptyListMessageExists();
|
||||
await ml.navigation.navigateToAnomalyExplorert();
|
||||
await ml.anomalyExplorer.assertAnomalyExplorerEmptyListMessageExists();
|
||||
});
|
||||
|
||||
it('loads the single metric viewer page', async () => {
|
||||
await ml.navigateToSingleMetricViewer();
|
||||
await ml.assertSingleMetricViewerEmptyListMessageExsist();
|
||||
await ml.navigation.navigateToSingleMetricViewer();
|
||||
await ml.singleMetricViewer.assertSingleMetricViewerEmptyListMessageExsist();
|
||||
});
|
||||
|
||||
it('loads the data frame page', async () => {
|
||||
await ml.navigateToDataFrames();
|
||||
await ml.assertDataFrameEmptyListMessageExists();
|
||||
await ml.navigation.navigateToDataFrames();
|
||||
await ml.dataFrames.assertDataFrameEmptyListMessageExists();
|
||||
});
|
||||
|
||||
it('loads the data visualizer page', async () => {
|
||||
await ml.navigateToDataVisualizer();
|
||||
await ml.assertDataVisualizerImportDataCardExists();
|
||||
await ml.assertDataVisualizerIndexDataCardExists();
|
||||
await ml.navigation.navigateToDataVisualizer();
|
||||
await ml.dataVisualizer.assertDataVisualizerImportDataCardExists();
|
||||
await ml.dataVisualizer.assertDataVisualizerIndexDataCardExists();
|
||||
});
|
||||
|
||||
it('loads the settings page', async () => {
|
||||
await ml.navigateToSettings();
|
||||
await ml.assertSettingsCalendarLinkExists();
|
||||
await ml.assertSettingsFilterlistLinkExists();
|
||||
await ml.navigation.navigateToSettings();
|
||||
await ml.settings.assertSettingsCalendarLinkExists();
|
||||
await ml.settings.assertSettingsFilterlistLinkExists();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
BIN
x-pack/test/functional/es_archives/ml/farequote/data.json.gz
Normal file
BIN
x-pack/test/functional/es_archives/ml/farequote/data.json.gz
Normal file
Binary file not shown.
1105
x-pack/test/functional/es_archives/ml/farequote/mappings.json
Normal file
1105
x-pack/test/functional/es_archives/ml/farequote/mappings.json
Normal file
File diff suppressed because it is too large
Load diff
|
@ -46,7 +46,7 @@ import { UserMenuProvider } from './user_menu';
|
|||
import { UptimeProvider } from './uptime';
|
||||
import { InfraSourceConfigurationFlyoutProvider } from './infra_source_configuration_flyout';
|
||||
import { InfraLogStreamProvider } from './infra_log_stream';
|
||||
import { MachineLearningProvider } from './machine_learning';
|
||||
import { MachineLearningProvider } from './ml';
|
||||
|
||||
import { SecurityServiceProvider, SpacesServiceProvider } from '../../common/services';
|
||||
|
||||
|
|
|
@ -1,88 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { FtrProviderContext } from '../ftr_provider_context';
|
||||
|
||||
export function MachineLearningProvider({ getService, getPageObjects }: FtrProviderContext) {
|
||||
const testSubjects = getService('testSubjects');
|
||||
const PageObjects = getPageObjects(['common']);
|
||||
|
||||
return {
|
||||
async navigateTo() {
|
||||
return await PageObjects.common.navigateToApp('ml');
|
||||
},
|
||||
|
||||
async navigateToJobManagement() {
|
||||
await testSubjects.click('mlTabJobManagement');
|
||||
await testSubjects.exists('mlPageJobManagement');
|
||||
},
|
||||
|
||||
async navigateToAnomalyExplorert() {
|
||||
await testSubjects.click('mlTabAnomalyExplorer');
|
||||
await testSubjects.exists('mlPageAnomalyExplorer');
|
||||
},
|
||||
|
||||
async navigateToSingleMetricViewer() {
|
||||
await testSubjects.click('mlTabSingleMetricViewer');
|
||||
await testSubjects.exists('mlPageSingleMetricViewer');
|
||||
},
|
||||
|
||||
async navigateToDataFrames() {
|
||||
await testSubjects.click('mlTabDataFrames');
|
||||
await testSubjects.exists('mlPageDataFrame');
|
||||
},
|
||||
|
||||
async navigateToDataVisualizer() {
|
||||
await testSubjects.click('mlTabDataVisualizer');
|
||||
await testSubjects.exists('mlPageDataVisualizerSelector');
|
||||
},
|
||||
|
||||
async navigateToSettings() {
|
||||
await testSubjects.click('mlTabSettings');
|
||||
await testSubjects.exists('mlPageSettings');
|
||||
},
|
||||
|
||||
async assertJobTableExists() {
|
||||
await testSubjects.existOrFail('mlJobListTable');
|
||||
},
|
||||
|
||||
async assertCreateNewJobButtonExists() {
|
||||
await testSubjects.existOrFail('mlCreateNewJobButton');
|
||||
},
|
||||
|
||||
async assertJobStatsBarExists() {
|
||||
await testSubjects.existOrFail('mlJobStatsBar');
|
||||
},
|
||||
|
||||
async assertAnomalyExplorerEmptyListMessageExists() {
|
||||
await testSubjects.existOrFail('mlNoJobsFound');
|
||||
},
|
||||
|
||||
async assertSingleMetricViewerEmptyListMessageExsist() {
|
||||
await testSubjects.existOrFail('mlNoSingleMetricJobsFound');
|
||||
},
|
||||
|
||||
async assertDataFrameEmptyListMessageExists() {
|
||||
await testSubjects.existOrFail('mlNoDataFrameTransformsFound');
|
||||
},
|
||||
|
||||
async assertDataVisualizerImportDataCardExists() {
|
||||
await testSubjects.existOrFail('mlDataVisualizerCardImportData');
|
||||
},
|
||||
|
||||
async assertDataVisualizerIndexDataCardExists() {
|
||||
await testSubjects.existOrFail('mlDataVisualizerCardIndexData');
|
||||
},
|
||||
|
||||
async assertSettingsCalendarLinkExists() {
|
||||
await testSubjects.existOrFail('ml_calendar_mng_button');
|
||||
},
|
||||
|
||||
async assertSettingsFilterlistLinkExists() {
|
||||
await testSubjects.existOrFail('ml_filter_lists_button');
|
||||
},
|
||||
};
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
/*
|
||||
* 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 { FtrProviderContext } from '../../ftr_provider_context';
|
||||
|
||||
export function MachineLearningAnomalyExplorerProvider({ getService }: FtrProviderContext) {
|
||||
const testSubjects = getService('testSubjects');
|
||||
|
||||
return {
|
||||
async assertAnomalyExplorerEmptyListMessageExists() {
|
||||
await testSubjects.existOrFail('mlNoJobsFound');
|
||||
},
|
||||
};
|
||||
}
|
47
x-pack/test/functional/services/machine_learning/api.ts
Normal file
47
x-pack/test/functional/services/machine_learning/api.ts
Normal file
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* 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 { isEmpty } from 'lodash';
|
||||
import { FtrProviderContext } from '../../ftr_provider_context';
|
||||
|
||||
export function MachineLearningAPIProvider({ getService }: FtrProviderContext) {
|
||||
const es = getService('es');
|
||||
const log = getService('log');
|
||||
const retry = getService('retry');
|
||||
|
||||
return {
|
||||
async deleteIndices(indices: string) {
|
||||
log.debug(`Deleting indices: '${indices}'...`);
|
||||
const deleteResponse = await es.indices.delete({
|
||||
index: indices,
|
||||
});
|
||||
expect(deleteResponse)
|
||||
.to.have.property('acknowledged')
|
||||
.eql(true);
|
||||
|
||||
await retry.waitForWithTimeout(`'${indices}' indices to be deleted`, 30 * 1000, async () => {
|
||||
const getRepsonse = await es.indices.get({
|
||||
index: indices,
|
||||
});
|
||||
|
||||
if (isEmpty(getRepsonse)) {
|
||||
return true;
|
||||
} else {
|
||||
throw new Error(`expected indices '${indices}' to be deleted`);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
async cleanMlIndices() {
|
||||
await this.deleteIndices('.ml-*');
|
||||
},
|
||||
|
||||
async cleanDataframeIndices() {
|
||||
await this.deleteIndices('.data-frame-*');
|
||||
},
|
||||
};
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
/*
|
||||
* 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 { FtrProviderContext } from '../../ftr_provider_context';
|
||||
|
||||
export function MachineLearningDataFramesProvider({ getService }: FtrProviderContext) {
|
||||
const testSubjects = getService('testSubjects');
|
||||
|
||||
return {
|
||||
async assertDataFrameEmptyListMessageExists() {
|
||||
await testSubjects.existOrFail('mlNoDataFrameTransformsFound');
|
||||
},
|
||||
};
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* 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 { FtrProviderContext } from '../../ftr_provider_context';
|
||||
|
||||
export function MachineLearningDataVisualizerProvider({ getService }: FtrProviderContext) {
|
||||
const testSubjects = getService('testSubjects');
|
||||
|
||||
return {
|
||||
async assertDataVisualizerImportDataCardExists() {
|
||||
await testSubjects.existOrFail('mlDataVisualizerCardImportData');
|
||||
},
|
||||
|
||||
async assertDataVisualizerIndexDataCardExists() {
|
||||
await testSubjects.existOrFail('mlDataVisualizerCardIndexData');
|
||||
},
|
||||
};
|
||||
}
|
17
x-pack/test/functional/services/machine_learning/index.ts
Normal file
17
x-pack/test/functional/services/machine_learning/index.ts
Normal file
|
@ -0,0 +1,17 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export { MachineLearningAnomalyExplorerProvider } from './anomaly_explorer';
|
||||
export { MachineLearningAPIProvider } from './api';
|
||||
export { MachineLearningDataFramesProvider } from './data_frames';
|
||||
export { MachineLearningDataVisualizerProvider } from './data_visualizer';
|
||||
export { MachineLearningJobManagementProvider } from './job_management';
|
||||
export { MachineLearningJobSourceSelectionProvider } from './job_source_selection';
|
||||
export { MachineLearningJobTypeSelectionProvider } from './job_type_selection';
|
||||
export { MachineLearningJobWizardCommonProvider } from './job_wizard_common';
|
||||
export { MachineLearningNavigationProvider } from './navigation';
|
||||
export { MachineLearningSettingsProvider } from './settings';
|
||||
export { MachineLearningSingleMetricViewerProvider } from './single_metric_viewer';
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* 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 { FtrProviderContext } from '../../ftr_provider_context';
|
||||
import { WebElementWrapper } from '../../../../../test/functional/services/lib/web_element_wrapper';
|
||||
|
||||
export function MachineLearningJobManagementProvider({ getService }: FtrProviderContext) {
|
||||
const testSubjects = getService('testSubjects');
|
||||
const retry = getService('retry');
|
||||
|
||||
return {
|
||||
async getJobsTable(): Promise<WebElementWrapper> {
|
||||
const tableContainer = await testSubjects.find('mlJobListTable');
|
||||
return await tableContainer.findByTagName('table');
|
||||
},
|
||||
|
||||
async isJobsTableLoadingIndicatorDisplayed(): Promise<boolean> {
|
||||
const mlJobListTable = await testSubjects.find('mlJobListTable');
|
||||
const innerText = await mlJobListTable.getVisibleText();
|
||||
return innerText.includes('Loading jobs...');
|
||||
},
|
||||
|
||||
async isNoItemsFoundMessageDisplayed(): Promise<boolean> {
|
||||
const mlJobListTable = await testSubjects.find('mlJobListTable');
|
||||
const innerText = await mlJobListTable.getVisibleText();
|
||||
return innerText.includes('No jobs found');
|
||||
},
|
||||
|
||||
async navigateToNewJobSourceSelection() {
|
||||
await testSubjects.clickWhenNotDisabled('mlCreateNewJobButton');
|
||||
await testSubjects.existOrFail('mlPageSourceSelection');
|
||||
},
|
||||
|
||||
async assertJobTableExists() {
|
||||
await testSubjects.existOrFail('mlJobListTable');
|
||||
},
|
||||
|
||||
async assertCreateNewJobButtonExists() {
|
||||
await testSubjects.existOrFail('mlCreateNewJobButton');
|
||||
},
|
||||
|
||||
async assertJobStatsBarExists() {
|
||||
await testSubjects.existOrFail('mlJobStatsBar');
|
||||
},
|
||||
|
||||
async waitForJobsTableToLoad() {
|
||||
await retry.waitFor(
|
||||
'jobs table to exist',
|
||||
async () => await testSubjects.exists('mlJobListTable')
|
||||
);
|
||||
|
||||
await retry.waitFor(
|
||||
'jobs table loading indicator to be invisible',
|
||||
async () => (await this.isJobsTableLoadingIndicatorDisplayed()) === false
|
||||
);
|
||||
},
|
||||
|
||||
async filterJobsTable(jobId: string) {
|
||||
await this.waitForJobsTableToLoad();
|
||||
const searchBar = await testSubjects.find('mlJobListSearchBar');
|
||||
const searchBarInput = await searchBar.findByTagName('input');
|
||||
await searchBarInput.clearValueWithKeyboard();
|
||||
await searchBarInput.type(jobId);
|
||||
},
|
||||
|
||||
async getJobRowByJobId(jobId: string): Promise<WebElementWrapper> {
|
||||
const table = await this.getJobsTable();
|
||||
return await table.findByCssSelector(`[data-row-id=${jobId}]`);
|
||||
},
|
||||
};
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* 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 { FtrProviderContext } from '../../ftr_provider_context';
|
||||
|
||||
export function MachineLearningJobSourceSelectionProvider({ getService }: FtrProviderContext) {
|
||||
const testSubjects = getService('testSubjects');
|
||||
|
||||
return {
|
||||
async selectSourceIndexPattern(indexPattern: string) {
|
||||
const subj = 'paginatedListItem-' + indexPattern;
|
||||
await testSubjects.clickWhenNotDisabled(subj);
|
||||
await testSubjects.existOrFail('mlPageJobTypeSelection');
|
||||
},
|
||||
};
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* 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 { FtrProviderContext } from '../../ftr_provider_context';
|
||||
|
||||
export function MachineLearningJobTypeSelectionProvider({ getService }: FtrProviderContext) {
|
||||
const testSubjects = getService('testSubjects');
|
||||
|
||||
return {
|
||||
async selectSingleMetricJob() {
|
||||
await testSubjects.clickWhenNotDisabled('mlJobTypeLinkSingleMetricJob');
|
||||
await testSubjects.existOrFail('mlPageJobWizard');
|
||||
},
|
||||
};
|
||||
}
|
|
@ -0,0 +1,202 @@
|
|||
/*
|
||||
* 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 MachineLearningJobWizardCommonProvider({ getService }: FtrProviderContext) {
|
||||
const comboBox = getService('comboBox');
|
||||
const retry = getService('retry');
|
||||
const testSubjects = getService('testSubjects');
|
||||
|
||||
return {
|
||||
async assertTimeRangeSectionExists() {
|
||||
await testSubjects.existOrFail('mlJobWizardStepTitleTimeRange');
|
||||
},
|
||||
|
||||
async assertPickFieldsSectionExists() {
|
||||
await testSubjects.existOrFail('mlJobWizardStepTitlePickFields');
|
||||
},
|
||||
|
||||
async assertJobDetailsSectionExists() {
|
||||
await testSubjects.existOrFail('mlJobWizardStepTitleJobDetails');
|
||||
},
|
||||
|
||||
async assertValidationSectionExists() {
|
||||
await testSubjects.existOrFail('mlJobWizardStepTitleValidation');
|
||||
},
|
||||
|
||||
async assertSummarySectionExists() {
|
||||
await testSubjects.existOrFail('mlJobWizardStepTitleSummary');
|
||||
},
|
||||
|
||||
async assertEventRateChartExists() {
|
||||
await testSubjects.existOrFail('mlEventRateChart');
|
||||
},
|
||||
|
||||
async assertAggAndFieldInputExists() {
|
||||
await testSubjects.existOrFail('mlJobWizardAggSelection comboBoxInput');
|
||||
},
|
||||
|
||||
async assertAggAndFieldSelection(identifier: string) {
|
||||
const comboBoxSelectedOptions = await comboBox.getComboBoxSelectedOptions(
|
||||
'mlJobWizardAggSelection comboBoxInput'
|
||||
);
|
||||
expect(comboBoxSelectedOptions.length).to.eql(1);
|
||||
expect(comboBoxSelectedOptions[0]).to.eql(identifier);
|
||||
},
|
||||
|
||||
async assertBucketSpanInputExists() {
|
||||
await testSubjects.existOrFail('mlJobWizardInputBucketSpan');
|
||||
},
|
||||
|
||||
async assertBucketSpanValue(expectedValue: string) {
|
||||
const actualBucketSpan = await testSubjects.getAttribute(
|
||||
'mlJobWizardInputBucketSpan',
|
||||
'value'
|
||||
);
|
||||
expect(actualBucketSpan).to.eql(expectedValue);
|
||||
},
|
||||
|
||||
async assertJobIdInputExists() {
|
||||
await testSubjects.existOrFail('mlJobWizardInputJobId');
|
||||
},
|
||||
|
||||
async assertJobIdValue(expectedValue: string) {
|
||||
const actualJobId = await testSubjects.getAttribute('mlJobWizardInputJobId', 'value');
|
||||
expect(actualJobId).to.eql(expectedValue);
|
||||
},
|
||||
|
||||
async assertJobDescriptionInputExists() {
|
||||
await testSubjects.existOrFail('mlJobWizardInputJobDescription');
|
||||
},
|
||||
|
||||
async assertJobDescriptionValue(expectedValue: string) {
|
||||
const actualJobDescription = await testSubjects.getVisibleText(
|
||||
'mlJobWizardInputJobDescription'
|
||||
);
|
||||
expect(actualJobDescription).to.eql(expectedValue);
|
||||
},
|
||||
|
||||
async assertJobGroupInputExists() {
|
||||
await testSubjects.existOrFail('mlJobWizardComboBoxJobGroups comboBoxInput');
|
||||
},
|
||||
|
||||
async assertJobGroupSelection(jobGroups: string[]) {
|
||||
const comboBoxSelectedOptions = await comboBox.getComboBoxSelectedOptions(
|
||||
'mlJobWizardComboBoxJobGroups comboBoxInput'
|
||||
);
|
||||
expect(comboBoxSelectedOptions.length).to.eql(jobGroups.length);
|
||||
expect(comboBoxSelectedOptions).to.eql(jobGroups);
|
||||
},
|
||||
|
||||
async assertModelPlotSwitchExists() {
|
||||
await this.ensureAdvancedSectionOpen();
|
||||
await testSubjects.existOrFail('mlJobWizardAdvancedSection mlJobWizardSwitchModelPlot', {
|
||||
allowHidden: true,
|
||||
});
|
||||
},
|
||||
|
||||
async assertDedicatedIndexSwitchExists() {
|
||||
await this.ensureAdvancedSectionOpen();
|
||||
await testSubjects.existOrFail(
|
||||
'mlJobWizardAdvancedSection mlJobWizardSwitchUseDedicatedIndex',
|
||||
{ allowHidden: true }
|
||||
);
|
||||
},
|
||||
|
||||
async assertDedicatedIndexSwitchCheckedState(expectedValue: boolean) {
|
||||
await this.ensureAdvancedSectionOpen();
|
||||
const actualCheckedState = await this.getDedicatedIndexSwitchCheckedState();
|
||||
expect(actualCheckedState).to.eql(expectedValue);
|
||||
},
|
||||
|
||||
async assertCreateJobButtonExists() {
|
||||
await testSubjects.existOrFail('mlJobWizardButtonCreateJob');
|
||||
},
|
||||
|
||||
async getDedicatedIndexSwitchCheckedState() {
|
||||
await this.ensureAdvancedSectionOpen();
|
||||
return await testSubjects.isSelected(
|
||||
'mlJobWizardAdvancedSection mlJobWizardSwitchUseDedicatedIndex'
|
||||
);
|
||||
},
|
||||
|
||||
async assertModelMemoryLimitInputExists() {
|
||||
await this.ensureAdvancedSectionOpen();
|
||||
await testSubjects.existOrFail('mlJobWizardAdvancedSection mlJobWizardInputModelMemoryLimit');
|
||||
},
|
||||
|
||||
async assertModelMemoryLimitValue(expectedValue: string) {
|
||||
await this.ensureAdvancedSectionOpen();
|
||||
const actualModelMemoryLimit = await testSubjects.getAttribute(
|
||||
'mlJobWizardAdvancedSection mlJobWizardInputModelMemoryLimit',
|
||||
'value'
|
||||
);
|
||||
expect(actualModelMemoryLimit).to.eql(expectedValue);
|
||||
},
|
||||
|
||||
async clickNextButton() {
|
||||
await testSubjects.clickWhenNotDisabled('mlJobWizardNavButtonNext');
|
||||
},
|
||||
|
||||
async clickPreviousButton() {
|
||||
await testSubjects.clickWhenNotDisabled('mlJobWizardNavButtonPrevious');
|
||||
},
|
||||
|
||||
async clickUseFullDataButton() {
|
||||
await testSubjects.clickWhenNotDisabled('mlButtonUseFullData');
|
||||
},
|
||||
|
||||
async selectAggAndField(identifier: string) {
|
||||
await comboBox.set('mlJobWizardAggSelection comboBoxInput', identifier);
|
||||
},
|
||||
|
||||
async setBucketSpan(bucketSpan: string) {
|
||||
await testSubjects.setValue('mlJobWizardInputBucketSpan', bucketSpan);
|
||||
},
|
||||
|
||||
async setJobId(jobId: string) {
|
||||
await testSubjects.setValue('mlJobWizardInputJobId', jobId);
|
||||
},
|
||||
|
||||
async setJobDescription(jobDescription: string) {
|
||||
await testSubjects.setValue('mlJobWizardInputJobDescription', jobDescription);
|
||||
},
|
||||
|
||||
async addJobGroup(jobGroup: string) {
|
||||
await comboBox.setCustom('mlJobWizardComboBoxJobGroups comboBoxInput', jobGroup);
|
||||
},
|
||||
|
||||
async ensureAdvancedSectionOpen() {
|
||||
await retry.try(async () => {
|
||||
if ((await testSubjects.exists('mlJobWizardAdvancedSection')) === false) {
|
||||
await testSubjects.click('mlJobWizardToggleAdvancedSection');
|
||||
await testSubjects.existOrFail('mlJobWizardAdvancedSection');
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
async activateDedicatedIndexSwitch() {
|
||||
if ((await this.getDedicatedIndexSwitchCheckedState()) === false) {
|
||||
await testSubjects.clickWhenNotDisabled('mlJobWizardSwitchUseDedicatedIndex');
|
||||
}
|
||||
},
|
||||
|
||||
async setModelMemoryLimit(modelMemoryLimit: string) {
|
||||
await testSubjects.setValue('mlJobWizardInputModelMemoryLimit', modelMemoryLimit);
|
||||
},
|
||||
|
||||
async createJobAndWaitForCompletion() {
|
||||
await testSubjects.clickWhenNotDisabled('mlJobWizardButtonCreateJob');
|
||||
await retry.waitForWithTimeout(
|
||||
'job processing to finish',
|
||||
5 * 60 * 1000,
|
||||
async () => (await testSubjects.exists('mlJobWizardButtonCreateJob')) === false
|
||||
);
|
||||
},
|
||||
};
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* 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 { FtrProviderContext } from '../../ftr_provider_context';
|
||||
|
||||
export function MachineLearningNavigationProvider({
|
||||
getService,
|
||||
getPageObjects,
|
||||
}: FtrProviderContext) {
|
||||
const retry = getService('retry');
|
||||
const testSubjects = getService('testSubjects');
|
||||
const PageObjects = getPageObjects(['common']);
|
||||
|
||||
return {
|
||||
async navigateToMl() {
|
||||
return await PageObjects.common.navigateToApp('ml');
|
||||
},
|
||||
|
||||
async navigateToArea(linkSubject: string, pageSubject: string) {
|
||||
await retry.try(async () => {
|
||||
if ((await testSubjects.exists(pageSubject)) === false) {
|
||||
await testSubjects.click(linkSubject);
|
||||
await testSubjects.existOrFail(pageSubject);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
async navigateToJobManagement() {
|
||||
await this.navigateToArea('mlTabJobManagement', 'mlPageJobManagement');
|
||||
},
|
||||
|
||||
async navigateToAnomalyExplorert() {
|
||||
await this.navigateToArea('mlTabAnomalyExplorer', 'mlPageAnomalyExplorer');
|
||||
},
|
||||
|
||||
async navigateToSingleMetricViewer() {
|
||||
await this.navigateToArea('mlTabSingleMetricViewer', 'mlPageSingleMetricViewer');
|
||||
},
|
||||
|
||||
async navigateToDataFrames() {
|
||||
await this.navigateToArea('mlTabDataFrames', 'mlPageDataFrame');
|
||||
},
|
||||
|
||||
async navigateToDataVisualizer() {
|
||||
await this.navigateToArea('mlTabDataVisualizer', 'mlPageDataVisualizerSelector');
|
||||
},
|
||||
|
||||
async navigateToSettings() {
|
||||
await this.navigateToArea('mlTabSettings', 'mlPageSettings');
|
||||
},
|
||||
};
|
||||
}
|
21
x-pack/test/functional/services/machine_learning/settings.ts
Normal file
21
x-pack/test/functional/services/machine_learning/settings.ts
Normal file
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* 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 { FtrProviderContext } from '../../ftr_provider_context';
|
||||
|
||||
export function MachineLearningSettingsProvider({ getService }: FtrProviderContext) {
|
||||
const testSubjects = getService('testSubjects');
|
||||
|
||||
return {
|
||||
async assertSettingsCalendarLinkExists() {
|
||||
await testSubjects.existOrFail('ml_calendar_mng_button');
|
||||
},
|
||||
|
||||
async assertSettingsFilterlistLinkExists() {
|
||||
await testSubjects.existOrFail('ml_filter_lists_button');
|
||||
},
|
||||
};
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
/*
|
||||
* 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 { FtrProviderContext } from '../../ftr_provider_context';
|
||||
|
||||
export function MachineLearningSingleMetricViewerProvider({ getService }: FtrProviderContext) {
|
||||
const testSubjects = getService('testSubjects');
|
||||
|
||||
return {
|
||||
async assertSingleMetricViewerEmptyListMessageExsist() {
|
||||
await testSubjects.existOrFail('mlNoSingleMetricJobsFound');
|
||||
},
|
||||
};
|
||||
}
|
49
x-pack/test/functional/services/ml.ts
Normal file
49
x-pack/test/functional/services/ml.ts
Normal file
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* 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 { FtrProviderContext } from '../ftr_provider_context';
|
||||
|
||||
import {
|
||||
MachineLearningAnomalyExplorerProvider,
|
||||
MachineLearningAPIProvider,
|
||||
MachineLearningDataFramesProvider,
|
||||
MachineLearningDataVisualizerProvider,
|
||||
MachineLearningJobManagementProvider,
|
||||
MachineLearningJobSourceSelectionProvider,
|
||||
MachineLearningJobTypeSelectionProvider,
|
||||
MachineLearningJobWizardCommonProvider,
|
||||
MachineLearningNavigationProvider,
|
||||
MachineLearningSettingsProvider,
|
||||
MachineLearningSingleMetricViewerProvider,
|
||||
} from './machine_learning';
|
||||
|
||||
export function MachineLearningProvider(context: FtrProviderContext) {
|
||||
const anomalyExplorer = MachineLearningAnomalyExplorerProvider(context);
|
||||
const api = MachineLearningAPIProvider(context);
|
||||
const dataFrames = MachineLearningDataFramesProvider(context);
|
||||
const dataVisualizer = MachineLearningDataVisualizerProvider(context);
|
||||
const jobManagement = MachineLearningJobManagementProvider(context);
|
||||
const jobSourceSelection = MachineLearningJobSourceSelectionProvider(context);
|
||||
const jobTypeSelection = MachineLearningJobTypeSelectionProvider(context);
|
||||
const jobWizardCommon = MachineLearningJobWizardCommonProvider(context);
|
||||
const navigation = MachineLearningNavigationProvider(context);
|
||||
const settings = MachineLearningSettingsProvider(context);
|
||||
const singleMetricViewer = MachineLearningSingleMetricViewerProvider(context);
|
||||
|
||||
return {
|
||||
anomalyExplorer,
|
||||
api,
|
||||
dataFrames,
|
||||
dataVisualizer,
|
||||
jobManagement,
|
||||
jobSourceSelection,
|
||||
jobTypeSelection,
|
||||
jobWizardCommon,
|
||||
navigation,
|
||||
settings,
|
||||
singleMetricViewer,
|
||||
};
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue