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
117var fs = require('fs'),
118 util = require('util');
119
120var 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 */
133var CHROMEDRIVER_EXE =
134 process.platform === 'win32' ? 'chromedriver.exe' : 'chromedriver';
135
136
137/**
138 * Custom command names supported by ChromeDriver.
139 * @enum {string}
140 */
141var 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 */
151function 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 */
175var 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} */
197ServiceBuilder.prototype.path_ = null;
198
199/** @private {number} */
200ServiceBuilder.prototype.port_ = 0;
201
202
203/** @private {(string|!Array.<string|number|!Stream|null|undefined>)} */
204ServiceBuilder.prototype.stdio_ = 'ignore';
205
206
207/** @private {Object.<string, string>} */
208ServiceBuilder.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 */
217ServiceBuilder.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 */
234ServiceBuilder.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 */
246ServiceBuilder.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 */
256ServiceBuilder.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 */
268ServiceBuilder.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 */
280ServiceBuilder.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 */
294ServiceBuilder.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 */
306ServiceBuilder.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 */
319ServiceBuilder.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} */
337var 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 */
345function 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 */
361function getDefaultService() {
362 if (!defaultService) {
363 defaultService = new ServiceBuilder().build();
364 }
365 return defaultService;
366}
367
368
369/**
370 * @type {string}
371 * @const
372 */
373var OPTIONS_CAPABILITY_KEY = 'chromeOptions';
374
375
376/**
377 * Class for managing ChromeDriver specific options.
378 * @constructor
379 * @extends {webdriver.Serializable}
380 */
381var 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};
396util.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 */
405Options.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 */
447Options.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 */
464Options.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 */
482Options.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 */
499Options.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 */
513Options.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 */
525Options.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 */
536Options.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 */
566Options.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 */
578Options.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 */
592Options.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 */
606Options.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 */
618Options.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 */
630Options.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 */
644Options.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 */
658Options.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 */
670Options.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 */
682Options.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 */
724Options.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 */
735Options.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 */
747Options.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 */
770Options.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 */
802var 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};
816util.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 */
824Driver.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 */
834Driver.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
844exports.Driver = Driver;
845exports.Options = Options;
846exports.ServiceBuilder = ServiceBuilder;
847exports.getDefaultService = getDefaultService;
848exports.setDefaultService = setDefaultService;