From 149a2acb123a047919ad6203afbb63d61c7f9a1a Mon Sep 17 00:00:00 2001 From: Craig Nishina Date: Thu, 15 Nov 2018 12:06:30 -0800 Subject: [PATCH] chore(promises): clean up driver providers and browser control flow (#5034) Driver providers and tests: - Use native promises over q promises in driver providers - Remove driverProviderUseExistingWebDriver since the generation of the selenium server is already accomplished when providing a selenium address in driverProvider.ts. Also clean up docs and tests. - Enabled the driverProviderLocal tests - Clean up JSDocs for q.promise Basic lib spec: - Remove auto unwrap test for a WebElement. Reference PR #3471 Browser: - Remove control flow from waitForAngularEnabled, waitForAngular, and angularAppRoot in the Browser class. --- docs/server-setup.md | 34 --- lib/browser.ts | 240 ++++++++---------- lib/config.ts | 8 - lib/driverProviders/attachSession.ts | 12 +- lib/driverProviders/browserStack.ts | 77 +++--- lib/driverProviders/direct.ts | 6 +- lib/driverProviders/driverProvider.ts | 63 ++--- lib/driverProviders/hosted.ts | 7 +- lib/driverProviders/index.ts | 8 - lib/driverProviders/kobiton.ts | 8 +- lib/driverProviders/local.ts | 39 +-- lib/driverProviders/mock.ts | 13 +- lib/driverProviders/sauce.ts | 18 +- lib/driverProviders/testObject.ts | 8 +- lib/driverProviders/useExistingWebDriver.ts | 57 ----- lib/runner.ts | 4 +- package.json | 1 + scripts/test.js | 2 - spec/basic/lib_spec.js | 6 - spec/driverProviderUseExistingWebDriver.js | 22 -- .../useExistingDriver_spec.js | 16 -- spec/interaction/interaction_spec.js | 3 - 22 files changed, 186 insertions(+), 466 deletions(-) delete mode 100644 lib/driverProviders/useExistingWebDriver.ts delete mode 100644 spec/driverProviderUseExistingWebDriver.js delete mode 100644 spec/driverProviders/useExistingWebDriver/useExistingDriver_spec.js diff --git a/docs/server-setup.md b/docs/server-setup.md index aa15e0e26..296722d2f 100644 --- a/docs/server-setup.md +++ b/docs/server-setup.md @@ -108,37 +108,3 @@ Protractor can test directly against Chrome and Firefox without using a Selenium - `directConnect: true` - Your test script communicates directly Chrome Driver or Firefox Driver, bypassing any Selenium Server. If this is true, settings for `seleniumAddress` and `seleniumServerJar` will be ignored. If you attempt to use a browser other than Chrome or Firefox an error will be thrown. The advantage of directly connecting to browser drivers is that your test scripts may start up and run faster. - -Re-using an Existing WebDriver ------------------------------- - -The use case for re-using an existing WebDriver is when you have existing -`selenium-webdriver` code and are already in control of how the WebDriver is -created, but would also like Protractor to use the same browser, so you can -use protractor's element locators and the rest of its API. This could be -done with the `attachSession` driver provider, but the `attachSession` API is -being removed in `selenium-webdriver` 4.0.0. - -Instead of a protractor config file, you create a config object in your test -setup code, and add your already-created WebDriver object and base URL. - -```javascript -const ProtractorConfigParser = require('protractor/built/configParser').ConfigParser; -const ProtractorRunner = require('protractor/built/runner').Runner; - -const ptorConfig = new ProtractorConfigParser().config_; -ptorConfig.baseUrl = myExistingBaseUrl; -ptorConfig.seleniumWebDriver = myExistingWebDriver; -ptorConfig.noGlobals = true; // local preference - -// looks similar to protractor/built/runner.js run() -const ptorRunner = new ProtractorRunner(ptorConfig); -ptorRunner.driverProvider_.setupEnv(); -const browser = ptorRunner.createBrowser(); -ptorRunner.setupGlobals_(browser); // now you can access protractor.$, etc. -``` - -Note that this driver provider leaves you in control of quitting the driver, -but that also means Protractor API calls that expect the driver to properly -quit and/or restart the browser, e.g. `restart`, `restartSync`, and -`forkNewDriverInstance`, will not behave as documented. diff --git a/lib/browser.ts b/lib/browser.ts index 6e98c21a3..5a6967a2c 100644 --- a/lib/browser.ts +++ b/lib/browser.ts @@ -193,24 +193,18 @@ export class ProtractorBrowser extends AbstractExtendedWebDriver { * this method is called use the new app root. Pass nothing to get a promise that * resolves to the value of the selector. * - * @param {string|webdriver.promise.Promise} value The new selector. + * @param {string|webdriver.promise.Promise} valuePromise The new selector. * @returns A promise that resolves with the value of the selector. */ - angularAppRoot(value: string|wdpromise.Promise = null): wdpromise.Promise { - return this.driver.controlFlow().execute(() => { - if (value != null) { - return wdpromise.when(value).then((value: string) => { - this.internalRootEl = value; - if (this.bpClient) { - const bpCommandPromise = this.bpClient.setWaitParams(value); - // Convert to webdriver promise as best as possible - return wdpromise.when(bpCommandPromise as any).then(() => this.internalRootEl); - } - return this.internalRootEl; - }); + async angularAppRoot(valuePromise: string|wdpromise.Promise = null): Promise { + if (valuePromise != null) { + const value = await valuePromise; + this.internalRootEl = value; + if (this.bpClient) { + await this.bpClient.setWaitParams(value); } - return wdpromise.when(this.internalRootEl); - }, `Set angular root selector to ${value}`); + } + return this.internalRootEl; } /** @@ -417,23 +411,17 @@ export class ProtractorBrowser extends AbstractExtendedWebDriver { * Call waitForAngularEnabled() without passing a value to read the current * state without changing it. */ - waitForAngularEnabled(enabled: boolean|wdpromise.Promise = null): - wdpromise.Promise { - if (enabled != null) { - const ret = this.driver.controlFlow().execute(() => { - return wdpromise.when(enabled).then((enabled: boolean) => { - if (this.bpClient) { - logger.debug('Setting waitForAngular' + !enabled); - const bpCommandPromise = this.bpClient.setWaitEnabled(enabled); - // Convert to webdriver promise as best as possible - return wdpromise.when(bpCommandPromise as any).then(() => enabled); - } - }); - }, `Set proxy synchronization enabled to ${enabled}`); + async waitForAngularEnabled(enabledPromise: boolean|wdpromise.Promise = null): + Promise { + if (enabledPromise != null) { + const enabled = await enabledPromise; + if (this.bpClient) { + logger.debug('Setting waitForAngular' + !enabled); + await this.bpClient.setWaitEnabled(enabled); + } this.internalIgnoreSynchronization = !enabled; - return ret; } - return wdpromise.when(!this.ignoreSynchronization); + return !this.ignoreSynchronization; } /** @@ -602,15 +590,15 @@ export class ProtractorBrowser extends AbstractExtendedWebDriver { * @template T */ private executeAsyncScript_(script: string|Function, description: string, ...scriptArgs: any[]): - wdpromise.Promise { + Promise { if (typeof script === 'function') { script = 'return (' + script + ').apply(null, arguments);'; } return this.driver.schedule( - new Command(CommandName.EXECUTE_ASYNC_SCRIPT) - .setParameter('script', script) - .setParameter('args', scriptArgs), - description); + new Command(CommandName.EXECUTE_ASYNC_SCRIPT) + .setParameter('script', script) + .setParameter('args', scriptArgs), + description) as Promise; } /** @@ -624,116 +612,90 @@ export class ProtractorBrowser extends AbstractExtendedWebDriver { * @returns {!webdriver.promise.Promise} A promise that will resolve to the * scripts return value. */ - waitForAngular(opt_description?: string): wdpromise.Promise { + async waitForAngular(opt_description?: string): Promise { let description = opt_description ? ' - ' + opt_description : ''; if (this.ignoreSynchronization) { - return this.driver.controlFlow().execute(() => { - return true; - }, 'Ignore Synchronization Protractor.waitForAngular()'); + return true; } - let runWaitForAngularScript: () => wdpromise.Promise = () => { + let runWaitForAngularScript = async(): Promise => { if (this.plugins_.skipAngularStability() || this.bpClient) { - return this.driver.controlFlow().execute(() => { - return wdpromise.when(null); - }, 'bpClient or plugin stability override'); + return null; } else { - // Need to wrap this so that we read rootEl in the control flow, not synchronously. - return this.angularAppRoot().then((rootEl: string) => { - return this.executeAsyncScript_( - clientSideScripts.waitForAngular, 'Protractor.waitForAngular()' + description, - rootEl); - }); + let rootEl = await this.angularAppRoot(); + return this.executeAsyncScript_( + clientSideScripts.waitForAngular, `Protractor.waitForAngular() ${description}`, rootEl); } }; - return runWaitForAngularScript() - .then((browserErr: Function) => { - if (browserErr) { - throw new Error( - 'Error while waiting for Protractor to ' + - 'sync with the page: ' + JSON.stringify(browserErr)); + try { + let browserErr = await runWaitForAngularScript(); + if (browserErr) { + throw new Error( + 'Error while waiting for Protractor to ' + + 'sync with the page: ' + JSON.stringify(browserErr)); + } + await this.plugins_.waitForPromise(this); + + await this.driver.wait(async () => { + let results = await this.plugins_.waitForCondition(this); + return results.reduce((x, y) => x && y, true); + }, this.allScriptsTimeout, 'Plugins.waitForCondition()'); + } catch (err) { + let timeout: RegExpExecArray; + if (/asynchronous script timeout/.test(err.message)) { + // Timeout on Chrome + timeout = /-?[\d\.]*\ seconds/.exec(err.message); + } else if (/Timed out waiting for async script/.test(err.message)) { + // Timeout on Firefox + timeout = /-?[\d\.]*ms/.exec(err.message); + } else if (/Timed out waiting for an asynchronous script/.test(err.message)) { + // Timeout on Safari + timeout = /-?[\d\.]*\ ms/.exec(err.message); + } + if (timeout) { + let errMsg = `Timed out waiting for asynchronous Angular tasks to finish after ` + + `${timeout}. This may be because the current page is not an Angular ` + + `application. Please see the FAQ for more details: ` + + `https://github.com/angular/protractor/blob/master/docs/timeouts.md#waiting-for-angular`; + if (description.indexOf(' - Locator: ') == 0) { + errMsg += '\nWhile waiting for element with locator' + description; + } + let pendingTimeoutsPromise: wdpromise.Promise; + if (this.trackOutstandingTimeouts_) { + pendingTimeoutsPromise = this.executeScriptWithDescription( + 'return window.NG_PENDING_TIMEOUTS', + 'Protractor.waitForAngular() - getting pending timeouts' + description); + } else { + pendingTimeoutsPromise = wdpromise.when({}); + } + let pendingHttpsPromise = this.executeScriptWithDescription( + clientSideScripts.getPendingHttpRequests, + 'Protractor.waitForAngular() - getting pending https' + description, + this.internalRootEl); + + let arr = await Promise.all([pendingTimeoutsPromise, pendingHttpsPromise]); + + let pendingTimeouts = arr[0] || []; + let pendingHttps = arr[1] || []; + + let key: string, pendingTasks: string[] = []; + for (key in pendingTimeouts) { + if (pendingTimeouts.hasOwnProperty(key)) { + pendingTasks.push(' - $timeout: ' + pendingTimeouts[key]); } - }) - .then( - () => { - return this.driver.controlFlow() - .execute( - () => { - return this.plugins_.waitForPromise(this); - }, - 'Plugins.waitForPromise()') - .then(() => { - return this.driver.wait(() => { - return this.plugins_.waitForCondition(this).then((results: boolean[]) => { - return results.reduce((x, y) => x && y, true); - }); - }, this.allScriptsTimeout, 'Plugins.waitForCondition()'); - }); - }, - (err: Error) => { - let timeout: RegExpExecArray; - if (/asynchronous script timeout/.test(err.message)) { - // Timeout on Chrome - timeout = /-?[\d\.]*\ seconds/.exec(err.message); - } else if (/Timed out waiting for async script/.test(err.message)) { - // Timeout on Firefox - timeout = /-?[\d\.]*ms/.exec(err.message); - } else if (/Timed out waiting for an asynchronous script/.test(err.message)) { - // Timeout on Safari - timeout = /-?[\d\.]*\ ms/.exec(err.message); - } - if (timeout) { - let errMsg = `Timed out waiting for asynchronous Angular tasks to finish after ` + - `${timeout}. This may be because the current page is not an Angular ` + - `application. Please see the FAQ for more details: ` + - `https://github.com/angular/protractor/blob/master/docs/timeouts.md#waiting-for-angular`; - if (description.indexOf(' - Locator: ') == 0) { - errMsg += '\nWhile waiting for element with locator' + description; - } - let pendingTimeoutsPromise: wdpromise.Promise; - if (this.trackOutstandingTimeouts_) { - pendingTimeoutsPromise = this.executeScriptWithDescription( - 'return window.NG_PENDING_TIMEOUTS', - 'Protractor.waitForAngular() - getting pending timeouts' + description); - } else { - pendingTimeoutsPromise = wdpromise.when({}); - } - let pendingHttpsPromise = this.executeScriptWithDescription( - clientSideScripts.getPendingHttpRequests, - 'Protractor.waitForAngular() - getting pending https' + description, - this.internalRootEl); - - return wdpromise.all([pendingTimeoutsPromise, pendingHttpsPromise]) - .then( - (arr: any[]) => { - let pendingTimeouts = arr[0] || []; - let pendingHttps = arr[1] || []; - - let key: string, pendingTasks: string[] = []; - for (key in pendingTimeouts) { - if (pendingTimeouts.hasOwnProperty(key)) { - pendingTasks.push(' - $timeout: ' + pendingTimeouts[key]); - } - } - for (key in pendingHttps) { - pendingTasks.push(' - $http: ' + pendingHttps[key].url); - } - if (pendingTasks.length) { - errMsg += '. \nThe following tasks were pending:\n'; - errMsg += pendingTasks.join('\n'); - } - err.message = errMsg; - throw err; - }, - () => { - err.message = errMsg; - throw err; - }); - } else { - throw err; - } - }); + } + for (key in pendingHttps) { + pendingTasks.push(' - $http: ' + pendingHttps[key].url); + } + if (pendingTasks.length) { + errMsg += '. \nThe following tasks were pending:\n'; + errMsg += pendingTasks.join('\n'); + } + err.message = errMsg; + } + throw err; + } } /** @@ -978,16 +940,14 @@ export class ProtractorBrowser extends AbstractExtendedWebDriver { .then(() => { // Reset bpClient sync if (this.bpClient) { - return this.driver.controlFlow().execute(() => { - return this.bpClient.setWaitEnabled(!this.internalIgnoreSynchronization); - }); + return this.bpClient.setWaitEnabled(!this.internalIgnoreSynchronization); } }) .then(() => { // Run Plugins - return this.driver.controlFlow().execute(() => { + if (!this.ignoreSynchronization) { return this.plugins_.onPageStable(this); - }); + } }) .then(() => null); } diff --git a/lib/config.ts b/lib/config.ts index 743ce2f3f..6295a3c0a 100644 --- a/lib/config.ts +++ b/lib/config.ts @@ -1,5 +1,3 @@ -import {WebDriver} from 'selenium-webdriver'; - import {PluginConfig} from './plugins'; export interface Config { @@ -230,12 +228,6 @@ export interface Config { */ firefoxPath?: string; - // ---- 8. To re-use an existing WebDriver object --------------------------- - - // This would not appear in a configuration file. Instead a configuration - // object would be created that includes an existing webdriver. - seleniumWebDriver?: WebDriver; - // --------------------------------------------------------------------------- // ----- What tests to run --------------------------------------------------- // --------------------------------------------------------------------------- diff --git a/lib/driverProviders/attachSession.ts b/lib/driverProviders/attachSession.ts index dd342a77e..37f6fe0a2 100644 --- a/lib/driverProviders/attachSession.ts +++ b/lib/driverProviders/attachSession.ts @@ -3,8 +3,7 @@ * It is responsible for setting up the account object, tearing * it down, and setting up the driver correctly. */ -import * as q from 'q'; -import {promise as wdpromise, WebDriver} from 'selenium-webdriver'; +import {WebDriver} from 'selenium-webdriver'; import {Config} from '../config'; import {Logger} from '../logger'; @@ -22,13 +21,12 @@ export class AttachSession extends DriverProvider { /** * Configure and launch (if applicable) the object's environment. - * @return {q.promise} A promise which will resolve when the environment is + * @return {Promise} A promise which will resolve when the environment is * ready to test. */ - protected setupDriverEnv(): q.Promise { + protected async setupDriverEnv(): Promise { logger.info('Using the selenium server at ' + this.config_.seleniumAddress); logger.info('Using session id - ' + this.config_.seleniumSessionId); - return q(undefined); } /** @@ -50,7 +48,5 @@ export class AttachSession extends DriverProvider { * * @public */ - quitDriver(): wdpromise.Promise { - return wdpromise.when(undefined); - } + async quitDriver(): Promise {} } diff --git a/lib/driverProviders/browserStack.ts b/lib/driverProviders/browserStack.ts index 0994df7ba..b1cf97910 100644 --- a/lib/driverProviders/browserStack.ts +++ b/lib/driverProviders/browserStack.ts @@ -3,8 +3,6 @@ * It is responsible for setting up the account object, tearing * it down, and setting up the driver correctly. */ -import * as https from 'https'; -import * as q from 'q'; import {Session, WebDriver} from 'selenium-webdriver'; import * as util from 'util'; @@ -29,59 +27,54 @@ export class BrowserStack extends DriverProvider { * Hook to update the BrowserStack job status. * @public * @param {Object} update - * @return {q.promise} A promise that will resolve when the update is complete. + * @return {Promise} A promise that will resolve when the update is complete. */ - updateJob(update: any): q.Promise { - let deferredArray = this.drivers_.map((driver: WebDriver) => { - let deferred = q.defer(); + async updateJob(update: any): Promise { + let mappedDrivers = this.drivers_.map(async (driver: WebDriver) => { + let session = await driver.getSession(); - driver.getSession().then((session: Session) => { - - // Fetching BrowserStack session details. - this.browserstackClient.getSession( - session.getId(), function(error: Error, automate_session: any) { - if (error) { + // Fetching BrowserStack session details. + this.browserstackClient.getSession( + session.getId(), function(error: Error, automate_session: any) { + if (error) { + logger.info( + 'BrowserStack results available at ' + + 'https://www.browserstack.com/automate'); + } else { + if (automate_session && automate_session.browser_url) { + logger.info('BrowserStack results available at ' + automate_session.browser_url); + } else { logger.info( 'BrowserStack results available at ' + 'https://www.browserstack.com/automate'); - } else { - if (automate_session && automate_session.browser_url) { - logger.info('BrowserStack results available at ' + automate_session.browser_url); - } else { - logger.info( - 'BrowserStack results available at ' + - 'https://www.browserstack.com/automate'); - } } - }); + } + }); - let jobStatus = update.passed ? 'completed' : 'error'; - let statusObj = {status: jobStatus}; + let jobStatus = update.passed ? 'completed' : 'error'; + let statusObj = {status: jobStatus}; - // Updating status of BrowserStack session. - this.browserstackClient.updateSession( - session.getId(), statusObj, function(error: Error, automate_session: any) { - if (error) { - throw new BrowserError( - logger, 'Error updating BrowserStack pass/fail status: ' + util.inspect(error)); - } else { - logger.info(automate_session); - deferred.resolve(); - } - }); - }); - return deferred.promise; + // Updating status of BrowserStack session. + this.browserstackClient.updateSession( + session.getId(), statusObj, function(error: Error, automate_session: any) { + if (error) { + throw new BrowserError( + logger, 'Error updating BrowserStack pass/fail status: ' + util.inspect(error)); + } else { + logger.info(automate_session); + } + }); }); - return q.all(deferredArray); + + return Promise.all(mappedDrivers); } /** * Configure and launch (if applicable) the object's environment. - * @return {q.promise} A promise which will resolve when the environment is + * @return {promise} A promise which will resolve when the environment is * ready to test. */ - protected setupDriverEnv(): q.Promise { - let deferred = q.defer(); + protected async setupDriverEnv(): Promise { this.config_.capabilities['browserstack.user'] = this.config_.browserstackUser; this.config_.capabilities['browserstack.key'] = this.config_.browserstackKey; this.config_.seleniumAddress = 'http://hub.browserstack.com/wd/hub'; @@ -99,8 +92,6 @@ export class BrowserStack extends DriverProvider { (':' + this.config_.specs.toString().replace(/^.*[\\\/]/, '')); } - logger.info('Using BrowserStack selenium server at ' + this.config_.seleniumAddress); - deferred.resolve(); - return deferred.promise; + logger.info(`Using BrowserStack selenium server at ${this.config_.seleniumAddress}`); } } diff --git a/lib/driverProviders/direct.ts b/lib/driverProviders/direct.ts index 71f0d49e9..e7caf929e 100644 --- a/lib/driverProviders/direct.ts +++ b/lib/driverProviders/direct.ts @@ -5,7 +5,6 @@ */ import * as fs from 'fs'; import * as path from 'path'; -import * as q from 'q'; import {Capabilities, WebDriver} from 'selenium-webdriver'; import {Driver as ChromeDriver, ServiceBuilder as ChromeServiceBuilder} from 'selenium-webdriver/chrome'; import {Driver as FirefoxDriver} from 'selenium-webdriver/firefox'; @@ -26,10 +25,10 @@ export class Direct extends DriverProvider { /** * Configure and launch (if applicable) the object's environment. - * @return {q.promise} A promise which will resolve when the environment is + * @return {Promise} A promise which will resolve when the environment is * ready to test. */ - protected setupDriverEnv(): q.Promise { + protected async setupDriverEnv(): Promise { switch (this.config_.capabilities.browserName) { case 'chrome': logger.info('Using ChromeDriver directly...'); @@ -43,7 +42,6 @@ export class Direct extends DriverProvider { 'browserName ' + this.config_.capabilities.browserName + ' is not supported with directConnect.'); } - return q.fcall(function() {}); } /** diff --git a/lib/driverProviders/driverProvider.ts b/lib/driverProviders/driverProvider.ts index 35a7616d4..c95000628 100644 --- a/lib/driverProviders/driverProvider.ts +++ b/lib/driverProviders/driverProvider.ts @@ -3,8 +3,7 @@ * It is responsible for setting up the account object, tearing * it down, and setting up the driver correctly. */ -import * as q from 'q'; -import {Builder, promise as wdpromise, Session, WebDriver} from 'selenium-webdriver'; +import {Builder, Session, WebDriver} from 'selenium-webdriver'; import {BlockingProxyRunner} from '../bpRunner'; import {Config} from '../config'; @@ -68,81 +67,61 @@ export abstract class DriverProvider { * @public * @param webdriver instance */ - quitDriver(driver: WebDriver): wdpromise.Promise { + async quitDriver(driver: WebDriver): Promise { let driverIndex = this.drivers_.indexOf(driver); if (driverIndex >= 0) { this.drivers_.splice(driverIndex, 1); - } - - if (driver.getSession() === undefined) { - return wdpromise.when(undefined); - } else { - return driver.getSession() - .then((session_: Session) => { - if (session_) { - return driver.quit(); - } - }) - .catch(function(err: Error) {}); + try { + await driver.quit(); + } catch (err) { + // This happens when Protractor keeps track of all the webdrivers + // created and calls quit. If a user calls driver.quit, then this will + // throw an error. This catch will swallow the error. + } } } - /** * Quits an array of drivers and returns a q promise instead of a webdriver one * * @param drivers {webdriver.WebDriver[]} The webdriver instances */ - static quitDrivers(provider: DriverProvider, drivers: WebDriver[]): q.Promise { - let deferred = q.defer(); - wdpromise - .all(drivers.map((driver: WebDriver) => { - return provider.quitDriver(driver); - })) - .then( - () => { - deferred.resolve(); - }, - () => { - deferred.resolve(); - }); - return deferred.promise; + static async quitDrivers(provider: DriverProvider, drivers: WebDriver[]): Promise { + await Promise.all(drivers.map((driver: WebDriver) => { + return provider.quitDriver(driver); + })); } /** * Default update job method. * @return a promise */ - updateJob(update: any): q.Promise { - return q.fcall(function() {}); - }; + async updateJob(update: any): Promise{}; /** * Default setup environment method, common to all driver providers. */ - setupEnv(): q.Promise { - let driverPromise = this.setupDriverEnv(); + async setupEnv(): Promise { + await this.setupDriverEnv(); if (this.config_.useBlockingProxy && !this.config_.blockingProxyUrl) { - // TODO(heathkit): If set, pass the webDriverProxy to BP. - return driverPromise.then(() => this.bpRunner.start()); + await this.bpRunner.start(); } - return driverPromise; }; /** * Set up environment specific to a particular driver provider. Overridden * by each driver provider. */ - protected abstract setupDriverEnv(): q.Promise; + protected async abstract setupDriverEnv(): Promise; /** * Teardown and destroy the environment and do any associated cleanup. * Shuts down the drivers. * * @public - * @return {q.Promise} A promise which will resolve when the environment is down. + * @return {Promise} A promise which will resolve when the environment is down. */ - teardownEnv(): q.Promise { - return DriverProvider.quitDrivers(this, this.drivers_); + async teardownEnv(): Promise { + await DriverProvider.quitDrivers(this, this.drivers_); } } diff --git a/lib/driverProviders/hosted.ts b/lib/driverProviders/hosted.ts index f6778787a..22a3e6258 100644 --- a/lib/driverProviders/hosted.ts +++ b/lib/driverProviders/hosted.ts @@ -3,8 +3,6 @@ * It is responsible for setting up the account object, tearing * it down, and setting up the driver correctly. */ -import * as q from 'q'; - import {Config} from '../config'; import {Logger} from '../logger'; @@ -19,11 +17,10 @@ export class Hosted extends DriverProvider { /** * Configure and launch (if applicable) the object's environment. * @public - * @return {q.promise} A promise which will resolve when the environment is + * @return {Promise} A promise which will resolve when the environment is * ready to test. */ - protected setupDriverEnv(): q.Promise { + protected async setupDriverEnv(): Promise { logger.info('Using the selenium server at ' + this.config_.seleniumAddress); - return q.fcall(function() {}); } } diff --git a/lib/driverProviders/index.ts b/lib/driverProviders/index.ts index 87bc431a1..25fcabcf6 100644 --- a/lib/driverProviders/index.ts +++ b/lib/driverProviders/index.ts @@ -8,7 +8,6 @@ export * from './mock'; export * from './sauce'; export * from './testObject'; export * from './kobiton'; -export * from './useExistingWebDriver'; import {AttachSession} from './attachSession'; @@ -21,7 +20,6 @@ import {Mock} from './mock'; import {Sauce} from './sauce'; import {TestObject} from './testObject'; import {Kobiton} from './kobiton'; -import {UseExistingWebDriver} from './useExistingWebDriver'; import {Config} from '../config'; import {Logger} from '../logger'; @@ -34,9 +32,6 @@ export let buildDriverProvider = (config: Config): DriverProvider => { if (config.directConnect) { driverProvider = new Direct(config); logWarnings('directConnect', config); - } else if (config.seleniumWebDriver) { - driverProvider = new UseExistingWebDriver(config); - logWarnings('useExistingWebDriver', config); } else if (config.seleniumAddress) { if (config.seleniumSessionId) { driverProvider = new AttachSession(config); @@ -114,9 +109,6 @@ export let logWarnings = (providerType: string, config: Config): void => { if ('mock' !== providerType && config.mockSelenium) { warnList.push('mockSelenium'); } - if ('useExistingWebDriver' !== providerType && config.seleniumWebDriver) { - warnList.push('seleniumWebDriver'); - } if (warnList.length !== 0) { logger.warn(warnInto + warnList.join(', ')); } diff --git a/lib/driverProviders/kobiton.ts b/lib/driverProviders/kobiton.ts index 8bfc53ddc..1a7a6bbe9 100644 --- a/lib/driverProviders/kobiton.ts +++ b/lib/driverProviders/kobiton.ts @@ -3,7 +3,6 @@ * It is responsible for setting up the account object, tearing * it down, and setting up the driver correctly. */ -import * as q from 'q'; import {Config} from '../config'; import {Logger} from '../logger'; import {DriverProvider} from './driverProvider'; @@ -17,18 +16,15 @@ export class Kobiton extends DriverProvider { /** * Configure and launch (if applicable) the object's environment. - * @return {q.promise} A promise which will resolve when the environment is + * @return {Promise} A promise which will resolve when the environment is * ready to test. */ - protected setupDriverEnv(): q.Promise { - let deferred = q.defer(); + protected async setupDriverEnv(): Promise { this.config_.capabilities['kobitonUser'] = this.config_.kobitonUser; this.config_.capabilities['kobitonKey'] = this.config_.kobitonKey; this.config_.seleniumAddress = 'https://' + this.config_.kobitonUser + ':' + this.config_.kobitonKey + '@api.kobiton.com/wd/hub'; logger.info('Using Kobiton selenium server at ' + this.config_.seleniumAddress); - deferred.resolve(); - return deferred.promise; } } diff --git a/lib/driverProviders/local.ts b/lib/driverProviders/local.ts index d766e2780..8dabc7043 100644 --- a/lib/driverProviders/local.ts +++ b/lib/driverProviders/local.ts @@ -8,7 +8,6 @@ */ import * as fs from 'fs'; import * as path from 'path'; -import * as q from 'q'; import {Config} from '../config'; import {BrowserError, ConfigError} from '../exitCodes'; @@ -120,10 +119,10 @@ export class Local extends DriverProvider { /** * Configure and launch (if applicable) the object's environment. * @public - * @return {q.promise} A promise which will resolve when the environment is + * @return {Promise} A promise which will resolve when the environment is * ready to test. */ - setupDriverEnv(): q.Promise { + async setupDriverEnv(): Promise { this.addDefaultBinaryLocs_(); logger.info('Starting selenium standalone server...'); @@ -155,37 +154,11 @@ export class Local extends DriverProvider { this.server_ = new remote.SeleniumServer(this.config_.seleniumServerJar, serverConf); - let deferred = q.defer(); // start local server, grab hosted address, and resolve promise - this.server_.start(this.config_.seleniumServerStartTimeout) - .then((url: string) => { - logger.info('Selenium standalone server started at ' + url); - return this.server_.address(); - }) - .then((address: string) => { - this.config_.seleniumAddress = address; - deferred.resolve(); - }) - .catch((err: string) => { - deferred.reject(err); - }); - - return deferred.promise; - } + const url = await this.server_.start(this.config_.seleniumServerStartTimeout); - /** - * Teardown and destroy the environment and do any associated cleanup. - * Shuts down the drivers and server. - * - * @public - * @override - * @return {q.promise} A promise which will resolve when the environment - * is down. - */ - teardownEnv(): q.Promise { - return super.teardownEnv().then(() => { - logger.info('Shutting down selenium standalone server.'); - return this.server_.stop(); - }); + logger.info('Selenium standalone server started at ' + url); + const address = await this.server_.address(); + this.config_.seleniumAddress = address; } } diff --git a/lib/driverProviders/mock.ts b/lib/driverProviders/mock.ts index c5e3a130a..d48b257cd 100644 --- a/lib/driverProviders/mock.ts +++ b/lib/driverProviders/mock.ts @@ -3,7 +3,6 @@ * It returns a fake webdriver and never actually contacts a selenium * server. */ -import * as q from 'q'; import {Session, WebDriver} from 'selenium-webdriver'; import {Config} from '../config'; @@ -21,20 +20,16 @@ export class Mock extends DriverProvider { /** * An execute function that returns a promise with a test value. */ - execute(): q.Promise { - let deferred = q.defer(); - deferred.resolve({value: 'test_response'}); - return deferred.promise; + async execute(): Promise { + return {value: 'test_response'}; } /** * Configure and launch (if applicable) the object's environment. * @public - * @return {q.promise} A promise which will resolve immediately. + * @return {Promise} A promise which will resolve immediately. */ - protected setupDriverEnv(): q.Promise { - return q.fcall(function() {}); - } + protected async setupDriverEnv(): Promise {} /** * Create a new driver. diff --git a/lib/driverProviders/sauce.ts b/lib/driverProviders/sauce.ts index d4b860551..d6a99288c 100644 --- a/lib/driverProviders/sauce.ts +++ b/lib/driverProviders/sauce.ts @@ -27,33 +27,29 @@ export class Sauce extends DriverProvider { * Hook to update the sauce job. * @public * @param {Object} update - * @return {q.promise} A promise that will resolve when the update is complete. + * @return {Promise} A promise that will resolve when the update is complete. */ - updateJob(update: any): q.Promise { - let deferredArray = this.drivers_.map((driver: WebDriver) => { - let deferred = q.defer(); + updateJob(update: any): Promise { + let mappedDrivers = this.drivers_.map((driver: WebDriver) => { driver.getSession().then((session: Session) => { logger.info('SauceLabs results available at http://saucelabs.com/jobs/' + session.getId()); this.sauceServer_.updateJob(session.getId(), update, (err: Error) => { if (err) { throw new Error('Error updating Sauce pass/fail status: ' + util.inspect(err)); } - deferred.resolve(); }); }); - return deferred.promise; }); - return q.all(deferredArray); + return Promise.all(mappedDrivers); } /** * Configure and launch (if applicable) the object's environment. * @public - * @return {q.promise} A promise which will resolve when the environment is + * @return {Promise} A promise which will resolve when the environment is * ready to test. */ - protected setupDriverEnv(): q.Promise { - let deferred = q.defer(); + protected async setupDriverEnv(): Promise { this.sauceServer_ = new SauceLabs({ username: this.config_.sauceUser, password: this.config_.sauceKey, @@ -79,7 +75,5 @@ export class Sauce extends DriverProvider { logger.info( 'Using SauceLabs selenium server at ' + this.config_.seleniumAddress.replace(/\/\/.+@/, '//')); - deferred.resolve(); - return deferred.promise; } } diff --git a/lib/driverProviders/testObject.ts b/lib/driverProviders/testObject.ts index 9e0a4266f..d510e84c8 100644 --- a/lib/driverProviders/testObject.ts +++ b/lib/driverProviders/testObject.ts @@ -3,7 +3,6 @@ * It is responsible for setting up the account object, tearing * it down, and setting up the driver correctly. */ -import * as q from 'q'; import {Config} from '../config'; import {Logger} from '../logger'; import {DriverProvider} from './driverProvider'; @@ -17,17 +16,14 @@ export class TestObject extends DriverProvider { /** * Configure and launch (if applicable) the object's environment. - * @return {q.promise} A promise which will resolve when the environment is + * @return {Promise} A promise which will resolve when the environment is * ready to test. */ - protected setupDriverEnv(): q.Promise { - let deferred = q.defer(); + protected async setupDriverEnv(): Promise { this.config_.capabilities['testobject.user'] = this.config_.testobjectUser; this.config_.capabilities['testobject_api_key'] = this.config_.testobjectKey; this.config_.seleniumAddress = 'https://us1.appium.testobject.com/wd/hub'; logger.info('Using TestObject selenium server at ' + this.config_.seleniumAddress); - deferred.resolve(); - return deferred.promise; } } diff --git a/lib/driverProviders/useExistingWebDriver.ts b/lib/driverProviders/useExistingWebDriver.ts deleted file mode 100644 index 36b279455..000000000 --- a/lib/driverProviders/useExistingWebDriver.ts +++ /dev/null @@ -1,57 +0,0 @@ -/* - * This is an implementation of the Use Existing WebDriver Driver Provider. - * It is responsible for setting up the account object, tearing it down, and - * setting up the driver correctly. - */ -import * as q from 'q'; -import {promise as wdpromise, WebDriver} from 'selenium-webdriver'; - -import {Config} from '../config'; -import {Logger} from '../logger'; - -import {DriverProvider} from './driverProvider'; - -const http = require('selenium-webdriver/http'); - -let logger = new Logger('useExistingWebDriver'); - -export class UseExistingWebDriver extends DriverProvider { - constructor(config: Config) { - super(config); - } - - /** - * Configure and launch (if applicable) the object's environment. - * @return {q.promise} A promise which will resolve when the environment is - * ready to test. - */ - protected setupDriverEnv(): q.Promise { - const defer = q.defer(); - this.config_.seleniumWebDriver.getSession().then((session) => { - logger.info('Using session id - ' + session.getId()); - return defer.resolve(); - }); - return q(undefined); - } - - /** - * Getting a new driver by attaching an existing session. - * - * @public - * @return {WebDriver} webdriver instance - */ - getNewDriver(): WebDriver { - const newDriver = this.config_.seleniumWebDriver; - this.drivers_.push(newDriver); - return newDriver; - } - - /** - * Maintains the existing session and does not quit the driver. - * - * @public - */ - quitDriver(): wdpromise.Promise { - return wdpromise.when(undefined); - } -} diff --git a/lib/runner.ts b/lib/runner.ts index 6d36e7540..e5a4144e8 100644 --- a/lib/runner.ts +++ b/lib/runner.ts @@ -347,10 +347,10 @@ export class Runner extends EventEmitter { /** * Final cleanup on exiting the runner. * - * @return {q.Promise} A promise which resolves on finish. + * @return {Promise} A promise which resolves on finish. * @private */ - shutdown_(): q.Promise { + shutdown_(): Promise { return DriverProvider.quitDrivers( this.driverprovider_, this.driverprovider_.getExistingDrivers()); } diff --git a/package.json b/package.json index f70249601..1bcea3357 100644 --- a/package.json +++ b/package.json @@ -76,6 +76,7 @@ "pretest": "gulp pretest", "start": "cd testapp && npm start", "test": "node scripts/test.js", + "tsc": "tsc", "website": "cd website && npm start", "compile_to_es5": "gulp compile_to_es5" }, diff --git a/scripts/test.js b/scripts/test.js index f80742151..8fa8c7c65 100755 --- a/scripts/test.js +++ b/scripts/test.js @@ -43,8 +43,6 @@ var passingTests = [ 'node built/cli.js spec/built/noCFBasicConf.js --useBlockingProxy', 'node built/cli.js spec/built/noCFPluginConf.js', // //'node scripts/driverProviderAttachSession.js', - // 'node built/cli.js spec/driverProviderUseExistingWebDriver.js', - // 'node built/cli.js spec/driverProviderUseExistingWebDriver.js --useBlockingProxy', // 'node scripts/errorTest.js', // // Interactive Element Explorer tasks // 'node scripts/interactive_tests/interactive_test.js', diff --git a/spec/basic/lib_spec.js b/spec/basic/lib_spec.js index a55a41978..30cc63600 100644 --- a/spec/basic/lib_spec.js +++ b/spec/basic/lib_spec.js @@ -42,12 +42,6 @@ describe('protractor library', () => { expect(await browser.driver.getCurrentUrl()).toMatch('#/form'); }); - it('should unwrap WebElements', async() => { - await browser.get('index.html'); - const ptorEl = element(by.binding('greet')); - await browser.executeScript('', ptorEl); // Will crash if element isn't unwrapped - }); - it('should have access to the processed config block', async() => { let containsMatching = (arr, string) => { let contains = false; diff --git a/spec/driverProviderUseExistingWebDriver.js b/spec/driverProviderUseExistingWebDriver.js deleted file mode 100644 index 6bf045579..000000000 --- a/spec/driverProviderUseExistingWebDriver.js +++ /dev/null @@ -1,22 +0,0 @@ -var env = require('./environment'); -var webdriver = require('selenium-webdriver'); - -var existingDriver = new webdriver.Builder() - .usingServer(env.seleniumAddress) - .withCapabilities(env.capabilities) - .build(); - -exports.config = { - - framework: 'jasmine', - - specs: [ - 'driverProviders/useExistingWebDriver/*_spec.js' - ], - - capabilities: env.capabilities, - - baseUrl: env.baseUrl, - - seleniumWebDriver: existingDriver, -}; diff --git a/spec/driverProviders/useExistingWebDriver/useExistingDriver_spec.js b/spec/driverProviders/useExistingWebDriver/useExistingDriver_spec.js deleted file mode 100644 index a69bf939b..000000000 --- a/spec/driverProviders/useExistingWebDriver/useExistingDriver_spec.js +++ /dev/null @@ -1,16 +0,0 @@ -describe('uses existing webdriver', function() { - var URL = '/ng2/#/async'; - - beforeEach(function() { - browser.get(URL); - }); - it('should be able to use an existing session', function() { - var increment = $('#increment'); - expect(increment).toBeDefined(); - }); - // the driverProvider is set up to ignore the quitDriver() call; - // so we call quit() ourselves to tidy up when testing is done. - afterEach(function() { - browser.quit(); - }); -}); diff --git a/spec/interaction/interaction_spec.js b/spec/interaction/interaction_spec.js index e39b5d22d..dab0423c5 100644 --- a/spec/interaction/interaction_spec.js +++ b/spec/interaction/interaction_spec.js @@ -75,9 +75,6 @@ describe('Browser', () => { describe('Multiple browsers', () => { - - - let p0, p1; beforeEach(async() => {