Skip to content
This repository has been archived by the owner on Jul 29, 2024. It is now read-only.

chore(types): Inherit from webdriver.WebDriver types #4016

Merged
merged 1 commit into from
Jan 30, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 11 additions & 84 deletions lib/browser.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {BPClient} from 'blocking-proxy';
import {ActionSequence, By, Capabilities, Command as WdCommand, FileDetector, ICommandName, Options, promise as wdpromise, Session, TargetLocator, TouchSequence, until, WebDriver, WebElement} from 'selenium-webdriver';
import {ActionSequence, By, Capabilities, Command as WdCommand, FileDetector, ICommandName, Options, promise as wdpromise, Session, TargetLocator, TouchSequence, until, WebDriver, WebElement, WebElementPromise} from 'selenium-webdriver';
import * as url from 'url';
import {extend as extendWD, ExtendedWebDriver} from 'webdriver-js-extender';

Expand Down Expand Up @@ -34,87 +34,14 @@ for (let foo in require('selenium-webdriver')) {
exports[foo] = require('selenium-webdriver')[foo];
}

// Explicitly define webdriver.WebDriver
// TODO: extend WebDriver from selenium-webdriver typings
export class AbstractWebDriver {
actions: () => ActionSequence;
call:
(fn: (...var_args: any[]) => any, opt_scope?: any,
...var_args: any[]) => wdpromise.Promise<any>;
close: () => void;
controlFlow: () => wdpromise.ControlFlow;
executeScript: (script: string|Function, ...var_args: any[]) => wdpromise.Promise<any>;
executeAsyncScript: (script: string|Function, ...var_args: any[]) => wdpromise.Promise<any>;
getCapabilities: () => wdpromise.Promise<Capabilities>;
getCurrentUrl: () => wdpromise.Promise<string>;
getPageSource: () => wdpromise.Promise<string>;
getSession: () => wdpromise.Promise<Session>;
getTitle: () => wdpromise.Promise<string>;
getWindowHandle: () => wdpromise.Promise<string>;
getAllWindowHandles: () => wdpromise.Promise<string[]>;
manage: () => Options;
quit: () => void;
schedule: (command: WdCommand, description: string) => wdpromise.Promise<any>;
setFileDetector: (detector: FileDetector) => void;
sleep: (ms: number) => wdpromise.Promise<void>;
switchTo: () => TargetLocator;
takeScreenshot: () => wdpromise.Promise<any>;
touchActions: () => TouchSequence;
wait:
(condition: wdpromise.Promise<any>|until.Condition<any>|Function, opt_timeout?: number,
opt_message?: string) => wdpromise.Promise<any>;
}

export class AbstractExtendedWebDriver extends AbstractWebDriver {
getNetworkConnection: () => wdpromise.Promise<0|1|2|3|4|5|6|7>;
setNetworkConnection:
(typeOrAirplaneMode: 0|1|2|3|4|5|6|7|boolean, wifi?: boolean,
data?: boolean) => wdpromise.Promise<void>;
toggleAirplaneMode: () => wdpromise.Promise<void>;
toggleWiFi: () => wdpromise.Promise<void>;
toggleData: () => wdpromise.Promise<void>;
toggleLocationServices: () => wdpromise.Promise<void>;
getGeolocation: () => wdpromise.Promise<{latitude: number, longitude: number, altitude: number}>;
setGeolocation:
(latitude?: number, longitude?: number, altitude?: number) => wdpromise.Promise<void>;
getCurrentDeviceActivity: () => wdpromise.Promise<string>;
startDeviceActivity:
(appPackage: string, appActivity: string, appWaitPackage?: string,
appWaitActivity?: string) => wdpromise.Promise<void>;
getAppiumSettings: () => wdpromise.Promise<{[name: string]: any}>;
setAppiumSettings: (settings: {[name: string]: any}) => wdpromise.Promise<void>;
getCurrentContext: () => wdpromise.Promise<string>;
selectContext: (name: string) => wdpromise.Promise<void>;
listContexts: () => wdpromise.Promise<string[]>;
getScreenOrientation: () => wdpromise.Promise<'LANDSCAPE'|'PORTRAIT'>;
setScreenOrientation: (orientation: string) => wdpromise.Promise<void>;
isDeviceLocked: () => wdpromise.Promise<boolean>;
lockDevice: (delay?: number) => wdpromise.Promise<void>;
unlockDevice: () => wdpromise.Promise<void>;
installApp: (appPath: string) => wdpromise.Promise<void>;
isAppInstalled: (bundleId: string) => wdpromise.Promise<boolean>;
removeApp: (appId: string) => wdpromise.Promise<void>;
pullFileFromDevice: (path: string) => wdpromise.Promise<string>;
pullFolderFromDevice: (path: string) => wdpromise.Promise<any>;
pushFileToDevice: (path: string, base64Data: string) => wdpromise.Promise<void>;
uploadFile: (base64Data: string) => wdpromise.Promise<void>;
switchToParentFrame: () => wdpromise.Promise<void>;
fullscreen: () => wdpromise.Promise<void>;
sendAppToBackground: (delay?: number) => wdpromise.Promise<void>;
closeApp: () => wdpromise.Promise<void>;
getAppStrings: (language?: string) => wdpromise.Promise<string[]>;
launchSession: () => wdpromise.Promise<void>;
resetApp: () => wdpromise.Promise<void>;
hideSoftKeyboard:
(strategy?: 'default'|'tapOutside'|'tapOut'|'swipeDown'|'pressKey'|'press',
key?: string) => wdpromise.Promise<void>;
getDeviceTime: () => wdpromise.Promise<string>;
openDeviceNotifications: () => wdpromise.Promise<void>;
rotationGesture:
(x?: number, y?: number, duration?: number, rotation?: number,
touchCount?: 1|2|3|4|5) => wdpromise.Promise<void>;
shakeDevice: () => wdpromise.Promise<void>;
}
// Explicitly define types for webdriver.WebDriver and ExtendedWebDriver.
// We do this because we use composition over inheritance to implement polymorphism, and therefore
// we don't want to inherit WebDriver's constructor.
export class AbstractWebDriver {}
export interface AbstractWebDriver extends WebDriver {}
export class AbstractExtendedWebDriver extends AbstractWebDriver {}
export interface AbstractExtendedWebDriver extends ExtendedWebDriver {}

