Skip to content

Commit

Permalink
chore(driverProviders): clean up driver provider q usage
Browse files Browse the repository at this point in the history
- 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.
- Use native promises over q promises.
  • Loading branch information
cnishina committed Nov 12, 2018
1 parent 02b3cb9 commit 12195c1
Show file tree
Hide file tree
Showing 17 changed files with 99 additions and 291 deletions.
34 changes: 0 additions & 34 deletions docs/server-setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
8 changes: 0 additions & 8 deletions lib/config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import {WebDriver} from 'selenium-webdriver';

import {PluginConfig} from './plugins';

export interface Config {
Expand Down Expand Up @@ -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 ---------------------------------------------------
// ---------------------------------------------------------------------------
Expand Down
11 changes: 5 additions & 6 deletions lib/driverProviders/attachSession.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -25,10 +24,10 @@ export class AttachSession extends DriverProvider {
* @return {q.promise} A promise which will resolve when the environment is
* ready to test.
*/
protected setupDriverEnv(): q.Promise<any> {
protected async setupDriverEnv(): Promise<any> {
logger.info('Using the selenium server at ' + this.config_.seleniumAddress);
logger.info('Using session id - ' + this.config_.seleniumSessionId);
return q(undefined);
return Promise.resolve();
}

/**
Expand All @@ -50,7 +49,7 @@ export class AttachSession extends DriverProvider {
*
* @public
*/
quitDriver(): wdpromise.Promise<void> {
return wdpromise.when(undefined);
quitDriver(): Promise<void> {
return Promise.resolve();
}
}
87 changes: 41 additions & 46 deletions lib/driverProviders/browserStack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -29,59 +27,57 @@ 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<any> {
let deferredArray = this.drivers_.map((driver: WebDriver) => {
let deferred = q.defer();
async updateJob(update: any): Promise<any> {
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) {
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');
}
}
});
// 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');
}
}
});

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();
}
});
// 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 deferred.promise;
});
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<any> {
let deferred = q.defer();
protected async setupDriverEnv(): Promise<any> {
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';
Expand All @@ -99,8 +95,7 @@ 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}`);
return Promise.resolve();
}
}
5 changes: 2 additions & 3 deletions lib/driverProviders/direct.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -29,7 +28,7 @@ export class Direct extends DriverProvider {
* @return {q.promise} A promise which will resolve when the environment is
* ready to test.
*/
protected setupDriverEnv(): q.Promise<any> {
protected async setupDriverEnv(): Promise<any> {
switch (this.config_.capabilities.browserName) {
case 'chrome':
logger.info('Using ChromeDriver directly...');
Expand All @@ -43,7 +42,7 @@ export class Direct extends DriverProvider {
'browserName ' + this.config_.capabilities.browserName +
' is not supported with directConnect.');
}
return q.fcall(function() {});
return Promise.resolve(function() {});
}

/**
Expand Down
56 changes: 22 additions & 34 deletions lib/driverProviders/driverProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -68,22 +67,20 @@ export abstract class DriverProvider {
* @public
* @param webdriver instance
*/
quitDriver(driver: WebDriver): wdpromise.Promise<void> {
async quitDriver(driver: WebDriver): Promise<void> {
let driverIndex = this.drivers_.indexOf(driver);
if (driverIndex >= 0) {
this.drivers_.splice(driverIndex, 1);
}

if (driver.getSession() === undefined) {
return wdpromise.when(undefined);
return Promise.resolve(undefined);
} else {
return driver.getSession()
.then<void>((session_: Session) => {
if (session_) {
return driver.quit();
}
})
.catch<void>(function(err: Error) {});
return driver.getSession().then<void>((session_: Session) => {
if (session_) {
return driver.quit();
}
}).catch<void>(function(_: Error) {});
}
}

Expand All @@ -93,56 +90,47 @@ export abstract class DriverProvider {
*
* @param drivers {webdriver.WebDriver[]} The webdriver instances
*/
static quitDrivers(provider: DriverProvider, drivers: WebDriver[]): q.Promise<void> {
let deferred = q.defer<void>();
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<void> {
return await Promise.all(
drivers.map(async(driver: WebDriver) => {
await provider.quitDriver(driver);
})).then(() => {});
}

/**
* Default update job method.
* @return a promise
*/
updateJob(update: any): q.Promise<any> {
return q.fcall(function() {});
updateJob(update: any): Promise<any> {
return Promise.resolve(() => {});
};

/**
* Default setup environment method, common to all driver providers.
*/
setupEnv(): q.Promise<any> {
async setupEnv(): Promise<any> {
let driverPromise = 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 driverPromise.then(() => this.bpRunner.start());
}
return driverPromise;
await driverPromise;
};

/**
* Set up environment specific to a particular driver provider. Overridden
* by each driver provider.
*/
protected abstract setupDriverEnv(): q.Promise<any>;
protected async abstract setupDriverEnv(): Promise<any>;

/**
* Teardown and destroy the environment and do any associated cleanup.
* Shuts down the drivers.
*
* @public
* @return {q.Promise<any>} A promise which will resolve when the environment is down.
* @return {Promise<any>} A promise which will resolve when the environment is down.
*/
teardownEnv(): q.Promise<any> {
return DriverProvider.quitDrivers(this, this.drivers_);
async teardownEnv(): Promise<any> {
await DriverProvider.quitDrivers(this, this.drivers_);
}
}
8 changes: 3 additions & 5 deletions lib/driverProviders/hosted.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -19,11 +17,11 @@ 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<any> {
protected setupDriverEnv(): Promise<any> {
logger.info('Using the selenium server at ' + this.config_.seleniumAddress);
return q.fcall(function() {});
return Promise.resolve(function() {});
}
}
Loading

0 comments on commit 12195c1

Please sign in to comment.