Skip to content
This repository has been archived by the owner on Jul 29, 2024. It is now read-only.

feat(driverProvider): Add useExistingWebDriver driver provider #4756

Merged
merged 1 commit into from
Aug 16, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions docs/server-setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,3 +108,37 @@ 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: 8 additions & 0 deletions lib/config.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import {WebDriver} from 'selenium-webdriver';

import {PluginConfig} from './plugins';

export interface Config {
Expand Down Expand Up @@ -221,6 +223,12 @@ 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
8 changes: 8 additions & 0 deletions lib/driverProviders/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export * from './mock';
export * from './sauce';
export * from './testObject';
export * from './kobiton';
export * from './useExistingWebDriver';


import {AttachSession} from './attachSession';
Expand All @@ -20,6 +21,7 @@ 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';
Expand All @@ -32,6 +34,9 @@ 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);
Expand Down Expand Up @@ -109,6 +114,9 @@ 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(', '));
}
Expand Down
57 changes: 57 additions & 0 deletions lib/driverProviders/useExistingWebDriver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* 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';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for a simple question, but why you can not use async ... await construction instead of q library?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mostly copied the code from another driver provider, but I could redo my PR with async/await if that helps it getting merged. I don't exactly know what the house rules are for protractor code but I didn't see anything prohibiting q, fwiw.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The driver providers were written before Promise was available in Node.js.
Probably it's fine to use async/await for new driver provider, but I am not sure whether it will just work since some other folks tried to refactor them using regular promise, but it turns out not easy. Since all the other driver providers are using q promise, I am also ok with q promise for this one.

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<any> {
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<void> {
return wdpromise.when(undefined);
}
}
2 changes: 2 additions & 0 deletions scripts/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ 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',
Expand Down
22 changes: 22 additions & 0 deletions spec/driverProviderUseExistingWebDriver.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
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,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
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();
});
});