cabbie-sync
Version:
A synchronous webdriver client
315 lines (281 loc) • 9.8 kB
Flow
/**
* @flow
* This file is generated automatically, run npm run build to re-generate.
**/
import type { SelectorType } from './enums/selector-types';
import type Driver from './driver';
import type { Options } from './flow-types/options';
import depd from 'depd';
import { resolve } from 'url';
import Alert from './alert';
import addDebugging from './add-debugging';
import Element from './element';
import Frame from './frame';
import GlobalMouse from './global-mouse';
import GlobalTouch from './global-touch';
import Navigator from './navigator';
import SelectorTypes from './enums/selector-types';
import BaseWindow from './base-window';
import WindowHandle from './window-handle';
import waitFor from './utils/wait-for';
const deprecate = depd('cabbie');
/*
* This is an object representing the currently active window. You can access the navigation for that window,
* touch and mouse objects for interacting via screen coordinates, and (most imporatantly) getElement and getElements.
*/
class ActiveWindow extends BaseWindow {
/*
* The global-touch object.
*/
touch: GlobalTouch;
/*
* Get the global-mouse object.
*/
mouse: GlobalMouse;
/*
* Get the Navigator object.
*
* @private
*/
navigator: Navigator;
/*
* Get the Frame object.
*/
frame: Frame;
/*
* Get the Alert object.
*/
alert: Alert;
_options: Options;
constructor(driver: Driver, options: Options) {
super(driver, 'current');
this.touch = new GlobalTouch(this.driver);
this.mouse = new GlobalMouse(this.driver);
this.navigator = new Navigator(this.driver, options);
this.frame = new Frame(this.driver);
this.alert = new Alert(this.driver);
this._options = options;
deprecate.property(this, 'navigator', 'All properties of navigator are now directly available on the ActiveWindow object');
}
/*
* Get a handle for the current window
*/
getWindowHandle(): WindowHandle {
const windowHandle = this.driver.requestJSON('GET', '/window_handle');
return new WindowHandle(this.driver, windowHandle);
}
/*
* Execute a script on the browser and return the result.
*
* Source should be either a function body as a string or a function.
* Keep in mind that if it is a function it will not have access to
* any variables from the node.js process.
*/
execute(script: string | Function, args: Array<any> = []): any {
return this.driver.requestJSON('POST', '/execute', { script: codeToString(script), args });
}
/*
* Execute a script asynchronously on the browser.
*
* Source should be either a function body as a string or a function.
* Keep in mind that if it is a function it will not have access to
* any variables from the node.js process.
*/
asyncExecute(script: string | Function, args: Array<any> = []): void {
this.driver.requestJSON('POST', '/execute_async', { script: codeToString(script), args });
}
/*
* Type a string of characters into the browser
*
* Note: Modifier keys is kept between calls, so mouse interactions can be performed
* while modifier keys are depressed.
*/
sendKeys(str: string | Array<string>): void {
this.driver.requestJSON('POST', '/keys', { value: Array.isArray(str) ? str : [str] });
}
/*
* Take a screenshot of the current page
*
* This returns the result as a buffer containing the binary image data
*/
takeScreenshot(): Buffer {
const base64data = this.driver.requestJSON('GET', '/screenshot');
return Buffer.from ? Buffer.from(base64data, 'base64') : new Buffer(base64data, 'base64');
}
/*
* Get the element on the page that currently has focus
*/
getActiveElement(): Element {
const elementHandle = this.driver.requestJSON('POST', '/element/active');
return new Element(this.driver, this.driver, '<active>', elementHandle);
}
/*
* Get an element via a selector.
* Will throw an error if the element does not exist.
*/
getElement(selector: string, selectorType: SelectorType = SelectorTypes.CSS): Element {
const elementHandle = waitFor(() => this.driver.requestJSON('POST', '/element', { using: selectorType, value: selector }));
return new Element(this.driver, this.driver, selector, elementHandle);
}
/*
* Get an element via a selector.
* Will return null if the element does not exist
*/
tryGetElement(selector: string, selectorType: SelectorType = SelectorTypes.CSS): Element | null {
try {
const elementHandle = this.driver.requestJSON('POST', '/element', { using: selectorType, value: selector });
return new Element(this.driver, this.driver, selector, elementHandle);
} catch (ex) {
if (ex.code === 'NoSuchElement' || ex.code === 'ElementNotVisible' || ex.code === 'ElementIsNotSelectable') {
return null;
}
throw ex;
}
}
/*
* Get elements via a selector.
*/
getElements(selector: string, selectorType: SelectorType = SelectorTypes.CSS): Array<Element> {
const elementHandles = this.driver.requestJSON('POST', '/elements', { using: selectorType, value: selector });
return elementHandles.map(elementHandle => {
return new Element(this.driver, this.driver, selector, elementHandle);
});
}
/*
* Get elements by its text content, optionally narrowed down using a selector.
*
* N.B. this is **much** slower than getting elements by ID or css selector.
*/
getElementsByTextContent(textContent: string, selector: string = '*', selectorType: SelectorType = SelectorTypes.CSS): Array<Element> {
if (selector === 'a' && (selectorType === SelectorTypes.CSS || selectorType === SelectorTypes.TAG)) {
selector = textContent;
selectorType = SelectorTypes.PARTIAL_LINK_TEXT;
}
textContent = textContent.trim();
const elements = this.getElements(selector, selectorType);
const elementsToReturn = [];
for (let i = 0; i < elements.length; i++) {
if (textContent === elements[i].getText().trim()) {
elementsToReturn.push(elements[i]);
}
}
return elementsToReturn;
}
/*
* Get elements by its text content, optionally narrowed down using a selector.
*
* N.B. this is **much** slower than getting elements by ID or css selector.
*/
getElementByTextContent(textContent: string, selector: string = '*', selectorType: SelectorType = SelectorTypes.CSS): Element {
const element = waitFor(() => this.tryGetElementByTextContent(textContent, selector, selectorType));
if (element) {
return element;
}
const err = new Error('Could not find an element with the text content: ' + textContent);
err.name = 'NoSuchElement';
(err: any).code = 'NoSuchElement';
throw err;
}
/*
* Get elements by its text content, optionally narrowed down using a selector.
*
* N.B. this is **much** slower than getting elements by ID or css selector.
*/
tryGetElementByTextContent(textContent: string, selector: string = '*', selectorType: SelectorType = SelectorTypes.CSS): Element | null {
if (selector === 'a' && (selectorType === SelectorTypes.CSS || selectorType === SelectorTypes.TAG)) {
selector = textContent;
selectorType = SelectorTypes.PARTIAL_LINK_TEXT;
}
textContent = textContent.trim().toLowerCase();
const elements = this.getElements(selector, selectorType);
for (let i = 0; i < elements.length; i++) {
if (textContent === elements[i].getText().trim().toLowerCase()) {
return elements[i];
}
}
return null;
}
/*
* Does a specific element exist?
*/
hasElement(selector: string, selectorType: SelectorType = SelectorTypes.CSS): boolean {
const elements = this.getElements(selector, selectorType);
return elements.length > 0;
}
/*
* Close the current window
*/
close(): void {
this.driver.requestJSON('DELETE', '/window');
}
/*
* Get the current page title
*/
getTitle(): string {
return this.driver.requestJSON('GET', '/title');
}
/*
* Get the current page source
*/
getSource(): string {
return this.driver.requestJSON('GET', '/source');
}
/*
* Navigate forwards in the browser history, if possible.
*/
goForward(): void {
this.driver.requestJSON('POST', '/forward');
}
/*
* Navigate backwards in the browser history, if possible.
*/
goBackward(): void {
this.driver.requestJSON('POST', '/back');
}
/*
* Refreshes the browser
*/
refresh(): void {
this.driver.requestJSON('POST', '/refresh');
}
/*
* Get the current url that the browser is displaying
*/
getUrl(): string {
return this.driver.requestJSON('GET', '/url');
}
/*
* Navigates the browser to the specified path
*
* - if `path` begins with a "/" it is relative to `options.base`
* - if `path` begins with "http" it is absolute
* - otherwise it is relative to the current path
*/
navigateTo(path: string): void {
if (path[0] === '/') {
const base = this._options.base;
if (!base) {
throw new Error('You must provide a "base" option to use urls starting with "/"');
}
path = base.replace(/\/$/, '') + path;
} else if (path.indexOf('http') !== 0) {
const base = this.getUrl();
this.navigateTo(resolve(base, path));
return;
}
this.driver.requestJSON('POST', '/url', { url: path });
}
}
/*
* Convert code to string before execution
*/
function codeToString(code: string | Function): string {
if (typeof code === 'function') {
// $FlowFixMe: intentionally concatenating string onto end of code
return 'return (' + code + ').apply(null, arguments);';
} else {
return code;
}
}
addDebugging(ActiveWindow);
export default ActiveWindow;