Skip to content

Commit b390490

Browse files
committed
[js] Builder.build() now returns a thenable WebDriver object. Users can issue
commands directly, or through a standard promise callback. This is the same pattern used for WebElements with WebDriver.findElement(). Removed Builder.buildAsync() as it was redundant with the change above. This change is part of #2969, to support awaiting the build result: async function demo(url); { let driver = await new Builder().forBrowser('firefox').build(); return driver.get(�url); }
1 parent de1d18e commit b390490

27 files changed

+649
-548
lines changed

javascript/node/selenium-webdriver/CHANGES.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,17 @@
2929
- Removed `#isPending()`
3030
- Removed `#cancel()`
3131
- Removed `#finally()`
32+
* Changed all subclasses of `webdriver.WebDriver` to overload the static
33+
function `WebDriver.createSession()` instead of doing work in the
34+
constructor. All constructors now inherit the base class' function signature.
35+
Users are still encouraged to use the `Builder` class instead of creating
36+
drivers directly.
37+
* `Builder#build()` now returns a "thenable" WebDriver instance, allowing users
38+
to immediately schedule commands (as before), or issue them through standard
39+
promise callbacks. This is the same pattern already employed for WebElements.
40+
* Removed `Builder#buildAsync()` as it was redundant with the new semantics of
41+
`build()`.
42+
3243

3344

3445
## v3.0.0-beta-3

javascript/node/selenium-webdriver/chrome.js

Lines changed: 18 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -119,8 +119,7 @@ const fs = require('fs'),
119119

