[ML] Adds extra functional tests for index data visualizer (#57439) (#57460)

* [ML] Adds extra functional tests for index data visualizer

* [ML] Remove test tag and uncomment clean-up
This commit is contained in:
Pete Harverson 2020-02-12 17:24:03 +00:00 committed by GitHub
parent 51f88f7a56
commit ccbb48df10
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 390 additions and 240 deletions

View file

@ -87,6 +87,7 @@ export const ActionsPanel: FC<Props> = ({ indexPattern }) => {
})}
onClick={openAdvancedJobWizard}
href={`#/jobs/new_job/advanced?index=${indexPattern}`}
data-test-subj="mlDataVisualizerCreateAdvancedJobCard"
/>
</div>
);

View file

@ -8,6 +8,24 @@ import { FtrProviderContext } from '../../../ftr_provider_context';
import { ML_JOB_FIELD_TYPES } from '../../../../../legacy/plugins/ml/common/constants/field_types';
import { FieldVisConfig } from '../../../../../legacy/plugins/ml/public/application/datavisualizer/index_based/common';
interface TestData {
suiteTitle: string;
sourceIndexOrSavedSearch: string;
advancedJobWizardDatafeedQuery: string;
metricFieldsFilter: string;
nonMetricFieldsFilter: string;
nonMetricFieldsTypeFilter: string;
expected: {
totalDocCount: number;
fieldsPanelCount: number;
metricCards?: FieldVisConfig[];
nonMetricCards?: FieldVisConfig[];
nonMetricFieldsTypeFilterCardCount: number;
metricFieldsFilterCardCount: number;
nonMetricFieldsFilterCardCount: number;
};
}
function getFieldTypes(cards: FieldVisConfig[]) {
const fieldTypes: ML_JOB_FIELD_TYPES[] = [];
cards.forEach(card => {
@ -25,154 +43,337 @@ export default function({ getService }: FtrProviderContext) {
const esArchiver = getService('esArchiver');
const ml = getService('ml');
const testDataList = [
{
suiteTitle: 'with full farequote index',
sourceIndexOrSavedSearch: 'farequote',
metricFieldsFilter: 'document',
nonMetricFieldsFilter: 'airline',
nonMetricFieldsTypeFilter: 'keyword',
expected: {
totalDocCount: 86274,
fieldsPanelCount: 2, // Metrics panel and Fields panel
metricCards: [
{
type: ML_JOB_FIELD_TYPES.NUMBER, // document count card
existsInDocs: true,
aggregatable: true,
loading: false,
},
{
fieldName: 'responsetime',
type: ML_JOB_FIELD_TYPES.NUMBER,
existsInDocs: true,
aggregatable: true,
loading: false,
},
],
nonMetricCards: [
{
fieldName: '@timestamp',
type: ML_JOB_FIELD_TYPES.DATE,
existsInDocs: true,
aggregatable: true,
loading: false,
},
{
fieldName: '@version',
type: ML_JOB_FIELD_TYPES.TEXT,
existsInDocs: true,
aggregatable: false,
loading: false,
},
{
fieldName: '@version.keyword',
type: ML_JOB_FIELD_TYPES.KEYWORD,
existsInDocs: true,
aggregatable: true,
loading: false,
},
{
fieldName: 'airline',
type: ML_JOB_FIELD_TYPES.KEYWORD,
existsInDocs: true,
aggregatable: true,
loading: false,
},
{
fieldName: 'type',
type: ML_JOB_FIELD_TYPES.TEXT,
existsInDocs: true,
aggregatable: false,
loading: false,
},
{
fieldName: 'type.keyword',
type: ML_JOB_FIELD_TYPES.KEYWORD,
existsInDocs: true,
aggregatable: true,
loading: false,
},
],
nonMetricFieldsTypeFilterCardCount: 3,
metricFieldsFilterCardCount: 1,
nonMetricFieldsFilterCardCount: 1,
},
const farequoteIndexPatternTestData: TestData = {
suiteTitle: 'index pattern',
sourceIndexOrSavedSearch: 'farequote',
advancedJobWizardDatafeedQuery: `{
"bool": {
"must": [
{
"match_all": {}
}
]
}
}`,
metricFieldsFilter: 'document',
nonMetricFieldsFilter: 'airline',
nonMetricFieldsTypeFilter: 'keyword',
expected: {
totalDocCount: 86274,
fieldsPanelCount: 2, // Metrics panel and Fields panel
metricCards: [
{
type: ML_JOB_FIELD_TYPES.NUMBER, // document count card
existsInDocs: true,
aggregatable: true,
loading: false,
},
{
fieldName: 'responsetime',
type: ML_JOB_FIELD_TYPES.NUMBER,
existsInDocs: true,
aggregatable: true,
loading: false,
},
],
nonMetricCards: [
{
fieldName: '@timestamp',
type: ML_JOB_FIELD_TYPES.DATE,
existsInDocs: true,
aggregatable: true,
loading: false,
},
{
fieldName: '@version',
type: ML_JOB_FIELD_TYPES.TEXT,
existsInDocs: true,
aggregatable: false,
loading: false,
},
{
fieldName: '@version.keyword',
type: ML_JOB_FIELD_TYPES.KEYWORD,
existsInDocs: true,
aggregatable: true,
loading: false,
},
{
fieldName: 'airline',
type: ML_JOB_FIELD_TYPES.KEYWORD,
existsInDocs: true,
aggregatable: true,
loading: false,
},
{
fieldName: 'type',
type: ML_JOB_FIELD_TYPES.TEXT,
existsInDocs: true,
aggregatable: false,
loading: false,
},
{
fieldName: 'type.keyword',
type: ML_JOB_FIELD_TYPES.KEYWORD,
existsInDocs: true,
aggregatable: true,
loading: false,
},
],
nonMetricFieldsTypeFilterCardCount: 3,
metricFieldsFilterCardCount: 1,
nonMetricFieldsFilterCardCount: 1,
},
{
suiteTitle: 'with lucene query on farequote index',
sourceIndexOrSavedSearch: 'farequote_lucene',
metricFieldsFilter: 'responsetime',
nonMetricFieldsFilter: 'version',
nonMetricFieldsTypeFilter: 'keyword',
expected: {
totalDocCount: 34416,
fieldsPanelCount: 2, // Metrics panel and Fields panel
metricCards: [
{
type: ML_JOB_FIELD_TYPES.NUMBER, // document count card
existsInDocs: true,
aggregatable: true,
loading: false,
},
{
fieldName: 'responsetime',
type: ML_JOB_FIELD_TYPES.NUMBER,
existsInDocs: true,
aggregatable: true,
loading: false,
},
],
nonMetricCards: [
{
fieldName: '@timestamp',
type: ML_JOB_FIELD_TYPES.DATE,
existsInDocs: true,
aggregatable: true,
loading: false,
},
{
fieldName: '@version',
type: ML_JOB_FIELD_TYPES.TEXT,
existsInDocs: true,
aggregatable: false,
loading: false,
},
{
fieldName: '@version.keyword',
type: ML_JOB_FIELD_TYPES.KEYWORD,
existsInDocs: true,
aggregatable: true,
loading: false,
},
{
fieldName: 'airline',
type: ML_JOB_FIELD_TYPES.KEYWORD,
existsInDocs: true,
aggregatable: true,
loading: false,
},
{
fieldName: 'type',
type: ML_JOB_FIELD_TYPES.TEXT,
existsInDocs: true,
aggregatable: false,
loading: false,
},
{
fieldName: 'type.keyword',
type: ML_JOB_FIELD_TYPES.KEYWORD,
existsInDocs: true,
aggregatable: true,
loading: false,
},
],
nonMetricFieldsTypeFilterCardCount: 3,
metricFieldsFilterCardCount: 2,
nonMetricFieldsFilterCardCount: 1,
},
};
const farequoteKQLSearchTestData: TestData = {
suiteTitle: 'KQL saved search',
sourceIndexOrSavedSearch: 'farequote_kuery',
advancedJobWizardDatafeedQuery: `{
"bool": {
"must": [
{
"match_all": {}
}
]
}
}`, // Note query is not currently passed to the wizard
metricFieldsFilter: 'responsetime',
nonMetricFieldsFilter: 'airline',
nonMetricFieldsTypeFilter: 'keyword',
expected: {
totalDocCount: 34415,
fieldsPanelCount: 2, // Metrics panel and Fields panel
metricCards: [
{
type: ML_JOB_FIELD_TYPES.NUMBER, // document count card
existsInDocs: true,
aggregatable: true,
loading: false,
},
{
fieldName: 'responsetime',
type: ML_JOB_FIELD_TYPES.NUMBER,
existsInDocs: true,
aggregatable: true,
loading: false,
},
],
nonMetricCards: [
{
fieldName: '@timestamp',
type: ML_JOB_FIELD_TYPES.DATE,
existsInDocs: true,
aggregatable: true,
loading: false,
},
{
fieldName: '@version',
type: ML_JOB_FIELD_TYPES.TEXT,
existsInDocs: true,
aggregatable: false,
loading: false,
},
{
fieldName: '@version.keyword',
type: ML_JOB_FIELD_TYPES.KEYWORD,
existsInDocs: true,
aggregatable: true,
loading: false,
},
{
fieldName: 'airline',
type: ML_JOB_FIELD_TYPES.KEYWORD,
existsInDocs: true,
aggregatable: true,
loading: false,
},
{
fieldName: 'type',
type: ML_JOB_FIELD_TYPES.TEXT,
existsInDocs: true,
aggregatable: false,
loading: false,
},
{
fieldName: 'type.keyword',
type: ML_JOB_FIELD_TYPES.KEYWORD,
existsInDocs: true,
aggregatable: true,
loading: false,
},
],
nonMetricFieldsTypeFilterCardCount: 3,
metricFieldsFilterCardCount: 2,
nonMetricFieldsFilterCardCount: 1,
},
];
};
const farequoteLuceneSearchTestData: TestData = {
suiteTitle: 'lucene saved search',
sourceIndexOrSavedSearch: 'farequote_lucene',
advancedJobWizardDatafeedQuery: `{
"bool": {
"must": [
{
"match_all": {}
}
]
}
}`, // Note query is not currently passed to the wizard
metricFieldsFilter: 'responsetime',
nonMetricFieldsFilter: 'version',
nonMetricFieldsTypeFilter: 'keyword',
expected: {
totalDocCount: 34416,
fieldsPanelCount: 2, // Metrics panel and Fields panel
metricCards: [
{
type: ML_JOB_FIELD_TYPES.NUMBER, // document count card
existsInDocs: true,
aggregatable: true,
loading: false,
},
{
fieldName: 'responsetime',
type: ML_JOB_FIELD_TYPES.NUMBER,
existsInDocs: true,
aggregatable: true,
loading: false,
},
],
nonMetricCards: [
{
fieldName: '@timestamp',
type: ML_JOB_FIELD_TYPES.DATE,
existsInDocs: true,
aggregatable: true,
loading: false,
},
{
fieldName: '@version',
type: ML_JOB_FIELD_TYPES.TEXT,
existsInDocs: true,
aggregatable: false,
loading: false,
},
{
fieldName: '@version.keyword',
type: ML_JOB_FIELD_TYPES.KEYWORD,
existsInDocs: true,
aggregatable: true,
loading: false,
},
{
fieldName: 'airline',
type: ML_JOB_FIELD_TYPES.KEYWORD,
existsInDocs: true,
aggregatable: true,
loading: false,
},
{
fieldName: 'type',
type: ML_JOB_FIELD_TYPES.TEXT,
existsInDocs: true,
aggregatable: false,
loading: false,
},
{
fieldName: 'type.keyword',
type: ML_JOB_FIELD_TYPES.KEYWORD,
existsInDocs: true,
aggregatable: true,
loading: false,
},
],
nonMetricFieldsTypeFilterCardCount: 3,
metricFieldsFilterCardCount: 2,
nonMetricFieldsFilterCardCount: 1,
},
};
function runTests(testData: TestData) {
it(`${testData.suiteTitle} loads the saved search selection page`, async () => {
await ml.dataVisualizer.navigateToIndexPatternSelection();
});
it(`${testData.suiteTitle} loads the index data visualizer page`, async () => {
await ml.jobSourceSelection.selectSourceForIndexBasedDataVisualizer(
testData.sourceIndexOrSavedSearch
);
});
it(`${testData.suiteTitle} displays the time range step`, async () => {
await ml.dataVisualizerIndexBased.assertTimeRangeSelectorSectionExists();
});
it(`${testData.suiteTitle} loads data for full time range`, async () => {
await ml.dataVisualizerIndexBased.clickUseFullDataButton(testData.expected.totalDocCount);
});
it(`${testData.suiteTitle} displays the panels of fields`, async () => {
await ml.dataVisualizerIndexBased.assertFieldsPanelsExist(testData.expected.fieldsPanelCount);
});
if (testData.expected.metricCards !== undefined && testData.expected.metricCards.length > 0) {
it(`${testData.suiteTitle} displays the Metrics panel`, async () => {
await ml.dataVisualizerIndexBased.assertFieldsPanelForTypesExist([
ML_JOB_FIELD_TYPES.NUMBER,
]); // document_count not exposed as a type in the panel
});
it(`${testData.suiteTitle} displays the expected metric field cards`, async () => {
for (const fieldCard of testData.expected.metricCards as FieldVisConfig[]) {
await ml.dataVisualizerIndexBased.assertCardExists(fieldCard.type, fieldCard.fieldName);
}
});
it(`${testData.suiteTitle} filters metric fields cards with search`, async () => {
await ml.dataVisualizerIndexBased.filterFieldsPanelWithSearchString(
['number'],
testData.metricFieldsFilter,
testData.expected.metricFieldsFilterCardCount
);
});
}
if (
testData.expected.nonMetricCards !== undefined &&
testData.expected.nonMetricCards.length > 0
) {
it(`${testData.suiteTitle} displays the non-metric Fields panel`, async () => {
await ml.dataVisualizerIndexBased.assertFieldsPanelForTypesExist(
getFieldTypes(testData.expected.nonMetricCards as FieldVisConfig[])
);
});
it(`${testData.suiteTitle} displays the expected non-metric field cards`, async () => {
for (const fieldCard of testData.expected.nonMetricCards!) {
await ml.dataVisualizerIndexBased.assertCardExists(fieldCard.type, fieldCard.fieldName);
}
});
it(`${testData.suiteTitle} sets the non metric field types input`, async () => {
const fieldTypes: ML_JOB_FIELD_TYPES[] = getFieldTypes(
testData.expected.nonMetricCards as FieldVisConfig[]
);
await ml.dataVisualizerIndexBased.assertFieldsPanelTypeInputExists(fieldTypes);
await ml.dataVisualizerIndexBased.setFieldsPanelTypeInputValue(
fieldTypes,
testData.nonMetricFieldsTypeFilter,
testData.expected.nonMetricFieldsTypeFilterCardCount
);
});
it(`${testData.suiteTitle} filters non-metric fields cards with search`, async () => {
await ml.dataVisualizerIndexBased.filterFieldsPanelWithSearchString(
getFieldTypes(testData.expected.nonMetricCards as FieldVisConfig[]),
testData.nonMetricFieldsFilter,
testData.expected.nonMetricFieldsFilterCardCount
);
});
}
}
describe('index based', function() {
this.tags(['smoke', 'mlqa']);
@ -187,101 +388,45 @@ export default function({ getService }: FtrProviderContext) {
// TODO - add tests for
// - validating metrics displayed inside the cards
// - selecting a document sample size
// - clicking on the link to the Advanced job wizard
// - a test suite using a KQL based saved search
for (const testData of testDataList) {
describe(`${testData.suiteTitle}`, function() {
it('loads the data visualizer selector page', async () => {
await ml.navigation.navigateToMl();
await ml.navigation.navigateToDataVisualizer();
});
it('loads the saved search selection page', async () => {
await ml.dataVisualizer.navigateToIndexPatternSelection();
});
it('loads the index data visualizer page', async () => {
await ml.jobSourceSelection.selectSourceForIndexBasedDataVisualizer(
testData.sourceIndexOrSavedSearch
);
});
it('displays the time range step', async () => {
await ml.dataVisualizerIndexBased.assertTimeRangeSelectorSectionExists();
});
it('loads data for full time range', async () => {
await ml.dataVisualizerIndexBased.clickUseFullDataButton(testData.expected.totalDocCount);
});
it('displays the panels of fields', async () => {
await ml.dataVisualizerIndexBased.assertFieldsPanelsExist(
testData.expected.fieldsPanelCount
);
});
if (testData.expected.metricCards && testData.expected.metricCards.length > 0) {
it('displays the Metrics panel', async () => {
await ml.dataVisualizerIndexBased.assertFieldsPanelForTypesExist([
ML_JOB_FIELD_TYPES.NUMBER,
]); // document_count not exposed as a type in the panel
});
it('displays the expected metric field cards', async () => {
for (const fieldCard of testData.expected.metricCards) {
await ml.dataVisualizerIndexBased.assertCardExists(
fieldCard.type,
fieldCard.fieldName
);
}
});
it('filters metric fields cards with search', async () => {
await ml.dataVisualizerIndexBased.filterFieldsPanelWithSearchString(
['number'],
testData.metricFieldsFilter,
testData.expected.metricFieldsFilterCardCount
);
});
}
if (testData.expected.nonMetricCards && testData.expected.nonMetricCards.length > 0) {
it('displays the non-metric Fields panel', async () => {
await ml.dataVisualizerIndexBased.assertFieldsPanelForTypesExist(
getFieldTypes(testData.expected.nonMetricCards)
);
});
it('displays the expected non-metric field cards', async () => {
for (const fieldCard of testData.expected.nonMetricCards) {
await ml.dataVisualizerIndexBased.assertCardExists(
fieldCard.type,
fieldCard.fieldName
);
}
});
it('sets the non metric field types input', async () => {
const fieldTypes: ML_JOB_FIELD_TYPES[] = getFieldTypes(
testData.expected.nonMetricCards
);
await ml.dataVisualizerIndexBased.assertFieldsPanelTypeInputExists(fieldTypes);
await ml.dataVisualizerIndexBased.setFieldsPanelTypeInputValue(
fieldTypes,
testData.nonMetricFieldsTypeFilter,
testData.expected.nonMetricFieldsTypeFilterCardCount
);
});
it('filters non-metric fields cards with search', async () => {
await ml.dataVisualizerIndexBased.filterFieldsPanelWithSearchString(
getFieldTypes(testData.expected.nonMetricCards),
testData.nonMetricFieldsFilter,
testData.expected.nonMetricFieldsFilterCardCount
);
});
}
describe('with farequote', function() {
// Run tests on full farequote index.
it(`${farequoteIndexPatternTestData.suiteTitle} loads the data visualizer selector page`, async () => {
// Start navigation from the base of the ML app.
await ml.navigation.navigateToMl();
await ml.navigation.navigateToDataVisualizer();
});
}
runTests(farequoteIndexPatternTestData);
// Run tests on farequote KQL saved search.
it(`${farequoteKQLSearchTestData.suiteTitle} loads the data visualizer selector page`, async () => {
// Only navigate back to the data visualizer selector page before running next tests,
// to ensure the time picker isn't set back to the default (last 15 minutes).
await ml.navigation.navigateToDataVisualizer();
});
runTests(farequoteKQLSearchTestData);
// Run tests on farequote lucene saved search.
it(`${farequoteLuceneSearchTestData.suiteTitle} loads the data visualizer selector page`, async () => {
// Only navigate back to the data visualizer selector page before running next tests,
// to ensure the time picker isn't set back to the default (last 15 minutes).
await ml.navigation.navigateToDataVisualizer();
});
runTests(farequoteLuceneSearchTestData);
// Test the Create advanced job button.
// Note the search is not currently passed to the wizard, just the index.
it(`${farequoteLuceneSearchTestData.suiteTitle} opens the advanced job wizard`, async () => {
await ml.dataVisualizerIndexBased.clickCreateAdvancedJobButton();
await ml.jobTypeSelection.assertAdvancedJobWizardOpen();
await ml.jobWizardAdvanced.assertDatafeedQueryEditorExists();
await ml.jobWizardAdvanced.assertDatafeedQueryEditorValue(
farequoteLuceneSearchTestData.advancedJobWizardDatafeedQuery
);
});
});
});
}

View file

@ -103,5 +103,9 @@ export function MachineLearningDataVisualizerIndexBasedProvider({
await this.assertFieldsPanelTypeInputValue(filterFieldType);
await this.assertFieldsPanelCardCount(panelFieldTypes, expectedCardCount);
},
async clickCreateAdvancedJobButton() {
await testSubjects.clickWhenNotDisabled('mlDataVisualizerCreateAdvancedJobCard');
},
};
}