[ML] Add functional tests for analytics UI: classification jobs (#56912) (#57199)

* add classification functional tests

* remove unneeded testing tag

* update jobId and description per suggestion

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
Melissa Alvarez 2020-02-10 11:13:02 -05:00 committed by GitHub
parent 2148dfe17e
commit 5b55ee90b9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 1743 additions and 3 deletions

View file

@ -218,7 +218,10 @@ export const EvaluatePanel: FC<Props> = ({ jobConfig, jobStatus, searchQuery })
} }
return ( return (
<EuiPanel style={{ width: `${panelWidth}px` }}> <EuiPanel
data-test-subj="mlDFAnalyticsClassificationExplorationEvaluatePanel"
style={{ width: `${panelWidth}px` }}
>
<EuiFlexGroup direction="column" gutterSize="s"> <EuiFlexGroup direction="column" gutterSize="s">
<EuiFlexItem> <EuiFlexItem>
<EuiFlexGroup alignItems="center" justifyContent="spaceBetween"> <EuiFlexGroup alignItems="center" justifyContent="spaceBetween">
@ -337,6 +340,7 @@ export const EvaluatePanel: FC<Props> = ({ jobConfig, jobStatus, searchQuery })
</EuiFlexItem> </EuiFlexItem>
<EuiFlexItem grow={false} style={{ width: '90%' }}> <EuiFlexItem grow={false} style={{ width: '90%' }}>
<EuiDataGrid <EuiDataGrid
data-test-subj="mlDFAnalyticsClassificationExplorationConfusionMatrix"
aria-label="Classification confusion matrix" aria-label="Classification confusion matrix"
columns={columns} columns={columns}
columnVisibility={{ visibleColumns, setVisibleColumns }} columnVisibility={{ visibleColumns, setVisibleColumns }}

View file

@ -401,7 +401,11 @@ export const ResultsTable: FC<Props> = React.memo(
: searchError; : searchError;
return ( return (
<EuiPanel grow={false} id="mlDataFrameAnalyticsTableResultsPanel"> <EuiPanel
grow={false}
id="mlDataFrameAnalyticsTableResultsPanel"
data-test-subj="mlDFAnalyticsClassificationExplorationTablePanel"
>
<EuiFlexGroup alignItems="center" justifyContent="spaceBetween" responsive={false}> <EuiFlexGroup alignItems="center" justifyContent="spaceBetween" responsive={false}>
<EuiFlexItem grow={false}> <EuiFlexItem grow={false}>
<EuiFlexGroup gutterSize="s"> <EuiFlexGroup gutterSize="s">

View file

@ -0,0 +1,174 @@
/*
* 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 default function({ getService }: FtrProviderContext) {
const esArchiver = getService('esArchiver');
const ml = getService('ml');
describe('classification creation', function() {
this.tags(['smoke']);
before(async () => {
await esArchiver.load('ml/bm_classification');
});
after(async () => {
await ml.api.cleanMlIndices();
await esArchiver.unload('ml/bm_classification');
});
const testDataList = [
{
suiteTitle: 'bank marketing',
jobType: 'classification',
jobId: `bm_1_${Date.now()}`,
jobDescription:
"Classification job based on 'bank-marketing' dataset with dependentVariable 'y' and trainingPercent '20'",
source: 'bank-marketing*',
get destinationIndex(): string {
return `dest_${this.jobId}`;
},
dependentVariable: 'y',
trainingPercent: '20',
modelMemory: '105mb',
createIndexPattern: true,
expected: {
row: {
type: 'classification',
status: 'stopped',
progress: '100',
},
},
},
];
for (const testData of testDataList) {
describe(`${testData.suiteTitle}`, function() {
after(async () => {
await ml.api.deleteIndices(testData.destinationIndex);
});
it('loads the data frame analytics page', async () => {
await ml.navigation.navigateToMl();
await ml.navigation.navigateToDataFrameAnalytics();
});
it('loads the job creation flyout', async () => {
await ml.dataFrameAnalytics.startAnalyticsCreation();
});
it('selects the job type', async () => {
await ml.dataFrameAnalyticsCreation.assertJobTypeSelectExists();
await ml.dataFrameAnalyticsCreation.selectJobType(testData.jobType);
});
it('inputs the job id', async () => {
await ml.dataFrameAnalyticsCreation.assertJobIdInputExists();
await ml.dataFrameAnalyticsCreation.setJobId(testData.jobId);
});
it('inputs the job description', async () => {
await ml.dataFrameAnalyticsCreation.assertJobDescriptionInputExists();
await ml.dataFrameAnalyticsCreation.setJobDescription(testData.jobDescription);
});
it('selects the source index', async () => {
await ml.dataFrameAnalyticsCreation.assertSourceIndexInputExists();
await ml.dataFrameAnalyticsCreation.selectSourceIndex(testData.source);
});
it('inputs the destination index', async () => {
await ml.dataFrameAnalyticsCreation.assertDestIndexInputExists();
await ml.dataFrameAnalyticsCreation.setDestIndex(testData.destinationIndex);
});
it('inputs the dependent variable', async () => {
await ml.dataFrameAnalyticsCreation.assertDependentVariableInputExists();
await ml.dataFrameAnalyticsCreation.selectDependentVariable(testData.dependentVariable);
});
it('inputs the training percent', async () => {
await ml.dataFrameAnalyticsCreation.assertTrainingPercentInputExists();
await ml.dataFrameAnalyticsCreation.setTrainingPercent(testData.trainingPercent);
});
it('inputs the model memory limit', async () => {
await ml.dataFrameAnalyticsCreation.assertModelMemoryInputExists();
await ml.dataFrameAnalyticsCreation.setModelMemory(testData.modelMemory);
});
it('sets the create index pattern switch', async () => {
await ml.dataFrameAnalyticsCreation.assertCreateIndexPatternSwitchExists();
await ml.dataFrameAnalyticsCreation.setCreateIndexPatternSwitchState(
testData.createIndexPattern
);
});
it('creates the analytics job', async () => {
await ml.dataFrameAnalyticsCreation.assertCreateButtonExists();
await ml.dataFrameAnalyticsCreation.createAnalyticsJob();
});
it('starts the analytics job', async () => {
await ml.dataFrameAnalyticsCreation.assertStartButtonExists();
await ml.dataFrameAnalyticsCreation.startAnalyticsJob();
});
it('closes the create job flyout', async () => {
await ml.dataFrameAnalyticsCreation.assertCloseButtonExists();
await ml.dataFrameAnalyticsCreation.closeCreateAnalyticsJobFlyout();
});
it('finishes analytics processing', async () => {
await ml.dataFrameAnalytics.waitForAnalyticsCompletion(testData.jobId);
});
it('displays the analytics table', async () => {
await ml.dataFrameAnalytics.assertAnalyticsTableExists();
});
it('displays the stats bar', async () => {
await ml.dataFrameAnalytics.assertAnalyticsStatsBarExists();
});
it('displays the created job in the analytics table', async () => {
await ml.dataFrameAnalyticsTable.refreshAnalyticsTable();
await ml.dataFrameAnalyticsTable.filterWithSearchString(testData.jobId);
const rows = await ml.dataFrameAnalyticsTable.parseAnalyticsTable();
const filteredRows = rows.filter(row => row.id === testData.jobId);
expect(filteredRows).to.have.length(
1,
`Filtered analytics table should have 1 row for job id '${testData.jobId}' (got matching items '${filteredRows}')`
);
});
it('displays details for the created job in the analytics table', async () => {
await ml.dataFrameAnalyticsTable.assertAnalyticsRowFields(testData.jobId, {
id: testData.jobId,
description: testData.jobDescription,
sourceIndex: testData.source,
destinationIndex: testData.destinationIndex,
type: testData.expected.row.type,
status: testData.expected.row.status,
progress: testData.expected.row.progress,
});
});
it('creates the destination index and writes results to it', async () => {
await ml.api.assertIndicesExist(testData.destinationIndex);
await ml.api.assertIndicesNotEmpty(testData.destinationIndex);
});
it('displays the results view for created job', async () => {
await ml.dataFrameAnalyticsTable.openResultsView();
await ml.dataFrameAnalytics.assertClassificationEvaluatePanelElementsExists();
await ml.dataFrameAnalytics.assertClassificationTablePanelExists();
});
});
}
});
}

View file

@ -11,5 +11,6 @@ export default function({ loadTestFile }: FtrProviderContext) {
loadTestFile(require.resolve('./outlier_detection_creation')); loadTestFile(require.resolve('./outlier_detection_creation'));
loadTestFile(require.resolve('./regression_creation')); loadTestFile(require.resolve('./regression_creation'));
loadTestFile(require.resolve('./classification_creation'));
}); });
} }

View file

@ -11,7 +11,7 @@ export default function({ getService }: FtrProviderContext) {
const esArchiver = getService('esArchiver'); const esArchiver = getService('esArchiver');
const ml = getService('ml'); const ml = getService('ml');
describe('outlier detection creation', function() { describe('regression creation', function() {
this.tags(['smoke']); this.tags(['smoke']);
before(async () => { before(async () => {
await esArchiver.load('ml/egs_regression'); await esArchiver.load('ml/egs_regression');

File diff suppressed because it is too large Load diff

View file

@ -44,6 +44,15 @@ export function MachineLearningDataFrameAnalyticsProvider(
await testSubjects.existOrFail('mlDFAnalyticsRegressionExplorationTablePanel'); await testSubjects.existOrFail('mlDFAnalyticsRegressionExplorationTablePanel');
}, },
async assertClassificationEvaluatePanelElementsExists() {
await testSubjects.existOrFail('mlDFAnalyticsClassificationExplorationEvaluatePanel');
await testSubjects.existOrFail('mlDFAnalyticsClassificationExplorationConfusionMatrix');
},
async assertClassificationTablePanelExists() {
await testSubjects.existOrFail('mlDFAnalyticsClassificationExplorationTablePanel');
},
async assertOutlierTablePanelExists() { async assertOutlierTablePanelExists() {
await testSubjects.existOrFail('mlDFAnalyticsOutlierExplorationTablePanel'); await testSubjects.existOrFail('mlDFAnalyticsOutlierExplorationTablePanel');
}, },