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,2919 @@
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 The heart of the WebDriver JavaScript API.
20
+ */
21
+
22
+ 'use strict'
23
+
24
+ const by = require('./by')
25
+ const { RelativeBy } = require('./by')
26
+ const command = require('./command')
27
+ const error = require('./error')
28
+ const input = require('./input')
29
+ const logging = require('./logging')
30
+ const promise = require('./promise')
31
+ const Symbols = require('./symbols')
32
+ const cdpTargets = ['page', 'browser']
33
+ const cdp = require('../devtools/CDPConnection')
34
+ const WebSocket = require('ws')
35
+ const http = require('../http/index')
36
+ const fs = require('fs')
37
+ const { Capabilities } = require('./capabilities')
38
+ const path = require('path')
39
+
40
+ // Capability names that are defined in the W3C spec.
41
+ const W3C_CAPABILITY_NAMES = new Set([
42
+ 'acceptInsecureCerts',
43
+ 'browserName',
44
+ 'browserVersion',
45
+ 'platformName',
46
+ 'pageLoadStrategy',
47
+ 'proxy',
48
+ 'setWindowRect',
49
+ 'timeouts',
50
+ 'strictFileInteractability',
51
+ 'unhandledPromptBehavior',
52
+ ])
53
+
54
+ /**
55
+ * Defines a condition for use with WebDriver's {@linkplain WebDriver#wait wait
56
+ * command}.
57
+ *
58
+ * @template OUT
59
+ */
60
+ class Condition {
61
+ /**
62
+ * @param {string} message A descriptive error message. Should complete the
63
+ * sentence "Waiting [...]"
64
+ * @param {function(!WebDriver): OUT} fn The condition function to
65
+ * evaluate on each iteration of the wait loop.
66
+ */
67
+ constructor(message, fn) {
68
+ /** @private {string} */
69
+ this.description_ = 'Waiting ' + message
70
+
71
+ /** @type {function(!WebDriver): OUT} */
72
+ this.fn = fn
73
+ }
74
+
75
+ /** @return {string} A description of this condition. */
76
+ description() {
77
+ return this.description_
78
+ }
79
+ }
80
+
81
+ /**
82
+ * Defines a condition that will result in a {@link WebElement}.
83
+ *
84
+ * @extends {Condition<!(WebElement|IThenable<!WebElement>)>}
85
+ */
86
+ class WebElementCondition extends Condition {
87
+ /**
88
+ * @param {string} message A descriptive error message. Should complete the
89
+ * sentence "Waiting [...]"
90
+ * @param {function(!WebDriver): !(WebElement|IThenable<!WebElement>)}
91
+ * fn The condition function to evaluate on each iteration of the wait
92
+ * loop.
93
+ */
94
+ constructor(message, fn) {
95
+ super(message, fn)
96
+ }
97
+ }
98
+
99
+ //////////////////////////////////////////////////////////////////////////////
100
+ //
101
+ // WebDriver
102
+ //
103
+ //////////////////////////////////////////////////////////////////////////////
104
+
105
+ /**
106
+ * Translates a command to its wire-protocol representation before passing it
107
+ * to the given `executor` for execution.
108
+ * @param {!command.Executor} executor The executor to use.
109
+ * @param {!command.Command} command The command to execute.
110
+ * @return {!Promise} A promise that will resolve with the command response.
111
+ */
112
+ function executeCommand(executor, command) {
113
+ return toWireValue(command.getParameters()).then(function (parameters) {
114
+ command.setParameters(parameters)
115
+ return executor.execute(command)
116
+ })
117
+ }
118
+
119
+ /**
120
+ * Converts an object to its JSON representation in the WebDriver wire protocol.
121
+ * When converting values of type object, the following steps will be taken:
122
+ * <ol>
123
+ * <li>if the object is a WebElement, the return value will be the element's
124
+ * server ID
125
+ * <li>if the object defines a {@link Symbols.serialize} method, this algorithm
126
+ * will be recursively applied to the object's serialized representation
127
+ * <li>if the object provides a "toJSON" function, this algorithm will
128
+ * recursively be applied to the result of that function
129
+ * <li>otherwise, the value of each key will be recursively converted according
130
+ * to the rules above.
131
+ * </ol>
132
+ *
133
+ * @param {*} obj The object to convert.
134
+ * @return {!Promise<?>} A promise that will resolve to the input value's JSON
135
+ * representation.
136
+ */
137
+ async function toWireValue(obj) {
138
+ let value = await Promise.resolve(obj)
139
+ if (value === void 0 || value === null) {
140
+ return value
141
+ }
142
+
143
+ if (
144
+ typeof value === 'boolean' ||
145
+ typeof value === 'number' ||
146
+ typeof value === 'string'
147
+ ) {
148
+ return value
149
+ }
150
+
151
+ if (Array.isArray(value)) {
152
+ return convertKeys(value)
153
+ }
154
+
155
+ if (typeof value === 'function') {
156
+ return '' + value
157
+ }
158
+
159
+ if (typeof value[Symbols.serialize] === 'function') {
160
+ return toWireValue(value[Symbols.serialize]())
161
+ } else if (typeof value.toJSON === 'function') {
162
+ return toWireValue(value.toJSON())
163
+ }
164
+ return convertKeys(value)
165
+ }
166
+
167
+ async function convertKeys(obj) {
168
+ const isArray = Array.isArray(obj)
169
+ const numKeys = isArray ? obj.length : Object.keys(obj).length
170
+ const ret = isArray ? new Array(numKeys) : {}
171
+ if (!numKeys) {
172
+ return ret
173
+ }
174
+
175
+ async function forEachKey(obj, fn) {
176
+ if (Array.isArray(obj)) {
177
+ for (let i = 0, n = obj.length; i < n; i++) {
178
+ await fn(obj[i], i)
179
+ }
180
+ } else {
181
+ for (let key in obj) {
182
+ await fn(obj[key], key)
183
+ }
184
+ }
185
+ }
186
+
187
+ await forEachKey(obj, async function (value, key) {
188
+ ret[key] = await toWireValue(value)
189
+ })
190
+
191
+ return ret
192
+ }
193
+
194
+ /**
195
+ * Converts a value from its JSON representation according to the WebDriver wire
196
+ * protocol. Any JSON object that defines a WebElement ID will be decoded to a
197
+ * {@link WebElement} object. All other values will be passed through as is.
198
+ *
199
+ * @param {!WebDriver} driver The driver to use as the parent of any unwrapped
200
+ * {@link WebElement} values.
201
+ * @param {*} value The value to convert.
202
+ * @return {*} The converted value.
203
+ */
204
+ function fromWireValue(driver, value) {
205
+ if (Array.isArray(value)) {
206
+ value = value.map((v) => fromWireValue(driver, v))
207
+ } else if (WebElement.isId(value)) {
208
+ let id = WebElement.extractId(value)
209
+ value = new WebElement(driver, id)
210
+ } else if (value && typeof value === 'object') {
211
+ let result = {}
212
+ for (let key in value) {
213
+ if (Object.prototype.hasOwnProperty.call(value, key)) {
214
+ result[key] = fromWireValue(driver, value[key])
215
+ }
216
+ }
217
+ value = result
218
+ }
219
+ return value
220
+ }
221
+
222
+ /**
223
+ * Resolves a wait message from either a function or a string.
224
+ * @param {(string|Function)=} message An optional message to use if the wait times out.
225
+ * @return {string} The resolved message
226
+ */
227
+ function resolveWaitMessage(message) {
228
+ return message
229
+ ? `${typeof message === 'function' ? message() : message}\n`
230
+ : ''
231
+ }
232
+
233
+ /**
234
+ * Structural interface for a WebDriver client.
235
+ *
236
+ * @record
237
+ */
238
+ class IWebDriver {
239
+ /**
240
+ * Executes the provided {@link command.Command} using this driver's
241
+ * {@link command.Executor}.
242
+ *
243
+ * @param {!command.Command} command The command to schedule.
244
+ * @return {!Promise<T>} A promise that will be resolved with the command
245
+ * result.
246
+ * @template T
247
+ */
248
+ execute(command) { } // eslint-disable-line
249
+
250
+ /**
251
+ * Sets the {@linkplain input.FileDetector file detector} that should be
252
+ * used with this instance.
253
+ * @param {input.FileDetector} detector The detector to use or `null`.
254
+ */
255
+ setFileDetector(detector) { } // eslint-disable-line
256
+
257
+ /**
258
+ * @return {!command.Executor} The command executor used by this instance.
259
+ */
260
+ getExecutor() { }
261
+
262
+ /**
263
+ * @return {!Promise<!Session>} A promise for this client's session.
264
+ */
265
+ getSession() { }
266
+
267
+ /**
268
+ * @return {!Promise<!Capabilities>} A promise that will resolve with
269
+ * the this instance's capabilities.
270
+ */
271
+ getCapabilities() { }
272
+
273
+ /**
274
+ * Terminates the browser session. After calling quit, this instance will be
275
+ * invalidated and may no longer be used to issue commands against the
276
+ * browser.
277
+ *
278
+ * @return {!Promise<void>} A promise that will be resolved when the
279
+ * command has completed.
280
+ */
281
+ quit() { }
282
+
283
+ /**
284
+ * Creates a new action sequence using this driver. The sequence will not be
285
+ * submitted for execution until
286
+ * {@link ./input.Actions#perform Actions.perform()} is called.
287
+ *
288
+ * @param {{async: (boolean|undefined),
289
+ * bridge: (boolean|undefined)}=} options Configuration options for
290
+ * the action sequence (see {@link ./input.Actions Actions} documentation
291
+ * for details).
292
+ * @return {!input.Actions} A new action sequence for this instance.
293
+ */
294
+ actions(options) { } // eslint-disable-line
295
+
296
+ /**
297
+ * Executes a snippet of JavaScript in the context of the currently selected
298
+ * frame or window. The script fragment will be executed as the body of an
299
+ * anonymous function. If the script is provided as a function object, that
300
+ * function will be converted to a string for injection into the target
301
+ * window.
302
+ *
303
+ * Any arguments provided in addition to the script will be included as script
304
+ * arguments and may be referenced using the `arguments` object. Arguments may
305
+ * be a boolean, number, string, or {@linkplain WebElement}. Arrays and
306
+ * objects may also be used as script arguments as long as each item adheres
307
+ * to the types previously mentioned.
308
+ *
309
+ * The script may refer to any variables accessible from the current window.
310
+ * Furthermore, the script will execute in the window's context, thus
311
+ * `document` may be used to refer to the current document. Any local
312
+ * variables will not be available once the script has finished executing,
313
+ * though global variables will persist.
314
+ *
315
+ * If the script has a return value (i.e. if the script contains a return
316
+ * statement), then the following steps will be taken for resolving this
317
+ * functions return value:
318
+ *
319
+ * - For a HTML element, the value will resolve to a {@linkplain WebElement}
320
+ * - Null and undefined return values will resolve to null</li>
321
+ * - Booleans, numbers, and strings will resolve as is</li>
322
+ * - Functions will resolve to their string representation</li>
323
+ * - For arrays and objects, each member item will be converted according to
324
+ * the rules above
325
+ *
326
+ * @param {!(string|Function)} script The script to execute.
327
+ * @param {...*} args The arguments to pass to the script.
328
+ * @return {!IThenable<T>} A promise that will resolve to the
329
+ * scripts return value.
330
+ * @template T
331
+ */
332
+ executeScript(script, ...args) { } // eslint-disable-line
333
+
334
+ /**
335
+ * Executes a snippet of asynchronous JavaScript in the context of the
336
+ * currently selected frame or window. The script fragment will be executed as
337
+ * the body of an anonymous function. If the script is provided as a function
338
+ * object, that function will be converted to a string for injection into the
339
+ * target window.
340
+ *
341
+ * Any arguments provided in addition to the script will be included as script
342
+ * arguments and may be referenced using the `arguments` object. Arguments may
343
+ * be a boolean, number, string, or {@linkplain WebElement}. Arrays and
344
+ * objects may also be used as script arguments as long as each item adheres
345
+ * to the types previously mentioned.
346
+ *
347
+ * Unlike executing synchronous JavaScript with {@link #executeScript},
348
+ * scripts executed with this function must explicitly signal they are
349
+ * finished by invoking the provided callback. This callback will always be
350
+ * injected into the executed function as the last argument, and thus may be
351
+ * referenced with `arguments[arguments.length - 1]`. The following steps
352
+ * will be taken for resolving this functions return value against the first
353
+ * argument to the script's callback function:
354
+ *
355
+ * - For a HTML element, the value will resolve to a {@link WebElement}
356
+ * - Null and undefined return values will resolve to null
357
+ * - Booleans, numbers, and strings will resolve as is
358
+ * - Functions will resolve to their string representation
359
+ * - For arrays and objects, each member item will be converted according to
360
+ * the rules above
361
+ *
362
+ * __Example #1:__ Performing a sleep that is synchronized with the currently
363
+ * selected window:
364
+ *
365
+ * var start = new Date().getTime();
366
+ * driver.executeAsyncScript(
367
+ * 'window.setTimeout(arguments[arguments.length - 1], 500);').
368
+ * then(function() {
369
+ * console.log(
370
+ * 'Elapsed time: ' + (new Date().getTime() - start) + ' ms');
371
+ * });
372
+ *
373
+ * __Example #2:__ Synchronizing a test with an AJAX application:
374
+ *
375
+ * var button = driver.findElement(By.id('compose-button'));
376
+ * button.click();
377
+ * driver.executeAsyncScript(
378
+ * 'var callback = arguments[arguments.length - 1];' +
379
+ * 'mailClient.getComposeWindowWidget().onload(callback);');
380
+ * driver.switchTo().frame('composeWidget');
381
+ * driver.findElement(By.id('to')).sendKeys('dog@example.com');
382
+ *
383
+ * __Example #3:__ Injecting a XMLHttpRequest and waiting for the result. In
384
+ * this example, the inject script is specified with a function literal. When
385
+ * using this format, the function is converted to a string for injection, so
386
+ * it should not reference any symbols not defined in the scope of the page
387
+ * under test.
388
+ *
389
+ * driver.executeAsyncScript(function() {
390
+ * var callback = arguments[arguments.length - 1];
391
+ * var xhr = new XMLHttpRequest();
392
+ * xhr.open("GET", "/resource/data.json", true);
393
+ * xhr.onreadystatechange = function() {
394
+ * if (xhr.readyState == 4) {
395
+ * callback(xhr.responseText);
396
+ * }
397
+ * };
398
+ * xhr.send('');
399
+ * }).then(function(str) {
400
+ * console.log(JSON.parse(str)['food']);
401
+ * });
402
+ *
403
+ * @param {!(string|Function)} script The script to execute.
404
+ * @param {...*} args The arguments to pass to the script.
405
+ * @return {!IThenable<T>} A promise that will resolve to the scripts return
406
+ * value.
407
+ * @template T
408
+ */
409
+ executeAsyncScript(script, ...args) { } // eslint-disable-line
410
+
411
+ /**
412
+ * Waits for a condition to evaluate to a "truthy" value. The condition may be
413
+ * specified by a {@link Condition}, as a custom function, or as any
414
+ * promise-like thenable.
415
+ *
416
+ * For a {@link Condition} or function, the wait will repeatedly
417
+ * evaluate the condition until it returns a truthy value. If any errors occur
418
+ * while evaluating the condition, they will be allowed to propagate. In the
419
+ * event a condition returns a {@linkplain Promise}, the polling loop will
420
+ * wait for it to be resolved and use the resolved value for whether the
421
+ * condition has been satisfied. The resolution time for a promise is always
422
+ * factored into whether a wait has timed out.
423
+ *
424
+ * If the provided condition is a {@link WebElementCondition}, then
425
+ * the wait will return a {@link WebElementPromise} that will resolve to the
426
+ * element that satisfied the condition.
427
+ *
428
+ * _Example:_ waiting up to 10 seconds for an element to be present on the
429
+ * page.
430
+ *
431
+ * async function example() {
432
+ * let button =
433
+ * await driver.wait(until.elementLocated(By.id('foo')), 10000);
434
+ * await button.click();
435
+ * }
436
+ *
437
+ * @param {!(IThenable<T>|
438
+ * Condition<T>|
439
+ * function(!WebDriver): T)} condition The condition to
440
+ * wait on, defined as a promise, condition object, or a function to
441
+ * evaluate as a condition.
442
+ * @param {number=} timeout The duration in milliseconds, how long to wait
443
+ * for the condition to be true.
444
+ * @param {(string|Function)=} message An optional message to use if the wait times out.
445
+ * @param {number=} pollTimeout The duration in milliseconds, how long to
446
+ * wait between polling the condition.
447
+ * @return {!(IThenable<T>|WebElementPromise)} A promise that will be
448
+ * resolved with the first truthy value returned by the condition
449
+ * function, or rejected if the condition times out. If the input
450
+ * input condition is an instance of a {@link WebElementCondition},
451
+ * the returned value will be a {@link WebElementPromise}.
452
+ * @throws {TypeError} if the provided `condition` is not a valid type.
453
+ * @template T
454
+ */
455
+ wait(
456
+ condition, // eslint-disable-line
457
+ timeout = undefined, // eslint-disable-line
458
+ message = undefined, // eslint-disable-line
459
+ pollTimeout = undefined // eslint-disable-line
460
+ ) { }
461
+
462
+ /**
463
+ * Makes the driver sleep for the given amount of time.
464
+ *
465
+ * @param {number} ms The amount of time, in milliseconds, to sleep.
466
+ * @return {!Promise<void>} A promise that will be resolved when the sleep has
467
+ * finished.
468
+ */
469
+ sleep(ms) { } // eslint-disable-line
470
+
471
+ /**
472
+ * Retrieves the current window handle.
473
+ *
474
+ * @return {!Promise<string>} A promise that will be resolved with the current
475
+ * window handle.
476
+ */
477
+ getWindowHandle() { }
478
+
479
+ /**
480
+ * Retrieves a list of all available window handles.
481
+ *
482
+ * @return {!Promise<!Array<string>>} A promise that will be resolved with an
483
+ * array of window handles.
484
+ */
485
+ getAllWindowHandles() { }
486
+
487
+ /**
488
+ * Retrieves the current page's source. The returned source is a representation
489
+ * of the underlying DOM: do not expect it to be formatted or escaped in the
490
+ * same way as the raw response sent from the web server.
491
+ *
492
+ * @return {!Promise<string>} A promise that will be resolved with the current
493
+ * page source.
494
+ */
495
+ getPageSource() { }
496
+
497
+ /**
498
+ * Closes the current window.
499
+ *
500
+ * @return {!Promise<void>} A promise that will be resolved when this command
501
+ * has completed.
502
+ */
503
+ close() { }
504
+
505
+ /**
506
+ * Navigates to the given URL.
507
+ *
508
+ * @param {string} url The fully qualified URL to open.
509
+ * @return {!Promise<void>} A promise that will be resolved when the document
510
+ * has finished loading.
511
+ */
512
+ get(url) { } // eslint-disable-line
513
+
514
+ /**
515
+ * Retrieves the URL for the current page.
516
+ *
517
+ * @return {!Promise<string>} A promise that will be resolved with the
518
+ * current URL.
519
+ */
520
+ getCurrentUrl() { }
521
+
522
+ /**
523
+ * Retrieves the current page title.
524
+ *
525
+ * @return {!Promise<string>} A promise that will be resolved with the current
526
+ * page's title.
527
+ */
528
+ getTitle() { }
529
+
530
+ /**
531
+ * Locates an element on the page. If the element cannot be found, a
532
+ * {@link error.NoSuchElementError} will be returned by the driver.
533
+ *
534
+ * This function should not be used to test whether an element is present on
535
+ * the page. Rather, you should use {@link #findElements}:
536
+ *
537
+ * driver.findElements(By.id('foo'))
538
+ * .then(found => console.log('Element found? %s', !!found.length));
539
+ *
540
+ * The search criteria for an element may be defined using one of the
541
+ * factories in the {@link webdriver.By} namespace, or as a short-hand
542
+ * {@link webdriver.By.Hash} object. For example, the following two statements
543
+ * are equivalent:
544
+ *
545
+ * var e1 = driver.findElement(By.id('foo'));
546
+ * var e2 = driver.findElement({id:'foo'});
547
+ *
548
+ * You may also provide a custom locator function, which takes as input this
549
+ * instance and returns a {@link WebElement}, or a promise that will resolve
550
+ * to a WebElement. If the returned promise resolves to an array of
551
+ * WebElements, WebDriver will use the first element. For example, to find the
552
+ * first visible link on a page, you could write:
553
+ *
554
+ * var link = driver.findElement(firstVisibleLink);
555
+ *
556
+ * function firstVisibleLink(driver) {
557
+ * var links = driver.findElements(By.tagName('a'));
558
+ * return promise.filter(links, function(link) {
559
+ * return link.isDisplayed();
560
+ * });
561
+ * }
562
+ *
563
+ * @param {!(by.By|Function)} locator The locator to use.
564
+ * @return {!WebElementPromise} A WebElement that can be used to issue
565
+ * commands against the located element. If the element is not found, the
566
+ * element will be invalidated and all scheduled commands aborted.
567
+ */
568
+ findElement(locator) { } // eslint-disable-line
569
+
570
+ /**
571
+ * Search for multiple elements on the page. Refer to the documentation on
572
+ * {@link #findElement(by)} for information on element locator strategies.
573
+ *
574
+ * @param {!(by.By|Function)} locator The locator to use.
575
+ * @return {!Promise<!Array<!WebElement>>} A promise that will resolve to an
576
+ * array of WebElements.
577
+ */
578
+ findElements(locator) { } // eslint-disable-line
579
+
580
+ /**
581
+ * Takes a screenshot of the current page. The driver makes a best effort to
582
+ * return a screenshot of the following, in order of preference:
583
+ *
584
+ * 1. Entire page
585
+ * 2. Current window
586
+ * 3. Visible portion of the current frame
587
+ * 4. The entire display containing the browser
588
+ *
589
+ * @return {!Promise<string>} A promise that will be resolved to the
590
+ * screenshot as a base-64 encoded PNG.
591
+ */
592
+ takeScreenshot() { }
593
+
594
+ /**
595
+ * @return {!Options} The options interface for this instance.
596
+ */
597
+ manage() { }
598
+
599
+ /**
600
+ * @return {!Navigation} The navigation interface for this instance.
601
+ */
602
+ navigate() { }
603
+
604
+ /**
605
+ * @return {!TargetLocator} The target locator interface for this
606
+ * instance.
607
+ */
608
+ switchTo() { }
609
+
610
+ /**
611
+ *
612
+ * Takes a PDF of the current page. The driver makes a best effort to
613
+ * return a PDF based on the provided parameters.
614
+ *
615
+ * @param {{orientation: (string|undefined),
616
+ * scale: (number|undefined),
617
+ * background: (boolean|undefined)
618
+ * width: (number|undefined)
619
+ * height: (number|undefined)
620
+ * top: (number|undefined)
621
+ * bottom: (number|undefined)
622
+ * left: (number|undefined)
623
+ * right: (number|undefined)
624
+ * shrinkToFit: (boolean|undefined)
625
+ * pageRanges: (<Array>|undefined)}} options.
626
+ */
627
+ printPage(options) { } // eslint-disable-line
628
+ }
629
+
630
+ /**
631
+ * @param {!Capabilities} capabilities A capabilities object.
632
+ * @return {!Capabilities} A copy of the parameter capabilities, omitting
633
+ * capability names that are not valid W3C names.
634
+ */
635
+ function filterNonW3CCaps(capabilities) {
636
+ let newCaps = new Capabilities(capabilities)
637
+ for (let k of newCaps.keys()) {
638
+ // Any key containing a colon is a vendor-prefixed capability.
639
+ if (!(W3C_CAPABILITY_NAMES.has(k) || k.indexOf(':') >= 0)) {
640
+ newCaps.delete(k)
641
+ }
642
+ }
643
+ return newCaps
644
+ }
645
+
646
+ /**
647
+ * Each WebDriver instance provides automated control over a browser session.
648
+ *
649
+ * @implements {IWebDriver}
650
+ */
651
+ class WebDriver {
652
+
653
+ /**
654
+ * @param {!(./session.Session|IThenable<!./session.Session>)} session Either
655
+ * a known session or a promise that will be resolved to a session.
656
+ * @param {!command.Executor} executor The executor to use when sending
657
+ * commands to the browser.
658
+ * @param {(function(this: void): ?)=} onQuit A function to call, if any,
659
+ * when the session is terminated.
660
+ */
661
+ constructor(session, executor, onQuit = undefined) {
662
+ /** @private {!Promise<!Session>} */
663
+ this.session_ = Promise.resolve(session)
664
+
665
+ // If session is a rejected promise, add a no-op rejection handler.
666
+ // This effectively hides setup errors until users attempt to interact
667
+ // with the session.
668
+ this.session_.catch(function () { })
669
+
670
+ /** @private {!command.Executor} */
671
+ this.executor_ = executor
672
+
673
+ /** @private {input.FileDetector} */
674
+ this.fileDetector_ = null
675
+
676
+ /** @private @const {(function(this: void): ?|undefined)} */
677
+ this.onQuit_ = onQuit
678
+ }
679
+
680
+ /**
681
+ * Creates a new WebDriver session.
682
+ *
683
+ * This function will always return a WebDriver instance. If there is an error
684
+ * creating the session, such as the aforementioned SessionNotCreatedError,
685
+ * the driver will have a rejected {@linkplain #getSession session} promise.
686
+ * This rejection will propagate through any subsequent commands scheduled
687
+ * on the returned WebDriver instance.
688
+ *
689
+ * let required = Capabilities.firefox();
690
+ * let driver = WebDriver.createSession(executor, {required});
691
+ *
692
+ * // If the createSession operation failed, then this command will also
693
+ * // also fail, propagating the creation failure.
694
+ * driver.get('http://www.google.com').catch(e => console.log(e));
695
+ *
696
+ * @param {!command.Executor} executor The executor to create the new session
697
+ * with.
698
+ * @param {!Capabilities} capabilities The desired capabilities for the new
699
+ * session.
700
+ * @param {(function(this: void): ?)=} onQuit A callback to invoke when
701
+ * the newly created session is terminated. This should be used to clean
702
+ * up any resources associated with the session.
703
+ * @return {!WebDriver} The driver for the newly created session.
704
+ */
705
+ static createSession(executor, capabilities, onQuit = undefined) {
706
+ let cmd = new command.Command(command.Name.NEW_SESSION)
707
+
708
+ // For OSS remote ends.
709
+ cmd.setParameter('desiredCapabilities', capabilities)
710
+ // For W3C remote ends.
711
+ cmd.setParameter('capabilities', {
712
+ alwaysMatch: filterNonW3CCaps(capabilities),
713
+ })
714
+
715
+ let session = executeCommand(executor, cmd)
716
+ if (typeof onQuit === 'function') {
717
+ session = session.catch((err) => {
718
+ return Promise.resolve(onQuit.call(void 0)).then((_) => {
719
+ throw err
720
+ })
721
+ })
722
+ }
723
+ return new this(session, executor, onQuit)
724
+ }
725
+
726
+ /** @override */
727
+ async execute(command) {
728
+ command.setParameter('sessionId', this.session_)
729
+ let parameters = await toWireValue(command.getParameters())
730
+ command.setParameters(parameters)
731
+ let value = await this.executor_.execute(command)
732
+ return fromWireValue(this, value)
733
+ }
734
+
735
+ /** @override */
736
+ setFileDetector(detector) {
737
+ this.fileDetector_ = detector
738
+ }
739
+
740
+ /** @override */
741
+ getExecutor() {
742
+ return this.executor_
743
+ }
744
+
745
+ /** @override */
746
+ getSession() {
747
+ return this.session_
748
+ }
749
+
750
+ /** @override */
751
+ getCapabilities() {
752
+ return this.session_.then((s) => s.getCapabilities())
753
+ }
754
+
755
+ /** @override */
756
+ quit() {
757
+ let result = this.execute(new command.Command(command.Name.QUIT))
758
+ // Delete our session ID when the quit command finishes; this will allow us
759
+ // to throw an error when attempting to use a driver post-quit.
760
+ return promise.finally(result, () => {
761
+ this.session_ = Promise.reject(
762
+ new error.NoSuchSessionError(
763
+ 'This driver instance does not have a valid session ID ' +
764
+ '(did you call WebDriver.quit()?) and may no longer be used.'
765
+ )
766
+ )
767
+
768
+ // Only want the session rejection to bubble if accessed.
769
+ this.session_.catch(function () { })
770
+
771
+ if (this.onQuit_) {
772
+ return this.onQuit_.call(void 0)
773
+ }
774
+ })
775
+ }
776
+
777
+ /** @override */
778
+ actions(options) {
779
+ return new input.Actions(this, options || undefined)
780
+ }
781
+
782
+ /** @override */
783
+ executeScript(script, ...args) {
784
+ if (typeof script === 'function') {
785
+ script = 'return (' + script + ').apply(null, arguments);'
786
+ }
787
+ return this.execute(
788
+ new command.Command(command.Name.EXECUTE_SCRIPT)
789
+ .setParameter('script', script)
790
+ .setParameter('args', args)
791
+ )
792
+ }
793
+
794
+ /** @override */
795
+ executeAsyncScript(script, ...args) {
796
+ if (typeof script === 'function') {
797
+ script = 'return (' + script + ').apply(null, arguments);'
798
+ }
799
+ return this.execute(
800
+ new command.Command(command.Name.EXECUTE_ASYNC_SCRIPT)
801
+ .setParameter('script', script)
802
+ .setParameter('args', args)
803
+ )
804
+ }
805
+
806
+ /** @override */
807
+ wait(condition, timeout = 0, message = undefined, pollTimeout = 200) {
808
+ if (typeof timeout !== 'number' || timeout < 0) {
809
+ throw TypeError('timeout must be a number >= 0: ' + timeout)
810
+ }
811
+
812
+ if (typeof pollTimeout !== 'number' || pollTimeout < 0) {
813
+ throw TypeError('pollTimeout must be a number >= 0: ' + pollTimeout)
814
+ }
815
+
816
+ if (promise.isPromise(condition)) {
817
+ return new Promise((resolve, reject) => {
818
+ if (!timeout) {
819
+ resolve(condition)
820
+ return
821
+ }
822
+
823
+ let start = Date.now()
824
+ let timer = setTimeout(function () {
825
+ timer = null
826
+ try {
827
+ let timeoutMessage = resolveWaitMessage(message)
828
+ reject(
829
+ new error.TimeoutError(
830
+ `${timeoutMessage}Timed out waiting for promise to resolve after ${Date.now() - start
831
+ }ms`
832
+ )
833
+ )
834
+ } catch (ex) {
835
+ reject(
836
+ new error.TimeoutError(
837
+ `${ex.message
838
+ }\nTimed out waiting for promise to resolve after ${Date.now() - start
839
+ }ms`
840
+ )
841
+ )
842
+ }
843
+ }, timeout)
844
+ const clearTimer = () => timer && clearTimeout(timer)
845
+
846
+ /** @type {!IThenable} */ condition.then(
847
+ function (value) {
848
+ clearTimer()
849
+ resolve(value)
850
+ },
851
+ function (error) {
852
+ clearTimer()
853
+ reject(error)
854
+ }
855
+ )
856
+ })
857
+ }
858
+
859
+ let fn = /** @type {!Function} */ (condition)
860
+ if (condition instanceof Condition) {
861
+ message = message || condition.description()
862
+ fn = condition.fn
863
+ }
864
+
865
+ if (typeof fn !== 'function') {
866
+ throw TypeError(
867
+ 'Wait condition must be a promise-like object, function, or a ' +
868
+ 'Condition object'
869
+ )
870
+ }
871
+
872
+ const driver = this
873
+ function evaluateCondition() {
874
+ return new Promise((resolve, reject) => {
875
+ try {
876
+ resolve(fn(driver))
877
+ } catch (ex) {
878
+ reject(ex)
879
+ }
880
+ })
881
+ }
882
+
883
+ let result = new Promise((resolve, reject) => {
884
+ const startTime = Date.now()
885
+ const pollCondition = async () => {
886
+ evaluateCondition().then(function (value) {
887
+ const elapsed = Date.now() - startTime
888
+ if (value) {
889
+ resolve(value)
890
+ } else if (timeout && elapsed >= timeout) {
891
+ try {
892
+ let timeoutMessage = resolveWaitMessage(message)
893
+ reject(
894
+ new error.TimeoutError(
895
+ `${timeoutMessage}Wait timed out after ${elapsed}ms`
896
+ )
897
+ )
898
+ } catch (ex) {
899
+ reject(
900
+ new error.TimeoutError(
901
+ `${ex.message}\nWait timed out after ${elapsed}ms`
902
+ )
903
+ )
904
+ }
905
+ } else {
906
+ setTimeout(pollCondition, pollTimeout)
907
+ }
908
+ }, reject)
909
+ }
910
+ pollCondition()
911
+ })
912
+
913
+ if (condition instanceof WebElementCondition) {
914
+ result = new WebElementPromise(
915
+ this,
916
+ result.then(function (value) {
917
+ if (!(value instanceof WebElement)) {
918
+ throw TypeError(
919
+ 'WebElementCondition did not resolve to a WebElement: ' +
920
+ Object.prototype.toString.call(value)
921
+ )
922
+ }
923
+ return value
924
+ })
925
+ )
926
+ }
927
+ return result
928
+ }
929
+
930
+ /** @override */
931
+ sleep(ms) {
932
+ return new Promise((resolve) => setTimeout(resolve, ms))
933
+ }
934
+
935
+ /** @override */
936
+ getWindowHandle() {
937
+ return this.execute(
938
+ new command.Command(command.Name.GET_CURRENT_WINDOW_HANDLE)
939
+ )
940
+ }
941
+
942
+ /** @override */
943
+ getAllWindowHandles() {
944
+ return this.execute(new command.Command(command.Name.GET_WINDOW_HANDLES))
945
+ }
946
+
947
+ /** @override */
948
+ getPageSource() {
949
+ return this.execute(new command.Command(command.Name.GET_PAGE_SOURCE))
950
+ }
951
+
952
+ /** @override */
953
+ close() {
954
+ return this.execute(new command.Command(command.Name.CLOSE))
955
+ }
956
+
957
+ /** @override */
958
+ get(url) {
959
+ return this.navigate().to(url)
960
+ }
961
+
962
+ /** @override */
963
+ getCurrentUrl() {
964
+ return this.execute(new command.Command(command.Name.GET_CURRENT_URL))
965
+ }
966
+
967
+ /** @override */
968
+ getTitle() {
969
+ return this.execute(new command.Command(command.Name.GET_TITLE))
970
+ }
971
+
972
+ /** @override */
973
+ findElement(locator) {
974
+ let id
975
+ locator = by.checkedLocator(locator)
976
+ if (typeof locator === 'function') {
977
+ id = this.findElementInternal_(locator, this)
978
+ } else {
979
+ let cmd = new command.Command(command.Name.FIND_ELEMENT)
980
+ .setParameter('using', locator.using)
981
+ .setParameter('value', locator.value)
982
+ id = this.execute(cmd)
983
+ }
984
+ return new WebElementPromise(this, id)
985
+ }
986
+
987
+ /**
988
+ * @param {!Function} locatorFn The locator function to use.
989
+ * @param {!(WebDriver|WebElement)} context The search context.
990
+ * @return {!Promise<!WebElement>} A promise that will resolve to a list of
991
+ * WebElements.
992
+ * @private
993
+ */
994
+ async findElementInternal_(locatorFn, context) {
995
+ let result = await locatorFn(context)
996
+ if (Array.isArray(result)) {
997
+ result = result[0]
998
+ }
999
+ if (!(result instanceof WebElement)) {
1000
+ throw new TypeError('Custom locator did not return a WebElement')
1001
+ }
1002
+ return result
1003
+ }
1004
+
1005
+ /** @override */
1006
+ async findElements(locator) {
1007
+ let cmd = null
1008
+ if (locator instanceof RelativeBy) {
1009
+ cmd = new command.Command(
1010
+ command.Name.FIND_ELEMENTS_RELATIVE
1011
+ ).setParameter('args', locator.marshall())
1012
+ } else {
1013
+ locator = by.checkedLocator(locator)
1014
+ }
1015
+
1016
+ if (typeof locator === 'function') {
1017
+ return this.findElementsInternal_(locator, this)
1018
+ } else if (cmd === null) {
1019
+ cmd = new command.Command(command.Name.FIND_ELEMENTS)
1020
+ .setParameter('using', locator.using)
1021
+ .setParameter('value', locator.value)
1022
+ }
1023
+ try {
1024
+ let res = await this.execute(cmd)
1025
+ return Array.isArray(res) ? res : []
1026
+ } catch (ex) {
1027
+ if (ex instanceof error.NoSuchElementError) {
1028
+ return []
1029
+ }
1030
+ throw ex
1031
+ }
1032
+ }
1033
+
1034
+ /**
1035
+ * @param {!Function} locatorFn The locator function to use.
1036
+ * @param {!(WebDriver|WebElement)} context The search context.
1037
+ * @return {!Promise<!Array<!WebElement>>} A promise that will resolve to an
1038
+ * array of WebElements.
1039
+ * @private
1040
+ */
1041
+ async findElementsInternal_(locatorFn, context) {
1042
+ const result = await locatorFn(context)
1043
+ if (result instanceof WebElement) {
1044
+ return [result]
1045
+ }
1046
+
1047
+ if (!Array.isArray(result)) {
1048
+ return []
1049
+ }
1050
+
1051
+ return result.filter(function (item) {
1052
+ return item instanceof WebElement
1053
+ })
1054
+ }
1055
+
1056
+ /** @override */
1057
+ takeScreenshot() {
1058
+ return this.execute(new command.Command(command.Name.SCREENSHOT))
1059
+ }
1060
+
1061
+ /** @override */
1062
+ manage() {
1063
+ return new Options(this)
1064
+ }
1065
+
1066
+ /** @override */
1067
+ navigate() {
1068
+ return new Navigation(this)
1069
+ }
1070
+
1071
+ /** @override */
1072
+ switchTo() {
1073
+ return new TargetLocator(this)
1074
+ }
1075
+
1076
+ validatePrintPageParams(keys, object) {
1077
+ let page = {}
1078
+ let margin = {}
1079
+ let data
1080
+ Object.keys(keys).forEach(function (key) {
1081
+ data = keys[key]
1082
+ let obj = {
1083
+ orientation: function () {
1084
+ object.orientation = data
1085
+ },
1086
+
1087
+ scale: function () {
1088
+ object.scale = data
1089
+ },
1090
+
1091
+ background: function () {
1092
+ object.background = data
1093
+ },
1094
+
1095
+ width: function () {
1096
+ page.width = data
1097
+ object.page = page
1098
+ },
1099
+
1100
+ height: function () {
1101
+ page.height = data
1102
+ object.page = page
1103
+ },
1104
+
1105
+ top: function () {
1106
+ margin.top = data
1107
+ object.margin = margin
1108
+ },
1109
+
1110
+ left: function () {
1111
+ margin.left = data
1112
+ object.margin = margin
1113
+ },
1114
+
1115
+ bottom: function () {
1116
+ margin.bottom = data
1117
+ object.margin = margin
1118
+ },
1119
+
1120
+ right: function () {
1121
+ margin.right = data
1122
+ object.margin = margin
1123
+ },
1124
+
1125
+ shrinkToFit: function () {
1126
+ object.shrinkToFit = data
1127
+ },
1128
+
1129
+ pageRanges: function () {
1130
+ object.pageRanges = data
1131
+ },
1132
+ }
1133
+
1134
+ if (!Object.prototype.hasOwnProperty.call(obj, key)) {
1135
+ throw new error.InvalidArgumentError(`Invalid Argument '${key}'`)
1136
+ } else {
1137
+ obj[key]()
1138
+ }
1139
+ })
1140
+
1141
+ return object
1142
+ }
1143
+
1144
+ /** @override */
1145
+ printPage(options = {}) {
1146
+ let keys = options
1147
+ let params = {}
1148
+ let resultObj
1149
+
1150
+ let self = this
1151
+ resultObj = self.validatePrintPageParams(keys, params)
1152
+
1153
+ return this.execute(
1154
+ new command.Command(command.Name.PRINT_PAGE).setParameters(resultObj)
1155
+ )
1156
+ }
1157
+
1158
+ /**
1159
+ * Creates a new WebSocket connection.
1160
+ * @return {!Promise<resolved>} A new CDP instance.
1161
+ */
1162
+ async createCDPConnection(target) {
1163
+ const caps = await this.getCapabilities()
1164
+ const seCdp = caps['map_'].get('se:cdp')
1165
+ const vendorInfo =
1166
+ caps['map_'].get(this.VENDOR_COMMAND_PREFIX + ':chromeOptions') ||
1167
+ caps['map_'].get(this.VENDOR_CAPABILITY_PREFIX + ':edgeOptions') ||
1168
+ caps['map_'].get('moz:debuggerAddress') ||
1169
+ new Map();
1170
+ const debuggerUrl = seCdp || vendorInfo['debuggerAddress'] || vendorInfo
1171
+ this._wsUrl = await this.getWsUrl(debuggerUrl, target)
1172
+
1173
+ return new Promise((resolve, reject) => {
1174
+ try {
1175
+ this._wsConnection = new WebSocket(this._wsUrl)
1176
+ } catch (err) {
1177
+ reject(err)
1178
+ return
1179
+ }
1180
+
1181
+ this._wsConnection.on('open', () => {
1182
+ this._cdpConnection = new cdp.CdpConnection(this._wsConnection)
1183
+ resolve(this._cdpConnection)
1184
+ })
1185
+
1186
+ this._wsConnection.on('error', (error) => {
1187
+ reject(error)
1188
+ })
1189
+ })
1190
+ }
1191
+
1192
+ /**
1193
+ * Retrieves 'webSocketDebuggerUrl' by sending a http request using debugger address
1194
+ * @param {string} debuggerAddress
1195
+ * @param {string} target
1196
+ * @return {string} Returns parsed webSocketDebuggerUrl obtained from the http request
1197
+ */
1198
+ async getWsUrl(debuggerAddress, target) {
1199
+ if (target && cdpTargets.indexOf(target.toLowerCase()) === -1) {
1200
+ throw new error.InvalidArgumentError('invalid target value')
1201
+ }
1202
+ let path
1203
+ if (target === 'page') {
1204
+ path = '/json'
1205
+ } else {
1206
+ path = '/json/version'
1207
+ }
1208
+ let request = new http.Request('GET', path)
1209
+ let client = new http.HttpClient('http://' + debuggerAddress)
1210
+ let response = await client.send(request)
1211
+ let url
1212
+ if (target.toLowerCase() === 'page') {
1213
+ url = JSON.parse(response.body)[0]['webSocketDebuggerUrl']
1214
+ } else {
1215
+ url = JSON.parse(response.body)['webSocketDebuggerUrl']
1216
+ }
1217
+
1218
+ return url
1219
+ }
1220
+
1221
+ /**
1222
+ * Sets a listener for Fetch.authRequired event from CDP
1223
+ * If event is triggered, it enter username and password
1224
+ * and allows the test to move forward
1225
+ * @param {string} username
1226
+ * @param {string} password
1227
+ * @param connection CDP Connection
1228
+ */
1229
+ async register(username, password, connection) {
1230
+ await connection.execute(
1231
+ 'Network.setCacheDisabled',
1232
+ this.getRandomNumber(1, 10),
1233
+ {
1234
+ cacheDisabled: true,
1235
+ },
1236
+ null
1237
+ )
1238
+
1239
+ this._wsConnection.on('message', (message) => {
1240
+ const params = JSON.parse(message)
1241
+
1242
+ if (params.method === 'Fetch.authRequired') {
1243
+ const requestParams = params['params']
1244
+ connection.execute(
1245
+ 'Fetch.continueWithAuth',
1246
+ this.getRandomNumber(1, 10),
1247
+ {
1248
+ requestId: requestParams['requestId'],
1249
+ authChallengeResponse: {
1250
+ response: 'ProvideCredentials',
1251
+ username: username,
1252
+ password: password,
1253
+ },
1254
+ }
1255
+ )
1256
+ } else if (params.method === 'Fetch.requestPaused') {
1257
+ const requestPausedParams = params['params']
1258
+ connection.execute(
1259
+ 'Fetch.continueRequest',
1260
+ this.getRandomNumber(1, 10),
1261
+ {
1262
+ requestId: requestPausedParams['requestId'],
1263
+ }
1264
+ )
1265
+ }
1266
+ })
1267
+
1268
+ await connection.execute(
1269
+ 'Fetch.enable',
1270
+ 1,
1271
+ {
1272
+ handleAuthRequests: true,
1273
+ },
1274
+ null
1275
+ )
1276
+ }
1277
+
1278
+ /**
1279
+ *
1280
+ * @param connection
1281
+ * @param callback
1282
+ * @returns {Promise<void>}
1283
+ */
1284
+ async onLogEvent(connection, callback) {
1285
+ await connection.execute(
1286
+ 'Runtime.enable',
1287
+ this.getRandomNumber(1, 10),
1288
+ {},
1289
+ null
1290
+ )
1291
+
1292
+ this._wsConnection.on('message', (message) => {
1293
+ const params = JSON.parse(message)
1294
+
1295
+ if (params.method === 'Runtime.consoleAPICalled') {
1296
+ const consoleEventParams = params['params']
1297
+ let event = {
1298
+ type: consoleEventParams['type'],
1299
+ timestamp: new Date(consoleEventParams['timestamp']),
1300
+ args: consoleEventParams['args'],
1301
+ }
1302
+
1303
+ callback(event)
1304
+ }
1305
+ })
1306
+ }
1307
+
1308
+ /**
1309
+ *
1310
+ * @param connection
1311
+ * @param callback
1312
+ * @returns {Promise<void>}
1313
+ */
1314
+ async onLogException(connection, callback) {
1315
+ await connection.execute(
1316
+ 'Runtime.enable',
1317
+ this.getRandomNumber(1, 10),
1318
+ {},
1319
+ null
1320
+ )
1321
+
1322
+ this._wsConnection.on('message', (message) => {
1323
+ const params = JSON.parse(message)
1324
+
1325
+ if (params.method === 'Runtime.exceptionThrown') {
1326
+ const exceptionEventParams = params['params']
1327
+ let event = {
1328
+ exceptionDetails: exceptionEventParams['exceptionDetails'],
1329
+ timestamp: new Date(exceptionEventParams['timestamp']),
1330
+ }
1331
+
1332
+ callback(event)
1333
+ }
1334
+ })
1335
+ }
1336
+
1337
+ /**
1338
+ * @param connection
1339
+ * @param callback
1340
+ * @returns {Promise<void>}
1341
+ */
1342
+ async logMutationEvents(connection, callback) {
1343
+ await connection.execute(
1344
+ 'Runtime.enable',
1345
+ this.getRandomNumber(1, 10),
1346
+ {},
1347
+ null
1348
+ )
1349
+ await connection.execute(
1350
+ 'Page.enable',
1351
+ this.getRandomNumber(1, 10),
1352
+ {},
1353
+ null
1354
+ )
1355
+
1356
+ await connection.execute(
1357
+ 'Runtime.addBinding',
1358
+ this.getRandomNumber(1, 10),
1359
+ {
1360
+ name: '__webdriver_attribute',
1361
+ },
1362
+ null
1363
+ )
1364
+
1365
+ let mutationListener = ''
1366
+ try {
1367
+ // Depending on what is running the code it could appear in 2 different places which is why we try
1368
+ // here and then the other location
1369
+ mutationListener = fs
1370
+ .readFileSync(
1371
+ './javascript/node/selenium-webdriver/lib/atoms/mutation-listener.js',
1372
+ 'utf-8'
1373
+ )
1374
+ .toString()
1375
+ } catch {
1376
+ mutationListener = fs
1377
+ .readFileSync(path.resolve(__dirname, './atoms/mutation-listener.js'), 'utf-8')
1378
+ .toString()
1379
+ }
1380
+
1381
+ this.executeScript(mutationListener)
1382
+
1383
+ await connection.execute(
1384
+ 'Page.addScriptToEvaluateOnNewDocument',
1385
+ this.getRandomNumber(1, 10),
1386
+ {
1387
+ source: mutationListener,
1388
+ },
1389
+ null
1390
+ )
1391
+
1392
+ this._wsConnection.on('message', async (message) => {
1393
+ const params = JSON.parse(message)
1394
+ if (params.method === 'Runtime.bindingCalled') {
1395
+ let payload = JSON.parse(params['params']['payload'])
1396
+ let elements = await this.findElements({
1397
+ css: '*[data-__webdriver_id=' + payload['target'],
1398
+ })
1399
+
1400
+ if (elements.length === 0) {
1401
+ return
1402
+ }
1403
+
1404
+ let event = {
1405
+ element: elements[0],
1406
+ attribute_name: payload['name'],
1407
+ current_value: payload['value'],
1408
+ old_value: payload['oldValue'],
1409
+ }
1410
+ callback(event)
1411
+ }
1412
+ })
1413
+ }
1414
+
1415
+ getRandomNumber(min, max) {
1416
+ return Math.floor(Math.random() * (max - min + 1) + min)
1417
+ }
1418
+ }
1419
+
1420
+ /**
1421
+ * Interface for navigating back and forth in the browser history.
1422
+ *
1423
+ * This class should never be instantiated directly. Instead, obtain an instance
1424
+ * with
1425
+ *
1426
+ * webdriver.navigate()
1427
+ *
1428
+ * @see WebDriver#navigate()
1429
+ */
1430
+ class Navigation {
1431
+ /**
1432
+ * @param {!WebDriver} driver The parent driver.
1433
+ * @private
1434
+ */
1435
+ constructor(driver) {
1436
+ /** @private {!WebDriver} */
1437
+ this.driver_ = driver
1438
+ }
1439
+
1440
+ /**
1441
+ * Navigates to a new URL.
1442
+ *
1443
+ * @param {string} url The URL to navigate to.
1444
+ * @return {!Promise<void>} A promise that will be resolved when the URL
1445
+ * has been loaded.
1446
+ */
1447
+ to(url) {
1448
+ return this.driver_.execute(
1449
+ new command.Command(command.Name.GET).setParameter('url', url)
1450
+ )
1451
+ }
1452
+
1453
+ /**
1454
+ * Moves backwards in the browser history.
1455
+ *
1456
+ * @return {!Promise<void>} A promise that will be resolved when the
1457
+ * navigation event has completed.
1458
+ */
1459
+ back() {
1460
+ return this.driver_.execute(new command.Command(command.Name.GO_BACK))
1461
+ }
1462
+
1463
+ /**
1464
+ * Moves forwards in the browser history.
1465
+ *
1466
+ * @return {!Promise<void>} A promise that will be resolved when the
1467
+ * navigation event has completed.
1468
+ */
1469
+ forward() {
1470
+ return this.driver_.execute(new command.Command(command.Name.GO_FORWARD))
1471
+ }
1472
+
1473
+ /**
1474
+ * Refreshes the current page.
1475
+ *
1476
+ * @return {!Promise<void>} A promise that will be resolved when the
1477
+ * navigation event has completed.
1478
+ */
1479
+ refresh() {
1480
+ return this.driver_.execute(new command.Command(command.Name.REFRESH))
1481
+ }
1482
+ }
1483
+
1484
+ /**
1485
+ * Provides methods for managing browser and driver state.
1486
+ *
1487
+ * This class should never be instantiated directly. Instead, obtain an instance
1488
+ * with {@linkplain WebDriver#manage() webdriver.manage()}.
1489
+ */
1490
+ class Options {
1491
+ /**
1492
+ * @param {!WebDriver} driver The parent driver.
1493
+ * @private
1494
+ */
1495
+ constructor(driver) {
1496
+ /** @private {!WebDriver} */
1497
+ this.driver_ = driver
1498
+ }
1499
+
1500
+ /**
1501
+ * Adds a cookie.
1502
+ *
1503
+ * __Sample Usage:__
1504
+ *
1505
+ * // Set a basic cookie.
1506
+ * driver.manage().addCookie({name: 'foo', value: 'bar'});
1507
+ *
1508
+ * // Set a cookie that expires in 10 minutes.
1509
+ * let expiry = new Date(Date.now() + (10 * 60 * 1000));
1510
+ * driver.manage().addCookie({name: 'foo', value: 'bar', expiry});
1511
+ *
1512
+ * // The cookie expiration may also be specified in seconds since epoch.
1513
+ * driver.manage().addCookie({
1514
+ * name: 'foo',
1515
+ * value: 'bar',
1516
+ * expiry: Math.floor(Date.now() / 1000)
1517
+ * });
1518
+ *
1519
+ * @param {!Options.Cookie} spec Defines the cookie to add.
1520
+ * @return {!Promise<void>} A promise that will be resolved
1521
+ * when the cookie has been added to the page.
1522
+ * @throws {error.InvalidArgumentError} if any of the cookie parameters are
1523
+ * invalid.
1524
+ * @throws {TypeError} if `spec` is not a cookie object.
1525
+ */
1526
+ addCookie({ name, value, path, domain, secure, httpOnly, expiry, sameSite }) {
1527
+ // We do not allow '=' or ';' in the name.
1528
+ if (/[;=]/.test(name)) {
1529
+ throw new error.InvalidArgumentError('Invalid cookie name "' + name + '"')
1530
+ }
1531
+
1532
+ // We do not allow ';' in value.
1533
+ if (/;/.test(value)) {
1534
+ throw new error.InvalidArgumentError(
1535
+ 'Invalid cookie value "' + value + '"'
1536
+ )
1537
+ }
1538
+
1539
+ if (typeof expiry === 'number') {
1540
+ expiry = Math.floor(expiry)
1541
+ } else if (expiry instanceof Date) {
1542
+ let date = /** @type {!Date} */ (expiry)
1543
+ expiry = Math.floor(date.getTime() / 1000)
1544
+ }
1545
+
1546
+ if (sameSite && !['Strict', 'Lax', 'None'].includes(sameSite)) {
1547
+ throw new error.InvalidArgumentError(
1548
+ `Invalid sameSite cookie value '${sameSite}'. It should be one of "Lax", "Strict" or "None"`
1549
+ )
1550
+ }
1551
+
1552
+ if (sameSite === 'None' && !secure) {
1553
+ throw new error.InvalidArgumentError(
1554
+ 'Invalid cookie configuration: SameSite=None must be Secure'
1555
+ )
1556
+ }
1557
+
1558
+ return this.driver_.execute(
1559
+ new command.Command(command.Name.ADD_COOKIE).setParameter('cookie', {
1560
+ name: name,
1561
+ value: value,
1562
+ path: path,
1563
+ domain: domain,
1564
+ secure: !!secure,
1565
+ httpOnly: !!httpOnly,
1566
+ expiry: expiry,
1567
+ sameSite: sameSite,
1568
+ })
1569
+ )
1570
+ }
1571
+
1572
+ /**
1573
+ * Deletes all cookies visible to the current page.
1574
+ *
1575
+ * @return {!Promise<void>} A promise that will be resolved
1576
+ * when all cookies have been deleted.
1577
+ */
1578
+ deleteAllCookies() {
1579
+ return this.driver_.execute(
1580
+ new command.Command(command.Name.DELETE_ALL_COOKIES)
1581
+ )
1582
+ }
1583
+
1584
+ /**
1585
+ * Deletes the cookie with the given name. This command is a no-op if there is
1586
+ * no cookie with the given name visible to the current page.
1587
+ *
1588
+ * @param {string} name The name of the cookie to delete.
1589
+ * @return {!Promise<void>} A promise that will be resolved
1590
+ * when the cookie has been deleted.
1591
+ */
1592
+ deleteCookie(name) {
1593
+ return this.driver_.execute(
1594
+ new command.Command(command.Name.DELETE_COOKIE).setParameter('name', name)
1595
+ )
1596
+ }
1597
+
1598
+ /**
1599
+ * Retrieves all cookies visible to the current page. Each cookie will be
1600
+ * returned as a JSON object as described by the WebDriver wire protocol.
1601
+ *
1602
+ * @return {!Promise<!Array<!Options.Cookie>>} A promise that will be
1603
+ * resolved with the cookies visible to the current browsing context.
1604
+ */
1605
+ getCookies() {
1606
+ return this.driver_.execute(
1607
+ new command.Command(command.Name.GET_ALL_COOKIES)
1608
+ )
1609
+ }
1610
+
1611
+ /**
1612
+ * Retrieves the cookie with the given name. Returns null if there is no such
1613
+ * cookie. The cookie will be returned as a JSON object as described by the
1614
+ * WebDriver wire protocol.
1615
+ *
1616
+ * @param {string} name The name of the cookie to retrieve.
1617
+ * @return {!Promise<?Options.Cookie>} A promise that will be resolved
1618
+ * with the named cookie
1619
+ * @throws {error.NoSuchCookieError} if there is no such cookie.
1620
+ */
1621
+ async getCookie(name) {
1622
+ try {
1623
+ const cookie = await this.driver_.execute(
1624
+ new command.Command(command.Name.GET_COOKIE).setParameter('name', name)
1625
+ )
1626
+ return cookie
1627
+ } catch (err) {
1628
+ if (
1629
+ !(err instanceof error.UnknownCommandError) &&
1630
+ !(err instanceof error.UnsupportedOperationError)
1631
+ ) {
1632
+ throw err
1633
+ }
1634
+
1635
+ const cookies = await this.getCookies()
1636
+ for (let cookie of cookies) {
1637
+ if (cookie && cookie['name'] === name) {
1638
+ return cookie
1639
+ }
1640
+ }
1641
+ return null
1642
+ }
1643
+ }
1644
+
1645
+ /**
1646
+ * Fetches the timeouts currently configured for the current session.
1647
+ *
1648
+ * @return {!Promise<{script: number,
1649
+ * pageLoad: number,
1650
+ * implicit: number}>} A promise that will be
1651
+ * resolved with the timeouts currently configured for the current
1652
+ * session.
1653
+ * @see #setTimeouts()
1654
+ */
1655
+ getTimeouts() {
1656
+ return this.driver_.execute(new command.Command(command.Name.GET_TIMEOUT))
1657
+ }
1658
+
1659
+ /**
1660
+ * Sets the timeout durations associated with the current session.
1661
+ *
1662
+ * The following timeouts are supported (all timeouts are specified in
1663
+ * milliseconds):
1664
+ *
1665
+ * - `implicit` specifies the maximum amount of time to wait for an element
1666
+ * locator to succeed when {@linkplain WebDriver#findElement locating}
1667
+ * {@linkplain WebDriver#findElements elements} on the page.
1668
+ * Defaults to 0 milliseconds.
1669
+ *
1670
+ * - `pageLoad` specifies the maximum amount of time to wait for a page to
1671
+ * finishing loading. Defaults to 300000 milliseconds.
1672
+ *
1673
+ * - `script` specifies the maximum amount of time to wait for an
1674
+ * {@linkplain WebDriver#executeScript evaluated script} to run. If set to
1675
+ * `null`, the script timeout will be indefinite.
1676
+ * Defaults to 30000 milliseconds.
1677
+ *
1678
+ * @param {{script: (number|null|undefined),
1679
+ * pageLoad: (number|null|undefined),
1680
+ * implicit: (number|null|undefined)}} conf
1681
+ * The desired timeout configuration.
1682
+ * @return {!Promise<void>} A promise that will be resolved when the timeouts
1683
+ * have been set.
1684
+ * @throws {!TypeError} if an invalid options object is provided.
1685
+ * @see #getTimeouts()
1686
+ * @see <https://w3c.github.io/webdriver/webdriver-spec.html#dfn-set-timeouts>
1687
+ */
1688
+ setTimeouts({ script, pageLoad, implicit } = {}) {
1689
+ let cmd = new command.Command(command.Name.SET_TIMEOUT)
1690
+
1691
+ let valid = false
1692
+ function setParam(key, value) {
1693
+ if (value === null || typeof value === 'number') {
1694
+ valid = true
1695
+ cmd.setParameter(key, value)
1696
+ } else if (typeof value !== 'undefined') {
1697
+ throw TypeError(
1698
+ 'invalid timeouts configuration:' +
1699
+ ` expected "${key}" to be a number, got ${typeof value}`
1700
+ )
1701
+ }
1702
+ }
1703
+ setParam('implicit', implicit)
1704
+ setParam('pageLoad', pageLoad)
1705
+ setParam('script', script)
1706
+
1707
+ if (valid) {
1708
+ return this.driver_.execute(cmd).catch(() => {
1709
+ // Fallback to the legacy method.
1710
+ let cmds = []
1711
+ if (typeof script === 'number') {
1712
+ cmds.push(legacyTimeout(this.driver_, 'script', script))
1713
+ }
1714
+ if (typeof implicit === 'number') {
1715
+ cmds.push(legacyTimeout(this.driver_, 'implicit', implicit))
1716
+ }
1717
+ if (typeof pageLoad === 'number') {
1718
+ cmds.push(legacyTimeout(this.driver_, 'page load', pageLoad))
1719
+ }
1720
+ return Promise.all(cmds)
1721
+ })
1722
+ }
1723
+ throw TypeError('no timeouts specified')
1724
+ }
1725
+
1726
+ /**
1727
+ * @return {!Logs} The interface for managing driver logs.
1728
+ */
1729
+ logs() {
1730
+ return new Logs(this.driver_)
1731
+ }
1732
+
1733
+ /**
1734
+ * @return {!Window} The interface for managing the current window.
1735
+ */
1736
+ window() {
1737
+ return new Window(this.driver_)
1738
+ }
1739
+ }
1740
+
1741
+ /**
1742
+ * @param {!WebDriver} driver
1743
+ * @param {string} type
1744
+ * @param {number} ms
1745
+ * @return {!Promise<void>}
1746
+ */
1747
+ function legacyTimeout(driver, type, ms) {
1748
+ return driver.execute(
1749
+ new command.Command(command.Name.SET_TIMEOUT)
1750
+ .setParameter('type', type)
1751
+ .setParameter('ms', ms)
1752
+ )
1753
+ }
1754
+
1755
+ /**
1756
+ * A record object describing a browser cookie.
1757
+ *
1758
+ * @record
1759
+ */
1760
+ Options.Cookie = function () { }
1761
+
1762
+ /**
1763
+ * The name of the cookie.
1764
+ *
1765
+ * @type {string}
1766
+ */
1767
+ Options.Cookie.prototype.name
1768
+
1769
+ /**
1770
+ * The cookie value.
1771
+ *
1772
+ * @type {string}
1773
+ */
1774
+ Options.Cookie.prototype.value
1775
+
1776
+ /**
1777
+ * The cookie path. Defaults to "/" when adding a cookie.
1778
+ *
1779
+ * @type {(string|undefined)}
1780
+ */
1781
+ Options.Cookie.prototype.path
1782
+
1783
+ /**
1784
+ * The domain the cookie is visible to. Defaults to the current browsing
1785
+ * context's document's URL when adding a cookie.
1786
+ *
1787
+ * @type {(string|undefined)}
1788
+ */
1789
+ Options.Cookie.prototype.domain
1790
+
1791
+ /**
1792
+ * Whether the cookie is a secure cookie. Defaults to false when adding a new
1793
+ * cookie.
1794
+ *
1795
+ * @type {(boolean|undefined)}
1796
+ */
1797
+ Options.Cookie.prototype.secure
1798
+
1799
+ /**
1800
+ * Whether the cookie is an HTTP only cookie. Defaults to false when adding a
1801
+ * new cookie.
1802
+ *
1803
+ * @type {(boolean|undefined)}
1804
+ */
1805
+ Options.Cookie.prototype.httpOnly
1806
+
1807
+ /**
1808
+ * When the cookie expires.
1809
+ *
1810
+ * When {@linkplain Options#addCookie() adding a cookie}, this may be specified
1811
+ * as a {@link Date} object, or in _seconds_ since Unix epoch (January 1, 1970).
1812
+ *
1813
+ * The expiry is always returned in seconds since epoch when
1814
+ * {@linkplain Options#getCookies() retrieving cookies} from the browser.
1815
+ *
1816
+ * @type {(!Date|number|undefined)}
1817
+ */
1818
+ Options.Cookie.prototype.expiry
1819
+
1820
+ /**
1821
+ * When the cookie applies to a SameSite policy.
1822
+ *
1823
+ * When {@linkplain Options#addCookie() adding a cookie}, this may be specified
1824
+ * as a {@link string} object which is one of 'Lax', 'Strict' or 'None'.
1825
+ *
1826
+ *
1827
+ * @type {(string|undefined)}
1828
+ */
1829
+ Options.Cookie.prototype.sameSite
1830
+
1831
+ /**
1832
+ * An interface for managing the current window.
1833
+ *
1834
+ * This class should never be instantiated directly. Instead, obtain an instance
1835
+ * with
1836
+ *
1837
+ * webdriver.manage().window()
1838
+ *
1839
+ * @see WebDriver#manage()
1840
+ * @see Options#window()
1841
+ */
1842
+ class Window {
1843
+ /**
1844
+ * @param {!WebDriver} driver The parent driver.
1845
+ * @private
1846
+ */
1847
+ constructor(driver) {
1848
+ /** @private {!WebDriver} */
1849
+ this.driver_ = driver
1850
+ }
1851
+
1852
+ /**
1853
+ * Retrieves the a rect describing the current top-level window's size and
1854
+ * position.
1855
+ *
1856
+ * @return {!Promise<{x: number, y: number, width: number, height: number}>}
1857
+ * A promise that will resolve to the window rect of the current window.
1858
+ */
1859
+ async getRect() {
1860
+ try {
1861
+ return await this.driver_.execute(
1862
+ new command.Command(command.Name.GET_WINDOW_RECT)
1863
+ )
1864
+ } catch (ex) {
1865
+ if (ex instanceof error.UnknownCommandError) {
1866
+ let { width, height } = await this.driver_.execute(
1867
+ new command.Command(command.Name.GET_WINDOW_SIZE).setParameter(
1868
+ 'windowHandle',
1869
+ 'current'
1870
+ )
1871
+ )
1872
+ let { x, y } = await this.driver_.execute(
1873
+ new command.Command(command.Name.GET_WINDOW_POSITION).setParameter(
1874
+ 'windowHandle',
1875
+ 'current'
1876
+ )
1877
+ )
1878
+ return { x, y, width, height }
1879
+ }
1880
+ throw ex
1881
+ }
1882
+ }
1883
+
1884
+ /**
1885
+ * Sets the current top-level window's size and position. You may update just
1886
+ * the size by omitting `x` & `y`, or just the position by omitting
1887
+ * `width` & `height` options.
1888
+ *
1889
+ * @param {{x: (number|undefined),
1890
+ * y: (number|undefined),
1891
+ * width: (number|undefined),
1892
+ * height: (number|undefined)}} options
1893
+ * The desired window size and position.
1894
+ * @return {!Promise<{x: number, y: number, width: number, height: number}>}
1895
+ * A promise that will resolve to the current window's updated window
1896
+ * rect.
1897
+ */
1898
+ async setRect({ x, y, width, height }) {
1899
+ try {
1900
+ return await this.driver_.execute(
1901
+ new command.Command(command.Name.SET_WINDOW_RECT).setParameters({
1902
+ x,
1903
+ y,
1904
+ width,
1905
+ height,
1906
+ })
1907
+ )
1908
+ } catch (ex) {
1909
+ if (ex instanceof error.UnknownCommandError) {
1910
+ if (typeof x === 'number' && typeof y === 'number') {
1911
+ await this.driver_.execute(
1912
+ new command.Command(command.Name.SET_WINDOW_POSITION)
1913
+ .setParameter('windowHandle', 'current')
1914
+ .setParameter('x', x)
1915
+ .setParameter('y', y)
1916
+ )
1917
+ }
1918
+
1919
+ if (typeof width === 'number' && typeof height === 'number') {
1920
+ await this.driver_.execute(
1921
+ new command.Command(command.Name.SET_WINDOW_SIZE)
1922
+ .setParameter('windowHandle', 'current')
1923
+ .setParameter('width', width)
1924
+ .setParameter('height', height)
1925
+ )
1926
+ }
1927
+ return this.getRect()
1928
+ }
1929
+ throw ex
1930
+ }
1931
+ }
1932
+
1933
+ /**
1934
+ * Maximizes the current window. The exact behavior of this command is
1935
+ * specific to individual window managers, but typically involves increasing
1936
+ * the window to the maximum available size without going full-screen.
1937
+ *
1938
+ * @return {!Promise<void>} A promise that will be resolved when the command
1939
+ * has completed.
1940
+ */
1941
+ maximize() {
1942
+ return this.driver_.execute(
1943
+ new command.Command(command.Name.MAXIMIZE_WINDOW).setParameter(
1944
+ 'windowHandle',
1945
+ 'current'
1946
+ )
1947
+ )
1948
+ }
1949
+
1950
+ /**
1951
+ * Minimizes the current window. The exact behavior of this command is
1952
+ * specific to individual window managers, but typically involves hiding
1953
+ * the window in the system tray.
1954
+ *
1955
+ * @return {!Promise<void>} A promise that will be resolved when the command
1956
+ * has completed.
1957
+ */
1958
+ minimize() {
1959
+ return this.driver_.execute(
1960
+ new command.Command(command.Name.MINIMIZE_WINDOW)
1961
+ )
1962
+ }
1963
+
1964
+ /**
1965
+ * Invokes the "full screen" operation on the current window. The exact
1966
+ * behavior of this command is specific to individual window managers, but
1967
+ * this will typically increase the window size to the size of the physical
1968
+ * display and hide the browser chrome.
1969
+ *
1970
+ * @return {!Promise<void>} A promise that will be resolved when the command
1971
+ * has completed.
1972
+ * @see <https://fullscreen.spec.whatwg.org/#fullscreen-an-element>
1973
+ */
1974
+ fullscreen() {
1975
+ return this.driver_.execute(
1976
+ new command.Command(command.Name.FULLSCREEN_WINDOW)
1977
+ )
1978
+ }
1979
+ }
1980
+
1981
+ /**
1982
+ * Interface for managing WebDriver log records.
1983
+ *
1984
+ * This class should never be instantiated directly. Instead, obtain an
1985
+ * instance with
1986
+ *
1987
+ * webdriver.manage().logs()
1988
+ *
1989
+ * @see WebDriver#manage()
1990
+ * @see Options#logs()
1991
+ */
1992
+ class Logs {
1993
+ /**
1994
+ * @param {!WebDriver} driver The parent driver.
1995
+ * @private
1996
+ */
1997
+ constructor(driver) {
1998
+ /** @private {!WebDriver} */
1999
+ this.driver_ = driver
2000
+ }
2001
+
2002
+ /**
2003
+ * Fetches available log entries for the given type.
2004
+ *
2005
+ * Note that log buffers are reset after each call, meaning that available
2006
+ * log entries correspond to those entries not yet returned for a given log
2007
+ * type. In practice, this means that this call will return the available log
2008
+ * entries since the last call, or from the start of the session.
2009
+ *
2010
+ * @param {!logging.Type} type The desired log type.
2011
+ * @return {!Promise<!Array.<!logging.Entry>>} A
2012
+ * promise that will resolve to a list of log entries for the specified
2013
+ * type.
2014
+ */
2015
+ get(type) {
2016
+ let cmd = new command.Command(command.Name.GET_LOG).setParameter(
2017
+ 'type',
2018
+ type
2019
+ )
2020
+ return this.driver_.execute(cmd).then(function (entries) {
2021
+ return entries.map(function (entry) {
2022
+ if (!(entry instanceof logging.Entry)) {
2023
+ return new logging.Entry(
2024
+ entry['level'],
2025
+ entry['message'],
2026
+ entry['timestamp'],
2027
+ entry['type']
2028
+ )
2029
+ }
2030
+ return entry
2031
+ })
2032
+ })
2033
+ }
2034
+
2035
+ /**
2036
+ * Retrieves the log types available to this driver.
2037
+ * @return {!Promise<!Array<!logging.Type>>} A
2038
+ * promise that will resolve to a list of available log types.
2039
+ */
2040
+ getAvailableLogTypes() {
2041
+ return this.driver_.execute(
2042
+ new command.Command(command.Name.GET_AVAILABLE_LOG_TYPES)
2043
+ )
2044
+ }
2045
+ }
2046
+
2047
+ /**
2048
+ * An interface for changing the focus of the driver to another frame or window.
2049
+ *
2050
+ * This class should never be instantiated directly. Instead, obtain an
2051
+ * instance with
2052
+ *
2053
+ * webdriver.switchTo()
2054
+ *
2055
+ * @see WebDriver#switchTo()
2056
+ */
2057
+ class TargetLocator {
2058
+ /**
2059
+ * @param {!WebDriver} driver The parent driver.
2060
+ * @private
2061
+ */
2062
+ constructor(driver) {
2063
+ /** @private {!WebDriver} */
2064
+ this.driver_ = driver
2065
+ }
2066
+
2067
+ /**
2068
+ * Locates the DOM element on the current page that corresponds to
2069
+ * `document.activeElement` or `document.body` if the active element is not
2070
+ * available.
2071
+ *
2072
+ * @return {!WebElementPromise} The active element.
2073
+ */
2074
+ activeElement() {
2075
+ var id = this.driver_.execute(
2076
+ new command.Command(command.Name.GET_ACTIVE_ELEMENT)
2077
+ )
2078
+ return new WebElementPromise(this.driver_, id)
2079
+ }
2080
+
2081
+ /**
2082
+ * Switches focus of all future commands to the topmost frame in the current
2083
+ * window.
2084
+ *
2085
+ * @return {!Promise<void>} A promise that will be resolved
2086
+ * when the driver has changed focus to the default content.
2087
+ */
2088
+ defaultContent() {
2089
+ return this.driver_.execute(
2090
+ new command.Command(command.Name.SWITCH_TO_FRAME).setParameter('id', null)
2091
+ )
2092
+ }
2093
+
2094
+ /**
2095
+ * Changes the focus of all future commands to another frame on the page. The
2096
+ * target frame may be specified as one of the following:
2097
+ *
2098
+ * - A number that specifies a (zero-based) index into [window.frames](
2099
+ * https://developer.mozilla.org/en-US/docs/Web/API/Window.frames).
2100
+ * - A {@link WebElement} reference, which correspond to a `frame` or `iframe`
2101
+ * DOM element.
2102
+ * - The `null` value, to select the topmost frame on the page. Passing `null`
2103
+ * is the same as calling {@link #defaultContent defaultContent()}.
2104
+ *
2105
+ * If the specified frame can not be found, the returned promise will be
2106
+ * rejected with a {@linkplain error.NoSuchFrameError}.
2107
+ *
2108
+ * @param {(number|WebElement|null)} id The frame locator.
2109
+ * @return {!Promise<void>} A promise that will be resolved
2110
+ * when the driver has changed focus to the specified frame.
2111
+ */
2112
+ frame(id) {
2113
+ return this.driver_.execute(
2114
+ new command.Command(command.Name.SWITCH_TO_FRAME).setParameter('id', id)
2115
+ )
2116
+ }
2117
+
2118
+ /**
2119
+ * Changes the focus of all future commands to the parent frame of the
2120
+ * currently selected frame. This command has no effect if the driver is
2121
+ * already focused on the top-level browsing context.
2122
+ *
2123
+ * @return {!Promise<void>} A promise that will be resolved when the command
2124
+ * has completed.
2125
+ */
2126
+ parentFrame() {
2127
+ return this.driver_.execute(
2128
+ new command.Command(command.Name.SWITCH_TO_FRAME_PARENT)
2129
+ )
2130
+ }
2131
+
2132
+ /**
2133
+ * Changes the focus of all future commands to another window. Windows may be
2134
+ * specified by their {@code window.name} attribute or by its handle
2135
+ * (as returned by {@link WebDriver#getWindowHandles}).
2136
+ *
2137
+ * If the specified window cannot be found, the returned promise will be
2138
+ * rejected with a {@linkplain error.NoSuchWindowError}.
2139
+ *
2140
+ * @param {string} nameOrHandle The name or window handle of the window to
2141
+ * switch focus to.
2142
+ * @return {!Promise<void>} A promise that will be resolved
2143
+ * when the driver has changed focus to the specified window.
2144
+ */
2145
+ window(nameOrHandle) {
2146
+ return this.driver_.execute(
2147
+ new command.Command(command.Name.SWITCH_TO_WINDOW)
2148
+ // "name" supports the legacy drivers. "handle" is the W3C
2149
+ // compliant parameter.
2150
+ .setParameter('name', nameOrHandle)
2151
+ .setParameter('handle', nameOrHandle)
2152
+ )
2153
+ }
2154
+
2155
+ /**
2156
+ * Creates a new browser window and switches the focus for future
2157
+ * commands of this driver to the new window.
2158
+ *
2159
+ * @param {string} typeHint 'window' or 'tab'. The created window is not
2160
+ * guaranteed to be of the requested type; if the driver does not support
2161
+ * the requested type, a new browser window will be created of whatever type
2162
+ * the driver does support.
2163
+ * @return {!Promise<void>} A promise that will be resolved
2164
+ * when the driver has changed focus to the new window.
2165
+ */
2166
+ newWindow(typeHint) {
2167
+ var driver = this.driver_
2168
+ return this.driver_
2169
+ .execute(
2170
+ new command.Command(command.Name.SWITCH_TO_NEW_WINDOW).setParameter(
2171
+ 'type',
2172
+ typeHint
2173
+ )
2174
+ )
2175
+ .then(function (response) {
2176
+ return driver.switchTo().window(response.handle)
2177
+ })
2178
+ }
2179
+
2180
+ /**
2181
+ * Changes focus to the active modal dialog, such as those opened by
2182
+ * `window.alert()`, `window.confirm()`, and `window.prompt()`. The returned
2183
+ * promise will be rejected with a
2184
+ * {@linkplain error.NoSuchAlertError} if there are no open alerts.
2185
+ *
2186
+ * @return {!AlertPromise} The open alert.
2187
+ */
2188
+ alert() {
2189
+ var text = this.driver_.execute(
2190
+ new command.Command(command.Name.GET_ALERT_TEXT)
2191
+ )
2192
+ var driver = this.driver_
2193
+ return new AlertPromise(
2194
+ driver,
2195
+ text.then(function (text) {
2196
+ return new Alert(driver, text)
2197
+ })
2198
+ )
2199
+ }
2200
+ }
2201
+
2202
+ //////////////////////////////////////////////////////////////////////////////
2203
+ //
2204
+ // WebElement
2205
+ //
2206
+ //////////////////////////////////////////////////////////////////////////////
2207
+
2208
+ const LEGACY_ELEMENT_ID_KEY = 'ELEMENT'
2209
+ const ELEMENT_ID_KEY = 'element-6066-11e4-a52e-4f735466cecf'
2210
+
2211
+ /**
2212
+ * Represents a DOM element. WebElements can be found by searching from the
2213
+ * document root using a {@link WebDriver} instance, or by searching
2214
+ * under another WebElement:
2215
+ *
2216
+ * driver.get('http://www.google.com');
2217
+ * var searchForm = driver.findElement(By.tagName('form'));
2218
+ * var searchBox = searchForm.findElement(By.name('q'));
2219
+ * searchBox.sendKeys('webdriver');
2220
+ */
2221
+ class WebElement {
2222
+ /**
2223
+ * @param {!WebDriver} driver the parent WebDriver instance for this element.
2224
+ * @param {(!IThenable<string>|string)} id The server-assigned opaque ID for
2225
+ * the underlying DOM element.
2226
+ */
2227
+ constructor(driver, id) {
2228
+ /** @private {!WebDriver} */
2229
+ this.driver_ = driver
2230
+
2231
+ /** @private {!Promise<string>} */
2232
+ this.id_ = Promise.resolve(id)
2233
+ }
2234
+
2235
+ /**
2236
+ * @param {string} id The raw ID.
2237
+ * @param {boolean=} noLegacy Whether to exclude the legacy element key.
2238
+ * @return {!Object} The element ID for use with WebDriver's wire protocol.
2239
+ */
2240
+ static buildId(id, noLegacy = false) {
2241
+ return noLegacy
2242
+ ? { [ELEMENT_ID_KEY]: id }
2243
+ : { [ELEMENT_ID_KEY]: id, [LEGACY_ELEMENT_ID_KEY]: id }
2244
+ }
2245
+
2246
+ /**
2247
+ * Extracts the encoded WebElement ID from the object.
2248
+ *
2249
+ * @param {?} obj The object to extract the ID from.
2250
+ * @return {string} the extracted ID.
2251
+ * @throws {TypeError} if the object is not a valid encoded ID.
2252
+ */
2253
+ static extractId(obj) {
2254
+ if (obj && typeof obj === 'object') {
2255
+ if (typeof obj[ELEMENT_ID_KEY] === 'string') {
2256
+ return obj[ELEMENT_ID_KEY]
2257
+ } else if (typeof obj[LEGACY_ELEMENT_ID_KEY] === 'string') {
2258
+ return obj[LEGACY_ELEMENT_ID_KEY]
2259
+ }
2260
+ }
2261
+ throw new TypeError('object is not a WebElement ID')
2262
+ }
2263
+
2264
+ /**
2265
+ * @param {?} obj the object to test.
2266
+ * @return {boolean} whether the object is a valid encoded WebElement ID.
2267
+ */
2268
+ static isId(obj) {
2269
+ return (
2270
+ obj &&
2271
+ typeof obj === 'object' &&
2272
+ (typeof obj[ELEMENT_ID_KEY] === 'string' ||
2273
+ typeof obj[LEGACY_ELEMENT_ID_KEY] === 'string')
2274
+ )
2275
+ }
2276
+
2277
+ /**
2278
+ * Compares two WebElements for equality.
2279
+ *
2280
+ * @param {!WebElement} a A WebElement.
2281
+ * @param {!WebElement} b A WebElement.
2282
+ * @return {!Promise<boolean>} A promise that will be
2283
+ * resolved to whether the two WebElements are equal.
2284
+ */
2285
+ static async equals(a, b) {
2286
+ if (a === b) {
2287
+ return true
2288
+ }
2289
+ return a.driver_.executeScript('return arguments[0] === arguments[1]', a, b)
2290
+ }
2291
+
2292
+ /** @return {!WebDriver} The parent driver for this instance. */
2293
+ getDriver() {
2294
+ return this.driver_
2295
+ }
2296
+
2297
+ /**
2298
+ * @return {!Promise<string>} A promise that resolves to
2299
+ * the server-assigned opaque ID assigned to this element.
2300
+ */
2301
+ getId() {
2302
+ return this.id_
2303
+ }
2304
+
2305
+ /**
2306
+ * @return {!Object} Returns the serialized representation of this WebElement.
2307
+ */
2308
+ [Symbols.serialize]() {
2309
+ return this.getId().then(WebElement.buildId)
2310
+ }
2311
+
2312
+ /**
2313
+ * Schedules a command that targets this element with the parent WebDriver
2314
+ * instance. Will ensure this element's ID is included in the command
2315
+ * parameters under the "id" key.
2316
+ *
2317
+ * @param {!command.Command} command The command to schedule.
2318
+ * @return {!Promise<T>} A promise that will be resolved with the result.
2319
+ * @template T
2320
+ * @see WebDriver#schedule
2321
+ * @private
2322
+ */
2323
+ execute_(command) {
2324
+ command.setParameter('id', this)
2325
+ return this.driver_.execute(command)
2326
+ }
2327
+
2328
+ /**
2329
+ * Schedule a command to find a descendant of this element. If the element
2330
+ * cannot be found, the returned promise will be rejected with a
2331
+ * {@linkplain error.NoSuchElementError NoSuchElementError}.
2332
+ *
2333
+ * The search criteria for an element may be defined using one of the static
2334
+ * factories on the {@link by.By} class, or as a short-hand
2335
+ * {@link ./by.ByHash} object. For example, the following two statements
2336
+ * are equivalent:
2337
+ *
2338
+ * var e1 = element.findElement(By.id('foo'));
2339
+ * var e2 = element.findElement({id:'foo'});
2340
+ *
2341
+ * You may also provide a custom locator function, which takes as input this
2342
+ * instance and returns a {@link WebElement}, or a promise that will resolve
2343
+ * to a WebElement. If the returned promise resolves to an array of
2344
+ * WebElements, WebDriver will use the first element. For example, to find the
2345
+ * first visible link on a page, you could write:
2346
+ *
2347
+ * var link = element.findElement(firstVisibleLink);
2348
+ *
2349
+ * function firstVisibleLink(element) {
2350
+ * var links = element.findElements(By.tagName('a'));
2351
+ * return promise.filter(links, function(link) {
2352
+ * return link.isDisplayed();
2353
+ * });
2354
+ * }
2355
+ *
2356
+ * @param {!(by.By|Function)} locator The locator strategy to use when
2357
+ * searching for the element.
2358
+ * @return {!WebElementPromise} A WebElement that can be used to issue
2359
+ * commands against the located element. If the element is not found, the
2360
+ * element will be invalidated and all scheduled commands aborted.
2361
+ */
2362
+ findElement(locator) {
2363
+ locator = by.checkedLocator(locator)
2364
+ let id
2365
+ if (typeof locator === 'function') {
2366
+ id = this.driver_.findElementInternal_(locator, this)
2367
+ } else {
2368
+ let cmd = new command.Command(command.Name.FIND_CHILD_ELEMENT)
2369
+ .setParameter('using', locator.using)
2370
+ .setParameter('value', locator.value)
2371
+ id = this.execute_(cmd)
2372
+ }
2373
+ return new WebElementPromise(this.driver_, id)
2374
+ }
2375
+
2376
+ /**
2377
+ * Locates all of the descendants of this element that match the given search
2378
+ * criteria.
2379
+ *
2380
+ * @param {!(by.By|Function)} locator The locator strategy to use when
2381
+ * searching for the element.
2382
+ * @return {!Promise<!Array<!WebElement>>} A promise that will resolve to an
2383
+ * array of WebElements.
2384
+ */
2385
+ async findElements(locator) {
2386
+ locator = by.checkedLocator(locator)
2387
+ if (typeof locator === 'function') {
2388
+ return this.driver_.findElementsInternal_(locator, this)
2389
+ } else {
2390
+ let cmd = new command.Command(command.Name.FIND_CHILD_ELEMENTS)
2391
+ .setParameter('using', locator.using)
2392
+ .setParameter('value', locator.value)
2393
+ let result = await this.execute_(cmd)
2394
+ return Array.isArray(result) ? result : []
2395
+ }
2396
+ }
2397
+
2398
+ /**
2399
+ * Clicks on this element.
2400
+ *
2401
+ * @return {!Promise<void>} A promise that will be resolved when the click
2402
+ * command has completed.
2403
+ */
2404
+ click() {
2405
+ return this.execute_(new command.Command(command.Name.CLICK_ELEMENT))
2406
+ }
2407
+
2408
+ /**
2409
+ * Types a key sequence on the DOM element represented by this instance.
2410
+ *
2411
+ * Modifier keys (SHIFT, CONTROL, ALT, META) are stateful; once a modifier is
2412
+ * processed in the key sequence, that key state is toggled until one of the
2413
+ * following occurs:
2414
+ *
2415
+ * - The modifier key is encountered again in the sequence. At this point the
2416
+ * state of the key is toggled (along with the appropriate keyup/down
2417
+ * events).
2418
+ * - The {@link input.Key.NULL} key is encountered in the sequence. When
2419
+ * this key is encountered, all modifier keys current in the down state are
2420
+ * released (with accompanying keyup events). The NULL key can be used to
2421
+ * simulate common keyboard shortcuts:
2422
+ *
2423
+ * element.sendKeys("text was",
2424
+ * Key.CONTROL, "a", Key.NULL,
2425
+ * "now text is");
2426
+ * // Alternatively:
2427
+ * element.sendKeys("text was",
2428
+ * Key.chord(Key.CONTROL, "a"),
2429
+ * "now text is");
2430
+ *
2431
+ * - The end of the key sequence is encountered. When there are no more keys
2432
+ * to type, all depressed modifier keys are released (with accompanying
2433
+ * keyup events).
2434
+ *
2435
+ * If this element is a file input ({@code <input type="file">}), the
2436
+ * specified key sequence should specify the path to the file to attach to
2437
+ * the element. This is analogous to the user clicking "Browse..." and entering
2438
+ * the path into the file select dialog.
2439
+ *
2440
+ * var form = driver.findElement(By.css('form'));
2441
+ * var element = form.findElement(By.css('input[type=file]'));
2442
+ * element.sendKeys('/path/to/file.txt');
2443
+ * form.submit();
2444
+ *
2445
+ * For uploads to function correctly, the entered path must reference a file
2446
+ * on the _browser's_ machine, not the local machine running this script. When
2447
+ * running against a remote Selenium server, a {@link input.FileDetector}
2448
+ * may be used to transparently copy files to the remote machine before
2449
+ * attempting to upload them in the browser.
2450
+ *
2451
+ * __Note:__ On browsers where native keyboard events are not supported
2452
+ * (e.g. Firefox on OS X), key events will be synthesized. Special
2453
+ * punctuation keys will be synthesized according to a standard QWERTY en-us
2454
+ * keyboard layout.
2455
+ *
2456
+ * @param {...(number|string|!IThenable<(number|string)>)} args The
2457
+ * sequence of keys to type. Number keys may be referenced numerically or
2458
+ * by string (1 or '1'). All arguments will be joined into a single
2459
+ * sequence.
2460
+ * @return {!Promise<void>} A promise that will be resolved when all keys
2461
+ * have been typed.
2462
+ */
2463
+ async sendKeys(...args) {
2464
+ let keys = []
2465
+ ; (await Promise.all(args)).forEach((key) => {
2466
+ let type = typeof key
2467
+ if (type === 'number') {
2468
+ key = String(key)
2469
+ } else if (type !== 'string') {
2470
+ throw TypeError('each key must be a number of string; got ' + type)
2471
+ }
2472
+
2473
+ // The W3C protocol requires keys to be specified as an array where
2474
+ // each element is a single key.
2475
+ keys.push(...key.split(''))
2476
+ })
2477
+
2478
+ if (!this.driver_.fileDetector_) {
2479
+ return this.execute_(
2480
+ new command.Command(command.Name.SEND_KEYS_TO_ELEMENT)
2481
+ .setParameter('text', keys.join(''))
2482
+ .setParameter('value', keys)
2483
+ )
2484
+ }
2485
+
2486
+ keys = await this.driver_.fileDetector_.handleFile(
2487
+ this.driver_,
2488
+ keys.join('')
2489
+ )
2490
+ return this.execute_(
2491
+ new command.Command(command.Name.SEND_KEYS_TO_ELEMENT)
2492
+ .setParameter('text', keys)
2493
+ .setParameter('value', keys.split(''))
2494
+ )
2495
+ }
2496
+
2497
+ /**
2498
+ * Retrieves the element's tag name.
2499
+ *
2500
+ * @return {!Promise<string>} A promise that will be resolved with the
2501
+ * element's tag name.
2502
+ */
2503
+ getTagName() {
2504
+ return this.execute_(new command.Command(command.Name.GET_ELEMENT_TAG_NAME))
2505
+ }
2506
+
2507
+ /**
2508
+ * Retrieves the value of a computed style property for this instance. If
2509
+ * the element inherits the named style from its parent, the parent will be
2510
+ * queried for its value. Where possible, color values will be converted to
2511
+ * their hex representation (e.g. #00ff00 instead of rgb(0, 255, 0)).
2512
+ *
2513
+ * _Warning:_ the value returned will be as the browser interprets it, so
2514
+ * it may be tricky to form a proper assertion.
2515
+ *
2516
+ * @param {string} cssStyleProperty The name of the CSS style property to look
2517
+ * up.
2518
+ * @return {!Promise<string>} A promise that will be resolved with the
2519
+ * requested CSS value.
2520
+ */
2521
+ getCssValue(cssStyleProperty) {
2522
+ var name = command.Name.GET_ELEMENT_VALUE_OF_CSS_PROPERTY
2523
+ return this.execute_(
2524
+ new command.Command(name).setParameter('propertyName', cssStyleProperty)
2525
+ )
2526
+ }
2527
+
2528
+ /**
2529
+ * Retrieves the current value of the given attribute of this element.
2530
+ * Will return the current value, even if it has been modified after the page
2531
+ * has been loaded. More exactly, this method will return the value
2532
+ * of the given attribute, unless that attribute is not present, in which case
2533
+ * the value of the property with the same name is returned. If neither value
2534
+ * is set, null is returned (for example, the "value" property of a textarea
2535
+ * element). The "style" attribute is converted as best can be to a
2536
+ * text representation with a trailing semi-colon. The following are deemed to
2537
+ * be "boolean" attributes and will return either "true" or null:
2538
+ *
2539
+ * async, autofocus, autoplay, checked, compact, complete, controls, declare,
2540
+ * defaultchecked, defaultselected, defer, disabled, draggable, ended,
2541
+ * formnovalidate, hidden, indeterminate, iscontenteditable, ismap, itemscope,
2542
+ * loop, multiple, muted, nohref, noresize, noshade, novalidate, nowrap, open,
2543
+ * paused, pubdate, readonly, required, reversed, scoped, seamless, seeking,
2544
+ * selected, spellcheck, truespeed, willvalidate
2545
+ *
2546
+ * Finally, the following commonly mis-capitalized attribute/property names
2547
+ * are evaluated as expected:
2548
+ *
2549
+ * - "class"
2550
+ * - "readonly"
2551
+ *
2552
+ * @param {string} attributeName The name of the attribute to query.
2553
+ * @return {!Promise<?string>} A promise that will be
2554
+ * resolved with the attribute's value. The returned value will always be
2555
+ * either a string or null.
2556
+ */
2557
+ getAttribute(attributeName) {
2558
+ return this.execute_(
2559
+ new command.Command(command.Name.GET_ELEMENT_ATTRIBUTE).setParameter(
2560
+ 'name',
2561
+ attributeName
2562
+ )
2563
+ )
2564
+ }
2565
+
2566
+ /**
2567
+ * Get the given property of the referenced web element
2568
+ * @param {string} propertyName The name of the attribute to query.
2569
+ * @return {!Promise<string>} A promise that will be
2570
+ * resolved with the element's property value
2571
+ */
2572
+ getProperty(propertyName) {
2573
+ return this.execute_(
2574
+ new command.Command(command.Name.GET_ELEMENT_PROPERTY).setParameter(
2575
+ 'name',
2576
+ propertyName
2577
+ )
2578
+ )
2579
+ }
2580
+
2581
+ /**
2582
+ * Get the visible (i.e. not hidden by CSS) innerText of this element,
2583
+ * including sub-elements, without any leading or trailing whitespace.
2584
+ *
2585
+ * @return {!Promise<string>} A promise that will be
2586
+ * resolved with the element's visible text.
2587
+ */
2588
+ getText() {
2589
+ return this.execute_(new command.Command(command.Name.GET_ELEMENT_TEXT))
2590
+ }
2591
+
2592
+ /**
2593
+ * Get the computed WAI-ARIA role of element.
2594
+ *
2595
+ * @return {!Promise<string>} A promise that will be
2596
+ * resolved with the element's computed role.
2597
+ */
2598
+ getAriaRole() {
2599
+ return this.execute_(new command.Command(command.Name.GET_COMPUTED_ROLE))
2600
+ }
2601
+
2602
+ /**
2603
+ * Get the computed WAI-ARIA label of element.
2604
+ *
2605
+ * @return {!Promise<string>} A promise that will be
2606
+ * resolved with the element's computed label.
2607
+ */
2608
+ getAccessibleName() {
2609
+ return this.execute_(new command.Command(command.Name.GET_COMPUTED_LABEL))
2610
+ }
2611
+ /**
2612
+ * Returns an object describing an element's location, in pixels relative to
2613
+ * the document element, and the element's size in pixels.
2614
+ *
2615
+ * @return {!Promise<{width: number, height: number, x: number, y: number}>}
2616
+ * A promise that will resolve with the element's rect.
2617
+ */
2618
+ async getRect() {
2619
+ try {
2620
+ return await this.execute_(
2621
+ new command.Command(command.Name.GET_ELEMENT_RECT)
2622
+ )
2623
+ } catch (err) {
2624
+ if (err instanceof error.UnknownCommandError) {
2625
+ const { width, height } = await this.execute_(
2626
+ new command.Command(command.Name.GET_ELEMENT_SIZE)
2627
+ )
2628
+ const { x, y } = await this.execute_(
2629
+ new command.Command(command.Name.GET_ELEMENT_LOCATION)
2630
+ )
2631
+ return { x, y, width, height }
2632
+ }
2633
+ }
2634
+ }
2635
+
2636
+ /**
2637
+ * Tests whether this element is enabled, as dictated by the `disabled`
2638
+ * attribute.
2639
+ *
2640
+ * @return {!Promise<boolean>} A promise that will be
2641
+ * resolved with whether this element is currently enabled.
2642
+ */
2643
+ isEnabled() {
2644
+ return this.execute_(new command.Command(command.Name.IS_ELEMENT_ENABLED))
2645
+ }
2646
+
2647
+ /**
2648
+ * Tests whether this element is selected.
2649
+ *
2650
+ * @return {!Promise<boolean>} A promise that will be
2651
+ * resolved with whether this element is currently selected.
2652
+ */
2653
+ isSelected() {
2654
+ return this.execute_(new command.Command(command.Name.IS_ELEMENT_SELECTED))
2655
+ }
2656
+
2657
+ /**
2658
+ * Submits the form containing this element (or this element if it is itself
2659
+ * a FORM element). his command is a no-op if the element is not contained in
2660
+ * a form.
2661
+ *
2662
+ * @return {!Promise<void>} A promise that will be resolved
2663
+ * when the form has been submitted.
2664
+ */
2665
+ submit() {
2666
+ return this.execute_(new command.Command(command.Name.SUBMIT_ELEMENT))
2667
+ }
2668
+
2669
+ /**
2670
+ * Clear the `value` of this element. This command has no effect if the
2671
+ * underlying DOM element is neither a text INPUT element nor a TEXTAREA
2672
+ * element.
2673
+ *
2674
+ * @return {!Promise<void>} A promise that will be resolved
2675
+ * when the element has been cleared.
2676
+ */
2677
+ clear() {
2678
+ return this.execute_(new command.Command(command.Name.CLEAR_ELEMENT))
2679
+ }
2680
+
2681
+ /**
2682
+ * Test whether this element is currently displayed.
2683
+ *
2684
+ * @return {!Promise<boolean>} A promise that will be
2685
+ * resolved with whether this element is currently visible on the page.
2686
+ */
2687
+ isDisplayed() {
2688
+ return this.execute_(new command.Command(command.Name.IS_ELEMENT_DISPLAYED))
2689
+ }
2690
+
2691
+ /**
2692
+ * Take a screenshot of the visible region encompassed by this element's
2693
+ * bounding rectangle.
2694
+ *
2695
+ * @param {boolean=} scroll Optional argument that indicates whether the
2696
+ * element should be scrolled into view before taking a screenshot.
2697
+ * Defaults to false.
2698
+ * @return {!Promise<string>} A promise that will be
2699
+ * resolved to the screenshot as a base-64 encoded PNG.
2700
+ */
2701
+ takeScreenshot(scroll = false) {
2702
+ return this.execute_(
2703
+ new command.Command(command.Name.TAKE_ELEMENT_SCREENSHOT).setParameter(
2704
+ 'scroll',
2705
+ scroll
2706
+ )
2707
+ )
2708
+ }
2709
+ }
2710
+
2711
+ /**
2712
+ * WebElementPromise is a promise that will be fulfilled with a WebElement.
2713
+ * This serves as a forward proxy on WebElement, allowing calls to be
2714
+ * scheduled without directly on this instance before the underlying
2715
+ * WebElement has been fulfilled. In other words, the following two statements
2716
+ * are equivalent:
2717
+ *
2718
+ * driver.findElement({id: 'my-button'}).click();
2719
+ * driver.findElement({id: 'my-button'}).then(function(el) {
2720
+ * return el.click();
2721
+ * });
2722
+ *
2723
+ * @implements {IThenable<!WebElement>}
2724
+ * @final
2725
+ */
2726
+ class WebElementPromise extends WebElement {
2727
+ /**
2728
+ * @param {!WebDriver} driver The parent WebDriver instance for this
2729
+ * element.
2730
+ * @param {!Promise<!WebElement>} el A promise
2731
+ * that will resolve to the promised element.
2732
+ */
2733
+ constructor(driver, el) {
2734
+ super(driver, 'unused')
2735
+
2736
+ /** @override */
2737
+ this.then = el.then.bind(el)
2738
+
2739
+ /** @override */
2740
+ this.catch = el.catch.bind(el)
2741
+
2742
+ /**
2743
+ * Defers returning the element ID until the wrapped WebElement has been
2744
+ * resolved.
2745
+ * @override
2746
+ */
2747
+ this.getId = function () {
2748
+ return el.then(function (el) {
2749
+ return el.getId()
2750
+ })
2751
+ }
2752
+ }
2753
+ }
2754
+
2755
+ //////////////////////////////////////////////////////////////////////////////
2756
+ //
2757
+ // Alert
2758
+ //
2759
+ //////////////////////////////////////////////////////////////////////////////
2760
+
2761
+ /**
2762
+ * Represents a modal dialog such as {@code alert}, {@code confirm}, or
2763
+ * {@code prompt}. Provides functions to retrieve the message displayed with
2764
+ * the alert, accept or dismiss the alert, and set the response text (in the
2765
+ * case of {@code prompt}).
2766
+ */
2767
+ class Alert {
2768
+ /**
2769
+ * @param {!WebDriver} driver The driver controlling the browser this alert
2770
+ * is attached to.
2771
+ * @param {string} text The message text displayed with this alert.
2772
+ */
2773
+ constructor(driver, text) {
2774
+ /** @private {!WebDriver} */
2775
+ this.driver_ = driver
2776
+
2777
+ /** @private {!Promise<string>} */
2778
+ this.text_ = Promise.resolve(text)
2779
+ }
2780
+
2781
+ /**
2782
+ * Retrieves the message text displayed with this alert. For instance, if the
2783
+ * alert were opened with alert("hello"), then this would return "hello".
2784
+ *
2785
+ * @return {!Promise<string>} A promise that will be
2786
+ * resolved to the text displayed with this alert.
2787
+ */
2788
+ getText() {
2789
+ return this.text_
2790
+ }
2791
+
2792
+ /**
2793
+ * Accepts this alert.
2794
+ *
2795
+ * @return {!Promise<void>} A promise that will be resolved
2796
+ * when this command has completed.
2797
+ */
2798
+ accept() {
2799
+ return this.driver_.execute(new command.Command(command.Name.ACCEPT_ALERT))
2800
+ }
2801
+
2802
+ /**
2803
+ * Dismisses this alert.
2804
+ *
2805
+ * @return {!Promise<void>} A promise that will be resolved
2806
+ * when this command has completed.
2807
+ */
2808
+ dismiss() {
2809
+ return this.driver_.execute(new command.Command(command.Name.DISMISS_ALERT))
2810
+ }
2811
+
2812
+ /**
2813
+ * Sets the response text on this alert. This command will return an error if
2814
+ * the underlying alert does not support response text (e.g. window.alert and
2815
+ * window.confirm).
2816
+ *
2817
+ * @param {string} text The text to set.
2818
+ * @return {!Promise<void>} A promise that will be resolved
2819
+ * when this command has completed.
2820
+ */
2821
+ sendKeys(text) {
2822
+ return this.driver_.execute(
2823
+ new command.Command(command.Name.SET_ALERT_TEXT).setParameter(
2824
+ 'text',
2825
+ text
2826
+ )
2827
+ )
2828
+ }
2829
+ }
2830
+
2831
+ /**
2832
+ * AlertPromise is a promise that will be fulfilled with an Alert. This promise
2833
+ * serves as a forward proxy on an Alert, allowing calls to be scheduled
2834
+ * directly on this instance before the underlying Alert has been fulfilled. In
2835
+ * other words, the following two statements are equivalent:
2836
+ *
2837
+ * driver.switchTo().alert().dismiss();
2838
+ * driver.switchTo().alert().then(function(alert) {
2839
+ * return alert.dismiss();
2840
+ * });
2841
+ *
2842
+ * @implements {IThenable<!Alert>}
2843
+ * @final
2844
+ */
2845
+ class AlertPromise extends Alert {
2846
+ /**
2847
+ * @param {!WebDriver} driver The driver controlling the browser this
2848
+ * alert is attached to.
2849
+ * @param {!Promise<!Alert>} alert A thenable
2850
+ * that will be fulfilled with the promised alert.
2851
+ */
2852
+ constructor(driver, alert) {
2853
+ super(driver, 'unused')
2854
+
2855
+ /** @override */
2856
+ this.then = alert.then.bind(alert)
2857
+
2858
+ /** @override */
2859
+ this.catch = alert.catch.bind(alert)
2860
+
2861
+ /**
2862
+ * Defer returning text until the promised alert has been resolved.
2863
+ * @override
2864
+ */
2865
+ this.getText = function () {
2866
+ return alert.then(function (alert) {
2867
+ return alert.getText()
2868
+ })
2869
+ }
2870
+
2871
+ /**
2872
+ * Defers action until the alert has been located.
2873
+ * @override
2874
+ */
2875
+ this.accept = function () {
2876
+ return alert.then(function (alert) {
2877
+ return alert.accept()
2878
+ })
2879
+ }
2880
+
2881
+ /**
2882
+ * Defers action until the alert has been located.
2883
+ * @override
2884
+ */
2885
+ this.dismiss = function () {
2886
+ return alert.then(function (alert) {
2887
+ return alert.dismiss()
2888
+ })
2889
+ }
2890
+
2891
+ /**
2892
+ * Defers action until the alert has been located.
2893
+ * @override
2894
+ */
2895
+ this.sendKeys = function (text) {
2896
+ return alert.then(function (alert) {
2897
+ return alert.sendKeys(text)
2898
+ })
2899
+ }
2900
+ }
2901
+ }
2902
+
2903
+ // PUBLIC API
2904
+
2905
+ module.exports = {
2906
+ Alert,
2907
+ AlertPromise,
2908
+ Condition,
2909
+ Logs,
2910
+ Navigation,
2911
+ Options,
2912
+ TargetLocator,
2913
+ IWebDriver,
2914
+ WebDriver,
2915
+ WebElement,
2916
+ WebElementCondition,
2917
+ WebElementPromise,
2918
+ Window,
2919
+ }