# chrome.js

1 | // Licensed to the Software Freedom Conservancy (SFC) under one
2 | // or more contributor license agreements. See the NOTICE file
3 | // distributed with this work for additional information
4 | // regarding copyright ownership. The SFC licenses this file
5 | // to you under the Apache License, Version 2.0 (the
6 | // "License"); you may not use this file except in compliance
7 | // with the License. You may obtain a copy of the License at
8 | //
9 | // http://www.apache.org/licenses/LICENSE-2.0
10 | //
11 | // Unless required by applicable law or agreed to in writing,
12 | // software distributed under the License is distributed on an
13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14 | // KIND, either express or implied. See the License for the
15 | // specific language governing permissions and limitations
16 | // under the License.
17 |
18 | /**
19 | * @fileoverview Defines a {@linkplain Driver WebDriver} client for the Chrome
20 | * web browser. Before using this module, you must download the latest
21 | * [ChromeDriver release] and ensure it can be found on your system [PATH].
22 | *
23 | * There are three primary classes exported by this module:
24 | *
25 | * 1. {@linkplain ServiceBuilder}: configures the
26 | * {@link selenium-webdriver/remote.DriverService remote.DriverService}
27 | * that manages the [ChromeDriver] child process.
28 | *
29 | * 2. {@linkplain Options}: defines configuration options for each new Chrome
30 | * session, such as which {@linkplain Options#setProxy proxy} to use,
31 | * what {@linkplain Options#addExtensions extensions} to install, or
32 | * what {@linkplain Options#addArguments command-line switches} to use when
33 | * starting the browser.
34 | *
35 | * 3. {@linkplain Driver}: the WebDriver client; each new instance will control
36 | * a unique browser session with a clean user profile (unless otherwise
37 | * configured through the {@link Options} class).
38 | *
39 | * __Customizing the ChromeDriver Server__ <a id="custom-server"></a>
40 | *
41 | * By default, every Chrome session will use a single driver service, which is
42 | * started the first time a {@link Driver} instance is created and terminated
43 | * when this process exits. The default service will inherit its environment
44 | * from the current process and direct all output to /dev/null. You may obtain
45 | * a handle to this default service using
46 | * {@link #getDefaultService getDefaultService()} and change its configuration
47 | * with {@link #setDefaultService setDefaultService()}.
48 | *
49 | * You may also create a {@link Driver} with its own driver service. This is
50 | * useful if you need to capture the server's log output for a specific session:
51 | *
52 | * var chrome = require('selenium-webdriver/chrome');
53 | *
54 | * var service = new chrome.ServiceBuilder()
55 | * .loggingTo('/my/log/file.txt')
56 | * .enableVerboseLogging()
57 | * .build();
58 | *
59 | * var options = new chrome.Options();
60 | * // configure browser options ...
61 | *
62 | * var driver = new chrome.Driver(options, service);
63 | *
64 | * Users should only instantiate the {@link Driver} class directly when they
65 | * need a custom driver service configuration (as shown above). For normal
66 | * operation, users should start Chrome using the
67 | * {@link selenium-webdriver.Builder}.
68 | *
69 | * __Working with Android__ <a id="android"></a>
70 | *
71 | * The [ChromeDriver][android] supports running tests on the Chrome browser as
72 | * well as [WebView apps][webview] starting in Android 4.4 (KitKat). In order to
73 | * work with Android, you must first start the adb
74 | *
75 | * adb start-server
76 | *
77 | * By default, adb will start on port 5037. You may change this port, but this
78 | * will require configuring a [custom server](#custom-server) that will connect
79 | * to adb on the {@linkplain ServiceBuilder#setAdbPort correct port}:
80 | *
81 | * var service = new chrome.ServiceBuilder()
82 | * .setAdbPort(1234)
83 | * build();
84 | * // etc.
85 | *
86 | * The ChromeDriver may be configured to launch Chrome on Android using
87 | * {@link Options#androidChrome()}:
88 | *
89 | * var driver = new Builder()
90 | * .forBrowser('chrome')
91 | * .setChromeOptions(new chrome.Options().androidChrome())
92 | * .build();
93 | *
94 | * Alternatively, you can configure the ChromeDriver to launch an app with a
95 | * Chrome-WebView by setting the {@linkplain Options#androidActivity
96 | * androidActivity} option:
97 | *
98 | * var driver = new Builder()
99 | * .forBrowser('chrome')
100 | * .setChromeOptions(new chrome.Options()
101 | * .androidPackage('com.example')
102 | * .androidActivity('com.example.Activity'))
103 | * .build();
104 | *
105 | * [Refer to the ChromeDriver site] for more information on using the
106 | * [ChromeDriver with Android][android].
107 | *
108 | * [ChromeDriver]: https://sites.google.com/a/chromium.org/chromedriver/
109 | * [ChromeDriver release]: http://chromedriver.storage.googleapis.com/index.html
110 | * [PATH]: http://en.wikipedia.org/wiki/PATH_%28variable%29
111 | * [android]: https://sites.google.com/a/chromium.org/chromedriver/getting-started/getting-started---android
112 | * [webview]: https://developer.chrome.com/multidevice/webview/overview
113 | */
114 |
115 | 'use strict';
116 |
117 | var fs = require('fs'),
118 | util = require('util');
119 |
120 | var webdriver = require('./index'),
121 | executors = require('./executors'),
122 | http = require('./http'),
123 | io = require('./io'),
124 | portprober = require('./net/portprober'),
125 | remote = require('./remote');
126 |
127 |
128 | /**
129 | * Name of the ChromeDriver executable.
130 | * @type {string}
131 | * @const
132 | */
133 | var CHROMEDRIVER_EXE =
134 | process.platform === 'win32' ? 'chromedriver.exe' : 'chromedriver';
135 |
136 |
137 | /**
138 | * Custom command names supported by ChromeDriver.
139 | * @enum {string}
140 | */
141 | var Command = {
142 | LAUNCH_APP: 'launchApp'
143 | };
144 |
145 |
146 | /**
147 | * Creates a command executor with support for ChromeDriver's custom commands.
148 | * @param {!webdriver.promise.Promise<string>} url The server's URL.
149 | * @return {!webdriver.CommandExecutor} The new command executor.
150 | */
151 | function createExecutor(url) {
152 | return new executors.DeferredExecutor(url.then(function(url) {
153 | var client = new http.HttpClient(url);
154 | var executor = new http.Executor(client);
155 | executor.defineCommand(
156 | Command.LAUNCH_APP,
157 | 'POST', '/session/:sessionId/chromium/launch_app');
158 | return executor;
159 | }));
160 | }
161 |
162 |
163 | /**
164 | * Creates {@link selenium-webdriver/remote.DriverService} instances that manage
165 | * a [ChromeDriver](https://sites.google.com/a/chromium.org/chromedriver/)
166 | * server in a child process.
167 | *
168 | * @param {string=} opt_exe Path to the server executable to use. If omitted,
169 | * the builder will attempt to locate the chromedriver on the current
170 | * PATH.
171 | * @throws {Error} If provided executable does not exist, or the chromedriver
172 | * cannot be found on the PATH.
173 | * @constructor
174 | */
175 | var ServiceBuilder = function(opt_exe) {
176 | /** @private {string} */
177 | this.exe_ = opt_exe || io.findInPath(CHROMEDRIVER_EXE, true);
178 | if (!this.exe_) {
179 | throw Error(
180 | 'The ChromeDriver could not be found on the current PATH. Please ' +
181 | 'download the latest version of the ChromeDriver from ' +
182 | 'http://chromedriver.storage.googleapis.com/index.html and ensure ' +
183 | 'it can be found on your PATH.');
184 | }
185 |
186 | if (!fs.existsSync(this.exe_)) {
187 | throw Error('File does not exist: ' + this.exe_);
188 | }
189 |
190 | /** @private {!Array.<string>} */
191 | this.args_ = [];
192 | this.stdio_ = 'ignore';
193 | };
194 |
195 |
196 | /** @private {string} */
197 | ServiceBuilder.prototype.path_ = null;
198 |
199 | /** @private {number} */
200 | ServiceBuilder.prototype.port_ = 0;
201 |
202 |
203 | /** @private {(string|!Array.<string|number|!Stream|null|undefined>)} */
204 | ServiceBuilder.prototype.stdio_ = 'ignore';
205 |
206 |
207 | /** @private {Object.<string, string>} */
208 | ServiceBuilder.prototype.env_ = null;
209 |
210 |
211 | /**
212 | * Sets the port to start the ChromeDriver on.
213 | * @param {number} port The port to use, or 0 for any free port.
214 | * @return {!ServiceBuilder} A self reference.
215 | * @throws {Error} If the port is invalid.
216 | */
217 | ServiceBuilder.prototype.usingPort = function(port) {
218 | if (port < 0) {
219 | throw Error('port must be >= 0: ' + port);
220 | }
221 | this.port_ = port;
222 | return this;
223 | };
224 |
225 |
226 | /**
227 | * Sets which port adb is listening to. _The ChromeDriver will connect to adb
228 | * if an {@linkplain Options#androidPackage Android session} is requested, but
229 | * adb **must** be started beforehand._
230 | *
231 | * @param {number} port Which port adb is running on.
232 | * @return {!ServiceBuilder} A self reference.
233 | */
234 | ServiceBuilder.prototype.setAdbPort = function(port) {
235 | this.args_.push('--adb-port=' + port);
236 | return this;
237 | };
238 |
239 |
240 | /**
241 | * Sets the path of the log file the driver should log to. If a log file is
242 | * not specified, the driver will log to stderr.
243 | * @param {string} path Path of the log file to use.
244 | * @return {!ServiceBuilder} A self reference.
245 | */
246 | ServiceBuilder.prototype.loggingTo = function(path) {
247 | this.args_.push('--log-path=' + path);
248 | return this;
249 | };
250 |
251 |
252 | /**
253 | * Enables verbose logging.
254 | * @return {!ServiceBuilder} A self reference.
255 | */
256 | ServiceBuilder.prototype.enableVerboseLogging = function() {
257 | this.args_.push('--verbose');
258 | return this;
259 | };
260 |
261 |
262 | /**
263 | * Sets the number of threads the driver should use to manage HTTP requests.
264 | * By default, the driver will use 4 threads.
265 | * @param {number} n The number of threads to use.
266 | * @return {!ServiceBuilder} A self reference.
267 | */
268 | ServiceBuilder.prototype.setNumHttpThreads = function(n) {
269 | this.args_.push('--http-threads=' + n);
270 | return this;
271 | };
272 |
273 |
274 | /**
275 | * Sets the base path for WebDriver REST commands (e.g. "/wd/hub").
276 | * By default, the driver will accept commands relative to "/".
277 | * @param {string} path The base path to use.
278 | * @return {!ServiceBuilder} A self reference.
279 | */
280 | ServiceBuilder.prototype.setUrlBasePath = function(path) {
281 | this.args_.push('--url-base=' + path);
282 | this.path_ = path;
283 | return this;
284 | };
285 |
286 |
287 | /**
288 | * Defines the stdio configuration for the driver service. See
289 | * {@code child_process.spawn} for more information.
290 | * @param {(string|!Array.<string|number|!Stream|null|undefined>)} config The
291 | * configuration to use.
292 | * @return {!ServiceBuilder} A self reference.
293 | */
294 | ServiceBuilder.prototype.setStdio = function(config) {
295 | this.stdio_ = config;
296 | return this;
297 | };
298 |
299 |
300 | /**
301 | * Defines the environment to start the server under. This settings will be
302 | * inherited by every browser session started by the server.
303 | * @param {!Object.<string, string>} env The environment to use.
304 | * @return {!ServiceBuilder} A self reference.
305 | */
306 | ServiceBuilder.prototype.withEnvironment = function(env) {
307 | this.env_ = env;
308 | return this;
309 | };
310 |
311 |
312 | /**
313 | * Creates a new DriverService using this instance's current configuration.
314 | * @return {remote.DriverService} A new driver service using this instance's
315 | * current configuration.
316 | * @throws {Error} If the driver exectuable was not specified and a default
317 | * could not be found on the current PATH.
318 | */
319 | ServiceBuilder.prototype.build = function() {
320 | var port = this.port_ || portprober.findFreePort();
321 | var args = this.args_.concat(); // Defensive copy.
322 |
323 | return new remote.DriverService(this.exe_, {
324 | loopback: true,
325 | path: this.path_,
326 | port: port,
327 | args: webdriver.promise.when(port, function(port) {
328 | return args.concat('--port=' + port);
329 | }),
330 | env: this.env_,
331 | stdio: this.stdio_
332 | });
333 | };
334 |
335 |
336 | /** @type {remote.DriverService} */
337 | var defaultService = null;
338 |
339 |
340 | /**
341 | * Sets the default service to use for new ChromeDriver instances.
342 | * @param {!remote.DriverService} service The service to use.
343 | * @throws {Error} If the default service is currently running.
344 | */
345 | function setDefaultService(service) {
346 | if (defaultService && defaultService.isRunning()) {
347 | throw Error(
348 | 'The previously configured ChromeDriver service is still running. ' +
349 | 'You must shut it down before you may adjust its configuration.');
350 | }
351 | defaultService = service;
352 | }
353 |
354 |
355 | /**
356 | * Returns the default ChromeDriver service. If such a service has not been
357 | * configured, one will be constructed using the default configuration for
358 | * a ChromeDriver executable found on the system PATH.
359 | * @return {!remote.DriverService} The default ChromeDriver service.
360 | */
361 | function getDefaultService() {
362 | if (!defaultService) {
363 | defaultService = new ServiceBuilder().build();
364 | }
365 | return defaultService;
366 | }
367 |
368 |
369 | /**
370 | * @type {string}
371 | * @const
372 | */
373 | var OPTIONS_CAPABILITY_KEY = 'chromeOptions';
374 |
375 |
376 | /**
377 | * Class for managing ChromeDriver specific options.
378 | * @constructor
379 | * @extends {webdriver.Serializable}
380 | */
381 | var Options = function() {
382 | webdriver.Serializable.call(this);
383 |
384 | /** @private {!Object} */
385 | this.options_ = {};
386 |
387 | /** @private {!Array.<(string|!Buffer)>} */
388 | this.extensions_ = [];
389 |
390 | /** @private {?webdriver.logging.Preferences} */
391 | this.logPrefs_ = null;
392 |
393 | /** @private {?webdriver.ProxyConfig} */
394 | this.proxy_ = null;
395 | };
396 | util.inherits(Options, webdriver.Serializable);
397 |
398 |
399 | /**
400 | * Extracts the ChromeDriver specific options from the given capabilities
401 | * object.
402 | * @param {!webdriver.Capabilities} capabilities The capabilities object.
403 | * @return {!Options} The ChromeDriver options.
404 | */
405 | Options.fromCapabilities = function(capabilities) {
406 | var options = new Options();
407 |
408 | var o = capabilities.get(OPTIONS_CAPABILITY_KEY);
409 | if (o instanceof Options) {
410 | options = o;
411 | } else if (o) {
412 | options.
413 | addArguments(o.args || []).
414 | addExtensions(o.extensions || []).
415 | detachDriver(o.detach).
416 | excludeSwitches(o.excludeSwitches || []).
417 | setChromeBinaryPath(o.binary).
418 | setChromeLogFile(o.logPath).
419 | setChromeMinidumpPath(o.minidumpPath).
420 | setLocalState(o.localState).
421 | setMobileEmulation(o.mobileEmulation).
422 | setUserPreferences(o.prefs).
423 | setPerfLoggingPrefs(o.perfLoggingPrefs);
424 | }
425 |
426 | if (capabilities.has(webdriver.Capability.PROXY)) {
427 | options.setProxy(capabilities.get(webdriver.Capability.PROXY));
428 | }
429 |
430 | if (capabilities.has(webdriver.Capability.LOGGING_PREFS)) {
431 | options.setLoggingPrefs(
432 | capabilities.get(webdriver.Capability.LOGGING_PREFS));
433 | }
434 |
435 | return options;
436 | };
437 |
438 |
439 | /**
440 | * Add additional command line arguments to use when launching the Chrome
441 | * browser. Each argument may be specified with or without the "--" prefix
442 | * (e.g. "--foo" and "foo"). Arguments with an associated value should be
443 | * delimited by an "=": "foo=bar".
444 | * @param {...(string|!Array.<string>)} var_args The arguments to add.
445 | * @return {!Options} A self reference.
446 | */
447 | Options.prototype.addArguments = function(var_args) {
448 | var args = this.options_.args || [];
449 | args = args.concat.apply(args, arguments);
450 | if (args.length) {
451 | this.options_.args = args;
452 | }
453 | return this;
454 | };
455 |
456 |
457 | /**
458 | * List of Chrome command line switches to exclude that ChromeDriver by default
459 | * passes when starting Chrome. Do not prefix switches with "--".
460 | *
461 | * @param {...(string|!Array<string>)} var_args The switches to exclude.
462 | * @return {!Options} A self reference.
463 | */
464 | Options.prototype.excludeSwitches = function(var_args) {
465 | var switches = this.options_.excludeSwitches || [];
466 | switches = switches.concat.apply(switches, arguments);
467 | if (switches.length) {
468 | this.options_.excludeSwitches = switches;
469 | }
470 | return this;
471 | };
472 |
473 |
474 | /**
475 | * Add additional extensions to install when launching Chrome. Each extension
476 | * should be specified as the path to the packed CRX file, or a Buffer for an
477 | * extension.
478 | * @param {...(string|!Buffer|!Array.<(string|!Buffer)>)} var_args The
479 | * extensions to add.
480 | * @return {!Options} A self reference.
481 | */
482 | Options.prototype.addExtensions = function(var_args) {
483 | this.extensions_ = this.extensions_.concat.apply(this.extensions_, arguments);
484 | return this;
485 | };
486 |
487 |
488 | /**
489 | * Sets the path to the Chrome binary to use. On Mac OS X, this path should
490 | * reference the actual Chrome executable, not just the application binary
491 | * (e.g. "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome").
492 | *
493 | * The binary path be absolute or relative to the chromedriver server
494 | * executable, but it must exist on the machine that will launch Chrome.
495 | *
496 | * @param {string} path The path to the Chrome binary to use.
497 | * @return {!Options} A self reference.
498 | */
499 | Options.prototype.setChromeBinaryPath = function(path) {
500 | this.options_.binary = path;
501 | return this;
502 | };
503 |
504 |
505 | /**
506 | * Sets whether to leave the started Chrome browser running if the controlling
507 | * ChromeDriver service is killed before {@link webdriver.WebDriver#quit()} is
508 | * called.
509 | * @param {boolean} detach Whether to leave the browser running if the
510 | * chromedriver service is killed before the session.
511 | * @return {!Options} A self reference.
512 | */
513 | Options.prototype.detachDriver = function(detach) {
514 | this.options_.detach = detach;
515 | return this;
516 | };
517 |
518 |
519 | /**
520 | * Sets the user preferences for Chrome's user profile. See the "Preferences"
521 | * file in Chrome's user data directory for examples.
522 | * @param {!Object} prefs Dictionary of user preferences to use.
523 | * @return {!Options} A self reference.
524 | */
525 | Options.prototype.setUserPreferences = function(prefs) {
526 | this.options_.prefs = prefs;
527 | return this;
528 | };
529 |
530 |
531 | /**
532 | * Sets the logging preferences for the new session.
533 | * @param {!webdriver.logging.Preferences} prefs The logging preferences.
534 | * @return {!Options} A self reference.
535 | */
536 | Options.prototype.setLoggingPrefs = function(prefs) {
537 | this.logPrefs_ = prefs;
538 | return this;
539 | };
540 |
541 |
542 | /**
543 | * Sets the performance logging preferences. Options include:
544 | *
545 | * - `enableNetwork`: Whether or not to collect events from Network domain.
546 | * - `enablePage`: Whether or not to collect events from Page domain.
547 | * - `enableTimeline`: Whether or not to collect events from Timeline domain.
548 | * Note: when tracing is enabled, Timeline domain is implicitly disabled,
549 | * unless `enableTimeline` is explicitly set to true.
550 | * - `tracingCategories`: A comma-separated string of Chrome tracing categories
551 | * for which trace events should be collected. An unspecified or empty
552 | * string disables tracing.
553 | * - `bufferUsageReportingInterval`: The requested number of milliseconds
554 | * between DevTools trace buffer usage events. For example, if 1000, then
555 | * once per second, DevTools will report how full the trace buffer is. If a
556 | * report indicates the buffer usage is 100%, a warning will be issued.
557 | *
558 | * @param {{enableNetwork: boolean,
559 | * enablePage: boolean,
560 | * enableTimeline: boolean,
561 | * tracingCategories: string,
562 | * bufferUsageReportingInterval: number}} prefs The performance
563 | * logging preferences.
564 | * @return {!Options} A self reference.
565 | */
566 | Options.prototype.setPerfLoggingPrefs = function(prefs) {
567 | this.options_.perfLoggingPrefs = prefs;
568 | return this;
569 | };
570 |
571 |
572 | /**
573 | * Sets preferences for the "Local State" file in Chrome's user data
574 | * directory.
575 | * @param {!Object} state Dictionary of local state preferences.
576 | * @return {!Options} A self reference.
577 | */
578 | Options.prototype.setLocalState = function(state) {
579 | this.options_.localState = state;
580 | return this;
581 | };
582 |
583 |
584 | /**
585 | * Sets the name of the activity hosting a Chrome-based Android WebView. This
586 | * option must be set to connect to an [Android WebView](
587 | * https://sites.google.com/a/chromium.org/chromedriver/getting-started/getting-started---android)
588 | *
589 | * @param {string} name The activity name.
590 | * @return {!Options} A self reference.
591 | */
592 | Options.prototype.androidActivity = function(name) {
593 | this.options_.androidActivity = name;
594 | return this;
595 | };
596 |
597 |
598 | /**
599 | * Sets the device serial number to connect to via ADB. If not specified, the
600 | * ChromeDriver will select an unused device at random. An error will be
601 | * returned if all devices already have active sessions.
602 | *
603 | * @param {string} serial The device serial number to connect to.
604 | * @return {!Options} A self reference.
605 | */
606 | Options.prototype.androidDeviceSerial = function(serial) {
607 | this.options_.androidDeviceSerial = serial;
608 | return this;
609 | };
610 |
611 |
612 | /**
613 | * Configures the ChromeDriver to launch Chrome on Android via adb. This
614 | * function is shorthand for
615 | * {@link #androidPackage options.androidPackage('com.android.chrome')}.
616 | * @return {!Options} A self reference.
617 | */
618 | Options.prototype.androidChrome = function() {
619 | return this.androidPackage('com.android.chrome');
620 | };
621 |
622 |
623 | /**
624 | * Sets the package name of the Chrome or WebView app.
625 | *
626 | * @param {?string} pkg The package to connect to, or `null` to disable Android
627 | * and switch back to using desktop Chrome.
628 | * @return {!Options} A self reference.
629 | */
630 | Options.prototype.androidPackage = function(pkg) {
631 | this.options_.androidPackage = pkg;
632 | return this;
633 | };
634 |
635 |
636 | /**
637 | * Sets the process name of the Activity hosting the WebView (as given by `ps`).
638 | * If not specified, the process name is assumed to be the same as
639 | * {@link #androidPackage}.
640 | *
641 | * @param {string} processName The main activity name.
642 | * @return {!Options} A self reference.
643 | */
644 | Options.prototype.androidProcess = function(processName) {
645 | this.options_.androidProcess = processName;
646 | return this;
647 | };
648 |
649 |
650 | /**
651 | * Sets whether to connect to an already-running instead of the specified
652 | * {@linkplain #androidProcess app} instead of launching the app with a clean
653 | * data directory.
654 | *
655 | * @param {boolean} useRunning Whether to connect to a running instance.
656 | * @return {!Options} A self reference.
657 | */
658 | Options.prototype.androidUseRunningApp = function(useRunning) {
659 | this.options_.androidUseRunningApp = useRunning;
660 | return this;
661 | };
662 |
663 |
664 | /**
665 | * Sets the path to Chrome's log file. This path should exist on the machine
666 | * that will launch Chrome.
667 | * @param {string} path Path to the log file to use.
668 | * @return {!Options} A self reference.
669 | */
670 | Options.prototype.setChromeLogFile = function(path) {
671 | this.options_.logPath = path;
672 | return this;
673 | };
674 |
675 |
676 | /**
677 | * Sets the directory to store Chrome minidumps in. This option is only
678 | * supported when ChromeDriver is running on Linux.
679 | * @param {string} path The directory path.
680 | * @return {!Options} A self reference.
681 | */
682 | Options.prototype.setChromeMinidumpPath = function(path) {
683 | this.options_.minidumpPath = path;
684 | return this;
685 | };
686 |
687 |
688 | /**
689 | * Configures Chrome to emulate a mobile device. For more information, refer to
690 | * the ChromeDriver project page on [mobile emulation][em]. Configuration
691 | * options include:
692 | *
693 | * - `deviceName`: The name of a pre-configured [emulated device][devem]
694 | * - `width`: screen width, in pixels
695 | * - `height`: screen height, in pixels
696 | * - `pixelRatio`: screen pixel ratio
697 | *
698 | * __Example 1: Using a Pre-configured Device__
699 | *
700 | * var options = new chrome.Options().setMobileEmulation(
701 | * {deviceName: 'Google Nexus 5'});
702 | *
703 | * var driver = new chrome.Driver(options);
704 | *
705 | * __Example 2: Using Custom Screen Configuration__
706 | *
707 | * var options = new chrome.Options().setMobileEmulation({
708 | * width: 360,
709 | * height: 640,
710 | * pixelRatio: 3.0
711 | * });
712 | *
713 | * var driver = new chrome.Driver(options);
714 | *
715 | *
716 | * [em]: https://sites.google.com/a/chromium.org/chromedriver/mobile-emulation
717 | * [devem]: https://developer.chrome.com/devtools/docs/device-mode
718 | *
719 | * @param {?({deviceName: string}|
720 | * {width: number, height: number, pixelRatio: number})} config The
721 | * mobile emulation configuration, or `null` to disable emulation.
722 | * @return {!Options} A self reference.
723 | */
724 | Options.prototype.setMobileEmulation = function(config) {
725 | this.options_.mobileEmulation = config;
726 | return this;
727 | };
728 |
729 |
730 | /**
731 | * Sets the proxy settings for the new session.
732 | * @param {webdriver.ProxyConfig} proxy The proxy configuration to use.
733 | * @return {!Options} A self reference.
734 | */
735 | Options.prototype.setProxy = function(proxy) {
736 | this.proxy_ = proxy;
737 | return this;
738 | };
739 |
740 |
741 | /**
742 | * Converts this options instance to a {@link webdriver.Capabilities} object.
743 | * @param {webdriver.Capabilities=} opt_capabilities The capabilities to merge
744 | * these options into, if any.
745 | * @return {!webdriver.Capabilities} The capabilities.
746 | */
747 | Options.prototype.toCapabilities = function(opt_capabilities) {
748 | var capabilities = opt_capabilities || webdriver.Capabilities.chrome();
749 | capabilities.
750 | set(webdriver.Capability.PROXY, this.proxy_).
751 | set(webdriver.Capability.LOGGING_PREFS, this.logPrefs_).
752 | set(OPTIONS_CAPABILITY_KEY, this);
753 | return capabilities;
754 | };
755 |
756 |
757 | /**
758 | * Converts this instance to its JSON wire protocol representation. Note this
759 | * function is an implementation not intended for general use.
760 | * @return {{args: !Array.<string>,
761 | * binary: (string|undefined),
762 | * detach: boolean,
763 | * extensions: !Array.<(string|!webdriver.promise.Promise.<string>)>,
764 | * localState: (Object|undefined),
765 | * logPath: (string|undefined),
766 | * prefs: (Object|undefined)}} The JSON wire protocol representation
767 | * of this instance.
768 | * @override
769 | */
770 | Options.prototype.serialize = function() {
771 | var json = {};
772 | for (var key in this.options_) {
773 | if (this.options_[key] != null) {
774 | json[key] = this.options_[key];
775 | }
776 | }
777 | if (this.extensions_.length) {
778 | json.extensions = this.extensions_.map(function(extension) {
779 | if (Buffer.isBuffer(extension)) {
780 | return extension.toString('base64');
781 | }
782 | return webdriver.promise.checkedNodeCall(
783 | fs.readFile, extension, 'base64');
784 | });
785 | }
786 | return json;
787 | };
788 |
789 |
790 | /**
791 | * Creates a new WebDriver client for Chrome.
792 | *
793 | * @param {(webdriver.Capabilities|Options)=} opt_config The configuration
794 | * options.
795 | * @param {remote.DriverService=} opt_service The session to use; will use
796 | * the {@linkplain #getDefaultService default service} by default.
797 | * @param {webdriver.promise.ControlFlow=} opt_flow The control flow to use, or
798 | * {@code null} to use the currently active flow.
799 | * @constructor
800 | * @extends {webdriver.WebDriver}
801 | */
802 | var Driver = function(opt_config, opt_service, opt_flow) {
803 | var service = opt_service || getDefaultService();
804 | var executor = createExecutor(service.start());
805 |
806 | var capabilities =
807 | opt_config instanceof Options ? opt_config.toCapabilities() :
808 | (opt_config || webdriver.Capabilities.chrome());
809 |
810 | var driver = webdriver.WebDriver.createSession(
811 | executor, capabilities, opt_flow);
812 |
813 | webdriver.WebDriver.call(
814 | this, driver.getSession(), executor, driver.controlFlow());
815 | };
816 | util.inherits(Driver, webdriver.WebDriver);
817 |
818 |
819 | /**
820 | * This function is a no-op as file detectors are not supported by this
821 | * implementation.
822 | * @override
823 | */
824 | Driver.prototype.setFileDetector = function() {
825 | };
826 |
827 |
828 | /**
829 | * Schedules a command to launch Chrome App with given ID.
830 | * @param {string} id ID of the App to launch.
831 | * @return {!webdriver.promise.Promise<void>} A promise that will be resolved
832 | * when app is launched.
833 | */
834 | Driver.prototype.launchApp = function(id) {
835 | return this.schedule(
836 | new webdriver.Command(Command.LAUNCH_APP).setParameter('id', id),
837 | 'Driver.launchApp()');
838 | };
839 |
840 |
841 | // PUBLIC API
842 |
843 |
844 | exports.Driver = Driver;
845 | exports.Options = Options;
846 | exports.ServiceBuilder = ServiceBuilder;
847 | exports.getDefaultService = getDefaultService;
848 | exports.setDefaultService = setDefaultService;