diff --git a/gulpfile.js b/gulpfile.js index fcd9adac8..f5e847e71 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -72,7 +72,7 @@ gulp.task('default',['prepublish']); gulp.task('types', function(done) { var folder = 'built'; - var files = ['ptor', 'browser', 'locators', 'expectedConditions']; + var files = ['ptor', 'browser', 'element', 'locators', 'expectedConditions']; var outputFile = path.resolve(folder, 'index.d.ts'); var contents = ''; files.forEach(file => { @@ -104,8 +104,29 @@ var parseTypingsFile = function(folder, file) { if (line.indexOf('export') !== -1) { line = line.replace('export', '').trim(); } + + // Remove webdriver types and plugins for now + line = removeTypes(line,'webdriver.ActionSequence'); + line = removeTypes(line,'webdriver.promise.Promise'); + line = removeTypes(line,'webdriver.util.Condition'); + line = removeTypes(line,'webdriver.WebDriver'); + line = removeTypes(line,'webdriver.Locator'); + line = removeTypes(line,'webdriver.WebElement'); + line = removeTypes(line,'Plugins'); contents += line + '\n'; } + } return contents; } + +var removeTypes = function(line, webdriverType) { + var tempLine = line.trim(); + if (tempLine.startsWith('/**') || tempLine.startsWith('*')) { + return line; + } + if (line.indexOf(webdriverType) !== -1) { + return line.replace(new RegExp(webdriverType,'g'), 'any'); + } + return line; +} diff --git a/lib/browser.ts b/lib/browser.ts index 79e219f9f..c1b3477ab 100644 --- a/lib/browser.ts +++ b/lib/browser.ts @@ -4,7 +4,7 @@ import * as url from 'url'; import * as util from 'util'; import {ElementArrayFinder, ElementFinder, build$, build$$} from './element'; -import * as EC from './expectedConditions'; +import {ExpectedConditions_} from './expectedConditions'; import {Locator, ProtractorBy} from './locators'; import {Logger} from './logger2'; import {Plugins} from './plugins'; @@ -30,6 +30,22 @@ for (let foo in webdriver) { exports[foo] = webdriver[foo]; } +// Explicitly define webdriver.WebDriver. +export class Webdriver { + actions: () => webdriver.ActionSequence = webdriver.WebDriver.actions; + wait: + (condition: webdriver.promise.Promise|webdriver.util.Condition|Function, + opt_timeout?: number, + opt_message?: + string) => webdriver.promise.Promise = webdriver.WebDriver.wait; + sleep: (ms: number) => webdriver.promise.Promise = webdriver.WebDriver.sleep; + getCurrentUrl: + () => webdriver.promise.Promise = webdriver.WebDriver.getCurrentUrl; + getTitle: () => webdriver.promise.Promise = webdriver.WebDriver.getTitle; + takeScreenshot: + () => webdriver.promise.Promise = webdriver.WebDriver.takeScreenshot; +} + /** * Mix a function from one object onto another. The function will still be * called in the context of the original object. @@ -85,7 +101,7 @@ function buildElementHelper(browser: Browser): ElementHelper { * @param {boolean=} opt_untrackOutstandingTimeouts Whether Protractor should * stop tracking outstanding $timeouts. */ -export class Browser { +export class Browser extends Webdriver { /** * @type {ProtractorBy} */ @@ -94,7 +110,7 @@ export class Browser { /** * @type {ExpectedConditions} */ - static ExpectedConditions = new EC.ExpectedConditions(); + static ExpectedConditions = new ExpectedConditions_(); /** * The wrapped webdriver instance. Use this to interact with pages that do @@ -225,6 +241,7 @@ export class Browser { constructor( webdriverInstance: webdriver.WebDriver, opt_baseUrl?: string, opt_rootElement?: string, opt_untrackOutstandingTimeouts?: boolean) { + super(); // These functions should delegate to the webdriver instance, but should // wait for Angular to sync up before performing the action. This does not // include functions which are overridden by protractor below. @@ -281,7 +298,7 @@ export class Browser { * * Set by the runner. * - * @return {q.Promise} A promise which resolves to the capabilities object. + * @return {webdriver.promise.Promise} A promise which resolves to the capabilities object. */ getProcessedConfig: () => webdriver.promise.Promise; diff --git a/lib/element.ts b/lib/element.ts index 2ca79f688..ee77f3b58 100644 --- a/lib/element.ts +++ b/lib/element.ts @@ -14,6 +14,32 @@ let WEB_ELEMENT_FUNCTIONS = [ 'serialize', 'takeScreenshot' ]; +// Explicitly define webdriver.WebElement. +export class WebdriverWebElement { + getDriver: () => webdriver.WebDriver; + getId: () => webdriver.promise.Promise; + getRawId: () => webdriver.promise.Promise; + serialize: () => webdriver.promise.Promise; + findElement: (subLocator: Locator) => webdriver.promise.Promise; + click: () => webdriver.promise.Promise; + sendKeys: (...args: (string|webdriver.promise.Promise)[]) => + webdriver.promise.Promise; + getTagName: () => webdriver.promise.Promise; + getCssValue: (cssStyleProperty: string) => webdriver.promise.Promise; + getAttribute: (attributeName: string) => webdriver.promise.Promise; + getText: () => webdriver.promise.Promise; + getSize: () => webdriver.promise.Promise; + getLocation: () => webdriver.promise.Promise; + isEnabled: () => webdriver.promise.Promise; + isSelected: () => webdriver.promise.Promise; + submit: () => webdriver.promise.Promise; + clear: () => webdriver.promise.Promise; + isDisplayed: () => webdriver.promise.Promise; + takeScreenshot: (opt_scroll?: boolean) => webdriver.promise.Promise; + getOuterHtml: () => webdriver.promise.Promise; + getInnerHtml: () => webdriver.promise.Promise; +} + /** * ElementArrayFinder is used for operations on an array of elements (as opposed * to a single element). @@ -67,13 +93,14 @@ let WEB_ELEMENT_FUNCTIONS = [ * action result, or null if no action has been called. * @return {ElementArrayFinder} */ -export class ElementArrayFinder { +export class ElementArrayFinder extends WebdriverWebElement { getWebElements: Function; constructor( private browser_: Browser, getWebElements?: Function, private locator_?: any, public actionResults_: webdriver.promise.Promise = null) { + super(); this.getWebElements = getWebElements || null; // TODO(juliemr): might it be easier to combine this with our docs and just @@ -379,7 +406,7 @@ export class ElementArrayFinder { * * @return {webdriver.Locator} */ - locator(): any { return this.locator_; } + locator(): Locator { return this.locator_; } /** * Apply an action function to every element in the ElementArrayFinder, @@ -661,12 +688,14 @@ export class ElementArrayFinder { * that this is branched from. * @return {ElementFinder} */ -export class ElementFinder { +export class ElementFinder extends WebdriverWebElement { parentElementArrayFinder: ElementArrayFinder; elementArrayFinder_: ElementArrayFinder; - then: Function = null; + then: (fn: Function, errorFn: Function) => webdriver.promise.Promise = null; - constructor(private browser_: Browser, elementArrayFinder: ElementArrayFinder) { + constructor( + private browser_: Browser, elementArrayFinder: ElementArrayFinder) { + super(); if (!elementArrayFinder) { throw new Error('BUG: elementArrayFinder cannot be empty'); } @@ -680,6 +709,7 @@ export class ElementFinder { * * @param {function(webdriver.promise.Promise)} fn Function which takes * the value of the underlying actionResult. + * @param {function(Error)} errorFn * * @return {webdriver.promise.Promise} Promise which contains the results of * evaluating fn. @@ -731,10 +761,12 @@ export class ElementFinder { }); } - static fromWebElement_(ptor: any, webElem: any, locator: any): ElementFinder { + static fromWebElement_( + browser: Browser, webElem: webdriver.WebElement, + locator: Locator): ElementFinder { let getWebElements = () => { return webdriver.promise.fulfilled([webElem]); }; - return new ElementArrayFinder(ptor, getWebElements, locator) + return new ElementArrayFinder(browser, getWebElements, locator) .toElementFinder_(); } @@ -802,7 +834,7 @@ export class ElementFinder { * @param {webdriver.Locator} subLocator * @return {ElementArrayFinder} */ - all(subLocator: any): ElementArrayFinder { + all(subLocator: Locator): ElementArrayFinder { return this.elementArrayFinder_.all(subLocator); } @@ -833,7 +865,7 @@ export class ElementFinder { * @param {webdriver.Locator} subLocator * @return {ElementFinder} */ - element(subLocator: any): ElementFinder { + element(subLocator: Locator): ElementFinder { return this.all(subLocator).toElementFinder_(); } diff --git a/lib/expectedConditions.ts b/lib/expectedConditions.ts index 1d90734bd..66bf4c673 100644 --- a/lib/expectedConditions.ts +++ b/lib/expectedConditions.ts @@ -1,5 +1,6 @@ -import {protractor} from './ptor'; import {ElementFinder} from './element'; +import {protractor} from './ptor'; + let webdriver = require('selenium-webdriver'); /* globals browser */ @@ -44,7 +45,7 @@ let webdriver = require('selenium-webdriver'); * * @constructor */ -export class ExpectedConditions { +export class ExpectedConditions_ { /** * Negates the result of a promise. * @@ -189,7 +190,8 @@ export class ExpectedConditions { * @return {!function} An expected condition that returns a promise * representing whether the text is present in the element. */ - textToBePresentInElement(elementFinder: ElementFinder, text: string): Function { + textToBePresentInElement(elementFinder: ElementFinder, text: string): + Function { var hasText = () => { return elementFinder.getText().then((actualText: string): boolean => { return actualText.indexOf(text) > -1; @@ -213,7 +215,8 @@ export class ExpectedConditions { * @return {!function} An expected condition that returns a promise * representing whether the text is present in the element's value. */ - textToBePresentInElementValue(elementFinder: ElementFinder, text: string): Function { + textToBePresentInElementValue(elementFinder: ElementFinder, text: string): + Function { var hasText = () => { return elementFinder.getAttribute('value').then( (actualText: string): boolean => { diff --git a/lib/globals.d.ts b/lib/globals.d.ts index 8a554924f..9aab2b31b 100644 --- a/lib/globals.d.ts +++ b/lib/globals.d.ts @@ -35,6 +35,9 @@ declare interface String { startsWith: Function; } declare namespace webdriver { var error: any; + + class ActionSequence {} + class WebDriver { findElements: Function; getSession: Function; @@ -61,12 +64,15 @@ declare namespace webdriver { } namespace promise { - interface Promise { - controlFlow: Function, - then: Function - } + interface Promise { + controlFlow: Function; + then: Function; + } } + namespace util { + interface Condition {} + } class Capabilities { get: Function; } diff --git a/lib/locators.ts b/lib/locators.ts index e81ed81a4..3ed7c8d56 100644 --- a/lib/locators.ts +++ b/lib/locators.ts @@ -2,13 +2,21 @@ import * as util from 'util'; let webdriver: any = require('selenium-webdriver'); let clientSideScripts: any = require('./clientsidescripts'); -/** - * webdriver's By is an enum of locator functions, so we must set it to - * a prototype before inheriting from it. - */ -export class WebdriverBy {} -WebdriverBy.prototype = webdriver.By; +// Explicitly define webdriver.By. +export class WebdriverBy { + className: (className: string) => Locator = webdriver.By.className; + css: (css: string) => Locator = webdriver.By.css; + id: (id: string) => Locator = webdriver.By.id; + linkText: (linkText: string) => Locator = webdriver.By.linkText; + js: (js: string) => Locator = webdriver.By.js; + name: (name: string) => Locator = webdriver.By.name; + partialLinkText: + (partialText: string) => Locator = webdriver.By.partialLinkText; + tagName: (tagName: string) => Locator = webdriver.By.tagName; + xpath: (xpath: string) => Locator = webdriver.By.xpath; +} +// Interface for webdriver.Locator. export interface Locator { findElementsOverride?: (driver: webdriver.WebDriver, using: webdriver.WebElement, @@ -27,17 +35,6 @@ export interface Locator { export class ProtractorBy extends WebdriverBy { [key: string]: any; - className: (className: string) => Locator = webdriver.By.className; - css: (css: string) => Locator = webdriver.By.css; - id: (id: string) => Locator = webdriver.By.id; - linkText: (linkText: string) => Locator = webdriver.By.linkText; - js: (js: string) => Locator = webdriver.By.js; - name: (name: string) => Locator = webdriver.By.name; - partialLinkText: - (partialText: string) => Locator = webdriver.By.partialLinkText; - tagName: (tagName: string) => Locator = webdriver.By.tagName; - xpath: (xpath: string) => Locator = webdriver.By.xpath; - /** * Add a locator to this instance of ProtractorBy. This locator can then be * used with element(by.locatorName(args)). diff --git a/lib/ptor.ts b/lib/ptor.ts index 936838c9d..6b37a1448 100644 --- a/lib/ptor.ts +++ b/lib/ptor.ts @@ -1,39 +1,41 @@ import {Browser, ElementHelper} from './browser'; +import {ElementArrayFinder, ElementFinder} from './element'; +import {ExpectedConditions_} from './expectedConditions'; import {ProtractorBy} from './locators'; -import {ElementFinder, ElementArrayFinder} from './element'; -import * as EC from './expectedConditions'; -export namespace protractor { - export let browser: Browser; - export let $ = function(search: string): ElementFinder { - return null; - }; - export let $$ = function(search: string): ElementArrayFinder { - return null; - }; - export let element: ElementHelper; - export let By: ProtractorBy; - export let by: ProtractorBy; +let webdriver = require('selenium-webdriver'); - // TODO: Might need to fix imports - export let wrapDriver: Function; - export let ExpectedConditions: EC.ExpectedConditions; - export let promise = { - controlFlow: require('selenium-webdriver/lib/promise').controlFlow, - createFlow: require('selenium-webdriver/lib/promise').createFlow, - defer: require('selenium-webdriver/lib/promise').defer, - delayed: require('selenium-webdriver/lib/promise').delayed, - filter: require('selenium-webdriver/lib/promise').filter, - fulfilled: require('selenium-webdriver/lib/promise').fulfilled, - fullyResolved: require('selenium-webdriver/lib/promise').fullyResolved, - isPromise: require('selenium-webdriver/lib/promise').isPromise, - rejected: require('selenium-webdriver/lib/promise').rejected, - thenFinally: require('selenium-webdriver/lib/promise').thenFinally, - when: require('selenium-webdriver/lib/promise').when - }; - export let ActionSequence = require('selenium-webdriver/lib/actions').ActionSequence; - export let Key = require('selenium-webdriver/lib/input').Key; +export namespace protractor { +export let browser: Browser; +export let $ = function(search: string): ElementFinder { + return null; +}; +export let $$ = function(search: string): ElementArrayFinder { + return null; +}; +export let element: ElementHelper; +export let By: ProtractorBy; +export let by: ProtractorBy; +export let wrapDriver: Function; +export let ExpectedConditions: ExpectedConditions_; - export let Command = require('selenium-webdriver/lib/command').Command; - export let CommandName = require('selenium-webdriver/lib/command').Name; +// Define selenium webdriver imports. +export let promise = { + controlFlow: require('selenium-webdriver/lib/promise').controlFlow, + createFlow: require('selenium-webdriver/lib/promise').createFlow, + defer: require('selenium-webdriver/lib/promise').defer, + delayed: require('selenium-webdriver/lib/promise').delayed, + filter: require('selenium-webdriver/lib/promise').filter, + fulfilled: require('selenium-webdriver/lib/promise').fulfilled, + fullyResolved: require('selenium-webdriver/lib/promise').fullyResolved, + isPromise: require('selenium-webdriver/lib/promise').isPromise, + rejected: require('selenium-webdriver/lib/promise').rejected, + thenFinally: require('selenium-webdriver/lib/promise').thenFinally, + when: require('selenium-webdriver/lib/promise').when +}; +export let ActionSequence = + require('selenium-webdriver/lib/actions').ActionSequence; +export let Key = require('selenium-webdriver/lib/input').Key; +export let Command = require('selenium-webdriver/lib/command').Command; +export let CommandName = require('selenium-webdriver/lib/command').Name; }