/**
* Mix a function from one object onto another. The function will still be
Expand Down Expand Up @@ -808,10 +735,10 @@ export class ProtractorBrowser extends AbstractExtendedWebDriver {
/**
* Waits for Angular to finish rendering before searching for elements.
* @see webdriver.WebDriver.findElement
* @returns {!webdriver.promise.Promise} A promise that will be resolved to
* @returns {!webdriver.WebElementPromise} A promise that will be resolved to
* the located {@link webdriver.WebElement}.
*/
findElement(locator: Locator): WebElement {
findElement(locator: Locator): WebElementPromise {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should have been WebElementPromise the whole time

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should fix the JSDoc @returns to reflect the same return type.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

return this.element(locator).getWebElement();
}

Expand All @@ -821,7 +748,7 @@ export class ProtractorBrowser extends AbstractExtendedWebDriver {
* @returns {!webdriver.promise.Promise} A promise that will be resolved to an
* array of the located {@link webdriver.WebElement}s.
*/
findElements(locator: Locator): wdpromise.Promise<any> {
findElements(locator: Locator): wdpromise.Promise<WebElement[]> {
return this.element.all(locator).getWebElements();
}

Expand Down
8 changes: 4 additions & 4 deletions lib/element.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {By, error as wderror, ILocation, ISize, promise as wdpromise, WebDriver,

import {ElementHelper, ProtractorBrowser} from './browser';
import {IError} from './exitCodes';
import {Locator} from './locators';
import {isProtractorLocator, Locator} from './locators';
import {Logger} from './logger';

let clientSideScripts = require('./clientsidescripts');
Expand Down Expand Up @@ -159,7 +159,7 @@ export class ElementArrayFinder extends WebdriverWebElement {
// This is the first time we are looking for an element
return ptor.waitForAngular('Locator: ' + locator)
.then((): wdpromise.Promise<WebElement[]> => {
if (locator.findElementsOverride) {
if (isProtractorLocator(locator)) {
return locator.findElementsOverride(ptor.driver, null, ptor.rootEl);
} else {
return ptor.driver.findElements(locator);
Expand All @@ -170,7 +170,7 @@ export class ElementArrayFinder extends WebdriverWebElement {
// For each parent web element, find their children and construct
// a list of Promise<List<child_web_element>>
let childrenPromiseList = parentWebElements.map((parentWebElement: WebElement) => {
return locator.findElementsOverride ?
return isProtractorLocator(locator) ?
locator.findElementsOverride(ptor.driver, parentWebElement, ptor.rootEl) :
parentWebElement.findElements(locator);
});
Expand Down Expand Up @@ -905,7 +905,7 @@ export class ElementFinder extends WebdriverWebElement {
* browser.driver.findElement(by.css('.parent'));
* browser.findElement(by.css('.parent'));
*
* @returns {webdriver.WebElement}
* @returns {webdriver.WebElementPromise}
*/
getWebElement(): WebElementPromise {
let id = this.elementArrayFinder_.getWebElements().then((parentWebElements: WebElement[]) => {
Expand Down
63 changes: 35 additions & 28 deletions lib/locators.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import {By, promise as wdpromise, WebDriver, WebElement} from 'selenium-webdriver';
import {By, ByHash, promise as wdpromise, WebDriver, WebElement} from 'selenium-webdriver';

let clientSideScripts = require('./clientsidescripts');


// Explicitly define webdriver.By.
// We do this because we want to inherit the static methods of webdriver.By, as opposed to
// inheriting from the webdriver.By class itself, which is actually analogous to ProtractorLocator.
export class WebdriverBy {
className: (className: string) => By = By.className;
css: (css: string) => By = By.css;
Expand All @@ -15,15 +16,21 @@ export class WebdriverBy {
tagName: (tagName: string) => By = By.tagName;
xpath: (xpath: string) => By = By.xpath;
}
export type WebDriverLocator = By | ByHash | Function;

// Protractor locator strategy
export interface Locator {
findElementsOverride?:
export interface ProtractorLocator {
findElementsOverride:
(driver: WebDriver, using: WebElement,
rootSelector: string) => wdpromise.Promise<WebElement[]>;
row?: (index: number) => Locator;
column?: (index: string) => Locator;
}
export type Locator = ProtractorLocator | WebDriverLocator;

export function isProtractorLocator(x: Locator): x is ProtractorLocator {
return x && (typeof(x as any).findElementsOverride === 'function');
}

/**
* The Protractor Locators. These provide ways of finding elements in
Expand Down Expand Up @@ -70,7 +77,7 @@ export class ProtractorBy extends WebdriverBy {
* element. It should return an array of elements.
*/
addLocator(name: string, script: Function|string) {
this[name] = (...args: any[]): Locator => {
this[name] = (...args: any[]): ProtractorLocator => {
let locatorArguments = args;
return {
findElementsOverride: (driver: WebDriver, using: WebElement, rootSelector: string):
Expand Down Expand Up @@ -119,9 +126,9 @@ export class ProtractorBy extends WebdriverBy {
* var deprecatedSyntax = element(by.binding('{{person.name}}'));
*
* @param {string} bindingDescriptor
* @returns {Locator} location strategy
* @returns {ProtractorLocator} location strategy
*/
binding(bindingDescriptor: string): Locator {
binding(bindingDescriptor: string): ProtractorLocator {
return {
findElementsOverride: (driver: WebDriver, using: WebElement, rootSelector: string):
wdpromise.Promise<WebElement[]> => {
Expand Down Expand Up @@ -151,9 +158,9 @@ export class ProtractorBy extends WebdriverBy {
* expect(element(by.exactBinding('phone')).isPresent()).toBe(false);
*
* @param {string} bindingDescriptor
* @returns {Locator} location strategy
* @returns {ProtractorLocator} location strategy
*/
exactBinding(bindingDescriptor: string): Locator {
exactBinding(bindingDescriptor: string): ProtractorLocator {
return {
findElementsOverride: (driver: WebDriver, using: WebElement, rootSelector: string):
wdpromise.Promise<WebElement[]> => {
Expand All @@ -179,9 +186,9 @@ export class ProtractorBy extends WebdriverBy {
* expect(input.getAttribute('value')).toBe('Foo123');
*
* @param {string} model ng-model expression.
* @returns {Locator} location strategy
* @returns {ProtractorLocator} location strategy
*/
model(model: string): Locator {
model(model: string): ProtractorLocator {
return {
findElementsOverride: (driver: WebDriver, using: WebElement, rootSelector: string):
wdpromise.Promise<WebElement[]> => {
Expand All @@ -204,9 +211,9 @@ export class ProtractorBy extends WebdriverBy {
* element(by.buttonText('Save'));
*
* @param {string} searchText
* @returns {Locator} location strategy
* @returns {ProtractorLocator} location strategy
*/
buttonText(searchText: string): Locator {
buttonText(searchText: string): ProtractorLocator {
return {
findElementsOverride: (driver: WebDriver, using: WebElement, rootSelector: string):
wdpromise.Promise<WebElement[]> => {
Expand All @@ -229,9 +236,9 @@ export class ProtractorBy extends WebdriverBy {
* element(by.partialButtonText('Save'));
*
* @param {string} searchText
* @returns {Locator} location strategy
* @returns {ProtractorLocator} location strategy
*/
partialButtonText(searchText: string): Locator {
partialButtonText(searchText: string): ProtractorLocator {
return {
findElementsOverride: (driver: WebDriver, using: WebElement, rootSelector: string):
wdpromise.Promise<WebElement[]> => {
Expand All @@ -245,7 +252,7 @@ export class ProtractorBy extends WebdriverBy {
};

// Generate either by.repeater or by.exactRepeater
private byRepeaterInner(exact: boolean, repeatDescriptor: string): Locator {
private byRepeaterInner(exact: boolean, repeatDescriptor: string): ProtractorLocator {
let name = 'by.' + (exact ? 'exactR' : 'r') + 'epeater';
return {
findElementsOverride: (driver: WebDriver, using: WebElement, rootSelector: string):
Expand All @@ -256,7 +263,7 @@ export class ProtractorBy extends WebdriverBy {
toString: (): string => {
return name + '("' + repeatDescriptor + '")';
},
row: (index: number): Locator => {
row: (index: number): ProtractorLocator => {
return {
findElementsOverride: (driver: WebDriver, using: WebElement, rootSelector: string):
wdpromise.Promise<WebElement[]> => {
Expand All @@ -267,7 +274,7 @@ export class ProtractorBy extends WebdriverBy {
toString: (): string => {
return name + '(' + repeatDescriptor + '").row("' + index + '")"';
},
column: (binding: string): Locator => {
column: (binding: string): ProtractorLocator => {
return {
findElementsOverride: (driver: WebDriver, using: WebElement, rootSelector: string):
wdpromise.Promise<WebElement[]> => {
Expand All @@ -283,7 +290,7 @@ export class ProtractorBy extends WebdriverBy {
}
};
},
column: (binding: string): Locator => {
column: (binding: string): ProtractorLocator => {
return {
findElementsOverride: (driver: WebDriver, using: WebElement, rootSelector: string):
wdpromise.Promise<WebElement[]> => {
Expand All @@ -294,7 +301,7 @@ export class ProtractorBy extends WebdriverBy {
toString: (): string => {
return name + '("' + repeatDescriptor + '").column("' + binding + '")';
},
row: (index: number): Locator => {
row: (index: number): ProtractorLocator => {
return {
findElementsOverride: (driver: WebDriver, using: WebElement, rootSelector: string):
wdpromise.Promise<WebElement[]> => {
Expand Down Expand Up @@ -365,9 +372,9 @@ export class ProtractorBy extends WebdriverBy {
* var divs = element.all(by.repeater('book in library'));
*
* @param {string} repeatDescriptor
* @returns {Locator} location strategy
* @returns {ProtractorLocator} location strategy
*/
repeater(repeatDescriptor: string): Locator {
repeater(repeatDescriptor: string): ProtractorLocator {
return this.byRepeaterInner(false, repeatDescriptor);
}

Expand All @@ -387,9 +394,9 @@ export class ProtractorBy extends WebdriverBy {
* expect(element(by.exactRepeater('car in cars')).isPresent()).toBe(true);
*
* @param {string} repeatDescriptor
* @returns {Locator} location strategy
* @returns {ProtractorLocator} location strategy
*/
exactRepeater(repeatDescriptor: string): Locator {
exactRepeater(repeatDescriptor: string): ProtractorLocator {
return this.byRepeaterInner(true, repeatDescriptor);
}

Expand All @@ -408,9 +415,9 @@ export class ProtractorBy extends WebdriverBy {
*
* @param {string} cssSelector css selector
* @param {string} searchString text search
* @returns {Locator} location strategy
* @returns {ProtractorLocator} location strategy
*/
cssContainingText(cssSelector: string, searchText: string): Locator {
cssContainingText(cssSelector: string, searchText: string): ProtractorLocator {
return {
findElementsOverride: (driver: WebDriver, using: WebElement, rootSelector: string):
wdpromise.Promise<WebElement[]> => {
Expand Down Expand Up @@ -441,9 +448,9 @@ export class ProtractorBy extends WebdriverBy {
* expect(firstOption.getText()).toEqual('red');
*
* @param {string} optionsDescriptor ng-options expression.
* @returns {Locator} location strategy
* @returns {ProtractorLocator} location strategy
*/
options(optionsDescriptor: string): Locator {
options(optionsDescriptor: string): ProtractorLocator {
return {
findElementsOverride: (driver: WebDriver, using: WebElement, rootSelector: string):
wdpromise.Promise<WebElement[]> => {
Expand Down