Skip to content

Commit 098bedd

Browse files
committed
Make it easier to create WebDriver instances in custom flows for parallel
execution. Fixes issue 7470.
1 parent 9e30b6d commit 098bedd

File tree

7 files changed

+167
-16
lines changed

7 files changed

+167
-16
lines changed

javascript/node/selenium-webdriver/CHANGES.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
`getAlert` will be removed in `2.45.0`.
1111
* FIXED: 7563: Mocha integration no longer disables timeouts. Default Mocha
1212
timeouts apply (2000 ms) and may be changed using `this.timeout(ms)`.
13+
* FIXED: 7470: Make it easier to create WebDriver instances in custom flows for
14+
parallel execution.
1315

1416
## v2.42.1
1517

javascript/node/selenium-webdriver/builder.js

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,22 +25,24 @@ var goog = base.require('goog'),
2525

2626
/**
2727
* @param {!webdriver.Capabilities} capabilities The desired capabilities.
28+
* @param {webdriver.promise.ControlFlow} flow The control flow to use, or
29+
* {@code null} to use the currently active flow.
2830
* @return {webdriver.WebDriver} A new WebDriver instance or {@code null}
2931
* if the requested browser is not natively supported in Node.
3032
*/
31-
function createNativeDriver(capabilities) {
33+
function createNativeDriver(capabilities, flow) {
3234
switch (capabilities.get(Capability.BROWSER_NAME)) {
3335
case Browser.CHROME:
3436
// Requiring 'chrome' above would create a cycle:
3537
// index -> builder -> chrome -> index
3638
var chrome = require('./chrome');
37-
return chrome.createDriver(capabilities);
39+
return chrome.createDriver(capabilities, null, flow);
3840

3941
case Browser.PHANTOM_JS:
4042
// Requiring 'phantomjs' would create a cycle:
4143
// index -> builder -> phantomjs -> index
4244
var phantomjs = require('./phantomjs');
43-
return phantomjs.createDriver(capabilities);
45+
return phantomjs.createDriver(capabilities, flow);
4446

4547
default:
4648
return null;
@@ -56,6 +58,9 @@ function createNativeDriver(capabilities) {
5658
*/
5759
var Builder = function() {
5860
goog.base(this);
61+
62+
/** @private {webdriver.promise.ControlFlow} */
63+
this.flow_ = null;
5964
};
6065
goog.inherits(Builder, AbstractBuilder);
6166

@@ -84,6 +89,20 @@ Builder.prototype.setChromeOptions = function(options) {
8489
};
8590

8691

92+
/**
93+
* Sets the control flow that created drivers should execute actions in. If
94+
* the flow is never set, or is set to {@code null}, it will use the active
95+
* flow at the time {@link #build()} is called.
96+
* @param {webdriver.promise.ControlFlow} flow The control flow to use, or
97+
* {@code null} to
98+
* @return {!Builder} A self reference.
99+
*/
100+
Builder.prototype.setControlFlow = function(flow) {
101+
this.flow_ = flow;
102+
return this;
103+
};
104+
105+
87106
/**
88107
* @override
89108
*/
@@ -93,7 +112,7 @@ Builder.prototype.build = function() {
93112
// If a remote server wasn't specified, check for browsers we support
94113
// natively in node before falling back to using the java Selenium server.
95114
if (!url) {
96-
var driver = createNativeDriver(this.getCapabilities());
115+
var driver = createNativeDriver(this.getCapabilities(), this.flow_);
97116
if (driver) {
98117
return driver;
99118
}
@@ -103,7 +122,7 @@ Builder.prototype.build = function() {
103122
}
104123

105124
var executor = executors.createExecutor(url);
106-
return WebDriver.createSession(executor, this.getCapabilities());
125+
return WebDriver.createSession(executor, this.getCapabilities(), this.flow_);
107126
};
108127

109128

javascript/node/selenium-webdriver/chrome.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -444,9 +444,11 @@ Options.prototype.toJSON = function() {
444444
* @param {(webdriver.Capabilities|Options)=} opt_options The session options.
445445
* @param {remote.DriverService=} opt_service The session to use; will use
446446
* the {@link getDefaultService default service} by default.
447+
* @param {webdriver.promise.ControlFlow=} opt_flow The control flow to use, or
448+
* {@code null} to use the currently active flow.
447449
* @return {!webdriver.WebDriver} A new WebDriver instance.
448450
*/
449-
function createDriver(opt_options, opt_service) {
451+
function createDriver(opt_options, opt_service, opt_flow) {
450452
var service = opt_service || getDefaultService();
451453
var executor = executors.createExecutor(service.start());
452454

@@ -458,7 +460,7 @@ function createDriver(opt_options, opt_service) {
458460
}
459461

460462
return webdriver.WebDriver.createSession(
461-
executor, options.toCapabilities());
463+
executor, options.toCapabilities(), opt_flow);
462464
}
463465

464466

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// Copyright 2014 Selenium committers
2+
// Copyright 2014 Software Freedom Conservancy
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
16+
/**
17+
* @fileoverview An example of starting multiple WebDriver clients that run
18+
* in parallel in separate control flows.
19+
*/
20+
21+
var webdriver = require('..');
22+
23+
for (var i = 0; i < 3; i++) {
24+
(function(n) {
25+
var flow = new webdriver.promise.ControlFlow()
26+
.on('uncaughtException', function(e) {
27+
console.log('uncaughtException in flow %d: %s', n, e);
28+
});
29+
30+
var driver = new webdriver.Builder().
31+
withCapabilities(webdriver.Capabilities.chrome()).
32+
setControlFlow(flow). // Comment out this line to see the difference.
33+
build();
34+
35+
// Position and resize window so it's easy to see them running together.
36+
driver.manage().window().setSize(600, 400);
37+
driver.manage().window().setPosition(300 * i, 400 * i);
38+
39+
driver.get('http://www.google.com');
40+
driver.findElement(webdriver.By.name('q')).sendKeys('webdriver');
41+
driver.findElement(webdriver.By.name('btnG')).click();
42+
driver.wait(function() {
43+
return driver.getTitle().then(function(title) {
44+
return 'webdriver - Google Search' === title;
45+
});
46+
}, 1000);
47+
48+
driver.quit();
49+
})(i);
50+
}
51+

javascript/node/selenium-webdriver/phantomjs.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,9 +101,11 @@ var WEBDRIVER_TO_PHANTOMJS_LEVEL = (function() {
101101
/**
102102
* Creates a new PhantomJS WebDriver client.
103103
* @param {webdriver.Capabilities=} opt_capabilities The desired capabilities.
104+
* @param {webdriver.promise.ControlFlow=} opt_flow The control flow to use, or
105+
* {@code null} to use the currently active flow.
104106
* @return {!webdriver.WebDriver} A new WebDriver instance.
105107
*/
106-
function createDriver(opt_capabilities) {
108+
function createDriver(opt_capabilities, opt_flow) {
107109
var capabilities = opt_capabilities || webdriver.Capabilities.phantomjs();
108110
var exe = findExecutable(capabilities.get(BINARY_PATH_CAPABILITY));
109111
var args = ['--webdriver-logfile=' + DEFAULT_LOG_FILE];
@@ -149,7 +151,8 @@ function createDriver(opt_capabilities) {
149151
});
150152

151153
var executor = executors.createExecutor(service.start());
152-
var driver = webdriver.WebDriver.createSession(executor, capabilities);
154+
var driver = webdriver.WebDriver.createSession(
155+
executor, capabilities, opt_flow);
153156
var boundQuit = driver.quit.bind(driver);
154157
driver.quit = function() {
155158
return boundQuit().thenFinally(service.kill.bind(service));

javascript/webdriver/test/webdriver_test.js

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,36 @@ function testAttachToSession_failsToGetSessionInfo() {
274274
}
275275

276276

277+
function testAttachToSession_usesActiveFlowByDefault() {
278+
var testHelper = TestHelper.expectingSuccess().
279+
expect(CName.DESCRIBE_SESSION).
280+
withParameters({'sessionId': SESSION_ID}).
281+
andReturnSuccess({}).
282+
replayAll();
283+
284+
var driver = webdriver.WebDriver.attachToSession(testHelper.executor,
285+
SESSION_ID);
286+
assertEquals(driver.controlFlow(), webdriver.promise.controlFlow());
287+
testHelper.execute();
288+
}
289+
290+
291+
function testAttachToSession_canAttachInCustomFlow() {
292+
var testHelper = TestHelper.expectingSuccess().
293+
expect(CName.DESCRIBE_SESSION).
294+
withParameters({'sessionId': SESSION_ID}).
295+
andReturnSuccess({}).
296+
replayAll();
297+
298+
var otherFlow = new webdriver.promise.ControlFlow(goog.global);
299+
var driver = webdriver.WebDriver.attachToSession(testHelper.executor,
300+
SESSION_ID, otherFlow);
301+
assertEquals(otherFlow, driver.controlFlow());
302+
assertNotEquals(otherFlow, webdriver.promise.controlFlow());
303+
testHelper.execute();
304+
}
305+
306+
277307
function testCreateSession_happyPathWithCapabilitiesHashObject() {
278308
var testHelper = TestHelper.expectingSuccess().
279309
expect(CName.NEW_SESSION).
@@ -343,6 +373,35 @@ function testCreateSession_failsToCreateSession() {
343373
}
344374

345375

376+
function testCreateSession_usesActiveFlowByDefault() {
377+
var testHelper = TestHelper.expectingSuccess().
378+
expect(CName.NEW_SESSION).
379+
withParameters({'desiredCapabilities': {}}).
380+
andReturnSuccess({}).
381+
replayAll();
382+
383+
var driver = webdriver.WebDriver.createSession(testHelper.executor, {});
384+
assertEquals(webdriver.promise.controlFlow(), driver.controlFlow());
385+
testHelper.execute();
386+
}
387+
388+
389+
function testCreateSession_canCreateInCustomFlow() {
390+
var testHelper = TestHelper.expectingSuccess().
391+
expect(CName.NEW_SESSION).
392+
withParameters({'desiredCapabilities': {}}).
393+
andReturnSuccess({}).
394+
replayAll();
395+
396+
var otherFlow = new webdriver.promise.ControlFlow(goog.global);
397+
var driver = webdriver.WebDriver.createSession(
398+
testHelper.executor, {}, otherFlow);
399+
assertEquals(otherFlow, driver.controlFlow());
400+
assertNotEquals(otherFlow, webdriver.promise.controlFlow());
401+
testHelper.execute();
402+
}
403+
404+
346405
function testToWireValue_function() {
347406
var fn = function() { return 'foo'; };
348407
var callback;

javascript/webdriver/webdriver.js

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -91,13 +91,17 @@ webdriver.WebDriver = function(session, executor, opt_flow) {
9191
* @param {!webdriver.CommandExecutor} executor Command executor to use when
9292
* querying for session details.
9393
* @param {string} sessionId ID of the session to attach to.
94+
* @param {webdriver.promise.ControlFlow=} opt_flow The control flow all driver
95+
* commands should execute under. Defaults to the
96+
* {@link webdriver.promise.controlFlow() currently active} control flow.
9497
* @return {!webdriver.WebDriver} A new client for the specified session.
9598
*/
96-
webdriver.WebDriver.attachToSession = function(executor, sessionId) {
99+
webdriver.WebDriver.attachToSession = function(executor, sessionId, opt_flow) {
97100
return webdriver.WebDriver.acquireSession_(executor,
98101
new webdriver.Command(webdriver.CommandName.DESCRIBE_SESSION).
99102
setParameter('sessionId', sessionId),
100-
'WebDriver.attachToSession()');
103+
'WebDriver.attachToSession()',
104+
opt_flow);
101105
};
102106

103107

@@ -107,13 +111,19 @@ webdriver.WebDriver.attachToSession = function(executor, sessionId) {
107111
* session with.
108112
* @param {!webdriver.Capabilities} desiredCapabilities The desired
109113
* capabilities for the new session.
114+
* @param {webdriver.promise.ControlFlow=} opt_flow The control flow all driver
115+
* commands should execute under, including the initial session creation.
116+
* Defaults to the {@link webdriver.promise.controlFlow() currently active}
117+
* control flow.
110118
* @return {!webdriver.WebDriver} The driver for the newly created session.
111119
*/
112-
webdriver.WebDriver.createSession = function(executor, desiredCapabilities) {
120+
webdriver.WebDriver.createSession = function(
121+
executor, desiredCapabilities, opt_flow) {
113122
return webdriver.WebDriver.acquireSession_(executor,
114123
new webdriver.Command(webdriver.CommandName.NEW_SESSION).
115124
setParameter('desiredCapabilities', desiredCapabilities),
116-
'WebDriver.createSession()');
125+
'WebDriver.createSession()',
126+
opt_flow);
117127
};
118128

119129

@@ -126,19 +136,24 @@ webdriver.WebDriver.createSession = function(executor, desiredCapabilities) {
126136
* @param {!webdriver.Command} command The command to send to fetch the session
127137
* details.
128138
* @param {string} description A descriptive debug label for this action.
139+
* @param {webdriver.promise.ControlFlow=} opt_flow The control flow all driver
140+
* commands should execute under. Defaults to the
141+
* {@link webdriver.promise.controlFlow() currently active} control flow.
129142
* @return {!webdriver.WebDriver} A new WebDriver client for the session.
130143
* @private
131144
*/
132-
webdriver.WebDriver.acquireSession_ = function(executor, command, description) {
133-
var session = webdriver.promise.controlFlow().execute(function() {
145+
webdriver.WebDriver.acquireSession_ = function(
146+
executor, command, description, opt_flow) {
147+
var flow = opt_flow || webdriver.promise.controlFlow();
148+
var session = flow.execute(function() {
134149
return webdriver.WebDriver.executeCommand_(executor, command).
135150
then(function(response) {
136151
bot.response.checkResponse(response);
137152
return new webdriver.Session(response['sessionId'],
138153
response['value']);
139154
});
140155
}, description);
141-
return new webdriver.WebDriver(session, executor);
156+
return new webdriver.WebDriver(session, executor, flow);
142157
};
143158

144159

0 commit comments

Comments
 (0)