safari.js 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  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 WebDriver client for Safari.
  19. */
  20. 'use strict';
  21. const http = require('./http');
  22. const io = require('./io');
  23. const {Capabilities, Capability} = require('./lib/capabilities');
  24. const command = require('./lib/command');
  25. const error = require('./lib/error');
  26. const logging = require('./lib/logging');
  27. const promise = require('./lib/promise');
  28. const Symbols = require('./lib/symbols');
  29. const webdriver = require('./lib/webdriver');
  30. const portprober = require('./net/portprober');
  31. const remote = require('./remote');
  32. /**
  33. * @return {string} .
  34. * @throws {Error}
  35. */
  36. function findSafariDriver() {
  37. let exe = io.findInPath('safaridriver', true);
  38. if (!exe) {
  39. throw Error(
  40. `The safaridriver executable could not be found on the current PATH.
  41. Please ensure you are using Safari 10.0 or above.`);
  42. }
  43. return exe;
  44. }
  45. /**
  46. * Creates {@link selenium-webdriver/remote.DriverService} instances that manage
  47. * a [safaridriver] server in a child process.
  48. *
  49. * [safaridriver]: https://developer.apple.com/library/prerelease/content/releasenotes/General/WhatsNewInSafari/Articles/Safari_10_0.html#//apple_ref/doc/uid/TP40014305-CH11-DontLinkElementID_28
  50. */
  51. class ServiceBuilder extends remote.DriverService.Builder {
  52. /**
  53. * @param {string=} opt_exe Path to the server executable to use. If omitted,
  54. * the builder will attempt to locate the safaridriver on the system PATH.
  55. */
  56. constructor(opt_exe) {
  57. super(opt_exe || findSafariDriver());
  58. this.setLoopback(true); // Required.
  59. }
  60. }
  61. const OPTIONS_CAPABILITY_KEY = 'safari.options';
  62. const TECHNOLOGY_PREVIEW_OPTIONS_KEY = 'technologyPreview';
  63. /**
  64. * Configuration options specific to the {@link Driver SafariDriver}.
  65. */
  66. class Options {
  67. constructor() {
  68. /** @private {Object<string, *>} */
  69. this.options_ = null;
  70. /** @private {./lib/logging.Preferences} */
  71. this.logPrefs_ = null;
  72. /** @private {?./lib/capabilities.ProxyConfig} */
  73. this.proxy_ = null;
  74. }
  75. /**
  76. * Extracts the SafariDriver specific options from the given capabilities
  77. * object.
  78. * @param {!Capabilities} capabilities The capabilities object.
  79. * @return {!Options} The SafariDriver options.
  80. */
  81. static fromCapabilities(capabilities) {
  82. var options = new Options();
  83. var o = capabilities.get(OPTIONS_CAPABILITY_KEY);
  84. if (o instanceof Options) {
  85. options = o;
  86. } else if (o) {
  87. options.setCleanSession(o.cleanSession);
  88. options.setTechnologyPreview(o[TECHNOLOGY_PREVIEW_OPTIONS_KEY]);
  89. }
  90. if (capabilities.has(Capability.PROXY)) {
  91. options.setProxy(capabilities.get(Capability.PROXY));
  92. }
  93. if (capabilities.has(Capability.LOGGING_PREFS)) {
  94. options.setLoggingPrefs(capabilities.get(Capability.LOGGING_PREFS));
  95. }
  96. return options;
  97. }
  98. /**
  99. * Sets whether to force Safari to start with a clean session. Enabling this
  100. * option will cause all global browser data to be deleted.
  101. * @param {boolean} clean Whether to make sure the session has no cookies,
  102. * cache entries, local storage, or databases.
  103. * @return {!Options} A self reference.
  104. */
  105. setCleanSession(clean) {
  106. if (!this.options_) {
  107. this.options_ = {};
  108. }
  109. this.options_['cleanSession'] = clean;
  110. return this;
  111. }
  112. /**
  113. * Sets the logging preferences for the new session.
  114. * @param {!./lib/logging.Preferences} prefs The logging preferences.
  115. * @return {!Options} A self reference.
  116. */
  117. setLoggingPrefs(prefs) {
  118. this.logPrefs_ = prefs;
  119. return this;
  120. }
  121. /**
  122. * Sets the proxy to use.
  123. *
  124. * @param {./lib/capabilities.ProxyConfig} proxy The proxy configuration to use.
  125. * @return {!Options} A self reference.
  126. */
  127. setProxy(proxy) {
  128. this.proxy_ = proxy;
  129. return this;
  130. }
  131. /**
  132. * Instruct the SafariDriver to use the Safari Technology Preview if true.
  133. * Otherwise, use the release version of Safari. Defaults to using the release version of Safari.
  134. *
  135. * @param {boolean} useTechnologyPreview
  136. * @return {!Options} A self reference.
  137. */
  138. setTechnologyPreview(useTechnologyPreview) {
  139. if (!this.options_) {
  140. this.options_ = {};
  141. }
  142. this.options_[TECHNOLOGY_PREVIEW_OPTIONS_KEY] = !!useTechnologyPreview;
  143. return this;
  144. }
  145. /**
  146. * Converts this options instance to a {@link Capabilities} object.
  147. * @param {Capabilities=} opt_capabilities The capabilities to
  148. * merge these options into, if any.
  149. * @return {!Capabilities} The capabilities.
  150. */
  151. toCapabilities(opt_capabilities) {
  152. var caps = opt_capabilities || Capabilities.safari();
  153. if (this.logPrefs_) {
  154. caps.set(Capability.LOGGING_PREFS, this.logPrefs_);
  155. }
  156. if (this.proxy_) {
  157. caps.set(Capability.PROXY, this.proxy_);
  158. }
  159. if (this.options_) {
  160. caps.set(OPTIONS_CAPABILITY_KEY, this);
  161. }
  162. return caps;
  163. }
  164. /**
  165. * Converts this instance to its JSON wire protocol representation. Note this
  166. * function is an implementation detail not intended for general use.
  167. * @return {!Object<string, *>} The JSON wire protocol representation of this
  168. * instance.
  169. */
  170. [Symbols.serialize]() {
  171. return this.options_ || {};
  172. }
  173. }
  174. /**
  175. * @param {(Options|Object<string, *>)=} o The options object
  176. * @return {boolean}
  177. */
  178. function useTechnologyPreview(o) {
  179. if (o instanceof Options) {
  180. return !!(o.options_ && o.options_[TECHNOLOGY_PREVIEW_OPTIONS_KEY]);
  181. }
  182. if (o && typeof o === 'object') {
  183. return !!o[TECHNOLOGY_PREVIEW_OPTIONS_KEY];
  184. }
  185. return false;
  186. }
  187. const SAFARIDRIVER_TECHNOLOGY_PREVIEW_EXE = '/Applications/Safari Technology Preview.app/Contents/MacOS/safaridriver';
  188. /**
  189. * A WebDriver client for Safari. This class should never be instantiated
  190. * directly; instead, use the {@linkplain ./builder.Builder Builder}:
  191. *
  192. * var driver = new Builder()
  193. * .forBrowser('safari')
  194. * .build();
  195. *
  196. */
  197. class Driver extends webdriver.WebDriver {
  198. /**
  199. * Creates a new Safari session.
  200. *
  201. * @param {(Options|Capabilities)=} opt_config The configuration
  202. * options for the new session.
  203. * @param {promise.ControlFlow=} opt_flow The control flow to create
  204. * the driver under.
  205. * @return {!Driver} A new driver instance.
  206. */
  207. static createSession(opt_config, opt_flow) {
  208. let caps, exe;
  209. if (opt_config instanceof Options) {
  210. caps = opt_config.toCapabilities();
  211. } else {
  212. caps = opt_config || Capabilities.safari();
  213. }
  214. if (useTechnologyPreview(caps.get(OPTIONS_CAPABILITY_KEY))) {
  215. exe = SAFARIDRIVER_TECHNOLOGY_PREVIEW_EXE;
  216. }
  217. let service = new ServiceBuilder(exe).build();
  218. let executor = new http.Executor(
  219. service.start().then(url => new http.HttpClient(url)));
  220. return /** @type {!Driver} */(super.createSession(
  221. executor, caps, opt_flow, () => service.kill()));
  222. }
  223. }
  224. // Public API
  225. exports.Driver = Driver;
  226. exports.Options = Options;
  227. exports.ServiceBuilder = ServiceBuilder;