Skip to content

Commit

Permalink
[js] For Firefox and Chrome, the builder will now always return a fir…
Browse files Browse the repository at this point in the history
…efox.Driver

or chrome.Driver instance, respectively, even when targeting a remote server.

This ensures users will always have access to browser-specific commands like
firefox.Driver.prototype.setContext or chrome.Driver.prototype.launchApp.
Whether the targeted remote end actually exposes these commands depends entirely
on what the user has setup and is outside our control.

Fixes #2464
  • Loading branch information
jleyba committed Aug 6, 2016
1 parent dcaf7b2 commit 83dfb2c
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 14 deletions.
8 changes: 6 additions & 2 deletions javascript/node/selenium-webdriver/CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,12 @@
* Properly send HTTP requests when using a WebDriver server proxy
* Properly configure proxies when using the geckodriver
* `http.Executor` now accepts a promised client. The `builder.Builder` class
will now use this over a `command.DeferredExecutor` when creating WebDriver
instances.
will now use this instead of a `command.DeferredExecutor` when creating
WebDriver instances.
* For Chrome and Firefox, the `builder.Builder` class will always return an
instanceof `chrome.Driver` and `firefox.Driver`, respectively, even when
configured to use a remote server (from `builder.Builder#usingServer(url)`,
`SELENIUM_REMOTE_URL`, etc).

### API Changes

Expand Down
9 changes: 9 additions & 0 deletions javascript/node/selenium-webdriver/builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,15 @@ class Builder {
let client = Promise.resolve(url)
.then(url => new _http.HttpClient(url, this.agent_, this.proxy_));
let executor = new _http.Executor(client);

if (browser === Browser.CHROME) {
return new chrome.Driver(capabilities, null, this.flow_, executor);
}

if (browser === Browser.FIREFOX) {
return new firefox.Driver(capabilities, this.flow_, executor);
}

return WebDriver.createSession(executor, capabilities, this.flow_);
}

Expand Down
39 changes: 34 additions & 5 deletions javascript/node/selenium-webdriver/chrome.js
Original file line number Diff line number Diff line change
Expand Up @@ -156,10 +156,20 @@ const Command = {
function createExecutor(url) {
let client = url.then(url => new http.HttpClient(url));
let executor = new http.Executor(client);
configureExecutor(executor);
return executor;
}


/**
* Configures the given executor with Chrome-specific commands.
* @param {!http.Executor} executor the executor to configure.
*/
function configureExecutor(executor) {
executor.defineCommand(
Command.LAUNCH_APP,
'POST', '/session/:sessionId/chromium/launch_app');
return executor;
'POST',
'/session/:sessionId/chromium/launch_app');
}


Expand Down Expand Up @@ -763,10 +773,29 @@ class Driver extends webdriver.WebDriver {
* 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.
*
* @throws {Error} if both `opt_service` and `opt_executor` are provided.
*/
constructor(opt_config, opt_service, opt_flow) {
let service = opt_service || getDefaultService();
let executor = createExecutor(service.start());
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');
}

let executor;
if (opt_executor) {
executor = opt_executor;
configureExecutor(executor);
} else {
let service = opt_service || getDefaultService();
executor = createExecutor(service.start());
}

let caps =
opt_config instanceof Options ? opt_config.toCapabilities() :
Expand Down
44 changes: 37 additions & 7 deletions javascript/node/selenium-webdriver/firefox/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -398,19 +398,28 @@ const ExtensionCommand = {
function createExecutor(serverUrl) {
let client = serverUrl.then(url => new http.HttpClient(url));
let executor = new http.Executor(client);
configureExecutor(executor);
return executor;
}


/**
* Configures the given executor with Firefox-specific commands.
* @param {!http.Executor} executor the executor to configure.
*/
function configureExecutor(executor) {
executor.defineCommand(
ExtensionCommand.GET_CONTEXT,
'GET',
'/session/:sessionId/moz/context');

executor.defineCommand(
ExtensionCommand.SET_CONTEXT,
'POST',
'/session/:sessionId/moz/context');

return executor;
}


/**
* A WebDriver client for Firefox.
*/
Expand All @@ -422,8 +431,18 @@ class Driver extends webdriver.WebDriver {
* object.
* @param {promise.ControlFlow=} opt_flow The flow to
* schedule commands through. Defaults to the active flow object.
* @param {http.Executor=} opt_executor A pre-configured command executor to
* use for communicating with an externally managed remoted end (which is
* assumed to already be running). The provided executor should not be
* reused with other clients as its internal command mappings will be
* updated to support Firefox-specific commands.
*
* _This parameter may only be used with Mozilla's GeckoDriver._
*
* @throws {Error} If a custom command executor is provided and the driver is
* configured to use the legacy FirefoxDriver from the Selenium project.
*/
constructor(opt_config, opt_flow) {
constructor(opt_config, opt_flow, opt_executor) {
let caps;
if (opt_config instanceof Options) {
caps = opt_config.toCapabilities();
Expand Down Expand Up @@ -453,9 +472,15 @@ class Driver extends webdriver.WebDriver {
let useMarionette = !noMarionette;

if (useMarionette) {
let service = createGeckoDriverService(binary);
serverUrl = service.start();
onQuit = () => service.kill();
if (opt_executor) {
configureExecutor(opt_executor);
serverUrl = Promise.reject(Error('unexpected variable use'));
onQuit = function noop() {};
} else {
let service = createGeckoDriverService(binary);
serverUrl = service.start();
onQuit = () => service.kill();
}

if (profile) {
caps.set(Capability.PROFILE, profile.encode());
Expand All @@ -474,6 +499,11 @@ class Driver extends webdriver.WebDriver {
caps = {required, desired: caps};
}
} else {
if (opt_executor) {
throw Error('You may not use a custom command executor with the legacy'
+ ' FirefoxDriver');
}

profile = profile || new Profile;

let freePort = portprober.findFreePort();
Expand Down Expand Up @@ -503,7 +533,7 @@ class Driver extends webdriver.WebDriver {
};
}

let executor = createExecutor(serverUrl);
let executor = opt_executor || createExecutor(serverUrl);
let driver = webdriver.WebDriver.createSession(executor, caps, opt_flow);
super(driver.getSession(), executor, driver.controlFlow());

Expand Down

0 comments on commit 83dfb2c

Please sign in to comment.