kibana/x-pack/test/functional/services/ml/data_frame_analytics_table.ts
Quynh Nguyen cbcc800a6b
[ML] Remove versioning check for Analytics list row expansion content functional tests (#98828)
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
2021-05-02 21:34:43 -05:00

437 lines
17 KiB
TypeScript

/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import expect from '@kbn/expect';
import { WebElementWrapper } from 'test/functional/services/lib/web_element_wrapper';
import { FtrProviderContext } from '../../ftr_provider_context';
type ExpectedSectionTableEntries = Record<string, string>;
export interface ExpectedSectionTable {
section: string;
expectedEntries: ExpectedSectionTableEntries;
}
export type AnalyticsTableRowDetails = Record<'jobDetails', ExpectedSectionTable[]>;
export function MachineLearningDataFrameAnalyticsTableProvider({ getService }: FtrProviderContext) {
const find = getService('find');
const retry = getService('retry');
const testSubjects = getService('testSubjects');
return new (class AnalyticsTable {
public async parseAnalyticsTable() {
const table = await testSubjects.find('~mlAnalyticsTable');
const $ = await table.parseDomContent();
const rows = [];
for (const tr of $.findTestSubjects('~mlAnalyticsTableRow').toArray()) {
const $tr = $(tr);
rows.push({
id: $tr
.findTestSubject('mlAnalyticsTableColumnId')
.find('.euiTableCellContent')
.text()
.trim(),
description: $tr
.findTestSubject('mlAnalyticsTableColumnJobDescription')
.find('.euiTableCellContent')
.text()
.trim(),
sourceIndex: $tr
.findTestSubject('mlAnalyticsTableColumnSourceIndex')
.find('.euiTableCellContent')
.text()
.trim(),
destinationIndex: $tr
.findTestSubject('mlAnalyticsTableColumnDestIndex')
.find('.euiTableCellContent')
.text()
.trim(),
type: $tr
.findTestSubject('mlAnalyticsTableColumnType')
.find('.euiTableCellContent')
.text()
.trim(),
status: $tr
.findTestSubject('mlAnalyticsTableColumnStatus')
.find('.euiTableCellContent')
.text()
.trim(),
progress: $tr
.findTestSubject('mlAnalyticsTableColumnProgress')
.findTestSubject('mlAnalyticsTableProgress')
.attr('value'),
});
}
return rows;
}
public rowSelector(analyticsId: string, subSelector?: string) {
const row = `~mlAnalyticsTable > ~row-${analyticsId}`;
return !subSelector ? row : `${row} > ${subSelector}`;
}
public async waitForRefreshButtonLoaded() {
await testSubjects.existOrFail('~mlAnalyticsRefreshListButton', { timeout: 10 * 1000 });
await testSubjects.existOrFail('mlAnalyticsRefreshListButton loaded', { timeout: 30 * 1000 });
}
public async refreshAnalyticsTable() {
await this.waitForRefreshButtonLoaded();
await testSubjects.click('~mlAnalyticsRefreshListButton');
await this.waitForRefreshButtonLoaded();
await this.waitForAnalyticsToLoad();
}
public async waitForAnalyticsToLoad() {
await testSubjects.existOrFail('~mlAnalyticsTable', { timeout: 60 * 1000 });
await testSubjects.existOrFail('mlAnalyticsTable loaded', { timeout: 30 * 1000 });
}
async getAnalyticsSearchInput(): Promise<WebElementWrapper> {
const tableListContainer = await testSubjects.find('mlAnalyticsTableContainer');
return await tableListContainer.findByClassName('euiFieldSearch');
}
public async assertJobRowViewButtonExists(analyticsId: string) {
await testSubjects.existOrFail(this.rowSelector(analyticsId, 'mlAnalyticsJobViewButton'));
}
public async assertJobRowMapButtonExists(analyticsId: string) {
await testSubjects.existOrFail(this.rowSelector(analyticsId, 'mlAnalyticsJobMapButton'));
}
public async assertJobRowViewButtonEnabled(analyticsId: string, expectedValue: boolean) {
const isEnabled = await testSubjects.isEnabled(
this.rowSelector(analyticsId, 'mlAnalyticsJobViewButton')
);
expect(isEnabled).to.eql(
expectedValue,
`Expected data frame analytics row "view results" button for job '${analyticsId}' to be '${
expectedValue ? 'enabled' : 'disabled'
}' (got '${isEnabled ? 'enabled' : 'disabled'}')`
);
}
public async openResultsView(analyticsId: string) {
await this.assertJobRowViewButtonExists(analyticsId);
await testSubjects.click(this.rowSelector(analyticsId, 'mlAnalyticsJobViewButton'));
await testSubjects.existOrFail('mlPageDataFrameAnalyticsExploration', { timeout: 20 * 1000 });
}
public async openMapView(analyticsId: string) {
await this.assertJobRowMapButtonExists(analyticsId);
await testSubjects.click(this.rowSelector(analyticsId, 'mlAnalyticsJobMapButton'));
await testSubjects.existOrFail('mlPageDataFrameAnalyticsMap', { timeout: 20 * 1000 });
}
public async assertAnalyticsSearchInputValue(expectedSearchValue: string) {
const searchBarInput = await this.getAnalyticsSearchInput();
const actualSearchValue = await searchBarInput.getAttribute('value');
expect(actualSearchValue).to.eql(
expectedSearchValue,
`Analytics search input value should be '${expectedSearchValue}' (got '${actualSearchValue}')`
);
}
public async filterWithSearchString(filter: string, expectedRowCount: number = 1) {
await this.waitForAnalyticsToLoad();
const searchBarInput = await this.getAnalyticsSearchInput();
await searchBarInput.clearValueWithKeyboard();
await searchBarInput.type(filter);
await this.assertAnalyticsSearchInputValue(filter);
const rows = await this.parseAnalyticsTable();
const filteredRows = rows.filter((row) => row.id === filter);
expect(filteredRows).to.have.length(
expectedRowCount,
`Filtered DFA job table should have ${expectedRowCount} row(s) for filter '${filter}' (got matching items '${filteredRows}')`
);
}
public async assertAnalyticsRowFields(analyticsId: string, expectedRow: object) {
await this.refreshAnalyticsTable();
const rows = await this.parseAnalyticsTable();
const analyticsRow = rows.filter((row) => row.id === analyticsId)[0];
expect(analyticsRow).to.eql(
expectedRow,
`Expected analytics row to be '${JSON.stringify(expectedRow)}' (got '${JSON.stringify(
analyticsRow
)}')`
);
}
public async assertJobRowActionsMenuButtonEnabled(analyticsId: string, expectedValue: boolean) {
const isEnabled = await testSubjects.isEnabled(
this.rowSelector(analyticsId, 'euiCollapsedItemActionsButton')
);
expect(isEnabled).to.eql(
expectedValue,
`Expected row action menu button for DFA job '${analyticsId}' to be '${
expectedValue ? 'enabled' : 'disabled'
}' (got '${isEnabled ? 'enabled' : 'disabled'}')`
);
}
public async ensureJobActionsMenuOpen(analyticsId: string) {
await retry.tryForTime(30 * 1000, async () => {
if (!(await testSubjects.exists('mlAnalyticsJobDeleteButton'))) {
await testSubjects.click(this.rowSelector(analyticsId, 'euiCollapsedItemActionsButton'));
await testSubjects.existOrFail('mlAnalyticsJobDeleteButton', { timeout: 5000 });
}
});
}
public async ensureJobActionsMenuClosed(analyticsId: string) {
await retry.tryForTime(30 * 1000, async () => {
if (await testSubjects.exists('mlAnalyticsJobDeleteButton')) {
await testSubjects.click(this.rowSelector(analyticsId, 'euiCollapsedItemActionsButton'));
await testSubjects.missingOrFail('mlAnalyticsJobDeleteButton', { timeout: 5000 });
}
});
}
public async assertJobActionViewButtonEnabled(analyticsId: string, expectedValue: boolean) {
await this.ensureJobActionsMenuOpen(analyticsId);
const actionMenuViewButton = await find.byCssSelector(
'[data-test-subj="mlAnalyticsJobViewButton"][class="euiContextMenuItem"]'
);
const isEnabled = await actionMenuViewButton.isEnabled();
expect(isEnabled).to.eql(
expectedValue,
`Expected "view" action menu button for DFA job '${analyticsId}' to be '${
expectedValue ? 'enabled' : 'disabled'
}' (got '${isEnabled ? 'enabled' : 'disabled'}')`
);
}
public async assertJobActionStartButtonEnabled(analyticsId: string, expectedValue: boolean) {
await this.ensureJobActionsMenuOpen(analyticsId);
const isEnabled = await testSubjects.isEnabled('mlAnalyticsJobStartButton');
expect(isEnabled).to.eql(
expectedValue,
`Expected "start" action menu button for DFA job '${analyticsId}' to be '${
expectedValue ? 'enabled' : 'disabled'
}' (got '${isEnabled ? 'enabled' : 'disabled'}')`
);
}
public async assertJobActionEditButtonEnabled(analyticsId: string, expectedValue: boolean) {
await this.ensureJobActionsMenuOpen(analyticsId);
const isEnabled = await testSubjects.isEnabled('mlAnalyticsJobEditButton');
expect(isEnabled).to.eql(
expectedValue,
`Expected "edit" action menu button for DFA job '${analyticsId}' to be '${
expectedValue ? 'enabled' : 'disabled'
}' (got '${isEnabled ? 'enabled' : 'disabled'}')`
);
}
public async assertJobActionCloneButtonEnabled(analyticsId: string, expectedValue: boolean) {
await this.ensureJobActionsMenuOpen(analyticsId);
const isEnabled = await testSubjects.isEnabled('mlAnalyticsJobCloneButton');
expect(isEnabled).to.eql(
expectedValue,
`Expected "clone" action menu button for DFA job '${analyticsId}' to be '${
expectedValue ? 'enabled' : 'disabled'
}' (got '${isEnabled ? 'enabled' : 'disabled'}')`
);
}
public async assertJobActionDeleteButtonEnabled(analyticsId: string, expectedValue: boolean) {
await this.ensureJobActionsMenuOpen(analyticsId);
const isEnabled = await testSubjects.isEnabled('mlAnalyticsJobDeleteButton');
expect(isEnabled).to.eql(
expectedValue,
`Expected "delete" action menu button for DFA job '${analyticsId}' to be '${
expectedValue ? 'enabled' : 'disabled'
}' (got '${isEnabled ? 'enabled' : 'disabled'}')`
);
}
public async openEditFlyout(analyticsId: string) {
await this.ensureJobActionsMenuOpen(analyticsId);
await testSubjects.click('mlAnalyticsJobEditButton');
await testSubjects.existOrFail('mlAnalyticsEditFlyout', { timeout: 5000 });
}
public async cloneJob(analyticsId: string) {
await this.ensureJobActionsMenuOpen(analyticsId);
await testSubjects.click(`mlAnalyticsJobCloneButton`);
await testSubjects.existOrFail('mlAnalyticsCreationContainer');
}
public detailsSelector(jobId: string, subSelector?: string) {
const row = `mlAnalyticsTableRowDetails-${jobId}`;
return !subSelector ? row : `${row} > ${subSelector}`;
}
public async assertRowDetailsTabExist(jobId: string, tabId: string) {
const selector = `~mlAnalyticsTableRowDetailsTab > ~${tabId} > ${jobId}`;
await testSubjects.existOrFail(selector);
}
public async withDetailsOpen<T>(jobId: string, block: () => Promise<T>): Promise<T> {
await this.ensureDetailsOpen(jobId);
try {
return await block();
} finally {
await this.ensureDetailsClosed(jobId);
}
}
public async ensureDetailsOpen(jobId: string) {
await retry.tryForTime(10000, async () => {
if (!(await testSubjects.exists(this.detailsSelector(jobId)))) {
await testSubjects.click(this.rowSelector(jobId, 'mlAnalyticsTableRowDetailsToggle'));
await testSubjects.existOrFail(this.detailsSelector(jobId), { timeout: 1000 });
}
});
}
public async ensureDetailsClosed(jobId: string) {
await retry.tryForTime(10000, async () => {
if (await testSubjects.exists(this.detailsSelector(jobId))) {
await testSubjects.click(this.rowSelector(jobId, 'mlAnalyticsTableRowDetailsToggle'));
await testSubjects.missingOrFail(this.detailsSelector(jobId), { timeout: 1000 });
}
});
}
public async assertRowDetailsTabsExist(tabTypeSubject: string, areaSubjects: string[]) {
await retry.tryForTime(10000, async () => {
const allTabs = await testSubjects.findAll(`~${tabTypeSubject}`, 3);
expect(allTabs).to.have.length(
areaSubjects.length,
`Expected number of '${tabTypeSubject}' to be '${areaSubjects.length}' (got '${allTabs.length}')`
);
for (const areaSubj of areaSubjects) {
await testSubjects.existOrFail(`~${tabTypeSubject}&~${areaSubj}`, { timeout: 1000 });
}
});
}
public async assertRowDetailsTabEnabled(tabSubject: string, expectedValue: boolean) {
const isEnabled = await testSubjects.isEnabled(tabSubject);
expect(isEnabled).to.eql(
expectedValue,
`Expected Analytics details tab '${tabSubject}' to be '${
expectedValue ? 'enabled' : 'disabled'
}' (got '${isEnabled ? 'enabled' : 'disabled'}')`
);
}
public async ensureDetailsTabOpen(jobId: string, tabSubject: string) {
const tabSelector = `~mlAnalyticsTableRowDetailsTab&~${tabSubject}&~${jobId}`;
const tabContentSelector = `~mlAnalyticsTableRowDetailsTabContent&~${tabSubject}&~${jobId}`;
await retry.tryForTime(10000, async () => {
if (!(await testSubjects.exists(tabContentSelector))) {
await this.assertRowDetailsTabEnabled(tabSelector, true);
await testSubjects.click(tabSelector);
await testSubjects.existOrFail(tabContentSelector, { timeout: 1000 });
}
});
}
public detailsSectionSelector(jobId: string, sectionSubject: string) {
const subSelector = `~mlAnalyticsTableRowDetailsSection&~${sectionSubject}`;
return this.detailsSelector(jobId, subSelector);
}
public async assertDetailsSectionExists(jobId: string, sectionSubject: string) {
const selector = this.detailsSectionSelector(jobId, sectionSubject);
await retry.tryForTime(10000, async () => {
await testSubjects.existOrFail(selector, { timeout: 1000 });
});
}
public async parseDetailsSectionTable(el: WebElementWrapper) {
const $ = await el.parseDomContent();
const vars: Record<string, string> = {};
for (const row of $('tr').toArray()) {
const [name, value] = $(row).find('td').toArray();
vars[$(name).text().trim()] = $(value).text().trim();
}
return vars;
}
public async assertRowDetailsSectionContent(
jobId: string,
sectionSubject: string,
expectedEntries: ExpectedSectionTable['expectedEntries']
) {
const sectionSelector = this.detailsSectionSelector(jobId, sectionSubject);
await this.assertDetailsSectionExists(jobId, sectionSubject);
const sectionTable = await testSubjects.find(`${sectionSelector}-table`);
const parsedSectionTableEntries = await this.parseDetailsSectionTable(sectionTable);
for (const [key, value] of Object.entries(expectedEntries)) {
expect(parsedSectionTableEntries)
.to.have.property(key)
.eql(
value,
`Expected ${sectionSubject} property '${key}' to exist with value '${value}'`
);
}
}
public async assertJobDetailsTabContent(jobId: string, sections: ExpectedSectionTable[]) {
const tabSubject = 'job-details';
await this.ensureDetailsTabOpen(jobId, tabSubject);
for (const { section, expectedEntries } of sections) {
await this.assertRowDetailsSectionContent(jobId, section, expectedEntries);
}
}
public async assertJobStatsTabContent(jobId: string) {
const tabSubject = 'job-stats';
await this.ensureDetailsTabOpen(jobId, tabSubject);
await this.assertDetailsSectionExists(jobId, 'stats');
await this.assertDetailsSectionExists(jobId, 'analysisStats');
}
public async assertJsonTabContent(jobId: string) {
const tabSubject = 'json';
await this.ensureDetailsTabOpen(jobId, tabSubject);
await testSubjects.existOrFail(this.detailsSelector(jobId, 'mlAnalyticsDetailsJsonPreview'));
}
public async assertJobMessagesTabContent(jobId: string) {
const tabSubject = 'job-messages';
await this.ensureDetailsTabOpen(jobId, tabSubject);
await testSubjects.existOrFail(
this.detailsSelector(jobId, 'mlAnalyticsDetailsJobMessagesTable')
);
}
public async assertAnalyticsRowDetails(
jobId: string,
expectedRowDetails: AnalyticsTableRowDetails
) {
return await this.withDetailsOpen(jobId, async () => {
await this.assertRowDetailsTabsExist('mlAnalyticsTableRowDetailsTab', [
'job-details',
'job-stats',
'json',
'job-messages',
]);
await this.assertJobDetailsTabContent(jobId, expectedRowDetails.jobDetails);
await this.assertJobStatsTabContent(jobId);
await this.assertJsonTabContent(jobId);
await this.assertJobMessagesTabContent(jobId);
});
}
})();
}