From 4880ccdb85643504ac72cb55ce224aa55412a260 Mon Sep 17 00:00:00 2001 From: Quynh Nguyen Date: Tue, 15 Dec 2020 14:38:07 -0600 Subject: [PATCH 1/9] [ML] Rename subjects, remove documentCountCard, add ensureDetailsClosed --- .../examples_list/examples_list.tsx | 2 +- .../field_data_card/top_values/top_values.tsx | 2 +- .../stats_datagrid/expanded_row.tsx | 2 +- .../stats_datagrid/stats_datagrid.tsx | 2 +- .../data_visualizer/index_data_visualizer.ts | 37 +-- .../ml/data_visualizer_index_based.ts | 252 +----------------- .../services/ml/data_visualizer_table.ts | 90 ++++--- 7 files changed, 80 insertions(+), 307 deletions(-) diff --git a/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/examples_list/examples_list.tsx b/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/examples_list/examples_list.tsx index 1b962b46bd62d..cbb1ceb16c7a9 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/examples_list/examples_list.tsx +++ b/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/examples_list/examples_list.tsx @@ -36,7 +36,7 @@ export const ExamplesList: FC = ({ examples }) => { }); return ( -
+
= ({ stats, fieldFormat, barColor, compressed } = stats; const progressBarMax = isTopValuesSampled === true ? topValuesSampleSize : count; return ( -
+
{Array.isArray(topValues) && topValues.map((value: any) => ( diff --git a/x-pack/plugins/ml/public/application/datavisualizer/stats_datagrid/expanded_row.tsx b/x-pack/plugins/ml/public/application/datavisualizer/stats_datagrid/expanded_row.tsx index fe6e0b6ec9a8a..cb83d6db83ed3 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/stats_datagrid/expanded_row.tsx +++ b/x-pack/plugins/ml/public/application/datavisualizer/stats_datagrid/expanded_row.tsx @@ -60,7 +60,7 @@ export const DataVisualizerFieldExpandedRow = ({ item }: { item: FieldVisConfig return (
{loading === true ? : getCardContent()}
diff --git a/x-pack/plugins/ml/public/application/datavisualizer/stats_datagrid/stats_datagrid.tsx b/x-pack/plugins/ml/public/application/datavisualizer/stats_datagrid/stats_datagrid.tsx index be85d7ad01596..3cd7851175a85 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/stats_datagrid/stats_datagrid.tsx +++ b/x-pack/plugins/ml/public/application/datavisualizer/stats_datagrid/stats_datagrid.tsx @@ -112,7 +112,7 @@ export const DataVisualizerDataGrid = ({ const direction = expandedRowItemIds.includes(item.fieldName) ? 'arrowUp' : 'arrowDown'; return ( toggleDetails(item)} aria-label={ expandedRowItemIds.includes(item.fieldName) diff --git a/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer.ts b/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer.ts index 4b793c4c3adba..13dcf5d59c135 100644 --- a/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer.ts +++ b/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer.ts @@ -28,7 +28,6 @@ interface TestData { expected: { totalDocCountFormatted: string; fieldsPanelCount: number; - documentCountCard: FieldVisConfig; metricCards?: MetricFieldVisConfig[]; nonMetricCards?: NonMetricFieldVisConfig[]; nonMetricFieldsTypeFilterCardCount: number; @@ -54,12 +53,6 @@ export default function ({ getService }: FtrProviderContext) { expected: { totalDocCountFormatted: '86,274', fieldsPanelCount: 2, // Metrics panel and Fields panel - documentCountCard: { - type: ML_JOB_FIELD_TYPES.NUMBER, - existsInDocs: true, - aggregatable: true, - loading: false, - }, metricCards: [ { fieldName: 'responsetime', @@ -146,12 +139,6 @@ export default function ({ getService }: FtrProviderContext) { expected: { totalDocCountFormatted: '34,415', fieldsPanelCount: 2, // Metrics panel and Fields panel - documentCountCard: { - type: ML_JOB_FIELD_TYPES.NUMBER, - existsInDocs: true, - aggregatable: true, - loading: false, - }, metricCards: [ { fieldName: 'responsetime', @@ -238,12 +225,6 @@ export default function ({ getService }: FtrProviderContext) { expected: { totalDocCountFormatted: '34,416', fieldsPanelCount: 2, // Metrics panel and Fields panel - documentCountCard: { - type: ML_JOB_FIELD_TYPES.NUMBER, // document count card - existsInDocs: true, - aggregatable: true, - loading: false, - }, metricCards: [ { fieldName: 'responsetime', @@ -345,17 +326,23 @@ export default function ({ getService }: FtrProviderContext) { testData.expected.totalDocCountFormatted ); - await ml.testExecution.logTestStep(`${testData.suiteTitle} displays the doc count`); + await ml.testExecution.logTestStep( + `${testData.suiteTitle} displays elements in the doc count panel correctly` + ); await ml.dataVisualizerIndexBased.assertTotalDocCountHeaderExist(); await ml.dataVisualizerIndexBased.assertTotalDocCountChartExist(); - await ml.testExecution.logTestStep(`${testData.suiteTitle} displays the search panel count`); + await ml.testExecution.logTestStep( + `${testData.suiteTitle} displays elements in the search panel correctly` + ); await ml.dataVisualizerIndexBased.assertSearchPanelExist(); await ml.dataVisualizerIndexBased.assertSampleSizeInputExists(); await ml.dataVisualizerIndexBased.assertFieldTypeInputExists(); await ml.dataVisualizerIndexBased.assertFieldNameInputExists(); - await ml.testExecution.logTestStep(`${testData.suiteTitle} displays the field count`); + await ml.testExecution.logTestStep( + `${testData.suiteTitle} displays elements in the field count panel correctly` + ); await ml.dataVisualizerIndexBased.assertFieldCountPanelExist(); await ml.dataVisualizerIndexBased.assertMetricFieldsSummaryExist(); await ml.dataVisualizerIndexBased.assertFieldsSummaryExist(); @@ -377,12 +364,12 @@ export default function ({ getService }: FtrProviderContext) { await ml.dataVisualizerIndexBased.assertDataVisualizerTableExist(); await ml.testExecution.logTestStep( - 'displays details for the created job in the analytics table' + 'displays details for metric fields and non-metric fields correctly' ); for (const fieldCard of testData.expected.metricCards as Array< Required >) { - await ml.dataVisualizerTable.assertNumberRowContents( + await ml.dataVisualizerTable.assertNumberFieldContents( fieldCard.fieldName, fieldCard.docCountFormatted ); @@ -395,7 +382,7 @@ export default function ({ getService }: FtrProviderContext) { } for (const fieldCard of testData.expected.nonMetricCards!) { - await ml.dataVisualizerTable.assertNonMetricCardContents( + await ml.dataVisualizerTable.assertNonMetricFieldContents( fieldCard.type, fieldCard.fieldName!, fieldCard.docCountFormatted diff --git a/x-pack/test/functional/services/ml/data_visualizer_index_based.ts b/x-pack/test/functional/services/ml/data_visualizer_index_based.ts index 413f4565ef30d..16e9744a24911 100644 --- a/x-pack/test/functional/services/ml/data_visualizer_index_based.ts +++ b/x-pack/test/functional/services/ml/data_visualizer_index_based.ts @@ -122,251 +122,6 @@ export function MachineLearningDataVisualizerIndexBasedProvider( await testSubjects.existOrFail(`mlDataVisualizerTable`); }, - async assertFieldsPanelForTypesExist(fieldTypes: MlJobFieldType[]) { - await testSubjects.existOrFail(`mlDataVisualizerFieldsPanel ${fieldTypes}`); - }, - - async assertCardExists(cardType: string, fieldName?: string) { - await testSubjects.existOrFail(`mlFieldDataCard ${fieldName} ${cardType}`); - }, - - async assertCardContentsExists(cardType: string, fieldName?: string) { - await testSubjects.existOrFail( - `mlFieldDataCard ${fieldName} ${cardType} > mlFieldDataCardContent` - ); - }, - - async assertNonMetricCardContents(cardType: string, fieldName: string, exampleCount?: number) { - await this.assertCardContentsExists(cardType, fieldName); - - // Currently the data used in the data visualizer tests only contains these field types. - if (cardType === ML_JOB_FIELD_TYPES.DATE) { - await this.assertDateCardContents(fieldName); - } else if (cardType === ML_JOB_FIELD_TYPES.KEYWORD) { - await this.assertKeywordCardContents(fieldName, exampleCount!); - } else if (cardType === ML_JOB_FIELD_TYPES.TEXT) { - await this.assertTextCardContents(fieldName, exampleCount!); - } - }, - - async assertDocumentCountCardContents() { - await this.assertCardContentsExists('number', undefined); - await testSubjects.existOrFail( - 'mlFieldDataCard undefined number > mlFieldDataCardDocumentCountChart' - ); - }, - - async assertNumberCardContents( - fieldName: string, - docCountFormatted: string, - statsMaxDecimalPlaces: number, - selectedDetailsMode: 'distribution' | 'top_values', - topValuesCount: number - ) { - await this.assertCardContentsExists('number', fieldName); - await this.assertFieldDocCountExists('number', fieldName); - await this.assertFieldDocCountContents('number', fieldName, docCountFormatted); - await this.assertFieldCardinalityExists('number', fieldName); - - await this.assertNumberStatsContents(fieldName, 'Min', statsMaxDecimalPlaces); - await this.assertNumberStatsContents(fieldName, 'Median', statsMaxDecimalPlaces); - await this.assertNumberStatsContents(fieldName, 'Max', statsMaxDecimalPlaces); - - await testSubjects.existOrFail( - `mlFieldDataCard ${fieldName} number > mlFieldDataCardDetailsSelect` - ); - - if (selectedDetailsMode === 'distribution') { - await mlCommonUI.assertRadioGroupValue( - `mlFieldDataCard ${fieldName} number > mlFieldDataCardDetailsSelect`, - 'distribution' - ); - await testSubjects.existOrFail( - `mlFieldDataCard ${fieldName} number > mlFieldDataCardMetricDistributionChart` - ); - - await mlCommonUI.selectRadioGroupValue( - `mlFieldDataCard ${fieldName} number > mlFieldDataCardDetailsSelect`, - 'top_values' - ); - await this.assertTopValuesContents('number', fieldName, topValuesCount); - } else { - await mlCommonUI.assertRadioGroupValue( - `mlFieldDataCard ${fieldName} number > mlFieldDataCardDetailsSelect`, - 'top_values' - ); - await this.assertTopValuesContents('number', fieldName, topValuesCount); - - await mlCommonUI.selectRadioGroupValue( - `mlFieldDataCard ${fieldName} number > mlFieldDataCardDetailsSelect`, - 'distribution' - ); - await testSubjects.existOrFail( - `mlFieldDataCard ${fieldName} number > mlFieldDataCardMetricDistributionChart` - ); - } - }, - - async assertDateCardContents(fieldName: string) { - await this.assertFieldDocCountExists('date', fieldName); - await testSubjects.existOrFail(`mlFieldDataCard ${fieldName} date > mlFieldDataCardEarliest`); - await testSubjects.existOrFail(`mlFieldDataCard ${fieldName} date > mlFieldDataCardLatest`); - }, - - async assertKeywordCardContents(fieldName: string, expectedTopValuesCount: number) { - await this.assertFieldDocCountExists('keyword', fieldName); - await this.assertFieldCardinalityExists('keyword', fieldName); - await this.assertTopValuesContents('keyword', fieldName, expectedTopValuesCount); - }, - - async assertTextCardContents(fieldName: string, expectedExamplesCount: number) { - const examplesList = await testSubjects.find( - `mlFieldDataCard ${fieldName} text > mlFieldDataCardExamplesList` - ); - const examplesListItems = await examplesList.findAllByTagName('li'); - expect(examplesListItems).to.have.length( - expectedExamplesCount, - `Expected example list item count for field '${fieldName}' to be '${expectedExamplesCount}' (got '${examplesListItems.length}')` - ); - }, - - async assertFieldDocCountExists(cardType: string, fieldName: string) { - await testSubjects.existOrFail( - `mlFieldDataCard ${fieldName} ${cardType} > mlFieldDataCardDocCount` - ); - }, - - async assertFieldDocCountContents( - cardType: string, - fieldName: string, - docCountFormatted: string - ) { - const docCountText = await testSubjects.getVisibleText( - `mlFieldDataCard ${fieldName} ${cardType} > mlFieldDataCardDocCount` - ); - expect(docCountText).to.contain( - docCountFormatted, - `Expected doc count for '${fieldName}' to be '${docCountFormatted}' (got contents '${docCountText}')` - ); - }, - - async assertFieldCardinalityExists(cardType: string, fieldName: string) { - await testSubjects.existOrFail( - `mlFieldDataCard ${fieldName} ${cardType} > mlFieldDataCardCardinality` - ); - }, - - async assertNumberStatsContents( - fieldName: string, - stat: 'Min' | 'Median' | 'Max', - maxDecimalPlaces: number - ) { - const statElement = await testSubjects.find( - `mlFieldDataCard ${fieldName} number > mlFieldDataCard${stat}` - ); - const statValue = await statElement.getVisibleText(); - const dotIdx = statValue.indexOf('.'); - const numDecimalPlaces = dotIdx === -1 ? 0 : statValue.length - dotIdx - 1; - expect(numDecimalPlaces).to.be.lessThan( - maxDecimalPlaces + 1, - `Expected number of decimal places for '${fieldName}' '${stat}' to be less than or equal to '${maxDecimalPlaces}' (got '${numDecimalPlaces}')` - ); - }, - - async assertTopValuesContents( - cardType: string, - fieldName: string, - expectedTopValuesCount: number - ) { - const topValuesElement = await testSubjects.find( - `mlFieldDataCard ${fieldName} ${cardType} > mlFieldDataCardTopValues` - ); - const topValuesBars = await topValuesElement.findAllByTestSubject( - 'mlFieldDataCardTopValueBar' - ); - expect(topValuesBars).to.have.length( - expectedTopValuesCount, - `Expected top values count for field '${fieldName}' to be '${expectedTopValuesCount}' (got '${topValuesBars.length}')` - ); - }, - - async assertFieldsPanelCardCount(panelFieldTypes: string[], expectedCardCount: number) { - await retry.tryForTime(5000, async () => { - const filteredCards = await testSubjects.findAll( - `mlDataVisualizerFieldsPanel ${panelFieldTypes} > ~mlFieldDataCard` - ); - expect(filteredCards).to.have.length( - expectedCardCount, - `Expected field card count for panels '${panelFieldTypes}' to be '${expectedCardCount}' (got '${filteredCards.length}')` - ); - }); - }, - - async assertFieldsPanelSearchInputValue(fieldTypes: string[], expectedSearchValue: string) { - const searchBar = await testSubjects.find( - `mlDataVisualizerFieldsPanel ${fieldTypes} > mlDataVisualizerFieldsSearchBarDiv` - ); - const searchBarInput = await searchBar.findByTagName('input'); - const actualSearchValue = await searchBarInput.getAttribute('value'); - expect(actualSearchValue).to.eql( - expectedSearchValue, - `Expected search value for field types '${fieldTypes}' to be '${expectedSearchValue}' (got '${actualSearchValue}')` - ); - }, - - async clearFieldsPanelSearchInput(fieldTypes: string[]) { - const searchBar = await testSubjects.find( - `mlDataVisualizerFieldsPanel ${fieldTypes} > mlDataVisualizerFieldsSearchBarDiv` - ); - const searchBarInput = await searchBar.findByTagName('input'); - await searchBarInput.clearValueWithKeyboard(); - await searchBarInput.pressKeys(browser.keys.ENTER); - }, - - async filterFieldsPanelWithSearchString( - fieldTypes: string[], - filter: string, - expectedCardCount: number - ) { - const searchBar = await testSubjects.find( - `mlDataVisualizerFieldsPanel ${fieldTypes} > mlDataVisualizerFieldsSearchBarDiv` - ); - const searchBarInput = await searchBar.findByTagName('input'); - await searchBarInput.clearValueWithKeyboard(); - await searchBarInput.type(filter); - await searchBarInput.pressKeys(browser.keys.ENTER); - await this.assertFieldsPanelSearchInputValue(fieldTypes, filter); - - await this.assertFieldsPanelCardCount(fieldTypes, expectedCardCount); - }, - - async assertFieldsPanelTypeInputExists(panelFieldTypes: string[]) { - await testSubjects.existOrFail( - `mlDataVisualizerFieldsPanel ${panelFieldTypes} > mlDataVisualizerFieldTypesSelect` - ); - }, - - async assertFieldsPanelTypeInputValue(expectedTypeValue: string) { - const actualTypeValue = await testSubjects.getAttribute( - 'mlDataVisualizerFieldTypesSelect', - 'value' - ); - expect(actualTypeValue).to.eql( - expectedTypeValue, - `Expected fields panel type value to be '${expectedTypeValue}' (got '${actualTypeValue}')` - ); - }, - - async setFieldsPanelTypeInputValue( - panelFieldTypes: string[], - filterFieldType: string, - expectedCardCount: number - ) { - await testSubjects.selectValue('mlDataVisualizerFieldTypesSelect', filterFieldType); - await this.assertFieldsPanelTypeInputValue(filterFieldType); - await this.assertFieldsPanelCardCount(panelFieldTypes, expectedCardCount); - }, - async assertShowEmptyFieldsSwitchExists() { await testSubjects.existOrFail('mlDataVisualizerShowEmptyFieldsSwitch'); }, @@ -393,9 +148,10 @@ export function MachineLearningDataVisualizerIndexBasedProvider( await testSubjects.existOrFail(`mlDataVisualizerShardSizeOption ${sampleSize}`); await testSubjects.click(`mlDataVisualizerShardSizeOption ${sampleSize}`); - await retry.tryForTime(5000, async () => { - await this.assertFieldDocCountContents(cardType, fieldName, docCountFormatted); - }); + // TODO: update + // await retry.tryForTime(5000, async () => { + // await this.assertFieldDocCountContents(cardType, fieldName, docCountFormatted); + // }); }, async assertActionsPanelExists() { diff --git a/x-pack/test/functional/services/ml/data_visualizer_table.ts b/x-pack/test/functional/services/ml/data_visualizer_table.ts index 94f7b618af998..30117f54bf771 100644 --- a/x-pack/test/functional/services/ml/data_visualizer_table.ts +++ b/x-pack/test/functional/services/ml/data_visualizer_table.ts @@ -61,21 +61,53 @@ export function MachineLearningDataVisualizerTableProvider({ getService }: FtrPr await testSubjects.existOrFail(this.rowSelector(fieldName)); } - public async expandRowDetails(fieldName: string, fieldType: string) { + public detailsSelector(fieldName: string, subSelector?: string) { + const row = `~mlDataVisualizerTable > ~mlDataVisualizerFieldExpandedRow-${fieldName}`; + return !subSelector ? row : `${row} > ${subSelector}`; + } + + public async ensureDetailsOpen(fieldName: string) { + await retry.tryForTime(10000, async () => { + if (!(await testSubjects.exists(this.detailsSelector(fieldName)))) { + await testSubjects.click(this.rowSelector(fieldName, 'mlDataVisualizerDetailsToggle')); + await testSubjects.existOrFail( + this.rowSelector(fieldName, `mlDataVisualizerDetailsToggle-${fieldName}-arrowUp`), + { + timeout: 1000, + } + ); + await testSubjects.existOrFail(this.detailsSelector(fieldName), { timeout: 1000 }); + } + }); + } + + public async ensureDetailsClosed(fieldName: string) { + await retry.tryForTime(10000, async () => { + if (await testSubjects.exists(this.detailsSelector(fieldName))) { + await testSubjects.click(this.rowSelector(fieldName, 'mlDataVisualizerDetailsToggle')); + await testSubjects.existOrFail( + this.rowSelector(fieldName, `mlDataVisualizerDetailsToggle-${fieldName}-arrowDown`), + { + timeout: 1000, + } + ); + await testSubjects.missingOrFail(this.detailsSelector(fieldName), { timeout: 1000 }); + } + }); + } + + public async openDetails(fieldName: string) { + await this.ensureDetailsClosed(fieldName); + const selector = this.rowSelector( fieldName, - `mlDataVisualizerToggleDetails ${fieldName} arrowDown` + `mlDataVisualizerDetailsToggle-${fieldName}-arrowDown` ); await testSubjects.existOrFail(selector); await testSubjects.click(selector); - await retry.tryForTime(5000, async () => { - await testSubjects.existOrFail( - this.rowSelector(fieldName, `mlDataVisualizerToggleDetails ${fieldName} arrowUp`) - ); - await testSubjects.existOrFail( - `mlDataVisualizerFieldExpandedRow ${fieldName} ${fieldType}` - ); - }); + await this.ensureDetailsOpen(fieldName); + + await retry.tryForTime(5000, async () => {}); } public async assertFieldDocCount(fieldName: string, docCountFormatted: string) { @@ -91,73 +123,71 @@ export function MachineLearningDataVisualizerTableProvider({ getService }: FtrPr ); } - public async assertNumberRowContents(fieldName: string, docCountFormatted: string) { + public async assertNumberFieldContents(fieldName: string, docCountFormatted: string) { const fieldType = ML_JOB_FIELD_TYPES.NUMBER; await this.assertRowExists(fieldName); await this.assertFieldDocCount(fieldName, docCountFormatted); - await this.expandRowDetails(fieldName, fieldType); + await this.openDetails(fieldName, fieldType); await testSubjects.existOrFail( - `mlDataVisualizerFieldExpandedRow ${fieldName} ${fieldType} > mlNumberSummaryTable` + `mlDataVisualizerFieldExpandedRow-${fieldName} > mlNumberSummaryTable` ); + await testSubjects.existOrFail(`mlDataVisualizerFieldExpandedRow-${fieldName} > mlTopValues`); await testSubjects.existOrFail( - `mlDataVisualizerFieldExpandedRow ${fieldName} ${fieldType} > mlTopValues` - ); - await testSubjects.existOrFail( - `mlDataVisualizerFieldExpandedRow ${fieldName} ${fieldType} > mlMetricDistribution` + `mlDataVisualizerFieldExpandedRow-${fieldName} > mlMetricDistribution` ); } - public async assertDateRowContents(fieldName: string, docCountFormatted: string) { + public async assertDateFieldContents(fieldName: string, docCountFormatted: string) { const fieldType = ML_JOB_FIELD_TYPES.DATE; await this.assertRowExists(fieldName); await this.assertFieldDocCount(fieldName, docCountFormatted); - await this.expandRowDetails(fieldName, fieldType); + await this.openDetails(fieldName, fieldType); await testSubjects.existOrFail( - `mlDataVisualizerFieldExpandedRow ${fieldName} ${fieldType} > mlDateSummaryTable` + `mlDataVisualizerFieldExpandedRow-${fieldName} > mlDateSummaryTable` ); } - public async assertKeywordRowContents(fieldName: string, docCountFormatted: string) { + public async assertKeywordFieldContents(fieldName: string, docCountFormatted: string) { const fieldType = ML_JOB_FIELD_TYPES.KEYWORD; await this.assertRowExists(fieldName); await this.assertFieldDocCount(fieldName, docCountFormatted); - await this.expandRowDetails(fieldName, fieldType); + await this.openDetails(fieldName, fieldType); await testSubjects.existOrFail( - `mlDataVisualizerFieldExpandedRow ${fieldName} ${fieldType} > mlFieldDataCardTopValues` + `mlDataVisualizerFieldExpandedRow-${fieldName} > mlFieldDataTopValues` ); } - public async assertTextRowContents(fieldName: string, docCountFormatted: string) { + public async assertTextFieldContents(fieldName: string, docCountFormatted: string) { const fieldType = ML_JOB_FIELD_TYPES.TEXT; await this.assertRowExists(fieldName); await this.assertFieldDocCount(fieldName, docCountFormatted); - await this.expandRowDetails(fieldName, fieldType); + await this.openDetails(fieldName, fieldType); await testSubjects.existOrFail( - `mlDataVisualizerFieldExpandedRow ${fieldName} ${fieldType} > mlFieldDataCardExamplesList` + `mlDataVisualizerFieldExpandedRow-${fieldName} > mlFieldDataExamplesList` ); } - async assertNonMetricCardContents( + async assertNonMetricFieldContents( cardType: string, fieldName: string, docCountFormatted: string ) { // Currently the data used in the data visualizer tests only contains these field types. if (cardType === ML_JOB_FIELD_TYPES.DATE) { - await this.assertDateRowContents(fieldName, docCountFormatted); + await this.assertDateFieldContents(fieldName, docCountFormatted); } else if (cardType === ML_JOB_FIELD_TYPES.KEYWORD) { - await this.assertKeywordRowContents(fieldName, docCountFormatted!); + await this.assertKeywordFieldContents(fieldName, docCountFormatted!); } else if (cardType === ML_JOB_FIELD_TYPES.TEXT) { - await this.assertTextRowContents(fieldName, docCountFormatted!); + await this.assertTextFieldContents(fieldName, docCountFormatted!); } } })(); From a9e37f3500b49c927a9c6997ad868c7c0c1dc83b Mon Sep 17 00:00:00 2001 From: Quynh Nguyen Date: Tue, 15 Dec 2020 19:20:03 -0600 Subject: [PATCH 2/9] [ML] Add content assertions --- .../multi_select_picker.tsx | 9 +- .../document_count_chart.tsx | 2 +- .../metric_distribution_chart.tsx | 2 +- .../field_data_card/top_values/top_values.tsx | 2 +- .../field_data_row/number_content_preview.tsx | 2 +- .../data_visualizer/index_data_visualizer.ts | 120 ++++++---- .../ml/data_visualizer_index_based.ts | 209 +++++++++++++++++- .../services/ml/data_visualizer_table.ts | 85 ++----- x-pack/test/functional/services/ml/index.ts | 6 +- 9 files changed, 309 insertions(+), 128 deletions(-) diff --git a/x-pack/plugins/ml/public/application/components/multi_select_picker/multi_select_picker.tsx b/x-pack/plugins/ml/public/application/components/multi_select_picker/multi_select_picker.tsx index 30af6b80266c6..a686552d65f74 100644 --- a/x-pack/plugins/ml/public/application/components/multi_select_picker/multi_select_picker.tsx +++ b/x-pack/plugins/ml/public/application/components/multi_select_picker/multi_select_picker.tsx @@ -84,6 +84,7 @@ export const MultiSelectPicker: FC<{ const button = ( - setSearchTerm(e.target.value)} /> + setSearchTerm(e.target.value)} + data-test-subj={`${dataTestSubj}-mlDataVisualizerMultiSelectButtonSearchInput`} + />
{Array.isArray(items) && items.length > 0 ? ( @@ -120,6 +126,7 @@ export const MultiSelectPicker: FC<{ key={index} onClick={() => handleOnChange(index)} style={{ flexDirection: 'row' }} + data-test-subj={`${dataTestSubj}-mlDataVisualizerMultiSelectOption-${item.value}`} > {item.name ?? item.value} diff --git a/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/document_count_chart/document_count_chart.tsx b/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/document_count_chart/document_count_chart.tsx index 6023d32767039..6a02cb6acebd4 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/document_count_chart/document_count_chart.tsx +++ b/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/document_count_chart/document_count_chart.tsx @@ -52,7 +52,7 @@ export const DocumentCountChart: FC = ({ const dateFormatter = niceTimeFormatter([timeRangeEarliest, timeRangeLatest]); return ( -
+
= ({ }; return ( -
+
= ({ stats, fieldFormat, barColor, compressed - + = ({ config }) => { const defaultChartData: MetricDistributionChartData[] = []; const [distributionChartData, setDistributionChartData] = useState(defaultChartData); const [legendText, setLegendText] = useState<{ min: number; max: number } | undefined>(); - const dataTestSubj = fieldName; + const dataTestSubj = `mlDataGridChart-${fieldName}`; useEffect(() => { const chartData = buildChartDataFromStats(stats, METRIC_DISTRIBUTION_CHART_WIDTH); if ( diff --git a/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer.ts b/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer.ts index 13dcf5d59c135..1eb8cf5dd26ec 100644 --- a/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer.ts +++ b/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer.ts @@ -16,27 +16,25 @@ interface MetricFieldVisConfig extends FieldVisConfig { interface NonMetricFieldVisConfig extends FieldVisConfig { docCountFormatted: string; - exampleCount?: number; + exampleCount: number; } interface TestData { suiteTitle: string; sourceIndexOrSavedSearch: string; - metricFieldsFilter: string; - nonMetricFieldsFilter: string; - nonMetricFieldsTypeFilter: string; + fieldNameFilters: string[]; + fieldTypeFilters: string[]; expected: { totalDocCountFormatted: string; - fieldsPanelCount: number; - metricCards?: MetricFieldVisConfig[]; - nonMetricCards?: NonMetricFieldVisConfig[]; - nonMetricFieldsTypeFilterCardCount: number; - metricFieldsFilterCardCount: number; - nonMetricFieldsFilterCardCount: number; + metricFields?: MetricFieldVisConfig[]; + nonMetricFields?: NonMetricFieldVisConfig[]; + emptyFields: string[]; visibleMetricFieldsCount: number; totalMetricFieldsCount: number; populatedFieldsCount: number; totalFieldsCount: number; + fieldNameFiltersResultCount: number; + fieldTypeFiltersResultCount: number; }; } @@ -47,13 +45,11 @@ export default function ({ getService }: FtrProviderContext) { const farequoteIndexPatternTestData: TestData = { suiteTitle: 'index pattern', sourceIndexOrSavedSearch: 'ft_farequote', - metricFieldsFilter: 'document', - nonMetricFieldsFilter: 'airline', - nonMetricFieldsTypeFilter: 'keyword', + fieldNameFilters: ['airline', '@timestamp'], + fieldTypeFilters: [ML_JOB_FIELD_TYPES.KEYWORD], expected: { totalDocCountFormatted: '86,274', - fieldsPanelCount: 2, // Metrics panel and Fields panel - metricCards: [ + metricFields: [ { fieldName: 'responsetime', type: ML_JOB_FIELD_TYPES.NUMBER, @@ -65,7 +61,7 @@ export default function ({ getService }: FtrProviderContext) { topValuesCount: 10, }, ], - nonMetricCards: [ + nonMetricFields: [ { fieldName: '@timestamp', type: ML_JOB_FIELD_TYPES.DATE, @@ -73,6 +69,7 @@ export default function ({ getService }: FtrProviderContext) { aggregatable: true, loading: false, docCountFormatted: '5000 (100%)', + exampleCount: 2, }, { fieldName: '@version', @@ -120,26 +117,24 @@ export default function ({ getService }: FtrProviderContext) { docCountFormatted: '5000 (100%)', }, ], - nonMetricFieldsTypeFilterCardCount: 3, - metricFieldsFilterCardCount: 1, - nonMetricFieldsFilterCardCount: 1, + emptyFields: ['sourcetype'], visibleMetricFieldsCount: 1, totalMetricFieldsCount: 1, populatedFieldsCount: 7, totalFieldsCount: 8, + fieldNameFiltersResultCount: 2, + fieldTypeFiltersResultCount: 3, }, }; const farequoteKQLSearchTestData: TestData = { suiteTitle: 'KQL saved search', sourceIndexOrSavedSearch: 'ft_farequote_kuery', - metricFieldsFilter: 'responsetime', - nonMetricFieldsFilter: 'airline', - nonMetricFieldsTypeFilter: 'keyword', + fieldNameFilters: ['@version'], + fieldTypeFilters: [ML_JOB_FIELD_TYPES.DATE, ML_JOB_FIELD_TYPES.TEXT], expected: { totalDocCountFormatted: '34,415', - fieldsPanelCount: 2, // Metrics panel and Fields panel - metricCards: [ + metricFields: [ { fieldName: 'responsetime', type: ML_JOB_FIELD_TYPES.NUMBER, @@ -151,7 +146,7 @@ export default function ({ getService }: FtrProviderContext) { topValuesCount: 10, }, ], - nonMetricCards: [ + nonMetricFields: [ { fieldName: '@timestamp', type: ML_JOB_FIELD_TYPES.DATE, @@ -159,6 +154,7 @@ export default function ({ getService }: FtrProviderContext) { aggregatable: true, loading: false, docCountFormatted: '5000 (100%)', + exampleCount: 2, }, { fieldName: '@version', @@ -206,26 +202,24 @@ export default function ({ getService }: FtrProviderContext) { docCountFormatted: '5000 (100%)', }, ], - nonMetricFieldsTypeFilterCardCount: 3, - metricFieldsFilterCardCount: 2, - nonMetricFieldsFilterCardCount: 1, + emptyFields: ['sourcetype'], visibleMetricFieldsCount: 1, totalMetricFieldsCount: 1, populatedFieldsCount: 7, totalFieldsCount: 8, + fieldNameFiltersResultCount: 1, + fieldTypeFiltersResultCount: 3, }, }; const farequoteLuceneSearchTestData: TestData = { suiteTitle: 'lucene saved search', sourceIndexOrSavedSearch: 'ft_farequote_lucene', - metricFieldsFilter: 'responsetime', - nonMetricFieldsFilter: 'version', - nonMetricFieldsTypeFilter: 'keyword', + fieldNameFilters: ['@version.keyword', 'type'], + fieldTypeFilters: [ML_JOB_FIELD_TYPES.NUMBER], expected: { totalDocCountFormatted: '34,416', - fieldsPanelCount: 2, // Metrics panel and Fields panel - metricCards: [ + metricFields: [ { fieldName: 'responsetime', type: ML_JOB_FIELD_TYPES.NUMBER, @@ -237,7 +231,7 @@ export default function ({ getService }: FtrProviderContext) { topValuesCount: 10, }, ], - nonMetricCards: [ + nonMetricFields: [ { fieldName: '@timestamp', type: ML_JOB_FIELD_TYPES.DATE, @@ -245,6 +239,7 @@ export default function ({ getService }: FtrProviderContext) { aggregatable: true, loading: false, docCountFormatted: '5000 (100%)', + exampleCount: 2, }, { fieldName: '@version', @@ -292,13 +287,13 @@ export default function ({ getService }: FtrProviderContext) { docCountFormatted: '5000 (100%)', }, ], - nonMetricFieldsTypeFilterCardCount: 3, - metricFieldsFilterCardCount: 2, - nonMetricFieldsFilterCardCount: 1, + emptyFields: ['sourcetype'], visibleMetricFieldsCount: 1, totalMetricFieldsCount: 1, populatedFieldsCount: 7, totalFieldsCount: 8, + fieldNameFiltersResultCount: 2, + fieldTypeFiltersResultCount: 1, }, }; @@ -366,28 +361,61 @@ export default function ({ getService }: FtrProviderContext) { await ml.testExecution.logTestStep( 'displays details for metric fields and non-metric fields correctly' ); - for (const fieldCard of testData.expected.metricCards as Array< + for (const fieldCard of testData.expected.metricFields as Array< Required >) { - await ml.dataVisualizerTable.assertNumberFieldContents( + await ml.dataVisualizerIndexBased.assertNumberFieldContents( fieldCard.fieldName, - fieldCard.docCountFormatted + fieldCard.docCountFormatted, + fieldCard.topValuesCount ); } - for (const fieldCard of testData.expected.nonMetricCards as Array< - Required + for (const fieldCard of testData.expected.nonMetricFields as Array< + Required >) { await ml.dataVisualizerTable.assertRowExists(fieldCard.fieldName); } - - for (const fieldCard of testData.expected.nonMetricCards!) { - await ml.dataVisualizerTable.assertNonMetricFieldContents( + for (const fieldCard of testData.expected.nonMetricFields!) { + await ml.dataVisualizerIndexBased.assertNonMetricFieldContents( fieldCard.type, fieldCard.fieldName!, - fieldCard.docCountFormatted + fieldCard.docCountFormatted, + fieldCard.exampleCount ); } + + await ml.testExecution.logTestStep( + `${testData.suiteTitle} sample size control changes non-metric field cards` + ); + await ml.dataVisualizerIndexBased.setSampleSizeInputValue(1000, 'airline', '1000 (100%)'); + await ml.dataVisualizerIndexBased.setSampleSizeInputValue(5000, '@timestamp', '5000 (100%)'); + + await ml.testExecution.logTestStep('sets and resets field type filter correctly'); + await ml.dataVisualizerIndexBased.setFieldTypeFilter( + testData.fieldTypeFilters, + testData.expected.fieldTypeFiltersResultCount + ); + await ml.dataVisualizerIndexBased.removeFieldTypeFilter( + testData.fieldTypeFilters, + testData.expected.populatedFieldsCount + ); + + await ml.testExecution.logTestStep('sets and resets field name filter correctly'); + await ml.dataVisualizerIndexBased.setFieldNameFilter( + testData.fieldNameFilters, + testData.expected.fieldNameFiltersResultCount + ); + await ml.dataVisualizerIndexBased.removeFieldNameFilter( + testData.fieldNameFilters, + testData.expected.populatedFieldsCount + ); + + await ml.testExecution.logTestStep('displays unpopulated fields correctly'); + await ml.dataVisualizerIndexBased.setShowEmptyFieldsSwitchState(true); + for (const field of testData.expected.emptyFields) { + await ml.dataVisualizerTable.assertRowExists(field); + } }); } diff --git a/x-pack/test/functional/services/ml/data_visualizer_index_based.ts b/x-pack/test/functional/services/ml/data_visualizer_index_based.ts index 16e9744a24911..f6514619fb1ba 100644 --- a/x-pack/test/functional/services/ml/data_visualizer_index_based.ts +++ b/x-pack/test/functional/services/ml/data_visualizer_index_based.ts @@ -6,13 +6,13 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../ftr_provider_context'; +import type { MlDataVisualizerTable } from './data_visualizer_table'; +import { asyncForEach } from '../../apps/ml/settings/common'; import { ML_JOB_FIELD_TYPES } from '../../../../plugins/ml/common/constants/field_types'; -import { MlCommonUI } from './common_ui'; -import { MlJobFieldType } from '../../../../plugins/ml/common/types/field_types'; export function MachineLearningDataVisualizerIndexBasedProvider( { getService }: FtrProviderContext, - mlCommonUI: MlCommonUI + mlDataVisualizerTable: MlDataVisualizerTable ) { const testSubjects = getService('testSubjects'); const retry = getService('retry'); @@ -43,7 +43,7 @@ export function MachineLearningDataVisualizerIndexBasedProvider( }, async assertTotalDocCountChartExist() { - await testSubjects.existOrFail(`mlFieldDataCardDocumentCountChart`); + await testSubjects.existOrFail(`mlFieldDataDocumentCountChart`); }, async assertSearchPanelExist() { @@ -126,6 +126,26 @@ export function MachineLearningDataVisualizerIndexBasedProvider( await testSubjects.existOrFail('mlDataVisualizerShowEmptyFieldsSwitch'); }, + async assertShowEmptyFieldsCheckState(expectedCheckState: boolean) { + const actualCheckState = + (await testSubjects.getAttribute( + 'mlDataVisualizerShowEmptyFieldsSwitch', + 'aria-checked' + )) === 'true'; + expect(actualCheckState).to.eql( + expectedCheckState, + `Show empty fields check state should be '${expectedCheckState}' (got '${actualCheckState}')` + ); + return actualCheckState === expectedCheckState; + }, + + async setShowEmptyFieldsSwitchState(checkState: boolean) { + if (await this.assertShowEmptyFieldsCheckState(!checkState)) { + await testSubjects.click('mlDataVisualizerShowEmptyFieldsSwitch'); + } + await this.assertShowEmptyFieldsCheckState(checkState); + }, + async assertFieldNameInputExists() { await testSubjects.existOrFail('mlDataVisualizerFieldNameSelect'); }, @@ -140,18 +160,89 @@ export function MachineLearningDataVisualizerIndexBasedProvider( async setSampleSizeInputValue( sampleSize: number, - cardType: string, fieldName: string, docCountFormatted: string ) { + await this.assertSampleSizeInputExists(); await testSubjects.clickWhenNotDisabled('mlDataVisualizerShardSizeSelect'); await testSubjects.existOrFail(`mlDataVisualizerShardSizeOption ${sampleSize}`); await testSubjects.click(`mlDataVisualizerShardSizeOption ${sampleSize}`); - // TODO: update - // await retry.tryForTime(5000, async () => { - // await this.assertFieldDocCountContents(cardType, fieldName, docCountFormatted); - // }); + await retry.tryForTime(5000, async () => { + await mlDataVisualizerTable.assertFieldDocCount(fieldName, docCountFormatted); + }); + }, + + async setFieldTypeFilter(fieldTypes: string[], expectedRowCount = 1) { + await this.assertFieldNameInputExists(); + await testSubjects.clickWhenNotDisabled( + 'mlDataVisualizerFieldTypeSelect-mlDataVisualizerMultiSelectButton' + ); + await testSubjects.existOrFail( + 'mlDataVisualizerFieldTypeSelect-mlDataVisualizerMultiSelectPopover' + ); + await testSubjects.existOrFail( + 'mlDataVisualizerFieldTypeSelect-mlDataVisualizerMultiSelectButtonSearchInput' + ); + const searchBarInput = await testSubjects.find( + `mlDataVisualizerFieldTypeSelect-mlDataVisualizerMultiSelectButtonSearchInput` + ); + + await asyncForEach(fieldTypes, async (fieldType) => { + await retry.tryForTime(5000, async () => { + await searchBarInput.clearValueWithKeyboard(); + await searchBarInput.type(fieldType); + await testSubjects.existOrFail( + `mlDataVisualizerFieldTypeSelect-mlDataVisualizerMultiSelectOption-${fieldType}` + ); + await testSubjects.click( + `mlDataVisualizerFieldTypeSelect-mlDataVisualizerMultiSelectOption-${fieldType}` + ); + }); + }); + + // escape popover + await browser.pressKeys(browser.keys.ESCAPE); + await mlDataVisualizerTable.assertTableRowCount(expectedRowCount); + }, + + async removeFieldTypeFilter(fieldTypes: string[], expectedRowCount = 1) { + await this.setFieldTypeFilter(fieldTypes, expectedRowCount); + }, + + async setFieldNameFilter(fieldNames: string[], expectedRowCount = 1) { + await this.assertFieldNameInputExists(); + await testSubjects.clickWhenNotDisabled( + 'mlDataVisualizerFieldNameSelect-mlDataVisualizerMultiSelectButton' + ); + await testSubjects.existOrFail( + 'mlDataVisualizerFieldNameSelect-mlDataVisualizerMultiSelectPopover' + ); + await testSubjects.existOrFail( + 'mlDataVisualizerFieldNameSelect-mlDataVisualizerMultiSelectButtonSearchInput' + ); + const searchBarInput = await testSubjects.find( + `mlDataVisualizerFieldNameSelect-mlDataVisualizerMultiSelectButtonSearchInput` + ); + + await asyncForEach(fieldNames, async (filterString) => { + await retry.tryForTime(5000, async () => { + await searchBarInput.clearValueWithKeyboard(); + await searchBarInput.type(filterString); + await testSubjects.existOrFail( + `mlDataVisualizerFieldNameSelect-mlDataVisualizerMultiSelectOption-${filterString}` + ); + await testSubjects.click( + `mlDataVisualizerFieldNameSelect-mlDataVisualizerMultiSelectOption-${filterString}` + ); + }); + }); + await browser.pressKeys(browser.keys.ESCAPE); + await mlDataVisualizerTable.assertTableRowCount(expectedRowCount); + }, + + async removeFieldNameFilter(fieldNames: string[], expectedRowCount: number) { + await this.setFieldNameFilter(fieldNames, expectedRowCount); }, async assertActionsPanelExists() { @@ -181,5 +272,105 @@ export function MachineLearningDataVisualizerIndexBasedProvider( async clickCreateAdvancedJobButton() { await testSubjects.clickWhenNotDisabled('mlDataVisualizerCreateAdvancedJobCard'); }, + + async assertTopValuesContents(fieldName: string, expectedTopValuesCount: number) { + const selector = mlDataVisualizerTable.detailsSelector(fieldName, 'mlFieldDataTopValues'); + const topValuesElement = await testSubjects.find(selector); + const topValuesBars = await topValuesElement.findAllByTestSubject('mlFieldDataTopValueBar'); + expect(topValuesBars).to.have.length( + expectedTopValuesCount, + `Expected top values count for field '${fieldName}' to be '${expectedTopValuesCount}' (got '${topValuesBars.length}')` + ); + }, + + async assertDistributionPreviewExist(fieldName: string) { + await testSubjects.existOrFail( + mlDataVisualizerTable.rowSelector(fieldName, `mlDataGridChart-${fieldName}`) + ); + await testSubjects.existOrFail( + mlDataVisualizerTable.rowSelector(fieldName, `mlDataGridChart-${fieldName}-histogram`) + ); + }, + + async assertNumberFieldContents( + fieldName: string, + docCountFormatted: string, + topValuesCount: number + ) { + await mlDataVisualizerTable.assertRowExists(fieldName); + await mlDataVisualizerTable.assertFieldDocCount(fieldName, docCountFormatted); + await mlDataVisualizerTable.openDetails(fieldName); + + await testSubjects.existOrFail( + mlDataVisualizerTable.detailsSelector(fieldName, 'mlNumberSummaryTable') + ); + + await testSubjects.existOrFail( + mlDataVisualizerTable.detailsSelector(fieldName, 'mlTopValues') + ); + await this.assertTopValuesContents(fieldName, topValuesCount); + + await this.assertDistributionPreviewExist(fieldName); + }, + + async assertDateFieldContents(fieldName: string, docCountFormatted: string) { + await mlDataVisualizerTable.assertRowExists(fieldName); + await mlDataVisualizerTable.assertFieldDocCount(fieldName, docCountFormatted); + await mlDataVisualizerTable.openDetails(fieldName); + + await testSubjects.existOrFail( + mlDataVisualizerTable.detailsSelector(fieldName, 'mlDateSummaryTable') + ); + }, + + async assertKeywordFieldContents( + fieldName: string, + docCountFormatted: string, + topValuesCount: number + ) { + await mlDataVisualizerTable.assertRowExists(fieldName); + await mlDataVisualizerTable.assertFieldDocCount(fieldName, docCountFormatted); + await mlDataVisualizerTable.openDetails(fieldName); + + await testSubjects.existOrFail( + mlDataVisualizerTable.detailsSelector(fieldName, 'mlFieldDataTopValues') + ); + await this.assertTopValuesContents(fieldName, topValuesCount); + }, + + async assertTextFieldContents( + fieldName: string, + docCountFormatted: string, + expectedExamplesCount: number + ) { + await mlDataVisualizerTable.assertRowExists(fieldName); + await mlDataVisualizerTable.assertFieldDocCount(fieldName, docCountFormatted); + await mlDataVisualizerTable.openDetails(fieldName); + + const examplesList = await testSubjects.find( + mlDataVisualizerTable.detailsSelector(fieldName, 'mlFieldDataExamplesList') + ); + const examplesListItems = await examplesList.findAllByTagName('li'); + expect(examplesListItems).to.have.length( + expectedExamplesCount, + `Expected example list item count for field '${fieldName}' to be '${expectedExamplesCount}' (got '${examplesListItems.length}')` + ); + }, + + async assertNonMetricFieldContents( + fieldType: string, + fieldName: string, + docCountFormatted: string, + exampleCount: number + ) { + // Currently the data used in the data visualizer tests only contains these field types. + if (fieldType === ML_JOB_FIELD_TYPES.DATE) { + await this.assertDateFieldContents(fieldName, docCountFormatted, exampleCount); + } else if (fieldType === ML_JOB_FIELD_TYPES.KEYWORD) { + await this.assertKeywordFieldContents(fieldName, docCountFormatted, exampleCount); + } else if (fieldType === ML_JOB_FIELD_TYPES.TEXT) { + await this.assertTextFieldContents(fieldName, docCountFormatted, exampleCount); + } + }, }; } diff --git a/x-pack/test/functional/services/ml/data_visualizer_table.ts b/x-pack/test/functional/services/ml/data_visualizer_table.ts index 30117f54bf771..720962cd313c0 100644 --- a/x-pack/test/functional/services/ml/data_visualizer_table.ts +++ b/x-pack/test/functional/services/ml/data_visualizer_table.ts @@ -4,8 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ import expect from '@kbn/expect'; +import { ProvidedType } from '@kbn/test/types/ftr'; import { FtrProviderContext } from '../../ftr_provider_context'; -import { ML_JOB_FIELD_TYPES } from '../../../../plugins/ml/common/constants/field_types'; +export type MlDataVisualizerTable = ProvidedType; export function MachineLearningDataVisualizerTableProvider({ getService }: FtrProviderContext) { const retry = getService('retry'); @@ -52,6 +53,16 @@ export function MachineLearningDataVisualizerTableProvider({ getService }: FtrPr return rows; } + public async assertTableRowCount(expectedRowCount: number) { + await retry.tryForTime(5000, async () => { + const filteredRows = await this.parseDataVisualizerTable(); + expect(filteredRows).to.have.length( + expectedRowCount, + `Filtered Data Visualizer table should have ${expectedRowCount} row(s) (got '${filteredRows.length}')` + ); + }); + } + public rowSelector(fieldName: string, subSelector?: string) { const row = `~mlDataVisualizerTable > ~row-${fieldName}`; return !subSelector ? row : `${row} > ${subSelector}`; @@ -119,76 +130,18 @@ export function MachineLearningDataVisualizerTableProvider({ getService }: FtrPr const docCount = await testSubjects.getVisibleText(docCountFormattedSelector); expect(docCount).to.eql( docCountFormatted, - `Expected total document count to be '${docCountFormatted}' (got '${docCount}')` - ); - } - - public async assertNumberFieldContents(fieldName: string, docCountFormatted: string) { - const fieldType = ML_JOB_FIELD_TYPES.NUMBER; - await this.assertRowExists(fieldName); - await this.assertFieldDocCount(fieldName, docCountFormatted); - - await this.openDetails(fieldName, fieldType); - - await testSubjects.existOrFail( - `mlDataVisualizerFieldExpandedRow-${fieldName} > mlNumberSummaryTable` - ); - - await testSubjects.existOrFail(`mlDataVisualizerFieldExpandedRow-${fieldName} > mlTopValues`); - await testSubjects.existOrFail( - `mlDataVisualizerFieldExpandedRow-${fieldName} > mlMetricDistribution` - ); - } - - public async assertDateFieldContents(fieldName: string, docCountFormatted: string) { - const fieldType = ML_JOB_FIELD_TYPES.DATE; - await this.assertRowExists(fieldName); - await this.assertFieldDocCount(fieldName, docCountFormatted); - - await this.openDetails(fieldName, fieldType); - - await testSubjects.existOrFail( - `mlDataVisualizerFieldExpandedRow-${fieldName} > mlDateSummaryTable` - ); - } - - public async assertKeywordFieldContents(fieldName: string, docCountFormatted: string) { - const fieldType = ML_JOB_FIELD_TYPES.KEYWORD; - await this.assertRowExists(fieldName); - await this.assertFieldDocCount(fieldName, docCountFormatted); - - await this.openDetails(fieldName, fieldType); - - await testSubjects.existOrFail( - `mlDataVisualizerFieldExpandedRow-${fieldName} > mlFieldDataTopValues` + `Expected field document count to be '${docCountFormatted}' (got '${docCount}')` ); } - public async assertTextFieldContents(fieldName: string, docCountFormatted: string) { - const fieldType = ML_JOB_FIELD_TYPES.TEXT; - await this.assertRowExists(fieldName); - await this.assertFieldDocCount(fieldName, docCountFormatted); - - await this.openDetails(fieldName, fieldType); - - await testSubjects.existOrFail( - `mlDataVisualizerFieldExpandedRow-${fieldName} > mlFieldDataExamplesList` - ); + public async assertFieldDistinctValuesExist(fieldName: string) { + const selector = this.rowSelector(fieldName, 'mlDataVisualizerTableColumnDistinctValues'); + await testSubjects.existOrFail(selector); } - async assertNonMetricFieldContents( - cardType: string, - fieldName: string, - docCountFormatted: string - ) { - // Currently the data used in the data visualizer tests only contains these field types. - if (cardType === ML_JOB_FIELD_TYPES.DATE) { - await this.assertDateFieldContents(fieldName, docCountFormatted); - } else if (cardType === ML_JOB_FIELD_TYPES.KEYWORD) { - await this.assertKeywordFieldContents(fieldName, docCountFormatted!); - } else if (cardType === ML_JOB_FIELD_TYPES.TEXT) { - await this.assertTextFieldContents(fieldName, docCountFormatted!); - } + public async assertFieldDistributionExist(fieldName: string) { + const selector = this.rowSelector(fieldName, 'mlDataVisualizerTableColumnDistribution'); + await testSubjects.existOrFail(selector); } })(); } diff --git a/x-pack/test/functional/services/ml/index.ts b/x-pack/test/functional/services/ml/index.ts index 72903b2c409d9..86299f41d2e7f 100644 --- a/x-pack/test/functional/services/ml/index.ts +++ b/x-pack/test/functional/services/ml/index.ts @@ -61,13 +61,15 @@ export function MachineLearningProvider(context: FtrProviderContext) { const dataFrameAnalyticsEdit = MachineLearningDataFrameAnalyticsEditProvider(context, commonUI); const dataFrameAnalyticsResults = MachineLearningDataFrameAnalyticsResultsProvider(context); const dataFrameAnalyticsTable = MachineLearningDataFrameAnalyticsTableProvider(context); + const dataVisualizer = MachineLearningDataVisualizerProvider(context); + const dataVisualizerTable = MachineLearningDataVisualizerTableProvider(context); + const dataVisualizerFileBased = MachineLearningDataVisualizerFileBasedProvider(context, commonUI); const dataVisualizerIndexBased = MachineLearningDataVisualizerIndexBasedProvider( context, - commonUI + dataVisualizerTable ); - const dataVisualizerTable = MachineLearningDataVisualizerTableProvider(context); const jobManagement = MachineLearningJobManagementProvider(context, api); const jobSelection = MachineLearningJobSelectionProvider(context); From 6564e928fdc1b4a29530e0f7bafca39764814bd8 Mon Sep 17 00:00:00 2001 From: Quynh Nguyen Date: Tue, 15 Dec 2020 19:31:23 -0600 Subject: [PATCH 3/9] [ML] Fix assertDateFieldContents --- .../test/functional/services/ml/data_visualizer_index_based.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/test/functional/services/ml/data_visualizer_index_based.ts b/x-pack/test/functional/services/ml/data_visualizer_index_based.ts index f6514619fb1ba..d094e54e76173 100644 --- a/x-pack/test/functional/services/ml/data_visualizer_index_based.ts +++ b/x-pack/test/functional/services/ml/data_visualizer_index_based.ts @@ -365,7 +365,7 @@ export function MachineLearningDataVisualizerIndexBasedProvider( ) { // Currently the data used in the data visualizer tests only contains these field types. if (fieldType === ML_JOB_FIELD_TYPES.DATE) { - await this.assertDateFieldContents(fieldName, docCountFormatted, exampleCount); + await this.assertDateFieldContents(fieldName, docCountFormatted); } else if (fieldType === ML_JOB_FIELD_TYPES.KEYWORD) { await this.assertKeywordFieldContents(fieldName, docCountFormatted, exampleCount); } else if (fieldType === ML_JOB_FIELD_TYPES.TEXT) { From 0e28362a81626c19f12b4d711b37ba9aceb984d7 Mon Sep 17 00:00:00 2001 From: Quynh Nguyen Date: Wed, 16 Dec 2020 15:05:42 -0600 Subject: [PATCH 4/9] [ML] Remove openDetails, add ensureDetailsClosed --- .../ml/data_visualizer_index_based.ts | 13 ++++++---- .../services/ml/data_visualizer_table.ts | 24 +++++++------------ 2 files changed, 17 insertions(+), 20 deletions(-) diff --git a/x-pack/test/functional/services/ml/data_visualizer_index_based.ts b/x-pack/test/functional/services/ml/data_visualizer_index_based.ts index d094e54e76173..8b7d3df605afa 100644 --- a/x-pack/test/functional/services/ml/data_visualizer_index_based.ts +++ b/x-pack/test/functional/services/ml/data_visualizer_index_based.ts @@ -299,7 +299,7 @@ export function MachineLearningDataVisualizerIndexBasedProvider( ) { await mlDataVisualizerTable.assertRowExists(fieldName); await mlDataVisualizerTable.assertFieldDocCount(fieldName, docCountFormatted); - await mlDataVisualizerTable.openDetails(fieldName); + await mlDataVisualizerTable.ensureDetailsOpen(fieldName); await testSubjects.existOrFail( mlDataVisualizerTable.detailsSelector(fieldName, 'mlNumberSummaryTable') @@ -311,16 +311,19 @@ export function MachineLearningDataVisualizerIndexBasedProvider( await this.assertTopValuesContents(fieldName, topValuesCount); await this.assertDistributionPreviewExist(fieldName); + + await mlDataVisualizerTable.ensureDetailsClosed(fieldName); }, async assertDateFieldContents(fieldName: string, docCountFormatted: string) { await mlDataVisualizerTable.assertRowExists(fieldName); await mlDataVisualizerTable.assertFieldDocCount(fieldName, docCountFormatted); - await mlDataVisualizerTable.openDetails(fieldName); + await mlDataVisualizerTable.ensureDetailsOpen(fieldName); await testSubjects.existOrFail( mlDataVisualizerTable.detailsSelector(fieldName, 'mlDateSummaryTable') ); + await mlDataVisualizerTable.ensureDetailsClosed(fieldName); }, async assertKeywordFieldContents( @@ -330,12 +333,13 @@ export function MachineLearningDataVisualizerIndexBasedProvider( ) { await mlDataVisualizerTable.assertRowExists(fieldName); await mlDataVisualizerTable.assertFieldDocCount(fieldName, docCountFormatted); - await mlDataVisualizerTable.openDetails(fieldName); + await mlDataVisualizerTable.ensureDetailsOpen(fieldName); await testSubjects.existOrFail( mlDataVisualizerTable.detailsSelector(fieldName, 'mlFieldDataTopValues') ); await this.assertTopValuesContents(fieldName, topValuesCount); + await mlDataVisualizerTable.ensureDetailsClosed(fieldName); }, async assertTextFieldContents( @@ -345,7 +349,7 @@ export function MachineLearningDataVisualizerIndexBasedProvider( ) { await mlDataVisualizerTable.assertRowExists(fieldName); await mlDataVisualizerTable.assertFieldDocCount(fieldName, docCountFormatted); - await mlDataVisualizerTable.openDetails(fieldName); + await mlDataVisualizerTable.ensureDetailsOpen(fieldName); const examplesList = await testSubjects.find( mlDataVisualizerTable.detailsSelector(fieldName, 'mlFieldDataExamplesList') @@ -355,6 +359,7 @@ export function MachineLearningDataVisualizerIndexBasedProvider( expectedExamplesCount, `Expected example list item count for field '${fieldName}' to be '${expectedExamplesCount}' (got '${examplesListItems.length}')` ); + await mlDataVisualizerTable.ensureDetailsClosed(fieldName); }, async assertNonMetricFieldContents( diff --git a/x-pack/test/functional/services/ml/data_visualizer_table.ts b/x-pack/test/functional/services/ml/data_visualizer_table.ts index 720962cd313c0..c1dc280796099 100644 --- a/x-pack/test/functional/services/ml/data_visualizer_table.ts +++ b/x-pack/test/functional/services/ml/data_visualizer_table.ts @@ -80,7 +80,11 @@ export function MachineLearningDataVisualizerTableProvider({ getService }: FtrPr public async ensureDetailsOpen(fieldName: string) { await retry.tryForTime(10000, async () => { if (!(await testSubjects.exists(this.detailsSelector(fieldName)))) { - await testSubjects.click(this.rowSelector(fieldName, 'mlDataVisualizerDetailsToggle')); + const selector = this.rowSelector( + fieldName, + `mlDataVisualizerDetailsToggle-${fieldName}-arrowDown` + ); + await testSubjects.click(selector); await testSubjects.existOrFail( this.rowSelector(fieldName, `mlDataVisualizerDetailsToggle-${fieldName}-arrowUp`), { @@ -95,7 +99,9 @@ export function MachineLearningDataVisualizerTableProvider({ getService }: FtrPr public async ensureDetailsClosed(fieldName: string) { await retry.tryForTime(10000, async () => { if (await testSubjects.exists(this.detailsSelector(fieldName))) { - await testSubjects.click(this.rowSelector(fieldName, 'mlDataVisualizerDetailsToggle')); + await testSubjects.click( + this.rowSelector(fieldName, `mlDataVisualizerDetailsToggle-${fieldName}-arrowUp`) + ); await testSubjects.existOrFail( this.rowSelector(fieldName, `mlDataVisualizerDetailsToggle-${fieldName}-arrowDown`), { @@ -107,20 +113,6 @@ export function MachineLearningDataVisualizerTableProvider({ getService }: FtrPr }); } - public async openDetails(fieldName: string) { - await this.ensureDetailsClosed(fieldName); - - const selector = this.rowSelector( - fieldName, - `mlDataVisualizerDetailsToggle-${fieldName}-arrowDown` - ); - await testSubjects.existOrFail(selector); - await testSubjects.click(selector); - await this.ensureDetailsOpen(fieldName); - - await retry.tryForTime(5000, async () => {}); - } - public async assertFieldDocCount(fieldName: string, docCountFormatted: string) { const docCountFormattedSelector = this.rowSelector( fieldName, From 087447586e82e8eb86c455303ce21a557b71fe50 Mon Sep 17 00:00:00 2001 From: Quynh Nguyen Date: Wed, 16 Dec 2020 15:10:12 -0600 Subject: [PATCH 5/9] [ML] Rename multiselect objs, remove card --- .../multi_select_picker.tsx | 8 ++-- .../data_visualizer/index_data_visualizer.ts | 25 ++++------ .../ml/data_visualizer_index_based.ts | 48 +++++-------------- .../services/ml/data_visualizer_table.ts | 6 +-- 4 files changed, 29 insertions(+), 58 deletions(-) diff --git a/x-pack/plugins/ml/public/application/components/multi_select_picker/multi_select_picker.tsx b/x-pack/plugins/ml/public/application/components/multi_select_picker/multi_select_picker.tsx index a686552d65f74..c982b087a31b1 100644 --- a/x-pack/plugins/ml/public/application/components/multi_select_picker/multi_select_picker.tsx +++ b/x-pack/plugins/ml/public/application/components/multi_select_picker/multi_select_picker.tsx @@ -84,7 +84,7 @@ export const MultiSelectPicker: FC<{ const button = ( setSearchTerm(e.target.value)} - data-test-subj={`${dataTestSubj}-mlDataVisualizerMultiSelectButtonSearchInput`} + data-test-subj={`${dataTestSubj}-searchInput`} />
@@ -126,7 +126,7 @@ export const MultiSelectPicker: FC<{ key={index} onClick={() => handleOnChange(index)} style={{ flexDirection: 'row' }} - data-test-subj={`${dataTestSubj}-mlDataVisualizerMultiSelectOption-${item.value}`} + data-test-subj={`${dataTestSubj}-option-${item.value}`} > {item.name ?? item.value} diff --git a/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer.ts b/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer.ts index 1eb8cf5dd26ec..6b79c01bd95ba 100644 --- a/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer.ts +++ b/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer.ts @@ -361,32 +361,27 @@ export default function ({ getService }: FtrProviderContext) { await ml.testExecution.logTestStep( 'displays details for metric fields and non-metric fields correctly' ); - for (const fieldCard of testData.expected.metricFields as Array< + for (const fieldRow of testData.expected.metricFields as Array< Required >) { await ml.dataVisualizerIndexBased.assertNumberFieldContents( - fieldCard.fieldName, - fieldCard.docCountFormatted, - fieldCard.topValuesCount + fieldRow.fieldName, + fieldRow.docCountFormatted, + fieldRow.topValuesCount ); } - for (const fieldCard of testData.expected.nonMetricFields as Array< - Required - >) { - await ml.dataVisualizerTable.assertRowExists(fieldCard.fieldName); - } - for (const fieldCard of testData.expected.nonMetricFields!) { + for (const fieldRow of testData.expected.nonMetricFields!) { await ml.dataVisualizerIndexBased.assertNonMetricFieldContents( - fieldCard.type, - fieldCard.fieldName!, - fieldCard.docCountFormatted, - fieldCard.exampleCount + fieldRow.type, + fieldRow.fieldName!, + fieldRow.docCountFormatted, + fieldRow.exampleCount ); } await ml.testExecution.logTestStep( - `${testData.suiteTitle} sample size control changes non-metric field cards` + `${testData.suiteTitle} sample size control changes non-metric fields` ); await ml.dataVisualizerIndexBased.setSampleSizeInputValue(1000, 'airline', '1000 (100%)'); await ml.dataVisualizerIndexBased.setSampleSizeInputValue(5000, '@timestamp', '5000 (100%)'); diff --git a/x-pack/test/functional/services/ml/data_visualizer_index_based.ts b/x-pack/test/functional/services/ml/data_visualizer_index_based.ts index 8b7d3df605afa..09497cc32fa4a 100644 --- a/x-pack/test/functional/services/ml/data_visualizer_index_based.ts +++ b/x-pack/test/functional/services/ml/data_visualizer_index_based.ts @@ -175,29 +175,17 @@ export function MachineLearningDataVisualizerIndexBasedProvider( async setFieldTypeFilter(fieldTypes: string[], expectedRowCount = 1) { await this.assertFieldNameInputExists(); - await testSubjects.clickWhenNotDisabled( - 'mlDataVisualizerFieldTypeSelect-mlDataVisualizerMultiSelectButton' - ); - await testSubjects.existOrFail( - 'mlDataVisualizerFieldTypeSelect-mlDataVisualizerMultiSelectPopover' - ); - await testSubjects.existOrFail( - 'mlDataVisualizerFieldTypeSelect-mlDataVisualizerMultiSelectButtonSearchInput' - ); - const searchBarInput = await testSubjects.find( - `mlDataVisualizerFieldTypeSelect-mlDataVisualizerMultiSelectButtonSearchInput` - ); + await testSubjects.clickWhenNotDisabled('mlDataVisualizerFieldTypeSelect-button'); + await testSubjects.existOrFail('mlDataVisualizerFieldTypeSelect-popover'); + await testSubjects.existOrFail('mlDataVisualizerFieldTypeSelect-searchInput'); + const searchBarInput = await testSubjects.find(`mlDataVisualizerFieldTypeSelect-searchInput`); await asyncForEach(fieldTypes, async (fieldType) => { await retry.tryForTime(5000, async () => { await searchBarInput.clearValueWithKeyboard(); await searchBarInput.type(fieldType); - await testSubjects.existOrFail( - `mlDataVisualizerFieldTypeSelect-mlDataVisualizerMultiSelectOption-${fieldType}` - ); - await testSubjects.click( - `mlDataVisualizerFieldTypeSelect-mlDataVisualizerMultiSelectOption-${fieldType}` - ); + await testSubjects.existOrFail(`mlDataVisualizerFieldTypeSelect-option-${fieldType}`); + await testSubjects.click(`mlDataVisualizerFieldTypeSelect-option-${fieldType}`); }); }); @@ -212,29 +200,17 @@ export function MachineLearningDataVisualizerIndexBasedProvider( async setFieldNameFilter(fieldNames: string[], expectedRowCount = 1) { await this.assertFieldNameInputExists(); - await testSubjects.clickWhenNotDisabled( - 'mlDataVisualizerFieldNameSelect-mlDataVisualizerMultiSelectButton' - ); - await testSubjects.existOrFail( - 'mlDataVisualizerFieldNameSelect-mlDataVisualizerMultiSelectPopover' - ); - await testSubjects.existOrFail( - 'mlDataVisualizerFieldNameSelect-mlDataVisualizerMultiSelectButtonSearchInput' - ); - const searchBarInput = await testSubjects.find( - `mlDataVisualizerFieldNameSelect-mlDataVisualizerMultiSelectButtonSearchInput` - ); + await testSubjects.clickWhenNotDisabled('mlDataVisualizerFieldNameSelect-button'); + await testSubjects.existOrFail('mlDataVisualizerFieldNameSelect-popover'); + await testSubjects.existOrFail('mlDataVisualizerFieldNameSelect-searchInput'); + const searchBarInput = await testSubjects.find(`mlDataVisualizerFieldNameSelect-searchInput`); await asyncForEach(fieldNames, async (filterString) => { await retry.tryForTime(5000, async () => { await searchBarInput.clearValueWithKeyboard(); await searchBarInput.type(filterString); - await testSubjects.existOrFail( - `mlDataVisualizerFieldNameSelect-mlDataVisualizerMultiSelectOption-${filterString}` - ); - await testSubjects.click( - `mlDataVisualizerFieldNameSelect-mlDataVisualizerMultiSelectOption-${filterString}` - ); + await testSubjects.existOrFail(`mlDataVisualizerFieldNameSelect-option-${filterString}`); + await testSubjects.click(`mlDataVisualizerFieldNameSelect-option-${filterString}`); }); }); await browser.pressKeys(browser.keys.ESCAPE); diff --git a/x-pack/test/functional/services/ml/data_visualizer_table.ts b/x-pack/test/functional/services/ml/data_visualizer_table.ts index c1dc280796099..9abc8ec4b8af2 100644 --- a/x-pack/test/functional/services/ml/data_visualizer_table.ts +++ b/x-pack/test/functional/services/ml/data_visualizer_table.ts @@ -55,10 +55,10 @@ export function MachineLearningDataVisualizerTableProvider({ getService }: FtrPr public async assertTableRowCount(expectedRowCount: number) { await retry.tryForTime(5000, async () => { - const filteredRows = await this.parseDataVisualizerTable(); - expect(filteredRows).to.have.length( + const tableRows = await this.parseDataVisualizerTable(); + expect(tableRows).to.have.length( expectedRowCount, - `Filtered Data Visualizer table should have ${expectedRowCount} row(s) (got '${filteredRows.length}')` + `Filtered Data Visualizer table should have ${expectedRowCount} row(s) (got '${tableRows.length}')` ); }); } From c636dc1fac604f0f058e6382649ee9a5dbc328bb Mon Sep 17 00:00:00 2001 From: Quynh Nguyen Date: Wed, 16 Dec 2020 15:39:46 -0600 Subject: [PATCH 6/9] [ML] Move some methods to table service --- .../data_visualizer/index_data_visualizer.ts | 33 ++- .../ml/data_visualizer_index_based.ts | 224 +----------------- .../services/ml/data_visualizer_table.ts | 207 ++++++++++++++++ 3 files changed, 226 insertions(+), 238 deletions(-) diff --git a/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer.ts b/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer.ts index 6b79c01bd95ba..a187a857c3f38 100644 --- a/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer.ts +++ b/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer.ts @@ -330,10 +330,10 @@ export default function ({ getService }: FtrProviderContext) { await ml.testExecution.logTestStep( `${testData.suiteTitle} displays elements in the search panel correctly` ); - await ml.dataVisualizerIndexBased.assertSearchPanelExist(); - await ml.dataVisualizerIndexBased.assertSampleSizeInputExists(); - await ml.dataVisualizerIndexBased.assertFieldTypeInputExists(); - await ml.dataVisualizerIndexBased.assertFieldNameInputExists(); + await ml.dataVisualizerTable.assertSearchPanelExist(); + await ml.dataVisualizerTable.assertSampleSizeInputExists(); + await ml.dataVisualizerTable.assertFieldTypeInputExists(); + await ml.dataVisualizerTable.assertFieldNameInputExists(); await ml.testExecution.logTestStep( `${testData.suiteTitle} displays elements in the field count panel correctly` @@ -341,7 +341,6 @@ export default function ({ getService }: FtrProviderContext) { await ml.dataVisualizerIndexBased.assertFieldCountPanelExist(); await ml.dataVisualizerIndexBased.assertMetricFieldsSummaryExist(); await ml.dataVisualizerIndexBased.assertFieldsSummaryExist(); - await ml.dataVisualizerIndexBased.assertShowEmptyFieldsSwitchExists(); await ml.dataVisualizerIndexBased.assertVisibleMetricFieldsCount( testData.expected.visibleMetricFieldsCount ); @@ -364,7 +363,7 @@ export default function ({ getService }: FtrProviderContext) { for (const fieldRow of testData.expected.metricFields as Array< Required >) { - await ml.dataVisualizerIndexBased.assertNumberFieldContents( + await ml.dataVisualizerTable.assertNumberFieldContents( fieldRow.fieldName, fieldRow.docCountFormatted, fieldRow.topValuesCount @@ -372,7 +371,7 @@ export default function ({ getService }: FtrProviderContext) { } for (const fieldRow of testData.expected.nonMetricFields!) { - await ml.dataVisualizerIndexBased.assertNonMetricFieldContents( + await ml.dataVisualizerTable.assertNonMetricFieldContents( fieldRow.type, fieldRow.fieldName!, fieldRow.docCountFormatted, @@ -383,34 +382,34 @@ export default function ({ getService }: FtrProviderContext) { await ml.testExecution.logTestStep( `${testData.suiteTitle} sample size control changes non-metric fields` ); - await ml.dataVisualizerIndexBased.setSampleSizeInputValue(1000, 'airline', '1000 (100%)'); - await ml.dataVisualizerIndexBased.setSampleSizeInputValue(5000, '@timestamp', '5000 (100%)'); + await ml.dataVisualizerTable.setSampleSizeInputValue(1000, 'airline', '1000 (100%)'); + await ml.dataVisualizerTable.setSampleSizeInputValue(5000, '@timestamp', '5000 (100%)'); await ml.testExecution.logTestStep('sets and resets field type filter correctly'); - await ml.dataVisualizerIndexBased.setFieldTypeFilter( + await ml.dataVisualizerTable.setFieldTypeFilter( testData.fieldTypeFilters, testData.expected.fieldTypeFiltersResultCount ); - await ml.dataVisualizerIndexBased.removeFieldTypeFilter( + await ml.dataVisualizerTable.removeFieldTypeFilter( testData.fieldTypeFilters, testData.expected.populatedFieldsCount ); await ml.testExecution.logTestStep('sets and resets field name filter correctly'); - await ml.dataVisualizerIndexBased.setFieldNameFilter( + await ml.dataVisualizerTable.setFieldNameFilter( testData.fieldNameFilters, testData.expected.fieldNameFiltersResultCount ); - await ml.dataVisualizerIndexBased.removeFieldNameFilter( + await ml.dataVisualizerTable.removeFieldNameFilter( testData.fieldNameFilters, testData.expected.populatedFieldsCount ); await ml.testExecution.logTestStep('displays unpopulated fields correctly'); - await ml.dataVisualizerIndexBased.setShowEmptyFieldsSwitchState(true); - for (const field of testData.expected.emptyFields) { - await ml.dataVisualizerTable.assertRowExists(field); - } + await ml.dataVisualizerTable.setShowEmptyFieldsSwitchState( + true, + testData.expected.emptyFields + ); }); } diff --git a/x-pack/test/functional/services/ml/data_visualizer_index_based.ts b/x-pack/test/functional/services/ml/data_visualizer_index_based.ts index 09497cc32fa4a..5fc5caf81c23b 100644 --- a/x-pack/test/functional/services/ml/data_visualizer_index_based.ts +++ b/x-pack/test/functional/services/ml/data_visualizer_index_based.ts @@ -4,19 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ import expect from '@kbn/expect'; - import { FtrProviderContext } from '../../ftr_provider_context'; -import type { MlDataVisualizerTable } from './data_visualizer_table'; -import { asyncForEach } from '../../apps/ml/settings/common'; -import { ML_JOB_FIELD_TYPES } from '../../../../plugins/ml/common/constants/field_types'; -export function MachineLearningDataVisualizerIndexBasedProvider( - { getService }: FtrProviderContext, - mlDataVisualizerTable: MlDataVisualizerTable -) { +export function MachineLearningDataVisualizerIndexBasedProvider({ + getService, +}: FtrProviderContext) { const testSubjects = getService('testSubjects'); const retry = getService('retry'); - const browser = getService('browser'); return { async assertTimeRangeSelectorSectionExists() { @@ -46,14 +40,6 @@ export function MachineLearningDataVisualizerIndexBasedProvider( await testSubjects.existOrFail(`mlFieldDataDocumentCountChart`); }, - async assertSearchPanelExist() { - await testSubjects.existOrFail(`mlDataVisualizerSearchPanel`); - }, - - async assertSearchQueryInputExist() { - await testSubjects.existOrFail(`mlDataVisualizerQueryInput`); - }, - async assertFieldCountPanelExist() { await testSubjects.existOrFail(`mlDataVisualizerFieldCountPanel`); }, @@ -122,105 +108,6 @@ export function MachineLearningDataVisualizerIndexBasedProvider( await testSubjects.existOrFail(`mlDataVisualizerTable`); }, - async assertShowEmptyFieldsSwitchExists() { - await testSubjects.existOrFail('mlDataVisualizerShowEmptyFieldsSwitch'); - }, - - async assertShowEmptyFieldsCheckState(expectedCheckState: boolean) { - const actualCheckState = - (await testSubjects.getAttribute( - 'mlDataVisualizerShowEmptyFieldsSwitch', - 'aria-checked' - )) === 'true'; - expect(actualCheckState).to.eql( - expectedCheckState, - `Show empty fields check state should be '${expectedCheckState}' (got '${actualCheckState}')` - ); - return actualCheckState === expectedCheckState; - }, - - async setShowEmptyFieldsSwitchState(checkState: boolean) { - if (await this.assertShowEmptyFieldsCheckState(!checkState)) { - await testSubjects.click('mlDataVisualizerShowEmptyFieldsSwitch'); - } - await this.assertShowEmptyFieldsCheckState(checkState); - }, - - async assertFieldNameInputExists() { - await testSubjects.existOrFail('mlDataVisualizerFieldNameSelect'); - }, - - async assertFieldTypeInputExists() { - await testSubjects.existOrFail('mlDataVisualizerFieldTypeSelect'); - }, - - async assertSampleSizeInputExists() { - await testSubjects.existOrFail('mlDataVisualizerShardSizeSelect'); - }, - - async setSampleSizeInputValue( - sampleSize: number, - fieldName: string, - docCountFormatted: string - ) { - await this.assertSampleSizeInputExists(); - await testSubjects.clickWhenNotDisabled('mlDataVisualizerShardSizeSelect'); - await testSubjects.existOrFail(`mlDataVisualizerShardSizeOption ${sampleSize}`); - await testSubjects.click(`mlDataVisualizerShardSizeOption ${sampleSize}`); - - await retry.tryForTime(5000, async () => { - await mlDataVisualizerTable.assertFieldDocCount(fieldName, docCountFormatted); - }); - }, - - async setFieldTypeFilter(fieldTypes: string[], expectedRowCount = 1) { - await this.assertFieldNameInputExists(); - await testSubjects.clickWhenNotDisabled('mlDataVisualizerFieldTypeSelect-button'); - await testSubjects.existOrFail('mlDataVisualizerFieldTypeSelect-popover'); - await testSubjects.existOrFail('mlDataVisualizerFieldTypeSelect-searchInput'); - const searchBarInput = await testSubjects.find(`mlDataVisualizerFieldTypeSelect-searchInput`); - - await asyncForEach(fieldTypes, async (fieldType) => { - await retry.tryForTime(5000, async () => { - await searchBarInput.clearValueWithKeyboard(); - await searchBarInput.type(fieldType); - await testSubjects.existOrFail(`mlDataVisualizerFieldTypeSelect-option-${fieldType}`); - await testSubjects.click(`mlDataVisualizerFieldTypeSelect-option-${fieldType}`); - }); - }); - - // escape popover - await browser.pressKeys(browser.keys.ESCAPE); - await mlDataVisualizerTable.assertTableRowCount(expectedRowCount); - }, - - async removeFieldTypeFilter(fieldTypes: string[], expectedRowCount = 1) { - await this.setFieldTypeFilter(fieldTypes, expectedRowCount); - }, - - async setFieldNameFilter(fieldNames: string[], expectedRowCount = 1) { - await this.assertFieldNameInputExists(); - await testSubjects.clickWhenNotDisabled('mlDataVisualizerFieldNameSelect-button'); - await testSubjects.existOrFail('mlDataVisualizerFieldNameSelect-popover'); - await testSubjects.existOrFail('mlDataVisualizerFieldNameSelect-searchInput'); - const searchBarInput = await testSubjects.find(`mlDataVisualizerFieldNameSelect-searchInput`); - - await asyncForEach(fieldNames, async (filterString) => { - await retry.tryForTime(5000, async () => { - await searchBarInput.clearValueWithKeyboard(); - await searchBarInput.type(filterString); - await testSubjects.existOrFail(`mlDataVisualizerFieldNameSelect-option-${filterString}`); - await testSubjects.click(`mlDataVisualizerFieldNameSelect-option-${filterString}`); - }); - }); - await browser.pressKeys(browser.keys.ESCAPE); - await mlDataVisualizerTable.assertTableRowCount(expectedRowCount); - }, - - async removeFieldNameFilter(fieldNames: string[], expectedRowCount: number) { - await this.setFieldNameFilter(fieldNames, expectedRowCount); - }, - async assertActionsPanelExists() { await testSubjects.existOrFail('mlDataVisualizerActionsPanel'); }, @@ -248,110 +135,5 @@ export function MachineLearningDataVisualizerIndexBasedProvider( async clickCreateAdvancedJobButton() { await testSubjects.clickWhenNotDisabled('mlDataVisualizerCreateAdvancedJobCard'); }, - - async assertTopValuesContents(fieldName: string, expectedTopValuesCount: number) { - const selector = mlDataVisualizerTable.detailsSelector(fieldName, 'mlFieldDataTopValues'); - const topValuesElement = await testSubjects.find(selector); - const topValuesBars = await topValuesElement.findAllByTestSubject('mlFieldDataTopValueBar'); - expect(topValuesBars).to.have.length( - expectedTopValuesCount, - `Expected top values count for field '${fieldName}' to be '${expectedTopValuesCount}' (got '${topValuesBars.length}')` - ); - }, - - async assertDistributionPreviewExist(fieldName: string) { - await testSubjects.existOrFail( - mlDataVisualizerTable.rowSelector(fieldName, `mlDataGridChart-${fieldName}`) - ); - await testSubjects.existOrFail( - mlDataVisualizerTable.rowSelector(fieldName, `mlDataGridChart-${fieldName}-histogram`) - ); - }, - - async assertNumberFieldContents( - fieldName: string, - docCountFormatted: string, - topValuesCount: number - ) { - await mlDataVisualizerTable.assertRowExists(fieldName); - await mlDataVisualizerTable.assertFieldDocCount(fieldName, docCountFormatted); - await mlDataVisualizerTable.ensureDetailsOpen(fieldName); - - await testSubjects.existOrFail( - mlDataVisualizerTable.detailsSelector(fieldName, 'mlNumberSummaryTable') - ); - - await testSubjects.existOrFail( - mlDataVisualizerTable.detailsSelector(fieldName, 'mlTopValues') - ); - await this.assertTopValuesContents(fieldName, topValuesCount); - - await this.assertDistributionPreviewExist(fieldName); - - await mlDataVisualizerTable.ensureDetailsClosed(fieldName); - }, - - async assertDateFieldContents(fieldName: string, docCountFormatted: string) { - await mlDataVisualizerTable.assertRowExists(fieldName); - await mlDataVisualizerTable.assertFieldDocCount(fieldName, docCountFormatted); - await mlDataVisualizerTable.ensureDetailsOpen(fieldName); - - await testSubjects.existOrFail( - mlDataVisualizerTable.detailsSelector(fieldName, 'mlDateSummaryTable') - ); - await mlDataVisualizerTable.ensureDetailsClosed(fieldName); - }, - - async assertKeywordFieldContents( - fieldName: string, - docCountFormatted: string, - topValuesCount: number - ) { - await mlDataVisualizerTable.assertRowExists(fieldName); - await mlDataVisualizerTable.assertFieldDocCount(fieldName, docCountFormatted); - await mlDataVisualizerTable.ensureDetailsOpen(fieldName); - - await testSubjects.existOrFail( - mlDataVisualizerTable.detailsSelector(fieldName, 'mlFieldDataTopValues') - ); - await this.assertTopValuesContents(fieldName, topValuesCount); - await mlDataVisualizerTable.ensureDetailsClosed(fieldName); - }, - - async assertTextFieldContents( - fieldName: string, - docCountFormatted: string, - expectedExamplesCount: number - ) { - await mlDataVisualizerTable.assertRowExists(fieldName); - await mlDataVisualizerTable.assertFieldDocCount(fieldName, docCountFormatted); - await mlDataVisualizerTable.ensureDetailsOpen(fieldName); - - const examplesList = await testSubjects.find( - mlDataVisualizerTable.detailsSelector(fieldName, 'mlFieldDataExamplesList') - ); - const examplesListItems = await examplesList.findAllByTagName('li'); - expect(examplesListItems).to.have.length( - expectedExamplesCount, - `Expected example list item count for field '${fieldName}' to be '${expectedExamplesCount}' (got '${examplesListItems.length}')` - ); - await mlDataVisualizerTable.ensureDetailsClosed(fieldName); - }, - - async assertNonMetricFieldContents( - fieldType: string, - fieldName: string, - docCountFormatted: string, - exampleCount: number - ) { - // Currently the data used in the data visualizer tests only contains these field types. - if (fieldType === ML_JOB_FIELD_TYPES.DATE) { - await this.assertDateFieldContents(fieldName, docCountFormatted); - } else if (fieldType === ML_JOB_FIELD_TYPES.KEYWORD) { - await this.assertKeywordFieldContents(fieldName, docCountFormatted, exampleCount); - } else if (fieldType === ML_JOB_FIELD_TYPES.TEXT) { - await this.assertTextFieldContents(fieldName, docCountFormatted, exampleCount); - } - }, }; } diff --git a/x-pack/test/functional/services/ml/data_visualizer_table.ts b/x-pack/test/functional/services/ml/data_visualizer_table.ts index 9abc8ec4b8af2..31b24e68ea1d3 100644 --- a/x-pack/test/functional/services/ml/data_visualizer_table.ts +++ b/x-pack/test/functional/services/ml/data_visualizer_table.ts @@ -6,11 +6,14 @@ import expect from '@kbn/expect'; import { ProvidedType } from '@kbn/test/types/ftr'; import { FtrProviderContext } from '../../ftr_provider_context'; +import { asyncForEach } from '../../apps/ml/settings/common'; +import { ML_JOB_FIELD_TYPES } from '../../../../plugins/ml/common/constants/field_types'; export type MlDataVisualizerTable = ProvidedType; export function MachineLearningDataVisualizerTableProvider({ getService }: FtrProviderContext) { const retry = getService('retry'); const testSubjects = getService('testSubjects'); + const browser = getService('browser'); return new (class DataVisualizerTable { public async parseDataVisualizerTable() { @@ -135,5 +138,209 @@ export function MachineLearningDataVisualizerTableProvider({ getService }: FtrPr const selector = this.rowSelector(fieldName, 'mlDataVisualizerTableColumnDistribution'); await testSubjects.existOrFail(selector); } + + public async assertSearchPanelExist() { + await testSubjects.existOrFail(`mlDataVisualizerSearchPanel`); + } + + public async assertFieldNameInputExists() { + await testSubjects.existOrFail('mlDataVisualizerFieldNameSelect'); + } + + public async assertFieldTypeInputExists() { + await testSubjects.existOrFail('mlDataVisualizerFieldTypeSelect'); + } + + public async assertSampleSizeInputExists() { + await testSubjects.existOrFail('mlDataVisualizerShardSizeSelect'); + } + + public async setSampleSizeInputValue( + sampleSize: number, + fieldName: string, + docCountFormatted: string + ) { + await this.assertSampleSizeInputExists(); + await testSubjects.clickWhenNotDisabled('mlDataVisualizerShardSizeSelect'); + await testSubjects.existOrFail(`mlDataVisualizerShardSizeOption ${sampleSize}`); + await testSubjects.click(`mlDataVisualizerShardSizeOption ${sampleSize}`); + + await retry.tryForTime(5000, async () => { + await this.assertFieldDocCount(fieldName, docCountFormatted); + }); + } + + public async setFieldTypeFilter(fieldTypes: string[], expectedRowCount = 1) { + await this.assertFieldNameInputExists(); + await testSubjects.clickWhenNotDisabled('mlDataVisualizerFieldTypeSelect-button'); + await testSubjects.existOrFail('mlDataVisualizerFieldTypeSelect-popover'); + await testSubjects.existOrFail('mlDataVisualizerFieldTypeSelect-searchInput'); + const searchBarInput = await testSubjects.find(`mlDataVisualizerFieldTypeSelect-searchInput`); + + await asyncForEach(fieldTypes, async (fieldType) => { + await retry.tryForTime(5000, async () => { + await searchBarInput.clearValueWithKeyboard(); + await searchBarInput.type(fieldType); + await testSubjects.existOrFail(`mlDataVisualizerFieldTypeSelect-option-${fieldType}`); + await testSubjects.click(`mlDataVisualizerFieldTypeSelect-option-${fieldType}`); + }); + }); + + // escape popover + await browser.pressKeys(browser.keys.ESCAPE); + await this.assertTableRowCount(expectedRowCount); + } + + async removeFieldTypeFilter(fieldTypes: string[], expectedRowCount = 1) { + await this.setFieldTypeFilter(fieldTypes, expectedRowCount); + } + + public async setFieldNameFilter(fieldNames: string[], expectedRowCount = 1) { + await this.assertFieldNameInputExists(); + await testSubjects.clickWhenNotDisabled('mlDataVisualizerFieldNameSelect-button'); + await testSubjects.existOrFail('mlDataVisualizerFieldNameSelect-popover'); + await testSubjects.existOrFail('mlDataVisualizerFieldNameSelect-searchInput'); + const searchBarInput = await testSubjects.find(`mlDataVisualizerFieldNameSelect-searchInput`); + + await asyncForEach(fieldNames, async (filterString) => { + await retry.tryForTime(5000, async () => { + await searchBarInput.clearValueWithKeyboard(); + await searchBarInput.type(filterString); + await testSubjects.existOrFail(`mlDataVisualizerFieldNameSelect-option-${filterString}`); + await testSubjects.click(`mlDataVisualizerFieldNameSelect-option-${filterString}`); + }); + }); + await browser.pressKeys(browser.keys.ESCAPE); + await this.assertTableRowCount(expectedRowCount); + } + + public async removeFieldNameFilter(fieldNames: string[], expectedRowCount: number) { + await this.setFieldNameFilter(fieldNames, expectedRowCount); + } + + public async assertShowEmptyFieldsSwitchExists() { + await testSubjects.existOrFail('mlDataVisualizerShowEmptyFieldsSwitch'); + } + + public async assertShowEmptyFieldsCheckState(expectedCheckState: boolean) { + const actualCheckState = + (await testSubjects.getAttribute( + 'mlDataVisualizerShowEmptyFieldsSwitch', + 'aria-checked' + )) === 'true'; + expect(actualCheckState).to.eql( + expectedCheckState, + `Show empty fields check state should be '${expectedCheckState}' (got '${actualCheckState}')` + ); + return actualCheckState === expectedCheckState; + } + + public async setShowEmptyFieldsSwitchState(checkState: boolean, expectedEmptyFields: string[]) { + await this.assertShowEmptyFieldsSwitchExists(); + await retry.tryForTime(5000, async () => { + if (await this.assertShowEmptyFieldsCheckState(!checkState)) { + await testSubjects.click('mlDataVisualizerShowEmptyFieldsSwitch'); + } + await this.assertShowEmptyFieldsCheckState(checkState); + for (const field of expectedEmptyFields) { + await this.assertRowExists(field); + } + }); + } + + public async assertTopValuesContents(fieldName: string, expectedTopValuesCount: number) { + const selector = this.detailsSelector(fieldName, 'mlFieldDataTopValues'); + const topValuesElement = await testSubjects.find(selector); + const topValuesBars = await topValuesElement.findAllByTestSubject('mlFieldDataTopValueBar'); + expect(topValuesBars).to.have.length( + expectedTopValuesCount, + `Expected top values count for field '${fieldName}' to be '${expectedTopValuesCount}' (got '${topValuesBars.length}')` + ); + } + + public async assertDistributionPreviewExist(fieldName: string) { + await testSubjects.existOrFail(this.rowSelector(fieldName, `mlDataGridChart-${fieldName}`)); + await testSubjects.existOrFail( + this.rowSelector(fieldName, `mlDataGridChart-${fieldName}-histogram`) + ); + } + + public async assertNumberFieldContents( + fieldName: string, + docCountFormatted: string, + topValuesCount: number + ) { + await this.assertRowExists(fieldName); + await this.assertFieldDocCount(fieldName, docCountFormatted); + await this.ensureDetailsOpen(fieldName); + + await testSubjects.existOrFail(this.detailsSelector(fieldName, 'mlNumberSummaryTable')); + + await testSubjects.existOrFail(this.detailsSelector(fieldName, 'mlTopValues')); + await this.assertTopValuesContents(fieldName, topValuesCount); + + await this.assertDistributionPreviewExist(fieldName); + + await this.ensureDetailsClosed(fieldName); + } + + public async assertDateFieldContents(fieldName: string, docCountFormatted: string) { + await this.assertRowExists(fieldName); + await this.assertFieldDocCount(fieldName, docCountFormatted); + await this.ensureDetailsOpen(fieldName); + + await testSubjects.existOrFail(this.detailsSelector(fieldName, 'mlDateSummaryTable')); + await this.ensureDetailsClosed(fieldName); + } + + public async assertKeywordFieldContents( + fieldName: string, + docCountFormatted: string, + topValuesCount: number + ) { + await this.assertRowExists(fieldName); + await this.assertFieldDocCount(fieldName, docCountFormatted); + await this.ensureDetailsOpen(fieldName); + + await testSubjects.existOrFail(this.detailsSelector(fieldName, 'mlFieldDataTopValues')); + await this.assertTopValuesContents(fieldName, topValuesCount); + await this.ensureDetailsClosed(fieldName); + } + + public async assertTextFieldContents( + fieldName: string, + docCountFormatted: string, + expectedExamplesCount: number + ) { + await this.assertRowExists(fieldName); + await this.assertFieldDocCount(fieldName, docCountFormatted); + await this.ensureDetailsOpen(fieldName); + + const examplesList = await testSubjects.find( + this.detailsSelector(fieldName, 'mlFieldDataExamplesList') + ); + const examplesListItems = await examplesList.findAllByTagName('li'); + expect(examplesListItems).to.have.length( + expectedExamplesCount, + `Expected example list item count for field '${fieldName}' to be '${expectedExamplesCount}' (got '${examplesListItems.length}')` + ); + await this.ensureDetailsClosed(fieldName); + } + + public async assertNonMetricFieldContents( + fieldType: string, + fieldName: string, + docCountFormatted: string, + exampleCount: number + ) { + // Currently the data used in the data visualizer tests only contains these field types. + if (fieldType === ML_JOB_FIELD_TYPES.DATE) { + await this.assertDateFieldContents(fieldName, docCountFormatted); + } else if (fieldType === ML_JOB_FIELD_TYPES.KEYWORD) { + await this.assertKeywordFieldContents(fieldName, docCountFormatted, exampleCount); + } else if (fieldType === ML_JOB_FIELD_TYPES.TEXT) { + await this.assertTextFieldContents(fieldName, docCountFormatted, exampleCount); + } + } })(); } From a17d93d3b530f2ea4791c62fa00bcfb65935a000 Mon Sep 17 00:00:00 2001 From: Quynh Nguyen Date: Wed, 16 Dec 2020 16:24:04 -0600 Subject: [PATCH 7/9] [ML] Add 'checked' marker to multi select comp --- .../multi_select_picker.tsx | 35 +++---- .../services/ml/data_visualizer_table.ts | 95 +++++++++++++------ 2 files changed, 87 insertions(+), 43 deletions(-) diff --git a/x-pack/plugins/ml/public/application/components/multi_select_picker/multi_select_picker.tsx b/x-pack/plugins/ml/public/application/components/multi_select_picker/multi_select_picker.tsx index c982b087a31b1..659c4a1005453 100644 --- a/x-pack/plugins/ml/public/application/components/multi_select_picker/multi_select_picker.tsx +++ b/x-pack/plugins/ml/public/application/components/multi_select_picker/multi_select_picker.tsx @@ -115,22 +115,25 @@ export const MultiSelectPicker: FC<{
{Array.isArray(items) && items.length > 0 ? ( - items.map((item, index) => ( - fieldValue === item.value) > -1 - ? 'on' - : undefined - } - key={index} - onClick={() => handleOnChange(index)} - style={{ flexDirection: 'row' }} - data-test-subj={`${dataTestSubj}-option-${item.value}`} - > - {item.name ?? item.value} - - )) + items.map((item, index) => { + const checked = + checkedOptions && + checkedOptions.findIndex((fieldValue) => fieldValue === item.value) > -1; + + return ( + handleOnChange(index)} + style={{ flexDirection: 'row' }} + data-test-subj={`${dataTestSubj}-option-${item.value}${ + checked ? '-checked' : '' + }`} + > + {item.name ?? item.value} + + ); + }) ) : ( )} diff --git a/x-pack/test/functional/services/ml/data_visualizer_table.ts b/x-pack/test/functional/services/ml/data_visualizer_table.ts index 31b24e68ea1d3..f5f9770508235 100644 --- a/x-pack/test/functional/services/ml/data_visualizer_table.ts +++ b/x-pack/test/functional/services/ml/data_visualizer_table.ts @@ -6,7 +6,6 @@ import expect from '@kbn/expect'; import { ProvidedType } from '@kbn/test/types/ftr'; import { FtrProviderContext } from '../../ftr_provider_context'; -import { asyncForEach } from '../../apps/ml/settings/common'; import { ML_JOB_FIELD_TYPES } from '../../../../plugins/ml/common/constants/field_types'; export type MlDataVisualizerTable = ProvidedType; @@ -170,52 +169,94 @@ export function MachineLearningDataVisualizerTableProvider({ getService }: FtrPr }); } - public async setFieldTypeFilter(fieldTypes: string[], expectedRowCount = 1) { - await this.assertFieldNameInputExists(); - await testSubjects.clickWhenNotDisabled('mlDataVisualizerFieldTypeSelect-button'); - await testSubjects.existOrFail('mlDataVisualizerFieldTypeSelect-popover'); - await testSubjects.existOrFail('mlDataVisualizerFieldTypeSelect-searchInput'); - const searchBarInput = await testSubjects.find(`mlDataVisualizerFieldTypeSelect-searchInput`); + public async setMultiSelectFilter( + testDataSubj: string, + fieldTypes: string[], + expectedRowCount = 1 + ) { + await testSubjects.clickWhenNotDisabled(`${testDataSubj}-button`); + await testSubjects.existOrFail(`${testDataSubj}-popover`); + await testSubjects.existOrFail(`${testDataSubj}-searchInput`); + const searchBarInput = await testSubjects.find(`${testDataSubj}-searchInput`); - await asyncForEach(fieldTypes, async (fieldType) => { + for (const fieldType of fieldTypes) { await retry.tryForTime(5000, async () => { await searchBarInput.clearValueWithKeyboard(); await searchBarInput.type(fieldType); - await testSubjects.existOrFail(`mlDataVisualizerFieldTypeSelect-option-${fieldType}`); - await testSubjects.click(`mlDataVisualizerFieldTypeSelect-option-${fieldType}`); + if (!(await testSubjects.exists(`${testDataSubj}-option-${fieldType}-checked`))) { + await testSubjects.existOrFail(`${testDataSubj}-option-${fieldType}`); + await testSubjects.click(`${testDataSubj}-option-${fieldType}`); + await testSubjects.existOrFail(`${testDataSubj}-option-${fieldType}-checked`); + } }); - }); + } // escape popover await browser.pressKeys(browser.keys.ESCAPE); await this.assertTableRowCount(expectedRowCount); } - async removeFieldTypeFilter(fieldTypes: string[], expectedRowCount = 1) { - await this.setFieldTypeFilter(fieldTypes, expectedRowCount); - } - - public async setFieldNameFilter(fieldNames: string[], expectedRowCount = 1) { - await this.assertFieldNameInputExists(); - await testSubjects.clickWhenNotDisabled('mlDataVisualizerFieldNameSelect-button'); - await testSubjects.existOrFail('mlDataVisualizerFieldNameSelect-popover'); - await testSubjects.existOrFail('mlDataVisualizerFieldNameSelect-searchInput'); - const searchBarInput = await testSubjects.find(`mlDataVisualizerFieldNameSelect-searchInput`); + async removeMultiSelectFilter( + testDataSubj: string, + fieldTypes: string[], + expectedRowCount = 1 + ) { + await testSubjects.clickWhenNotDisabled(`${testDataSubj}-button`); + await testSubjects.existOrFail(`${testDataSubj}-popover`); + await testSubjects.existOrFail(`${testDataSubj}-searchInput`); + const searchBarInput = await testSubjects.find(`${testDataSubj}-searchInput`); - await asyncForEach(fieldNames, async (filterString) => { + for (const fieldType of fieldTypes) { await retry.tryForTime(5000, async () => { await searchBarInput.clearValueWithKeyboard(); - await searchBarInput.type(filterString); - await testSubjects.existOrFail(`mlDataVisualizerFieldNameSelect-option-${filterString}`); - await testSubjects.click(`mlDataVisualizerFieldNameSelect-option-${filterString}`); + await searchBarInput.type(fieldType); + if (!(await testSubjects.exists(`${testDataSubj}-option-${fieldType}`))) { + await testSubjects.existOrFail(`${testDataSubj}-option-${fieldType}-checked`); + await testSubjects.click(`${testDataSubj}-option-${fieldType}-checked`); + await testSubjects.existOrFail(`${testDataSubj}-option-${fieldType}`); + } }); - }); + } + + // escape popover await browser.pressKeys(browser.keys.ESCAPE); await this.assertTableRowCount(expectedRowCount); } + public async setFieldTypeFilter(fieldTypes: string[], expectedRowCount = 1) { + await this.assertFieldTypeInputExists(); + await this.setMultiSelectFilter( + 'mlDataVisualizerFieldTypeSelect', + fieldTypes, + expectedRowCount + ); + } + + async removeFieldTypeFilter(fieldTypes: string[], expectedRowCount = 1) { + await this.assertFieldTypeInputExists(); + await this.removeMultiSelectFilter( + 'mlDataVisualizerFieldTypeSelect', + fieldTypes, + expectedRowCount + ); + } + + public async setFieldNameFilter(fieldNames: string[], expectedRowCount = 1) { + await this.assertFieldNameInputExists(); + await this.setMultiSelectFilter( + 'mlDataVisualizerFieldNameSelect', + fieldNames, + expectedRowCount + ); + } + public async removeFieldNameFilter(fieldNames: string[], expectedRowCount: number) { - await this.setFieldNameFilter(fieldNames, expectedRowCount); + await this.assertFieldNameInputExists(); + await this.removeMultiSelectFilter( + 'mlDataVisualizerFieldNameSelect', + fieldNames, + expectedRowCount + ); } public async assertShowEmptyFieldsSwitchExists() { From a64d33ac15e0019205cf6100577af81e5f1e8a44 Mon Sep 17 00:00:00 2001 From: Quynh Nguyen Date: Wed, 16 Dec 2020 16:47:20 -0600 Subject: [PATCH 8/9] [ML] Remove dataVisualizerTable arg --- x-pack/test/functional/services/ml/index.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/x-pack/test/functional/services/ml/index.ts b/x-pack/test/functional/services/ml/index.ts index 86299f41d2e7f..b5c823b0cbd65 100644 --- a/x-pack/test/functional/services/ml/index.ts +++ b/x-pack/test/functional/services/ml/index.ts @@ -66,10 +66,7 @@ export function MachineLearningProvider(context: FtrProviderContext) { const dataVisualizerTable = MachineLearningDataVisualizerTableProvider(context); const dataVisualizerFileBased = MachineLearningDataVisualizerFileBasedProvider(context, commonUI); - const dataVisualizerIndexBased = MachineLearningDataVisualizerIndexBasedProvider( - context, - dataVisualizerTable - ); + const dataVisualizerIndexBased = MachineLearningDataVisualizerIndexBasedProvider(context); const jobManagement = MachineLearningJobManagementProvider(context, api); const jobSelection = MachineLearningJobSelectionProvider(context); From 55819316abe5e29c01932dbc71b53f51ece2f9d6 Mon Sep 17 00:00:00 2001 From: Quynh Nguyen Date: Thu, 17 Dec 2020 09:58:20 -0600 Subject: [PATCH 9/9] [ML] Move setMultiSelectFilter, removeMultiSelectFilter to mlCommonUi, remove Filtered, rearrange assertion block --- .../data_visualizer/index_data_visualizer.ts | 12 +-- .../test/functional/services/ml/common_ui.ts | 45 +++++++++ .../services/ml/data_visualizer_table.ts | 91 +++---------------- x-pack/test/functional/services/ml/index.ts | 2 +- 4 files changed, 63 insertions(+), 87 deletions(-) diff --git a/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer.ts b/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer.ts index a187a857c3f38..5a8b9bfc114ee 100644 --- a/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer.ts +++ b/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer.ts @@ -328,16 +328,15 @@ export default function ({ getService }: FtrProviderContext) { await ml.dataVisualizerIndexBased.assertTotalDocCountChartExist(); await ml.testExecution.logTestStep( - `${testData.suiteTitle} displays elements in the search panel correctly` + `${testData.suiteTitle} displays elements in the data visualizer table correctly` ); + await ml.dataVisualizerIndexBased.assertDataVisualizerTableExist(); + await ml.dataVisualizerTable.assertSearchPanelExist(); await ml.dataVisualizerTable.assertSampleSizeInputExists(); await ml.dataVisualizerTable.assertFieldTypeInputExists(); await ml.dataVisualizerTable.assertFieldNameInputExists(); - await ml.testExecution.logTestStep( - `${testData.suiteTitle} displays elements in the field count panel correctly` - ); await ml.dataVisualizerIndexBased.assertFieldCountPanelExist(); await ml.dataVisualizerIndexBased.assertMetricFieldsSummaryExist(); await ml.dataVisualizerIndexBased.assertFieldsSummaryExist(); @@ -352,11 +351,6 @@ export default function ({ getService }: FtrProviderContext) { ); await ml.dataVisualizerIndexBased.assertTotalFieldsCount(testData.expected.totalFieldsCount); - await ml.testExecution.logTestStep( - `${testData.suiteTitle} displays the data visualizer table` - ); - await ml.dataVisualizerIndexBased.assertDataVisualizerTableExist(); - await ml.testExecution.logTestStep( 'displays details for metric fields and non-metric fields correctly' ); diff --git a/x-pack/test/functional/services/ml/common_ui.ts b/x-pack/test/functional/services/ml/common_ui.ts index ef2fccab8a2cc..8113a1ab9ac5f 100644 --- a/x-pack/test/functional/services/ml/common_ui.ts +++ b/x-pack/test/functional/services/ml/common_ui.ts @@ -20,6 +20,7 @@ export function MachineLearningCommonUIProvider({ getService }: FtrProviderConte const retry = getService('retry'); const testSubjects = getService('testSubjects'); const find = getService('find'); + const browser = getService('browser'); return { async setValueWithChecks( @@ -116,5 +117,49 @@ export function MachineLearningCommonUIProvider({ getService }: FtrProviderConte await label.click(); await this.assertRadioGroupValue(testSubject, value); }, + + async setMultiSelectFilter(testDataSubj: string, fieldTypes: string[]) { + await testSubjects.clickWhenNotDisabled(`${testDataSubj}-button`); + await testSubjects.existOrFail(`${testDataSubj}-popover`); + await testSubjects.existOrFail(`${testDataSubj}-searchInput`); + const searchBarInput = await testSubjects.find(`${testDataSubj}-searchInput`); + + for (const fieldType of fieldTypes) { + await retry.tryForTime(5000, async () => { + await searchBarInput.clearValueWithKeyboard(); + await searchBarInput.type(fieldType); + if (!(await testSubjects.exists(`${testDataSubj}-option-${fieldType}-checked`))) { + await testSubjects.existOrFail(`${testDataSubj}-option-${fieldType}`); + await testSubjects.click(`${testDataSubj}-option-${fieldType}`); + await testSubjects.existOrFail(`${testDataSubj}-option-${fieldType}-checked`); + } + }); + } + + // escape popover + await browser.pressKeys(browser.keys.ESCAPE); + }, + + async removeMultiSelectFilter(testDataSubj: string, fieldTypes: string[]) { + await testSubjects.clickWhenNotDisabled(`${testDataSubj}-button`); + await testSubjects.existOrFail(`${testDataSubj}-popover`); + await testSubjects.existOrFail(`${testDataSubj}-searchInput`); + const searchBarInput = await testSubjects.find(`${testDataSubj}-searchInput`); + + for (const fieldType of fieldTypes) { + await retry.tryForTime(5000, async () => { + await searchBarInput.clearValueWithKeyboard(); + await searchBarInput.type(fieldType); + if (!(await testSubjects.exists(`${testDataSubj}-option-${fieldType}`))) { + await testSubjects.existOrFail(`${testDataSubj}-option-${fieldType}-checked`); + await testSubjects.click(`${testDataSubj}-option-${fieldType}-checked`); + await testSubjects.existOrFail(`${testDataSubj}-option-${fieldType}`); + } + }); + } + + // escape popover + await browser.pressKeys(browser.keys.ESCAPE); + }, }; } diff --git a/x-pack/test/functional/services/ml/data_visualizer_table.ts b/x-pack/test/functional/services/ml/data_visualizer_table.ts index f5f9770508235..f8623842a596d 100644 --- a/x-pack/test/functional/services/ml/data_visualizer_table.ts +++ b/x-pack/test/functional/services/ml/data_visualizer_table.ts @@ -7,12 +7,15 @@ import expect from '@kbn/expect'; import { ProvidedType } from '@kbn/test/types/ftr'; import { FtrProviderContext } from '../../ftr_provider_context'; import { ML_JOB_FIELD_TYPES } from '../../../../plugins/ml/common/constants/field_types'; +import { MlCommonUI } from './common_ui'; export type MlDataVisualizerTable = ProvidedType; -export function MachineLearningDataVisualizerTableProvider({ getService }: FtrProviderContext) { +export function MachineLearningDataVisualizerTableProvider( + { getService }: FtrProviderContext, + mlCommonUI: MlCommonUI +) { const retry = getService('retry'); const testSubjects = getService('testSubjects'); - const browser = getService('browser'); return new (class DataVisualizerTable { public async parseDataVisualizerTable() { @@ -60,7 +63,7 @@ export function MachineLearningDataVisualizerTableProvider({ getService }: FtrPr const tableRows = await this.parseDataVisualizerTable(); expect(tableRows).to.have.length( expectedRowCount, - `Filtered Data Visualizer table should have ${expectedRowCount} row(s) (got '${tableRows.length}')` + `Data Visualizer table should have ${expectedRowCount} row(s) (got '${tableRows.length}')` ); }); } @@ -169,94 +172,28 @@ export function MachineLearningDataVisualizerTableProvider({ getService }: FtrPr }); } - public async setMultiSelectFilter( - testDataSubj: string, - fieldTypes: string[], - expectedRowCount = 1 - ) { - await testSubjects.clickWhenNotDisabled(`${testDataSubj}-button`); - await testSubjects.existOrFail(`${testDataSubj}-popover`); - await testSubjects.existOrFail(`${testDataSubj}-searchInput`); - const searchBarInput = await testSubjects.find(`${testDataSubj}-searchInput`); - - for (const fieldType of fieldTypes) { - await retry.tryForTime(5000, async () => { - await searchBarInput.clearValueWithKeyboard(); - await searchBarInput.type(fieldType); - if (!(await testSubjects.exists(`${testDataSubj}-option-${fieldType}-checked`))) { - await testSubjects.existOrFail(`${testDataSubj}-option-${fieldType}`); - await testSubjects.click(`${testDataSubj}-option-${fieldType}`); - await testSubjects.existOrFail(`${testDataSubj}-option-${fieldType}-checked`); - } - }); - } - - // escape popover - await browser.pressKeys(browser.keys.ESCAPE); - await this.assertTableRowCount(expectedRowCount); - } - - async removeMultiSelectFilter( - testDataSubj: string, - fieldTypes: string[], - expectedRowCount = 1 - ) { - await testSubjects.clickWhenNotDisabled(`${testDataSubj}-button`); - await testSubjects.existOrFail(`${testDataSubj}-popover`); - await testSubjects.existOrFail(`${testDataSubj}-searchInput`); - const searchBarInput = await testSubjects.find(`${testDataSubj}-searchInput`); - - for (const fieldType of fieldTypes) { - await retry.tryForTime(5000, async () => { - await searchBarInput.clearValueWithKeyboard(); - await searchBarInput.type(fieldType); - if (!(await testSubjects.exists(`${testDataSubj}-option-${fieldType}`))) { - await testSubjects.existOrFail(`${testDataSubj}-option-${fieldType}-checked`); - await testSubjects.click(`${testDataSubj}-option-${fieldType}-checked`); - await testSubjects.existOrFail(`${testDataSubj}-option-${fieldType}`); - } - }); - } - - // escape popover - await browser.pressKeys(browser.keys.ESCAPE); - await this.assertTableRowCount(expectedRowCount); - } - public async setFieldTypeFilter(fieldTypes: string[], expectedRowCount = 1) { await this.assertFieldTypeInputExists(); - await this.setMultiSelectFilter( - 'mlDataVisualizerFieldTypeSelect', - fieldTypes, - expectedRowCount - ); + await mlCommonUI.setMultiSelectFilter('mlDataVisualizerFieldTypeSelect', fieldTypes); + await this.assertTableRowCount(expectedRowCount); } async removeFieldTypeFilter(fieldTypes: string[], expectedRowCount = 1) { await this.assertFieldTypeInputExists(); - await this.removeMultiSelectFilter( - 'mlDataVisualizerFieldTypeSelect', - fieldTypes, - expectedRowCount - ); + await mlCommonUI.removeMultiSelectFilter('mlDataVisualizerFieldTypeSelect', fieldTypes); + await this.assertTableRowCount(expectedRowCount); } public async setFieldNameFilter(fieldNames: string[], expectedRowCount = 1) { await this.assertFieldNameInputExists(); - await this.setMultiSelectFilter( - 'mlDataVisualizerFieldNameSelect', - fieldNames, - expectedRowCount - ); + await mlCommonUI.setMultiSelectFilter('mlDataVisualizerFieldNameSelect', fieldNames); + await this.assertTableRowCount(expectedRowCount); } public async removeFieldNameFilter(fieldNames: string[], expectedRowCount: number) { await this.assertFieldNameInputExists(); - await this.removeMultiSelectFilter( - 'mlDataVisualizerFieldNameSelect', - fieldNames, - expectedRowCount - ); + await mlCommonUI.removeMultiSelectFilter('mlDataVisualizerFieldNameSelect', fieldNames); + await this.assertTableRowCount(expectedRowCount); } public async assertShowEmptyFieldsSwitchExists() { diff --git a/x-pack/test/functional/services/ml/index.ts b/x-pack/test/functional/services/ml/index.ts index b5c823b0cbd65..8f8f8b7251242 100644 --- a/x-pack/test/functional/services/ml/index.ts +++ b/x-pack/test/functional/services/ml/index.ts @@ -63,7 +63,7 @@ export function MachineLearningProvider(context: FtrProviderContext) { const dataFrameAnalyticsTable = MachineLearningDataFrameAnalyticsTableProvider(context); const dataVisualizer = MachineLearningDataVisualizerProvider(context); - const dataVisualizerTable = MachineLearningDataVisualizerTableProvider(context); + const dataVisualizerTable = MachineLearningDataVisualizerTableProvider(context, commonUI); const dataVisualizerFileBased = MachineLearningDataVisualizerFileBasedProvider(context, commonUI); const dataVisualizerIndexBased = MachineLearningDataVisualizerIndexBasedProvider(context);