UNPKG

13.3 kBJavaScriptView Raw
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 Microsoft's
20 * Internet Explorer. Before using the IEDriver, you must download the latest
21 * [IEDriverServer](http://selenium-release.storage.googleapis.com/index.html)
22 * and place it on your
23 * [PATH](http://en.wikipedia.org/wiki/PATH_%28variable%29). You must also apply
24 * the system configuration outlined on the Selenium project
25 * [wiki](https://github.com/SeleniumHQ/selenium/wiki/InternetExplorerDriver)
26 */
27
28'use strict';
29
30const fs = require('fs');
31const util = require('util');
32
33const http = require('./http');
34const io = require('./io');
35const portprober = require('./net/portprober');
36const promise = require('./lib/promise');
37const remote = require('./remote');
38const webdriver = require('./lib/webdriver');
39const {Browser, Capabilities, Capability} = require('./lib/capabilities');
40
41
42const IEDRIVER_EXE = 'IEDriverServer.exe';
43
44
45
46/**
47 * IEDriverServer logging levels.
48 * @enum {string}
49 */
50const Level = {
51 FATAL: 'FATAL',
52 ERROR: 'ERROR',
53 WARN: 'WARN',
54 INFO: 'INFO',
55 DEBUG: 'DEBUG',
56 TRACE: 'TRACE'
57};
58
59
60
61/**
62 * Option keys:
63 * https://github.com/SeleniumHQ/selenium/wiki/DesiredCapabilities#ie-specific
64 * @enum {string}
65 */
66const Key = {
67 IGNORE_PROTECTED_MODE_SETTINGS: 'ignoreProtectedModeSettings',
68 IGNORE_ZOOM_SETTING: 'ignoreZoomSetting',
69 INITIAL_BROWSER_URL: 'initialBrowserUrl',
70 ENABLE_PERSISTENT_HOVER: 'enablePersistentHover',
71 ENABLE_ELEMENT_CACHE_CLEANUP: 'enableElementCacheCleanup',
72 REQUIRE_WINDOW_FOCUS: 'requireWindowFocus',
73 BROWSER_ATTACH_TIMEOUT: 'browserAttachTimeout',
74 FORCE_CREATE_PROCESS: 'ie.forceCreateProcessApi',
75 BROWSER_COMMAND_LINE_SWITCHES: 'ie.browserCommandLineSwitches',
76 USE_PER_PROCESS_PROXY: 'ie.usePerProcessProxy',
77 ENSURE_CLEAN_SESSION: 'ie.ensureCleanSession',
78 LOG_FILE: 'logFile',
79 LOG_LEVEL: 'logLevel',
80 HOST: 'host',
81 EXTRACT_PATH: 'extractPath',
82 SILENT: 'silent'
83};
84
85
86/**
87 * Class for managing IEDriver specific options.
88 */
89class Options extends Capabilities {
90 /**
91 * @param {(Capabilities|Map<string, ?>|Object)=} other Another set of
92 * capabilities to initialize this instance from.
93 */
94 constructor(other = undefined) {
95 super(other);
96 this.setBrowserName(Browser.IE);
97 }
98
99 /**
100 * Whether to disable the protected mode settings check when the session is
101 * created. Disbling this setting may lead to significant instability as the
102 * browser may become unresponsive/hang. Only "best effort" support is provided
103 * when using this capability.
104 *
105 * For more information, refer to the IEDriver's
106 * [required system configuration](http://goo.gl/eH0Yi3).
107 *
108 * @param {boolean} ignoreSettings Whether to ignore protected mode settings.
109 * @return {!Options} A self reference.
110 */
111 introduceFlakinessByIgnoringProtectedModeSettings(ignoreSettings) {
112 this.set(Key.IGNORE_PROTECTED_MODE_SETTINGS, !!ignoreSettings);
113 return this;
114 }
115
116 /**
117 * Indicates whether to skip the check that the browser's zoom level is set to
118 * 100%.
119 *
120 * @param {boolean} ignore Whether to ignore the browser's zoom level settings.
121 * @return {!Options} A self reference.
122 */
123 ignoreZoomSetting(ignore) {
124 this.set(Key.IGNORE_ZOOM_SETTING, !!ignore);
125 return this;
126 }
127
128 /**
129 * Sets the initial URL loaded when IE starts. This is intended to be used with
130 * {@link #ignoreProtectedModeSettings} to allow the user to initialize IE in
131 * the proper Protected Mode zone. Setting this option may cause browser
132 * instability or flaky and unresponsive code. Only "best effort" support is
133 * provided when using this option.
134 *
135 * @param {string} url The initial browser URL.
136 * @return {!Options} A self reference.
137 */
138 initialBrowserUrl(url) {
139 this.set(Key.INITIAL_BROWSER_URL, url);
140 return this;
141 }
142
143 /**
144 * Configures whether to enable persistent mouse hovering (true by default).
145 * Persistent hovering is achieved by continuously firing mouse over events at
146 * the last location the mouse cursor has been moved to.
147 *
148 * @param {boolean} enable Whether to enable persistent hovering.
149 * @return {!Options} A self reference.
150 */
151 enablePersistentHover(enable) {
152 this.set(Key.ENABLE_PERSISTENT_HOVER, !!enable);
153 return this;
154 }
155
156 /**
157 * Configures whether the driver should attempt to remove obsolete
158 * {@linkplain webdriver.WebElement WebElements} from its internal cache on
159 * page navigation (true by default). Disabling this option will cause the
160 * driver to run with a larger memory footprint.
161 *
162 * @param {boolean} enable Whether to enable element reference cleanup.
163 * @return {!Options} A self reference.
164 */
165 enableElementCacheCleanup(enable) {
166 this.set(Key.ENABLE_ELEMENT_CACHE_CLEANUP, !!enable);
167 return this;
168 }
169
170 /**
171 * Configures whether to require the IE window to have input focus before
172 * performing any user interactions (i.e. mouse or keyboard events). This
173 * option is disabled by default, but delivers much more accurate interaction
174 * events when enabled.
175 *
176 * @param {boolean} require Whether to require window focus.
177 * @return {!Options} A self reference.
178 */
179 requireWindowFocus(require) {
180 this.set(Key.REQUIRE_WINDOW_FOCUS, !!require);
181 return this;
182 }
183
184 /**
185 * Configures the timeout, in milliseconds, that the driver will attempt to
186 * located and attach to a newly opened instance of Internet Explorer. The
187 * default is zero, which indicates waiting indefinitely.
188 *
189 * @param {number} timeout How long to wait for IE.
190 * @return {!Options} A self reference.
191 */
192 browserAttachTimeout(timeout) {
193 this.set(Key.BROWSER_ATTACH_TIMEOUT, Math.max(timeout, 0));
194 return this;
195 }
196
197 /**
198 * Configures whether to launch Internet Explorer using the CreateProcess API.
199 * If this option is not specified, IE is launched using IELaunchURL, if
200 * available. For IE 8 and above, this option requires the TabProcGrowth
201 * registry value to be set to 0.
202 *
203 * @param {boolean} force Whether to use the CreateProcess API.
204 * @return {!Options} A self reference.
205 */
206 forceCreateProcessApi(force) {
207 this.set(Key.FORCE_CREATE_PROCESS, !!force);
208 return this;
209 }
210
211 /**
212 * Specifies command-line switches to use when launching Internet Explorer.
213 * This is only valid when used with {@link #forceCreateProcessApi}.
214 *
215 * @param {...(string|!Array.<string>)} args The arguments to add.
216 * @return {!Options} A self reference.
217 */
218 addArguments(...args) {
219 let current = this.get(Key.BROWSER_COMMAND_LINE_SWITCHES) || [];
220 this.set(
221 Key.BROWSER_COMMAND_LINE_SWITCHES,
222 current.concat.apply(current, args));
223 return this;
224 }
225
226 /**
227 * Configures whether proxies should be configured on a per-process basis. If
228 * not set, setting a {@linkplain #setProxy proxy} will configure the system
229 * proxy. The default behavior is to use the system proxy.
230 *
231 * @param {boolean} enable Whether to enable per-process proxy settings.
232 * @return {!Options} A self reference.
233 */
234 usePerProcessProxy(enable) {
235 this.set(Key.USE_PER_PROCESS_PROXY, !!enable);
236 return this;
237 }
238
239 /**
240 * Configures whether to clear the cache, cookies, history, and saved form data
241 * before starting the browser. _Using this capability will clear session data
242 * for all running instances of Internet Explorer, including those started
243 * manually._
244 *
245 * @param {boolean} cleanSession Whether to clear all session data on startup.
246 * @return {!Options} A self reference.
247 */
248 ensureCleanSession(cleanSession) {
249 this.set(Key.ENSURE_CLEAN_SESSION, !!cleanSession);
250 return this;
251 }
252
253 /**
254 * Sets the path to the log file the driver should log to.
255 * @param {string} file The log file path.
256 * @return {!Options} A self reference.
257 */
258 setLogFile(file) {
259 this.set(Key.LOG_FILE, file);
260 return this;
261 }
262
263 /**
264 * Sets the IEDriverServer's logging {@linkplain Level level}.
265 * @param {Level} level The logging level.
266 * @return {!Options} A self reference.
267 */
268 setLogLevel(level) {
269 this.set(Key.LOG_LEVEL, level);
270 return this;
271 }
272
273 /**
274 * Sets the IP address of the driver's host adapter.
275 * @param {string} host The IP address to use.
276 * @return {!Options} A self reference.
277 */
278 setHost(host) {
279 this.set(Key.HOST, host);
280 return this;
281 }
282
283 /**
284 * Sets the path of the temporary data directory to use.
285 * @param {string} path The log file path.
286 * @return {!Options} A self reference.
287 */
288 setExtractPath(path) {
289 this.set(Key.EXTRACT_PATH, path);
290 return this;
291 }
292
293 /**
294 * Sets whether the driver should start in silent mode.
295 * @param {boolean} silent Whether to run in silent mode.
296 * @return {!Options} A self reference.
297 */
298 silent(silent) {
299 this.set(Key.SILENT, silent);
300 return this;
301 }
302}
303
304
305/**
306 * _Synchronously_ attempts to locate the IE driver executable on the current
307 * system.
308 *
309 * @return {?string} the located executable, or `null`.
310 */
311function locateSynchronously() {
312 return process.platform === 'win32'
313 ? io.findInPath(IEDRIVER_EXE, true) : null;
314}
315
316
317function createServiceFromCapabilities(capabilities) {
318 if (process.platform !== 'win32') {
319 throw Error(
320 'The IEDriver may only be used on Windows, but you appear to be on ' +
321 process.platform + '. Did you mean to run against a remote ' +
322 'WebDriver server?');
323 }
324
325 let exe = locateSynchronously();
326 if (!exe || !fs.existsSync(exe)) {
327 throw Error(
328 `${IEDRIVER_EXE} could not be found on the current PATH. Please ` +
329 `download the latest version of ${IEDRIVER_EXE} from ` +
330 'http://selenium-release.storage.googleapis.com/index.html and ' +
331 'ensure it can be found on your system PATH.');
332 }
333
334 var args = [];
335 if (capabilities.has(Key.HOST)) {
336 args.push('--host=' + capabilities.get(Key.HOST));
337 }
338 if (capabilities.has(Key.LOG_FILE)) {
339 args.push('--log-file=' + capabilities.get(Key.LOG_FILE));
340 }
341 if (capabilities.has(Key.LOG_LEVEL)) {
342 args.push('--log-level=' + capabilities.get(Key.LOG_LEVEL));
343 }
344 if (capabilities.has(Key.EXTRACT_PATH)) {
345 args.push('--extract-path=' + capabilities.get(Key.EXTRACT_PATH));
346 }
347 if (capabilities.get(Key.SILENT)) {
348 args.push('--silent');
349 }
350
351 var port = portprober.findFreePort();
352 return new remote.DriverService(exe, {
353 loopback: true,
354 port: port,
355 args: port.then(function(port) {
356 return args.concat('--port=' + port);
357 }),
358 stdio: 'ignore'
359 });
360}
361
362
363/**
364 * Creates {@link selenium-webdriver/remote.DriverService} instances that manage
365 * an [IEDriverServer](https://github.com/SeleniumHQ/selenium/wiki/InternetExplorerDriver)
366 * server in a child process.
367 */
368class ServiceBuilder extends remote.DriverService.Builder {
369 /**
370 * @param {string=} opt_exe Path to the server executable to use. If omitted,
371 * the builder will attempt to locate the IEDriverServer on the system PATH.
372 */
373 constructor(opt_exe) {
374 super(opt_exe || IEDRIVER_EXE);
375 this.setLoopback(true); // Required.
376 }
377}
378
379
380/**
381 * A WebDriver client for Microsoft's Internet Explorer.
382 */
383class Driver extends webdriver.WebDriver {
384 /**
385 * Creates a new session for Microsoft's Internet Explorer.
386 *
387 * @param {(Capabilities|Options)=} options The configuration options.
388 * @param {(remote.DriverService)=} opt_service The `DriverService` to use
389 * to start the IEDriverServer in a child process, optionally.
390 * @return {!Driver} A new driver instance.
391 */
392 static createSession(options, opt_service) {
393 options = options || new Options();
394
395 let service;
396
397 if (opt_service instanceof remote.DriverService) {
398 service = opt_service;
399 } else {
400 service = createServiceFromCapabilities(options);
401 }
402
403 let client = service.start().then(url => new http.HttpClient(url));
404 let executor = new http.Executor(client);
405
406 return /** @type {!Driver} */(super.createSession(
407 executor, options, () => service.kill()));
408 }
409
410 /**
411 * This function is a no-op as file detectors are not supported by this
412 * implementation.
413 * @override
414 */
415 setFileDetector() {}
416}
417
418
419// PUBLIC API
420
421
422exports.Driver = Driver;
423exports.Options = Options;
424exports.Level = Level;
425exports.ServiceBuilder = ServiceBuilder;
426exports.locateSynchronously = locateSynchronously;
427