mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
Backports the following commits to 7.0: - [ftr/services/pipelineList] reduce calls to the browser to avoid flakiness (#31366)
This commit is contained in:
parent
03eeb6b025
commit
d565777054
9 changed files with 90 additions and 83 deletions
|
@ -209,6 +209,7 @@ module.exports = {
|
|||
{
|
||||
files: [
|
||||
'test/functional/services/lib/leadfoot_element_wrapper/scroll_into_view_if_necessary.js',
|
||||
'**/browser_exec_scripts/**/*',
|
||||
],
|
||||
rules: {
|
||||
'prefer-object-spread/prefer-object-spread': 'off',
|
||||
|
@ -220,6 +221,7 @@ module.exports = {
|
|||
'ArrowFunctionExpression',
|
||||
'AwaitExpression',
|
||||
'ClassDeclaration',
|
||||
'ImportDeclaration',
|
||||
'RestElement',
|
||||
'SpreadElement',
|
||||
'YieldExpression',
|
||||
|
|
|
@ -17,9 +17,13 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { cloneDeep } from 'lodash';
|
||||
|
||||
import { modifyUrl } from '../../../src/core/utils';
|
||||
import Keys from 'leadfoot/keys';
|
||||
|
||||
import { LeadfootElementWrapper } from './lib/leadfoot_element_wrapper';
|
||||
|
||||
export function BrowserProvider({ getService }) {
|
||||
const leadfoot = getService('__leadfoot__');
|
||||
|
||||
|
@ -260,8 +264,12 @@ export function BrowserProvider({ getService }) {
|
|||
* @param {string|function} function
|
||||
* @param {...any[]} args
|
||||
*/
|
||||
async execute(...args) {
|
||||
return await leadfoot.execute(...args);
|
||||
async execute(fn, ...args) {
|
||||
return await leadfoot.execute(fn, cloneDeep(args, arg => {
|
||||
if (arg instanceof LeadfootElementWrapper) {
|
||||
return arg._leadfootElement;
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -214,6 +214,10 @@ export function TestSubjectsProvider({ getService }) {
|
|||
async waitForAttributeToChange(selector, attribute, value) {
|
||||
await find.waitForAttributeToChange(testSubjSelector(selector), attribute, value);
|
||||
}
|
||||
|
||||
getCssSelector(selector) {
|
||||
return testSubjSelector(selector);
|
||||
}
|
||||
}
|
||||
|
||||
return new TestSubjects();
|
||||
|
|
|
@ -80,6 +80,11 @@ exports[`PipelinesTable component renders component as expected 1`] = `
|
|||
}
|
||||
}
|
||||
responsive={true}
|
||||
rowProps={
|
||||
Object {
|
||||
"data-test-subj": "row",
|
||||
}
|
||||
}
|
||||
search={
|
||||
Object {
|
||||
"box": Object {
|
||||
|
|
|
@ -206,6 +206,9 @@ function PipelinesTableUi({
|
|||
search={search}
|
||||
selection={selectionOptions}
|
||||
sorting={true}
|
||||
rowProps={{
|
||||
'data-test-subj': 'row'
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -67,7 +67,7 @@ export default function ({ getService, getPageObjects }) {
|
|||
await pipelineList.assertExists();
|
||||
await pipelineList.setFilter(id);
|
||||
|
||||
const rows = await pipelineList.getRowsFromTable();
|
||||
const rows = await pipelineList.readRows();
|
||||
const newRow = rows.find(row => row.id === id);
|
||||
|
||||
expect(newRow)
|
||||
|
@ -78,13 +78,13 @@ export default function ({ getService, getPageObjects }) {
|
|||
describe('cancel button', () => {
|
||||
it('discards the pipeline and redirects to the list', async () => {
|
||||
await PageObjects.logstash.gotoPipelineList();
|
||||
const originalRows = await pipelineList.getRowsFromTable();
|
||||
const originalRows = await pipelineList.readRows();
|
||||
|
||||
await PageObjects.logstash.gotoNewPipelineEditor();
|
||||
await pipelineEditor.clickCancel();
|
||||
|
||||
await pipelineList.assertExists();
|
||||
const currentRows = await pipelineList.getRowsFromTable();
|
||||
const currentRows = await pipelineList.readRows();
|
||||
expect(originalRows).to.eql(currentRows);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -31,7 +31,7 @@ export default function ({ getService, getPageObjects }) {
|
|||
});
|
||||
|
||||
it('shows example pipelines', async () => {
|
||||
const rows = await pipelineList.getRowsFromTable();
|
||||
const rows = await pipelineList.readRows();
|
||||
const rowsWithoutTime = rows.map(row => omit(row, 'lastModified'));
|
||||
|
||||
for (const time of rows.map(row => row.lastModified)) {
|
||||
|
@ -67,14 +67,14 @@ export default function ({ getService, getPageObjects }) {
|
|||
// select all
|
||||
await pipelineList.clickSelectAll();
|
||||
|
||||
for (const row of await pipelineList.getRowsFromTable()) {
|
||||
for (const row of await pipelineList.readRows()) {
|
||||
expect(row).to.have.property('selected', true);
|
||||
}
|
||||
|
||||
// unselect all
|
||||
await pipelineList.clickSelectAll();
|
||||
|
||||
for (const row of await pipelineList.getRowsFromTable()) {
|
||||
for (const row of await pipelineList.readRows()) {
|
||||
expect(row).to.have.property('selected', false);
|
||||
}
|
||||
});
|
||||
|
@ -113,7 +113,7 @@ export default function ({ getService, getPageObjects }) {
|
|||
describe('filter', () => {
|
||||
it('filters the pipeline list', async () => {
|
||||
await pipelineList.setFilter('tweets');
|
||||
const rows = await pipelineList.getRowsFromTable();
|
||||
const rows = await pipelineList.readRows();
|
||||
|
||||
expect(rows).to.have.length(1);
|
||||
expect(rows[0]).to.have.property('id', 'tweets_and_beats');
|
||||
|
@ -140,7 +140,7 @@ export default function ({ getService, getPageObjects }) {
|
|||
|
||||
it('takes user to the second page', async () => {
|
||||
await pipelineList.clickNextPage();
|
||||
const rows = await pipelineList.getRowsFromTable();
|
||||
const rows = await pipelineList.readRows();
|
||||
const rowsWithoutTime = rows.map(row => omit(row, 'lastModified'));
|
||||
|
||||
for (const time of rows.map(row => row.lastModified)) {
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export function readPipelineListRows(container, cssSelectors) {
|
||||
return Array.prototype.map.call(
|
||||
container.querySelectorAll(cssSelectors.ROW),
|
||||
function (row) {
|
||||
return {
|
||||
selected: row.querySelector('input[type=checkbox]').checked,
|
||||
id: row.querySelector(cssSelectors.CELL_ID).innerText.trim(),
|
||||
description: row.querySelector(cssSelectors.CELL_DESCRIPTION).innerText.trim(),
|
||||
lastModified: row.querySelector(cssSelectors.CELL_LAST_MODIFIED).innerText.trim(),
|
||||
username: row.querySelector(cssSelectors.CELL_USERNAME).innerText.trim(),
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
|
@ -4,24 +4,14 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import expect from 'expect.js';
|
||||
import { times, mapValues } from 'lodash';
|
||||
import { readPipelineListRows } from './browser_exec_scripts/read_pipeline_list_rows';
|
||||
|
||||
export function PipelineListProvider({ getService }) {
|
||||
const testSubjects = getService('testSubjects');
|
||||
const retry = getService('retry');
|
||||
const browser = getService('browser');
|
||||
const random = getService('random');
|
||||
|
||||
function assertLengthsMatch(arrays) {
|
||||
const lengths = arrays.map(array => array.length);
|
||||
|
||||
try {
|
||||
expect(Math.min(...lengths)).to.be(Math.max(...lengths));
|
||||
} catch (err) {
|
||||
throw new Error(`Expected lengths of arrays to match, ${JSON.stringify(arrays)}`);
|
||||
}
|
||||
}
|
||||
|
||||
// test subject selectors
|
||||
const SUBJ_CONTAINER = `pipelineList`;
|
||||
const SUBJ_BTN_ADD = `pipelineList btnAdd`;
|
||||
|
@ -30,12 +20,16 @@ export function PipelineListProvider({ getService }) {
|
|||
const SUBJ_FILTER = `pipelineList filter`;
|
||||
const SUBJ_SELECT_ALL = `pipelineList pipelineTable checkboxSelectAll`;
|
||||
const getSelectCheckbox = id => `pipelineList pipelineTable checkboxSelectRow-${id}`;
|
||||
const SUBJ_CELL_ID = `pipelineList pipelineTable cellId`;
|
||||
const SUBJ_CELL_DESCRIPTION = `pipelineList pipelineTable cellDescription`;
|
||||
const SUBJ_CELL_LAST_MODIFIED = `pipelineList pipelineTable cellLastModified`;
|
||||
const SUBJ_CELL_USERNAME = `pipelineList pipelineTable cellUsername`;
|
||||
const SUBJ_BTN_NEXT_PAGE = `pipelineList pagination-button-next`;
|
||||
|
||||
const INNER_SUBJ_ROW = `row`;
|
||||
const INNER_SUBJ_CELL_ID = `cellId`;
|
||||
const INNER_SUBJ_CELL_DESCRIPTION = `cellDescription`;
|
||||
const INNER_SUBJ_CELL_LAST_MODIFIED = `cellLastModified`;
|
||||
const INNER_SUBJ_CELL_USERNAME = `cellUsername`;
|
||||
|
||||
const SUBJ_CELL_ID = `${SUBJ_CONTAINER} ${INNER_SUBJ_ROW} ${INNER_SUBJ_CELL_ID}`;
|
||||
|
||||
return new class PipelineList {
|
||||
/**
|
||||
* Set the text of the pipeline list filter
|
||||
|
@ -52,38 +46,13 @@ export function PipelineListProvider({ getService }) {
|
|||
* @return {Promise<Object>}
|
||||
*/
|
||||
async getRowCounts() {
|
||||
const ids = await this.getRowIds();
|
||||
const isSelecteds = await Promise.all(
|
||||
ids.map(async (id) => await testSubjects.isSelected(getSelectCheckbox(id)))
|
||||
);
|
||||
const total = isSelecteds.length;
|
||||
const isSelected = isSelecteds.filter(Boolean).length;
|
||||
const rows = await this.readRows();
|
||||
const total = rows.length;
|
||||
const isSelected = rows.reduce((acc, row) => acc + (row.selected ? 1 : 0), 0);
|
||||
const isUnselected = total - isSelected;
|
||||
return { total, isSelected, isUnselected };
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the rows from the table, mapping the cell values to key names
|
||||
* in an array of objects
|
||||
* @return {Promise<Array<Object>>}
|
||||
*/
|
||||
async getRowsFromTable() {
|
||||
const ids = await this.getRowIds();
|
||||
const selected = await Promise.all(ids.map(async (id) => await testSubjects.isSelected(getSelectCheckbox(id))));
|
||||
const description = await testSubjects.getVisibleTextAll(SUBJ_CELL_DESCRIPTION);
|
||||
const lastModified = await testSubjects.getVisibleTextAll(SUBJ_CELL_LAST_MODIFIED);
|
||||
const username = await testSubjects.getVisibleTextAll(SUBJ_CELL_USERNAME);
|
||||
const valuesByKey = { selected, id: ids, description, lastModified, username };
|
||||
|
||||
// ensure that we got values for every row, otherwise we can't
|
||||
// recombine these into a list of rows
|
||||
assertLengthsMatch(Object.values(valuesByKey));
|
||||
|
||||
return times(valuesByKey.id.length, i => {
|
||||
return mapValues(valuesByKey, values => values[i]);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Click the selectAll checkbox until all rows are selected
|
||||
* @return {Promise<undefined>}
|
||||
|
@ -128,35 +97,33 @@ export function PipelineListProvider({ getService }) {
|
|||
throw new Error('pipelineList.selectRandomRow() requires at least one unselected row');
|
||||
}
|
||||
|
||||
// get pick an unselected selectbox and click it
|
||||
await retry.try(async () => {
|
||||
const ids = await this.getRowIds();
|
||||
const rowToClick = await random.pickOne(ids);
|
||||
const checkboxId = getSelectCheckbox(rowToClick);
|
||||
const isSelected = await testSubjects.isSelected(checkboxId);
|
||||
// pick an unselected selectbox and select it
|
||||
const rows = await this.readRows();
|
||||
const rowToClick = await random.pickOne(rows.filter(r => !r.selected));
|
||||
await testSubjects.click(getSelectCheckbox(rowToClick.id));
|
||||
|
||||
if (isSelected) {
|
||||
throw new Error('randomly chosen row was already selected');
|
||||
}
|
||||
|
||||
await testSubjects.click(checkboxId);
|
||||
});
|
||||
|
||||
// wait for the selected count to grow
|
||||
await retry.try(async () => {
|
||||
const now = await this.getRowCounts();
|
||||
if (initial.isSelected >= now.isSelected) {
|
||||
throw new Error(`randomly selected row still not selected`);
|
||||
}
|
||||
});
|
||||
await retry.waitFor('selected count to grow', async () => (
|
||||
(await this.getRowCounts()).isSelected > initial.isSelected
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of all pipeline IDs in the current table
|
||||
* @return {Promise<any>}
|
||||
* Read the rows from the table, mapping the cell values to key names
|
||||
* in an array of objects
|
||||
* @return {Promise<Array<Object>>}
|
||||
*/
|
||||
async getRowIds() {
|
||||
return await testSubjects.getVisibleTextAll(SUBJ_CELL_ID);
|
||||
async readRows() {
|
||||
return await browser.execute(
|
||||
readPipelineListRows,
|
||||
await testSubjects.find(SUBJ_CONTAINER),
|
||||
{
|
||||
ROW: testSubjects.getCssSelector(INNER_SUBJ_ROW),
|
||||
CELL_ID: testSubjects.getCssSelector(INNER_SUBJ_CELL_ID),
|
||||
CELL_DESCRIPTION: testSubjects.getCssSelector(INNER_SUBJ_CELL_DESCRIPTION),
|
||||
CELL_LAST_MODIFIED: testSubjects.getCssSelector(INNER_SUBJ_CELL_LAST_MODIFIED),
|
||||
CELL_USERNAME: testSubjects.getCssSelector(INNER_SUBJ_CELL_USERNAME),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -196,11 +163,9 @@ export function PipelineListProvider({ getService }) {
|
|||
* @return {Promise<undefined>}
|
||||
*/
|
||||
async assertExists() {
|
||||
await retry.try(async () => {
|
||||
if (!(await testSubjects.exists(SUBJ_CONTAINER))) {
|
||||
throw new Error('Expected to find the pipeline list');
|
||||
}
|
||||
});
|
||||
await retry.waitFor('pipline list visible on screen', async () => (
|
||||
await testSubjects.exists(SUBJ_CONTAINER)
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue