Skip to content

Commit

Permalink
[js] Builder.build() now returns a thenable WebDriver object. Users c…
Browse files Browse the repository at this point in the history
…an 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);
}
  • Loading branch information
jleyba committed Nov 2, 2016
1 parent de1d18e commit b390490
Show file tree
Hide file tree
Showing 27 changed files with 649 additions and 548 deletions.
11 changes: 11 additions & 0 deletions javascript/node/selenium-webdriver/CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,17 @@
- Removed `#isPending()`
- Removed `#cancel()`
- Removed `#finally()`
* Changed all subclasses of `webdriver.WebDriver` to overload the static
function `WebDriver.createSession()` instead of doing work in the
constructor. All constructors now inherit the base class' function signature.
Users are still encouraged to use the `Builder` class instead of creating
drivers directly.
* `Builder#build()` now returns a "thenable" WebDriver instance, allowing users
to immediately schedule commands (as before), or issue them through standard
promise callbacks. This is the same pattern already employed for WebElements.
* Removed `Builder#buildAsync()` as it was redundant with the new semantics of
`build()`.



## v3.0.0-beta-3
Expand Down
45 changes: 18 additions & 27 deletions javascript/node/selenium-webdriver/chrome.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,7 @@ const fs = require('fs'),

const http = require('./http'),
io = require('./io'),
Capabilities = require('./lib/capabilities').Capabilities,
Capability = require('./lib/capabilities').Capability,
{Capabilities, Capability} = require('./lib/capabilities'),
command = require('./lib/command'),
logging = require('./lib/logging'),
promise = require('./lib/promise'),
Expand Down Expand Up @@ -678,44 +677,36 @@ class Options {
* Creates a new WebDriver client for Chrome.
*/
class Driver extends webdriver.WebDriver {

/**
* @param {(Capabilities|Options)=} opt_config The configuration
* options.
* @param {remote.DriverService=} opt_service The session to use; will use
* the {@linkplain #getDefaultService default service} by default.
* @param {promise.ControlFlow=} opt_flow The control flow to use,
* or {@code null} to use the currently active flow.
* @param {http.Executor=} opt_executor A pre-configured command executor that
* should be used to send commands to the remote end. The provided
* executor should not be reused with other clients as its internal
* command mappings will be updated to support Chrome-specific commands.
*
* You may provide either a custom executor or a driver service, but not both.
* Creates a new session with the ChromeDriver.
*
* @throws {Error} if both `opt_service` and `opt_executor` are provided.
* @param {(Capabilities|Options)=} opt_config The configuration options.
* @param {(remote.DriverService|http.Executor)=} opt_serviceExecutor Either
* a DriverService to use for the remote end, or a preconfigured executor
* for an externally managed endpoint. If neither is provided, the
* {@linkplain ##getDefaultService default service} will be used by
* default.
* @param {promise.ControlFlow=} opt_flow The control flow to use, or `null`
* to use the currently active flow.
* @return {!Driver} A new driver instance.
*/
constructor(opt_config, opt_service, opt_flow, opt_executor) {
if (opt_service && opt_executor) {
throw Error(
'Either a DriverService or Executor may be provided, but not both');
}

static createSession(opt_config, opt_serviceExecutor, opt_flow) {
let executor;
if (opt_executor) {
executor = opt_executor;
if (opt_serviceExecutor instanceof http.Executor) {
executor = opt_serviceExecutor;
configureExecutor(executor);
} else {
let service = opt_service || getDefaultService();
let service = opt_serviceExecutor || getDefaultService();
executor = createExecutor(service.start());
}

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

let driver = webdriver.WebDriver.createSession(executor, caps, opt_flow);

super(driver.getSession(), executor, driver.controlFlow());
return /** @type {!Driver} */(
webdriver.WebDriver.createSession(executor, caps, opt_flow, this));
}

/**
Expand Down
15 changes: 6 additions & 9 deletions javascript/node/selenium-webdriver/edge.js
Original file line number Diff line number Diff line change
Expand Up @@ -259,14 +259,17 @@ function getDefaultService() {
*/
class Driver extends webdriver.WebDriver {
/**
* Creates a new browser session for Microsoft's Edge browser.
*
* @param {(capabilities.Capabilities|Options)=} opt_config The configuration
* options.
* @param {remote.DriverService=} opt_service The session to use; will use
* the {@linkplain #getDefaultService default service} by default.
* @param {promise.ControlFlow=} opt_flow The control flow to use, or
* {@code null} to use the currently active flow.
* @return {!Driver} A new driver instance.
*/
constructor(opt_config, opt_service, opt_flow) {
static createSession(opt_config, opt_service, opt_flow) {
var service = opt_service || getDefaultService();
var client = service.start().then(url => new http.HttpClient(url));
var executor = new http.Executor(client);
Expand All @@ -275,14 +278,8 @@ class Driver extends webdriver.WebDriver {
opt_config instanceof Options ? opt_config.toCapabilities() :
(opt_config || capabilities.Capabilities.edge());

var driver = webdriver.WebDriver.createSession(executor, caps, opt_flow);
super(driver.getSession(), executor, driver.controlFlow());

/** @override */
this.quit = () => {
return /** @type {!promise.Thenable} */(
promise.finally(super.quit(), service.kill.bind(service)));
};
return /** @type {!Driver} */(webdriver.WebDriver.createSession(
executor, caps, opt_flow, this, () => service.kill()));
}

/**
Expand Down
71 changes: 30 additions & 41 deletions javascript/node/selenium-webdriver/firefox/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,11 @@ class ServiceBuilder extends remote.DriverService.Builder {


/**
* @typedef {{driver: !webdriver.WebDriver, onQuit: function()}}
* @typedef {{executor: !command.Executor,
* capabilities: (!capabilities.Capabilities|
* {desired: (capabilities.Capabilities|undefined),
* required: (capabilities.Capabilities|undefined)}),
* onQuit: function(this: void): ?}}
*/
var DriverSpec;

Expand All @@ -450,11 +454,9 @@ var DriverSpec;
* @param {!capabilities.Capabilities} caps
* @param {Profile} profile
* @param {Binary} binary
* @param {(promise.ControlFlow|undefined)} flow
* @return {DriverSpec}
*/
function createGeckoDriver(
executor, caps, profile, binary, flow) {
function createGeckoDriver(executor, caps, profile, binary) {
let firefoxOptions = {};
caps.set('moz:firefoxOptions', firefoxOptions);

Expand Down Expand Up @@ -499,7 +501,7 @@ function createGeckoDriver(
sessionCaps = {required, desired: caps};
}

/** @type {(command.Executor|undefined)} */
/** @type {!command.Executor} */
let cmdExecutor;
let onQuit = function() {};

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

let driver =
webdriver.WebDriver.createSession(
/** @type {!http.Executor} */(cmdExecutor),
sessionCaps,
flow);
return {driver, onQuit};
return {
executor: cmdExecutor,
capabilities: sessionCaps,
onQuit
};
}


/**
* @param {!capabilities.Capabilities} caps
* @param {Profile} profile
* @param {!Binary} binary
* @param {(promise.ControlFlow|undefined)} flow
* @return {DriverSpec}
*/
function createLegacyDriver(caps, profile, binary, flow) {
Expand All @@ -555,18 +555,18 @@ function createLegacyDriver(caps, profile, binary, flow) {
return ready.then(() => serverUrl);
});

let onQuit = function() {
return command.then(command => {
command.kill();
return preparedProfile.then(io.rmDir)
.then(() => command.result(),
() => command.result());
});
return {
executor: createExecutor(serverUrl),
capabilities: caps,
onQuit: function() {
return command.then(command => {
command.kill();
return preparedProfile.then(io.rmDir)
.then(() => command.result(),
() => command.result());
});
}
};

let executor = createExecutor(serverUrl);
let driver = webdriver.WebDriver.createSession(executor, caps, flow);
return {driver, onQuit};
}


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

let serverUrl, onQuit;

// Users must now explicitly disable marionette to use the legacy
// FirefoxDriver.
let noMarionette =
Expand All @@ -627,12 +628,7 @@ class Driver extends webdriver.WebDriver {

let spec;
if (useMarionette) {
spec = createGeckoDriver(
opt_executor,
caps,
profile,
binary,
opt_flow);
spec = createGeckoDriver(opt_executor, caps, profile, binary);
} else {
if (opt_executor) {
throw Error('You may not use a custom command executor with the legacy'
Expand All @@ -641,15 +637,8 @@ class Driver extends webdriver.WebDriver {
spec = createLegacyDriver(caps, profile, binary, opt_flow);
}

super(spec.driver.getSession(),
spec.driver.getExecutor(),
spec.driver.controlFlow());

/** @override */
this.quit = () => {
return /** @type {!promise.Thenable} */(
promise.finally(super.quit(), spec.onQuit));
};
return /** @type {!Driver} */(webdriver.WebDriver.createSession(
spec.executor, spec.capabilities, opt_flow, this, spec.onQuit));
}

/**
Expand Down
15 changes: 6 additions & 9 deletions javascript/node/selenium-webdriver/ie.js
Original file line number Diff line number Diff line change
Expand Up @@ -403,28 +403,25 @@ function createServiceFromCapabilities(capabilities) {
*/
class Driver extends webdriver.WebDriver {
/**
* Creates a new session for Microsoft's Internet Explorer.
*
* @param {(capabilities.Capabilities|Options)=} opt_config The configuration
* options.
* @param {promise.ControlFlow=} opt_flow The control flow to use,
* or {@code null} to use the currently active flow.
* @return {!Driver} A new driver instance.
*/
constructor(opt_config, opt_flow) {
static createSession(opt_config, opt_flow) {
var caps = opt_config instanceof Options ?
opt_config.toCapabilities() :
(opt_config || capabilities.Capabilities.ie());

var service = createServiceFromCapabilities(caps);
var client = service.start().then(url => new http.HttpClient(url));
var executor = new http.Executor(client);
var driver = webdriver.WebDriver.createSession(executor, caps, opt_flow);

super(driver.getSession(), executor, driver.controlFlow());

/** @override */
this.quit = () => {
return /** @type {!promise.Thenable} */(
promise.finally(super.quit(), service.kill.bind(service)));
};
return /** @type {!Driver} */(webdriver.WebDriver.createSession(
executor, caps, opt_flow, this, () => service.kill()));
}

/**
Expand Down
Loading

0 comments on commit b390490

Please sign in to comment.