441 lines
14 KiB
JavaScript
441 lines
14 KiB
JavaScript
// 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.
|
|
|
|
/**
|
|
* @fileoverview Defines a {@linkplain Driver WebDriver} client for Microsoft's
|
|
* Internet Explorer. Before using the IEDriver, you must download the latest
|
|
* [IEDriverServer](http://selenium-release.storage.googleapis.com/index.html)
|
|
* and place it on your
|
|
* [PATH](http://en.wikipedia.org/wiki/PATH_%28variable%29). You must also apply
|
|
* the system configuration outlined on the Selenium project
|
|
* [wiki](https://github.com/SeleniumHQ/selenium/wiki/InternetExplorerDriver)
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
const fs = require('fs'),
|
|
util = require('util');
|
|
|
|
const http = require('./http'),
|
|
io = require('./io'),
|
|
capabilities = require('./lib/capabilities'),
|
|
promise = require('./lib/promise'),
|
|
webdriver = require('./lib/webdriver'),
|
|
portprober = require('./net/portprober'),
|
|
remote = require('./remote');
|
|
|
|
|
|
const IEDRIVER_EXE = 'IEDriverServer.exe';
|
|
|
|
|
|
|
|
/**
|
|
* IEDriverServer logging levels.
|
|
* @enum {string}
|
|
*/
|
|
const Level = {
|
|
FATAL: 'FATAL',
|
|
ERROR: 'ERROR',
|
|
WARN: 'WARN',
|
|
INFO: 'INFO',
|
|
DEBUG: 'DEBUG',
|
|
TRACE: 'TRACE'
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
* Option keys:
|
|
* https://github.com/SeleniumHQ/selenium/wiki/DesiredCapabilities#ie-specific
|
|
* @enum {string}
|
|
*/
|
|
const Key = {
|
|
IGNORE_PROTECTED_MODE_SETTINGS: 'ignoreProtectedModeSettings',
|
|
IGNORE_ZOOM_SETTING: 'ignoreZoomSetting',
|
|
INITIAL_BROWSER_URL: 'initialBrowserUrl',
|
|
ENABLE_PERSISTENT_HOVER: 'enablePersistentHover',
|
|
ENABLE_ELEMENT_CACHE_CLEANUP: 'enableElementCacheCleanup',
|
|
REQUIRE_WINDOW_FOCUS: 'requireWindowFocus',
|
|
BROWSER_ATTACH_TIMEOUT: 'browserAttachTimeout',
|
|
FORCE_CREATE_PROCESS: 'ie.forceCreateProcessApi',
|
|
BROWSER_COMMAND_LINE_SWITCHES: 'ie.browserCommandLineSwitches',
|
|
USE_PER_PROCESS_PROXY: 'ie.usePerProcessProxy',
|
|
ENSURE_CLEAN_SESSION: 'ie.ensureCleanSession',
|
|
LOG_FILE: 'logFile',
|
|
LOG_LEVEL: 'logLevel',
|
|
HOST: 'host',
|
|
EXTRACT_PATH: 'extractPath',
|
|
SILENT: 'silent'
|
|
};
|
|
|
|
|
|
/**
|
|
* Class for managing IEDriver specific options.
|
|
*/
|
|
class Options {
|
|
constructor() {
|
|
/** @private {!Object<(boolean|number|string|!Array<string>)>} */
|
|
this.options_ = {};
|
|
|
|
/** @private {(capabilities.ProxyConfig|null)} */
|
|
this.proxy_ = null;
|
|
}
|
|
|
|
/**
|
|
* Extracts the IEDriver specific options from the given capabilities
|
|
* object.
|
|
* @param {!capabilities.Capabilities} caps The capabilities object.
|
|
* @return {!Options} The IEDriver options.
|
|
*/
|
|
static fromCapabilities(caps) {
|
|
var options = new Options();
|
|
var map = options.options_;
|
|
|
|
Object.keys(Key).forEach(function(key) {
|
|
key = Key[key];
|
|
if (caps.has(key)) {
|
|
map[key] = caps.get(key);
|
|
}
|
|
});
|
|
|
|
if (caps.has(capabilities.Capability.PROXY)) {
|
|
options.setProxy(caps.get(capabilities.Capability.PROXY));
|
|
}
|
|
|
|
return options;
|
|
}
|
|
|
|
/**
|
|
* Whether to disable the protected mode settings check when the session is
|
|
* created. Disbling this setting may lead to significant instability as the
|
|
* browser may become unresponsive/hang. Only "best effort" support is provided
|
|
* when using this capability.
|
|
*
|
|
* For more information, refer to the IEDriver's
|
|
* [required system configuration](http://goo.gl/eH0Yi3).
|
|
*
|
|
* @param {boolean} ignoreSettings Whether to ignore protected mode settings.
|
|
* @return {!Options} A self reference.
|
|
*/
|
|
introduceFlakinessByIgnoringProtectedModeSettings(ignoreSettings) {
|
|
this.options_[Key.IGNORE_PROTECTED_MODE_SETTINGS] = !!ignoreSettings;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Indicates whether to skip the check that the browser's zoom level is set to
|
|
* 100%.
|
|
*
|
|
* @param {boolean} ignore Whether to ignore the browser's zoom level settings.
|
|
* @return {!Options} A self reference.
|
|
*/
|
|
ignoreZoomSetting(ignore) {
|
|
this.options_[Key.IGNORE_ZOOM_SETTING] = !!ignore;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Sets the initial URL loaded when IE starts. This is intended to be used with
|
|
* {@link #ignoreProtectedModeSettings} to allow the user to initialize IE in
|
|
* the proper Protected Mode zone. Setting this option may cause browser
|
|
* instability or flaky and unresponsive code. Only "best effort" support is
|
|
* provided when using this option.
|
|
*
|
|
* @param {string} url The initial browser URL.
|
|
* @return {!Options} A self reference.
|
|
*/
|
|
initialBrowserUrl(url) {
|
|
this.options_[Key.INITIAL_BROWSER_URL] = url;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Configures whether to enable persistent mouse hovering (true by default).
|
|
* Persistent hovering is achieved by continuously firing mouse over events at
|
|
* the last location the mouse cursor has been moved to.
|
|
*
|
|
* @param {boolean} enable Whether to enable persistent hovering.
|
|
* @return {!Options} A self reference.
|
|
*/
|
|
enablePersistentHover(enable) {
|
|
this.options_[Key.ENABLE_PERSISTENT_HOVER] = !!enable;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Configures whether the driver should attempt to remove obsolete
|
|
* {@linkplain webdriver.WebElement WebElements} from its internal cache on
|
|
* page navigation (true by default). Disabling this option will cause the
|
|
* driver to run with a larger memory footprint.
|
|
*
|
|
* @param {boolean} enable Whether to enable element reference cleanup.
|
|
* @return {!Options} A self reference.
|
|
*/
|
|
enableElementCacheCleanup(enable) {
|
|
this.options_[Key.ENABLE_ELEMENT_CACHE_CLEANUP] = !!enable;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Configures whether to require the IE window to have input focus before
|
|
* performing any user interactions (i.e. mouse or keyboard events). This
|
|
* option is disabled by default, but delivers much more accurate interaction
|
|
* events when enabled.
|
|
*
|
|
* @param {boolean} require Whether to require window focus.
|
|
* @return {!Options} A self reference.
|
|
*/
|
|
requireWindowFocus(require) {
|
|
this.options_[Key.REQUIRE_WINDOW_FOCUS] = !!require;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Configures the timeout, in milliseconds, that the driver will attempt to
|
|
* located and attach to a newly opened instance of Internet Explorer. The
|
|
* default is zero, which indicates waiting indefinitely.
|
|
*
|
|
* @param {number} timeout How long to wait for IE.
|
|
* @return {!Options} A self reference.
|
|
*/
|
|
browserAttachTimeout(timeout) {
|
|
this.options_[Key.BROWSER_ATTACH_TIMEOUT] = Math.max(timeout, 0);
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Configures whether to launch Internet Explorer using the CreateProcess API.
|
|
* If this option is not specified, IE is launched using IELaunchURL, if
|
|
* available. For IE 8 and above, this option requires the TabProcGrowth
|
|
* registry value to be set to 0.
|
|
*
|
|
* @param {boolean} force Whether to use the CreateProcess API.
|
|
* @return {!Options} A self reference.
|
|
*/
|
|
forceCreateProcessApi(force) {
|
|
this.options_[Key.FORCE_CREATE_PROCESS] = !!force;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Specifies command-line switches to use when launching Internet Explorer.
|
|
* This is only valid when used with {@link #forceCreateProcessApi}.
|
|
*
|
|
* @param {...(string|!Array.<string>)} var_args The arguments to add.
|
|
* @return {!Options} A self reference.
|
|
*/
|
|
addArguments(var_args) {
|
|
var args = this.options_[Key.BROWSER_COMMAND_LINE_SWITCHES] || [];
|
|
args = args.concat.apply(args, arguments);
|
|
this.options_[Key.BROWSER_COMMAND_LINE_SWITCHES] = args;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Configures whether proxies should be configured on a per-process basis. If
|
|
* not set, setting a {@linkplain #setProxy proxy} will configure the system
|
|
* proxy. The default behavior is to use the system proxy.
|
|
*
|
|
* @param {boolean} enable Whether to enable per-process proxy settings.
|
|
* @return {!Options} A self reference.
|
|
*/
|
|
usePerProcessProxy(enable) {
|
|
this.options_[Key.USE_PER_PROCESS_PROXY] = !!enable;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Configures whether to clear the cache, cookies, history, and saved form data
|
|
* before starting the browser. _Using this capability will clear session data
|
|
* for all running instances of Internet Explorer, including those started
|
|
* manually._
|
|
*
|
|
* @param {boolean} cleanSession Whether to clear all session data on startup.
|
|
* @return {!Options} A self reference.
|
|
*/
|
|
ensureCleanSession(cleanSession) {
|
|
this.options_[Key.ENSURE_CLEAN_SESSION] = !!cleanSession;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Sets the path to the log file the driver should log to.
|
|
* @param {string} file The log file path.
|
|
* @return {!Options} A self reference.
|
|
*/
|
|
setLogFile(file) {
|
|
this.options_[Key.LOG_FILE] = file;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Sets the IEDriverServer's logging {@linkplain Level level}.
|
|
* @param {Level} level The logging level.
|
|
* @return {!Options} A self reference.
|
|
*/
|
|
setLogLevel(level) {
|
|
this.options_[Key.LOG_LEVEL] = level;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Sets the IP address of the driver's host adapter.
|
|
* @param {string} host The IP address to use.
|
|
* @return {!Options} A self reference.
|
|
*/
|
|
setHost(host) {
|
|
this.options_[Key.HOST] = host;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Sets the path of the temporary data directory to use.
|
|
* @param {string} path The log file path.
|
|
* @return {!Options} A self reference.
|
|
*/
|
|
setExtractPath(path) {
|
|
this.options_[Key.EXTRACT_PATH] = path;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Sets whether the driver should start in silent mode.
|
|
* @param {boolean} silent Whether to run in silent mode.
|
|
* @return {!Options} A self reference.
|
|
*/
|
|
silent(silent) {
|
|
this.options_[Key.SILENT] = silent;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Sets the proxy settings for the new session.
|
|
* @param {capabilities.ProxyConfig} proxy The proxy configuration to use.
|
|
* @return {!Options} A self reference.
|
|
*/
|
|
setProxy(proxy) {
|
|
this.proxy_ = proxy;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Converts this options instance to a {@link capabilities.Capabilities}
|
|
* object.
|
|
* @param {capabilities.Capabilities=} opt_capabilities The capabilities to
|
|
* merge these options into, if any.
|
|
* @return {!capabilities.Capabilities} The capabilities.
|
|
*/
|
|
toCapabilities(opt_capabilities) {
|
|
var caps = opt_capabilities || capabilities.Capabilities.ie();
|
|
if (this.proxy_) {
|
|
caps.set(capabilities.Capability.PROXY, this.proxy_);
|
|
}
|
|
Object.keys(this.options_).forEach(function(key) {
|
|
caps.set(key, this.options_[key]);
|
|
}, this);
|
|
return caps;
|
|
}
|
|
}
|
|
|
|
|
|
function createServiceFromCapabilities(capabilities) {
|
|
if (process.platform !== 'win32') {
|
|
throw Error(
|
|
'The IEDriver may only be used on Windows, but you appear to be on ' +
|
|
process.platform + '. Did you mean to run against a remote ' +
|
|
'WebDriver server?');
|
|
}
|
|
|
|
let exe = io.findInPath(IEDRIVER_EXE, true);
|
|
if (!exe || !fs.existsSync(exe)) {
|
|
throw Error(
|
|
`${IEDRIVER_EXE} could not be found on the current PATH. Please ` +
|
|
`download the latest version of ${IEDRIVER_EXE} from ` +
|
|
'http://selenium-release.storage.googleapis.com/index.html and ' +
|
|
'ensure it can be found on your system PATH.');
|
|
}
|
|
|
|
var args = [];
|
|
if (capabilities.has(Key.HOST)) {
|
|
args.push('--host=' + capabilities.get(Key.HOST));
|
|
}
|
|
if (capabilities.has(Key.LOG_FILE)) {
|
|
args.push('--log-file=' + capabilities.get(Key.LOG_FILE));
|
|
}
|
|
if (capabilities.has(Key.LOG_LEVEL)) {
|
|
args.push('--log-level=' + capabilities.get(Key.LOG_LEVEL));
|
|
}
|
|
if (capabilities.has(Key.EXTRACT_PATH)) {
|
|
args.push('--extract-path=' + capabilities.get(Key.EXTRACT_PATH));
|
|
}
|
|
if (capabilities.get(Key.SILENT)) {
|
|
args.push('--silent');
|
|
}
|
|
|
|
var port = portprober.findFreePort();
|
|
return new remote.DriverService(exe, {
|
|
loopback: true,
|
|
port: port,
|
|
args: port.then(function(port) {
|
|
return args.concat('--port=' + port);
|
|
}),
|
|
stdio: 'ignore'
|
|
});
|
|
}
|
|
|
|
|
|
/**
|
|
* A WebDriver client for Microsoft's Internet Explorer.
|
|
*/
|
|
class Driver extends webdriver.WebDriver {
|
|
/**
|
|
* Creates a new session for Microsoft's Internet Explorer.
|
|
*
|
|
* @param {(capabilities.Capabilities|Options)=} opt_config The configuration
|
|
* options.
|
|
* @param {promise.ControlFlow=} opt_flow The control flow to use,
|
|
* or {@code null} to use the currently active flow.
|
|
* @return {!Driver} A new driver instance.
|
|
*/
|
|
static createSession(opt_config, opt_flow) {
|
|
var caps = opt_config instanceof Options ?
|
|
opt_config.toCapabilities() :
|
|
(opt_config || capabilities.Capabilities.ie());
|
|
|
|
var service = createServiceFromCapabilities(caps);
|
|
var client = service.start().then(url => new http.HttpClient(url));
|
|
var executor = new http.Executor(client);
|
|
|
|
return /** @type {!Driver} */(super.createSession(
|
|
executor, caps, opt_flow, () => service.kill()));
|
|
}
|
|
|
|
/**
|
|
* This function is a no-op as file detectors are not supported by this
|
|
* implementation.
|
|
* @override
|
|
*/
|
|
setFileDetector() {}
|
|
}
|
|
|
|
|
|
// PUBLIC API
|
|
|
|
|
|
exports.Driver = Driver;
|
|
exports.Options = Options;
|
|
exports.Level = Level;
|