kibana/test/functional/services/browser.ts
Dmitry Lemeshko b5654b4660
[services/remote] print Firefox console logs in stdout (#46700) (#47532)
* [services/remote] print Firefox console logs in stdout

* [ftr/firefox] setup a socket for firefox to write stdout to

* remove unused imports

* unsubscribe from polling logs on cleanup

* tear down more completely on cleanup
2019-10-08 09:00:39 +02:00

437 lines
15 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { cloneDeep } from 'lodash';
import { Key, Origin } from 'selenium-webdriver';
// @ts-ignore internal modules are not typed
import { LegacyActionSequence } from 'selenium-webdriver/lib/actions';
import { modifyUrl } from '../../../src/core/utils';
import { WebElementWrapper } from './lib/web_element_wrapper';
import { FtrProviderContext } from '../ftr_provider_context';
import { Browsers } from './remote/browsers';
export async function BrowserProvider({ getService }: FtrProviderContext) {
const log = getService('log');
const { driver, browserType, consoleLog$ } = await getService('__webdriver__').init();
consoleLog$.subscribe(({ message, level }) => {
log[level === 'SEVERE' || level === 'error' ? 'error' : 'debug'](
`browser[${level}] ${message}`
);
});
const isW3CEnabled = (driver as any).executor_.w3c === true;
return new (class BrowserService {
/**
* Keyboard events
*/
public readonly keys = Key;
/**
* Browser name
*/
public readonly browserType: string = browserType;
public readonly isChrome: boolean = browserType === Browsers.Chrome;
public readonly isFirefox: boolean = browserType === Browsers.Firefox;
/**
* Is WebDriver instance W3C compatible
*/
isW3CEnabled = isW3CEnabled;
/**
* Returns instance of Actions API based on driver w3c flag
* https://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/lib/webdriver_exports_WebDriver.html#actions
*/
public getActions() {
return this.isW3CEnabled ? driver.actions() : driver.actions({ bridge: true });
}
/**
* Get handle for an alert, confirm, or prompt dialog. (if any).
* @return {Promise<void>}
*/
public async getAlert() {
try {
return await driver.switchTo().alert();
} catch (e) {
return null;
}
}
/**
* Retrieves the a rect describing the current top-level window's size and position.
* https://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/lib/webdriver_exports_Window.html
*
* @return {Promise<{height: number, width: number, x: number, y: number}>}
*/
public async getWindowSize(): Promise<{ height: number; width: number; x: number; y: number }> {
return await driver
.manage()
.window()
.getRect();
}
/**
* Sets the dimensions of a window.
* https://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/lib/webdriver_exports_Window.html
*
* @param {number} width
* @param {number} height
* @return {Promise<void>}
*/
public async setWindowSize(width: number, height: number) {
await driver
.manage()
.window()
.setRect({ width, height });
}
/**
* Gets the URL that is loaded in the focused window/frame.
* https://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/lib/webdriver_exports_WebDriver.html#getCurrentUrl
*
* @return {Promise<string>}
*/
public async getCurrentUrl() {
// strip _t=Date query param when url is read
const current = await driver.getCurrentUrl();
const currentWithoutTime = modifyUrl(current, parsed => {
delete (parsed.query as any)._t;
return void 0;
});
return currentWithoutTime;
}
/**
* Navigates the focused window/frame to a new URL.
* https://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/chrome_exports_Driver.html#get
*
* @param {string} url
* @param {boolean} insertTimestamp Optional
* @return {Promise<void>}
*/
public async get(url: string, insertTimestamp: boolean = true) {
if (insertTimestamp) {
const urlWithTime = modifyUrl(url, parsed => {
(parsed.query as any)._t = Date.now();
return void 0;
});
return await driver.get(urlWithTime);
}
return await driver.get(url);
}
/**
* 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 {x: number, y: number} point on browser page
* @return {Promise<void>}
*/
public async moveMouseTo(point: { x: number; y: number }): Promise<void> {
if (this.isW3CEnabled) {
await this.getActions()
.move({ x: 0, y: 0 })
.perform();
await this.getActions()
.move({ x: point.x, y: point.y, origin: Origin.POINTER })
.perform();
} else {
await this.getActions()
.pause(this.getActions().mouse)
.move({ x: point.x, y: point.y, origin: Origin.POINTER })
.perform();
}
}
/**
* Does a drag-and-drop action from one point to another
* https://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/lib/input_exports_Actions.html#dragAndDrop
*
* @param {{element: WebElementWrapper | {x: number, y: number}, offset: {x: number, y: number}}} from
* @param {{element: WebElementWrapper | {x: number, y: number}, offset: {x: number, y: number}}} to
* @return {Promise<void>}
*/
public async dragAndDrop(
from: { offset: { x: any; y: any }; location: any },
to: { offset: { x: any; y: any }; location: any }
) {
if (this.isW3CEnabled) {
// 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: Origin.POINTER };
};
const startPoint = getW3CPoint(from);
const endPoint = getW3CPoint(to);
await this.getActions()
.move({ x: 0, y: 0 })
.perform();
return await this.getActions()
.move(startPoint)
.press()
.move(endPoint)
.release()
.perform();
} else {
// 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.location._webElement })
.press()
.move({ x: to.location.x, y: to.location.y, origin: Origin.POINTER })
.release()
.perform();
} else {
return await new LegacyActionSequence(driver)
.mouseMove(from.location._webElement, getOffset(from.offset))
.mouseDown()
.mouseMove(to.location._webElement, getOffset(to.offset))
.mouseUp()
.perform();
}
}
}
/**
* Reloads the current browser window/frame.
* https://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/lib/webdriver_exports_Navigation.html#refresh
*
* @return {Promise<void>}
*/
public async refresh() {
await driver.navigate().refresh();
}
/**
* Navigates the focused window/frame back one page using the browsers navigation history.
* https://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/lib/webdriver_exports_Navigation.html#back
*
* @return {Promise<void>}
*/
public async goBack() {
await driver.navigate().back();
}
/**
* Moves forwards in the browser history.
* https://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/lib/webdriver_exports_Navigation.html#forward
*/
public async goForward() {
await driver.navigate().forward();
}
/**
* Sends a sequance of keyboard keys. For each key, this will record a pair of keyDown and keyUp actions
* https://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/lib/input_exports_Actions.html#sendKeys
*
* @param {string|string[]} keys
* @return {Promise<void>}
*/
public async pressKeys(keys: string | string[]): Promise<void>;
public async pressKeys(...args: string[]): Promise<void>;
public async pressKeys(...args: string[]): Promise<void> {
const chord = this.keys.chord(...args);
await this.getActions()
.sendKeys(chord)
.perform();
}
/**
* 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 {x: number, y: number} point on browser page
* @return {Promise<void>}
*/
public async clickMouseButton(point: { x: number; y: number }) {
if (this.isW3CEnabled) {
await this.getActions()
.move({ x: 0, y: 0 })
.perform();
await this.getActions()
.move({ x: point.x, y: point.y, origin: Origin.POINTER })
.click()
.perform();
} else {
await this.getActions()
.pause(this.getActions().mouse)
.move({ x: point.x, y: point.y, origin: Origin.POINTER })
.click()
.perform();
}
}
/**
* Gets the HTML loaded in the focused window/frame. This markup is serialised by the remote
* environment so may not exactly match the HTML provided by the Web server.
* https://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/lib/webdriver_exports_WebDriver.html#getPageSource
*
* @return {Promise<string>}
*/
public async getPageSource() {
return await driver.getPageSource();
}
/**
* Gets a screenshot of the focused window and returns it as a base-64 encoded PNG
* https://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/lib/webdriver_exports_WebDriver.html#takeScreenshot
*
* @return {Promise<Buffer>}
*/
public async takeScreenshot() {
return await driver.takeScreenshot();
}
/**
* 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() {
await this.getActions()
.doubleClick()
.perform();
}
/**
* Changes the focus of all future commands to another window. Windows may be specified
* by their window.name attributeor by its handle (as returned by WebDriver#getWindowHandles).
* https://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/lib/webdriver_exports_TargetLocator.html
*
* @param {string} handle
* @return {Promise<void>}
*/
public async switchToWindow(nameOrHandle: string) {
await driver.switchTo().window(nameOrHandle);
}
/**
* Gets a list of identifiers for all currently open windows.
* https://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/lib/webdriver_exports_WebDriver.html#getAllWindowHandles
*
* @return {Promise<string[]>}
*/
public async getAllWindowHandles() {
return await driver.getAllWindowHandles();
}
/**
* Sets a value in local storage for the focused window/frame.
*
* @param {string} key
* @param {string} value
* @return {Promise<void>}
*/
public async setLocalStorageItem(key: string, value: string): Promise<void> {
await driver.executeScript(
'return window.localStorage.setItem(arguments[0], arguments[1]);',
key,
value
);
}
/**
* Closes the currently focused window. In most environments, after the window has been
* closed, it is necessary to explicitly switch to whatever window is now focused.
* https://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/lib/webdriver_exports_WebDriver.html#close
*
* @return {Promise<void>}
*/
public async closeCurrentWindow() {
await driver.close();
}
/**
* Executes JavaScript code within the focused window/frame. The code should return a value synchronously.
* https://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/lib/webdriver_exports_WebDriver.html#executeScript
*
* @param {string|function} fn
* @param {...any[]} args
*/
public async execute<A extends any[], R>(
fn: string | ((...args: A) => R),
...args: A
): Promise<R> {
return await driver.executeScript(
fn,
...cloneDeep<any>(args, arg => {
if (arg instanceof WebElementWrapper) {
return arg._webElement;
}
})
);
}
public async executeAsync<A extends any[], R>(
fn: string | ((...args: A) => R),
...args: A
): Promise<R> {
return await driver.executeAsyncScript(
fn,
...cloneDeep<any>(args, arg => {
if (arg instanceof WebElementWrapper) {
return arg._webElement;
}
})
);
}
public async getScrollTop() {
const scrollSize = await driver.executeScript<string>('return document.body.scrollTop');
return parseInt(scrollSize, 10);
}
public async getScrollLeft() {
const scrollSize = await driver.executeScript<string>('return document.body.scrollLeft');
return parseInt(scrollSize, 10);
}
// return promise with REAL scroll position
public async setScrollTop(scrollSize: number | string) {
await driver.executeScript('document.body.scrollTop = ' + scrollSize);
return this.getScrollTop();
}
public async setScrollLeft(scrollSize: number | string) {
await driver.executeScript('document.body.scrollLeft = ' + scrollSize);
return this.getScrollLeft();
}
})();
}