UrgentcareCLI 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (308) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +3 -0
  3. data/Gemfile.lock +60 -25
  4. data/Notes +1 -1
  5. data/README.md +5 -5
  6. data/Rakefile +2 -0
  7. data/UrgentCare.gemspec +3 -3
  8. data/background.jpg +0 -0
  9. data/lib/UrgentCare.rb +3 -0
  10. data/lib/UrgentCare/CLI.rb +6 -2
  11. data/lib/UrgentCare/version.rb +1 -1
  12. data/lib/Urgentcare/Scraper.rb +65 -23
  13. data/node_modules/.bin/rimraf +1 -0
  14. data/node_modules/.package-lock.json +250 -0
  15. data/node_modules/balanced-match/.github/FUNDING.yml +2 -0
  16. data/node_modules/balanced-match/LICENSE.md +21 -0
  17. data/node_modules/balanced-match/README.md +97 -0
  18. data/node_modules/balanced-match/index.js +62 -0
  19. data/node_modules/balanced-match/package.json +48 -0
  20. data/node_modules/brace-expansion/LICENSE +21 -0
  21. data/node_modules/brace-expansion/README.md +129 -0
  22. data/node_modules/brace-expansion/index.js +201 -0
  23. data/node_modules/brace-expansion/package.json +47 -0
  24. data/node_modules/concat-map/.travis.yml +4 -0
  25. data/node_modules/concat-map/LICENSE +18 -0
  26. data/node_modules/concat-map/README.markdown +62 -0
  27. data/node_modules/concat-map/example/map.js +6 -0
  28. data/node_modules/concat-map/index.js +13 -0
  29. data/node_modules/concat-map/package.json +43 -0
  30. data/node_modules/concat-map/test/map.js +39 -0
  31. data/node_modules/core-util-is/LICENSE +19 -0
  32. data/node_modules/core-util-is/README.md +3 -0
  33. data/node_modules/core-util-is/float.patch +604 -0
  34. data/node_modules/core-util-is/lib/util.js +107 -0
  35. data/node_modules/core-util-is/package.json +32 -0
  36. data/node_modules/core-util-is/test.js +68 -0
  37. data/node_modules/fs.realpath/LICENSE +43 -0
  38. data/node_modules/fs.realpath/README.md +33 -0
  39. data/node_modules/fs.realpath/index.js +66 -0
  40. data/node_modules/fs.realpath/old.js +303 -0
  41. data/node_modules/fs.realpath/package.json +26 -0
  42. data/node_modules/glob/LICENSE +21 -0
  43. data/node_modules/glob/README.md +375 -0
  44. data/node_modules/glob/changelog.md +67 -0
  45. data/node_modules/glob/common.js +234 -0
  46. data/node_modules/glob/glob.js +788 -0
  47. data/node_modules/glob/package.json +51 -0
  48. data/node_modules/glob/sync.js +484 -0
  49. data/node_modules/immediate/LICENSE.txt +20 -0
  50. data/node_modules/immediate/README.md +93 -0
  51. data/node_modules/immediate/dist/immediate.js +75 -0
  52. data/node_modules/immediate/dist/immediate.min.js +1 -0
  53. data/node_modules/immediate/lib/browser.js +69 -0
  54. data/node_modules/immediate/lib/index.js +73 -0
  55. data/node_modules/immediate/package.json +42 -0
  56. data/node_modules/inflight/LICENSE +15 -0
  57. data/node_modules/inflight/README.md +37 -0
  58. data/node_modules/inflight/inflight.js +54 -0
  59. data/node_modules/inflight/package.json +29 -0
  60. data/node_modules/inherits/LICENSE +16 -0
  61. data/node_modules/inherits/README.md +42 -0
  62. data/node_modules/inherits/inherits.js +9 -0
  63. data/node_modules/inherits/inherits_browser.js +27 -0
  64. data/node_modules/inherits/package.json +29 -0
  65. data/node_modules/isarray/.npmignore +1 -0
  66. data/node_modules/isarray/.travis.yml +4 -0
  67. data/node_modules/isarray/Makefile +6 -0
  68. data/node_modules/isarray/README.md +60 -0
  69. data/node_modules/isarray/component.json +19 -0
  70. data/node_modules/isarray/index.js +5 -0
  71. data/node_modules/isarray/package.json +45 -0
  72. data/node_modules/isarray/test.js +20 -0
  73. data/node_modules/jszip/.codeclimate.yml +16 -0
  74. data/node_modules/jszip/.editorconfig +8 -0
  75. data/node_modules/jszip/.jshintignore +1 -0
  76. data/node_modules/jszip/.jshintrc +21 -0
  77. data/node_modules/jszip/.travis.yml +17 -0
  78. data/node_modules/jszip/CHANGES.md +163 -0
  79. data/node_modules/jszip/LICENSE.markdown +651 -0
  80. data/node_modules/jszip/README.markdown +35 -0
  81. data/node_modules/jszip/dist/jszip.js +30 -0
  82. data/node_modules/jszip/dist/jszip.min.js +13 -0
  83. data/node_modules/jszip/index.d.ts +270 -0
  84. data/node_modules/jszip/lib/base64.js +106 -0
  85. data/node_modules/jszip/lib/compressedObject.js +74 -0
  86. data/node_modules/jszip/lib/compressions.js +14 -0
  87. data/node_modules/jszip/lib/crc32.js +77 -0
  88. data/node_modules/jszip/lib/defaults.js +11 -0
  89. data/node_modules/jszip/lib/external.js +19 -0
  90. data/node_modules/jszip/lib/flate.js +85 -0
  91. data/node_modules/jszip/lib/generate/ZipFileWorker.js +540 -0
  92. data/node_modules/jszip/lib/generate/index.js +57 -0
  93. data/node_modules/jszip/lib/index.js +52 -0
  94. data/node_modules/jszip/lib/license_header.js +11 -0
  95. data/node_modules/jszip/lib/load.js +81 -0
  96. data/node_modules/jszip/lib/nodejs/NodejsStreamInputAdapter.js +74 -0
  97. data/node_modules/jszip/lib/nodejs/NodejsStreamOutputAdapter.js +42 -0
  98. data/node_modules/jszip/lib/nodejsUtils.js +57 -0
  99. data/node_modules/jszip/lib/object.js +389 -0
  100. data/node_modules/jszip/lib/readable-stream-browser.js +9 -0
  101. data/node_modules/jszip/lib/reader/ArrayReader.js +57 -0
  102. data/node_modules/jszip/lib/reader/DataReader.js +116 -0
  103. data/node_modules/jszip/lib/reader/NodeBufferReader.js +19 -0
  104. data/node_modules/jszip/lib/reader/StringReader.js +38 -0
  105. data/node_modules/jszip/lib/reader/Uint8ArrayReader.js +22 -0
  106. data/node_modules/jszip/lib/reader/readerFor.js +28 -0
  107. data/node_modules/jszip/lib/signature.js +7 -0
  108. data/node_modules/jszip/lib/stream/ConvertWorker.js +26 -0
  109. data/node_modules/jszip/lib/stream/Crc32Probe.js +24 -0
  110. data/node_modules/jszip/lib/stream/DataLengthProbe.js +29 -0
  111. data/node_modules/jszip/lib/stream/DataWorker.js +116 -0
  112. data/node_modules/jszip/lib/stream/GenericWorker.js +263 -0
  113. data/node_modules/jszip/lib/stream/StreamHelper.js +212 -0
  114. data/node_modules/jszip/lib/support.js +38 -0
  115. data/node_modules/jszip/lib/utf8.js +275 -0
  116. data/node_modules/jszip/lib/utils.js +476 -0
  117. data/node_modules/jszip/lib/zipEntries.js +262 -0
  118. data/node_modules/jszip/lib/zipEntry.js +294 -0
  119. data/node_modules/jszip/lib/zipObject.js +133 -0
  120. data/node_modules/jszip/package.json +63 -0
  121. data/node_modules/jszip/vendor/FileSaver.js +247 -0
  122. data/node_modules/lie/README.md +62 -0
  123. data/node_modules/lie/dist/lie.js +350 -0
  124. data/node_modules/lie/dist/lie.min.js +1 -0
  125. data/node_modules/lie/dist/lie.polyfill.js +358 -0
  126. data/node_modules/lie/dist/lie.polyfill.min.js +1 -0
  127. data/node_modules/lie/lib/browser.js +273 -0
  128. data/node_modules/lie/lib/index.js +298 -0
  129. data/node_modules/lie/license.md +7 -0
  130. data/node_modules/lie/lie.d.ts +244 -0
  131. data/node_modules/lie/package.json +69 -0
  132. data/node_modules/lie/polyfill.js +4 -0
  133. data/node_modules/minimatch/LICENSE +15 -0
  134. data/node_modules/minimatch/README.md +209 -0
  135. data/node_modules/minimatch/minimatch.js +923 -0
  136. data/node_modules/minimatch/package.json +30 -0
  137. data/node_modules/once/LICENSE +15 -0
  138. data/node_modules/once/README.md +79 -0
  139. data/node_modules/once/once.js +42 -0
  140. data/node_modules/once/package.json +33 -0
  141. data/node_modules/pako/CHANGELOG.md +164 -0
  142. data/node_modules/pako/LICENSE +21 -0
  143. data/node_modules/pako/README.md +191 -0
  144. data/node_modules/pako/dist/pako.js +6818 -0
  145. data/node_modules/pako/dist/pako.min.js +1 -0
  146. data/node_modules/pako/dist/pako_deflate.js +3997 -0
  147. data/node_modules/pako/dist/pako_deflate.min.js +1 -0
  148. data/node_modules/pako/dist/pako_inflate.js +3300 -0
  149. data/node_modules/pako/dist/pako_inflate.min.js +1 -0
  150. data/node_modules/pako/index.js +14 -0
  151. data/node_modules/pako/lib/deflate.js +400 -0
  152. data/node_modules/pako/lib/inflate.js +423 -0
  153. data/node_modules/pako/lib/utils/common.js +105 -0
  154. data/node_modules/pako/lib/utils/strings.js +187 -0
  155. data/node_modules/pako/lib/zlib/README +59 -0
  156. data/node_modules/pako/lib/zlib/adler32.js +51 -0
  157. data/node_modules/pako/lib/zlib/constants.js +68 -0
  158. data/node_modules/pako/lib/zlib/crc32.js +59 -0
  159. data/node_modules/pako/lib/zlib/deflate.js +1874 -0
  160. data/node_modules/pako/lib/zlib/gzheader.js +58 -0
  161. data/node_modules/pako/lib/zlib/inffast.js +345 -0
  162. data/node_modules/pako/lib/zlib/inflate.js +1556 -0
  163. data/node_modules/pako/lib/zlib/inftrees.js +343 -0
  164. data/node_modules/pako/lib/zlib/messages.js +32 -0
  165. data/node_modules/pako/lib/zlib/trees.js +1222 -0
  166. data/node_modules/pako/lib/zlib/zstream.js +47 -0
  167. data/node_modules/pako/package.json +44 -0
  168. data/node_modules/path-is-absolute/index.js +20 -0
  169. data/node_modules/path-is-absolute/license +21 -0
  170. data/node_modules/path-is-absolute/package.json +43 -0
  171. data/node_modules/path-is-absolute/readme.md +59 -0
  172. data/node_modules/process-nextick-args/index.js +45 -0
  173. data/node_modules/process-nextick-args/license.md +19 -0
  174. data/node_modules/process-nextick-args/package.json +25 -0
  175. data/node_modules/process-nextick-args/readme.md +18 -0
  176. data/node_modules/readable-stream/.travis.yml +34 -0
  177. data/node_modules/readable-stream/CONTRIBUTING.md +38 -0
  178. data/node_modules/readable-stream/GOVERNANCE.md +136 -0
  179. data/node_modules/readable-stream/LICENSE +47 -0
  180. data/node_modules/readable-stream/README.md +58 -0
  181. data/node_modules/readable-stream/doc/wg-meetings/2015-01-30.md +60 -0
  182. data/node_modules/readable-stream/duplex-browser.js +1 -0
  183. data/node_modules/readable-stream/duplex.js +1 -0
  184. data/node_modules/readable-stream/lib/_stream_duplex.js +131 -0
  185. data/node_modules/readable-stream/lib/_stream_passthrough.js +47 -0
  186. data/node_modules/readable-stream/lib/_stream_readable.js +1019 -0
  187. data/node_modules/readable-stream/lib/_stream_transform.js +214 -0
  188. data/node_modules/readable-stream/lib/_stream_writable.js +687 -0
  189. data/node_modules/readable-stream/lib/internal/streams/BufferList.js +79 -0
  190. data/node_modules/readable-stream/lib/internal/streams/destroy.js +74 -0
  191. data/node_modules/readable-stream/lib/internal/streams/stream-browser.js +1 -0
  192. data/node_modules/readable-stream/lib/internal/streams/stream.js +1 -0
  193. data/node_modules/readable-stream/package.json +52 -0
  194. data/node_modules/readable-stream/passthrough.js +1 -0
  195. data/node_modules/readable-stream/readable-browser.js +7 -0
  196. data/node_modules/readable-stream/readable.js +19 -0
  197. data/node_modules/readable-stream/transform.js +1 -0
  198. data/node_modules/readable-stream/writable-browser.js +1 -0
  199. data/node_modules/readable-stream/writable.js +8 -0
  200. data/node_modules/rimraf/LICENSE +15 -0
  201. data/node_modules/rimraf/README.md +101 -0
  202. data/node_modules/rimraf/bin.js +50 -0
  203. data/node_modules/rimraf/package.json +29 -0
  204. data/node_modules/rimraf/rimraf.js +372 -0
  205. data/node_modules/safe-buffer/LICENSE +21 -0
  206. data/node_modules/safe-buffer/README.md +584 -0
  207. data/node_modules/safe-buffer/index.d.ts +187 -0
  208. data/node_modules/safe-buffer/index.js +62 -0
  209. data/node_modules/safe-buffer/package.json +37 -0
  210. data/node_modules/selenium-webdriver/CHANGES.md +1114 -0
  211. data/node_modules/selenium-webdriver/LICENSE +202 -0
  212. data/node_modules/selenium-webdriver/NOTICE +2 -0
  213. data/node_modules/selenium-webdriver/README.md +229 -0
  214. data/node_modules/selenium-webdriver/chrome.js +295 -0
  215. data/node_modules/selenium-webdriver/chromium.js +829 -0
  216. data/node_modules/selenium-webdriver/devtools/CDPConnection.js +35 -0
  217. data/node_modules/selenium-webdriver/edge.js +224 -0
  218. data/node_modules/selenium-webdriver/example/chrome_android.js +45 -0
  219. data/node_modules/selenium-webdriver/example/chrome_mobile_emulation.js +46 -0
  220. data/node_modules/selenium-webdriver/example/firefox_channels.js +84 -0
  221. data/node_modules/selenium-webdriver/example/google_search.js +50 -0
  222. data/node_modules/selenium-webdriver/example/google_search_test.js +70 -0
  223. data/node_modules/selenium-webdriver/example/headless.js +63 -0
  224. data/node_modules/selenium-webdriver/example/logging.js +64 -0
  225. data/node_modules/selenium-webdriver/firefox.js +789 -0
  226. data/node_modules/selenium-webdriver/http/index.js +324 -0
  227. data/node_modules/selenium-webdriver/http/util.js +172 -0
  228. data/node_modules/selenium-webdriver/ie.js +503 -0
  229. data/node_modules/selenium-webdriver/index.js +825 -0
  230. data/node_modules/selenium-webdriver/io/exec.js +162 -0
  231. data/node_modules/selenium-webdriver/io/index.js +348 -0
  232. data/node_modules/selenium-webdriver/io/zip.js +223 -0
  233. data/node_modules/selenium-webdriver/lib/atoms/find-elements.js +123 -0
  234. data/node_modules/selenium-webdriver/lib/atoms/get-attribute.js +101 -0
  235. data/node_modules/selenium-webdriver/lib/atoms/is-displayed.js +101 -0
  236. data/node_modules/selenium-webdriver/lib/atoms/mutation-listener.js +55 -0
  237. data/node_modules/selenium-webdriver/lib/by.js +415 -0
  238. data/node_modules/selenium-webdriver/lib/capabilities.js +553 -0
  239. data/node_modules/selenium-webdriver/lib/command.js +206 -0
  240. data/node_modules/selenium-webdriver/lib/error.js +605 -0
  241. data/node_modules/selenium-webdriver/lib/http.js +704 -0
  242. data/node_modules/selenium-webdriver/lib/input.js +946 -0
  243. data/node_modules/selenium-webdriver/lib/logging.js +661 -0
  244. data/node_modules/selenium-webdriver/lib/promise.js +285 -0
  245. data/node_modules/selenium-webdriver/lib/proxy.js +212 -0
  246. data/node_modules/selenium-webdriver/lib/session.js +77 -0
  247. data/node_modules/selenium-webdriver/lib/symbols.js +37 -0
  248. data/node_modules/selenium-webdriver/lib/until.js +429 -0
  249. data/node_modules/selenium-webdriver/lib/webdriver.js +2919 -0
  250. data/node_modules/selenium-webdriver/net/index.js +107 -0
  251. data/node_modules/selenium-webdriver/net/portprober.js +75 -0
  252. data/node_modules/selenium-webdriver/opera.js +406 -0
  253. data/node_modules/selenium-webdriver/package.json +54 -0
  254. data/node_modules/selenium-webdriver/proxy.js +32 -0
  255. data/node_modules/selenium-webdriver/remote/index.js +624 -0
  256. data/node_modules/selenium-webdriver/safari.js +168 -0
  257. data/node_modules/selenium-webdriver/testing/index.js +504 -0
  258. data/node_modules/set-immediate-shim/index.js +7 -0
  259. data/node_modules/set-immediate-shim/package.json +34 -0
  260. data/node_modules/set-immediate-shim/readme.md +31 -0
  261. data/node_modules/string_decoder/.travis.yml +50 -0
  262. data/node_modules/string_decoder/LICENSE +48 -0
  263. data/node_modules/string_decoder/README.md +47 -0
  264. data/node_modules/string_decoder/lib/string_decoder.js +296 -0
  265. data/node_modules/string_decoder/package.json +31 -0
  266. data/node_modules/tmp/CHANGELOG.md +288 -0
  267. data/node_modules/tmp/LICENSE +21 -0
  268. data/node_modules/tmp/README.md +365 -0
  269. data/node_modules/tmp/lib/tmp.js +780 -0
  270. data/node_modules/tmp/node_modules/.bin/rimraf +1 -0
  271. data/node_modules/tmp/node_modules/rimraf/CHANGELOG.md +65 -0
  272. data/node_modules/tmp/node_modules/rimraf/LICENSE +15 -0
  273. data/node_modules/tmp/node_modules/rimraf/README.md +101 -0
  274. data/node_modules/tmp/node_modules/rimraf/bin.js +68 -0
  275. data/node_modules/tmp/node_modules/rimraf/package.json +32 -0
  276. data/node_modules/tmp/node_modules/rimraf/rimraf.js +360 -0
  277. data/node_modules/tmp/package.json +58 -0
  278. data/node_modules/util-deprecate/History.md +16 -0
  279. data/node_modules/util-deprecate/LICENSE +24 -0
  280. data/node_modules/util-deprecate/README.md +53 -0
  281. data/node_modules/util-deprecate/browser.js +67 -0
  282. data/node_modules/util-deprecate/node.js +6 -0
  283. data/node_modules/util-deprecate/package.json +27 -0
  284. data/node_modules/wrappy/LICENSE +15 -0
  285. data/node_modules/wrappy/README.md +36 -0
  286. data/node_modules/wrappy/package.json +29 -0
  287. data/node_modules/wrappy/wrappy.js +33 -0
  288. data/node_modules/ws/LICENSE +21 -0
  289. data/node_modules/ws/README.md +496 -0
  290. data/node_modules/ws/browser.js +8 -0
  291. data/node_modules/ws/index.js +10 -0
  292. data/node_modules/ws/lib/buffer-util.js +129 -0
  293. data/node_modules/ws/lib/constants.js +10 -0
  294. data/node_modules/ws/lib/event-target.js +184 -0
  295. data/node_modules/ws/lib/extension.js +223 -0
  296. data/node_modules/ws/lib/limiter.js +55 -0
  297. data/node_modules/ws/lib/permessage-deflate.js +517 -0
  298. data/node_modules/ws/lib/receiver.js +507 -0
  299. data/node_modules/ws/lib/sender.js +405 -0
  300. data/node_modules/ws/lib/stream.js +165 -0
  301. data/node_modules/ws/lib/validation.js +104 -0
  302. data/node_modules/ws/lib/websocket-server.js +418 -0
  303. data/node_modules/ws/lib/websocket.js +942 -0
  304. data/node_modules/ws/package.json +56 -0
  305. data/package-lock.json +458 -0
  306. data/package.json +5 -0
  307. data/selenium.log +1 -0
  308. metadata +314 -19
