UrgentcareCLI 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (308) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +3 -0
  3. data/Gemfile.lock +60 -25
  4. data/Notes +1 -1
  5. data/README.md +5 -5
  6. data/Rakefile +2 -0
  7. data/UrgentCare.gemspec +3 -3
  8. data/background.jpg +0 -0
  9. data/lib/UrgentCare.rb +3 -0
  10. data/lib/UrgentCare/CLI.rb +6 -2
  11. data/lib/UrgentCare/version.rb +1 -1
  12. data/lib/Urgentcare/Scraper.rb +65 -23
  13. data/node_modules/.bin/rimraf +1 -0
  14. data/node_modules/.package-lock.json +250 -0
  15. data/node_modules/balanced-match/.github/FUNDING.yml +2 -0
  16. data/node_modules/balanced-match/LICENSE.md +21 -0
  17. data/node_modules/balanced-match/README.md +97 -0
  18. data/node_modules/balanced-match/index.js +62 -0
  19. data/node_modules/balanced-match/package.json +48 -0
  20. data/node_modules/brace-expansion/LICENSE +21 -0
  21. data/node_modules/brace-expansion/README.md +129 -0
  22. data/node_modules/brace-expansion/index.js +201 -0
  23. data/node_modules/brace-expansion/package.json +47 -0
  24. data/node_modules/concat-map/.travis.yml +4 -0
  25. data/node_modules/concat-map/LICENSE +18 -0
  26. data/node_modules/concat-map/README.markdown +62 -0
  27. data/node_modules/concat-map/example/map.js +6 -0
  28. data/node_modules/concat-map/index.js +13 -0
  29. data/node_modules/concat-map/package.json +43 -0
  30. data/node_modules/concat-map/test/map.js +39 -0
  31. data/node_modules/core-util-is/LICENSE +19 -0
  32. data/node_modules/core-util-is/README.md +3 -0
  33. data/node_modules/core-util-is/float.patch +604 -0
  34. data/node_modules/core-util-is/lib/util.js +107 -0
  35. data/node_modules/core-util-is/package.json +32 -0
  36. data/node_modules/core-util-is/test.js +68 -0
  37. data/node_modules/fs.realpath/LICENSE +43 -0
  38. data/node_modules/fs.realpath/README.md +33 -0
  39. data/node_modules/fs.realpath/index.js +66 -0
  40. data/node_modules/fs.realpath/old.js +303 -0
  41. data/node_modules/fs.realpath/package.json +26 -0
  42. data/node_modules/glob/LICENSE +21 -0
  43. data/node_modules/glob/README.md +375 -0
  44. data/node_modules/glob/changelog.md +67 -0
  45. data/node_modules/glob/common.js +234 -0
  46. data/node_modules/glob/glob.js +788 -0
  47. data/node_modules/glob/package.json +51 -0
  48. data/node_modules/glob/sync.js +484 -0
  49. data/node_modules/immediate/LICENSE.txt +20 -0
  50. data/node_modules/immediate/README.md +93 -0
  51. data/node_modules/immediate/dist/immediate.js +75 -0
  52. data/node_modules/immediate/dist/immediate.min.js +1 -0
  53. data/node_modules/immediate/lib/browser.js +69 -0
  54. data/node_modules/immediate/lib/index.js +73 -0
  55. data/node_modules/immediate/package.json +42 -0
  56. data/node_modules/inflight/LICENSE +15 -0
  57. data/node_modules/inflight/README.md +37 -0
  58. data/node_modules/inflight/inflight.js +54 -0
  59. data/node_modules/inflight/package.json +29 -0
  60. data/node_modules/inherits/LICENSE +16 -0
  61. data/node_modules/inherits/README.md +42 -0
  62. data/node_modules/inherits/inherits.js +9 -0
  63. data/node_modules/inherits/inherits_browser.js +27 -0
  64. data/node_modules/inherits/package.json +29 -0
  65. data/node_modules/isarray/.npmignore +1 -0
  66. data/node_modules/isarray/.travis.yml +4 -0
  67. data/node_modules/isarray/Makefile +6 -0
  68. data/node_modules/isarray/README.md +60 -0
  69. data/node_modules/isarray/component.json +19 -0
  70. data/node_modules/isarray/index.js +5 -0
  71. data/node_modules/isarray/package.json +45 -0
  72. data/node_modules/isarray/test.js +20 -0
  73. data/node_modules/jszip/.codeclimate.yml +16 -0
  74. data/node_modules/jszip/.editorconfig +8 -0
  75. data/node_modules/jszip/.jshintignore +1 -0
  76. data/node_modules/jszip/.jshintrc +21 -0
  77. data/node_modules/jszip/.travis.yml +17 -0
  78. data/node_modules/jszip/CHANGES.md +163 -0
  79. data/node_modules/jszip/LICENSE.markdown +651 -0
  80. data/node_modules/jszip/README.markdown +35 -0
  81. data/node_modules/jszip/dist/jszip.js +30 -0
  82. data/node_modules/jszip/dist/jszip.min.js +13 -0
  83. data/node_modules/jszip/index.d.ts +270 -0
  84. data/node_modules/jszip/lib/base64.js +106 -0
  85. data/node_modules/jszip/lib/compressedObject.js +74 -0
  86. data/node_modules/jszip/lib/compressions.js +14 -0
  87. data/node_modules/jszip/lib/crc32.js +77 -0
  88. data/node_modules/jszip/lib/defaults.js +11 -0
  89. data/node_modules/jszip/lib/external.js +19 -0
  90. data/node_modules/jszip/lib/flate.js +85 -0
  91. data/node_modules/jszip/lib/generate/ZipFileWorker.js +540 -0
  92. data/node_modules/jszip/lib/generate/index.js +57 -0
  93. data/node_modules/jszip/lib/index.js +52 -0
  94. data/node_modules/jszip/lib/license_header.js +11 -0
  95. data/node_modules/jszip/lib/load.js +81 -0
  96. data/node_modules/jszip/lib/nodejs/NodejsStreamInputAdapter.js +74 -0
  97. data/node_modules/jszip/lib/nodejs/NodejsStreamOutputAdapter.js +42 -0
  98. data/node_modules/jszip/lib/nodejsUtils.js +57 -0
  99. data/node_modules/jszip/lib/object.js +389 -0
  100. data/node_modules/jszip/lib/readable-stream-browser.js +9 -0
  101. data/node_modules/jszip/lib/reader/ArrayReader.js +57 -0
  102. data/node_modules/jszip/lib/reader/DataReader.js +116 -0
  103. data/node_modules/jszip/lib/reader/NodeBufferReader.js +19 -0
  104. data/node_modules/jszip/lib/reader/StringReader.js +38 -0
  105. data/node_modules/jszip/lib/reader/Uint8ArrayReader.js +22 -0
  106. data/node_modules/jszip/lib/reader/readerFor.js +28 -0
  107. data/node_modules/jszip/lib/signature.js +7 -0
  108. data/node_modules/jszip/lib/stream/ConvertWorker.js +26 -0
  109. data/node_modules/jszip/lib/stream/Crc32Probe.js +24 -0
  110. data/node_modules/jszip/lib/stream/DataLengthProbe.js +29 -0
  111. data/node_modules/jszip/lib/stream/DataWorker.js +116 -0
  112. data/node_modules/jszip/lib/stream/GenericWorker.js +263 -0
  113. data/node_modules/jszip/lib/stream/StreamHelper.js +212 -0
  114. data/node_modules/jszip/lib/support.js +38 -0
  115. data/node_modules/jszip/lib/utf8.js +275 -0
  116. data/node_modules/jszip/lib/utils.js +476 -0
  117. data/node_modules/jszip/lib/zipEntries.js +262 -0
  118. data/node_modules/jszip/lib/zipEntry.js +294 -0
  119. data/node_modules/jszip/lib/zipObject.js +133 -0
  120. data/node_modules/jszip/package.json +63 -0
  121. data/node_modules/jszip/vendor/FileSaver.js +247 -0
  122. data/node_modules/lie/README.md +62 -0
  123. data/node_modules/lie/dist/lie.js +350 -0
  124. data/node_modules/lie/dist/lie.min.js +1 -0
  125. data/node_modules/lie/dist/lie.polyfill.js +358 -0
  126. data/node_modules/lie/dist/lie.polyfill.min.js +1 -0
  127. data/node_modules/lie/lib/browser.js +273 -0
  128. data/node_modules/lie/lib/index.js +298 -0
  129. data/node_modules/lie/license.md +7 -0
  130. data/node_modules/lie/lie.d.ts +244 -0
  131. data/node_modules/lie/package.json +69 -0
  132. data/node_modules/lie/polyfill.js +4 -0
  133. data/node_modules/minimatch/LICENSE +15 -0
  134. data/node_modules/minimatch/README.md +209 -0
  135. data/node_modules/minimatch/minimatch.js +923 -0
  136. data/node_modules/minimatch/package.json +30 -0
  137. data/node_modules/once/LICENSE +15 -0
  138. data/node_modules/once/README.md +79 -0
  139. data/node_modules/once/once.js +42 -0
  140. data/node_modules/once/package.json +33 -0
  141. data/node_modules/pako/CHANGELOG.md +164 -0
  142. data/node_modules/pako/LICENSE +21 -0
  143. data/node_modules/pako/README.md +191 -0
  144. data/node_modules/pako/dist/pako.js +6818 -0
  145. data/node_modules/pako/dist/pako.min.js +1 -0
  146. data/node_modules/pako/dist/pako_deflate.js +3997 -0
  147. data/node_modules/pako/dist/pako_deflate.min.js +1 -0
  148. data/node_modules/pako/dist/pako_inflate.js +3300 -0
  149. data/node_modules/pako/dist/pako_inflate.min.js +1 -0
  150. data/node_modules/pako/index.js +14 -0
  151. data/node_modules/pako/lib/deflate.js +400 -0
  152. data/node_modules/pako/lib/inflate.js +423 -0
  153. data/node_modules/pako/lib/utils/common.js +105 -0
  154. data/node_modules/pako/lib/utils/strings.js +187 -0
  155. data/node_modules/pako/lib/zlib/README +59 -0
  156. data/node_modules/pako/lib/zlib/adler32.js +51 -0
  157. data/node_modules/pako/lib/zlib/constants.js +68 -0
  158. data/node_modules/pako/lib/zlib/crc32.js +59 -0
  159. data/node_modules/pako/lib/zlib/deflate.js +1874 -0
  160. data/node_modules/pako/lib/zlib/gzheader.js +58 -0
  161. data/node_modules/pako/lib/zlib/inffast.js +345 -0
  162. data/node_modules/pako/lib/zlib/inflate.js +1556 -0
  163. data/node_modules/pako/lib/zlib/inftrees.js +343 -0
  164. data/node_modules/pako/lib/zlib/messages.js +32 -0
  165. data/node_modules/pako/lib/zlib/trees.js +1222 -0
  166. data/node_modules/pako/lib/zlib/zstream.js +47 -0
  167. data/node_modules/pako/package.json +44 -0
  168. data/node_modules/path-is-absolute/index.js +20 -0
  169. data/node_modules/path-is-absolute/license +21 -0
  170. data/node_modules/path-is-absolute/package.json +43 -0
  171. data/node_modules/path-is-absolute/readme.md +59 -0
  172. data/node_modules/process-nextick-args/index.js +45 -0
  173. data/node_modules/process-nextick-args/license.md +19 -0
  174. data/node_modules/process-nextick-args/package.json +25 -0
  175. data/node_modules/process-nextick-args/readme.md +18 -0
  176. data/node_modules/readable-stream/.travis.yml +34 -0
  177. data/node_modules/readable-stream/CONTRIBUTING.md +38 -0
  178. data/node_modules/readable-stream/GOVERNANCE.md +136 -0
  179. data/node_modules/readable-stream/LICENSE +47 -0
  180. data/node_modules/readable-stream/README.md +58 -0
  181. data/node_modules/readable-stream/doc/wg-meetings/2015-01-30.md +60 -0
  182. data/node_modules/readable-stream/duplex-browser.js +1 -0
  183. data/node_modules/readable-stream/duplex.js +1 -0
  184. data/node_modules/readable-stream/lib/_stream_duplex.js +131 -0
  185. data/node_modules/readable-stream/lib/_stream_passthrough.js +47 -0
  186. data/node_modules/readable-stream/lib/_stream_readable.js +1019 -0
  187. data/node_modules/readable-stream/lib/_stream_transform.js +214 -0
  188. data/node_modules/readable-stream/lib/_stream_writable.js +687 -0
  189. data/node_modules/readable-stream/lib/internal/streams/BufferList.js +79 -0
  190. data/node_modules/readable-stream/lib/internal/streams/destroy.js +74 -0
  191. data/node_modules/readable-stream/lib/internal/streams/stream-browser.js +1 -0
  192. data/node_modules/readable-stream/lib/internal/streams/stream.js +1 -0
  193. data/node_modules/readable-stream/package.json +52 -0
  194. data/node_modules/readable-stream/passthrough.js +1 -0
  195. data/node_modules/readable-stream/readable-browser.js +7 -0
  196. data/node_modules/readable-stream/readable.js +19 -0
  197. data/node_modules/readable-stream/transform.js +1 -0
  198. data/node_modules/readable-stream/writable-browser.js +1 -0
  199. data/node_modules/readable-stream/writable.js +8 -0
  200. data/node_modules/rimraf/LICENSE +15 -0
  201. data/node_modules/rimraf/README.md +101 -0
  202. data/node_modules/rimraf/bin.js +50 -0
  203. data/node_modules/rimraf/package.json +29 -0
  204. data/node_modules/rimraf/rimraf.js +372 -0
  205. data/node_modules/safe-buffer/LICENSE +21 -0
  206. data/node_modules/safe-buffer/README.md +584 -0
  207. data/node_modules/safe-buffer/index.d.ts +187 -0
  208. data/node_modules/safe-buffer/index.js +62 -0
  209. data/node_modules/safe-buffer/package.json +37 -0
  210. data/node_modules/selenium-webdriver/CHANGES.md +1114 -0
  211. data/node_modules/selenium-webdriver/LICENSE +202 -0
  212. data/node_modules/selenium-webdriver/NOTICE +2 -0
  213. data/node_modules/selenium-webdriver/README.md +229 -0
  214. data/node_modules/selenium-webdriver/chrome.js +295 -0
  215. data/node_modules/selenium-webdriver/chromium.js +829 -0
  216. data/node_modules/selenium-webdriver/devtools/CDPConnection.js +35 -0
  217. data/node_modules/selenium-webdriver/edge.js +224 -0
  218. data/node_modules/selenium-webdriver/example/chrome_android.js +45 -0
  219. data/node_modules/selenium-webdriver/example/chrome_mobile_emulation.js +46 -0
  220. data/node_modules/selenium-webdriver/example/firefox_channels.js +84 -0
  221. data/node_modules/selenium-webdriver/example/google_search.js +50 -0
  222. data/node_modules/selenium-webdriver/example/google_search_test.js +70 -0
  223. data/node_modules/selenium-webdriver/example/headless.js +63 -0
  224. data/node_modules/selenium-webdriver/example/logging.js +64 -0
  225. data/node_modules/selenium-webdriver/firefox.js +789 -0
  226. data/node_modules/selenium-webdriver/http/index.js +324 -0
  227. data/node_modules/selenium-webdriver/http/util.js +172 -0
  228. data/node_modules/selenium-webdriver/ie.js +503 -0
  229. data/node_modules/selenium-webdriver/index.js +825 -0
  230. data/node_modules/selenium-webdriver/io/exec.js +162 -0
  231. data/node_modules/selenium-webdriver/io/index.js +348 -0
  232. data/node_modules/selenium-webdriver/io/zip.js +223 -0
  233. data/node_modules/selenium-webdriver/lib/atoms/find-elements.js +123 -0
  234. data/node_modules/selenium-webdriver/lib/atoms/get-attribute.js +101 -0
  235. data/node_modules/selenium-webdriver/lib/atoms/is-displayed.js +101 -0
  236. data/node_modules/selenium-webdriver/lib/atoms/mutation-listener.js +55 -0
  237. data/node_modules/selenium-webdriver/lib/by.js +415 -0
  238. data/node_modules/selenium-webdriver/lib/capabilities.js +553 -0
  239. data/node_modules/selenium-webdriver/lib/command.js +206 -0
  240. data/node_modules/selenium-webdriver/lib/error.js +605 -0
  241. data/node_modules/selenium-webdriver/lib/http.js +704 -0
  242. data/node_modules/selenium-webdriver/lib/input.js +946 -0
  243. data/node_modules/selenium-webdriver/lib/logging.js +661 -0
  244. data/node_modules/selenium-webdriver/lib/promise.js +285 -0
  245. data/node_modules/selenium-webdriver/lib/proxy.js +212 -0
  246. data/node_modules/selenium-webdriver/lib/session.js +77 -0
  247. data/node_modules/selenium-webdriver/lib/symbols.js +37 -0
  248. data/node_modules/selenium-webdriver/lib/until.js +429 -0
  249. data/node_modules/selenium-webdriver/lib/webdriver.js +2919 -0
  250. data/node_modules/selenium-webdriver/net/index.js +107 -0
  251. data/node_modules/selenium-webdriver/net/portprober.js +75 -0
  252. data/node_modules/selenium-webdriver/opera.js +406 -0
  253. data/node_modules/selenium-webdriver/package.json +54 -0
  254. data/node_modules/selenium-webdriver/proxy.js +32 -0
  255. data/node_modules/selenium-webdriver/remote/index.js +624 -0
  256. data/node_modules/selenium-webdriver/safari.js +168 -0
  257. data/node_modules/selenium-webdriver/testing/index.js +504 -0
  258. data/node_modules/set-immediate-shim/index.js +7 -0
  259. data/node_modules/set-immediate-shim/package.json +34 -0
  260. data/node_modules/set-immediate-shim/readme.md +31 -0
  261. data/node_modules/string_decoder/.travis.yml +50 -0
  262. data/node_modules/string_decoder/LICENSE +48 -0
  263. data/node_modules/string_decoder/README.md +47 -0
  264. data/node_modules/string_decoder/lib/string_decoder.js +296 -0
  265. data/node_modules/string_decoder/package.json +31 -0
  266. data/node_modules/tmp/CHANGELOG.md +288 -0
  267. data/node_modules/tmp/LICENSE +21 -0
  268. data/node_modules/tmp/README.md +365 -0
  269. data/node_modules/tmp/lib/tmp.js +780 -0
  270. data/node_modules/tmp/node_modules/.bin/rimraf +1 -0
  271. data/node_modules/tmp/node_modules/rimraf/CHANGELOG.md +65 -0
  272. data/node_modules/tmp/node_modules/rimraf/LICENSE +15 -0
  273. data/node_modules/tmp/node_modules/rimraf/README.md +101 -0
  274. data/node_modules/tmp/node_modules/rimraf/bin.js +68 -0
  275. data/node_modules/tmp/node_modules/rimraf/package.json +32 -0
  276. data/node_modules/tmp/node_modules/rimraf/rimraf.js +360 -0
  277. data/node_modules/tmp/package.json +58 -0
  278. data/node_modules/util-deprecate/History.md +16 -0
  279. data/node_modules/util-deprecate/LICENSE +24 -0
  280. data/node_modules/util-deprecate/README.md +53 -0
  281. data/node_modules/util-deprecate/browser.js +67 -0
  282. data/node_modules/util-deprecate/node.js +6 -0
  283. data/node_modules/util-deprecate/package.json +27 -0
  284. data/node_modules/wrappy/LICENSE +15 -0
  285. data/node_modules/wrappy/README.md +36 -0
  286. data/node_modules/wrappy/package.json +29 -0
  287. data/node_modules/wrappy/wrappy.js +33 -0
  288. data/node_modules/ws/LICENSE +21 -0
  289. data/node_modules/ws/README.md +496 -0
  290. data/node_modules/ws/browser.js +8 -0
  291. data/node_modules/ws/index.js +10 -0
  292. data/node_modules/ws/lib/buffer-util.js +129 -0
  293. data/node_modules/ws/lib/constants.js +10 -0
  294. data/node_modules/ws/lib/event-target.js +184 -0
  295. data/node_modules/ws/lib/extension.js +223 -0
  296. data/node_modules/ws/lib/limiter.js +55 -0
  297. data/node_modules/ws/lib/permessage-deflate.js +517 -0
  298. data/node_modules/ws/lib/receiver.js +507 -0
  299. data/node_modules/ws/lib/sender.js +405 -0
  300. data/node_modules/ws/lib/stream.js +165 -0
  301. data/node_modules/ws/lib/validation.js +104 -0
  302. data/node_modules/ws/lib/websocket-server.js +418 -0
  303. data/node_modules/ws/lib/websocket.js +942 -0
  304. data/node_modules/ws/package.json +56 -0
  305. data/package-lock.json +458 -0
  306. data/package.json +5 -0
  307. data/selenium.log +1 -0
  308. metadata +314 -19
