FTR: fix WebDriver Actions calls (#44605)

* [services/web_element_wrapper] add actions API methods

* fixes

* [services/browser] fix dragAnDrop to work in both w3c/non-w3c modes

* small review changes

* dragAndDrop: add default values for offset
This commit is contained in:
Dmitry Lemeshko 2019-09-06 07:54:26 +02:00 committed by GitHub
parent 5049b460b4
commit 3a158b9d7d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 151 additions and 148 deletions

View file

@ -25,7 +25,6 @@ export default function ({ getService, getPageObjects }) {
const PageObjects = getPageObjects(['dashboard']);
describe('dashboard grid', function () {
this.tags(['skipFirefox']);
before(async () => {
await PageObjects.dashboard.gotoDashboardLandingPage();

View file

@ -21,7 +21,6 @@ import expect from '@kbn/expect';
export default function ({ getService, getPageObjects }) {
const retry = getService('retry');
const browser = getService('browser');
const dashboardPanelActions = getService('dashboardPanelActions');
const PageObjects = getPageObjects(['dashboard', 'common']);
@ -69,7 +68,7 @@ export default function ({ getService, getPageObjects }) {
it('exits when the text button is clicked on', async () => {
const logoButton = await PageObjects.dashboard.getExitFullScreenLogoButton();
await browser.moveMouseTo(logoButton);
await logoButton.moveMouseTo();
await PageObjects.dashboard.clickExitFullScreenTextButton();
await retry.try(async () => {

View file

@ -160,7 +160,7 @@ export default function ({ getService, getPageObjects }) {
const newDurationHours = await PageObjects.timePicker.getTimeDurationInHours();
expect(Math.round(newDurationHours)).to.be(3);
const rowData = await PageObjects.discover.getDocTableIndex(1);
const rowData = await PageObjects.discover.getDocTableField(1);
expect(rowData).to.have.string('Sep 20, 2015 @ 02:56:02.323');
});

View file

@ -370,7 +370,7 @@ export function CommonPageProvider({ getService, getPageObjects }) {
throw new Error('Toast is not visible yet');
}
});
await browser.moveMouseTo(toast);
await toast.moveMouseTo();
const title = await (await find.byCssSelector('.euiToastHeader__title')).getVisibleText();
log.debug(title);
await find.clickByCssSelector('.euiToast__closeButton');
@ -381,7 +381,7 @@ export function CommonPageProvider({ getService, getPageObjects }) {
const toasts = await find.allByCssSelector('.euiToast');
for (const toastElement of toasts) {
try {
await browser.moveMouseTo(toastElement);
await toastElement.moveMouseTo();
const closeBtn = await toastElement.findByCssSelector('.euiToast__closeButton');
await closeBtn.click();
} catch (err) {

View file

@ -193,6 +193,13 @@ export function DiscoverPageProvider({ getService, getPageObjects }) {
return await row.getVisibleText();
}
async getDocTableField(index) {
const field = await find.byCssSelector(
`tr.kbnDocTable__row:nth-child(${index}) > [data-test-subj='docTableField']`
);
return await field.getVisibleText();
}
async clickDocSortDown() {
await find.clickByCssSelector('.fa-sort-down');
}

View file

@ -447,7 +447,7 @@ export function VisualBuilderPageProvider({ getService, getPageObjects }: FtrPro
public async clickColorPicker(): Promise<void> {
const picker = await find.byCssSelector('.tvbColorPicker button');
await browser.clickMouseButton(picker);
await picker.clickMouseButton();
}
public async setBackgroundColor(colorHex: string): Promise<void> {

View file

@ -1187,7 +1187,7 @@ export function VisualizePageProvider({ getService, getPageObjects, updateBaseli
await retry.try(async () => {
const table = await testSubjects.find('tableVis');
const cell = await table.findByCssSelector(`tbody tr:nth-child(${row}) td:nth-child(${column})`);
await browser.moveMouseTo(cell);
await cell.moveMouseTo();
const filterBtn = await testSubjects.findDescendant('filterForCellValue', cell);
await filterBtn.click();
});

View file

@ -155,49 +155,26 @@ export async function BrowserProvider({ getService }: FtrProviderContext) {
}
/**
* Moves the remote environments mouse cursor to the specified element or relative
* position.
* Moves the remote environments mouse cursor to the specified point {x, y} which is
* offset to browser page top left corner.
* https://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/lib/input_exports_Actions.html#move
*
* @param {WebElementWrapper} element Optional
* @param {number} xOffset Optional
* @param {number} yOffset Optional
* @param {x: number, y: number} point on browser page
* @return {Promise<void>}
*/
public async moveMouseTo(element: any, xOffset: number, yOffset: number): Promise<void>;
public async moveMouseTo(element: WebElementWrapper): Promise<void>;
public async moveMouseTo(
element: WebElementWrapper,
xOffset?: number,
yOffset?: number
): Promise<void> {
public async moveMouseTo(point: { x: number; y: number }): Promise<void> {
if (this.isW3CEnabled) {
// Workaround for scrolling bug in W3C mode: move pointer to { x: 0, y: 0 }
// https://github.com/mozilla/geckodriver/issues/776
await this.getActions()
.move({ x: 0, y: 0 })
.perform();
if (element instanceof WebElementWrapper) {
await this.getActions()
.move({ x: xOffset || 10, y: yOffset || 10, origin: element._webElement })
.perform();
} else {
await this.getActions()
.move({ origin: { x: xOffset, y: yOffset } })
.perform();
}
await this.getActions()
.move({ x: point.x, y: point.y, origin: 'pointer' })
.perform();
} else {
if (element instanceof WebElementWrapper) {
await this.getActions()
.pause(this.getActions().mouse)
.move({ origin: element._webElement })
.perform();
} else {
await this.getActions()
.pause(this.getActions().mouse)
.move({ origin: { x: xOffset, y: yOffset } })
.perform();
}
await this.getActions()
.pause(this.getActions().mouse)
.move({ x: point.x, y: point.y, origin: 'pointer' })
.perform();
}
}
@ -213,70 +190,47 @@ export async function BrowserProvider({ getService }: FtrProviderContext) {
from: { offset: { x: any; y: any }; location: any },
to: { offset: { x: any; y: any }; location: any }
) {
// tslint:disable-next-line:variable-name
let _from;
// tslint:disable-next-line:variable-name
let _to;
// tslint:disable-next-line:variable-name
const _fromOffset = from.offset
? { x: from.offset.x || 0, y: from.offset.y || 0 }
: { x: 0, y: 0 };
// tslint:disable-next-line:variable-name
const _toOffset = to.offset ? { x: to.offset.x || 0, y: to.offset.y || 0 } : { x: 0, y: 0 };
// tslint:disable-next-line:variable-name
const _convertPointW3C = async (point: any, offset: { x: any; y: any }) => {
if (point.location instanceof WebElementWrapper) {
const position = await point.location.getPosition();
return {
x: Math.round(position.x + offset.x),
y: Math.round(position.y + offset.y),
};
} else {
return {
x: Math.round(point.location.x + offset.x),
y: Math.round(point.location.y + offset.y),
};
}
};
// tslint:disable-next-line:variable-name
const _convertPoint = (point: any) => {
return point.location instanceof WebElementWrapper
? point.location._webElement
: point.location;
};
if (this.isW3CEnabled) {
// tslint:disable-next-line:variable-name
_from = await _convertPointW3C(from, _fromOffset);
// tslint:disable-next-line:variable-name
_to = await _convertPointW3C(to, _toOffset);
// tslint:disable-next-line:variable-name
const _offset = { x: _to.x - _from.x, y: _to.y - _from.y };
// The offset should be specified in pixels relative to the center of the element's bounding box
const getW3CPoint = (data: any) => {
if (!data.offset) {
data.offset = {};
}
return data.location instanceof WebElementWrapper
? { x: data.offset.x || 0, y: data.offset.y || 0, origin: data.location._webElement }
: { x: data.location.x, y: data.location.y, origin: 'pointer' };
};
const startPoint = getW3CPoint(from);
const endPoint = getW3CPoint(to);
await this.getActions()
.move({ x: 0, y: 0 })
.perform();
return await this.getActions()
.move({ x: _from.x, y: _from.y, origin: 'pointer' })
.move(startPoint)
.press()
.move({ x: _offset.x, y: _offset.y, origin: 'pointer' })
.move(endPoint)
.release()
.perform();
} else {
// until Chromedriver is not supporting W3C Webdriver Actions API
// tslint:disable-next-line:variable-name
_from = _convertPoint(from);
// tslint:disable-next-line:variable-name
_to = _convertPoint(to);
if (from.location instanceof WebElementWrapper && typeof to.location.x === 'number') {
// The offset should be specified in pixels relative to the top-left corner of the element's bounding box
const getOffset: any = (offset: { x: number; y: number }) =>
offset ? { x: offset.x || 0, y: offset.y || 0 } : { x: 0, y: 0 };
if (from.location instanceof WebElementWrapper === false) {
throw new Error('Dragging point should be WebElementWrapper instance');
} else if (typeof to.location.x === 'number') {
return await this.getActions()
.move({ origin: _from })
.move({ origin: from.location._webElement })
.press()
.move({ x: _to.x, y: _to.y, origin: 'pointer' })
.move({ x: to.location.x, y: to.location.y, origin: 'pointer' })
.release()
.perform();
} else {
return await new LegacyActionSequence(driver)
.mouseMove(_from, _fromOffset)
.mouseMove(from.location._webElement, getOffset(from.offset))
.mouseDown()
.mouseMove(_to, _toOffset)
.mouseMove(to.location._webElement, getOffset(to.offset))
.mouseUp()
.perform();
}
@ -320,34 +274,29 @@ export async function BrowserProvider({ getService }: FtrProviderContext) {
}
/**
* Inserts an action for moving the mouse x and y pixels relative to the specified origin.
* The origin may be defined as the mouse's current position, the viewport, or the center
* of a specific WebElement. Then adds an action for left-click (down/up) with the mouse.
* Moves the remote environments mouse cursor to the specified point {x, y} which is
* offset to browser page top left corner.
* Then adds an action for left-click (down/up) with the mouse.
* https://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/lib/input_exports_Actions.html#click
*
* @param {WebElementWrapper} element Optional
* @param {number} xOffset Optional
* @param {number} yOffset Optional
* @param {x: number, y: number} point on browser page
* @return {Promise<void>}
*/
public async clickMouseButton(element: any, xOffset: number, yOffset: number): Promise<void>;
public async clickMouseButton(element: WebElementWrapper): Promise<void>;
public async clickMouseButton(...args: unknown[]): Promise<void> {
const arg0 = args[0];
if (arg0 instanceof WebElementWrapper) {
public async clickMouseButton(point: { x: number; y: number }): Promise<void> {
if (this.isW3CEnabled) {
await this.getActions()
.pause(this.getActions().mouse)
.move({ origin: arg0._webElement })
.click()
.move({ x: 0, y: 0 })
.perform();
} else if (isNaN(args[1] as number) || isNaN(args[2] as number) === false) {
await this.getActions()
.pause(this.getActions().mouse)
.move({ origin: { x: args[1], y: args[2] } })
.move({ x: point.x, y: point.y, origin: 'pointer' })
.click()
.perform();
} else {
throw new Error('Element or coordinates should be provided');
await this.getActions()
.pause(this.getActions().mouse)
.move({ x: point.x, y: point.y, origin: 'pointer' })
.click()
.perform();
}
}
@ -378,16 +327,10 @@ export async function BrowserProvider({ getService }: FtrProviderContext) {
* @param {WebElementWrapper} element
* @return {Promise<void>}
*/
public async doubleClick(element?: WebElementWrapper): Promise<void> {
if (element instanceof WebElementWrapper) {
await this.getActions()
.doubleClick(element._webElement)
.perform();
} else {
await this.getActions()
.doubleClick()
.perform();
}
public async doubleClick(): Promise<void> {
await this.getActions()
.doubleClick()
.perform();
}
/**

View file

@ -47,7 +47,7 @@ export function ComboBoxProvider({ getService, getPageObjects }: FtrProviderCont
}
private async clickOption(isMouseClick: boolean, element: WebElementWrapper) {
return isMouseClick ? await browser.clickMouseButton(element) : await element.click();
return isMouseClick ? await element.clickMouseButton() : await element.click();
}
/**

View file

@ -26,7 +26,6 @@ const OPEN_INSPECTOR_TEST_SUBJ = 'embeddablePanelAction-openInspector';
export function DashboardPanelActionsProvider({ getService, getPageObjects }) {
const log = getService('log');
const browser = getService('browser');
const testSubjects = getService('testSubjects');
const PageObjects = getPageObjects(['header', 'common']);
@ -45,7 +44,7 @@ export function DashboardPanelActionsProvider({ getService, getPageObjects }) {
async toggleContextMenu(parent) {
log.debug('toggleContextMenu');
await (parent ? browser.moveMouseTo(parent) : testSubjects.moveMouseTo('dashboardPanelTitle'));
await (parent ? parent.moveMouseTo() : testSubjects.moveMouseTo('dashboardPanelTitle'));
const toggleMenuItem = await this.findContextMenu(parent);
await toggleMenuItem.click();
}

View file

@ -22,7 +22,6 @@ import expect from '@kbn/expect';
export function InspectorProvider({ getService }) {
const log = getService('log');
const retry = getService('retry');
const browser = getService('browser');
const renderable = getService('renderable');
const flyout = getService('flyout');
const testSubjects = getService('testSubjects');
@ -132,7 +131,7 @@ export function InspectorProvider({ getService }) {
await retry.try(async () => {
const table = await testSubjects.find('inspectorTable');
const cell = await table.findByCssSelector(`tbody tr:nth-child(${row}) td:nth-child(${column})`);
await browser.moveMouseTo(cell);
await cell.moveMouseTo();
const filterBtn = await testSubjects.findDescendant('filterForInspectorCellValue', cell);
await filterBtn.click();
});
@ -143,7 +142,7 @@ export function InspectorProvider({ getService }) {
await retry.try(async () => {
const table = await testSubjects.find('inspectorTable');
const cell = await table.findByCssSelector(`tbody tr:nth-child(${row}) td:nth-child(${column})`);
await browser.moveMouseTo(cell);
await cell.moveMouseTo();
const filterBtn = await testSubjects.findDescendant('filterOutInspectorCellValue', cell);
await filterBtn.click();
});

View file

@ -58,6 +58,7 @@ export class WebElementWrapper {
private Keys: IKey = this.webDriver.Key;
private driver: WebDriver = this.webDriver.driver;
public LegacyAction: any = this.webDriver.LegacyActionSequence;
public isW3CEnabled: boolean = (this.webDriver.driver as any).executor_.w3c === true;
public static create(
webElement: WebElement | WebElementWrapper,
@ -149,6 +150,12 @@ export class WebElementWrapper {
}
}
private getActions(): any {
return this.isW3CEnabled
? (this.driver as any).actions()
: (this.driver as any).actions({ bridge: true });
}
/**
* Returns whether or not the element would be visible to an actual user. This means
* that the following types of elements are considered to be not displayed:
@ -402,29 +409,81 @@ export class WebElementWrapper {
}
/**
* Moves the remote environments mouse cursor to the current element
* Moves the remote environments mouse cursor to the current element with optional offset
* https://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/lib/input_exports_Actions.html#move
*
* @param { xOffset: 0, yOffset: 0 } options
* @return {Promise<void>}
*/
public async moveMouseTo() {
public async moveMouseTo(options = { xOffset: 0, yOffset: 0 }) {
await this.retryCall(async function moveMouseTo(wrapper) {
await wrapper.scrollIntoViewIfNecessary();
if (wrapper.browserType === Browsers.Firefox) {
const actions = (wrapper.driver as any).actions();
await actions.move({ x: 0, y: 0 }).perform();
await actions.move({ x: 10, y: 10, origin: wrapper._webElement }).perform();
if (wrapper.isW3CEnabled) {
await wrapper
.getActions()
.move({ x: 0, y: 0 })
.perform();
await wrapper
.getActions()
.move({ x: options.xOffset, y: options.yOffset, origin: wrapper._webElement })
.perform();
} else {
const mouse = (wrapper.driver.actions() as any).mouse();
const actions = (wrapper.driver as any).actions({ bridge: true });
await actions
.pause(mouse)
.move({ origin: wrapper._webElement })
await wrapper
.getActions()
.pause(wrapper.getActions().mouse)
.move({ x: options.xOffset, y: options.yOffset, origin: wrapper._webElement })
.perform();
}
});
}
/**
* Inserts an action for moving the mouse to element center, unless optional offset is provided.
* Then adds an action for left-click (down/up) with the mouse.
* https://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/lib/input_exports_Actions.html#click
*
* @param { xOffset: 0, yOffset: 0 } options Optional
* @return {Promise<void>}
*/
public async clickMouseButton(options = { xOffset: 0, yOffset: 0 }): Promise<void> {
await this.retryCall(async function clickMouseButton(wrapper) {
await wrapper.scrollIntoViewIfNecessary();
if (wrapper.isW3CEnabled) {
await wrapper
.getActions()
.move({ x: 0, y: 0 })
.perform();
await wrapper
.getActions()
.move({ x: options.xOffset, y: options.yOffset, origin: wrapper._webElement })
.click()
.perform();
} else {
await wrapper
.getActions()
.pause(wrapper.getActions().mouse)
.move({ x: options.xOffset, y: options.yOffset, origin: wrapper._webElement })
.click()
.perform();
}
});
}
/**
* Inserts action for performing a double left-click with the mouse.
* https://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/lib/input_exports_Actions.html#doubleClick
* @param {WebElementWrapper} element
* @return {Promise<void>}
*/
public async doubleClick(): Promise<void> {
await this.retryCall(async function clickMouseButton(wrapper) {
await wrapper.scrollIntoViewIfNecessary();
await wrapper
.getActions()
.doubleClick(wrapper._webElement)
.perform();
});
}
/**
* Gets the first element inside this element matching the given CSS selector.
* https://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/lib/webdriver_exports_WebElement.html#findElement

View file

@ -30,7 +30,6 @@ interface ExistsOptions {
export function TestSubjectsProvider({ getService }: FtrProviderContext) {
const log = getService('log');
const retry = getService('retry');
const browser = getService('browser');
const find = getService('find');
const config = getService('config');
@ -96,7 +95,7 @@ export function TestSubjectsProvider({ getService }: FtrProviderContext) {
log.debug(`TestSubjects.doubleClick(${selector})`);
const element = await this.find(selector, timeout);
await element.moveMouseTo();
await browser.doubleClick(element);
await element.doubleClick();
});
}

View file

@ -37,7 +37,7 @@ export default function codeIntelligenceFunctionalTests({
expect(spans.length).to.greaterThan(1);
const userModelSpan = spans[1];
expect(await userModelSpan.getVisibleText()).to.equal('UserModel');
await browser.moveMouseTo(userModelSpan);
await userModelSpan.moveMouseTo();
// Expect the go to definition button show up eventually.
expect(await exists('codeGoToDefinitionButton')).to.be(true);
@ -180,7 +180,7 @@ export default function codeIntelligenceFunctionalTests({
expect(spans.length).to.greaterThan(1);
const userModelSpan = spans[0];
expect(await userModelSpan.getVisibleText()).to.equal('UserModel');
await browser.moveMouseTo(userModelSpan);
await userModelSpan.moveMouseTo();
// Expect the go to definition button show up eventually.
expect(await exists('codeFindReferenceButton')).to.be(true);
@ -231,7 +231,7 @@ export default function codeIntelligenceFunctionalTests({
expect(spans.length).to.greaterThan(1);
const asyncSpan = spans[1];
expect(await asyncSpan.getVisibleText()).to.equal('async');
await browser.moveMouseTo(asyncSpan);
await asyncSpan.moveMouseTo();
// Expect the go to definition button show up eventually.
expect(await exists('codeGoToDefinitionButton')).to.be(true);

View file

@ -8,7 +8,6 @@ import expect from '@kbn/expect';
export default function ({ getService, getPageObjects }) {
const PageObjects = getPageObjects(['maps', 'common']);
const browser = getService('browser');
const retry = getService('retry');
describe('full screen mode', () => {
@ -40,7 +39,7 @@ export default function ({ getService, getPageObjects }) {
it('exits when the text button is clicked on', async () => {
const logoButton = await PageObjects.maps.getExitFullScreenLogoButton();
await browser.moveMouseTo(logoButton);
await logoButton.moveMouseTo();
await PageObjects.maps.clickExitFullScreenTextButton();
await retry.try(async () => {

View file

@ -601,8 +601,8 @@ export function GisPageProvider({ getService, getPageObjects }) {
async lockTooltipAtPosition(xOffset, yOffset) {
await retry.try(async () => {
const mapContainerElement = await testSubjects.find('mapContainer');
await browser.moveMouseTo(mapContainerElement, xOffset, yOffset);
await browser.clickMouseButton(mapContainerElement, xOffset, yOffset);
await mapContainerElement.moveMouseTo({ xOffset, yOffset });
await mapContainerElement.clickMouseButton({ xOffset, yOffset });
// Close button is only displayed with tooltip is locked
const hasCloseButton = await testSubjects.exists('mapTooltipCloseButton');
if (!hasCloseButton) {

View file

@ -164,7 +164,7 @@ export function SecurityPageProvider({ getService, getPageObjects }) {
async clickSaveEditRole() {
const saveButton = await retry.try(() => testSubjects.find('roleFormSaveButton'));
await browser.moveMouseTo(saveButton);
await saveButton.moveMouseTo();
await saveButton.click();
await PageObjects.header.waitUntilLoadingHasFinished();
}