@@ -0,0 +1,70 @@
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 An example test that may be run using Mocha.
20
+ *
21
+ * This example uses the `selenium-webdriver/testing.suite` function, which will
22
+ * automatically run tests against every available WebDriver browser on the
23
+ * current system. Alternatively, you may use the `SELENIUM_BROWSER`
24
+ * environment variable to narrow the scope at runtime.
25
+ *
26
+ * Usage:
27
+ *
28
+ * # Automatically determine which browsers to run against.
29
+ * mocha -t 10000 selenium-webdriver/example/google_search_test.js
30
+ *
31
+ * # Configure tests to only run against Google Chrome.
32
+ * SELENIUM_BROWSER=chrome \
33
+ * mocha -t 10000 selenium-webdriver/example/google_search_test.js
34
+ */
35
+
36
+ const { Browser, By, Key, until } = require('..')
37
+ const { ignore, suite } = require('../testing')
38
+
39
+ suite(function (env) {
40
+ describe('Google Search', function () {
41
+ let driver
42
+
43
+ before(async function () {
44
+ // env.builder() returns a Builder instance preconfigured for the
45
+ // envrionment's target browser (you may still define browser specific
46
+ // options if necessary (i.e. firefox.Options or chrome.Options)).
47
+ driver = await env.builder().build()
48
+ })
49
+
50
+ it('demo', async function () {
51
+ await driver.get('https://www.google.com/ncr')
52
+ await driver.findElement(By.name('q')).sendKeys('webdriver', Key.RETURN)
53
+ await driver.wait(until.titleIs('webdriver - Google Search'), 1000)
54
+ })
55
+
56
+ // The ignore function returns wrappers around describe & it that will
57
+ // suppress tests if the provided predicate returns true. You may provide
58
+ // any synchronous predicate. The env.browsers(...) function generates a
59
+ // predicate that will suppress tests if the env targets one of the
60
+ // specified browsers.
61
+ //
62
+ // This example is always configured to skip Chrome.
63
+ ignore(env.browsers(Browser.CHROME)).it('demo 2', async function () {
64
+ await driver.get('https://www.google.com/ncr')
65
+ await driver.wait(until.urlIs('https://www.google.com/'), 1500)
66
+ })
67
+
68
+ after(() => driver && driver.quit())
69
+ })
70
+ })
@@ -0,0 +1,63 @@
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 An example of running Chrome or Firefox in headless mode.
20
+ *
21
+ * To run with Chrome, ensure you have Chrome 59+ installed and that
22
+ * chromedriver 2.30+ is present on your system PATH:
23
+ * <https://chromedriver.chromium.org/downloads>
24
+ *
25
+ * SELENIUM_BROWSER=chrome node selenium-webdriver/example/headless.js
26
+ *
27
+ * To run with Firefox, ensure you have Firefox 57+ installed and that
28
+ * geckodriver 0.19.0+ is present on your system PATH:
29
+ * <https://github.com/mozilla/geckodriver/releases>
30
+ *
31
+ * SELENIUM_BROWSER=firefox node selenium-webdriver/example/headless.js
32
+ */
33
+
34
+ const chrome = require('../chrome')
35
+ const firefox = require('../firefox')
36
+ const { Builder, By, Key, until } = require('..')
37
+
38
+ const width = 640
39
+ const height = 480
40
+
41
+ let driver = new Builder()
42
+ .forBrowser('chrome')
43
+ .setChromeOptions(
44
+ new chrome.Options().headless().windowSize({ width, height })
45
+ )
46
+ .setFirefoxOptions(
47
+ new firefox.Options().headless().windowSize({ width, height })
48
+ )
49
+ .build()
50
+
51
+ driver
52
+ .get('http://www.google.com/ncr')
53
+ .then((_) =>
54
+ driver.findElement(By.name('q')).sendKeys('webdriver', Key.RETURN)
55
+ )
56
+ .then((_) => driver.wait(until.titleIs('webdriver - Google Search'), 1000))
57
+ .then(
58
+ (_) => driver.quit(),
59
+ (e) =>
60
+ driver.quit().then(() => {
61
+ throw e
62
+ })
63
+ )
@@ -0,0 +1,64 @@
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 Demonstrates how to use WebDriver's logging sysem.
20
+ */
21
+
22
+ 'use strict'
23
+
24
+ const chrome = require('../chrome')
25
+ const firefox = require('../firefox')
26
+ const edge = require('../edge')
27
+ const { Builder, By, Key, logging, until } = require('..')
28
+
29
+ logging.installConsoleHandler()
30
+ logging.getLogger('webdriver.http').setLevel(logging.Level.ALL)
31
+ ;(async function () {
32
+ let driver
33
+ try {
34
+ driver = await new Builder()
35
+ // Default to using Firefox. This can be overridden at runtime by
36
+ // setting the SELENIUM_BROWSER environment variable:
37
+ //
38
+ // SELENIUM_BROWSER=chrome node selenium-webdriver/example/logging.js
39
+ .forBrowser('firefox')
40
+ // Configure the service for each browser to enable verbose logging and
41
+ // to inherit the stdio settings from the current process. The builder
42
+ // will only start the service if needed for the selected browser.
43
+ .setChromeService(
44
+ new chrome.ServiceBuilder().enableVerboseLogging().setStdio('inherit')
45
+ )
46
+ .setEdgeService(
47
+ process.platform === 'win32'
48
+ ? new edge.ServiceBuilder().enableVerboseLogging().setStdio('inherit')
49
+ : null
50
+ )
51
+ .setFirefoxService(
52
+ new firefox.ServiceBuilder().enableVerboseLogging().setStdio('inherit')
53
+ )
54
+ .build()
55
+
56
+ await driver.get('http://www.google.com/ncr')
57
+ await driver.findElement(By.name('q')).sendKeys('webdriver', Key.RETURN)
58
+ await driver.wait(until.titleIs('webdriver - Google Search'), 1000)
59
+ } finally {
60
+ if (driver) {
61
+ await driver.quit()
62
+ }
63
+ }
64
+ })()
@@ -0,0 +1,789 @@
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 the {@linkplain Driver WebDriver} client for Firefox.
20
+ * Before using this module, you must download the latest
21
+ * [geckodriver release] and ensure it can be found on your system [PATH].
22
+ *
23
+ * Each FirefoxDriver instance will be created with an anonymous profile,
24
+ * ensuring browser historys do not share session data (cookies, history, cache,
25
+ * offline storage, etc.)
26
+ *
27
+ * __Customizing the Firefox Profile__
28
+ *
29
+ * The profile used for each WebDriver session may be configured using the
30
+ * {@linkplain Options} class. For example, you may install an extension, like
31
+ * Firebug:
32
+ *
33
+ * const {Builder} = require('selenium-webdriver');
34
+ * const firefox = require('selenium-webdriver/firefox');
35
+ *
36
+ * let options = new firefox.Options()
37
+ * .addExtensions('/path/to/firebug.xpi')
38
+ * .setPreference('extensions.firebug.showChromeErrors', true);
39
+ *
40
+ * let driver = new Builder()
41
+ * .forBrowser('firefox')
42
+ * .setFirefoxOptions(options)
43
+ * .build();
44
+ *
45
+ * The {@linkplain Options} class may also be used to configure WebDriver based
46
+ * on a pre-existing browser profile:
47
+ *
48
+ * let profile = '/usr/local/home/bob/.mozilla/firefox/3fgog75h.testing';
49
+ * let options = new firefox.Options().setProfile(profile);
50
+ *
51
+ * The FirefoxDriver will _never_ modify a pre-existing profile; instead it will
52
+ * create a copy for it to modify. By extension, there are certain browser
53
+ * preferences that are required for WebDriver to function properly and they
54
+ * will always be overwritten.
55
+ *
56
+ * __Using a Custom Firefox Binary__
57
+ *
58
+ * On Windows and MacOS, the FirefoxDriver will search for Firefox in its
59
+ * default installation location:
60
+ *
61
+ * - Windows: C:\Program Files and C:\Program Files (x86).
62
+ * - MacOS: /Applications/Firefox.app
63
+ *
64
+ * For Linux, Firefox will always be located on the PATH: `$(where firefox)`.
65
+ *
66
+ * Several methods are provided for starting Firefox with a custom executable.
67
+ * First, on Windows and MacOS, you may configure WebDriver to check the default
68
+ * install location for a non-release channel. If the requested channel cannot
69
+ * be found in its default location, WebDriver will fallback to searching your
70
+ * PATH. _Note:_ on Linux, Firefox is _always_ located on your path, regardless
71
+ * of the requested channel.
72
+ *
73
+ * const {Builder} = require('selenium-webdriver');
74
+ * const firefox = require('selenium-webdriver/firefox');
75
+ *
76
+ * let options = new firefox.Options().setBinary(firefox.Channel.NIGHTLY);
77
+ * let driver = new Builder()
78
+ * .forBrowser('firefox')
79
+ * .setFirefoxOptions(options)
80
+ * .build();
81
+ *
82
+ * On all platforms, you may configure WebDriver to use a Firefox specific
83
+ * executable:
84
+ *
85
+ * let options = new firefox.Options()
86
+ * .setBinary('/my/firefox/install/dir/firefox-bin');
87
+ *
88
+ * __Remote Testing__
89
+ *
90
+ * You may customize the Firefox binary and profile when running against a
91
+ * remote Selenium server. Your custom profile will be packaged as a zip and
92
+ * transferred to the remote host for use. The profile will be transferred
93
+ * _once for each new session_. The performance impact should be minimal if
94
+ * you've only configured a few extra browser preferences. If you have a large
95
+ * profile with several extensions, you should consider installing it on the
96
+ * remote host and defining its path via the {@link Options} class. Custom
97
+ * binaries are never copied to remote machines and must be referenced by
98
+ * installation path.
99
+ *
100
+ * const {Builder} = require('selenium-webdriver');
101
+ * const firefox = require('selenium-webdriver/firefox');
102
+ *
103
+ * let options = new firefox.Options()
104
+ * .setProfile('/profile/path/on/remote/host')
105
+ * .setBinary('/install/dir/on/remote/host/firefox-bin');
106
+ *
107
+ * let driver = new Builder()
108
+ * .forBrowser('firefox')
109
+ * .usingServer('http://127.0.0.1:4444/wd/hub')
110
+ * .setFirefoxOptions(options)
111
+ * .build();
112
+ *
113
+ * [geckodriver release]: https://github.com/mozilla/geckodriver/releases/
114
+ * [PATH]: http://en.wikipedia.org/wiki/PATH_%28variable%29
115
+ */
116
+
117
+ 'use strict'
118
+
119
+ const path = require('path')
120
+ const Symbols = require('./lib/symbols')
121
+ const command = require('./lib/command')
122
+ const http = require('./http')
123
+ const io = require('./io')
124
+ const remote = require('./remote')
125
+ const webdriver = require('./lib/webdriver')
126
+ const zip = require('./io/zip')
127
+ const cdp = require('./devtools/CDPConnection')
128
+ const { Browser, Capabilities } = require('./lib/capabilities')
129
+ const { Zip } = require('./io/zip')
130
+
131
+ /**
132
+ * Thrown when there an add-on is malformed.
133
+ * @final
134
+ */
135
+ class AddonFormatError extends Error {
136
+ /** @param {string} msg The error message. */
137
+ constructor(msg) {
138
+ super(msg)
139
+ /** @override */
140
+ this.name = this.constructor.name
141
+ }
142
+ }
143
+
144
+ /**
145
+ * Installs an extension to the given directory.
146
+ * @param {string} extension Path to the xpi extension file to install.
147
+ * @param {string} dir Path to the directory to install the extension in.
148
+ * @return {!Promise<string>} A promise for the add-on ID once
149
+ * installed.
150
+ */
151
+ async function installExtension(extension, dir) {
152
+ const ext = extension.slice(-4)
153
+ if (ext !== '.xpi' && ext !== '.zip') {
154
+ throw Error('File name does not end in ".zip" or ".xpi": ' + ext)
155
+ }
156
+
157
+ let archive = await zip.load(extension)
158
+ if (!archive.has('manifest.json')) {
159
+ throw new AddonFormatError(`Couldn't find manifest.json in ${extension}`)
160
+ }
161
+
162
+ let buf = await archive.getFile('manifest.json')
163
+ let parsedJSON = JSON.parse(buf.toString('utf8'))
164
+
165
+ let { browser_specific_settings } =
166
+ /** @type {{browser_specific_settings:{gecko:{id:string}}}} */
167
+ parsedJSON
168
+
169
+ if (browser_specific_settings && browser_specific_settings.gecko) {
170
+ /* browser_specific_settings is an alternative to applications
171
+ * It is meant to facilitate cross-browser plugins since Firefox48
172
+ * see https://bugzilla.mozilla.org/show_bug.cgi?id=1262005
173
+ */
174
+ parsedJSON.applications = browser_specific_settings
175
+ }
176
+
177
+ let { applications } =
178
+ /** @type {{applications:{gecko:{id:string}}}} */
179
+ parsedJSON
180
+ if (!(applications && applications.gecko && applications.gecko.id)) {
181
+ throw new AddonFormatError(`Could not find add-on ID for ${extension}`)
182
+ }
183
+
184
+ await io.copy(extension, `${path.join(dir, applications.gecko.id)}.xpi`)
185
+ return applications.gecko.id
186
+ }
187
+
188
+ class Profile {
189
+ constructor() {
190
+ /** @private {?string} */
191
+ this.template_ = null
192
+
193
+ /** @private {!Array<string>} */
194
+ this.extensions_ = []
195
+ }
196
+
197
+ addExtensions(/** !Array<string> */ paths) {
198
+ this.extensions_ = this.extensions_.concat(...paths)
199
+ }
200
+
201
+ /**
202
+ * @return {(!Promise<string>|undefined)} a promise for a base64 encoded
203
+ * profile, or undefined if there's no data to include.
204
+ */
205
+ [Symbols.serialize]() {
206
+ if (this.template_ || this.extensions_.length) {
207
+ return buildProfile(this.template_, this.extensions_)
208
+ }
209
+ return undefined
210
+ }
211
+ }
212
+
213
+ /**
214
+ * @param {?string} template path to an existing profile to use as a template.
215
+ * @param {!Array<string>} extensions paths to extensions to install in the new
216
+ * profile.
217
+ * @return {!Promise<string>} a promise for the base64 encoded profile.
218
+ */
219
+ async function buildProfile(template, extensions) {
220
+ let dir = template
221
+
222
+ if (extensions.length) {
223
+ dir = await io.tmpDir()
224
+ if (template) {
225
+ await io.copyDir(
226
+ /** @type {string} */(template),
227
+ dir,
228
+ /(parent\.lock|lock|\.parentlock)/
229
+ )
230
+ }
231
+
232
+ const extensionsDir = path.join(dir, 'extensions')
233
+ await io.mkdir(extensionsDir)
234
+
235
+ for (let i = 0; i < extensions.length; i++) {
236
+ await installExtension(extensions[i], extensionsDir)
237
+ }
238
+ }
239
+
240
+ let zip = new Zip()
241
+ return zip
242
+ .addDir(dir)
243
+ .then(() => zip.toBuffer())
244
+ .then((buf) => buf.toString('base64'))
245
+ }
246
+
247
+ /**
248
+ * Configuration options for the FirefoxDriver.
249
+ */
250
+ class Options extends Capabilities {
251
+ /**
252
+ * @param {(Capabilities|Map<string, ?>|Object)=} other Another set of
253
+ * capabilities to initialize this instance from.
254
+ */
255
+ constructor(other) {
256
+ super(other)
257
+ this.setBrowserName(Browser.FIREFOX)
258
+ }
259
+
260
+ /**
261
+ * @return {!Object}
262
+ * @private
263
+ */
264
+ firefoxOptions_() {
265
+ let options = this.get('moz:firefoxOptions')
266
+ if (!options) {
267
+ options = {}
268
+ this.set('moz:firefoxOptions', options)
269
+ }
270
+ return options
271
+ }
272
+
273
+ /**
274
+ * @return {!Profile}
275
+ * @private
276
+ */
277
+ profile_() {
278
+ let options = this.firefoxOptions_()
279
+ if (!options.profile) {
280
+ options.profile = new Profile()
281
+ }
282
+ return options.profile
283
+ }
284
+
285
+ /**
286
+ * Specify additional command line arguments that should be used when starting
287
+ * the Firefox browser.
288
+ *
289
+ * @param {...(string|!Array<string>)} args The arguments to include.
290
+ * @return {!Options} A self reference.
291
+ */
292
+ addArguments(...args) {
293
+ if (args.length) {
294
+ let options = this.firefoxOptions_()
295
+ options.args = options.args ? options.args.concat(...args) : args
296
+ }
297
+ return this
298
+ }
299
+
300
+ /**
301
+ * Configures the geckodriver to start Firefox in headless mode.
302
+ *
303
+ * @return {!Options} A self reference.
304
+ */
305
+ headless() {
306
+ return this.addArguments('-headless')
307
+ }
308
+
309
+ /**
310
+ * Sets the initial window size when running in
311
+ * {@linkplain #headless headless} mode.
312
+ *
313
+ * @param {{width: number, height: number}} size The desired window size.
314
+ * @return {!Options} A self reference.
315
+ * @throws {TypeError} if width or height is unspecified, not a number, or
316
+ * less than or equal to 0.
317
+ */
318
+ windowSize({ width, height }) {
319
+ function checkArg(arg) {
320
+ if (typeof arg !== 'number' || arg <= 0) {
321
+ throw TypeError('Arguments must be {width, height} with numbers > 0')
322
+ }
323
+ }
324
+ checkArg(width)
325
+ checkArg(height)
326
+ return this.addArguments(`--width=${width}`, `--height=${height}`)
327
+ }
328
+
329
+ /**
330
+ * Add extensions that should be installed when starting Firefox.
331
+ *
332
+ * @param {...string} paths The paths to the extension XPI files to install.
333
+ * @return {!Options} A self reference.
334
+ */
335
+ addExtensions(...paths) {
336
+ this.profile_().addExtensions(paths)
337
+ return this
338
+ }
339
+
340
+ /**
341
+ * @param {string} key the preference key.
342
+ * @param {(string|number|boolean)} value the preference value.
343
+ * @return {!Options} A self reference.
344
+ * @throws {TypeError} if either the key or value has an invalid type.
345
+ */
346
+ setPreference(key, value) {
347
+ if (typeof key !== 'string') {
348
+ throw TypeError(`key must be a string, but got ${typeof key}`)
349
+ }
350
+ if (
351
+ typeof value !== 'string' &&
352
+ typeof value !== 'number' &&
353
+ typeof value !== 'boolean'
354
+ ) {
355
+ throw TypeError(
356
+ `value must be a string, number, or boolean, but got ${typeof value}`
357
+ )
358
+ }
359
+ let options = this.firefoxOptions_()
360
+ options.prefs = options.prefs || {}
361
+ options.prefs[key] = value
362
+ return this
363
+ }
364
+
365
+ /**
366
+ * Sets the path to an existing profile to use as a template for new browser
367
+ * sessions. This profile will be copied for each new session - changes will
368
+ * not be applied to the profile itself.
369
+ *
370
+ * @param {string} profile The profile to use.
371
+ * @return {!Options} A self reference.
372
+ * @throws {TypeError} if profile is not a string.
373
+ */
374
+ setProfile(profile) {
375
+ if (typeof profile !== 'string') {
376
+ throw TypeError(`profile must be a string, but got ${typeof profile}`)
377
+ }
378
+ this.profile_().template_ = profile
379
+ return this
380
+ }
381
+
382
+ /**
383
+ * Sets the binary to use. The binary may be specified as the path to a
384
+ * Firefox executable or a desired release {@link Channel}.
385
+ *
386
+ * @param {(string|!Channel)} binary The binary to use.
387
+ * @return {!Options} A self reference.
388
+ * @throws {TypeError} If `binary` is an invalid type.
389
+ */
390
+ setBinary(binary) {
391
+ if (binary instanceof Channel || typeof binary === 'string') {
392
+ this.firefoxOptions_().binary = binary
393
+ return this
394
+ }
395
+ throw TypeError('binary must be a string path or Channel object')
396
+ }
397
+ }
398
+
399
+ /**
400
+ * Enum of available command contexts.
401
+ *
402
+ * Command contexts are specific to Marionette, and may be used with the
403
+ * {@link #context=} method. Contexts allow you to direct all subsequent
404
+ * commands to either "content" (default) or "chrome". The latter gives
405
+ * you elevated security permissions.
406
+ *
407
+ * @enum {string}
408
+ */
409
+ const Context = {
410
+ CONTENT: 'content',
411
+ CHROME: 'chrome',
412
+ }
413
+
414
+ const GECKO_DRIVER_EXE =
415
+ process.platform === 'win32' ? 'geckodriver.exe' : 'geckodriver'
416
+
417
+ /**
418
+ * _Synchronously_ attempts to locate the geckodriver executable on the current
419
+ * system.
420
+ *
421
+ * @return {?string} the located executable, or `null`.
422
+ */
423
+ function locateSynchronously() {
424
+ return io.findInPath(GECKO_DRIVER_EXE, true)
425
+ }
426
+
427
+ /**
428
+ * @return {string} .
429
+ * @throws {Error}
430
+ */
431
+ function findGeckoDriver() {
432
+ let exe = locateSynchronously()
433
+ if (!exe) {
434
+ throw Error(
435
+ 'The ' +
436
+ GECKO_DRIVER_EXE +
437
+ ' executable could not be found on the current ' +
438
+ 'PATH. Please download the latest version from ' +
439
+ 'https://github.com/mozilla/geckodriver/releases/ ' +
440
+ 'and ensure it can be found on your PATH.'
441
+ )
442
+ }
443
+ return exe
444
+ }
445
+
446
+ /**
447
+ * @param {string} file Path to the file to find, relative to the program files
448
+ * root.
449
+ * @return {!Promise<?string>} A promise for the located executable.
450
+ * The promise will resolve to {@code null} if Firefox was not found.
451
+ */
452
+ function findInProgramFiles(file) {
453
+ let files = [
454
+ process.env['PROGRAMFILES'] || 'C:\\Program Files',
455
+ process.env['PROGRAMFILES(X86)'] || 'C:\\Program Files (x86)',
456
+ ].map((prefix) => path.join(prefix, file))
457
+ return io.exists(files[0]).then(function (exists) {
458
+ return exists
459
+ ? files[0]
460
+ : io.exists(files[1]).then(function (exists) {
461
+ return exists ? files[1] : null
462
+ })
463
+ })
464
+ }
465
+
466
+ /** @enum {string} */
467
+ const ExtensionCommand = {
468
+ GET_CONTEXT: 'getContext',
469
+ SET_CONTEXT: 'setContext',
470
+ INSTALL_ADDON: 'install addon',
471
+ UNINSTALL_ADDON: 'uninstall addon',
472
+ }
473
+
474
+ /**
475
+ * Creates a command executor with support for Marionette's custom commands.
476
+ * @param {!Promise<string>} serverUrl The server's URL.
477
+ * @return {!command.Executor} The new command executor.
478
+ */
479
+ function createExecutor(serverUrl) {
480
+ let client = serverUrl.then((url) => new http.HttpClient(url))
481
+ let executor = new http.Executor(client)
482
+ configureExecutor(executor)
483
+ return executor
484
+ }
485
+
486
+ /**
487
+ * Configures the given executor with Firefox-specific commands.
488
+ * @param {!http.Executor} executor the executor to configure.
489
+ */
490
+ function configureExecutor(executor) {
491
+ executor.defineCommand(
492
+ ExtensionCommand.GET_CONTEXT,
493
+ 'GET',
494
+ '/session/:sessionId/moz/context'
495
+ )
496
+
497
+ executor.defineCommand(
498
+ ExtensionCommand.SET_CONTEXT,
499
+ 'POST',
500
+ '/session/:sessionId/moz/context'
501
+ )
502
+
503
+ executor.defineCommand(
504
+ ExtensionCommand.INSTALL_ADDON,
505
+ 'POST',
506
+ '/session/:sessionId/moz/addon/install'
507
+ )
508
+
509
+ executor.defineCommand(
510
+ ExtensionCommand.UNINSTALL_ADDON,
511
+ 'POST',
512
+ '/session/:sessionId/moz/addon/uninstall'
513
+ )
514
+ }
515
+
516
+ /**
517
+ * Creates {@link selenium-webdriver/remote.DriverService} instances that manage
518
+ * a [geckodriver](https://github.com/mozilla/geckodriver) server in a child
519
+ * process.
520
+ */
521
+ class ServiceBuilder extends remote.DriverService.Builder {
522
+ /**
523
+ * @param {string=} opt_exe Path to the server executable to use. If omitted,
524
+ * the builder will attempt to locate the geckodriver on the system PATH.
525
+ */
526
+ constructor(opt_exe) {
527
+ super(opt_exe || findGeckoDriver())
528
+ this.setLoopback(true) // Required.
529
+ }
530
+
531
+ /**
532
+ * Enables verbose logging.
533
+ *
534
+ * @param {boolean=} opt_trace Whether to enable trace-level logging. By
535
+ * default, only debug logging is enabled.
536
+ * @return {!ServiceBuilder} A self reference.
537
+ */
538
+ enableVerboseLogging(opt_trace) {
539
+ return this.addArguments(opt_trace ? '-vv' : '-v')
540
+ }
541
+ }
542
+
543
+ /**
544
+ * A WebDriver client for Firefox.
545
+ */
546
+ class Driver extends webdriver.WebDriver {
547
+ /**
548
+ * Creates a new Firefox session.
549
+ *
550
+ * @param {(Options|Capabilities|Object)=} opt_config The
551
+ * configuration options for this driver, specified as either an
552
+ * {@link Options} or {@link Capabilities}, or as a raw hash object.
553
+ * @param {(http.Executor|remote.DriverService)=} opt_executor Either a
554
+ * pre-configured command executor to use for communicating with an
555
+ * externally managed remote end (which is assumed to already be running),
556
+ * or the `DriverService` to use to start the geckodriver in a child
557
+ * process.
558
+ *
559
+ * If an executor is provided, care should e taken not to use reuse it with
560
+ * other clients as its internal command mappings will be updated to support
561
+ * Firefox-specific commands.
562
+ *
563
+ * _This parameter may only be used with Mozilla's GeckoDriver._
564
+ *
565
+ * @throws {Error} If a custom command executor is provided and the driver is
566
+ * configured to use the legacy FirefoxDriver from the Selenium project.
567
+ * @return {!Driver} A new driver instance.
568
+ */
569
+ static createSession(opt_config, opt_executor) {
570
+ let caps =
571
+ opt_config instanceof Capabilities ? opt_config : new Options(opt_config)
572
+
573
+ let executor
574
+ let onQuit
575
+
576
+ if (opt_executor instanceof http.Executor) {
577
+ executor = opt_executor
578
+ configureExecutor(executor)
579
+ } else if (opt_executor instanceof remote.DriverService) {
580
+ executor = createExecutor(opt_executor.start())
581
+ onQuit = () => opt_executor.kill()
582
+ } else {
583
+ let service = new ServiceBuilder().build()
584
+ executor = createExecutor(service.start())
585
+ onQuit = () => service.kill()
586
+ }
587
+
588
+ return /** @type {!Driver} */ (super.createSession(executor, caps, onQuit))
589
+ }
590
+
591
+ /**
592
+ * This function is a no-op as file detectors are not supported by this
593
+ * implementation.
594
+ * @override
595
+ */
596
+ setFileDetector() { }
597
+
598
+ /**
599
+ * Get the context that is currently in effect.
600
+ *
601
+ * @return {!Promise<Context>} Current context.
602
+ */
603
+ getContext() {
604
+ return this.execute(new command.Command(ExtensionCommand.GET_CONTEXT))
605
+ }
606
+
607
+ /**
608
+ * Changes target context for commands between chrome- and content.
609
+ *
610
+ * Changing the current context has a stateful impact on all subsequent
611
+ * commands. The {@link Context.CONTENT} context has normal web
612
+ * platform document permissions, as if you would evaluate arbitrary
613
+ * JavaScript. The {@link Context.CHROME} context gets elevated
614
+ * permissions that lets you manipulate the browser chrome itself,
615
+ * with full access to the XUL toolkit.
616
+ *
617
+ * Use your powers wisely.
618
+ *
619
+ * @param {!Promise<void>} ctx The context to switch to.
620
+ */
621
+ setContext(ctx) {
622
+ return this.execute(
623
+ new command.Command(ExtensionCommand.SET_CONTEXT).setParameter(
624
+ 'context',
625
+ ctx
626
+ )
627
+ )
628
+ }
629
+
630
+ /**
631
+ * Installs a new addon with the current session. This function will return an
632
+ * ID that may later be used to {@linkplain #uninstallAddon uninstall} the
633
+ * addon.
634
+ *
635
+ *
636
+ * @param {string} path Path on the local filesystem to the web extension to
637
+ * install.
638
+ * @param {boolean} temporary Flag indicating whether the extension should be
639
+ * installed temporarily - gets removed on restart
640
+ * @return {!Promise<string>} A promise that will resolve to an ID for the
641
+ * newly installed addon.
642
+ * @see #uninstallAddon
643
+ */
644
+ async installAddon(path, temporary = false) {
645
+ let buf = await io.read(path)
646
+ return this.execute(
647
+ new command.Command(ExtensionCommand.INSTALL_ADDON)
648
+ .setParameter('addon', buf.toString('base64'))
649
+ .setParameter('temporary', temporary)
650
+ )
651
+ }
652
+
653
+ /**
654
+ * Uninstalls an addon from the current browser session's profile.
655
+ *
656
+ * @param {(string|!Promise<string>)} id ID of the addon to uninstall.
657
+ * @return {!Promise} A promise that will resolve when the operation has
658
+ * completed.
659
+ * @see #installAddon
660
+ */
661
+ async uninstallAddon(id) {
662
+ id = await Promise.resolve(id)
663
+ return this.execute(
664
+ new command.Command(ExtensionCommand.UNINSTALL_ADDON).setParameter(
665
+ 'id',
666
+ id
667
+ )
668
+ )
669
+ }
670
+ }
671
+
672
+ /**
673
+ * Provides methods for locating the executable for a Firefox release channel
674
+ * on Windows and MacOS. For other systems (i.e. Linux), Firefox will always
675
+ * be located on the system PATH.
676
+ *
677
+ * @final
678
+ */
679
+ class Channel {
680
+ /**
681
+ * @param {string} darwin The path to check when running on MacOS.
682
+ * @param {string} win32 The path to check when running on Windows.
683
+ */
684
+ constructor(darwin, win32) {
685
+ /** @private @const */ this.darwin_ = darwin
686
+ /** @private @const */ this.win32_ = win32
687
+ /** @private {Promise<string>} */
688
+ this.found_ = null
689
+ }
690
+
691
+ /**
692
+ * Attempts to locate the Firefox executable for this release channel. This
693
+ * will first check the default installation location for the channel before
694
+ * checking the user's PATH. The returned promise will be rejected if Firefox
695
+ * can not be found.
696
+ *
697
+ * @return {!Promise<string>} A promise for the location of the located
698
+ * Firefox executable.
699
+ */
700
+ locate() {
701
+ if (this.found_) {
702
+ return this.found_
703
+ }
704
+
705
+ let found
706
+ switch (process.platform) {
707
+ case 'darwin':
708
+ found = io
709
+ .exists(this.darwin_)
710
+ .then((exists) => (exists ? this.darwin_ : io.findInPath('firefox')))
711
+ break
712
+
713
+ case 'win32':
714
+ found = findInProgramFiles(this.win32_).then(
715
+ (found) => found || io.findInPath('firefox.exe')
716
+ )
717
+ break
718
+
719
+ default:
720
+ found = Promise.resolve(io.findInPath('firefox'))
721
+ break
722
+ }
723
+
724
+ this.found_ = found.then((found) => {
725
+ if (found) {
726
+ // TODO: verify version info.
727
+ return found
728
+ }
729
+ throw Error('Could not locate Firefox on the current system')
730
+ })
731
+ return this.found_
732
+ }
733
+
734
+ /** @return {!Promise<string>} */
735
+ [Symbols.serialize]() {
736
+ return this.locate()
737
+ }
738
+ }
739
+
740
+ /**
741
+ * Firefox's developer channel.
742
+ * @const
743
+ * @see <https://www.mozilla.org/en-US/firefox/channel/desktop/#aurora>
744
+ */
745
+ Channel.AURORA = new Channel(
746
+ '/Applications/FirefoxDeveloperEdition.app/Contents/MacOS/firefox-bin',
747
+ 'Firefox Developer Edition\\firefox.exe'
748
+ )
749
+
750
+ /**
751
+ * Firefox's beta channel. Note this is provided mainly for convenience as
752
+ * the beta channel has the same installation location as the main release
753
+ * channel.
754
+ * @const
755
+ * @see <https://www.mozilla.org/en-US/firefox/channel/desktop/#beta>
756
+ */
757
+ Channel.BETA = new Channel(
758
+ '/Applications/Firefox.app/Contents/MacOS/firefox-bin',
759
+ 'Mozilla Firefox\\firefox.exe'
760
+ )
761
+
762
+ /**
763
+ * Firefox's release channel.
764
+ * @const
765
+ * @see <https://www.mozilla.org/en-US/firefox/desktop/>
766
+ */
767
+ Channel.RELEASE = new Channel(
768
+ '/Applications/Firefox.app/Contents/MacOS/firefox-bin',
769
+ 'Mozilla Firefox\\firefox.exe'
770
+ )
771
+
772
+ /**
773
+ * Firefox's nightly release channel.
774
+ * @const
775
+ * @see <https://www.mozilla.org/en-US/firefox/channel/desktop/#nightly>
776
+ */
777
+ Channel.NIGHTLY = new Channel(
778
+ '/Applications/Firefox Nightly.app/Contents/MacOS/firefox-bin',
779
+ 'Nightly\\firefox.exe'
780
+ )
781
+
782
+ // PUBLIC API
783
+
784
+ exports.Channel = Channel
785
+ exports.Context = Context
786
+ exports.Driver = Driver
787
+ exports.Options = Options
788
+ exports.ServiceBuilder = ServiceBuilder
789
+ exports.locateSynchronously = locateSynchronously