@@ -0,0 +1,704 @@
1
+ // Licensed to the Software Freedom Conservancy (SFC) under one
2
+ // or more contributor license agreements. See the NOTICE file
3
+ // distributed with this work for additional information
4
+ // regarding copyright ownership. The SFC licenses this file
5
+ // to you under the Apache License, Version 2.0 (the
6
+ // "License"); you may not use this file except in compliance
7
+ // with the License. You may obtain a copy of the License at
8
+ //
9
+ // http://www.apache.org/licenses/LICENSE-2.0
10
+ //
11
+ // Unless required by applicable law or agreed to in writing,
12
+ // software distributed under the License is distributed on an
13
+ // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ // KIND, either express or implied. See the License for the
15
+ // specific language governing permissions and limitations
16
+ // under the License.
17
+
18
+ /**
19
+ * @fileoverview Defines an environment agnostic {@linkplain cmd.Executor
20
+ * command executor} that communicates with a remote end using JSON over HTTP.
21
+ *
22
+ * Clients should implement the {@link Client} interface, which is used by
23
+ * the {@link Executor} to send commands to the remote end.
24
+ */
25
+
26
+ 'use strict'
27
+
28
+ const path = require('path')
29
+ const cmd = require('./command')
30
+ const error = require('./error')
31
+ const logging = require('./logging')
32
+ const promise = require('./promise')
33
+ const { Session } = require('./session')
34
+ const { WebElement } = require('./webdriver')
35
+
36
+ const getAttribute = requireAtom(
37
+ 'get-attribute.js',
38
+ '//javascript/node/selenium-webdriver/lib/atoms:get-attribute.js'
39
+ )
40
+ const isDisplayed = requireAtom(
41
+ 'is-displayed.js',
42
+ '//javascript/node/selenium-webdriver/lib/atoms:is-displayed.js'
43
+ )
44
+ const findElements = requireAtom(
45
+ 'find-elements.js',
46
+ '//javascript/node/selenium-webdriver/lib/atoms:find-elements.js'
47
+ )
48
+
49
+ /**
50
+ * @param {string} module
51
+ * @param {string} bazelTarget
52
+ * @return {!Function}
53
+ */
54
+ function requireAtom(module, bazelTarget) {
55
+ try {
56
+ return require('./atoms/' + module)
57
+ } catch (ex) {
58
+ try {
59
+ const file = bazelTarget.slice(2).replace(':', '/')
60
+ console.log(`../../../bazel-bin/${file}`)
61
+ return require(path.resolve(`../../../bazel-bin/${file}`))
62
+ } catch (ex2) {
63
+ console.log(ex2)
64
+ throw Error(
65
+ `Failed to import atoms module ${module}. If running in dev mode, you` +
66
+ ` need to run \`bazel build ${bazelTarget}\` from the project` +
67
+ `root: ${ex}`
68
+ )
69
+ }
70
+ }
71
+ }
72
+
73
+ /**
74
+ * Converts a headers map to a HTTP header block string.
75
+ * @param {!Map<string, string>} headers The map to convert.
76
+ * @return {string} The headers as a string.
77
+ */
78
+ function headersToString(headers) {
79
+ const ret = []
80
+ headers.forEach(function (value, name) {
81
+ ret.push(`${name.toLowerCase()}: ${value}`)
82
+ })
83
+ return ret.join('\n')
84
+ }
85
+
86
+ /**
87
+ * Represents a HTTP request message. This class is a "partial" request and only
88
+ * defines the path on the server to send a request to. It is each client's
89
+ * responsibility to build the full URL for the final request.
90
+ * @final
91
+ */
92
+ class Request {
93
+ /**
94
+ * @param {string} method The HTTP method to use for the request.
95
+ * @param {string} path The path on the server to send the request to.
96
+ * @param {Object=} opt_data This request's non-serialized JSON payload data.
97
+ */
98
+ constructor(method, path, opt_data) {
99
+ this.method = /** string */ method
100
+ this.path = /** string */ path
101
+ this.data = /** Object */ opt_data
102
+ this.headers = /** !Map<string, string> */ new Map([
103
+ ['Accept', 'application/json; charset=utf-8'],
104
+ ])
105
+ }
106
+
107
+ /** @override */
108
+ toString() {
109
+ let ret = `${this.method} ${this.path} HTTP/1.1\n`
110
+ ret += headersToString(this.headers) + '\n\n'
111
+ if (this.data) {
112
+ ret += JSON.stringify(this.data)
113
+ }
114
+ return ret
115
+ }
116
+ }
117
+
118
+ /**
119
+ * Represents a HTTP response message.
120
+ * @final
121
+ */
122
+ class Response {
123
+ /**
124
+ * @param {number} status The response code.
125
+ * @param {!Object<string>} headers The response headers. All header names
126
+ * will be converted to lowercase strings for consistent lookups.
127
+ * @param {string} body The response body.
128
+ */
129
+ constructor(status, headers, body) {
130
+ this.status = /** number */ status
131
+ this.body = /** string */ body
132
+ this.headers = /** !Map<string, string>*/ new Map()
133
+ for (let header in headers) {
134
+ this.headers.set(header.toLowerCase(), headers[header])
135
+ }
136
+ }
137
+
138
+ /** @override */
139
+ toString() {
140
+ let ret = `HTTP/1.1 ${this.status}\n${headersToString(this.headers)}\n\n`
141
+ if (this.body) {
142
+ ret += this.body
143
+ }
144
+ return ret
145
+ }
146
+ }
147
+
148
+ /** @enum {!Function} */
149
+ const Atom = {
150
+ GET_ATTRIBUTE: getAttribute,
151
+ IS_DISPLAYED: isDisplayed,
152
+ FIND_ELEMENTS: findElements,
153
+ }
154
+
155
+ const LOG = logging.getLogger('webdriver.http')
156
+
157
+ function post(path) {
158
+ return resource('POST', path)
159
+ }
160
+ function del(path) {
161
+ return resource('DELETE', path)
162
+ }
163
+ function get(path) {
164
+ return resource('GET', path)
165
+ }
166
+ function resource(method, path) {
167
+ return { method: method, path: path }
168
+ }
169
+
170
+ /** @typedef {{method: string, path: string}} */
171
+ var CommandSpec // eslint-disable-line
172
+
173
+ /** @typedef {function(!cmd.Command): !cmd.Command} */
174
+ var CommandTransformer // eslint-disable-line
175
+
176
+ class InternalTypeError extends TypeError {}
177
+
178
+ /**
179
+ * @param {!cmd.Command} command The initial command.
180
+ * @param {Atom} atom The name of the atom to execute.
181
+ * @return {!cmd.Command} The transformed command to execute.
182
+ */
183
+ function toExecuteAtomCommand(command, atom, ...params) {
184
+ if (typeof atom !== 'function') {
185
+ throw new InternalTypeError('atom is not a function: ' + typeof atom)
186
+ }
187
+
188
+ return new cmd.Command(cmd.Name.EXECUTE_SCRIPT)
189
+ .setParameter('sessionId', command.getParameter('sessionId'))
190
+ .setParameter('script', `return (${atom}).apply(null, arguments)`)
191
+ .setParameter(
192
+ 'args',
193
+ params.map((param) => command.getParameter(param))
194
+ )
195
+ }
196
+
197
+ /** @const {!Map<string, CommandSpec>} */
198
+ const COMMAND_MAP = new Map([
199
+ [cmd.Name.GET_SERVER_STATUS, get('/status')],
200
+ [cmd.Name.NEW_SESSION, post('/session')],
201
+ [cmd.Name.GET_SESSIONS, get('/sessions')],
202
+ [cmd.Name.QUIT, del('/session/:sessionId')],
203
+ [cmd.Name.CLOSE, del('/session/:sessionId/window')],
204
+ [
205
+ cmd.Name.GET_CURRENT_WINDOW_HANDLE,
206
+ get('/session/:sessionId/window_handle'),
207
+ ],
208
+ [cmd.Name.GET_WINDOW_HANDLES, get('/session/:sessionId/window_handles')],
209
+ [cmd.Name.GET_CURRENT_URL, get('/session/:sessionId/url')],
210
+ [cmd.Name.GET, post('/session/:sessionId/url')],
211
+ [cmd.Name.GO_BACK, post('/session/:sessionId/back')],
212
+ [cmd.Name.GO_FORWARD, post('/session/:sessionId/forward')],
213
+ [cmd.Name.REFRESH, post('/session/:sessionId/refresh')],
214
+ [cmd.Name.ADD_COOKIE, post('/session/:sessionId/cookie')],
215
+ [cmd.Name.GET_ALL_COOKIES, get('/session/:sessionId/cookie')],
216
+ [cmd.Name.DELETE_ALL_COOKIES, del('/session/:sessionId/cookie')],
217
+ [cmd.Name.DELETE_COOKIE, del('/session/:sessionId/cookie/:name')],
218
+ [cmd.Name.FIND_ELEMENT, post('/session/:sessionId/element')],
219
+ [cmd.Name.FIND_ELEMENTS, post('/session/:sessionId/elements')],
220
+ [cmd.Name.GET_ACTIVE_ELEMENT, post('/session/:sessionId/element/active')],
221
+ [
222
+ cmd.Name.FIND_CHILD_ELEMENT,
223
+ post('/session/:sessionId/element/:id/element'),
224
+ ],
225
+ [
226
+ cmd.Name.FIND_CHILD_ELEMENTS,
227
+ post('/session/:sessionId/element/:id/elements'),
228
+ ],
229
+ [cmd.Name.CLEAR_ELEMENT, post('/session/:sessionId/element/:id/clear')],
230
+ [cmd.Name.CLICK_ELEMENT, post('/session/:sessionId/element/:id/click')],
231
+ [
232
+ cmd.Name.SEND_KEYS_TO_ELEMENT,
233
+ post('/session/:sessionId/element/:id/value'),
234
+ ],
235
+ [cmd.Name.SUBMIT_ELEMENT, post('/session/:sessionId/element/:id/submit')],
236
+ [cmd.Name.GET_ELEMENT_TEXT, get('/session/:sessionId/element/:id/text')],
237
+ [
238
+ cmd.Name.GET_COMPUTED_ROLE,
239
+ get('/session/:sessionId/element/:id/computedrole'),
240
+ ],
241
+ [
242
+ cmd.Name.GET_COMPUTED_LABEL,
243
+ get('/session/:sessionId/element/:id/computedlabel'),
244
+ ],
245
+ [cmd.Name.GET_ELEMENT_TAG_NAME, get('/session/:sessionId/element/:id/name')],
246
+ [
247
+ cmd.Name.IS_ELEMENT_SELECTED,
248
+ get('/session/:sessionId/element/:id/selected'),
249
+ ],
250
+ [cmd.Name.IS_ELEMENT_ENABLED, get('/session/:sessionId/element/:id/enabled')],
251
+ [
252
+ cmd.Name.IS_ELEMENT_DISPLAYED,
253
+ get('/session/:sessionId/element/:id/displayed'),
254
+ ],
255
+ [
256
+ cmd.Name.GET_ELEMENT_LOCATION,
257
+ get('/session/:sessionId/element/:id/location'),
258
+ ],
259
+ [cmd.Name.GET_ELEMENT_SIZE, get('/session/:sessionId/element/:id/size')],
260
+ [
261
+ cmd.Name.GET_ELEMENT_ATTRIBUTE,
262
+ get('/session/:sessionId/element/:id/attribute/:name'),
263
+ ],
264
+ [
265
+ cmd.Name.GET_ELEMENT_PROPERTY,
266
+ get('/session/:sessionId/element/:id/property/:name'),
267
+ ],
268
+ [
269
+ cmd.Name.GET_ELEMENT_VALUE_OF_CSS_PROPERTY,
270
+ get('/session/:sessionId/element/:id/css/:propertyName'),
271
+ ],
272
+ [
273
+ cmd.Name.TAKE_ELEMENT_SCREENSHOT,
274
+ get('/session/:sessionId/element/:id/screenshot'),
275
+ ],
276
+ [cmd.Name.SWITCH_TO_WINDOW, post('/session/:sessionId/window')],
277
+ [
278
+ cmd.Name.MAXIMIZE_WINDOW,
279
+ post('/session/:sessionId/window/current/maximize'),
280
+ ],
281
+ [
282
+ cmd.Name.GET_WINDOW_POSITION,
283
+ get('/session/:sessionId/window/current/position'),
284
+ ],
285
+ [
286
+ cmd.Name.SET_WINDOW_POSITION,
287
+ post('/session/:sessionId/window/current/position'),
288
+ ],
289
+ [cmd.Name.GET_WINDOW_SIZE, get('/session/:sessionId/window/current/size')],
290
+ [cmd.Name.SET_WINDOW_SIZE, post('/session/:sessionId/window/current/size')],
291
+ [cmd.Name.SWITCH_TO_FRAME, post('/session/:sessionId/frame')],
292
+ [cmd.Name.SWITCH_TO_FRAME_PARENT, post('/session/:sessionId/frame/parent')],
293
+ [cmd.Name.GET_PAGE_SOURCE, get('/session/:sessionId/source')],
294
+ [cmd.Name.GET_TITLE, get('/session/:sessionId/title')],
295
+ [cmd.Name.EXECUTE_SCRIPT, post('/session/:sessionId/execute')],
296
+ [cmd.Name.EXECUTE_ASYNC_SCRIPT, post('/session/:sessionId/execute_async')],
297
+ [cmd.Name.SCREENSHOT, get('/session/:sessionId/screenshot')],
298
+ [cmd.Name.GET_TIMEOUT, get('/session/:sessionId/timeouts')],
299
+ [cmd.Name.SET_TIMEOUT, post('/session/:sessionId/timeouts')],
300
+ [cmd.Name.ACCEPT_ALERT, post('/session/:sessionId/accept_alert')],
301
+ [cmd.Name.DISMISS_ALERT, post('/session/:sessionId/dismiss_alert')],
302
+ [cmd.Name.GET_ALERT_TEXT, get('/session/:sessionId/alert_text')],
303
+ [cmd.Name.SET_ALERT_TEXT, post('/session/:sessionId/alert_text')],
304
+ [cmd.Name.GET_LOG, post('/session/:sessionId/log')],
305
+ [cmd.Name.GET_AVAILABLE_LOG_TYPES, get('/session/:sessionId/log/types')],
306
+ [cmd.Name.GET_SESSION_LOGS, post('/logs')],
307
+ [cmd.Name.UPLOAD_FILE, post('/session/:sessionId/se/file')],
308
+ ])
309
+
310
+ /** @const {!Map<string, (CommandSpec|CommandTransformer)>} */
311
+ const W3C_COMMAND_MAP = new Map([
312
+ // Server status.
313
+ [cmd.Name.GET_SERVER_STATUS, get('/status')],
314
+ // Session management.
315
+ [cmd.Name.NEW_SESSION, post('/session')],
316
+ [cmd.Name.QUIT, del('/session/:sessionId')],
317
+ [cmd.Name.GET_TIMEOUT, get('/session/:sessionId/timeouts')],
318
+ [cmd.Name.SET_TIMEOUT, post('/session/:sessionId/timeouts')],
319
+ // Navigation.
320
+ [cmd.Name.GET_CURRENT_URL, get('/session/:sessionId/url')],
321
+ [cmd.Name.GET, post('/session/:sessionId/url')],
322
+ [cmd.Name.GO_BACK, post('/session/:sessionId/back')],
323
+ [cmd.Name.GO_FORWARD, post('/session/:sessionId/forward')],
324
+ [cmd.Name.REFRESH, post('/session/:sessionId/refresh')],
325
+ // Page inspection.
326
+ [cmd.Name.GET_PAGE_SOURCE, get('/session/:sessionId/source')],
327
+ [cmd.Name.GET_TITLE, get('/session/:sessionId/title')],
328
+ // Script execution.
329
+ [cmd.Name.EXECUTE_SCRIPT, post('/session/:sessionId/execute/sync')],
330
+ [cmd.Name.EXECUTE_ASYNC_SCRIPT, post('/session/:sessionId/execute/async')],
331
+ // Frame selection.
332
+ [cmd.Name.SWITCH_TO_FRAME, post('/session/:sessionId/frame')],
333
+ [cmd.Name.SWITCH_TO_FRAME_PARENT, post('/session/:sessionId/frame/parent')],
334
+ // Window management.
335
+ [cmd.Name.GET_CURRENT_WINDOW_HANDLE, get('/session/:sessionId/window')],
336
+ [cmd.Name.CLOSE, del('/session/:sessionId/window')],
337
+ [cmd.Name.SWITCH_TO_WINDOW, post('/session/:sessionId/window')],
338
+ [cmd.Name.SWITCH_TO_NEW_WINDOW, post('/session/:sessionId/window/new')],
339
+ [cmd.Name.GET_WINDOW_HANDLES, get('/session/:sessionId/window/handles')],
340
+ [cmd.Name.GET_WINDOW_RECT, get('/session/:sessionId/window/rect')],
341
+ [cmd.Name.SET_WINDOW_RECT, post('/session/:sessionId/window/rect')],
342
+ [cmd.Name.MAXIMIZE_WINDOW, post('/session/:sessionId/window/maximize')],
343
+ [cmd.Name.MINIMIZE_WINDOW, post('/session/:sessionId/window/minimize')],
344
+ [cmd.Name.FULLSCREEN_WINDOW, post('/session/:sessionId/window/fullscreen')],
345
+ // Actions.
346
+ [cmd.Name.ACTIONS, post('/session/:sessionId/actions')],
347
+ [cmd.Name.CLEAR_ACTIONS, del('/session/:sessionId/actions')],
348
+ // Locating elements.
349
+ [cmd.Name.GET_ACTIVE_ELEMENT, get('/session/:sessionId/element/active')],
350
+ [cmd.Name.FIND_ELEMENT, post('/session/:sessionId/element')],
351
+ [cmd.Name.FIND_ELEMENTS, post('/session/:sessionId/elements')],
352
+ [
353
+ cmd.Name.FIND_ELEMENTS_RELATIVE,
354
+ (cmd) => {
355
+ return toExecuteAtomCommand(cmd, Atom.FIND_ELEMENTS, 'args')
356
+ },
357
+ ],
358
+ [
359
+ cmd.Name.FIND_CHILD_ELEMENT,
360
+ post('/session/:sessionId/element/:id/element'),
361
+ ],
362
+ [
363
+ cmd.Name.FIND_CHILD_ELEMENTS,
364
+ post('/session/:sessionId/element/:id/elements'),
365
+ ],
366
+ // Element interaction.
367
+ [cmd.Name.GET_ELEMENT_TAG_NAME, get('/session/:sessionId/element/:id/name')],
368
+ [
369
+ cmd.Name.GET_ELEMENT_PROPERTY,
370
+ get('/session/:sessionId/element/:id/property/:name'),
371
+ ],
372
+ [
373
+ cmd.Name.GET_ELEMENT_VALUE_OF_CSS_PROPERTY,
374
+ get('/session/:sessionId/element/:id/css/:propertyName'),
375
+ ],
376
+ [cmd.Name.GET_ELEMENT_RECT, get('/session/:sessionId/element/:id/rect')],
377
+ [cmd.Name.CLEAR_ELEMENT, post('/session/:sessionId/element/:id/clear')],
378
+ [cmd.Name.CLICK_ELEMENT, post('/session/:sessionId/element/:id/click')],
379
+ [
380
+ cmd.Name.SEND_KEYS_TO_ELEMENT,
381
+ post('/session/:sessionId/element/:id/value'),
382
+ ],
383
+ [cmd.Name.GET_ELEMENT_TEXT, get('/session/:sessionId/element/:id/text')],
384
+ [
385
+ cmd.Name.GET_COMPUTED_ROLE,
386
+ get('/session/:sessionId/element/:id/computedrole'),
387
+ ],
388
+ [
389
+ cmd.Name.GET_COMPUTED_LABEL,
390
+ get('/session/:sessionId/element/:id/computedlabel'),
391
+ ],
392
+ [cmd.Name.IS_ELEMENT_ENABLED, get('/session/:sessionId/element/:id/enabled')],
393
+ [
394
+ cmd.Name.GET_ELEMENT_ATTRIBUTE,
395
+ (cmd) => {
396
+ return toExecuteAtomCommand(cmd, Atom.GET_ATTRIBUTE, 'id', 'name')
397
+ },
398
+ ],
399
+ [
400
+ cmd.Name.IS_ELEMENT_DISPLAYED,
401
+ (cmd) => {
402
+ return toExecuteAtomCommand(cmd, Atom.IS_DISPLAYED, 'id')
403
+ },
404
+ ],
405
+ // Cookie management.
406
+ [cmd.Name.GET_ALL_COOKIES, get('/session/:sessionId/cookie')],
407
+ [cmd.Name.ADD_COOKIE, post('/session/:sessionId/cookie')],
408
+ [cmd.Name.DELETE_ALL_COOKIES, del('/session/:sessionId/cookie')],
409
+ [cmd.Name.GET_COOKIE, get('/session/:sessionId/cookie/:name')],
410
+ [cmd.Name.DELETE_COOKIE, del('/session/:sessionId/cookie/:name')],
411
+ // Alert management.
412
+ [cmd.Name.ACCEPT_ALERT, post('/session/:sessionId/alert/accept')],
413
+ [cmd.Name.DISMISS_ALERT, post('/session/:sessionId/alert/dismiss')],
414
+ [cmd.Name.GET_ALERT_TEXT, get('/session/:sessionId/alert/text')],
415
+ [cmd.Name.SET_ALERT_TEXT, post('/session/:sessionId/alert/text')],
416
+ // Screenshots.
417
+ [cmd.Name.SCREENSHOT, get('/session/:sessionId/screenshot')],
418
+ [
419
+ cmd.Name.TAKE_ELEMENT_SCREENSHOT,
420
+ get('/session/:sessionId/element/:id/screenshot'),
421
+ ],
422
+ // print page.
423
+ [cmd.Name.PRINT_PAGE, post('/session/:sessionId/print')],
424
+ // Log extensions.
425
+ [cmd.Name.GET_LOG, post('/session/:sessionId/se/log')],
426
+ [cmd.Name.GET_AVAILABLE_LOG_TYPES, get('/session/:sessionId/se/log/types')],
427
+ ])
428
+
429
+ /**
430
+ * Handles sending HTTP messages to a remote end.
431
+ *
432
+ * @interface
433
+ */
434
+ class Client {
435
+ /**
436
+ * Sends a request to the server. The client will automatically follow any
437
+ * redirects returned by the server, fulfilling the returned promise with the
438
+ * final response.
439
+ *
440
+ * @param {!Request} httpRequest The request to send.
441
+ * @return {!Promise<Response>} A promise that will be fulfilled with the
442
+ * server's response.
443
+ */
444
+ send(httpRequest) {} // eslint-disable-line
445
+ }
446
+
447
+ /**
448
+ * @param {Map<string, CommandSpec>} customCommands
449
+ * A map of custom command definitions.
450
+ * @param {boolean} w3c Whether to use W3C command mappings.
451
+ * @param {!cmd.Command} command The command to resolve.
452
+ * @return {!Request} A promise that will resolve with the
453
+ * command to execute.
454
+ */
455
+ function buildRequest(customCommands, w3c, command) {
456
+ LOG.finest(() => `Translating command: ${command.getName()}`)
457
+ let spec = customCommands && customCommands.get(command.getName())
458
+ if (spec) {
459
+ return toHttpRequest(spec)
460
+ }
461
+
462
+ if (w3c) {
463
+ spec = W3C_COMMAND_MAP.get(command.getName())
464
+ if (typeof spec === 'function') {
465
+ LOG.finest(() => `Transforming command for W3C: ${command.getName()}`)
466
+ let newCommand = spec(command)
467
+ return buildRequest(customCommands, w3c, newCommand)
468
+ } else if (spec) {
469
+ return toHttpRequest(spec)
470
+ }
471
+ }
472
+
473
+ spec = COMMAND_MAP.get(command.getName())
474
+ if (spec) {
475
+ return toHttpRequest(spec)
476
+ }
477
+ throw new error.UnknownCommandError(
478
+ 'Unrecognized command: ' + command.getName()
479
+ )
480
+
481
+ /**
482
+ * @param {CommandSpec} resource
483
+ * @return {!Request}
484
+ */
485
+ function toHttpRequest(resource) {
486
+ LOG.finest(() => `Building HTTP request: ${JSON.stringify(resource)}`)
487
+ let parameters = command.getParameters()
488
+ let path = buildPath(resource.path, parameters)
489
+ return new Request(resource.method, path, parameters)
490
+ }
491
+ }
492
+
493
+ const CLIENTS = /** !WeakMap<!Executor, !(Client|IThenable<!Client>)> */ new WeakMap()
494
+
495
+ /**
496
+ * A command executor that communicates with the server using JSON over HTTP.
497
+ *
498
+ * By default, each instance of this class will use the legacy wire protocol
499
+ * from [Selenium project][json]. The executor will automatically switch to the
500
+ * [W3C wire protocol][w3c] if the remote end returns a compliant response to
501
+ * a new session command.
502
+ *
503
+ * [json]: https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol
504
+ * [w3c]: https://w3c.github.io/webdriver/webdriver-spec.html
505
+ *
506
+ * @implements {cmd.Executor}
507
+ */
508
+ class Executor {
509
+ /**
510
+ * @param {!(Client|IThenable<!Client>)} client The client to use for sending
511
+ * requests to the server, or a promise-like object that will resolve to
512
+ * to the client.
513
+ */
514
+ constructor(client) {
515
+ CLIENTS.set(this, client)
516
+
517
+ /**
518
+ * Whether this executor should use the W3C wire protocol. The executor
519
+ * will automatically switch if the remote end sends a compliant response
520
+ * to a new session command, however, this property may be directly set to
521
+ * `true` to force the executor into W3C mode.
522
+ * @type {boolean}
523
+ */
524
+ this.w3c = false
525
+
526
+ /** @private {Map<string, CommandSpec>} */
527
+ this.customCommands_ = null
528
+
529
+ /** @private {!logging.Logger} */
530
+ this.log_ = logging.getLogger('webdriver.http.Executor')
531
+ }
532
+
533
+ /**
534
+ * Defines a new command for use with this executor. When a command is sent,
535
+ * the {@code path} will be preprocessed using the command's parameters; any
536
+ * path segments prefixed with ":" will be replaced by the parameter of the
537
+ * same name. For example, given "/person/:name" and the parameters
538
+ * "{name: 'Bob'}", the final command path will be "/person/Bob".
539
+ *
540
+ * @param {string} name The command name.
541
+ * @param {string} method The HTTP method to use when sending this command.
542
+ * @param {string} path The path to send the command to, relative to
543
+ * the WebDriver server's command root and of the form
544
+ * "/path/:variable/segment".
545
+ */
546
+ defineCommand(name, method, path) {
547
+ if (!this.customCommands_) {
548
+ this.customCommands_ = new Map()
549
+ }
550
+ this.customCommands_.set(name, { method, path })
551
+ }
552
+
553
+ /** @override */
554
+ async execute(command) {
555
+ let request = buildRequest(this.customCommands_, this.w3c, command)
556
+ this.log_.finer(() => `>>> ${request.method} ${request.path}`)
557
+
558
+ let client = CLIENTS.get(this)
559
+ if (promise.isPromise(client)) {
560
+ client = await client
561
+ CLIENTS.set(this, client)
562
+ }
563
+
564
+ let response = await client.send(request)
565
+ this.log_.finer(() => `>>>\n${request}\n<<<\n${response}`)
566
+
567
+ let httpResponse = /** @type {!Response} */ (response)
568
+ let { isW3C, value } = parseHttpResponse(command, httpResponse)
569
+
570
+ if (command.getName() === cmd.Name.NEW_SESSION) {
571
+ if (!value || !value.sessionId) {
572
+ throw new error.WebDriverError(
573
+ `Unable to parse new session response: ${response.body}`
574
+ )
575
+ }
576
+
577
+ // The remote end is a W3C compliant server if there is no `status`
578
+ // field in the response.
579
+ if (command.getName() === cmd.Name.NEW_SESSION) {
580
+ this.w3c = this.w3c || isW3C
581
+ }
582
+
583
+ // No implementations use the `capabilities` key yet...
584
+ let capabilities = value.capabilities || value.value
585
+ return new Session(
586
+ /** @type {{sessionId: string}} */ (value).sessionId,
587
+ capabilities
588
+ )
589
+ }
590
+
591
+ return typeof value === 'undefined' ? null : value
592
+ }
593
+ }
594
+
595
+ /**
596
+ * @param {string} str .
597
+ * @return {?} .
598
+ */
599
+ function tryParse(str) {
600
+ try {
601
+ return JSON.parse(str)
602
+ } catch (ignored) {
603
+ // Do nothing.
604
+ }
605
+ }
606
+
607
+ /**
608
+ * Callback used to parse {@link Response} objects from a
609
+ * {@link HttpClient}.
610
+ *
611
+ * @param {!cmd.Command} command The command the response is for.
612
+ * @param {!Response} httpResponse The HTTP response to parse.
613
+ * @return {{isW3C: boolean, value: ?}} An object describing the parsed
614
+ * response. This object will have two fields: `isW3C` indicates whether
615
+ * the response looks like it came from a remote end that conforms with the
616
+ * W3C WebDriver spec, and `value`, the actual response value.
617
+ * @throws {WebDriverError} If the HTTP response is an error.
618
+ */
619
+ function parseHttpResponse(command, httpResponse) {
620
+ if (httpResponse.status < 200) {
621
+ // This should never happen, but throw the raw response so users report it.
622
+ throw new error.WebDriverError(`Unexpected HTTP response:\n${httpResponse}`)
623
+ }
624
+
625
+ let parsed = tryParse(httpResponse.body)
626
+ if (parsed && typeof parsed === 'object') {
627
+ let value = parsed.value
628
+ let isW3C =
629
+ value !== null &&
630
+ typeof value === 'object' &&
631
+ typeof parsed.status === 'undefined'
632
+
633
+ if (!isW3C) {
634
+ error.checkLegacyResponse(parsed)
635
+
636
+ // Adjust legacy new session responses to look like W3C to simplify
637
+ // later processing.
638
+ if (command.getName() === cmd.Name.NEW_SESSION) {
639
+ value = parsed
640
+ }
641
+ } else if (httpResponse.status > 399) {
642
+ error.throwDecodedError(value)
643
+ }
644
+
645
+ return { isW3C, value }
646
+ }
647
+
648
+ if (parsed !== undefined) {
649
+ return { isW3C: false, value: parsed }
650
+ }
651
+
652
+ let value = httpResponse.body.replace(/\r\n/g, '\n')
653
+
654
+ // 404 represents an unknown command; anything else > 399 is a generic unknown
655
+ // error.
656
+ if (httpResponse.status == 404) {
657
+ throw new error.UnsupportedOperationError(command.getName() + ': ' + value)
658
+ } else if (httpResponse.status >= 400) {
659
+ throw new error.WebDriverError(value)
660
+ }
661
+
662
+ return { isW3C: false, value: value || null }
663
+ }
664
+
665
+ /**
666
+ * Builds a fully qualified path using the given set of command parameters. Each
667
+ * path segment prefixed with ':' will be replaced by the value of the
668
+ * corresponding parameter. All parameters spliced into the path will be
669
+ * removed from the parameter map.
670
+ * @param {string} path The original resource path.
671
+ * @param {!Object<*>} parameters The parameters object to splice into the path.
672
+ * @return {string} The modified path.
673
+ */
674
+ function buildPath(path, parameters) {
675
+ let pathParameters = path.match(/\/:(\w+)\b/g)
676
+ if (pathParameters) {
677
+ for (let i = 0; i < pathParameters.length; ++i) {
678
+ let key = pathParameters[i].substring(2) // Trim the /:
679
+ if (key in parameters) {
680
+ let value = parameters[key]
681
+ if (WebElement.isId(value)) {
682
+ // When inserting a WebElement into the URL, only use its ID value,
683
+ // not the full JSON.
684
+ value = WebElement.extractId(value)
685
+ }
686
+ path = path.replace(pathParameters[i], '/' + value)
687
+ delete parameters[key]
688
+ } else {
689
+ throw new error.InvalidArgumentError(
690
+ 'Missing required parameter: ' + key
691
+ )
692
+ }
693
+ }
694
+ }
695
+ return path
696
+ }
697
+
698
+ // PUBLIC API
699
+
700
+ exports.Executor = Executor
701
+ exports.Client = Client
702
+ exports.Request = Request
703
+ exports.Response = Response
704
+ exports.buildPath = buildPath // Exported for testing.