aboutsummaryrefslogtreecommitdiff
path: root/node_modules/selenium-webdriver/remote/index.js
diff options
context:
space:
mode:
authorFlorian Dold <florian.dold@gmail.com>2016-11-03 01:33:53 +0100
committerFlorian Dold <florian.dold@gmail.com>2016-11-03 01:33:53 +0100
commitd1291f67551c58168af43698a359cb5ddfd266b0 (patch)
tree55a13ed29fe1915e3f42f1b1b7038dafa2e975a7 /node_modules/selenium-webdriver/remote/index.js
parentd0a0695fb5d34996850723f7d4b1b59c3df909c2 (diff)
node_modules
Diffstat (limited to 'node_modules/selenium-webdriver/remote/index.js')
-rw-r--r--node_modules/selenium-webdriver/remote/index.js593
1 files changed, 593 insertions, 0 deletions
diff --git a/node_modules/selenium-webdriver/remote/index.js b/node_modules/selenium-webdriver/remote/index.js
new file mode 100644
index 000000000..ab76b4476
--- /dev/null
+++ b/node_modules/selenium-webdriver/remote/index.js
@@ -0,0 +1,593 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+'use strict';
+
+const AdmZip = require('adm-zip');
+const fs = require('fs');
+const path = require('path');
+const url = require('url');
+const util = require('util');
+
+const httpUtil = require('../http/util');
+const io = require('../io');
+const exec = require('../io/exec');
+const cmd = require('../lib/command');
+const input = require('../lib/input');
+const promise = require('../lib/promise');
+const webdriver = require('../lib/webdriver');
+const net = require('../net');
+const portprober = require('../net/portprober');
+
+
+/**
+ * @typedef {(string|!Array<string|number|!stream.Stream|null|undefined>)}
+ */
+var StdIoOptions;
+
+
+/**
+ * @typedef {(string|!IThenable<string>)}
+ */
+var CommandLineFlag;
+
+
+/**
+ * A record object that defines the configuration options for a DriverService
+ * instance.
+ *
+ * @record
+ */
+function ServiceOptions() {}
+
+/**
+ * Whether the service should only be accessed on this host's loopback address.
+ *
+ * @type {(boolean|undefined)}
+ */
+ServiceOptions.prototype.loopback;
+
+/**
+ * The host name to access the server on. If this option is specified, the
+ * {@link #loopback} option will be ignored.
+ *
+ * @type {(string|undefined)}
+ */
+ServiceOptions.prototype.hostname;
+
+/**
+ * The port to start the server on (must be > 0). If the port is provided as a
+ * promise, the service will wait for the promise to resolve before starting.
+ *
+ * @type {(number|!IThenable<number>)}
+ */
+ServiceOptions.prototype.port;
+
+/**
+ * The arguments to pass to the service. If a promise is provided, the service
+ * will wait for it to resolve before starting.
+ *
+ * @type {!(Array<CommandLineFlag>|IThenable<!Array<CommandLineFlag>>)}
+ */
+ServiceOptions.prototype.args;
+
+/**
+ * The base path on the server for the WebDriver wire protocol (e.g. '/wd/hub').
+ * Defaults to '/'.
+ *
+ * @type {(string|undefined|null)}
+ */
+ServiceOptions.prototype.path;
+
+/**
+ * The environment variables that should be visible to the server process.
+ * Defaults to inheriting the current process's environment.
+ *
+ * @type {(Object<string, string>|undefined)}
+ */
+ServiceOptions.prototype.env;
+
+/**
+ * IO configuration for the spawned server process. For more information, refer
+ * to the documentation of `child_process.spawn`.
+ *
+ * @type {(StdIoOptions|undefined)}
+ * @see https://nodejs.org/dist/latest-v4.x/docs/api/child_process.html#child_process_options_stdio
+ */
+ServiceOptions.prototype.stdio;
+
+
+/**
+ * Manages the life and death of a native executable WebDriver server.
+ *
+ * It is expected that the driver server implements the
+ * https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol.
+ * Furthermore, the managed server should support multiple concurrent sessions,
+ * so that this class may be reused for multiple clients.
+ */
+class DriverService {
+ /**
+ * @param {string} executable Path to the executable to run.
+ * @param {!ServiceOptions} options Configuration options for the service.
+ */
+ constructor(executable, options) {
+ /** @private {string} */
+ this.executable_ = executable;
+
+ /** @private {boolean} */
+ this.loopbackOnly_ = !!options.loopback;
+
+ /** @private {(string|undefined)} */
+ this.hostname_ = options.hostname;
+
+ /** @private {(number|!IThenable<number>)} */
+ this.port_ = options.port;
+
+ /**
+ * @private {!(Array<CommandLineFlag>|
+ * IThenable<!Array<CommandLineFlag>>)}
+ */
+ this.args_ = options.args;
+
+ /** @private {string} */
+ this.path_ = options.path || '/';
+
+ /** @private {!Object<string, string>} */
+ this.env_ = options.env || process.env;
+
+ /**
+ * @private {(string|!Array<string|number|!stream.Stream|null|undefined>)}
+ */
+ this.stdio_ = options.stdio || 'ignore';
+
+ /**
+ * A promise for the managed subprocess, or null if the server has not been
+ * started yet. This promise will never be rejected.
+ * @private {Promise<!exec.Command>}
+ */
+ this.command_ = null;
+
+ /**
+ * Promise that resolves to the server's address or null if the server has
+ * not been started. This promise will be rejected if the server terminates
+ * before it starts accepting WebDriver requests.
+ * @private {Promise<string>}
+ */
+ this.address_ = null;
+ }
+
+ /**
+ * @return {!Promise<string>} A promise that resolves to the server's address.
+ * @throws {Error} If the server has not been started.
+ */
+ address() {
+ if (this.address_) {
+ return this.address_;
+ }
+ throw Error('Server has not been started.');
+ }
+
+ /**
+ * Returns whether the underlying process is still running. This does not take
+ * into account whether the process is in the process of shutting down.
+ * @return {boolean} Whether the underlying service process is running.
+ */
+ isRunning() {
+ return !!this.address_;
+ }
+
+ /**
+ * Starts the server if it is not already running.
+ * @param {number=} opt_timeoutMs How long to wait, in milliseconds, for the
+ * server to start accepting requests. Defaults to 30 seconds.
+ * @return {!Promise<string>} A promise that will resolve to the server's base
+ * URL when it has started accepting requests. If the timeout expires
+ * before the server has started, the promise will be rejected.
+ */
+ start(opt_timeoutMs) {
+ if (this.address_) {
+ return this.address_;
+ }
+
+ var timeout = opt_timeoutMs || DriverService.DEFAULT_START_TIMEOUT_MS;
+ var self = this;
+
+ let resolveCommand;
+ this.command_ = new Promise(resolve => resolveCommand = resolve);
+
+ this.address_ = new Promise((resolveAddress, rejectAddress) => {
+ resolveAddress(Promise.resolve(this.port_).then(port => {
+ if (port <= 0) {
+ throw Error('Port must be > 0: ' + port);
+ }
+
+ return resolveCommandLineFlags(this.args_).then(args => {
+ var command = exec(self.executable_, {
+ args: args,
+ env: self.env_,
+ stdio: self.stdio_
+ });
+
+ resolveCommand(command);
+
+ var earlyTermination = command.result().then(function(result) {
+ var error = result.code == null ?
+ Error('Server was killed with ' + result.signal) :
+ Error('Server terminated early with status ' + result.code);
+ rejectAddress(error);
+ self.address_ = null;
+ self.command_ = null;
+ throw error;
+ });
+
+ var hostname = self.hostname_;
+ if (!hostname) {
+ hostname = !self.loopbackOnly_ && net.getAddress()
+ || net.getLoopbackAddress();
+ }
+
+ var serverUrl = url.format({
+ protocol: 'http',
+ hostname: hostname,
+ port: port + '',
+ pathname: self.path_
+ });
+
+ return new Promise(function(fulfill, reject) {
+ var ready = httpUtil.waitForServer(serverUrl, timeout)
+ .then(fulfill, reject);
+ earlyTermination.catch(function(e) {
+ ready.cancel(/** @type {Error} */(e));
+ reject(Error(e.message));
+ });
+ }).then(function() {
+ return serverUrl;
+ });
+ });
+ }));
+ });
+
+ return this.address_;
+ }
+
+ /**
+ * Stops the service if it is not currently running. This function will kill
+ * the server immediately. To synchronize with the active control flow, use
+ * {@link #stop()}.
+ * @return {!Promise} A promise that will be resolved when the server has been
+ * stopped.
+ */
+ kill() {
+ if (!this.address_ || !this.command_) {
+ return Promise.resolve(); // Not currently running.
+ }
+ return this.command_.then(function(command) {
+ command.kill('SIGTERM');
+ });
+ }
+
+ /**
+ * Schedules a task in the current control flow to stop the server if it is
+ * currently running.
+ * @return {!promise.Promise} A promise that will be resolved when
+ * the server has been stopped.
+ */
+ stop() {
+ return promise.controlFlow().execute(this.kill.bind(this));
+ }
+}
+
+
+/**
+ * @param {!(Array<CommandLineFlag>|IThenable<!Array<CommandLineFlag>>)} args
+ * @return {!Promise<!Array<string>>}
+ */
+function resolveCommandLineFlags(args) {
+ return Promise.resolve(args) // Resolve the outer array.
+ .then(args => Promise.all(args)); // Then resolve the individual flags.
+}
+
+
+/**
+ * The default amount of time, in milliseconds, to wait for the server to
+ * start.
+ * @const {number}
+ */
+DriverService.DEFAULT_START_TIMEOUT_MS = 30 * 1000;
+
+
+/**
+ * Creates {@link DriverService} objects that manage a WebDriver server in a
+ * child process.
+ */
+DriverService.Builder = class {
+ /**
+ * @param {string} exe Path to the executable to use. This executable must
+ * accept the `--port` flag for defining the port to start the server on.
+ * @throws {Error} If the provided executable path does not exist.
+ */
+ constructor(exe) {
+ if (!fs.existsSync(exe)) {
+ throw Error(`The specified executable path does not exist: ${exe}`);
+ }
+
+ /** @private @const {string} */
+ this.exe_ = exe;
+
+ /** @private {!ServiceOptions} */
+ this.options_ = {
+ args: [],
+ port: 0,
+ env: null,
+ stdio: 'ignore'
+ };
+ }
+
+ /**
+ * Define additional command line arguments to use when starting the server.
+ *
+ * @param {...CommandLineFlag} var_args The arguments to include.
+ * @return {!THIS} A self reference.
+ * @this {THIS}
+ * @template THIS
+ */
+ addArguments(var_args) {
+ let args = Array.prototype.slice.call(arguments, 0);
+ this.options_.args = this.options_.args.concat(args);
+ return this;
+ }
+
+ /**
+ * Sets the host name to access the server on. If specified, the
+ * {@linkplain #setLoopback() loopback} setting will be ignored.
+ *
+ * @param {string} hostname
+ * @return {!DriverService.Builder} A self reference.
+ */
+ setHostname(hostname) {
+ this.options_.hostname = hostname;
+ return this;
+ }
+
+ /**
+ * Sets whether the service should be accessed at this host's loopback
+ * address.
+ *
+ * @param {boolean} loopback
+ * @return {!DriverService.Builder} A self reference.
+ */
+ setLoopback(loopback) {
+ this.options_.loopback = loopback;
+ return this;
+ }
+
+ /**
+ * Sets the base path for WebDriver REST commands (e.g. "/wd/hub").
+ * By default, the driver will accept commands relative to "/".
+ *
+ * @param {?string} basePath The base path to use, or `null` to use the
+ * default.
+ * @return {!DriverService.Builder} A self reference.
+ */
+ setPath(basePath) {
+ this.options_.path = basePath;
+ return this;
+ }
+
+ /**
+ * Sets the port to start the server on.
+ *
+ * @param {number} port The port to use, or 0 for any free port.
+ * @return {!DriverService.Builder} A self reference.
+ * @throws {Error} If an invalid port is specified.
+ */
+ setPort(port) {
+ if (port < 0) {
+ throw Error(`port must be >= 0: ${port}`);
+ }
+ this.options_.port = port;
+ return this;
+ }
+
+ /**
+ * Defines the environment to start the server under. This setting will be
+ * inherited by every browser session started by the server. By default, the
+ * server will inherit the enviroment of the current process.
+ *
+ * @param {(Map<string, string>|Object<string, string>|null)} env The desired
+ * environment to use, or `null` if the server should inherit the
+ * current environment.
+ * @return {!DriverService.Builder} A self reference.
+ */
+ setEnvironment(env) {
+ if (env instanceof Map) {
+ let tmp = {};
+ env.forEach((value, key) => tmp[key] = value);
+ env = tmp;
+ }
+ this.options_.env = env;
+ return this;
+ }
+
+ /**
+ * IO configuration for the spawned server process. For more information,
+ * refer to the documentation of `child_process.spawn`.
+ *
+ * @param {StdIoOptions} config The desired IO configuration.
+ * @return {!DriverService.Builder} A self reference.
+ * @see https://nodejs.org/dist/latest-v4.x/docs/api/child_process.html#child_process_options_stdio
+ */
+ setStdio(config) {
+ this.options_.stdio = config;
+ return this;
+ }
+
+ /**
+ * Creates a new DriverService using this instance's current configuration.
+ *
+ * @return {!DriverService} A new driver service.
+ */
+ build() {
+ let port = this.options_.port || portprober.findFreePort();
+ let args = Promise.resolve(port).then(port => {
+ return this.options_.args.concat('--port=' + port);
+ });
+
+ let options =
+ /** @type {!ServiceOptions} */
+ (Object.assign({}, this.options_, {args, port}));
+ return new DriverService(this.exe_, options);
+ }
+};
+
+
+/**
+ * Manages the life and death of the
+ * <a href="http://selenium-release.storage.googleapis.com/index.html">
+ * standalone Selenium server</a>.
+ */
+class SeleniumServer extends DriverService {
+ /**
+ * @param {string} jar Path to the Selenium server jar.
+ * @param {SeleniumServer.Options=} opt_options Configuration options for the
+ * server.
+ * @throws {Error} If the path to the Selenium jar is not specified or if an
+ * invalid port is specified.
+ */
+ constructor(jar, opt_options) {
+ if (!jar) {
+ throw Error('Path to the Selenium jar not specified');
+ }
+
+ var options = opt_options || {};
+
+ if (options.port < 0) {
+ throw Error('Port must be >= 0: ' + options.port);
+ }
+
+ let port = options.port || portprober.findFreePort();
+ let args = Promise.all([port, options.jvmArgs || [], options.args || []])
+ .then(resolved => {
+ let port = resolved[0];
+ let jvmArgs = resolved[1];
+ let args = resolved[2];
+ return jvmArgs.concat('-jar', jar, '-port', port).concat(args);
+ });
+
+ super('java', {
+ loopback: options.loopback,
+ port: port,
+ args: args,
+ path: '/wd/hub',
+ env: options.env,
+ stdio: options.stdio
+ });
+ }
+}
+
+
+/**
+ * Options for the Selenium server:
+ *
+ * - `loopback` - Whether the server should only be accessed on this host's
+ * loopback address.
+ * - `port` - The port to start the server on (must be > 0). If the port is
+ * provided as a promise, the service will wait for the promise to resolve
+ * before starting.
+ * - `args` - The arguments to pass to the service. If a promise is provided,
+ * the service will wait for it to resolve before starting.
+ * - `jvmArgs` - The arguments to pass to the JVM. If a promise is provided,
+ * the service will wait for it to resolve before starting.
+ * - `env` - The environment variables that should be visible to the server
+ * process. Defaults to inheriting the current process's environment.
+ * - `stdio` - IO configuration for the spawned server process. For more
+ * information, refer to the documentation of `child_process.spawn`.
+ *
+ * @typedef {{
+ * loopback: (boolean|undefined),
+ * port: (number|!promise.Promise<number>),
+ * args: !(Array<string>|promise.Promise<!Array<string>>),
+ * jvmArgs: (!Array<string>|
+ * !promise.Promise<!Array<string>>|
+ * undefined),
+ * env: (!Object<string, string>|undefined),
+ * stdio: (string|!Array<string|number|!stream.Stream|null|undefined>|
+ * undefined)
+ * }}
+ */
+SeleniumServer.Options;
+
+
+
+/**
+ * A {@link webdriver.FileDetector} that may be used when running
+ * against a remote
+ * [Selenium server](http://selenium-release.storage.googleapis.com/index.html).
+ *
+ * When a file path on the local machine running this script is entered with
+ * {@link webdriver.WebElement#sendKeys WebElement#sendKeys}, this file detector
+ * will transfer the specified file to the Selenium server's host; the sendKeys
+ * command will be updated to use the transfered file's path.
+ *
+ * __Note:__ This class depends on a non-standard command supported on the
+ * Java Selenium server. The file detector will fail if used with a server that
+ * only supports standard WebDriver commands (such as the ChromeDriver).
+ *
+ * @final
+ */
+class FileDetector extends input.FileDetector {
+ /**
+ * Prepares a `file` for use with the remote browser. If the provided path
+ * does not reference a normal file (i.e. it does not exist or is a
+ * directory), then the promise returned by this method will be resolved with
+ * the original file path. Otherwise, this method will upload the file to the
+ * remote server, which will return the file's path on the remote system so
+ * it may be referenced in subsequent commands.
+ *
+ * @override
+ */
+ handleFile(driver, file) {
+ return io.stat(file).then(function(stats) {
+ if (stats.isDirectory()) {
+ return file; // Not a valid file, return original input.
+ }
+
+ var zip = new AdmZip();
+ zip.addLocalFile(file);
+ // Stored compression, see https://en.wikipedia.org/wiki/Zip_(file_format)
+ zip.getEntries()[0].header.method = 0;
+
+ var command = new cmd.Command(cmd.Name.UPLOAD_FILE)
+ .setParameter('file', zip.toBuffer().toString('base64'));
+ return driver.schedule(command,
+ 'remote.FileDetector.handleFile(' + file + ')');
+ }, function(err) {
+ if (err.code === 'ENOENT') {
+ return file; // Not a file; return original input.
+ }
+ throw err;
+ });
+ }
+}
+
+
+// PUBLIC API
+
+exports.DriverService = DriverService;
+exports.FileDetector = FileDetector;
+exports.SeleniumServer = SeleniumServer;
+exports.ServiceOptions = ServiceOptions; // Exported for API docs.