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

Support spawning additional browser instances #1537

Closed
wants to merge 1 commit into from
Closed
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
25 changes: 18 additions & 7 deletions lib/driverProviders/direct.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,12 @@ DirectDriverProvider.prototype.teardownEnv = function() {
};

/**
* Retrieve the webdriver for the runner.
* Retrieve a new instance of this webdriver.
* @public
* @return webdriver instance
*/
DirectDriverProvider.prototype.getDriver = function() {
if (this.driver_) {
return this.driver_;
}
DirectDriverProvider.prototype.newDriverInstance = function() {
var driver = null;
switch (this.config_.capabilities.browserName) {
case 'chrome':
var chromeDriverFile = this.config_.chromeDriver ||
Expand All @@ -78,19 +76,32 @@ DirectDriverProvider.prototype.getDriver = function() {
}

var service = new chrome.ServiceBuilder(chromeDriverFile).build();
this.driver_ = chrome.createDriver(
driver = chrome.createDriver(
new webdriver.Capabilities(this.config_.capabilities), service);
break;
case 'firefox':
if (this.config_.firefoxPath) {
this.config_.capabilities.firefox_binary = this.config_.firefoxPath;
}
this.driver_ = new firefox.Driver(this.config_.capabilities);
driver = new firefox.Driver(this.config_.capabilities);
break;
default:
throw new Error('browserName ' + this.config_.capabilities.browserName +
'is not supported with directConnect.');
}
return driver;
};

/**
* Retrieve the webdriver for the runner.
* @public
* @return webdriver instance
*/
DirectDriverProvider.prototype.getDriver = function() {
if (this.driver_) {
return this.driver_;
}
this.driver_ = this.newDriverInstance();
return this.driver_;
};

Expand Down
103 changes: 100 additions & 3 deletions lib/protractor.js
Original file line number Diff line number Diff line change
Expand Up @@ -991,12 +991,13 @@ var buildElementHelper = function(ptor) {
* @alias browser
* @constructor
* @extends {webdriver.WebDriver}
* @param {Runner} runner
* @param {webdriver.WebDriver} webdriver
* @param {string=} opt_baseUrl A base URL to run get requests against.
* @param {string=} opt_rootElement Selector element that has an ng-app in
* scope.
*/
var Protractor = function(webdriverInstance, opt_baseUrl, opt_rootElement) {
var Protractor = function(runner, webdriverInstance, opt_baseUrl, opt_rootElement) {
// 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.
Expand All @@ -1022,6 +1023,14 @@ var Protractor = function(webdriverInstance, opt_baseUrl, opt_rootElement) {
*/
this.driver = webdriverInstance;

/**
* The runner that created this Protractor.
* Use this to spawn new instances of the underlying driver.
*
* @type {Runner}
*/
this.runner = runner;

/**
* Helper function for finding elements.
*
Expand Down Expand Up @@ -1104,9 +1113,96 @@ var Protractor = function(webdriverInstance, opt_baseUrl, opt_rootElement) {
*/
this.mockModules_ = [];

/**
* Spawned child instances of this instance.
*
* @type {Array<{Protractor}>}
*/
this.instances_ = [];

/**
* Reference to the Protractor instance that spawned us.
*
* @type {Protractor}
*/
this.parentInstance = null;

this.addBaseMockModules_();
};

/**
* Returns a browser object for a new instance of the underlying driver,
* allowing scripts to test cross-window operations.
* Throws an error if the underlying driver does not support multiple
* instances.
*
* @public
* @param {string} url The url to navigate the new browser to. Defaults to the
* current url of the browser instance newInstance() is
* called on if url is falsy. If url === '', the default
* Protractor browser URL will be used.
* @return {Protractor}
*/
Protractor.prototype.newInstance = function(url) {
if (!('newDriverInstance' in this.runner.driverprovider_)) {
throw 'Protractor cannot spawn another browser instance; '
+ 'not supported by underlying driver';
}

var driver = this.runner.driverprovider_.newDriverInstance();
// lib/runner.js, Runner.prototype.run setup, step 2
driver.manage().timeouts().setScriptTimeout(this.runner.config_.allScriptsTimeout);
var browser = new Protractor(this.runner, driver, this.baseUrl, this.rootEl);
browser.parentInstance = this;

if (url !== '') {
url = url || this.executeScript_('return window.location.href;');
browser.get(url);
}

this.instances_.push(browser);
return browser;
};

/**
* Closes this browser instance, its driver, and any child instances.
* Note that it is *your* responsibility to quit all child instances, as this
* will not be done by Protractor automatically.
* Throws an exception if trying to quite the root browser instance.
*
* @public
* @return {!webdriver.promise.Promise.<T>} A promise that will resolve when
* all related browser instances have
* exited.
*/
Protractor.prototype.quit = function() {
if (this === global.browser) {
throw 'Tried to quit root browser instance.';
}

var qpromises = [];
var self = this;

// Notify our parent that we've exited
qpromises.push(this.driver.getSession().then(function(session_) {
if (session_) {
return self.driver.quit();
}
}));
var i = this.parentInstance.instances_.indexOf(this);
if (i >= 0) {
this.parentInstance.instances_.splice(i, 1);
}

// Make our children exit
this.instances_.forEach(function(i) {
qpromises.push(i.quit());
});
this.instances_ = [];

return webdriver.promise.all(qpromises);
};

/**
* The same as {@code webdriver.WebDriver.prototype.executeScript},
* but with a customized description for debugging.
Expand Down Expand Up @@ -1543,12 +1639,13 @@ Protractor.prototype.pause = function(opt_debugPort) {
/**
* Create a new instance of Protractor by wrapping a webdriver instance.
*
* @param {Runner} runner The Runner that created this Protractor.
* @param {webdriver.WebDriver} webdriver The configured webdriver instance.
* @param {string=} opt_baseUrl A URL to prepend to relative gets.
* @return {Protractor}
*/
exports.wrapDriver = function(webdriver, opt_baseUrl, opt_rootElement) {
return new Protractor(webdriver, opt_baseUrl, opt_rootElement);
exports.wrapDriver = function(runner, webdriver, opt_baseUrl, opt_rootElement) {
return new Protractor(runner, webdriver, opt_baseUrl, opt_rootElement);
};

/**
Expand Down
1 change: 1 addition & 0 deletions lib/runner.js
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ Runner.prototype.controlFlow = function() {
*/
Runner.prototype.setupGlobals_ = function(driver) {
var browser = protractor.wrapDriver(
this,
driver,
this.config_.baseUrl,
this.config_.rootElement);
Expand Down