120120
const http = require('./http'),
121121
io = require('./io'),
122-
Capabilities = require('./lib/capabilities').Capabilities,
123-
Capability = require('./lib/capabilities').Capability,
122+
{Capabilities, Capability} = require('./lib/capabilities'),
124123
command = require('./lib/command'),
125124
logging = require('./lib/logging'),
126125
promise = require('./lib/promise'),
@@ -678,44 +677,36 @@ class Options {
678677
* Creates a new WebDriver client for Chrome.
679678
*/
680679
class Driver extends webdriver.WebDriver {
680+
681681
/**
682-
* @param {(Capabilities|Options)=} opt_config The configuration
683-
* options.
684-
* @param {remote.DriverService=} opt_service The session to use; will use
685-
* the {@linkplain #getDefaultService default service} by default.
686-
* @param {promise.ControlFlow=} opt_flow The control flow to use,
687-
* or {@code null} to use the currently active flow.
688-
* @param {http.Executor=} opt_executor A pre-configured command executor that
689-
* should be used to send commands to the remote end. The provided
690-
* executor should not be reused with other clients as its internal
691-
* command mappings will be updated to support Chrome-specific commands.
692-
*
693-
* You may provide either a custom executor or a driver service, but not both.
682+
* Creates a new session with the ChromeDriver.
694683
*
695-
* @throws {Error} if both `opt_service` and `opt_executor` are provided.
684+
* @param {(Capabilities|Options)=} opt_config The configuration options.
685+
* @param {(remote.DriverService|http.Executor)=} opt_serviceExecutor Either
686+
* a DriverService to use for the remote end, or a preconfigured executor
687+
* for an externally managed endpoint. If neither is provided, the
688+
* {@linkplain ##getDefaultService default service} will be used by
689+
* default.
690+
* @param {promise.ControlFlow=} opt_flow The control flow to use, or `null`
691+
* to use the currently active flow.
692+
* @return {!Driver} A new driver instance.
696693
*/
697-
constructor(opt_config, opt_service, opt_flow, opt_executor) {
698-
if (opt_service && opt_executor) {
699-
throw Error(
700-
'Either a DriverService or Executor may be provided, but not both');
701-
}
702-
694+
static createSession(opt_config, opt_serviceExecutor, opt_flow) {
703695
let executor;
704-
if (opt_executor) {
705-
executor = opt_executor;
696+
if (opt_serviceExecutor instanceof http.Executor) {
697+
executor = opt_serviceExecutor;
706698
configureExecutor(executor);
707699
} else {
708-
let service = opt_service || getDefaultService();
700+
let service = opt_serviceExecutor || getDefaultService();
709701
executor = createExecutor(service.start());
710702
}
711703

712704
let caps =
713705
opt_config instanceof Options ? opt_config.toCapabilities() :
714706
(opt_config || Capabilities.chrome());
715707

716-
let driver = webdriver.WebDriver.createSession(executor, caps, opt_flow);
717-
718-
super(driver.getSession(), executor, driver.controlFlow());
708+
return /** @type {!Driver} */(
709+
webdriver.WebDriver.createSession(executor, caps, opt_flow, this));
719710
}
720711

721712
/**

javascript/node/selenium-webdriver/edge.js

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -259,14 +259,17 @@ function getDefaultService() {
259259
*/
260260
class Driver extends webdriver.WebDriver {
261261
/**
262+
* Creates a new browser session for Microsoft's Edge browser.
263+
*
262264
* @param {(capabilities.Capabilities|Options)=} opt_config The configuration
263265
* options.
264266
* @param {remote.DriverService=} opt_service The session to use; will use
265267
* the {@linkplain #getDefaultService default service} by default.
266268
* @param {promise.ControlFlow=} opt_flow The control flow to use, or
267269
* {@code null} to use the currently active flow.
270+
* @return {!Driver} A new driver instance.
268271
*/
269-
constructor(opt_config, opt_service, opt_flow) {
272+
static createSession(opt_config, opt_service, opt_flow) {
270273
var service = opt_service || getDefaultService();
271274
var client = service.start().then(url => new http.HttpClient(url));
272275
var executor = new http.Executor(client);
@@ -275,14 +278,8 @@ class Driver extends webdriver.WebDriver {
275278
opt_config instanceof Options ? opt_config.toCapabilities() :
276279
(opt_config || capabilities.Capabilities.edge());
277280

278-
var driver = webdriver.WebDriver.createSession(executor, caps, opt_flow);
279-
super(driver.getSession(), executor, driver.controlFlow());
280-
281-
/** @override */
282-
this.quit = () => {
283-
return /** @type {!promise.Thenable} */(
284-
promise.finally(super.quit(), service.kill.bind(service)));
285-
};
281+
return /** @type {!Driver} */(webdriver.WebDriver.createSession(
282+
executor, caps, opt_flow, this, () => service.kill()));
286283
}
287284

288285
/**

javascript/node/selenium-webdriver/firefox/index.js

Lines changed: 30 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -440,7 +440,11 @@ class ServiceBuilder extends remote.DriverService.Builder {
440440

441441

442442
/**
443-
* @typedef {{driver: !webdriver.WebDriver, onQuit: function()}}
443+
* @typedef {{executor: !command.Executor,
444+
* capabilities: (!capabilities.Capabilities|
445+
* {desired: (capabilities.Capabilities|undefined),
446+
* required: (capabilities.Capabilities|undefined)}),
447+
* onQuit: function(this: void): ?}}
444448
*/
445449
var DriverSpec;
446450

@@ -450,11 +454,9 @@ var DriverSpec;
450454
* @param {!capabilities.Capabilities} caps
451455
* @param {Profile} profile
452456
* @param {Binary} binary
453-
* @param {(promise.ControlFlow|undefined)} flow
454457
* @return {DriverSpec}
455458
*/
456-
function createGeckoDriver(
457-
executor, caps, profile, binary, flow) {
459+
function createGeckoDriver(executor, caps, profile, binary) {
458460
let firefoxOptions = {};
459461
caps.set('moz:firefoxOptions', firefoxOptions);
460462

@@ -499,7 +501,7 @@ function createGeckoDriver(
499501
sessionCaps = {required, desired: caps};
500502
}
501503

502-
/** @type {(command.Executor|undefined)} */
504+
/** @type {!command.Executor} */
503505
let cmdExecutor;
504506
let onQuit = function() {};
505507

@@ -519,20 +521,18 @@ function createGeckoDriver(
519521
onQuit = () => service.kill();
520522
}
521523

522-
let driver =
523-
webdriver.WebDriver.createSession(
524-
/** @type {!http.Executor} */(cmdExecutor),
525-
sessionCaps,
526-
flow);
527-
return {driver, onQuit};
524+
return {
525+
executor: cmdExecutor,
526+
capabilities: sessionCaps,
527+
onQuit
528+
};
528529
}
529530

530531

531532
/**
532533
* @param {!capabilities.Capabilities} caps
533534
* @param {Profile} profile
534535
* @param {!Binary} binary
535-
* @param {(promise.ControlFlow|undefined)} flow
536536
* @return {DriverSpec}
537537
*/
538538
function createLegacyDriver(caps, profile, binary, flow) {
@@ -555,18 +555,18 @@ function createLegacyDriver(caps, profile, binary, flow) {
555555
return ready.then(() => serverUrl);
556556
});
557557

558-
let onQuit = function() {
559-
return command.then(command => {
560-
command.kill();
561-
return preparedProfile.then(io.rmDir)
562-
.then(() => command.result(),
563-
() => command.result());
564-
});
558+
return {
559+
executor: createExecutor(serverUrl),
560+
capabilities: caps,
561+
onQuit: function() {
562+
return command.then(command => {
563+
command.kill();
564+
return preparedProfile.then(io.rmDir)
565+
.then(() => command.result(),
566+
() => command.result());
567+
});
568+
}
565569
};
566-
567-
let executor = createExecutor(serverUrl);
568-
let driver = webdriver.WebDriver.createSession(executor, caps, flow);
569-
return {driver, onQuit};
570570
}
571571

572572

@@ -575,6 +575,8 @@ function createLegacyDriver(caps, profile, binary, flow) {
575575
*/
576576
class Driver extends webdriver.WebDriver {
577577
/**
578+
* Creates a new Firefox session.
579+
*
578580
* @param {(Options|capabilities.Capabilities|Object)=} opt_config The
579581
* configuration options for this driver, specified as either an
580582
* {@link Options} or {@link capabilities.Capabilities}, or as a raw hash
@@ -595,8 +597,9 @@ class Driver extends webdriver.WebDriver {
595597
* schedule commands through. Defaults to the active flow object.
596598
* @throws {Error} If a custom command executor is provided and the driver is
597599
* configured to use the legacy FirefoxDriver from the Selenium project.
600+
* @return {!Driver} A new driver instance.
598601
*/
599-
constructor(opt_config, opt_executor, opt_flow) {
602+
static createSession(opt_config, opt_executor, opt_flow) {
600603
let caps;
601604
if (opt_config instanceof Options) {
602605
caps = opt_config.toCapabilities();
@@ -616,8 +619,6 @@ class Driver extends webdriver.WebDriver {
616619
caps.delete(Capability.PROFILE);
617620
}
618621

619-
let serverUrl, onQuit;
620-
621622
// Users must now explicitly disable marionette to use the legacy
622623
// FirefoxDriver.
623624
let noMarionette =
@@ -627,12 +628,7 @@ class Driver extends webdriver.WebDriver {
627628

628629
let spec;
629630
if (useMarionette) {
630-
spec = createGeckoDriver(
631-
opt_executor,
632-
caps,
633-
profile,
634-
binary,
635-
opt_flow);
631+
spec = createGeckoDriver(opt_executor, caps, profile, binary);
636632
} else {
637633
if (opt_executor) {
638634
throw Error('You may not use a custom command executor with the legacy'
@@ -641,15 +637,8 @@ class Driver extends webdriver.WebDriver {
641637
spec = createLegacyDriver(caps, profile, binary, opt_flow);
642638
}
643639

644-
super(spec.driver.getSession(),
645-
spec.driver.getExecutor(),
646-
spec.driver.controlFlow());
647-
648-
/** @override */
649-
this.quit = () => {
650-
return /** @type {!promise.Thenable} */(
651-
promise.finally(super.quit(), spec.onQuit));
652-
};
640+
return /** @type {!Driver} */(webdriver.WebDriver.createSession(
641+
spec.executor, spec.capabilities, opt_flow, this, spec.onQuit));
653642
}
654643

655644
/**

javascript/node/selenium-webdriver/ie.js

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -403,28 +403,25 @@ function createServiceFromCapabilities(capabilities) {
403403
*/
404404
class Driver extends webdriver.WebDriver {
405405
/**
406+
* Creates a new session for Microsoft's Internet Explorer.
407+
*
406408
* @param {(capabilities.Capabilities|Options)=} opt_config The configuration
407409
* options.
408410
* @param {promise.ControlFlow=} opt_flow The control flow to use,
409411
* or {@code null} to use the currently active flow.
412+
* @return {!Driver} A new driver instance.
410413
*/
411-
constructor(opt_config, opt_flow) {
414+
static createSession(opt_config, opt_flow) {
412415
var caps = opt_config instanceof Options ?
413416
opt_config.toCapabilities() :
414417
(opt_config || capabilities.Capabilities.ie());
415418

416419
var service = createServiceFromCapabilities(caps);
417420
var client = service.start().then(url => new http.HttpClient(url));
418421
var executor = new http.Executor(client);
419-
var driver = webdriver.WebDriver.createSession(executor, caps, opt_flow);
420-
421-
super(driver.getSession(), executor, driver.controlFlow());
422422

423-
/** @override */
424-
this.quit = () => {
425-
return /** @type {!promise.Thenable} */(
426-
promise.finally(super.quit(), service.kill.bind(service)));
427-
};
423+
return /** @type {!Driver} */(webdriver.WebDriver.createSession(
424+
executor, caps, opt_flow, this, () => service.kill()));
428425
}
429426

430427
/**

0 commit comments

Comments
 (0)