Skip to content

Commit 0c9bea5

Browse files
sjelinigniteram
authored andcommitted
chore(types): Inherit from webdriver.WebDriver types (angular#4016)
I decided to address this comment: angular#4000 (comment) While doing do I decided to take on this TODO: https://github.com/angular/protractor/blob/ccf02ab5f1070f0d7124318dc0099252f3c747e2/lib/browser.ts#L38 One possible issue here is that `ProtractorBrowser` only copies over methods, so it doesn't actually implement `WebDriver`'s interface. This isn't a problem right now, since `WebDriver`'s interface only has functions on it. But it could be a problem in the future.
1 parent 095d232 commit 0c9bea5

File tree

3 files changed

+50
-116
lines changed

3 files changed

+50
-116
lines changed

lib/browser.ts

+11-84
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {BPClient} from 'blocking-proxy';
2-
import {ActionSequence, By, Capabilities, Command as WdCommand, FileDetector, ICommandName, Options, promise as wdpromise, Session, TargetLocator, TouchSequence, until, WebDriver, WebElement} from 'selenium-webdriver';
2+
import {ActionSequence, By, Capabilities, Command as WdCommand, FileDetector, ICommandName, Options, promise as wdpromise, Session, TargetLocator, TouchSequence, until, WebDriver, WebElement, WebElementPromise} from 'selenium-webdriver';
33
import * as url from 'url';
44
import {extend as extendWD, ExtendedWebDriver} from 'webdriver-js-extender';
55

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

37-
// Explicitly define webdriver.WebDriver
38-
// TODO: extend WebDriver from selenium-webdriver typings
39-
export class AbstractWebDriver {
40-
actions: () => ActionSequence;
41-
call:
42-
(fn: (...var_args: any[]) => any, opt_scope?: any,
43-
...var_args: any[]) => wdpromise.Promise<any>;
44-
close: () => void;
45-
controlFlow: () => wdpromise.ControlFlow;
46-
executeScript: (script: string|Function, ...var_args: any[]) => wdpromise.Promise<any>;
47-
executeAsyncScript: (script: string|Function, ...var_args: any[]) => wdpromise.Promise<any>;
48-
getCapabilities: () => wdpromise.Promise<Capabilities>;
49-
getCurrentUrl: () => wdpromise.Promise<string>;
50-
getPageSource: () => wdpromise.Promise<string>;
51-
getSession: () => wdpromise.Promise<Session>;
52-
getTitle: () => wdpromise.Promise<string>;
53-
getWindowHandle: () => wdpromise.Promise<string>;
54-
getAllWindowHandles: () => wdpromise.Promise<string[]>;
55-
manage: () => Options;
56-
quit: () => void;
57-
schedule: (command: WdCommand, description: string) => wdpromise.Promise<any>;
58-
setFileDetector: (detector: FileDetector) => void;
59-
sleep: (ms: number) => wdpromise.Promise<void>;
60-
switchTo: () => TargetLocator;
61-
takeScreenshot: () => wdpromise.Promise<any>;
62-
touchActions: () => TouchSequence;
63-
wait:
64-
(condition: wdpromise.Promise<any>|until.Condition<any>|Function, opt_timeout?: number,
65-
opt_message?: string) => wdpromise.Promise<any>;
66-
}
6737

68-
export class AbstractExtendedWebDriver extends AbstractWebDriver {
69-
getNetworkConnection: () => wdpromise.Promise<0|1|2|3|4|5|6|7>;
70-
setNetworkConnection:
71-
(typeOrAirplaneMode: 0|1|2|3|4|5|6|7|boolean, wifi?: boolean,
72-
data?: boolean) => wdpromise.Promise<void>;
73-
toggleAirplaneMode: () => wdpromise.Promise<void>;
74-
toggleWiFi: () => wdpromise.Promise<void>;
75-
toggleData: () => wdpromise.Promise<void>;
76-
toggleLocationServices: () => wdpromise.Promise<void>;
77-
getGeolocation: () => wdpromise.Promise<{latitude: number, longitude: number, altitude: number}>;
78-
setGeolocation:
79-
(latitude?: number, longitude?: number, altitude?: number) => wdpromise.Promise<void>;
80-
getCurrentDeviceActivity: () => wdpromise.Promise<string>;
81-
startDeviceActivity:
82-
(appPackage: string, appActivity: string, appWaitPackage?: string,
83-
appWaitActivity?: string) => wdpromise.Promise<void>;
84-
getAppiumSettings: () => wdpromise.Promise<{[name: string]: any}>;
85-
setAppiumSettings: (settings: {[name: string]: any}) => wdpromise.Promise<void>;
86-
getCurrentContext: () => wdpromise.Promise<string>;
87-
selectContext: (name: string) => wdpromise.Promise<void>;
88-
listContexts: () => wdpromise.Promise<string[]>;
89-
getScreenOrientation: () => wdpromise.Promise<'LANDSCAPE'|'PORTRAIT'>;
90-
setScreenOrientation: (orientation: string) => wdpromise.Promise<void>;
91-
isDeviceLocked: () => wdpromise.Promise<boolean>;
92-
lockDevice: (delay?: number) => wdpromise.Promise<void>;
93-
unlockDevice: () => wdpromise.Promise<void>;
94-
installApp: (appPath: string) => wdpromise.Promise<void>;
95-
isAppInstalled: (bundleId: string) => wdpromise.Promise<boolean>;
96-
removeApp: (appId: string) => wdpromise.Promise<void>;
97-
pullFileFromDevice: (path: string) => wdpromise.Promise<string>;
98-
pullFolderFromDevice: (path: string) => wdpromise.Promise<any>;
99-
pushFileToDevice: (path: string, base64Data: string) => wdpromise.Promise<void>;
100-
uploadFile: (base64Data: string) => wdpromise.Promise<void>;
101-
switchToParentFrame: () => wdpromise.Promise<void>;
102-
fullscreen: () => wdpromise.Promise<void>;
103-
sendAppToBackground: (delay?: number) => wdpromise.Promise<void>;
104-
closeApp: () => wdpromise.Promise<void>;
105-
getAppStrings: (language?: string) => wdpromise.Promise<string[]>;
106-
launchSession: () => wdpromise.Promise<void>;
107-
resetApp: () => wdpromise.Promise<void>;
108-
hideSoftKeyboard:
109-
(strategy?: 'default'|'tapOutside'|'tapOut'|'swipeDown'|'pressKey'|'press',
110-
key?: string) => wdpromise.Promise<void>;
111-
getDeviceTime: () => wdpromise.Promise<string>;
112-
openDeviceNotifications: () => wdpromise.Promise<void>;
113-
rotationGesture:
114-
(x?: number, y?: number, duration?: number, rotation?: number,
115-
touchCount?: 1|2|3|4|5) => wdpromise.Promise<void>;
116-
shakeDevice: () => wdpromise.Promise<void>;
117-
}
38+
// Explicitly define types for webdriver.WebDriver and ExtendedWebDriver.
39+
// We do this because we use composition over inheritance to implement polymorphism, and therefore
40+
// we don't want to inherit WebDriver's constructor.
41+
export class AbstractWebDriver {}
42+
export interface AbstractWebDriver extends WebDriver {}
43+
export class AbstractExtendedWebDriver extends AbstractWebDriver {}
44+
export interface AbstractExtendedWebDriver extends ExtendedWebDriver {}
11845

11946
/**
12047
* Mix a function from one object onto another. The function will still be
@@ -808,10 +735,10 @@ export class ProtractorBrowser extends AbstractExtendedWebDriver {
808735
/**
809736
* Waits for Angular to finish rendering before searching for elements.
810737
* @see webdriver.WebDriver.findElement
811-
* @returns {!webdriver.promise.Promise} A promise that will be resolved to
738+
* @returns {!webdriver.WebElementPromise} A promise that will be resolved to
812739
* the located {@link webdriver.WebElement}.
813740
*/
814-
findElement(locator: Locator): WebElement {
741+
findElement(locator: Locator): WebElementPromise {
815742
return this.element(locator).getWebElement();
816743
}
817744

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

lib/element.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import {By, error as wderror, ILocation, ISize, promise as wdpromise, WebDriver,
22

33
import {ElementHelper, ProtractorBrowser} from './browser';
44
import {IError} from './exitCodes';
5-
import {Locator} from './locators';
5+
import {isProtractorLocator, Locator} from './locators';
66
import {Logger} from './logger';
77
import {falseIfMissing} from './util';
88

@@ -160,7 +160,7 @@ export class ElementArrayFinder extends WebdriverWebElement {
160160
// This is the first time we are looking for an element
161161
return ptor.waitForAngular('Locator: ' + locator)
162162
.then((): wdpromise.Promise<WebElement[]> => {
163-
if (locator.findElementsOverride) {
163+
if (isProtractorLocator(locator)) {
164164
return locator.findElementsOverride(ptor.driver, null, ptor.rootEl);
165165
} else {
166166
return ptor.driver.findElements(locator);
@@ -171,7 +171,7 @@ export class ElementArrayFinder extends WebdriverWebElement {
171171
// For each parent web element, find their children and construct
172172
// a list of Promise<List<child_web_element>>
173173
let childrenPromiseList = parentWebElements.map((parentWebElement: WebElement) => {
174-
return locator.findElementsOverride ?
174+
return isProtractorLocator(locator) ?
175175
locator.findElementsOverride(ptor.driver, parentWebElement, ptor.rootEl) :
176176
parentWebElement.findElements(locator);
177177
});
@@ -921,7 +921,7 @@ export class ElementFinder extends WebdriverWebElement {
921921
* browser.driver.findElement(by.css('.parent'));
922922
* browser.findElement(by.css('.parent'));
923923
*
924-
* @returns {webdriver.WebElement}
924+
* @returns {webdriver.WebElementPromise}
925925
*/
926926
getWebElement(): WebElementPromise {
927927
let id = this.elementArrayFinder_.getWebElements().then((parentWebElements: WebElement[]) => {

lib/locators.ts

+35-28
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
import {By, promise as wdpromise, WebDriver, WebElement} from 'selenium-webdriver';
1+
import {By, ByHash, promise as wdpromise, WebDriver, WebElement} from 'selenium-webdriver';
22

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

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

1921
// Protractor locator strategy
20-
export interface Locator {
21-
findElementsOverride?:
22+
export interface ProtractorLocator {
23+
findElementsOverride:
2224
(driver: WebDriver, using: WebElement,
2325
rootSelector: string) => wdpromise.Promise<WebElement[]>;
2426
row?: (index: number) => Locator;
2527
column?: (index: string) => Locator;
2628
}
29+
export type Locator = ProtractorLocator | WebDriverLocator;
30+
31+
export function isProtractorLocator(x: Locator): x is ProtractorLocator {
32+
return x && (typeof(x as any).findElementsOverride === 'function');
33+
}
2734

2835
/**
2936
* The Protractor Locators. These provide ways of finding elements in
@@ -70,7 +77,7 @@ export class ProtractorBy extends WebdriverBy {
7077
* element. It should return an array of elements.
7178
*/
7279
addLocator(name: string, script: Function|string) {
73-
this[name] = (...args: any[]): Locator => {
80+
this[name] = (...args: any[]): ProtractorLocator => {
7481
let locatorArguments = args;
7582
return {
7683
findElementsOverride: (driver: WebDriver, using: WebElement, rootSelector: string):
@@ -119,9 +126,9 @@ export class ProtractorBy extends WebdriverBy {
119126
* var deprecatedSyntax = element(by.binding('{{person.name}}'));
120127
*
121128
* @param {string} bindingDescriptor
122-
* @returns {Locator} location strategy
129+
* @returns {ProtractorLocator} location strategy
123130
*/
124-
binding(bindingDescriptor: string): Locator {
131+
binding(bindingDescriptor: string): ProtractorLocator {
125132
return {
126133
findElementsOverride: (driver: WebDriver, using: WebElement, rootSelector: string):
127134
wdpromise.Promise<WebElement[]> => {
@@ -151,9 +158,9 @@ export class ProtractorBy extends WebdriverBy {
151158
* expect(element(by.exactBinding('phone')).isPresent()).toBe(false);
152159
*
153160
* @param {string} bindingDescriptor
154-
* @returns {Locator} location strategy
161+
* @returns {ProtractorLocator} location strategy
155162
*/
156-
exactBinding(bindingDescriptor: string): Locator {
163+
exactBinding(bindingDescriptor: string): ProtractorLocator {
157164
return {
158165
findElementsOverride: (driver: WebDriver, using: WebElement, rootSelector: string):
159166
wdpromise.Promise<WebElement[]> => {
@@ -179,9 +186,9 @@ export class ProtractorBy extends WebdriverBy {
179186
* expect(input.getAttribute('value')).toBe('Foo123');
180187
*
181188
* @param {string} model ng-model expression.
182-
* @returns {Locator} location strategy
189+
* @returns {ProtractorLocator} location strategy
183190
*/
184-
model(model: string): Locator {
191+
model(model: string): ProtractorLocator {
185192
return {
186193
findElementsOverride: (driver: WebDriver, using: WebElement, rootSelector: string):
187194
wdpromise.Promise<WebElement[]> => {
@@ -204,9 +211,9 @@ export class ProtractorBy extends WebdriverBy {
204211
* element(by.buttonText('Save'));
205212
*
206213
* @param {string} searchText
207-
* @returns {Locator} location strategy
214+
* @returns {ProtractorLocator} location strategy
208215
*/
209-
buttonText(searchText: string): Locator {
216+
buttonText(searchText: string): ProtractorLocator {
210217
return {
211218
findElementsOverride: (driver: WebDriver, using: WebElement, rootSelector: string):
212219
wdpromise.Promise<WebElement[]> => {
@@ -229,9 +236,9 @@ export class ProtractorBy extends WebdriverBy {
229236
* element(by.partialButtonText('Save'));
230237
*
231238
* @param {string} searchText
232-
* @returns {Locator} location strategy
239+
* @returns {ProtractorLocator} location strategy
233240
*/
234-
partialButtonText(searchText: string): Locator {
241+
partialButtonText(searchText: string): ProtractorLocator {
235242
return {
236243
findElementsOverride: (driver: WebDriver, using: WebElement, rootSelector: string):
237244
wdpromise.Promise<WebElement[]> => {
@@ -245,7 +252,7 @@ export class ProtractorBy extends WebdriverBy {
245252
};
246253

247254
// Generate either by.repeater or by.exactRepeater
248-
private byRepeaterInner(exact: boolean, repeatDescriptor: string): Locator {
255+
private byRepeaterInner(exact: boolean, repeatDescriptor: string): ProtractorLocator {
249256
let name = 'by.' + (exact ? 'exactR' : 'r') + 'epeater';
250257
return {
251258
findElementsOverride: (driver: WebDriver, using: WebElement, rootSelector: string):
@@ -256,7 +263,7 @@ export class ProtractorBy extends WebdriverBy {
256263
toString: (): string => {
257264
return name + '("' + repeatDescriptor + '")';
258265
},
259-
row: (index: number): Locator => {
266+
row: (index: number): ProtractorLocator => {
260267
return {
261268
findElementsOverride: (driver: WebDriver, using: WebElement, rootSelector: string):
262269
wdpromise.Promise<WebElement[]> => {
@@ -267,7 +274,7 @@ export class ProtractorBy extends WebdriverBy {
267274
toString: (): string => {
268275
return name + '(' + repeatDescriptor + '").row("' + index + '")"';
269276
},
270-
column: (binding: string): Locator => {
277+
column: (binding: string): ProtractorLocator => {
271278
return {
272279
findElementsOverride: (driver: WebDriver, using: WebElement, rootSelector: string):
273280
wdpromise.Promise<WebElement[]> => {
@@ -283,7 +290,7 @@ export class ProtractorBy extends WebdriverBy {
283290
}
284291
};
285292
},
286-
column: (binding: string): Locator => {
293+
column: (binding: string): ProtractorLocator => {
287294
return {
288295
findElementsOverride: (driver: WebDriver, using: WebElement, rootSelector: string):
289296
wdpromise.Promise<WebElement[]> => {
@@ -294,7 +301,7 @@ export class ProtractorBy extends WebdriverBy {
294301
toString: (): string => {
295302
return name + '("' + repeatDescriptor + '").column("' + binding + '")';
296303
},
297-
row: (index: number): Locator => {
304+
row: (index: number): ProtractorLocator => {
298305
return {
299306
findElementsOverride: (driver: WebDriver, using: WebElement, rootSelector: string):
300307
wdpromise.Promise<WebElement[]> => {
@@ -365,9 +372,9 @@ export class ProtractorBy extends WebdriverBy {
365372
* var divs = element.all(by.repeater('book in library'));
366373
*
367374
* @param {string} repeatDescriptor
368-
* @returns {Locator} location strategy
375+
* @returns {ProtractorLocator} location strategy
369376
*/
370-
repeater(repeatDescriptor: string): Locator {
377+
repeater(repeatDescriptor: string): ProtractorLocator {
371378
return this.byRepeaterInner(false, repeatDescriptor);
372379
}
373380

@@ -387,9 +394,9 @@ export class ProtractorBy extends WebdriverBy {
387394
* expect(element(by.exactRepeater('car in cars')).isPresent()).toBe(true);
388395
*
389396
* @param {string} repeatDescriptor
390-
* @returns {Locator} location strategy
397+
* @returns {ProtractorLocator} location strategy
391398
*/
392-
exactRepeater(repeatDescriptor: string): Locator {
399+
exactRepeater(repeatDescriptor: string): ProtractorLocator {
393400
return this.byRepeaterInner(true, repeatDescriptor);
394401
}
395402

@@ -408,9 +415,9 @@ export class ProtractorBy extends WebdriverBy {
408415
*
409416
* @param {string} cssSelector css selector
410417
* @param {string} searchString text search
411-
* @returns {Locator} location strategy
418+
* @returns {ProtractorLocator} location strategy
412419
*/
413-
cssContainingText(cssSelector: string, searchText: string): Locator {
420+
cssContainingText(cssSelector: string, searchText: string): ProtractorLocator {
414421
return {
415422
findElementsOverride: (driver: WebDriver, using: WebElement, rootSelector: string):
416423
wdpromise.Promise<WebElement[]> => {
@@ -441,9 +448,9 @@ export class ProtractorBy extends WebdriverBy {
441448
* expect(firstOption.getText()).toEqual('red');
442449
*
443450
* @param {string} optionsDescriptor ng-options expression.
444-
* @returns {Locator} location strategy
451+
* @returns {ProtractorLocator} location strategy
445452
*/
446-
options(optionsDescriptor: string): Locator {
453+
options(optionsDescriptor: string): ProtractorLocator {
447454
return {
448455
findElementsOverride: (driver: WebDriver, using: WebElement, rootSelector: string):
449456
wdpromise.Promise<WebElement[]> => {

0 commit comments

Comments
 (0)