UrgentcareCLI 0.1.1 → 0.2.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (318) hide show
  1. checksums.yaml +4 -4
  2. data/lib/UrgentCare/CLI.rb +57 -22
  3. data/lib/UrgentCare/Office.rb +1 -5
  4. data/lib/UrgentCare/Scraper.rb +71 -0
  5. data/lib/UrgentCare/version.rb +2 -2
  6. data/lib/UrgentCare.rb +2 -1
  7. metadata +27 -321
  8. data/.gitignore +0 -11
  9. data/.rspec +0 -3
  10. data/.travis.yml +0 -5
  11. data/CODE_OF_CONDUCT.md +0 -74
  12. data/Gemfile +0 -12
  13. data/Gemfile.lock +0 -84
  14. data/LICENSE.txt +0 -21
  15. data/Notes +0 -41
  16. data/README.md +0 -41
  17. data/Rakefile +0 -8
  18. data/UrgentCare.gemspec +0 -39
  19. data/background.jpg +0 -0
  20. data/data.txt +0 -4
  21. data/lib/Urgentcare/Scraper.rb +0 -78
  22. data/node_modules/.bin/rimraf +0 -1
  23. data/node_modules/.package-lock.json +0 -250
  24. data/node_modules/balanced-match/.github/FUNDING.yml +0 -2
  25. data/node_modules/balanced-match/LICENSE.md +0 -21
  26. data/node_modules/balanced-match/README.md +0 -97
  27. data/node_modules/balanced-match/index.js +0 -62
  28. data/node_modules/balanced-match/package.json +0 -48
  29. data/node_modules/brace-expansion/LICENSE +0 -21
  30. data/node_modules/brace-expansion/README.md +0 -129
  31. data/node_modules/brace-expansion/index.js +0 -201
  32. data/node_modules/brace-expansion/package.json +0 -47
  33. data/node_modules/concat-map/.travis.yml +0 -4
  34. data/node_modules/concat-map/LICENSE +0 -18
  35. data/node_modules/concat-map/README.markdown +0 -62
  36. data/node_modules/concat-map/example/map.js +0 -6
  37. data/node_modules/concat-map/index.js +0 -13
  38. data/node_modules/concat-map/package.json +0 -43
  39. data/node_modules/concat-map/test/map.js +0 -39
  40. data/node_modules/core-util-is/LICENSE +0 -19
  41. data/node_modules/core-util-is/README.md +0 -3
  42. data/node_modules/core-util-is/float.patch +0 -604
  43. data/node_modules/core-util-is/lib/util.js +0 -107
  44. data/node_modules/core-util-is/package.json +0 -32
  45. data/node_modules/core-util-is/test.js +0 -68
  46. data/node_modules/fs.realpath/LICENSE +0 -43
  47. data/node_modules/fs.realpath/README.md +0 -33
  48. data/node_modules/fs.realpath/index.js +0 -66
  49. data/node_modules/fs.realpath/old.js +0 -303
  50. data/node_modules/fs.realpath/package.json +0 -26
  51. data/node_modules/glob/LICENSE +0 -21
  52. data/node_modules/glob/README.md +0 -375
  53. data/node_modules/glob/changelog.md +0 -67
  54. data/node_modules/glob/common.js +0 -234
  55. data/node_modules/glob/glob.js +0 -788
  56. data/node_modules/glob/package.json +0 -51
  57. data/node_modules/glob/sync.js +0 -484
  58. data/node_modules/immediate/LICENSE.txt +0 -20
  59. data/node_modules/immediate/README.md +0 -93
  60. data/node_modules/immediate/dist/immediate.js +0 -75
  61. data/node_modules/immediate/dist/immediate.min.js +0 -1
  62. data/node_modules/immediate/lib/browser.js +0 -69
  63. data/node_modules/immediate/lib/index.js +0 -73
  64. data/node_modules/immediate/package.json +0 -42
  65. data/node_modules/inflight/LICENSE +0 -15
  66. data/node_modules/inflight/README.md +0 -37
  67. data/node_modules/inflight/inflight.js +0 -54
  68. data/node_modules/inflight/package.json +0 -29
  69. data/node_modules/inherits/LICENSE +0 -16
  70. data/node_modules/inherits/README.md +0 -42
  71. data/node_modules/inherits/inherits.js +0 -9
  72. data/node_modules/inherits/inherits_browser.js +0 -27
  73. data/node_modules/inherits/package.json +0 -29
  74. data/node_modules/isarray/.npmignore +0 -1
  75. data/node_modules/isarray/.travis.yml +0 -4
  76. data/node_modules/isarray/Makefile +0 -6
  77. data/node_modules/isarray/README.md +0 -60
  78. data/node_modules/isarray/component.json +0 -19
  79. data/node_modules/isarray/index.js +0 -5
  80. data/node_modules/isarray/package.json +0 -45
  81. data/node_modules/isarray/test.js +0 -20
  82. data/node_modules/jszip/.codeclimate.yml +0 -16
  83. data/node_modules/jszip/.editorconfig +0 -8
  84. data/node_modules/jszip/.jshintignore +0 -1
  85. data/node_modules/jszip/.jshintrc +0 -21
  86. data/node_modules/jszip/.travis.yml +0 -17
  87. data/node_modules/jszip/CHANGES.md +0 -163
  88. data/node_modules/jszip/LICENSE.markdown +0 -651
  89. data/node_modules/jszip/README.markdown +0 -35
  90. data/node_modules/jszip/dist/jszip.js +0 -30
  91. data/node_modules/jszip/dist/jszip.min.js +0 -13
  92. data/node_modules/jszip/index.d.ts +0 -270
  93. data/node_modules/jszip/lib/base64.js +0 -106
  94. data/node_modules/jszip/lib/compressedObject.js +0 -74
  95. data/node_modules/jszip/lib/compressions.js +0 -14
  96. data/node_modules/jszip/lib/crc32.js +0 -77
  97. data/node_modules/jszip/lib/defaults.js +0 -11
  98. data/node_modules/jszip/lib/external.js +0 -19
  99. data/node_modules/jszip/lib/flate.js +0 -85
  100. data/node_modules/jszip/lib/generate/ZipFileWorker.js +0 -540
  101. data/node_modules/jszip/lib/generate/index.js +0 -57
  102. data/node_modules/jszip/lib/index.js +0 -52
  103. data/node_modules/jszip/lib/license_header.js +0 -11
  104. data/node_modules/jszip/lib/load.js +0 -81
  105. data/node_modules/jszip/lib/nodejs/NodejsStreamInputAdapter.js +0 -74
  106. data/node_modules/jszip/lib/nodejs/NodejsStreamOutputAdapter.js +0 -42
  107. data/node_modules/jszip/lib/nodejsUtils.js +0 -57
  108. data/node_modules/jszip/lib/object.js +0 -389
  109. data/node_modules/jszip/lib/readable-stream-browser.js +0 -9
  110. data/node_modules/jszip/lib/reader/ArrayReader.js +0 -57
  111. data/node_modules/jszip/lib/reader/DataReader.js +0 -116
  112. data/node_modules/jszip/lib/reader/NodeBufferReader.js +0 -19
  113. data/node_modules/jszip/lib/reader/StringReader.js +0 -38
  114. data/node_modules/jszip/lib/reader/Uint8ArrayReader.js +0 -22
  115. data/node_modules/jszip/lib/reader/readerFor.js +0 -28
  116. data/node_modules/jszip/lib/signature.js +0 -7
  117. data/node_modules/jszip/lib/stream/ConvertWorker.js +0 -26
  118. data/node_modules/jszip/lib/stream/Crc32Probe.js +0 -24
  119. data/node_modules/jszip/lib/stream/DataLengthProbe.js +0 -29
  120. data/node_modules/jszip/lib/stream/DataWorker.js +0 -116
  121. data/node_modules/jszip/lib/stream/GenericWorker.js +0 -263
  122. data/node_modules/jszip/lib/stream/StreamHelper.js +0 -212
  123. data/node_modules/jszip/lib/support.js +0 -38
  124. data/node_modules/jszip/lib/utf8.js +0 -275
  125. data/node_modules/jszip/lib/utils.js +0 -476
  126. data/node_modules/jszip/lib/zipEntries.js +0 -262
  127. data/node_modules/jszip/lib/zipEntry.js +0 -294
  128. data/node_modules/jszip/lib/zipObject.js +0 -133
  129. data/node_modules/jszip/package.json +0 -63
  130. data/node_modules/jszip/vendor/FileSaver.js +0 -247
  131. data/node_modules/lie/README.md +0 -62
  132. data/node_modules/lie/dist/lie.js +0 -350
  133. data/node_modules/lie/dist/lie.min.js +0 -1
  134. data/node_modules/lie/dist/lie.polyfill.js +0 -358
  135. data/node_modules/lie/dist/lie.polyfill.min.js +0 -1
  136. data/node_modules/lie/lib/browser.js +0 -273
  137. data/node_modules/lie/lib/index.js +0 -298
  138. data/node_modules/lie/license.md +0 -7
  139. data/node_modules/lie/lie.d.ts +0 -244
  140. data/node_modules/lie/package.json +0 -69
  141. data/node_modules/lie/polyfill.js +0 -4
  142. data/node_modules/minimatch/LICENSE +0 -15
  143. data/node_modules/minimatch/README.md +0 -209
  144. data/node_modules/minimatch/minimatch.js +0 -923
  145. data/node_modules/minimatch/package.json +0 -30
  146. data/node_modules/once/LICENSE +0 -15
  147. data/node_modules/once/README.md +0 -79
  148. data/node_modules/once/once.js +0 -42
  149. data/node_modules/once/package.json +0 -33
  150. data/node_modules/pako/CHANGELOG.md +0 -164
  151. data/node_modules/pako/LICENSE +0 -21
  152. data/node_modules/pako/README.md +0 -191
  153. data/node_modules/pako/dist/pako.js +0 -6818
  154. data/node_modules/pako/dist/pako.min.js +0 -1
  155. data/node_modules/pako/dist/pako_deflate.js +0 -3997
  156. data/node_modules/pako/dist/pako_deflate.min.js +0 -1
  157. data/node_modules/pako/dist/pako_inflate.js +0 -3300
  158. data/node_modules/pako/dist/pako_inflate.min.js +0 -1
  159. data/node_modules/pako/index.js +0 -14
  160. data/node_modules/pako/lib/deflate.js +0 -400
  161. data/node_modules/pako/lib/inflate.js +0 -423
  162. data/node_modules/pako/lib/utils/common.js +0 -105
  163. data/node_modules/pako/lib/utils/strings.js +0 -187
  164. data/node_modules/pako/lib/zlib/README +0 -59
  165. data/node_modules/pako/lib/zlib/adler32.js +0 -51
  166. data/node_modules/pako/lib/zlib/constants.js +0 -68
  167. data/node_modules/pako/lib/zlib/crc32.js +0 -59
  168. data/node_modules/pako/lib/zlib/deflate.js +0 -1874
  169. data/node_modules/pako/lib/zlib/gzheader.js +0 -58
  170. data/node_modules/pako/lib/zlib/inffast.js +0 -345
  171. data/node_modules/pako/lib/zlib/inflate.js +0 -1556
  172. data/node_modules/pako/lib/zlib/inftrees.js +0 -343
  173. data/node_modules/pako/lib/zlib/messages.js +0 -32
  174. data/node_modules/pako/lib/zlib/trees.js +0 -1222
  175. data/node_modules/pako/lib/zlib/zstream.js +0 -47
  176. data/node_modules/pako/package.json +0 -44
  177. data/node_modules/path-is-absolute/index.js +0 -20
  178. data/node_modules/path-is-absolute/license +0 -21
  179. data/node_modules/path-is-absolute/package.json +0 -43
  180. data/node_modules/path-is-absolute/readme.md +0 -59
  181. data/node_modules/process-nextick-args/index.js +0 -45
  182. data/node_modules/process-nextick-args/license.md +0 -19
  183. data/node_modules/process-nextick-args/package.json +0 -25
  184. data/node_modules/process-nextick-args/readme.md +0 -18
  185. data/node_modules/readable-stream/.travis.yml +0 -34
  186. data/node_modules/readable-stream/CONTRIBUTING.md +0 -38
  187. data/node_modules/readable-stream/GOVERNANCE.md +0 -136
  188. data/node_modules/readable-stream/LICENSE +0 -47
  189. data/node_modules/readable-stream/README.md +0 -58
  190. data/node_modules/readable-stream/doc/wg-meetings/2015-01-30.md +0 -60
  191. data/node_modules/readable-stream/duplex-browser.js +0 -1
  192. data/node_modules/readable-stream/duplex.js +0 -1
  193. data/node_modules/readable-stream/lib/_stream_duplex.js +0 -131
  194. data/node_modules/readable-stream/lib/_stream_passthrough.js +0 -47
  195. data/node_modules/readable-stream/lib/_stream_readable.js +0 -1019
  196. data/node_modules/readable-stream/lib/_stream_transform.js +0 -214
  197. data/node_modules/readable-stream/lib/_stream_writable.js +0 -687
  198. data/node_modules/readable-stream/lib/internal/streams/BufferList.js +0 -79
  199. data/node_modules/readable-stream/lib/internal/streams/destroy.js +0 -74
  200. data/node_modules/readable-stream/lib/internal/streams/stream-browser.js +0 -1
  201. data/node_modules/readable-stream/lib/internal/streams/stream.js +0 -1
  202. data/node_modules/readable-stream/package.json +0 -52
  203. data/node_modules/readable-stream/passthrough.js +0 -1
  204. data/node_modules/readable-stream/readable-browser.js +0 -7
  205. data/node_modules/readable-stream/readable.js +0 -19
  206. data/node_modules/readable-stream/transform.js +0 -1
  207. data/node_modules/readable-stream/writable-browser.js +0 -1
  208. data/node_modules/readable-stream/writable.js +0 -8
  209. data/node_modules/rimraf/LICENSE +0 -15
  210. data/node_modules/rimraf/README.md +0 -101
  211. data/node_modules/rimraf/bin.js +0 -50
  212. data/node_modules/rimraf/package.json +0 -29
  213. data/node_modules/rimraf/rimraf.js +0 -372
  214. data/node_modules/safe-buffer/LICENSE +0 -21
  215. data/node_modules/safe-buffer/README.md +0 -584
  216. data/node_modules/safe-buffer/index.d.ts +0 -187
  217. data/node_modules/safe-buffer/index.js +0 -62
  218. data/node_modules/safe-buffer/package.json +0 -37
  219. data/node_modules/selenium-webdriver/CHANGES.md +0 -1114
  220. data/node_modules/selenium-webdriver/LICENSE +0 -202
  221. data/node_modules/selenium-webdriver/NOTICE +0 -2
  222. data/node_modules/selenium-webdriver/README.md +0 -229
  223. data/node_modules/selenium-webdriver/chrome.js +0 -295
  224. data/node_modules/selenium-webdriver/chromium.js +0 -829
  225. data/node_modules/selenium-webdriver/devtools/CDPConnection.js +0 -35
  226. data/node_modules/selenium-webdriver/edge.js +0 -224
  227. data/node_modules/selenium-webdriver/example/chrome_android.js +0 -45
  228. data/node_modules/selenium-webdriver/example/chrome_mobile_emulation.js +0 -46
  229. data/node_modules/selenium-webdriver/example/firefox_channels.js +0 -84
  230. data/node_modules/selenium-webdriver/example/google_search.js +0 -50
  231. data/node_modules/selenium-webdriver/example/google_search_test.js +0 -70
  232. data/node_modules/selenium-webdriver/example/headless.js +0 -63
  233. data/node_modules/selenium-webdriver/example/logging.js +0 -64
  234. data/node_modules/selenium-webdriver/firefox.js +0 -789
  235. data/node_modules/selenium-webdriver/http/index.js +0 -324
  236. data/node_modules/selenium-webdriver/http/util.js +0 -172
  237. data/node_modules/selenium-webdriver/ie.js +0 -503
  238. data/node_modules/selenium-webdriver/index.js +0 -825
  239. data/node_modules/selenium-webdriver/io/exec.js +0 -162
  240. data/node_modules/selenium-webdriver/io/index.js +0 -348
  241. data/node_modules/selenium-webdriver/io/zip.js +0 -223
  242. data/node_modules/selenium-webdriver/lib/atoms/find-elements.js +0 -123
  243. data/node_modules/selenium-webdriver/lib/atoms/get-attribute.js +0 -101
  244. data/node_modules/selenium-webdriver/lib/atoms/is-displayed.js +0 -101
  245. data/node_modules/selenium-webdriver/lib/atoms/mutation-listener.js +0 -55
  246. data/node_modules/selenium-webdriver/lib/by.js +0 -415
  247. data/node_modules/selenium-webdriver/lib/capabilities.js +0 -553
  248. data/node_modules/selenium-webdriver/lib/command.js +0 -206
  249. data/node_modules/selenium-webdriver/lib/error.js +0 -605
  250. data/node_modules/selenium-webdriver/lib/http.js +0 -704
  251. data/node_modules/selenium-webdriver/lib/input.js +0 -946
  252. data/node_modules/selenium-webdriver/lib/logging.js +0 -661
  253. data/node_modules/selenium-webdriver/lib/promise.js +0 -285
  254. data/node_modules/selenium-webdriver/lib/proxy.js +0 -212
  255. data/node_modules/selenium-webdriver/lib/session.js +0 -77
  256. data/node_modules/selenium-webdriver/lib/symbols.js +0 -37
  257. data/node_modules/selenium-webdriver/lib/until.js +0 -429
  258. data/node_modules/selenium-webdriver/lib/webdriver.js +0 -2919
  259. data/node_modules/selenium-webdriver/net/index.js +0 -107
  260. data/node_modules/selenium-webdriver/net/portprober.js +0 -75
  261. data/node_modules/selenium-webdriver/opera.js +0 -406
  262. data/node_modules/selenium-webdriver/package.json +0 -54
  263. data/node_modules/selenium-webdriver/proxy.js +0 -32
  264. data/node_modules/selenium-webdriver/remote/index.js +0 -624
  265. data/node_modules/selenium-webdriver/safari.js +0 -168
  266. data/node_modules/selenium-webdriver/testing/index.js +0 -504
  267. data/node_modules/set-immediate-shim/index.js +0 -7
  268. data/node_modules/set-immediate-shim/package.json +0 -34
  269. data/node_modules/set-immediate-shim/readme.md +0 -31
  270. data/node_modules/string_decoder/.travis.yml +0 -50
  271. data/node_modules/string_decoder/LICENSE +0 -48
  272. data/node_modules/string_decoder/README.md +0 -47
  273. data/node_modules/string_decoder/lib/string_decoder.js +0 -296
  274. data/node_modules/string_decoder/package.json +0 -31
  275. data/node_modules/tmp/CHANGELOG.md +0 -288
  276. data/node_modules/tmp/LICENSE +0 -21
  277. data/node_modules/tmp/README.md +0 -365
  278. data/node_modules/tmp/lib/tmp.js +0 -780
  279. data/node_modules/tmp/node_modules/.bin/rimraf +0 -1
  280. data/node_modules/tmp/node_modules/rimraf/CHANGELOG.md +0 -65
  281. data/node_modules/tmp/node_modules/rimraf/LICENSE +0 -15
  282. data/node_modules/tmp/node_modules/rimraf/README.md +0 -101
  283. data/node_modules/tmp/node_modules/rimraf/bin.js +0 -68
  284. data/node_modules/tmp/node_modules/rimraf/package.json +0 -32
  285. data/node_modules/tmp/node_modules/rimraf/rimraf.js +0 -360
  286. data/node_modules/tmp/package.json +0 -58
  287. data/node_modules/util-deprecate/History.md +0 -16
  288. data/node_modules/util-deprecate/LICENSE +0 -24
  289. data/node_modules/util-deprecate/README.md +0 -53
  290. data/node_modules/util-deprecate/browser.js +0 -67
  291. data/node_modules/util-deprecate/node.js +0 -6
  292. data/node_modules/util-deprecate/package.json +0 -27
  293. data/node_modules/wrappy/LICENSE +0 -15
  294. data/node_modules/wrappy/README.md +0 -36
  295. data/node_modules/wrappy/package.json +0 -29
  296. data/node_modules/wrappy/wrappy.js +0 -33
  297. data/node_modules/ws/LICENSE +0 -21
  298. data/node_modules/ws/README.md +0 -496
  299. data/node_modules/ws/browser.js +0 -8
  300. data/node_modules/ws/index.js +0 -10
  301. data/node_modules/ws/lib/buffer-util.js +0 -129
  302. data/node_modules/ws/lib/constants.js +0 -10
  303. data/node_modules/ws/lib/event-target.js +0 -184
  304. data/node_modules/ws/lib/extension.js +0 -223
  305. data/node_modules/ws/lib/limiter.js +0 -55
  306. data/node_modules/ws/lib/permessage-deflate.js +0 -517
  307. data/node_modules/ws/lib/receiver.js +0 -507
  308. data/node_modules/ws/lib/sender.js +0 -405
  309. data/node_modules/ws/lib/stream.js +0 -165
  310. data/node_modules/ws/lib/validation.js +0 -104
  311. data/node_modules/ws/lib/websocket-server.js +0 -418
  312. data/node_modules/ws/lib/websocket.js +0 -942
  313. data/node_modules/ws/package.json +0 -56
  314. data/package-lock.json +0 -458
  315. data/package.json +0 -5
  316. data/selenium.log +0 -1
  317. data/spec.md +0 -6
  318. data/test.data +0 -2110
@@ -1,2919 +0,0 @@
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
- }