diff --git a/lib/protractor.ts b/lib/browser.ts similarity index 98% rename from lib/protractor.ts rename to lib/browser.ts index 924c3b548..941e269bd 100644 --- a/lib/protractor.ts +++ b/lib/browser.ts @@ -8,6 +8,7 @@ import {ProtractorBy} from './locators'; import {ElementArrayFinder, ElementFinder, build$, build$$} from './element'; export {ElementFinder, ElementArrayFinder}; import {Plugins} from './plugins'; +import {protractor} from './ptor'; let clientSideScripts = require('./clientsidescripts'); @@ -16,8 +17,6 @@ let clientSideScripts = require('./clientsidescripts'); import * as EC from './expectedConditions'; let webdriver = require('selenium-webdriver'); -let Command = require('selenium-webdriver/lib/command').Command; -let CommandName = require('selenium-webdriver/lib/command').Name; // jshint browser: true /* global angular */ @@ -76,14 +75,14 @@ export interface ElementHelper extends Function { * @param {Protractor} ptor * @return {function(webdriver.Locator): ElementFinder} */ -function buildElementHelper(ptor: Protractor): ElementHelper { +function buildElementHelper(browser: Browser): ElementHelper { let element: ElementHelper = function(locator: webdriver.Locator) { - return new ElementArrayFinder(ptor).all(locator).toElementFinder_(); + return new ElementArrayFinder(browser).all(locator).toElementFinder_(); }; element.all = function(locator: webdriver.Locator) { - return new ElementArrayFinder(ptor).all(locator); + return new ElementArrayFinder(browser).all(locator); } return element; @@ -100,7 +99,9 @@ function buildElementHelper(ptor: Protractor): ElementHelper { * @param {boolean=} opt_untrackOutstandingTimeouts Whether Protractor should * stop tracking outstanding $timeouts. */ -export class Protractor { +export class Browser { + ExpectedConditions = new EC.ExpectedConditions(); + /** * The wrapped webdriver instance. Use this to interact with pages that do * not contain Angular (such as a log-in screen). @@ -121,14 +122,14 @@ export class Protractor { * * @type {function(string): ElementFinder} */ - $: Function; + $: (query: string) => ElementFinder; /** * Shorthand function for finding arrays of elements by css. * * @type {function(string): ElementArrayFinder} */ - $$: Function; + $$: (query: string) => ElementArrayFinder; /** * All get methods will be resolved against this base URL. Relative URLs are = @@ -184,7 +185,7 @@ export class Protractor { * * @type {Plugins} Object containing plugin funtions from config. */ - private plugins_: Plugins; + plugins_: Plugins; /** * The reset URL to use between page loads. @@ -198,7 +199,7 @@ export class Protractor { * error message if Protractor fails to synchronize with Angular in time. * @private {boolean} */ - private trackOutstandingTimeouts_: boolean; + trackOutstandingTimeouts_: boolean; /** * If set, will be the universal timeout applied to all tests run by @@ -213,16 +214,16 @@ export class Protractor { * @type {Array<{name: string, script: function|string, args: * Array.}>} */ - private mockModules_: any[]; + mockModules_: any[]; /** * If specified, start a debugger server at specified port instead of repl * when running element explorer. * @private {number} */ - private debuggerServerPort_: number; + debuggerServerPort_: number; - private debuggerValidated_: boolean; + debuggerValidated_: boolean; // This index type allows looking up methods by name so we can do mixins. [key: string]: any; @@ -339,7 +340,7 @@ export class Protractor { } return this.driver.schedule( - new Command(CommandName.EXECUTE_SCRIPT) + new protractor.Command(protractor.CommandName.EXECUTE_SCRIPT) .setParameter('script', script) .setParameter('args', scriptArgs), description); @@ -364,7 +365,7 @@ export class Protractor { script = 'return (' + script + ').apply(null, arguments);'; } return this.driver.schedule( - new Command(CommandName.EXECUTE_ASYNC_SCRIPT) + new protractor.Command(protractor.CommandName.EXECUTE_ASYNC_SCRIPT) .setParameter('script', script) .setParameter('args', scriptArgs), description); @@ -972,7 +973,7 @@ export class Protractor { var context: Object = {require: require}; global.list = (locator: webdriver.Locator) => { /* globals browser */ - return browser.findElements(locator).then( + return protractor.browser.findElements(locator).then( (arr: webdriver.WebElement[]) => { var found: string[] = []; for (var i = 0; i < arr.length; ++i) { @@ -1200,7 +1201,7 @@ export class Protractor { */ export let wrapDriver = (webdriver: webdriver.WebDriver, baseUrl?: string, rootElement?: string, - untrackOutstandingTimeouts?: boolean) => { - return new Protractor( + untrackOutstandingTimeouts?: boolean): Browser => { + return new Browser( webdriver, baseUrl, rootElement, untrackOutstandingTimeouts); }; diff --git a/lib/configParser.ts b/lib/configParser.ts index dfcf3db5e..19831301b 100644 --- a/lib/configParser.ts +++ b/lib/configParser.ts @@ -117,7 +117,8 @@ export class ConfigParser { if (patterns) { for (let fileName of patterns) { - let matches = glob.hasMagic(fileName) ? glob.sync(fileName, {cwd}) : [fileName]; + let matches = + glob.hasMagic(fileName) ? glob.sync(fileName, {cwd}) : [fileName]; if (!matches.length && !opt_omitWarnings) { logger.warn('pattern ' + fileName + ' did not match any files.'); } diff --git a/lib/driverProviders/driverProvider.ts b/lib/driverProviders/driverProvider.ts index 6e6254480..65b201f54 100644 --- a/lib/driverProviders/driverProvider.ts +++ b/lib/driverProviders/driverProvider.ts @@ -72,6 +72,22 @@ export class DriverProvider { return deferred.promise; } + /** + * Default update job method. + * @return a promise + */ + updateJob(update: any): q.Promise { + return q.fcall(function() {}); + }; + + /** + * Default setup environment method. + * @return a promise + */ + setupEnv(): q.Promise { + return q.fcall(function() {}); + }; + /** * Teardown and destroy the environment and do any associated cleanup. * Shuts down the drivers. diff --git a/lib/element.ts b/lib/element.ts index 3298d7e95..b1fe1166e 100644 --- a/lib/element.ts +++ b/lib/element.ts @@ -2,7 +2,7 @@ let webdriver = require('selenium-webdriver'); let clientSideScripts = require('./clientsidescripts'); import {Logger} from './logger2'; -import {Protractor} from './protractor'; +import {Browser} from './browser'; let logger = new Logger('element'); @@ -56,7 +56,7 @@ let WEB_ELEMENT_FUNCTIONS = [ * }); * * @constructor - * @param {Protractor} ptor A protractor instance. + * @param {Browser} browser A protractor instance. * @param {function(): Array.} getWebElements A function * that returns a list of the underlying Web Elements. * @param {webdriver.Locator} locator The most relevant locator. It is only @@ -70,7 +70,7 @@ export class ElementArrayFinder { getWebElements: Function; constructor( - private ptor_: Protractor, getWebElements?: Function, + private browser_: Browser, getWebElements?: Function, private locator_?: any, public actionResults_: webdriver.Promise = null) { this.getWebElements = getWebElements || null; @@ -96,7 +96,7 @@ export class ElementArrayFinder { // modified. (Locator can be modified by the user, but that should // rarely/never happen and it doesn't affect functionalities). return new ElementArrayFinder( - this.ptor_, this.getWebElements, this.locator_, this.actionResults_); + this.browser_, this.getWebElements, this.locator_, this.actionResults_); } /** @@ -132,7 +132,7 @@ export class ElementArrayFinder { * @return {ElementArrayFinder} */ all(locator: any): ElementArrayFinder { - let ptor = this.ptor_; + let ptor = this.browser_; let getWebElements = () => { if (this.getWebElements === null) { // This is the first time we are looking for an element @@ -172,7 +172,7 @@ export class ElementArrayFinder { }); } }; - return new ElementArrayFinder(this.ptor_, getWebElements, locator); + return new ElementArrayFinder(this.browser_, getWebElements, locator); } /** @@ -210,7 +210,7 @@ export class ElementArrayFinder { let list = parentWebElements.map((parentWebElement: any, index: number) => { let elementFinder = ElementFinder.fromWebElement_( - this.ptor_, parentWebElement, this.locator_); + this.browser_, parentWebElement, this.locator_); return filterFn(elementFinder, index); }); @@ -222,7 +222,7 @@ export class ElementArrayFinder { }); }); }; - return new ElementArrayFinder(this.ptor_, getWebElements, this.locator_); + return new ElementArrayFinder(this.browser_, getWebElements, this.locator_); } /** @@ -268,7 +268,7 @@ export class ElementArrayFinder { return [parentWebElements[i]]; }); }; - return new ElementArrayFinder(this.ptor_, getWebElements, this.locator_) + return new ElementArrayFinder(this.browser_, getWebElements, this.locator_) .toElementFinder_(); } @@ -329,7 +329,7 @@ export class ElementArrayFinder { * @private */ toElementFinder_(): ElementFinder { - return new ElementFinder(this.ptor_, this); + return new ElementFinder(this.browser_, this); } /** @@ -399,7 +399,7 @@ export class ElementArrayFinder { throw e; }); return new ElementArrayFinder( - this.ptor_, this.getWebElements, this.locator_, actionResults); + this.browser_, this.getWebElements, this.locator_, actionResults); } /** @@ -412,7 +412,7 @@ export class ElementArrayFinder { return this.getWebElements().then((arr: webdriver.WebElement[]) => { return arr.map((webElem: webdriver.WebElement) => { return ElementFinder.fromWebElement_( - this.ptor_, webElem, this.locator_); + this.browser_, webElem, this.locator_); }); }); } diff --git a/lib/expectedConditions.ts b/lib/expectedConditions.ts index 821ee3b29..26df0fae3 100644 --- a/lib/expectedConditions.ts +++ b/lib/expectedConditions.ts @@ -1,4 +1,5 @@ -var webdriver = require('selenium-webdriver'); +import {protractor} from './ptor'; +let webdriver = require('selenium-webdriver'); /* globals browser */ @@ -140,7 +141,7 @@ export class ExpectedConditions { */ alertIsPresent(): Function { return () => { - return browser.switchTo().alert().then( + return protractor.browser.driver.switchTo().alert().then( (): boolean => { return true; }, (err: any) => { if (err.code == webdriver.error.ErrorCode.NO_SUCH_ALERT) { @@ -237,9 +238,10 @@ export class ExpectedConditions { */ titleContains(title: string): Function { return () => { - return browser.getTitle().then((actualTitle: string): boolean => { - return actualTitle.indexOf(title) > -1; - }); + return protractor.browser.driver.getTitle().then( + (actualTitle: string): boolean => { + return actualTitle.indexOf(title) > -1; + }); }; } @@ -258,7 +260,7 @@ export class ExpectedConditions { */ titleIs(title: string): Function { return () => { - return browser.getTitle().then( + return protractor.browser.driver.getTitle().then( (actualTitle: string): boolean => { return actualTitle === title; }); }; } diff --git a/lib/globals.d.ts b/lib/globals.d.ts index d5b42118a..410268fd7 100644 --- a/lib/globals.d.ts +++ b/lib/globals.d.ts @@ -1,18 +1,3 @@ -// Typescript transpiling will give a warning about 'global'. This work around -// is to allow protractor to set global variables. Warning message as follows: -// -// lib/globals.d.ts(2,19): error TS2300: Duplicate identifier 'global'. -// typings/main/ambient/node/index.d.ts(33,13): error TS2300: Duplicate -// identifier 'global'. -declare var browser: any; -declare var protractor: any; -declare var $: any; -declare var $$: any; -declare var element: any; -declare var by: any; -declare var By: any; -declare var DartObject: any; - // Used for Protractor mock module loader. declare namespace angular { var module: Function; @@ -35,7 +20,11 @@ declare namespace NodeJS { element: any; by: any; By: any; + ExpectedConditions: any; DartObject: any; + ActionSequence: any; + Command: any; + CommandName: any; // Helper function added by the debugger in protractor.ps list: (locator: webdriver.Locator) => string[]; [key: string]: any; @@ -45,16 +34,21 @@ declare namespace NodeJS { declare interface String { startsWith: Function; } declare namespace webdriver { + var error: any; class WebDriver { findElements: Function; getSession: Function; quit: Function; executeScript: Function; getCapabilities: Function; + getCurrentUrl: Function; + getPageSource: Function; + getTitle: Function; navigate: Function; get: Function; wait: Function; schedule: Function; + switchTo: Function; controlFlow: Function; static attachToSession: Function; // This index type allows looking up methods by name so we can do mixins. @@ -87,6 +81,18 @@ declare namespace webdriver { code: number; } + class By { + static css: (css: string) => webdriver.By; + static id: (id: string) => webdriver.By; + static linkText: (linkText: string) => webdriver.By; + static js: (js: string) => webdriver.By; + static name: (name: string) => webdriver.By; + static partialLinkText: (partialLinkText: string) => webdriver.By; + static tagName: (tagName: string) => webdriver.By; + static xpath: (xpath: string) => webdriver.By; + toString(): string; + } + interface Locator { toString(): string; isPresent?: Function; diff --git a/lib/ptor.ts b/lib/ptor.ts new file mode 100644 index 000000000..2a23c4212 --- /dev/null +++ b/lib/ptor.ts @@ -0,0 +1,40 @@ +import {Browser, ElementHelper} from './browser'; +import {ProtractorBy} from './locators'; +import {ElementFinder, ElementArrayFinder} from './element'; +import * as EC from './expectedConditions'; +import {Promise} from './selenium-webdriver/promise'; + +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; + + // TODO: Might need to fix imports + export let wrapDriver: Function; + export let ExpectedConditions = new 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 let Command = require('selenium-webdriver/lib/command').Command; + export let CommandName = require('selenium-webdriver/lib/command').Name; +} diff --git a/lib/runner.ts b/lib/runner.ts index ff3ff3d7d..b60f970b8 100644 --- a/lib/runner.ts +++ b/lib/runner.ts @@ -6,10 +6,15 @@ import {Config} from './configParser'; import {Logger} from './logger2'; import {AttachSession, BrowserStack, Direct, Hosted, Local, Mock, Sauce} from './driverProviders'; import {Plugins} from './plugins'; +import {Browser} from './browser'; +import * as browserFn from './browser'; -var protractor = require('./protractor'), - webdriver = require('selenium-webdriver'); +import {ProtractorBy} from './locators'; +import {DriverProvider} from './driverProviders'; +import {protractor} from './ptor'; +var webdriver = require('selenium-webdriver'); +let promsie = require('selenium-webdriver/lib/promise'); let logger = new Logger('runner'); /* * Runner is responsible for starting the execution of a test run and triggering @@ -26,7 +31,7 @@ let logger = new Logger('runner'); export class Runner extends EventEmitter { config_: Config; preparer_: any; - driverprovider_: any; + driverprovider_: DriverProvider; o: any; constructor(config: Config) { @@ -148,13 +153,16 @@ export class Runner extends EventEmitter { * Sets up convenience globals for test specs * @private */ - setupGlobals_(browser_: any) { + setupGlobals_(browser_: Browser) { // Keep $, $$, element, and by/By under the global protractor namespace protractor.browser = browser_; protractor.$ = browser_.$; protractor.$$ = browser_.$$; protractor.element = browser_.element; - protractor.by = protractor.By; + protractor.by = protractor.By = browserFn.By; + + // TODO: fix imports + protractor.wrapDriver = browserFn.wrapDriver; if (!this.config_.noGlobals) { // Export protractor to the global namespace to be used in tests. @@ -164,6 +172,7 @@ export class Runner extends EventEmitter { global.element = browser_.element; global.by = global.By = protractor.By; } + global.protractor = protractor; if (!this.config_.skipSourceMapSupport) { @@ -190,7 +199,7 @@ export class Runner extends EventEmitter { var config = this.config_; var driver = this.driverprovider_.getNewDriver(); - var browser_ = protractor.wrapDriver( + var browser_ = browserFn.wrapDriver( driver, config.baseUrl, config.rootElement, config.untrackOutstandingTimeouts); @@ -250,7 +259,7 @@ export class Runner extends EventEmitter { */ shutdown_(): q.Promise { return q.all(this.driverprovider_.getExistingDrivers().map( - this.driverprovider_.quitDriver.bind(this.driverprovider_))); + (webdriver) => { return this.driverprovider_.quitDriver(webdriver); })); } /** diff --git a/spec/basic/expected_conditions_spec.js b/spec/basic/expected_conditions_spec.js index 6c54e648a..6f4d57015 100644 --- a/spec/basic/expected_conditions_spec.js +++ b/spec/basic/expected_conditions_spec.js @@ -11,7 +11,7 @@ describe('expected conditions', function() { var alertButton = $('#alertbutton'); alertButton.click(); - browser.wait(protractor.ExpectedConditions.alertIsPresent(), 1000); + browser.wait(protractor.ExpectedConditions.alertIsPresent(), 1000); browser.switchTo().alert().accept(); });