ie.js 13 KB

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