[7.x] [Endpoint] Functional Tests cleanup (#68756) (#68822)

* [Endpoint] Functional Tests cleanup (#68756)

* Removed several unnecessary disabled eslint rules
* moved common pageobject from endpoint_list to page_utils
* Rename functional_endpoint to security_solution_endpoint
* Delete `functional_endpoint_ingest_failure` no longer applicable

# Conflicts:
#	.github/CODEOWNERS
#	x-pack/test/security_solution_endpoint/apps/endpoint/resolver.ts

* Sync Endpoint Alerts page objects with master

- PR https://github.com/elastic/kibana/pull/64704 was never
  backported, thus this change is needed to ensure build is
  successful.

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
Paul Tavares 2020-06-12 08:04:45 -04:00 committed by GitHub
parent bd980f8d7c
commit 521493f344
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
26 changed files with 473 additions and 198 deletions

View file

@ -35,8 +35,7 @@ export const ManagementPageView = memo<Omit<PageViewProps, 'tabs'>>((options) =>
href: getManagementUrl({ name: 'policyList' }),
},
];
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [tabName]);
}, [options.viewType, tabName]);
return <PageView {...options} tabs={tabs} />;
});

View file

@ -137,15 +137,14 @@ export const HostDetails = memo(({ details }: { details: HostMetadata }) => {
description: details.agent.version,
},
];
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [
details.agent.version,
details.endpoint.policy.id,
details.host.hostname,
details.host.ip,
policyResponseUri.search,
policyStatusClickHandler,
details.host.hostname,
details.agent.version,
policyStatus,
policyResponseUri,
policyStatusClickHandler,
]);
return (

View file

@ -40,6 +40,5 @@ export const mockManagementState: Immutable<ManagementState> = {
export const managementReducer = immutableCombineReducers({
[MANAGEMENT_STORE_POLICY_LIST_NAMESPACE]: policyListReducer,
[MANAGEMENT_STORE_POLICY_DETAILS_NAMESPACE]: policyDetailsReducer,
// @ts-ignore
[MANAGEMENT_STORE_ENDPOINTS_NAMESPACE]: hostListReducer,
});

View file

@ -8,7 +8,7 @@ require('@kbn/plugin-helpers').babelRegister();
require('@kbn/test').runTestsCli([
require.resolve('../test/functional/config.js'),
require.resolve('../test/functional_endpoint/config.ts'),
require.resolve('../test/security_solution_endpoint/config.ts'),
require.resolve('../test/functional_with_es_ssl/config.ts'),
require.resolve('../test/functional/config_security_basic.ts'),
require.resolve('../test/functional/config_security_trial.ts'),

View file

@ -1,27 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { FtrProviderContext } from '../ftr_provider_context';
export function EndpointAlertsPageProvider({ getService }: FtrProviderContext) {
const testSubjects = getService('testSubjects');
return {
async enterSearchBarQuery(query: string) {
return await testSubjects.setValue('alertsSearchBar', query, { clearWithKeyboard: true });
},
async submitSearchBarFilter() {
return await testSubjects.click('querySubmitButton');
},
async setSearchBarDate(timestamp: string) {
await testSubjects.click('superDatePickerShowDatesButton');
await testSubjects.click('superDatePickerstartDatePopoverButton');
await testSubjects.click('superDatePickerAbsoluteTab');
await testSubjects.setValue('superDatePickerAbsoluteDateInput', timestamp);
await this.submitSearchBarFilter();
},
};
}

View file

@ -1,30 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { FtrProviderContext } from '../ftr_provider_context';
export function EndpointPageUtils({ getService }: FtrProviderContext) {
const find = getService('find');
return {
/**
* Finds a given EuiCheckbox by test subject and clicks on it
*
* @param euiCheckBoxTestId
*/
async clickOnEuiCheckbox(euiCheckBoxTestId: string) {
// This utility is needed because EuiCheckbox forwards the test subject on to
// the actual `<input>` which is not actually visible/accessible on the page.
// In order to actually cause the state of the checkbox to change, the `<label>`
// must be clicked.
const euiCheckboxLabelElement = await find.byXPath(
`//input[@data-test-subj='${euiCheckBoxTestId}']/../label`
);
await euiCheckboxLabelElement.click();
},
};
}

View file

@ -1,14 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { FtrProviderContext } from '../../ftr_provider_context';
export default function ({ loadTestFile }: FtrProviderContext) {
describe('endpoint when the ingest manager fails to setup correctly', function () {
this.tags('ciGroup7');
loadTestFile(require.resolve('./landing_page'));
});
}

View file

@ -1,22 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { FtrProviderContext } from '../../ftr_provider_context';
export default ({ getPageObjects, getService }: FtrProviderContext) => {
describe('home page', function () {
const pageObjects = getPageObjects(['common']);
const testSubjects = getService('testSubjects');
before(async () => {
await pageObjects.common.navigateToApp('endpoint');
});
it('displays an error toast', async () => {
await testSubjects.existOrFail('euiToastHeader');
});
});
};

View file

@ -1,30 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { resolve } from 'path';
import { FtrConfigProviderContext } from '@kbn/test/types/ftr';
export default async function ({ readConfigFile }: FtrConfigProviderContext) {
const xpackFunctionalConfig = await readConfigFile(
require.resolve('../functional_endpoint/config.ts')
);
return {
...xpackFunctionalConfig.getAll(),
testFiles: [resolve(__dirname, './apps/endpoint')],
junit: {
reportName: 'X-Pack Endpoint Without Ingest Functional Tests',
},
kbnTestServer: {
...xpackFunctionalConfig.get('kbnTestServer'),
serverArgs: [
...xpackFunctionalConfig.get('kbnTestServer.serverArgs'),
// use a bogus port so the ingest manager setup will fail
'--xpack.ingestManager.epm.registryUrl=http://127.0.0.1:12345',
],
},
};
}

View file

@ -1,12 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { GenericFtrProviderContext } from '@kbn/test/types/ftr';
import { pageObjects } from '../functional_endpoint/page_objects';
import { services } from '../functional/services';
export type FtrProviderContext = GenericFtrProviderContext<typeof services, typeof pageObjects>;

View file

@ -16,7 +16,7 @@ import { pageObjects } from './page_objects';
/* eslint-disable import/no-default-export */
export default async function ({ readConfigFile }: FtrConfigProviderContext) {
const xpackFunctionalConfig = await readConfigFile(
require.resolve('../functional_endpoint/config.ts')
require.resolve('../security_solution_endpoint/config.ts')
);
// Find all folders in ./plugins since we treat all them as plugin folder

View file

@ -8,7 +8,7 @@ import expect from '@kbn/expect';
import { FtrProviderContext } from '../../ftr_provider_context';
export default ({ getPageObjects, getService }: FtrProviderContext) => {
const pageObjects = getPageObjects(['common', 'endpoint', 'header']);
const pageObjects = getPageObjects(['common', 'endpoint', 'header', 'endpointPageUtils']);
const esArchiver = getService('esArchiver');
const testSubjects = getService('testSubjects');
@ -73,7 +73,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
'Jan 24, 2020 @ 16:06:09.541',
],
];
const tableData = await pageObjects.endpoint.getEndpointAppTableData('hostListTable');
const tableData = await pageObjects.endpointPageUtils.tableData('hostListTable');
expect(tableData).to.eql(expectedData);
});
@ -126,7 +126,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
});
it('displays no items found when empty', async () => {
// get the endpoint list table data and verify message
const [, [noItemsFoundMessage]] = await pageObjects.endpoint.getEndpointAppTableData(
const [, [noItemsFoundMessage]] = await pageObjects.endpointPageUtils.tableData(
'hostListTable'
);
expect(noItemsFoundMessage).to.equal('No items found');

View file

@ -8,7 +8,7 @@ import { FtrProviderContext } from '../../ftr_provider_context';
import { PolicyTestResourceInfo } from '../../services/endpoint_policy';
export default function ({ getPageObjects, getService }: FtrProviderContext) {
const pageObjects = getPageObjects(['common', 'endpoint', 'policy']);
const pageObjects = getPageObjects(['common', 'endpoint', 'policy', 'endpointPageUtils']);
const testSubjects = getService('testSubjects');
const policyTestResources = getService('policyTestResources');
const RELATIVE_DATE_FORMAT = /\d (?:seconds|minutes) ago/i;
@ -31,7 +31,9 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
expect(policyTotal).to.equal('0 Policies');
});
it('has correct table headers', async () => {
const allHeaderCells = await pageObjects.endpoint.tableHeaderVisibleText('policyTable');
const allHeaderCells = await pageObjects.endpointPageUtils.tableHeaderVisibleText(
'policyTable'
);
expect(allHeaderCells).to.eql([
'Policy Name',
'Created By',
@ -43,7 +45,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
]);
});
it('should show empty table results message', async () => {
const [, [noItemsFoundMessage]] = await pageObjects.endpoint.getEndpointAppTableData(
const [, [noItemsFoundMessage]] = await pageObjects.endpointPageUtils.tableData(
'policyTable'
);
expect(noItemsFoundMessage).to.equal('No items found');
@ -65,7 +67,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
});
it('should show policy on the list', async () => {
const [, policyRow] = await pageObjects.endpoint.getEndpointAppTableData('policyTable');
const [, policyRow] = await pageObjects.endpointPageUtils.tableData('policyTable');
// Validate row data with the exception of the Date columns - since those are initially
// shown as relative.
expect([policyRow[0], policyRow[1], policyRow[3], policyRow[5], policyRow[6]]).to.eql([

View file

@ -0,0 +1,259 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import expect from '@kbn/expect';
import { FtrProviderContext } from '../../ftr_provider_context';
export default function ({ getPageObjects, getService }: FtrProviderContext) {
const pageObjects = getPageObjects(['common', 'timePicker', 'endpointAlerts']);
const testSubjects = getService('testSubjects');
const esArchiver = getService('esArchiver');
const retry = getService('retry');
const browser = getService('browser');
describe('Endpoint Alert Resolver', function () {
this.tags(['ciGroup7']);
before(async () => {
const fromTime = 'Sep 22, 2019 @ 20:31:44.000';
const toTime = 'Now';
await esArchiver.load('endpoint/resolver_tree/api_feature');
await pageObjects.common.navigateToUrlWithBrowserHistory('endpoint', '/alerts');
await testSubjects.existOrFail('superDatePickerShowDatesButton', { timeout: 20000 });
await pageObjects.timePicker.setAbsoluteRange(fromTime, toTime);
await testSubjects.existOrFail('alertListPage');
await testSubjects.click('alertTypeCellLink');
await testSubjects.existOrFail('alertDetailFlyout');
await testSubjects.click('overviewResolverTab');
await testSubjects.existOrFail('resolverEmbeddable', { timeout: 20000 });
await browser.setWindowSize(2400, 1800);
});
it('resolver column Process Name exits', async () => {
await testSubjects.existOrFail('tableHeaderCell_name_0');
});
it('resolver column Timestamp exits', async () => {
await testSubjects.existOrFail('tableHeaderCell_timestamp_1');
});
it('resolver Table and Node data same length', async () => {
let count = 1;
const tableData = await pageObjects.endpointAlerts.getEndpointAlertResolverTableData(
'resolverEmbeddable',
'tr'
);
await retry.try(async function () {
await testSubjects.click('zoom-out');
const Nodes = await testSubjects.findAll('resolverNode');
expect(tableData.length - 1).to.eql(Nodes.length);
count++;
});
for (let i = 0; i < count; i++) {
await testSubjects.click('zoom-in');
}
});
it('compare resolver Nodes and Table data', async () => {
const $: string[] = [];
const tableData = await pageObjects.endpointAlerts.getEndpointAlertResolverTableData(
'resolverEmbeddable',
'tr'
);
await testSubjects.click('zoom-out');
const Nodes = await testSubjects.findAll('euiButton__text');
for (const value of Nodes) {
$.push(await value._webElement.getText());
}
for (let i = 0; i < $.length; i++) {
expect(tableData[i + 1][0]).to.eql($[i]);
}
await testSubjects.click('zoom-in');
});
it('resolver Nodes navigation Up', async () => {
const OriginalNodeDataStyle = await pageObjects.endpointAlerts.parseStyles(
'resolverNode',
'style'
);
await testSubjects.click('north-button');
const NewNodeDataStyle = await pageObjects.endpointAlerts.parseStyles(
'resolverNode',
'style'
);
for (let i = 0; i < OriginalNodeDataStyle.length; i++) {
expect(parseFloat(OriginalNodeDataStyle[i].top)).to.lessThan(
parseFloat(NewNodeDataStyle[i].top)
);
expect(parseFloat(OriginalNodeDataStyle[i].left)).to.equal(
parseFloat(NewNodeDataStyle[i].left)
);
}
await testSubjects.click('center-button');
});
it('resolver Nodes navigation Down', async () => {
const OriginalNodeDataStyle = await pageObjects.endpointAlerts.parseStyles(
'resolverNode',
'style'
);
await testSubjects.click('south-button');
const NewNodeDataStyle = await pageObjects.endpointAlerts.parseStyles(
'resolverNode',
'style'
);
for (let i = 0; i < NewNodeDataStyle.length; i++) {
expect(parseFloat(NewNodeDataStyle[i].top)).to.lessThan(
parseFloat(OriginalNodeDataStyle[i].top)
);
expect(parseFloat(OriginalNodeDataStyle[i].left)).to.equal(
parseFloat(NewNodeDataStyle[i].left)
);
}
await testSubjects.click('center-button');
});
it('resolver Nodes navigation Right', async () => {
const OriginalNodeDataStyle = await pageObjects.endpointAlerts.parseStyles(
'resolverNode',
'style'
);
await testSubjects.click('west-button');
const NewNodeDataStyle = await pageObjects.endpointAlerts.parseStyles(
'resolverNode',
'style'
);
for (let i = 0; i < NewNodeDataStyle.length; i++) {
expect(parseFloat(OriginalNodeDataStyle[i].left)).to.lessThan(
parseFloat(NewNodeDataStyle[i].left)
);
expect(parseFloat(NewNodeDataStyle[i].top)).to.equal(
parseFloat(OriginalNodeDataStyle[i].top)
);
}
await testSubjects.click('center-button');
});
it('resolver Nodes navigation Left', async () => {
const OriginalNodeDataStyle = await pageObjects.endpointAlerts.parseStyles(
'resolverNode',
'style'
);
await testSubjects.click('east-button');
const NewNodeDataStyle = await pageObjects.endpointAlerts.parseStyles(
'resolverNode',
'style'
);
for (let i = 0; i < OriginalNodeDataStyle.length; i++) {
expect(parseFloat(NewNodeDataStyle[i].left)).to.lessThan(
parseFloat(OriginalNodeDataStyle[i].left)
);
expect(parseFloat(NewNodeDataStyle[i].top)).to.equal(
parseFloat(OriginalNodeDataStyle[i].top)
);
}
await testSubjects.click('center-button');
});
it('resolver Nodes navigation Center', async () => {
const OriginalNodeDataStyle = await pageObjects.endpointAlerts.parseStyles(
'resolverNode',
'style'
);
await testSubjects.click('east-button');
await testSubjects.click('south-button');
const NewNodeDataStyle = await pageObjects.endpointAlerts.parseStyles(
'resolverNode',
'style'
);
for (let i = 0; i < NewNodeDataStyle.length; i++) {
expect(parseFloat(NewNodeDataStyle[i].left)).to.lessThan(
parseFloat(OriginalNodeDataStyle[i].left)
);
expect(parseFloat(NewNodeDataStyle[i].top)).to.lessThan(
parseFloat(OriginalNodeDataStyle[i].top)
);
}
await (await testSubjects.find('center-button')).click();
const CenterNodeDataStyle = await pageObjects.endpointAlerts.parseStyles(
'resolverNode',
'style'
);
for (let i = 0; i < CenterNodeDataStyle.length; i++) {
expect(parseFloat(CenterNodeDataStyle[i].left)).to.equal(
parseFloat(OriginalNodeDataStyle[i].left)
);
expect(parseFloat(CenterNodeDataStyle[i].top)).to.equal(
parseFloat(OriginalNodeDataStyle[i].top)
);
}
});
it('resolver Nodes navigation zoom in', async () => {
const OriginalNodeDataStyle = await pageObjects.endpointAlerts.parseStyles(
'resolverNode',
'style'
);
await testSubjects.click('zoom-in');
const NewNodeDataStyle = await pageObjects.endpointAlerts.parseStyles(
'resolverNode',
'style'
);
for (let i = 1; i < NewNodeDataStyle.length; i++) {
expect(parseFloat(OriginalNodeDataStyle[i].left)).to.lessThan(
parseFloat(NewNodeDataStyle[i].left)
);
expect(parseFloat(OriginalNodeDataStyle[i].top)).to.lessThan(
parseFloat(NewNodeDataStyle[i].top)
);
expect(parseFloat(OriginalNodeDataStyle[i].width)).to.lessThan(
parseFloat(NewNodeDataStyle[i].width)
);
expect(parseFloat(OriginalNodeDataStyle[i].height)).to.lessThan(
parseFloat(NewNodeDataStyle[i].height)
);
await testSubjects.click('zoom-out');
}
});
it('resolver Nodes navigation zoom out', async () => {
const OriginalNodeDataStyle = await pageObjects.endpointAlerts.parseStyles(
'resolverNode',
'style'
);
await testSubjects.click('zoom-out');
const NewNodeDataStyle1 = await pageObjects.endpointAlerts.parseStyles(
'resolverNode',
'style'
);
for (let i = 1; i < OriginalNodeDataStyle.length; i++) {
expect(parseFloat(NewNodeDataStyle1[i].left)).to.lessThan(
parseFloat(OriginalNodeDataStyle[i].left)
);
expect(parseFloat(NewNodeDataStyle1[i].top)).to.lessThan(
parseFloat(OriginalNodeDataStyle[i].top)
);
expect(parseFloat(NewNodeDataStyle1[i].width)).to.lessThan(
parseFloat(OriginalNodeDataStyle[i].width)
);
expect(parseFloat(NewNodeDataStyle1[i].height)).to.lessThan(
parseFloat(OriginalNodeDataStyle[i].height)
);
}
await testSubjects.click('zoom-in');
});
after(async () => {
await browser.setWindowSize(1600, 1000);
await testSubjects.click('euiFlyoutCloseButton');
await pageObjects.common.sleep(2000);
await esArchiver.unload('endpoint/resolver_tree/api_feature');
});
});
}

View file

@ -0,0 +1,120 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { FtrProviderContext } from '../ftr_provider_context';
import { WebElementWrapper } from '../../../../test/functional/services/lib/web_element_wrapper';
export function EndpointAlertsPageProvider({ getService }: FtrProviderContext) {
const testSubjects = getService('testSubjects');
const retry = getService('retry');
/**
* @function parseStyles
* Parses a string of inline styles into a javascript object with casing for react
*
* @param {string} styles
* @returns {Object}
*/
const parseStyle = (styles: any) =>
styles
.split(';')
.filter((style: any) => style.split(':')[0] && style.split(':')[1])
.map((style: any) => [
style
.split(':')[0]
.trim()
.replace(/-./g, (c: any) => c.substr(1).toUpperCase()),
style.split(':').slice(1).join(':').trim(),
])
.reduce(
(styleObj: any, style: any) => ({
...styleObj,
[style[0]]: style[1],
}),
{}
);
return {
async enterSearchBarQuery(query: string) {
return await testSubjects.setValue('alertsSearchBar', query, { clearWithKeyboard: true });
},
async submitSearchBarFilter() {
return testSubjects.click('querySubmitButton');
},
async setSearchBarDate(timestamp: string) {
await testSubjects.click('superDatePickerShowDatesButton');
await testSubjects.click('superDatePickerstartDatePopoverButton');
await testSubjects.click('superDatePickerAbsoluteTab');
await testSubjects.setValue('superDatePickerAbsoluteDateInput', timestamp);
await this.submitSearchBarFilter();
},
/**
* Finds a table and returns the data in a nested array with row 0 is the headers if they exist.
* It uses euiTableCellContent to avoid poluting the array data with the euiTableRowCell__mobileHeader data.
* @param dataTestSubj
* @param element
* @returns Promise<string[][]>
*/
async getEndpointAlertResolverTableData(dataTestSubj: string, element: string) {
await testSubjects.exists(dataTestSubj);
const hostTable: WebElementWrapper = await testSubjects.find(dataTestSubj);
const $ = await hostTable.parseDomContent();
return $(element)
.toArray()
.map((row) =>
$(row)
.find('.euiTableCellContent')
.toArray()
.map((cell) =>
$(cell)
.text()
.replace(/&nbsp;/g, '')
.trim()
)
);
},
/**
* Finds a nodes and returns the data in a nested array of nodes.
* @param dataTestSubj
* @param element
* @returns Promise<string[][]>
*/
async getEndpointAlertResolverNodeData(dataTestSubj: string, element: string) {
await testSubjects.exists(dataTestSubj);
const Elements = await testSubjects.findAll(dataTestSubj);
const $ = [];
for (const value of Elements) {
$.push(await value.getAttribute(element));
}
return $;
},
/**
* Gets a array of not parsed styles and returns the Array of parsed styles.
* @returns Promise<string[][]>
* @param dataTestSubj
* @param element
*/
async parseStyles(dataTestSubj: string, element: string) {
const tableData = await this.getEndpointAlertResolverNodeData(dataTestSubj, element);
const $ = [];
for (let i = 1; i < tableData.length; i++) {
const eachStyle = parseStyle(tableData[i]);
$.push(eachStyle);
}
return $;
},
async waitForTableToHaveData(dataTestSubj: string) {
await retry.waitForWithTimeout('table to have data', 2000, async () => {
const tableData = await this.getEndpointAlertResolverTableData(dataTestSubj, 'tr');
if (tableData[1][0] === 'No items found') {
return false;
}
return true;
});
},
};
}

View file

@ -9,7 +9,7 @@ import { FtrProviderContext } from '../ftr_provider_context';
export function EndpointPageProvider({ getService, getPageObjects }: FtrProviderContext) {
const testSubjects = getService('testSubjects');
const pageObjects = getPageObjects(['common', 'header']);
const pageObjects = getPageObjects(['common', 'header', 'endpointPageUtils']);
const retry = getService('retry');
return {
@ -23,53 +23,9 @@ export function EndpointPageProvider({ getService, getPageObjects }: FtrProvider
await pageObjects.header.waitUntilLoadingHasFinished();
},
/**
* Finds the Table with the given `selector` (test subject) and returns
* back an array containing the table's header column text
*
* @param selector
* @returns Promise<string[]>
*/
async tableHeaderVisibleText(selector: string) {
const $ = await (await testSubjects.find('policyTable')).parseDomContent();
return $('thead tr th')
.toArray()
.map((th) =>
$(th)
.text()
.replace(/&nbsp;/g, '')
.trim()
);
},
/**
* Finds a table and returns the data in a nested array with row 0 is the headers if they exist.
* It uses euiTableCellContent to avoid poluting the array data with the euiTableRowCell__mobileHeader data.
* @param dataTestSubj
* @returns Promise<string[][]>
*/
async getEndpointAppTableData(dataTestSubj: string) {
await testSubjects.exists(dataTestSubj);
const hostTable: WebElementWrapper = await testSubjects.find(dataTestSubj);
const $ = await hostTable.parseDomContent();
return $('tr')
.toArray()
.map((row) =>
$(row)
.find('.euiTableCellContent')
.toArray()
.map((cell) =>
$(cell)
.text()
.replace(/&nbsp;/g, '')
.trim()
)
);
},
async waitForTableToHaveData(dataTestSubj: string) {
await retry.waitForWithTimeout('table to have data', 2000, async () => {
const tableData = await this.getEndpointAppTableData(dataTestSubj);
const tableData = await pageObjects.endpointPageUtils.tableData(dataTestSubj);
if (tableData[1][0] === 'No items found') {
return false;
}

View file

@ -0,0 +1,76 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { FtrProviderContext } from '../ftr_provider_context';
import { WebElementWrapper } from '../../../../test/functional/services/lib/web_element_wrapper';
export function EndpointPageUtils({ getService }: FtrProviderContext) {
const testSubjects = getService('testSubjects');
const find = getService('find');
return {
/**
* Finds a given EuiCheckbox by test subject and clicks on it
*
* @param euiCheckBoxTestId
*/
async clickOnEuiCheckbox(euiCheckBoxTestId: string) {
// This utility is needed because EuiCheckbox forwards the test subject on to
// the actual `<input>` which is not actually visible/accessible on the page.
// In order to actually cause the state of the checkbox to change, the `<label>`
// must be clicked.
const euiCheckboxLabelElement = await find.byXPath(
`//input[@data-test-subj='${euiCheckBoxTestId}']/../label`
);
await euiCheckboxLabelElement.click();
},
/**
* Finds the Table with the given `selector` (test subject) and returns
* back an array containing the table's header column text
*
* @param selector
* @returns Promise<string[]>
*/
async tableHeaderVisibleText(selector: string) {
const $ = await (await testSubjects.find(selector)).parseDomContent();
return $('thead tr th')
.toArray()
.map((th) =>
$(th)
.text()
.replace(/&nbsp;/g, '')
.trim()
);
},
/**
* Finds a table and returns the data in a nested array with row 0 is the headers if they exist.
* It uses euiTableCellContent to avoid poluting the array data with the euiTableRowCell__mobileHeader data.
* @param dataTestSubj
* @returns Promise<string[][]>
*/
async tableData(dataTestSubj: string) {
await testSubjects.exists(dataTestSubj);
const hostTable: WebElementWrapper = await testSubjects.find(dataTestSubj);
const $ = await hostTable.parseDomContent();
return $('tr')
.toArray()
.map((row) =>
$(row)
.find('.euiTableCellContent')
.toArray()
.map((cell) =>
$(cell)
.text()
.replace(/&nbsp;/g, '')
.trim()
)
);
},
};
}