selenium-webdriver 3.5.0 → 4.0.0-alpha.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (366) hide show
  1. package/CHANGES.md +246 -0
  2. package/LICENSE +1 -1
  3. package/NOTICE +1 -1
  4. package/README.md +40 -40
  5. package/chrome.js +118 -134
  6. package/edge.js +43 -117
  7. package/example/chrome_android.js +8 -9
  8. package/example/chrome_mobile_emulation.js +8 -9
  9. package/example/firefox_channels.js +23 -19
  10. package/example/google_search.js +3 -3
  11. package/example/google_search_test.js +38 -27
  12. package/example/{chrome_headless.js → headless.js} +15 -7
  13. package/example/logging.js +40 -7
  14. package/firefox.js +789 -0
  15. package/http/index.js +62 -9
  16. package/http/util.js +11 -6
  17. package/ie.js +83 -97
  18. package/index.js +198 -114
  19. package/io/exec.js +29 -15
  20. package/io/index.js +1 -1
  21. package/io/zip.js +1 -1
  22. package/lib/actions.js +68 -63
  23. package/lib/atoms/get-attribute.js +86 -0
  24. package/lib/atoms/is-displayed.js +76 -76
  25. package/lib/by.js +7 -4
  26. package/lib/capabilities.js +310 -210
  27. package/lib/command.js +27 -51
  28. package/lib/error.js +100 -41
  29. package/lib/http.js +161 -120
  30. package/lib/input.js +1044 -8
  31. package/lib/logging.js +1 -5
  32. package/lib/promise.js +222 -3331
  33. package/lib/proxy.js +134 -39
  34. package/lib/session.js +2 -2
  35. package/lib/symbols.js +0 -0
  36. package/lib/until.js +1 -1
  37. package/lib/webdriver.js +717 -953
  38. package/net/index.js +21 -25
  39. package/net/portprober.js +51 -68
  40. package/package.json +17 -12
  41. package/proxy.js +0 -0
  42. package/remote/index.js +63 -44
  43. package/safari.js +41 -128
  44. package/testing/index.js +386 -315
  45. package/.npmignore +0 -2
  46. package/example/async_await_test.js +0 -68
  47. package/example/google_search_generator.js +0 -50
  48. package/example/parallel_flows.js +0 -54
  49. package/firefox/binary.js +0 -344
  50. package/firefox/extension.js +0 -224
  51. package/firefox/index.js +0 -626
  52. package/firefox/profile.js +0 -394
  53. package/lib/README +0 -5
  54. package/lib/atoms/getAttribute.js +0 -9
  55. package/lib/devmode.js +0 -34
  56. package/lib/events.js +0 -210
  57. package/lib/firefox/webdriver.json +0 -70
  58. package/lib/test/build.js +0 -151
  59. package/lib/test/data/ClickTest_testClicksASurroundingStrongTag.html +0 -11
  60. package/lib/test/data/Page.aspx +0 -17
  61. package/lib/test/data/Page.aspx.cs +0 -22
  62. package/lib/test/data/Redirect.aspx +0 -11
  63. package/lib/test/data/Redirect.aspx.cs +0 -9
  64. package/lib/test/data/Settings.StyleCop +0 -759
  65. package/lib/test/data/Web.Config +0 -59
  66. package/lib/test/data/actualXhtmlPage.xhtml +0 -14
  67. package/lib/test/data/ajaxy_page.html +0 -81
  68. package/lib/test/data/alerts.html +0 -85
  69. package/lib/test/data/banner.gif +0 -0
  70. package/lib/test/data/beach.jpg +0 -0
  71. package/lib/test/data/blank.html +0 -1
  72. package/lib/test/data/bodyTypingTest.html +0 -41
  73. package/lib/test/data/booleanAttributes.html +0 -19
  74. package/lib/test/data/child/childPage.html +0 -8
  75. package/lib/test/data/child/grandchild/grandchildPage.html +0 -8
  76. package/lib/test/data/clickEventPage.html +0 -26
  77. package/lib/test/data/click_frames.html +0 -10
  78. package/lib/test/data/click_jacker.html +0 -38
  79. package/lib/test/data/click_out_of_bounds.html +0 -23
  80. package/lib/test/data/click_out_of_bounds_overflow.html +0 -85
  81. package/lib/test/data/click_rtl.html +0 -19
  82. package/lib/test/data/click_source.html +0 -18
  83. package/lib/test/data/click_tests/click_iframe.html +0 -6
  84. package/lib/test/data/click_tests/click_in_iframe.html +0 -8
  85. package/lib/test/data/click_tests/disabled_element.html +0 -12
  86. package/lib/test/data/click_tests/disappearing_element.html +0 -62
  87. package/lib/test/data/click_tests/google_map.html +0 -15
  88. package/lib/test/data/click_tests/google_map.png +0 -0
  89. package/lib/test/data/click_tests/html5_submit_buttons.html +0 -16
  90. package/lib/test/data/click_tests/issue5237.html +0 -9
  91. package/lib/test/data/click_tests/issue5237_frame.html +0 -1
  92. package/lib/test/data/click_tests/issue5237_target.html +0 -10
  93. package/lib/test/data/click_tests/link_that_wraps.html +0 -11
  94. package/lib/test/data/click_tests/mapped_page1.html +0 -9
  95. package/lib/test/data/click_tests/mapped_page2.html +0 -9
  96. package/lib/test/data/click_tests/mapped_page3.html +0 -9
  97. package/lib/test/data/click_tests/overlapping_elements.html +0 -70
  98. package/lib/test/data/click_tests/partially_overlapping_elements.html +0 -124
  99. package/lib/test/data/click_tests/span_that_wraps.html +0 -11
  100. package/lib/test/data/click_tests/submitted_page.html +0 -9
  101. package/lib/test/data/click_tests/wrapped_overlapping_elements.html +0 -13
  102. package/lib/test/data/click_too_big.html +0 -10
  103. package/lib/test/data/click_too_big_in_frame.html +0 -11
  104. package/lib/test/data/clicks.html +0 -35
  105. package/lib/test/data/closeable_window.html +0 -8
  106. package/lib/test/data/cn-test.html +0 -156
  107. package/lib/test/data/colorPage.html +0 -20
  108. package/lib/test/data/cookies.html +0 -30
  109. package/lib/test/data/coordinates_tests/element_in_frame.html +0 -9
  110. package/lib/test/data/coordinates_tests/element_in_nested_frame.html +0 -9
  111. package/lib/test/data/coordinates_tests/page_with_element_out_of_view.html +0 -11
  112. package/lib/test/data/coordinates_tests/page_with_empty_element.html +0 -10
  113. package/lib/test/data/coordinates_tests/page_with_fixed_element.html +0 -12
  114. package/lib/test/data/coordinates_tests/page_with_hidden_element.html +0 -10
  115. package/lib/test/data/coordinates_tests/page_with_invisible_element.html +0 -10
  116. package/lib/test/data/coordinates_tests/page_with_transparent_element.html +0 -10
  117. package/lib/test/data/coordinates_tests/simple_page.html +0 -10
  118. package/lib/test/data/css/ui-lightness/images/ui-bg_diagonals-thick_18_b81900_40x40.png +0 -0
  119. package/lib/test/data/css/ui-lightness/images/ui-bg_diagonals-thick_20_666666_40x40.png +0 -0
  120. package/lib/test/data/css/ui-lightness/images/ui-bg_flat_10_000000_40x100.png +0 -0
  121. package/lib/test/data/css/ui-lightness/images/ui-bg_glass_100_f6f6f6_1x400.png +0 -0
  122. package/lib/test/data/css/ui-lightness/images/ui-bg_glass_100_fdf5ce_1x400.png +0 -0
  123. package/lib/test/data/css/ui-lightness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  124. package/lib/test/data/css/ui-lightness/images/ui-bg_gloss-wave_35_f6a828_500x100.png +0 -0
  125. package/lib/test/data/css/ui-lightness/images/ui-bg_highlight-soft_100_eeeeee_1x100.png +0 -0
  126. package/lib/test/data/css/ui-lightness/images/ui-bg_highlight-soft_75_ffe45c_1x100.png +0 -0
  127. package/lib/test/data/css/ui-lightness/images/ui-icons_222222_256x240.png +0 -0
  128. package/lib/test/data/css/ui-lightness/images/ui-icons_228ef1_256x240.png +0 -0
  129. package/lib/test/data/css/ui-lightness/images/ui-icons_ef8c08_256x240.png +0 -0
  130. package/lib/test/data/css/ui-lightness/images/ui-icons_ffd27a_256x240.png +0 -0
  131. package/lib/test/data/css/ui-lightness/images/ui-icons_ffffff_256x240.png +0 -0
  132. package/lib/test/data/css/ui-lightness/jquery-ui-1.8.10.custom.css +0 -573
  133. package/lib/test/data/cssTransform.html +0 -61
  134. package/lib/test/data/cssTransform2.html +0 -20
  135. package/lib/test/data/document_write_in_onload.html +0 -13
  136. package/lib/test/data/dragAndDropInsideScrolledDiv.html +0 -67
  137. package/lib/test/data/dragAndDropTest.html +0 -102
  138. package/lib/test/data/dragDropOverflow.html +0 -104
  139. package/lib/test/data/draggableLists.html +0 -67
  140. package/lib/test/data/droppableItems.html +0 -65
  141. package/lib/test/data/dynamic.html +0 -39
  142. package/lib/test/data/dynamicallyModifiedPage.html +0 -42
  143. package/lib/test/data/errors.html +0 -15
  144. package/lib/test/data/firefox/jetpack-sample.xpi +0 -0
  145. package/lib/test/data/firefox/sample.xpi +0 -0
  146. package/lib/test/data/firefox/webextension.xpi +0 -0
  147. package/lib/test/data/fixedFooterNoScroll.html +0 -13
  148. package/lib/test/data/fixedFooterNoScrollQuirksMode.html +0 -12
  149. package/lib/test/data/formPage.html +0 -175
  150. package/lib/test/data/formSelectionPage.html +0 -46
  151. package/lib/test/data/form_handling_js_submit.html +0 -30
  152. package/lib/test/data/framePage3.html +0 -7
  153. package/lib/test/data/frameScrollChild.html +0 -26
  154. package/lib/test/data/frameScrollPage.html +0 -14
  155. package/lib/test/data/frameScrollParent.html +0 -11
  156. package/lib/test/data/frameWithAnimals.html +0 -11
  157. package/lib/test/data/frame_switching_tests/bug4876.html +0 -9
  158. package/lib/test/data/frame_switching_tests/bug4876_iframe.html +0 -9
  159. package/lib/test/data/frame_switching_tests/deletingFrame.html +0 -29
  160. package/lib/test/data/frame_switching_tests/deletingFrame_iframe.html +0 -8
  161. package/lib/test/data/frame_switching_tests/deletingFrame_iframe2.html +0 -7
  162. package/lib/test/data/frameset.html +0 -14
  163. package/lib/test/data/framesetPage2.html +0 -7
  164. package/lib/test/data/framesetPage3.html +0 -4
  165. package/lib/test/data/globalscope.html +0 -15
  166. package/lib/test/data/hidden.html +0 -5
  167. package/lib/test/data/hidden_partially.html +0 -45
  168. package/lib/test/data/html5/blue.jpg +0 -0
  169. package/lib/test/data/html5/database.js +0 -84
  170. package/lib/test/data/html5/geolocation.js +0 -18
  171. package/lib/test/data/html5/green.jpg +0 -0
  172. package/lib/test/data/html5/red.jpg +0 -0
  173. package/lib/test/data/html5/status.html +0 -1
  174. package/lib/test/data/html5/test.appcache +0 -11
  175. package/lib/test/data/html5/yellow.jpg +0 -0
  176. package/lib/test/data/html5Page.html +0 -32
  177. package/lib/test/data/icon.gif +0 -0
  178. package/lib/test/data/idElements.html +0 -2
  179. package/lib/test/data/iframeAtBottom.html +0 -15
  180. package/lib/test/data/iframeWithAlert.html +0 -1
  181. package/lib/test/data/iframeWithIframe.html +0 -1
  182. package/lib/test/data/iframes.html +0 -11
  183. package/lib/test/data/injectableContent.html +0 -22
  184. package/lib/test/data/javascriptEnhancedForm.html +0 -30
  185. package/lib/test/data/javascriptPage.html +0 -285
  186. package/lib/test/data/jquery-1.3.2.js +0 -4376
  187. package/lib/test/data/js/jquery-1.4.4.min.js +0 -167
  188. package/lib/test/data/js/jquery-ui-1.8.10.custom.min.js +0 -782
  189. package/lib/test/data/js/skins/lightgray/content.inline.min.css +0 -1
  190. package/lib/test/data/js/skins/lightgray/content.min.css +0 -1
  191. package/lib/test/data/js/skins/lightgray/fonts/readme.md +0 -1
  192. package/lib/test/data/js/skins/lightgray/fonts/tinymce-small.dev.svg +0 -175
  193. package/lib/test/data/js/skins/lightgray/fonts/tinymce-small.eot +0 -0
  194. package/lib/test/data/js/skins/lightgray/fonts/tinymce-small.svg +0 -62
  195. package/lib/test/data/js/skins/lightgray/fonts/tinymce-small.ttf +0 -0
  196. package/lib/test/data/js/skins/lightgray/fonts/tinymce-small.woff +0 -0
  197. package/lib/test/data/js/skins/lightgray/fonts/tinymce.dev.svg +0 -153
  198. package/lib/test/data/js/skins/lightgray/fonts/tinymce.eot +0 -0
  199. package/lib/test/data/js/skins/lightgray/fonts/tinymce.svg +0 -63
  200. package/lib/test/data/js/skins/lightgray/fonts/tinymce.ttf +0 -0
  201. package/lib/test/data/js/skins/lightgray/fonts/tinymce.woff +0 -0
  202. package/lib/test/data/js/skins/lightgray/img/anchor.gif +0 -0
  203. package/lib/test/data/js/skins/lightgray/img/loader.gif +0 -0
  204. package/lib/test/data/js/skins/lightgray/img/object.gif +0 -0
  205. package/lib/test/data/js/skins/lightgray/img/trans.gif +0 -0
  206. package/lib/test/data/js/skins/lightgray/skin.ie7.min.css +0 -1
  207. package/lib/test/data/js/skins/lightgray/skin.min.css +0 -1
  208. package/lib/test/data/js/themes/modern/theme.min.js +0 -1
  209. package/lib/test/data/js/tinymce.min.js +0 -10
  210. package/lib/test/data/key_logger.html +0 -34
  211. package/lib/test/data/key_tests/remove_on_keypress.html +0 -36
  212. package/lib/test/data/keyboard_shortcut.html +0 -36
  213. package/lib/test/data/linked_image.html +0 -16
  214. package/lib/test/data/locators_tests/boolean_attribute_selected.html +0 -13
  215. package/lib/test/data/locators_tests/boolean_attribute_selected_html4.html +0 -13
  216. package/lib/test/data/longContentPage.html +0 -55
  217. package/lib/test/data/macbeth.html +0 -5255
  218. package/lib/test/data/map.png +0 -0
  219. package/lib/test/data/map_visibility.html +0 -8
  220. package/lib/test/data/markerTransparent.png +0 -0
  221. package/lib/test/data/messages.html +0 -15
  222. package/lib/test/data/meta-redirect.html +0 -11
  223. package/lib/test/data/missedJsReference.html +0 -11
  224. package/lib/test/data/modal_dialogs/modal_1.html +0 -21
  225. package/lib/test/data/modal_dialogs/modal_2.html +0 -21
  226. package/lib/test/data/modal_dialogs/modal_3.html +0 -15
  227. package/lib/test/data/modal_dialogs/modalindex.html +0 -21
  228. package/lib/test/data/mouseOver.html +0 -17
  229. package/lib/test/data/mousePositionTracker.html +0 -33
  230. package/lib/test/data/nestedElements.html +0 -164
  231. package/lib/test/data/overflow/x_auto_y_auto.html +0 -30
  232. package/lib/test/data/overflow/x_auto_y_hidden.html +0 -30
  233. package/lib/test/data/overflow/x_auto_y_scroll.html +0 -30
  234. package/lib/test/data/overflow/x_hidden_y_auto.html +0 -30
  235. package/lib/test/data/overflow/x_hidden_y_hidden.html +0 -30
  236. package/lib/test/data/overflow/x_hidden_y_scroll.html +0 -30
  237. package/lib/test/data/overflow/x_scroll_y_auto.html +0 -30
  238. package/lib/test/data/overflow/x_scroll_y_hidden.html +0 -30
  239. package/lib/test/data/overflow/x_scroll_y_scroll.html +0 -30
  240. package/lib/test/data/overflow-body.html +0 -15
  241. package/lib/test/data/pageWithOnBeforeUnloadMessage.html +0 -20
  242. package/lib/test/data/pageWithOnLoad.html +0 -6
  243. package/lib/test/data/pageWithOnUnload.html +0 -6
  244. package/lib/test/data/page_with_link_to_slow_loading_page.html +0 -6
  245. package/lib/test/data/plain.txt +0 -1
  246. package/lib/test/data/proxy/page1.html +0 -20
  247. package/lib/test/data/proxy/page2.html +0 -24
  248. package/lib/test/data/proxy/page3.html +0 -5
  249. package/lib/test/data/readOnlyPage.html +0 -24
  250. package/lib/test/data/rectangles.html +0 -40
  251. package/lib/test/data/resultPage.html +0 -25
  252. package/lib/test/data/rich_text.html +0 -161
  253. package/lib/test/data/safari/frames_benchmark.html +0 -31
  254. package/lib/test/data/screen/screen.css +0 -19
  255. package/lib/test/data/screen/screen.html +0 -72
  256. package/lib/test/data/screen/screen.js +0 -7
  257. package/lib/test/data/screen/screen_frame1.html +0 -72
  258. package/lib/test/data/screen/screen_frame2.html +0 -72
  259. package/lib/test/data/screen/screen_frames.html +0 -11
  260. package/lib/test/data/screen/screen_iframes.html +0 -12
  261. package/lib/test/data/screen/screen_too_long.html +0 -68
  262. package/lib/test/data/screen/screen_x_long.html +0 -72
  263. package/lib/test/data/screen/screen_x_too_long.html +0 -72
  264. package/lib/test/data/screen/screen_y_long.html +0 -72
  265. package/lib/test/data/screen/screen_y_too_long.html +0 -72
  266. package/lib/test/data/scroll.html +0 -27
  267. package/lib/test/data/scroll2.html +0 -21
  268. package/lib/test/data/scroll3.html +0 -8
  269. package/lib/test/data/scroll4.html +0 -7
  270. package/lib/test/data/scroll5.html +0 -18
  271. package/lib/test/data/scrolling_tests/frame_with_height_above_200.html +0 -26
  272. package/lib/test/data/scrolling_tests/frame_with_height_above_2000.html +0 -26
  273. package/lib/test/data/scrolling_tests/frame_with_nested_scrolling_frame.html +0 -11
  274. package/lib/test/data/scrolling_tests/frame_with_nested_scrolling_frame_out_of_view.html +0 -12
  275. package/lib/test/data/scrolling_tests/frame_with_small_height.html +0 -10
  276. package/lib/test/data/scrolling_tests/page_with_double_overflow_auto.html +0 -19
  277. package/lib/test/data/scrolling_tests/page_with_frame_out_of_view.html +0 -12
  278. package/lib/test/data/scrolling_tests/page_with_nested_scrolling_frames.html +0 -11
  279. package/lib/test/data/scrolling_tests/page_with_nested_scrolling_frames_out_of_view.html +0 -12
  280. package/lib/test/data/scrolling_tests/page_with_non_scrolling_frame.html +0 -11
  281. package/lib/test/data/scrolling_tests/page_with_scrolling_frame.html +0 -11
  282. package/lib/test/data/scrolling_tests/page_with_scrolling_frame_out_of_view.html +0 -12
  283. package/lib/test/data/scrolling_tests/page_with_tall_frame.html +0 -11
  284. package/lib/test/data/scrolling_tests/page_with_y_overflow_auto.html +0 -14
  285. package/lib/test/data/scrolling_tests/target_page.html +0 -9
  286. package/lib/test/data/selectPage.html +0 -58
  287. package/lib/test/data/selectableItems.html +0 -65
  288. package/lib/test/data/sessionCookie.html +0 -21
  289. package/lib/test/data/sessionCookieDest.html +0 -34
  290. package/lib/test/data/simple.xml +0 -5
  291. package/lib/test/data/simpleTest.html +0 -98
  292. package/lib/test/data/single_text_input.html +0 -12
  293. package/lib/test/data/slowLoadingAlert.html +0 -10
  294. package/lib/test/data/slowLoadingResourcePage.html +0 -12
  295. package/lib/test/data/slow_loading_iframes.html +0 -14
  296. package/lib/test/data/styledPage.html +0 -28
  297. package/lib/test/data/svgPiechart.xhtml +0 -81
  298. package/lib/test/data/svgTest.svg +0 -4
  299. package/lib/test/data/tables.html +0 -36
  300. package/lib/test/data/tinymce.html +0 -10
  301. package/lib/test/data/transformable.xml +0 -11
  302. package/lib/test/data/transformable.xsl +0 -37
  303. package/lib/test/data/transparentUpload.html +0 -70
  304. package/lib/test/data/underscore.html +0 -9
  305. package/lib/test/data/unicode_ltr.html +0 -8
  306. package/lib/test/data/upload.html +0 -45
  307. package/lib/test/data/userDefinedProperty.html +0 -8
  308. package/lib/test/data/veryLargeCanvas.html +0 -81
  309. package/lib/test/data/visibility-css.html +0 -21
  310. package/lib/test/data/win32frameset.html +0 -8
  311. package/lib/test/data/window_switching_tests/page_with_frame.html +0 -12
  312. package/lib/test/data/window_switching_tests/simple_page.html +0 -9
  313. package/lib/test/data/xhtmlFormPage.xhtml +0 -17
  314. package/lib/test/data/xhtmlTest.html +0 -76
  315. package/lib/test/fileserver.js +0 -321
  316. package/lib/test/httpserver.js +0 -120
  317. package/lib/test/index.js +0 -270
  318. package/lib/test/promise.js +0 -79
  319. package/lib/test/resources.js +0 -44
  320. package/opera.js +0 -405
  321. package/phantomjs.js +0 -282
  322. package/test/actions_test.js +0 -52
  323. package/test/chrome/options_test.js +0 -227
  324. package/test/chrome/service_test.js +0 -45
  325. package/test/cookie_test.js +0 -214
  326. package/test/element_finding_test.js +0 -426
  327. package/test/execute_script_test.js +0 -350
  328. package/test/fingerprint_test.js +0 -62
  329. package/test/firefox/extension_test.js +0 -120
  330. package/test/firefox/firefox_test.js +0 -261
  331. package/test/firefox/profile_test.js +0 -140
  332. package/test/http/http_test.js +0 -223
  333. package/test/http/util_test.js +0 -178
  334. package/test/io/io_test.js +0 -360
  335. package/test/io/zip_test.js +0 -128
  336. package/test/lib/by_test.js +0 -160
  337. package/test/lib/capabilities_test.js +0 -111
  338. package/test/lib/error_test.js +0 -305
  339. package/test/lib/events_test.js +0 -177
  340. package/test/lib/http_test.js +0 -696
  341. package/test/lib/logging_test.js +0 -272
  342. package/test/lib/promise_aplus_test.js +0 -78
  343. package/test/lib/promise_error_test.js +0 -884
  344. package/test/lib/promise_flow_test.js +0 -2288
  345. package/test/lib/promise_generator_test.js +0 -310
  346. package/test/lib/promise_test.js +0 -1109
  347. package/test/lib/testutil.js +0 -90
  348. package/test/lib/until_test.js +0 -478
  349. package/test/lib/webdriver_test.js +0 -2311
  350. package/test/logging_test.js +0 -167
  351. package/test/net/index_test.js +0 -60
  352. package/test/net/portprober_test.js +0 -128
  353. package/test/page_loading_test.js +0 -166
  354. package/test/phantomjs/execute_phantomjs_test.js +0 -59
  355. package/test/proxy_test.js +0 -180
  356. package/test/rect_test.js +0 -60
  357. package/test/remote_test.js +0 -117
  358. package/test/safari_test.js +0 -108
  359. package/test/session_test.js +0 -99
  360. package/test/stale_element_test.js +0 -63
  361. package/test/tag_name_test.js +0 -36
  362. package/test/testing/assert_test.js +0 -373
  363. package/test/testing/index_test.js +0 -224
  364. package/test/upload_test.js +0 -86
  365. package/test/window_test.js +0 -161
  366. package/testing/assert.js +0 -378
package/lib/webdriver.js CHANGED
@@ -21,16 +21,29 @@
21
21
 
22
22
  'use strict';
23
23
 
24
- const actions = require('./actions');
25
24
  const by = require('./by');
26
- const Capabilities = require('./capabilities').Capabilities;
27
25
  const command = require('./command');
28
26
  const error = require('./error');
29
27
  const input = require('./input');
30
28
  const logging = require('./logging');
31
- const {Session} = require('./session');
32
- const Symbols = require('./symbols');
33
29
  const promise = require('./promise');
30
+ const Symbols = require('./symbols');
31
+ const {Capabilities} = require('./capabilities');
32
+ const {Session} = require('./session');
33
+
34
+
35
+ // Capability names that are defined in the W3C spec.
36
+ const W3C_CAPABILITY_NAMES = new Set([
37
+ 'acceptInsecureCerts',
38
+ 'browserName',
39
+ 'browserVersion',
40
+ 'platformName',
41
+ 'pageLoadStrategy',
42
+ 'proxy',
43
+ 'setWindowRect',
44
+ 'timeouts',
45
+ 'unhandledPromptBehavior',
46
+ ]);
34
47
 
35
48
 
36
49
  /**
@@ -121,15 +134,8 @@ function executeCommand(executor, command) {
121
134
  * @return {!Promise<?>} A promise that will resolve to the input value's JSON
122
135
  * representation.
123
136
  */
124
- function toWireValue(obj) {
125
- if (promise.isPromise(obj)) {
126
- return Promise.resolve(obj).then(toWireValue);
127
- }
128
- return Promise.resolve(convertValue(obj));
129
- }
130
-
131
-
132
- function convertValue(value) {
137
+ async function toWireValue(obj) {
138
+ let value = await Promise.resolve(obj);
133
139
  if (value === void 0 || value === null) {
134
140
  return value;
135
141
  }
@@ -157,53 +163,33 @@ function convertValue(value) {
157
163
  }
158
164
 
159
165
 
160
- function convertKeys(obj) {
166
+ async function convertKeys(obj) {
161
167
  const isArray = Array.isArray(obj);
162
168
  const numKeys = isArray ? obj.length : Object.keys(obj).length;
163
169
  const ret = isArray ? new Array(numKeys) : {};
164
170
  if (!numKeys) {
165
- return Promise.resolve(ret);
171
+ return ret;
166
172
  }
167
173
 
168
174
  let numResolved = 0;
169
175
 
170
- function forEachKey(obj, fn) {
176
+ async function forEachKey(obj, fn) {
171
177
  if (Array.isArray(obj)) {
172
178
  for (let i = 0, n = obj.length; i < n; i++) {
173
- fn(obj[i], i);
179
+ await fn(obj[i], i);
174
180
  }
175
181
  } else {
176
182
  for (let key in obj) {
177
- fn(obj[key], key);
183
+ await fn(obj[key], key);
178
184
  }
179
185
  }
180
186
  }
181
187
 
182
- return new Promise(function(done, reject) {
183
- forEachKey(obj, function(value, key) {
184
- if (promise.isPromise(value)) {
185
- value.then(toWireValue).then(setValue, reject);
186
- } else {
187
- value = convertValue(value);
188
- if (promise.isPromise(value)) {
189
- value.then(toWireValue).then(setValue, reject);
190
- } else {
191
- setValue(value);
192
- }
193
- }
194
-
195
- function setValue(value) {
196
- ret[key] = value;
197
- maybeFulfill();
198
- }
199
- });
200
-
201
- function maybeFulfill() {
202
- if (++numResolved === numKeys) {
203
- done(ret);
204
- }
205
- }
188
+ await forEachKey(obj, async function(value, key) {
189
+ ret[key] = await toWireValue(value);
206
190
  });
191
+
192
+ return ret;
207
193
  }
208
194
 
209
195
 
@@ -243,25 +229,21 @@ function fromWireValue(driver, value) {
243
229
  */
244
230
  class IWebDriver {
245
231
 
246
- /** @return {!promise.ControlFlow} The control flow used by this instance. */
247
- controlFlow() {}
248
-
249
232
  /**
250
- * Schedules a {@link command.Command} to be executed by this driver's
233
+ * Executes the provided {@link command.Command} using this driver's
251
234
  * {@link command.Executor}.
252
235
  *
253
236
  * @param {!command.Command} command The command to schedule.
254
- * @param {string} description A description of the command for debugging.
255
- * @return {!promise.Thenable<T>} A promise that will be resolved
256
- * with the command result.
237
+ * @return {!Promise<T>} A promise that will be resolved with the command
238
+ * result.
257
239
  * @template T
258
240
  */
259
- schedule(command, description) {}
241
+ execute(command) {}
260
242
 
261
243
  /**
262
244
  * Sets the {@linkplain input.FileDetector file detector} that should be
263
245
  * used with this instance.
264
- * @param {input.FileDetector} detector The detector to use or {@code null}.
246
+ * @param {input.FileDetector} detector The detector to use or `null`.
265
247
  */
266
248
  setFileDetector(detector) {}
267
249
 
@@ -271,12 +253,12 @@ class IWebDriver {
271
253
  getExecutor() {}
272
254
 
273
255
  /**
274
- * @return {!promise.Thenable<!Session>} A promise for this client's session.
256
+ * @return {!Promise<!Session>} A promise for this client's session.
275
257
  */
276
258
  getSession() {}
277
259
 
278
260
  /**
279
- * @return {!promise.Thenable<!Capabilities>} A promise that will resolve with
261
+ * @return {!Promise<!Capabilities>} A promise that will resolve with
280
262
  * the this instance's capabilities.
281
263
  */
282
264
  getCapabilities() {}
@@ -286,56 +268,40 @@ class IWebDriver {
286
268
  * invalidated and may no longer be used to issue commands against the
287
269
  * browser.
288
270
  *
289
- * @return {!promise.Thenable<void>} A promise that will be resolved when the
271
+ * @return {!Promise<void>} A promise that will be resolved when the
290
272
  * command has completed.
291
273
  */
292
274
  quit() {}
293
275
 
294
276
  /**
295
277
  * Creates a new action sequence using this driver. The sequence will not be
296
- * scheduled for execution until {@link actions.ActionSequence#perform} is
297
- * called. Example:
278
+ * submitted for execution until
279
+ * {@link ./input.Actions#perform Actions.perform()} is called.
298
280
  *
299
- * driver.actions().
300
- * mouseDown(element1).
301
- * mouseMove(element2).
302
- * mouseUp().
303
- * perform();
304
- *
305
- * @return {!actions.ActionSequence} A new action sequence for this instance.
281
+ * @param {{async: (boolean|undefined),
282
+ * bridge: (boolean|undefined)}=} options Configuration options for
283
+ * the action sequence (see {@link ./input.Actions Actions} documentation
284
+ * for details).
285
+ * @return {!input.Actions} A new action sequence for this instance.
306
286
  */
307
- actions() {}
287
+ actions(options) {}
308
288
 
309
289
  /**
310
- * Creates a new touch sequence using this driver. The sequence will not be
311
- * scheduled for execution until {@link actions.TouchSequence#perform} is
312
- * called. Example:
313
- *
314
- * driver.touchActions().
315
- * tap(element1).
316
- * doubleTap(element2).
317
- * perform();
318
- *
319
- * @return {!actions.TouchSequence} A new touch sequence for this instance.
320
- */
321
- touchActions() {}
322
-
323
- /**
324
- * Schedules a command to execute JavaScript in the context of the currently
325
- * selected frame or window. The script fragment will be executed as the body
326
- * of an anonymous function. If the script is provided as a function object,
327
- * that function will be converted to a string for injection into the target
290
+ * Executes a snippet of JavaScript in the context of the currently selected
291
+ * frame or window. The script fragment will be executed as the body of an
292
+ * anonymous function. If the script is provided as a function object, that
293
+ * function will be converted to a string for injection into the target
328
294
  * window.
329
295
  *
330
296
  * Any arguments provided in addition to the script will be included as script
331
- * arguments and may be referenced using the {@code arguments} object.
332
- * Arguments may be a boolean, number, string, or {@linkplain WebElement}.
333
- * Arrays and objects may also be used as script arguments as long as each item
334
- * adheres to the types previously mentioned.
297
+ * arguments and may be referenced using the `arguments` object. Arguments may
298
+ * be a boolean, number, string, or {@linkplain WebElement}. Arrays and
299
+ * objects may also be used as script arguments as long as each item adheres
300
+ * to the types previously mentioned.
335
301
  *
336
302
  * The script may refer to any variables accessible from the current window.
337
303
  * Furthermore, the script will execute in the window's context, thus
338
- * {@code document} may be used to refer to the current document. Any local
304
+ * `document` may be used to refer to the current document. Any local
339
305
  * variables will not be available once the script has finished executing,
340
306
  * though global variables will persist.
341
307
  *
@@ -351,36 +317,35 @@ class IWebDriver {
351
317
  * the rules above
352
318
  *
353
319
  * @param {!(string|Function)} script The script to execute.
354
- * @param {...*} var_args The arguments to pass to the script.
355
- * @return {!promise.Thenable<T>} A promise that will resolve to the
320
+ * @param {...*} args The arguments to pass to the script.
321
+ * @return {!IThenable<T>} A promise that will resolve to the
356
322
  * scripts return value.
357
323
  * @template T
358
324
  */
359
- executeScript(script, var_args) {}
325
+ executeScript(script, ...args) {}
360
326
 
361
327
  /**
362
- * Schedules a command to execute asynchronous JavaScript in the context of the
328
+ * Executes a snippet of asynchronous JavaScript in the context of the
363
329
  * currently selected frame or window. The script fragment will be executed as
364
330
  * the body of an anonymous function. If the script is provided as a function
365
331
  * object, that function will be converted to a string for injection into the
366
332
  * target window.
367
333
  *
368
334
  * Any arguments provided in addition to the script will be included as script
369
- * arguments and may be referenced using the {@code arguments} object.
370
- * Arguments may be a boolean, number, string, or {@code WebElement}.
371
- * Arrays and objects may also be used as script arguments as long as each item
372
- * adheres to the types previously mentioned.
335
+ * arguments and may be referenced using the `arguments` object. Arguments may
336
+ * be a boolean, number, string, or {@linkplain WebElement}. Arrays and
337
+ * objects may also be used as script arguments as long as each item adheres
338
+ * to the types previously mentioned.
373
339
  *
374
340
  * Unlike executing synchronous JavaScript with {@link #executeScript},
375
- * scripts executed with this function must explicitly signal they are finished
376
- * by invoking the provided callback. This callback will always be injected
377
- * into the executed function as the last argument, and thus may be referenced
378
- * with {@code arguments[arguments.length - 1]}. The following steps will be
379
- * taken for resolving this functions return value against the first argument
380
- * to the script's callback function:
381
- *
382
- * - For a HTML element, the value will resolve to a
383
- * {@link WebElement}
341
+ * scripts executed with this function must explicitly signal they are
342
+ * finished by invoking the provided callback. This callback will always be
343
+ * injected into the executed function as the last argument, and thus may be
344
+ * referenced with `arguments[arguments.length - 1]`. The following steps
345
+ * will be taken for resolving this functions return value against the first
346
+ * argument to the script's callback function:
347
+ *
348
+ * - For a HTML element, the value will resolve to a {@link WebElement}
384
349
  * - Null and undefined return values will resolve to null
385
350
  * - Booleans, numbers, and strings will resolve as is
386
351
  * - Functions will resolve to their string representation
@@ -410,9 +375,9 @@ class IWebDriver {
410
375
  *
411
376
  * __Example #3:__ Injecting a XMLHttpRequest and waiting for the result. In
412
377
  * this example, the inject script is specified with a function literal. When
413
- * using this format, the function is converted to a string for injection, so it
414
- * should not reference any symbols not defined in the scope of the page under
415
- * test.
378
+ * using this format, the function is converted to a string for injection, so
379
+ * it should not reference any symbols not defined in the scope of the page
380
+ * under test.
416
381
  *
417
382
  * driver.executeAsyncScript(function() {
418
383
  * var callback = arguments[arguments.length - 1];
@@ -429,70 +394,50 @@ class IWebDriver {
429
394
  * });
430
395
  *
431
396
  * @param {!(string|Function)} script The script to execute.
432
- * @param {...*} var_args The arguments to pass to the script.
433
- * @return {!promise.Thenable<T>} A promise that will resolve to the
434
- * scripts return value.
435
- * @template T
436
- */
437
- executeAsyncScript(script, var_args) {}
438
-
439
- /**
440
- * Schedules a command to execute a custom function.
441
- * @param {function(...): (T|IThenable<T>)} fn The function to execute.
442
- * @param {Object=} opt_scope The object in whose scope to execute the function.
443
- * @param {...*} var_args Any arguments to pass to the function.
444
- * @return {!promise.Thenable<T>} A promise that will be resolved'
445
- * with the function's result.
397
+ * @param {...*} args The arguments to pass to the script.
398
+ * @return {!IThenable<T>} A promise that will resolve to the scripts return
399
+ * value.
446
400
  * @template T
447
401
  */
448
- call(fn, opt_scope, var_args) {}
402
+ executeAsyncScript(script, ...args) {}
449
403
 
450
404
  /**
451
- * Schedules a command to wait for a condition to hold. The condition may be
405
+ * Waits for a condition to evaluate to a "truthy" value. The condition may be
452
406
  * specified by a {@link Condition}, as a custom function, or as any
453
407
  * promise-like thenable.
454
408
  *
455
409
  * For a {@link Condition} or function, the wait will repeatedly
456
410
  * evaluate the condition until it returns a truthy value. If any errors occur
457
411
  * while evaluating the condition, they will be allowed to propagate. In the
458
- * event a condition returns a {@link promise.Promise promise}, the polling
459
- * loop will wait for it to be resolved and use the resolved value for whether
460
- * the condition has been satisfied. Note the resolution time for a promise
461
- * is factored into whether a wait has timed out.
412
+ * event a condition returns a {@linkplain Promise}, the polling loop will
413
+ * wait for it to be resolved and use the resolved value for whether the
414
+ * condition has been satisfied. The resolution time for a promise is always
415
+ * factored into whether a wait has timed out.
462
416
  *
463
- * Note, if the provided condition is a {@link WebElementCondition}, then
417
+ * If the provided condition is a {@link WebElementCondition}, then
464
418
  * the wait will return a {@link WebElementPromise} that will resolve to the
465
419
  * element that satisfied the condition.
466
420
  *
467
421
  * _Example:_ waiting up to 10 seconds for an element to be present on the
468
422
  * page.
469
423
  *
470
- * var button = driver.wait(until.elementLocated(By.id('foo')), 10000);
471
- * button.click();
472
- *
473
- * This function may also be used to block the command flow on the resolution
474
- * of any thenable promise object. When given a promise, the command will
475
- * simply wait for its resolution before completing. A timeout may be provided
476
- * to fail the command if the promise does not resolve before the timeout
477
- * expires.
478
- *
479
- * _Example:_ Suppose you have a function, `startTestServer`, that returns a
480
- * promise for when a server is ready for requests. You can block a WebDriver
481
- * client on this promise with:
482
- *
483
- * var started = startTestServer();
484
- * driver.wait(started, 5 * 1000, 'Server should start within 5 seconds');
485
- * driver.get(getServerUrl());
424
+ * async function example() {
425
+ * let button =
426
+ * await driver.wait(until.elementLocated(By.id('foo')), 10000);
427
+ * await button.click();
428
+ * }
486
429
  *
487
430
  * @param {!(IThenable<T>|
488
431
  * Condition<T>|
489
432
  * function(!WebDriver): T)} condition The condition to
490
433
  * wait on, defined as a promise, condition object, or a function to
491
434
  * evaluate as a condition.
492
- * @param {number=} opt_timeout How long to wait for the condition to be true.
493
- * @param {string=} opt_message An optional message to use if the wait times
494
- * out.
495
- * @return {!(promise.Thenable<T>|WebElementPromise)} A promise that will be
435
+ * @param {number=} timeout The duration in milliseconds, how long to wait
436
+ * for the condition to be true.
437
+ * @param {string=} message An optional message to use if the wait times out.
438
+ * @param {number=} pollTimeout The duration in milliseconds, how long to
439
+ * wait between polling the condition.
440
+ * @return {!(IThenable<T>|WebElementPromise)} A promise that will be
496
441
  * resolved with the first truthy value returned by the condition
497
442
  * function, or rejected if the condition times out. If the input
498
443
  * input condition is an instance of a {@link WebElementCondition},
@@ -500,76 +445,82 @@ class IWebDriver {
500
445
  * @throws {TypeError} if the provided `condition` is not a valid type.
501
446
  * @template T
502
447
  */
503
- wait(condition, opt_timeout, opt_message) {}
448
+ wait(condition, timeout = undefined, message = undefined, pollTimeout = undefined) {}
504
449
 
505
450
  /**
506
- * Schedules a command to make the driver sleep for the given amount of time.
451
+ * Makes the driver sleep for the given amount of time.
452
+ *
507
453
  * @param {number} ms The amount of time, in milliseconds, to sleep.
508
- * @return {!promise.Thenable<void>} A promise that will be resolved
509
- * when the sleep has finished.
454
+ * @return {!Promise<void>} A promise that will be resolved when the sleep has
455
+ * finished.
510
456
  */
511
457
  sleep(ms) {}
512
458
 
513
459
  /**
514
- * Schedules a command to retrieve the current window handle.
515
- * @return {!promise.Thenable<string>} A promise that will be
516
- * resolved with the current window handle.
460
+ * Retrieves the current window handle.
461
+ *
462
+ * @return {!Promise<string>} A promise that will be resolved with the current
463
+ * window handle.
517
464
  */
518
465
  getWindowHandle() {}
519
466
 
520
467
  /**
521
- * Schedules a command to retrieve the current list of available window handles.
522
- * @return {!promise.Thenable<!Array<string>>} A promise that will
523
- * be resolved with an array of window handles.
468
+ * Retrieves a list of all available window handles.
469
+ *
470
+ * @return {!Promise<!Array<string>>} A promise that will be resolved with an
471
+ * array of window handles.
524
472
  */
525
473
  getAllWindowHandles() {}
526
474
 
527
475
  /**
528
- * Schedules a command to retrieve the current page's source. The page source
529
- * returned is a representation of the underlying DOM: do not expect it to be
530
- * formatted or escaped in the same way as the response sent from the web
531
- * server.
532
- * @return {!promise.Thenable<string>} A promise that will be
533
- * resolved with the current page source.
476
+ * Retrieves the current page's source. The returned souce is a representation
477
+ * of the underlying DOM: do not expect it to be formatted or escaped in the
478
+ * same way as the raw response sent from the web server.
479
+ *
480
+ * @return {!Promise<string>} A promise that will be resolved with the current
481
+ * page source.
534
482
  */
535
483
  getPageSource() {}
536
484
 
537
485
  /**
538
- * Schedules a command to close the current window.
539
- * @return {!promise.Thenable<void>} A promise that will be resolved
540
- * when this command has completed.
486
+ * Closes the current window.
487
+ *
488
+ * @return {!Promise<void>} A promise that will be resolved when this command
489
+ * has completed.
541
490
  */
542
491
  close() {}
543
492
 
544
493
  /**
545
- * Schedules a command to navigate to the given URL.
494
+ * Navigates to the given URL.
495
+ *
546
496
  * @param {string} url The fully qualified URL to open.
547
- * @return {!promise.Thenable<void>} A promise that will be resolved
548
- * when the document has finished loading.
497
+ * @return {!Promise<void>} A promise that will be resolved when the document
498
+ * has finished loading.
549
499
  */
550
500
  get(url) {}
551
501
 
552
502
  /**
553
- * Schedules a command to retrieve the URL of the current page.
554
- * @return {!promise.Thenable<string>} A promise that will be
555
- * resolved with the current URL.
503
+ * Retrieves the URL for the current page.
504
+ *
505
+ * @return {!Promise<string>} A promise that will be resolved with the
506
+ * current URL.
556
507
  */
557
508
  getCurrentUrl() {}
558
509
 
559
510
  /**
560
- * Schedules a command to retrieve the current page's title.
561
- * @return {!promise.Thenable<string>} A promise that will be
562
- * resolved with the current page's title.
511
+ * Retrieves the current page title.
512
+ *
513
+ * @return {!Promise<string>} A promise that will be resolved with the current
514
+ * page's title.
563
515
  */
564
516
  getTitle() {}
565
517
 
566
518
  /**
567
- * Schedule a command to find an element on the page. If the element cannot be
568
- * found, a {@link bot.ErrorCode.NO_SUCH_ELEMENT} result will be returned
569
- * by the driver. Unlike other commands, this error cannot be suppressed. In
570
- * other words, scheduling a command to find an element doubles as an assert
571
- * that the element is present on the page. To test whether an element is
572
- * present on the page, use {@link #findElements}:
519
+ * Locates an element on the page. If the element cannot be found, a
520
+ * {@link error.NoSuchEementError} will be returned by the driver.
521
+ *
522
+ * This function should not be used to test whether an element is present on
523
+ * the page. Rather, you should use {@link #findElements}:
573
524
  *
574
525
  * driver.findElements(By.id('foo'))
575
526
  * .then(found => console.log('Element found? %s', !!found.length));
@@ -605,16 +556,17 @@ class IWebDriver {
605
556
  findElement(locator) {}
606
557
 
607
558
  /**
608
- * Schedule a command to search for multiple elements on the page.
559
+ * Search for multiple elements on the page. Refer to the documentation on
560
+ * {@link #findElement(by)} for information on element locator strategies.
609
561
  *
610
562
  * @param {!(by.By|Function)} locator The locator to use.
611
- * @return {!promise.Thenable<!Array<!WebElement>>} A
612
- * promise that will resolve to an array of WebElements.
563
+ * @return {!Promise<!Array<!WebElement>>} A promise that will resolve to an
564
+ * array of WebElements.
613
565
  */
614
566
  findElements(locator) {}
615
567
 
616
568
  /**
617
- * Schedule a command to take a screenshot. The driver makes a best effort to
569
+ * Takes a screenshot of the current page. The driver makes a best effort to
618
570
  * return a screenshot of the following, in order of preference:
619
571
  *
620
572
  * 1. Entire page
@@ -622,8 +574,8 @@ class IWebDriver {
622
574
  * 3. Visible portion of the current frame
623
575
  * 4. The entire display containing the browser
624
576
  *
625
- * @return {!promise.Thenable<string>} A promise that will be
626
- * resolved to the screenshot as a base-64 encoded PNG.
577
+ * @return {!Promise<string>} A promise that will be resolved to the
578
+ * screenshot as a base-64 encoded PNG.
627
579
  */
628
580
  takeScreenshot() {}
629
581
 
@@ -645,6 +597,23 @@ class IWebDriver {
645
597
  }
646
598
 
647
599
 
600
+ /**
601
+ * @param {!Capabilities} capabilities A capabilities object.
602
+ * @return {!Capabilities} A copy of the parameter capabilities, omitting
603
+ * capability names that are not valid W3C names.
604
+ */
605
+ function filterNonW3CCaps(capabilities) {
606
+ let newCaps = new Capabilities(capabilities);
607
+ for (let k of newCaps.keys()) {
608
+ // Any key containing a colon is a vendor-prefixed capability.
609
+ if (!(W3C_CAPABILITY_NAMES.has(k) || k.indexOf(':') >= 0)) {
610
+ newCaps.delete(k);
611
+ }
612
+ }
613
+ return newCaps;
614
+ }
615
+
616
+
648
617
  /**
649
618
  * Each WebDriver instance provides automated control over a browser session.
650
619
  *
@@ -652,21 +621,21 @@ class IWebDriver {
652
621
  */
653
622
  class WebDriver {
654
623
  /**
655
- * @param {!(Session|IThenable<!Session>)} session Either a known session or a
656
- * promise that will be resolved to a session.
624
+ * @param {!(./session.Session|IThenable<!./session.Session>)} session Either
625
+ * a known session or a promise that will be resolved to a session.
657
626
  * @param {!command.Executor} executor The executor to use when sending
658
627
  * commands to the browser.
659
- * @param {promise.ControlFlow=} opt_flow The flow to
660
- * schedule commands through. Defaults to the active flow object.
661
- * @param {(function(this: void): ?)=} opt_onQuit A function to call, if any,
628
+ * @param {(function(this: void): ?)=} onQuit A function to call, if any,
662
629
  * when the session is terminated.
663
630
  */
664
- constructor(session, executor, opt_flow, opt_onQuit) {
665
- /** @private {!promise.ControlFlow} */
666
- this.flow_ = opt_flow || promise.controlFlow();
631
+ constructor(session, executor, onQuit = undefined) {
632
+ /** @private {!Promise<!Session>} */
633
+ this.session_ = Promise.resolve(session);
667
634
 
668
- /** @private {!promise.Thenable<!Session>} */
669
- this.session_ = this.flow_.promise(resolve => resolve(session));
635
+ // If session is a rejected promise, add a no-op rejection handler.
636
+ // This effectively hides setup errors until users attempt to interact
637
+ // with the session.
638
+ this.session_.catch(function() {});
670
639
 
671
640
  /** @private {!command.Executor} */
672
641
  this.executor_ = executor;
@@ -675,63 +644,17 @@ class WebDriver {
675
644
  this.fileDetector_ = null;
676
645
 
677
646
  /** @private @const {(function(this: void): ?|undefined)} */
678
- this.onQuit_ = opt_onQuit;
679
- }
680
-
681
- /**
682
- * Creates a new WebDriver client for an existing session.
683
- * @param {!command.Executor} executor Command executor to use when querying
684
- * for session details.
685
- * @param {string} sessionId ID of the session to attach to.
686
- * @param {promise.ControlFlow=} opt_flow The control flow all
687
- * driver commands should execute under. Defaults to the
688
- * {@link promise.controlFlow() currently active} control flow.
689
- * @return {!WebDriver} A new client for the specified session.
690
- */
691
- static attachToSession(executor, sessionId, opt_flow) {
692
- let flow = opt_flow || promise.controlFlow();
693
- let cmd = new command.Command(command.Name.DESCRIBE_SESSION)
694
- .setParameter('sessionId', sessionId);
695
- let session = flow.execute(
696
- () => executeCommand(executor, cmd).catch(err => {
697
- // The DESCRIBE_SESSION command is not supported by the W3C spec, so
698
- // if we get back an unknown command, just return a session with
699
- // unknown capabilities.
700
- if (err instanceof error.UnknownCommandError) {
701
- return new Session(sessionId, new Capabilities);
702
- }
703
- throw err;
704
- }),
705
- 'WebDriver.attachToSession()');
706
- return new WebDriver(session, executor, flow);
647
+ this.onQuit_ = onQuit;
707
648
  }
708
649
 
709
650
  /**
710
651
  * Creates a new WebDriver session.
711
652
  *
712
- * By default, the requested session `capabilities` are merely "desired" and
713
- * the remote end will still create a new session even if it cannot satisfy
714
- * all of the requested capabilities. You can query which capabilities a
715
- * session actually has using the
716
- * {@linkplain #getCapabilities() getCapabilities()} method on the returned
717
- * WebDriver instance.
718
- *
719
- * To define _required capabilities_, provide the `capabilities` as an object
720
- * literal with `required` and `desired` keys. The `desired` key may be
721
- * omitted if all capabilities are required, and vice versa. If the server
722
- * cannot create a session with all of the required capabilities, it will
723
- * return an {@linkplain error.SessionNotCreatedError}.
724
- *
725
- * let required = new Capabilities().set('browserName', 'firefox');
726
- * let desired = new Capabilities().set('version', '45');
727
- * let driver = WebDriver.createSession(executor, {required, desired});
728
- *
729
653
  * This function will always return a WebDriver instance. If there is an error
730
654
  * creating the session, such as the aforementioned SessionNotCreatedError,
731
655
  * the driver will have a rejected {@linkplain #getSession session} promise.
732
- * It is recommended that this promise is left _unhandled_ so it will
733
- * propagate through the {@linkplain promise.ControlFlow control flow} and
734
- * cause subsequent commands to fail.
656
+ * This rejection will propagate through any subsequent commands scheduled
657
+ * on the returned WebDriver instance.
735
658
  *
736
659
  * let required = Capabilities.firefox();
737
660
  * let driver = WebDriver.createSession(executor, {required});
@@ -742,77 +665,39 @@ class WebDriver {
742
665
  *
743
666
  * @param {!command.Executor} executor The executor to create the new session
744
667
  * with.
745
- * @param {(!Capabilities|
746
- * {desired: (Capabilities|undefined),
747
- * required: (Capabilities|undefined)})} capabilities The desired
748
- * capabilities for the new session.
749
- * @param {promise.ControlFlow=} opt_flow The control flow all driver
750
- * commands should execute under, including the initial session creation.
751
- * Defaults to the {@link promise.controlFlow() currently active}
752
- * control flow.
753
- * @param {(function(this: void): ?)=} opt_onQuit A callback to invoke when
668
+ * @param {!Capabilities} capabilities The desired capabilities for the new
669
+ * session.
670
+ * @param {(function(this: void): ?)=} onQuit A callback to invoke when
754
671
  * the newly created session is terminated. This should be used to clean
755
672
  * up any resources associated with the session.
756
673
  * @return {!WebDriver} The driver for the newly created session.
757
674
  */
758
- static createSession(executor, capabilities, opt_flow, opt_onQuit) {
759
- let flow = opt_flow || promise.controlFlow();
675
+ static createSession(executor, capabilities, onQuit = undefined) {
760
676
  let cmd = new command.Command(command.Name.NEW_SESSION);
761
677
 
762
- if (capabilities && (capabilities.desired || capabilities.required)) {
763
- cmd.setParameter('desiredCapabilities', capabilities.desired);
764
- cmd.setParameter('requiredCapabilities', capabilities.required);
765
- } else {
766
- cmd.setParameter('desiredCapabilities', capabilities);
767
- }
678
+ // For OSS remote ends.
679
+ cmd.setParameter('desiredCapabilities', capabilities);
680
+ // For W3C remote ends.
681
+ cmd.setParameter('capabilities', {
682
+ alwaysMatch: filterNonW3CCaps(capabilities),
683
+ });
768
684
 
769
- let session = flow.execute(
770
- () => executeCommand(executor, cmd),
771
- 'WebDriver.createSession()');
772
- if (typeof opt_onQuit === 'function') {
685
+ let session = executeCommand(executor, cmd);
686
+ if (typeof onQuit === 'function') {
773
687
  session = session.catch(err => {
774
- return Promise.resolve(opt_onQuit.call(void 0)).then(_ => {throw err;});
688
+ return Promise.resolve(onQuit.call(void 0)).then(_ => {throw err;});
775
689
  });
776
690
  }
777
- return new this(session, executor, flow, opt_onQuit);
778
- }
779
-
780
- /** @override */
781
- controlFlow() {
782
- return this.flow_;
691
+ return new this(session, executor, onQuit);
783
692
  }
784
693
 
785
694
  /** @override */
786
- schedule(command, description) {
695
+ async execute(command) {
787
696
  command.setParameter('sessionId', this.session_);
788
-
789
- // If any of the command parameters are rejected promises, those
790
- // rejections may be reported as unhandled before the control flow
791
- // attempts to execute the command. To ensure parameters errors
792
- // propagate through the command itself, we resolve all of the
793
- // command parameters now, but suppress any errors until the ControlFlow
794
- // actually executes the command. This addresses scenarios like catching
795
- // an element not found error in:
796
- //
797
- // driver.findElement(By.id('foo')).click().catch(function(e) {
798
- // if (e instanceof NoSuchElementError) {
799
- // // Do something.
800
- // }
801
- // });
802
- var prepCommand = toWireValue(command.getParameters());
803
- prepCommand.catch(function() {});
804
-
805
- var flow = this.flow_;
806
- var executor = this.executor_;
807
- return flow.execute(() => {
808
- // Retrieve resolved command parameters; any previously suppressed errors
809
- // will now propagate up through the control flow as part of the command
810
- // execution.
811
- return prepCommand.then(function(parameters) {
812
- command.setParameters(parameters);
813
- return executor.execute(command);
814
- }).then(value => fromWireValue(this, value));
815
- }, description);
697
+ let parameters = await toWireValue(command.getParameters());
698
+ command.setParameters(parameters);
699
+ let value = await this.executor_.execute(command);
700
+ return fromWireValue(this, value);
816
701
  }
817
702
 
818
703
  /** @override */
@@ -837,17 +722,13 @@ class WebDriver {
837
722
 
838
723
  /** @override */
839
724
  quit() {
840
- var result = this.schedule(
841
- new command.Command(command.Name.QUIT),
842
- 'WebDriver.quit()');
725
+ let result = this.execute(new command.Command(command.Name.QUIT));
843
726
  // Delete our session ID when the quit command finishes; this will allow us
844
727
  // to throw an error when attempting to use a driver post-quit.
845
- return /** @type {!promise.Thenable} */(promise.finally(result, () => {
846
- this.session_ = this.flow_.promise((_, reject) => {
847
- reject(new error.NoSuchSessionError(
728
+ return promise.finally(result, () => {
729
+ this.session_ = Promise.reject(new error.NoSuchSessionError(
848
730
  'This driver instance does not have a valid session ID ' +
849
731
  '(did you call WebDriver.quit()?) and may no longer be used.'));
850
- });
851
732
 
852
733
  // Only want the session rejection to bubble if accessed.
853
734
  this.session_.catch(function() {});
@@ -855,70 +736,77 @@ class WebDriver {
855
736
  if (this.onQuit_) {
856
737
  return this.onQuit_.call(void 0);
857
738
  }
858
- }));
859
- }
860
-
861
- /** @override */
862
- actions() {
863
- return new actions.ActionSequence(this);
739
+ });
864
740
  }
865
741
 
866
742
  /** @override */
867
- touchActions() {
868
- return new actions.TouchSequence(this);
743
+ actions(options) {
744
+ return new input.Actions(this, options || undefined);
869
745
  }
870
746
 
871
747
  /** @override */
872
- executeScript(script, var_args) {
748
+ executeScript(script, ...args) {
873
749
  if (typeof script === 'function') {
874
750
  script = 'return (' + script + ').apply(null, arguments);';
875
751
  }
876
- let args =
877
- arguments.length > 1 ? Array.prototype.slice.call(arguments, 1) : [];
878
- return this.schedule(
752
+ return this.execute(
879
753
  new command.Command(command.Name.EXECUTE_SCRIPT).
880
754
  setParameter('script', script).
881
- setParameter('args', args),
882
- 'WebDriver.executeScript()');
755
+ setParameter('args', args));
883
756
  }
884
757
 
885
758
  /** @override */
886
- executeAsyncScript(script, var_args) {
759
+ executeAsyncScript(script, ...args) {
887
760
  if (typeof script === 'function') {
888
761
  script = 'return (' + script + ').apply(null, arguments);';
889
762
  }
890
- let args = Array.prototype.slice.call(arguments, 1);
891
- return this.schedule(
763
+ return this.execute(
892
764
  new command.Command(command.Name.EXECUTE_ASYNC_SCRIPT).
893
765
  setParameter('script', script).
894
- setParameter('args', args),
895
- 'WebDriver.executeScript()');
766
+ setParameter('args', args));
896
767
  }
897
768
 
898
769
  /** @override */
899
- call(fn, opt_scope, var_args) {
900
- let args = Array.prototype.slice.call(arguments, 2);
901
- return this.flow_.execute(function() {
902
- return promise.fullyResolved(args).then(function(args) {
903
- if (promise.isGenerator(fn)) {
904
- args.unshift(fn, opt_scope);
905
- return promise.consume.apply(null, args);
906
- }
907
- return fn.apply(opt_scope, args);
908
- });
909
- }, 'WebDriver.call(' + (fn.name || 'function') + ')');
910
- }
770
+ wait(condition, timeout = 0, message = undefined, pollTimeout = 200) {
771
+ if (typeof timeout !== 'number' || timeout < 0) {
772
+ throw TypeError('timeout must be a number >= 0: ' + timeout);
773
+ }
774
+
775
+ if (typeof pollTimeout !== 'number' || pollTimeout < 0) {
776
+ throw TypeError('pollTimeout must be a number >= 0: ' + pollTimeout);
777
+ }
911
778
 
912
- /** @override */
913
- wait(condition, opt_timeout, opt_message) {
914
779
  if (promise.isPromise(condition)) {
915
- return this.flow_.wait(
916
- /** @type {!IThenable} */(condition),
917
- opt_timeout, opt_message);
780
+ return new Promise((resolve, reject) => {
781
+ if (!timeout) {
782
+ resolve(condition);
783
+ return;
784
+ }
785
+
786
+ let start = Date.now();
787
+ let timer = setTimeout(function() {
788
+ timer = null;
789
+ reject(
790
+ new error.TimeoutError(
791
+ (message ? `${message}\n` : '')
792
+ + 'Timed out waiting for promise to resolve after '
793
+ + (Date.now() - start) + 'ms'));
794
+ }, timeout);
795
+ const clearTimer = () => timer && clearTimeout(timer);
796
+
797
+ /** @type {!IThenable} */(condition).then(
798
+ function(value) {
799
+ clearTimer();
800
+ resolve(value);
801
+ },
802
+ function(error) {
803
+ clearTimer();
804
+ reject(error);
805
+ });
806
+ });
918
807
  }
919
808
 
920
- var message = opt_message;
921
- var fn = /** @type {!Function} */(condition);
809
+ let fn = /** @type {!Function} */(condition);
922
810
  if (condition instanceof Condition) {
923
811
  message = message || condition.description();
924
812
  fn = condition.fn;
@@ -930,13 +818,36 @@ class WebDriver {
930
818
  + 'Condition object');
931
819
  }
932
820
 
933
- var driver = this;
934
- var result = this.flow_.wait(function() {
935
- if (promise.isGenerator(fn)) {
936
- return promise.consume(fn, null, [driver]);
937
- }
938
- return fn(driver);
939
- }, opt_timeout, message);
821
+ const driver = this;
822
+ function evaluateCondition() {
823
+ return new Promise((resolve, reject) => {
824
+ try {
825
+ resolve(fn(driver));
826
+ } catch (ex) {
827
+ reject(ex);
828
+ }
829
+ });
830
+ }
831
+
832
+ let result = new Promise((resolve, reject) => {
833
+ const startTime = Date.now();
834
+ const pollCondition = async () => {
835
+ evaluateCondition().then(function(value) {
836
+ const elapsed = Date.now() - startTime;
837
+ if (!!value) {
838
+ resolve(value);
839
+ } else if (timeout && elapsed >= timeout) {
840
+ reject(
841
+ new error.TimeoutError(
842
+ (message ? `${message}\n` : '')
843
+ + `Wait timed out after ${elapsed}ms`));
844
+ } else {
845
+ setTimeout(pollCondition, pollTimeout);
846
+ }
847
+ }, reject);
848
+ };
849
+ pollCondition();
850
+ });
940
851
 
941
852
  if (condition instanceof WebElementCondition) {
942
853
  result = new WebElementPromise(this, result.then(function(value) {
@@ -953,34 +864,30 @@ class WebDriver {
953
864
 
954
865
  /** @override */
955
866
  sleep(ms) {
956
- return this.flow_.timeout(ms, 'WebDriver.sleep(' + ms + ')');
867
+ return new Promise(resolve => setTimeout(() => resolve(), ms));
957
868
  }
958
869
 
959
870
  /** @override */
960
871
  getWindowHandle() {
961
- return this.schedule(
962
- new command.Command(command.Name.GET_CURRENT_WINDOW_HANDLE),
963
- 'WebDriver.getWindowHandle()');
872
+ return this.execute(
873
+ new command.Command(command.Name.GET_CURRENT_WINDOW_HANDLE));
964
874
  }
965
875
 
966
876
  /** @override */
967
877
  getAllWindowHandles() {
968
- return this.schedule(
969
- new command.Command(command.Name.GET_WINDOW_HANDLES),
970
- 'WebDriver.getAllWindowHandles()');
878
+ return this.execute(
879
+ new command.Command(command.Name.GET_WINDOW_HANDLES));
971
880
  }
972
881
 
973
882
  /** @override */
974
883
  getPageSource() {
975
- return this.schedule(
976
- new command.Command(command.Name.GET_PAGE_SOURCE),
977
- 'WebDriver.getPageSource()');
884
+ return this.execute(
885
+ new command.Command(command.Name.GET_PAGE_SOURCE));
978
886
  }
979
887
 
980
888
  /** @override */
981
889
  close() {
982
- return this.schedule(new command.Command(command.Name.CLOSE),
983
- 'WebDriver.close()');
890
+ return this.execute(new command.Command(command.Name.CLOSE));
984
891
  }
985
892
 
986
893
  /** @override */
@@ -990,15 +897,12 @@ class WebDriver {
990
897
 
991
898
  /** @override */
992
899
  getCurrentUrl() {
993
- return this.schedule(
994
- new command.Command(command.Name.GET_CURRENT_URL),
995
- 'WebDriver.getCurrentUrl()');
900
+ return this.execute(new command.Command(command.Name.GET_CURRENT_URL));
996
901
  }
997
902
 
998
903
  /** @override */
999
904
  getTitle() {
1000
- return this.schedule(new command.Command(command.Name.GET_TITLE),
1001
- 'WebDriver.getTitle()');
905
+ return this.execute(new command.Command(command.Name.GET_TITLE));
1002
906
  }
1003
907
 
1004
908
  /** @override */
@@ -1011,33 +915,31 @@ class WebDriver {
1011
915
  let cmd = new command.Command(command.Name.FIND_ELEMENT).
1012
916
  setParameter('using', locator.using).
1013
917
  setParameter('value', locator.value);
1014
- id = this.schedule(cmd, 'WebDriver.findElement(' + locator + ')');
918
+ id = this.execute(cmd);
1015
919
  }
1016
920
  return new WebElementPromise(this, id);
1017
921
  }
1018
922
 
1019
923
  /**
1020
924
  * @param {!Function} locatorFn The locator function to use.
1021
- * @param {!(WebDriver|WebElement)} context The search
1022
- * context.
1023
- * @return {!promise.Thenable<!WebElement>} A
1024
- * promise that will resolve to a list of WebElements.
925
+ * @param {!(WebDriver|WebElement)} context The search context.
926
+ * @return {!Promise<!WebElement>} A promise that will resolve to a list of
927
+ * WebElements.
1025
928
  * @private
1026
929
  */
1027
- findElementInternal_(locatorFn, context) {
1028
- return this.call(() => locatorFn(context)).then(function(result) {
1029
- if (Array.isArray(result)) {
1030
- result = result[0];
1031
- }
1032
- if (!(result instanceof WebElement)) {
1033
- throw new TypeError('Custom locator did not return a WebElement');
1034
- }
1035
- return result;
1036
- });
930
+ async findElementInternal_(locatorFn, context) {
931
+ let result = await locatorFn(context);
932
+ if (Array.isArray(result)) {
933
+ result = result[0];
934
+ }
935
+ if (!(result instanceof WebElement)) {
936
+ throw new TypeError('Custom locator did not return a WebElement');
937
+ }
938
+ return result;
1037
939
  }
1038
940
 
1039
941
  /** @override */
1040
- findElements(locator) {
942
+ async findElements(locator) {
1041
943
  locator = by.checkedLocator(locator);
1042
944
  if (typeof locator === 'function') {
1043
945
  return this.findElementsInternal_(locator, this);
@@ -1045,43 +947,43 @@ class WebDriver {
1045
947
  let cmd = new command.Command(command.Name.FIND_ELEMENTS).
1046
948
  setParameter('using', locator.using).
1047
949
  setParameter('value', locator.value);
1048
- let res = this.schedule(cmd, 'WebDriver.findElements(' + locator + ')');
1049
- return res.catch(function(e) {
1050
- if (e instanceof error.NoSuchElementError) {
950
+ try {
951
+ let res = await this.execute(cmd);
952
+ return Array.isArray(res) ? res : [];
953
+ } catch (ex) {
954
+ if (ex instanceof error.NoSuchElementError) {
1051
955
  return [];
1052
956
  }
1053
- throw e;
1054
- });
957
+ throw ex;
958
+ }
1055
959
  }
1056
960
  }
1057
961
 
1058
962
  /**
1059
963
  * @param {!Function} locatorFn The locator function to use.
1060
964
  * @param {!(WebDriver|WebElement)} context The search context.
1061
- * @return {!promise.Thenable<!Array<!WebElement>>} A promise that
1062
- * will resolve to an array of WebElements.
965
+ * @return {!Promise<!Array<!WebElement>>} A promise that will resolve to an
966
+ * array of WebElements.
1063
967
  * @private
1064
968
  */
1065
- findElementsInternal_(locatorFn, context) {
1066
- return this.call(() => locatorFn(context)).then(function(result) {
1067
- if (result instanceof WebElement) {
1068
- return [result];
1069
- }
969
+ async findElementsInternal_(locatorFn, context) {
970
+ const result = await locatorFn(context);
971
+ if (result instanceof WebElement) {
972
+ return [result];
973
+ }
1070
974
 
1071
- if (!Array.isArray(result)) {
1072
- return [];
1073
- }
975
+ if (!Array.isArray(result)) {
976
+ return [];
977
+ }
1074
978
 
1075
- return result.filter(function(item) {
1076
- return item instanceof WebElement;
1077
- });
979
+ return result.filter(function(item) {
980
+ return item instanceof WebElement;
1078
981
  });
1079
982
  }
1080
983
 
1081
984
  /** @override */
1082
985
  takeScreenshot() {
1083
- return this.schedule(new command.Command(command.Name.SCREENSHOT),
1084
- 'WebDriver.takeScreenshot()');
986
+ return this.execute(new command.Command(command.Name.SCREENSHOT));
1085
987
  }
1086
988
 
1087
989
  /** @override */
@@ -1122,49 +1024,46 @@ class Navigation {
1122
1024
  }
1123
1025
 
1124
1026
  /**
1125
- * Schedules a command to navigate to a new URL.
1027
+ * Navigates to a new URL.
1028
+ *
1126
1029
  * @param {string} url The URL to navigate to.
1127
- * @return {!promise.Thenable<void>} A promise that will be resolved
1128
- * when the URL has been loaded.
1030
+ * @return {!Promise<void>} A promise that will be resolved when the URL
1031
+ * has been loaded.
1129
1032
  */
1130
1033
  to(url) {
1131
- return this.driver_.schedule(
1034
+ return this.driver_.execute(
1132
1035
  new command.Command(command.Name.GET).
1133
- setParameter('url', url),
1134
- 'WebDriver.navigate().to(' + url + ')');
1036
+ setParameter('url', url));
1135
1037
  }
1136
1038
 
1137
1039
  /**
1138
- * Schedules a command to move backwards in the browser history.
1139
- * @return {!promise.Thenable<void>} A promise that will be resolved
1140
- * when the navigation event has completed.
1040
+ * Moves backwards in the browser history.
1041
+ *
1042
+ * @return {!Promise<void>} A promise that will be resolved when the
1043
+ * navigation event has completed.
1141
1044
  */
1142
1045
  back() {
1143
- return this.driver_.schedule(
1144
- new command.Command(command.Name.GO_BACK),
1145
- 'WebDriver.navigate().back()');
1046
+ return this.driver_.execute(new command.Command(command.Name.GO_BACK));
1146
1047
  }
1147
1048
 
1148
1049
  /**
1149
- * Schedules a command to move forwards in the browser history.
1150
- * @return {!promise.Thenable<void>} A promise that will be resolved
1151
- * when the navigation event has completed.
1050
+ * Moves forwards in the browser history.
1051
+ *
1052
+ * @return {!Promise<void>} A promise that will be resolved when the
1053
+ * navigation event has completed.
1152
1054
  */
1153
1055
  forward() {
1154
- return this.driver_.schedule(
1155
- new command.Command(command.Name.GO_FORWARD),
1156
- 'WebDriver.navigate().forward()');
1056
+ return this.driver_.execute(new command.Command(command.Name.GO_FORWARD));
1157
1057
  }
1158
1058
 
1159
1059
  /**
1160
- * Schedules a command to refresh the current page.
1161
- * @return {!promise.Thenable<void>} A promise that will be resolved
1162
- * when the navigation event has completed.
1060
+ * Refreshes the current page.
1061
+ *
1062
+ * @return {!Promise<void>} A promise that will be resolved when the
1063
+ * navigation event has completed.
1163
1064
  */
1164
1065
  refresh() {
1165
- return this.driver_.schedule(
1166
- new command.Command(command.Name.REFRESH),
1167
- 'WebDriver.navigate().refresh()');
1066
+ return this.driver_.execute(new command.Command(command.Name.REFRESH));
1168
1067
  }
1169
1068
  }
1170
1069
 
@@ -1186,142 +1085,137 @@ class Options {
1186
1085
  }
1187
1086
 
1188
1087
  /**
1189
- * Schedules a command to add a cookie.
1088
+ * Adds a cookie.
1190
1089
  *
1191
1090
  * __Sample Usage:__
1192
1091
  *
1193
1092
  * // Set a basic cookie.
1194
- * driver.options().addCookie({name: 'foo', value: 'bar'});
1093
+ * driver.manage().addCookie({name: 'foo', value: 'bar'});
1195
1094
  *
1196
1095
  * // Set a cookie that expires in 10 minutes.
1197
1096
  * let expiry = new Date(Date.now() + (10 * 60 * 1000));
1198
- * driver.options().addCookie({name: 'foo', value: 'bar', expiry});
1097
+ * driver.manage().addCookie({name: 'foo', value: 'bar', expiry});
1199
1098
  *
1200
1099
  * // The cookie expiration may also be specified in seconds since epoch.
1201
- * driver.options().addCookie({
1100
+ * driver.manage().addCookie({
1202
1101
  * name: 'foo',
1203
1102
  * value: 'bar',
1204
1103
  * expiry: Math.floor(Date.now() / 1000)
1205
1104
  * });
1206
1105
  *
1207
1106
  * @param {!Options.Cookie} spec Defines the cookie to add.
1208
- * @return {!promise.Thenable<void>} A promise that will be resolved
1107
+ * @return {!Promise<void>} A promise that will be resolved
1209
1108
  * when the cookie has been added to the page.
1210
1109
  * @throws {error.InvalidArgumentError} if any of the cookie parameters are
1211
1110
  * invalid.
1212
1111
  * @throws {TypeError} if `spec` is not a cookie object.
1213
1112
  */
1214
- addCookie(spec) {
1215
- if (!spec || typeof spec !== 'object') {
1216
- throw TypeError('addCookie called with non-cookie parameter');
1217
- }
1218
-
1113
+ addCookie({name, value, path, domain, secure, httpOnly, expiry}) {
1219
1114
  // We do not allow '=' or ';' in the name.
1220
- let name = spec.name;
1221
1115
  if (/[;=]/.test(name)) {
1222
1116
  throw new error.InvalidArgumentError(
1223
1117
  'Invalid cookie name "' + name + '"');
1224
1118
  }
1225
1119
 
1226
1120
  // We do not allow ';' in value.
1227
- let value = spec.value;
1228
1121
  if (/;/.test(value)) {
1229
1122
  throw new error.InvalidArgumentError(
1230
1123
  'Invalid cookie value "' + value + '"');
1231
1124
  }
1232
1125
 
1233
- let cookieString = name + '=' + value +
1234
- (spec.domain ? ';domain=' + spec.domain : '') +
1235
- (spec.path ? ';path=' + spec.path : '') +
1236
- (spec.secure ? ';secure' : '');
1237
-
1238
- let expiry;
1239
- if (typeof spec.expiry === 'number') {
1240
- expiry = Math.floor(spec.expiry);
1241
- cookieString += ';expires=' + new Date(spec.expiry * 1000).toUTCString();
1242
- } else if (spec.expiry instanceof Date) {
1243
- let date = /** @type {!Date} */(spec.expiry);
1126
+ if (typeof expiry === 'number') {
1127
+ expiry = Math.floor(expiry);
1128
+ } else if (expiry instanceof Date) {
1129
+ let date = /** @type {!Date} */(expiry);
1244
1130
  expiry = Math.floor(date.getTime() / 1000);
1245
- cookieString += ';expires=' + date.toUTCString();
1246
1131
  }
1247
1132
 
1248
- return this.driver_.schedule(
1133
+ return this.driver_.execute(
1249
1134
  new command.Command(command.Name.ADD_COOKIE).
1250
1135
  setParameter('cookie', {
1251
1136
  'name': name,
1252
1137
  'value': value,
1253
- 'path': spec.path,
1254
- 'domain': spec.domain,
1255
- 'secure': !!spec.secure,
1138
+ 'path': path,
1139
+ 'domain': domain,
1140
+ 'secure': !!secure,
1141
+ 'httpOnly': !!httpOnly,
1256
1142
  'expiry': expiry
1257
- }),
1258
- 'WebDriver.manage().addCookie(' + cookieString + ')');
1143
+ }));
1259
1144
  }
1260
1145
 
1261
1146
  /**
1262
- * Schedules a command to delete all cookies visible to the current page.
1263
- * @return {!promise.Thenable<void>} A promise that will be resolved
1147
+ * Deletes all cookies visible to the current page.
1148
+ *
1149
+ * @return {!Promise<void>} A promise that will be resolved
1264
1150
  * when all cookies have been deleted.
1265
1151
  */
1266
1152
  deleteAllCookies() {
1267
- return this.driver_.schedule(
1268
- new command.Command(command.Name.DELETE_ALL_COOKIES),
1269
- 'WebDriver.manage().deleteAllCookies()');
1153
+ return this.driver_.execute(
1154
+ new command.Command(command.Name.DELETE_ALL_COOKIES));
1270
1155
  }
1271
1156
 
1272
1157
  /**
1273
- * Schedules a command to delete the cookie with the given name. This command
1274
- * is a no-op if there is no cookie with the given name visible to the current
1275
- * page.
1158
+ * Deletes the cookie with the given name. This command is a no-op if there is
1159
+ * no cookie with the given name visible to the current page.
1160
+ *
1276
1161
  * @param {string} name The name of the cookie to delete.
1277
- * @return {!promise.Thenable<void>} A promise that will be resolved
1162
+ * @return {!Promise<void>} A promise that will be resolved
1278
1163
  * when the cookie has been deleted.
1279
1164
  */
1280
1165
  deleteCookie(name) {
1281
- return this.driver_.schedule(
1166
+ return this.driver_.execute(
1282
1167
  new command.Command(command.Name.DELETE_COOKIE).
1283
- setParameter('name', name),
1284
- 'WebDriver.manage().deleteCookie(' + name + ')');
1168
+ setParameter('name', name));
1285
1169
  }
1286
1170
 
1287
1171
  /**
1288
- * Schedules a command to retrieve all cookies visible to the current page.
1289
- * Each cookie will be returned as a JSON object as described by the WebDriver
1290
- * wire protocol.
1291
- * @return {!promise.Thenable<!Array<!Options.Cookie>>} A promise that will be
1172
+ * Retrieves all cookies visible to the current page. Each cookie will be
1173
+ * returned as a JSON object as described by the WebDriver wire protocol.
1174
+ *
1175
+ * @return {!Promise<!Array<!Options.Cookie>>} A promise that will be
1292
1176
  * resolved with the cookies visible to the current browsing context.
1293
1177
  */
1294
1178
  getCookies() {
1295
- return this.driver_.schedule(
1296
- new command.Command(command.Name.GET_ALL_COOKIES),
1297
- 'WebDriver.manage().getCookies()');
1179
+ return this.driver_.execute(
1180
+ new command.Command(command.Name.GET_ALL_COOKIES));
1298
1181
  }
1299
1182
 
1300
1183
  /**
1301
- * Schedules a command to retrieve the cookie with the given name. Returns null
1302
- * if there is no such cookie. The cookie will be returned as a JSON object as
1303
- * described by the WebDriver wire protocol.
1184
+ * Retrieves the cookie with the given name. Returns null if there is no such
1185
+ * cookie. The cookie will be returned as a JSON object as described by the
1186
+ * WebDriver wire protocol.
1304
1187
  *
1305
1188
  * @param {string} name The name of the cookie to retrieve.
1306
- * @return {!promise.Thenable<?Options.Cookie>} A promise that will be resolved
1189
+ * @return {!Promise<?Options.Cookie>} A promise that will be resolved
1307
1190
  * with the named cookie, or `null` if there is no such cookie.
1308
1191
  */
1309
- getCookie(name) {
1310
- return this.getCookies().then(function(cookies) {
1192
+ async getCookie(name) {
1193
+ try {
1194
+ const cookie =
1195
+ await this.driver_.execute(
1196
+ new command.Command(command.Name.GET_COOKIE)
1197
+ .setParameter('name', name));
1198
+ return cookie;
1199
+ } catch (err) {
1200
+ if (!(err instanceof error.UnknownCommandError)
1201
+ && !(err instanceof error.UnsupportedOperationError)) {
1202
+ throw err;
1203
+ }
1204
+
1205
+ const cookies = await this.getCookies();
1311
1206
  for (let cookie of cookies) {
1312
1207
  if (cookie && cookie['name'] === name) {
1313
1208
  return cookie;
1314
1209
  }
1315
1210
  }
1316
1211
  return null;
1317
- });
1212
+ }
1318
1213
  }
1319
1214
 
1320
1215
  /**
1321
- * Schedules a command to fetch the timeouts currently configured for the
1322
- * current session.
1216
+ * Fetches the timeouts currently configured for the current session.
1323
1217
  *
1324
- * @return {!promise.Thenable<{script: number,
1218
+ * @return {!Promise<{script: number,
1325
1219
  * pageLoad: number,
1326
1220
  * implicit: number}>} A promise that will be
1327
1221
  * resolved with the timeouts currently configured for the current
@@ -1329,14 +1223,11 @@ class Options {
1329
1223
  * @see #setTimeouts()
1330
1224
  */
1331
1225
  getTimeouts() {
1332
- return this.driver_.schedule(
1333
- new command.Command(command.Name.GET_TIMEOUT),
1334
- `WebDriver.manage().getTimeouts()`)
1226
+ return this.driver_.execute(new command.Command(command.Name.GET_TIMEOUT));
1335
1227
  }
1336
1228
 
1337
1229
  /**
1338
- * Schedules a command to set timeout durations associated with the current
1339
- * session.
1230
+ * Sets the timeout durations associated with the current session.
1340
1231
  *
1341
1232
  * The following timeouts are supported (all timeouts are specified in
1342
1233
  * milliseconds):
@@ -1358,8 +1249,8 @@ class Options {
1358
1249
  * pageLoad: (number|null|undefined),
1359
1250
  * implicit: (number|null|undefined)}} conf
1360
1251
  * The desired timeout configuration.
1361
- * @return {!promise.Thenable<void>} A promise that will be resolved when the
1362
- * timeouts have been set.
1252
+ * @return {!Promise<void>} A promise that will be resolved when the timeouts
1253
+ * have been set.
1363
1254
  * @throws {!TypeError} if an invalid options object is provided.
1364
1255
  * @see #getTimeouts()
1365
1256
  * @see <https://w3c.github.io/webdriver/webdriver-spec.html#dfn-set-timeouts>
@@ -1383,7 +1274,7 @@ class Options {
1383
1274
  setParam('script', script);
1384
1275
 
1385
1276
  if (valid) {
1386
- return this.driver_.schedule(cmd, `WebDriver.manage().setTimeouts()`)
1277
+ return this.driver_.execute(cmd)
1387
1278
  .catch(() => {
1388
1279
  // Fallback to the legacy method.
1389
1280
  let cmds = [];
@@ -1403,21 +1294,12 @@ class Options {
1403
1294
  }
1404
1295
 
1405
1296
  /**
1406
- * @return {!Logs} The interface for managing driver
1407
- * logs.
1297
+ * @return {!Logs} The interface for managing driver logs.
1408
1298
  */
1409
1299
  logs() {
1410
1300
  return new Logs(this.driver_);
1411
1301
  }
1412
1302
 
1413
- /**
1414
- * @return {!Timeouts} The interface for managing driver timeouts.
1415
- * @deprecated Use {@link #setTimeouts()} instead.
1416
- */
1417
- timeouts() {
1418
- return new Timeouts(this.driver_);
1419
- }
1420
-
1421
1303
  /**
1422
1304
  * @return {!Window} The interface for managing the current window.
1423
1305
  */
@@ -1431,14 +1313,13 @@ class Options {
1431
1313
  * @param {!WebDriver} driver
1432
1314
  * @param {string} type
1433
1315
  * @param {number} ms
1434
- * @return {!promise.Thenable<void>}
1316
+ * @return {!Promise<void>}
1435
1317
  */
1436
1318
  function legacyTimeout(driver, type, ms) {
1437
- return driver.schedule(
1319
+ return driver.execute(
1438
1320
  new command.Command(command.Name.SET_TIMEOUT)
1439
1321
  .setParameter('type', type)
1440
- .setParameter('ms', ms),
1441
- `WebDriver.manage().setTimeouts({${type}: ${ms}})`);
1322
+ .setParameter('ms', ms));
1442
1323
  }
1443
1324
 
1444
1325
 
@@ -1506,8 +1387,7 @@ Options.Cookie.prototype.httpOnly;
1506
1387
  * When the cookie expires.
1507
1388
  *
1508
1389
  * When {@linkplain Options#addCookie() adding a cookie}, this may be specified
1509
- * in _seconds_ since Unix epoch (January 1, 1970). The expiry will default to
1510
- * 20 years in the future if omitted.
1390
+ * as a {@link Date} object, or in _seconds_ since Unix epoch (January 1, 1970).
1511
1391
  *
1512
1392
  * The expiry is always returned in seconds since epoch when
1513
1393
  * {@linkplain Options#getCookies() retrieving cookies} from the browser.
@@ -1517,89 +1397,6 @@ Options.Cookie.prototype.httpOnly;
1517
1397
  Options.Cookie.prototype.expiry;
1518
1398
 
1519
1399
 
1520
- /**
1521
- * An interface for managing timeout behavior for WebDriver instances.
1522
- *
1523
- * This class should never be instantiated directly. Instead, obtain an instance
1524
- * with
1525
- *
1526
- * webdriver.manage().timeouts()
1527
- *
1528
- * @deprecated This has been deprecated in favor of
1529
- * {@link Options#setTimeouts()}, which supports setting multiple timeouts
1530
- * at once.
1531
- * @see WebDriver#manage()
1532
- * @see Options#timeouts()
1533
- */
1534
- class Timeouts {
1535
- /**
1536
- * @param {!WebDriver} driver The parent driver.
1537
- * @private
1538
- */
1539
- constructor(driver) {
1540
- /** @private {!WebDriver} */
1541
- this.driver_ = driver;
1542
- }
1543
-
1544
- /**
1545
- * Specifies the amount of time the driver should wait when searching for an
1546
- * element if it is not immediately present.
1547
- *
1548
- * When searching for a single element, the driver should poll the page
1549
- * until the element has been found, or this timeout expires before failing
1550
- * with a {@link bot.ErrorCode.NO_SUCH_ELEMENT} error. When searching
1551
- * for multiple elements, the driver should poll the page until at least one
1552
- * element has been found or this timeout has expired.
1553
- *
1554
- * Setting the wait timeout to 0 (its default value), disables implicit
1555
- * waiting.
1556
- *
1557
- * Increasing the implicit wait timeout should be used judiciously as it
1558
- * will have an adverse effect on test run time, especially when used with
1559
- * slower location strategies like XPath.
1560
- *
1561
- * @param {number} ms The amount of time to wait, in milliseconds.
1562
- * @return {!promise.Thenable<void>} A promise that will be resolved
1563
- * when the implicit wait timeout has been set.
1564
- * @deprecated Use {@link Options#setTimeouts()
1565
- * driver.manage().setTimeouts({implicit: ms})}.
1566
- */
1567
- implicitlyWait(ms) {
1568
- return this.driver_.manage().setTimeouts({implicit: ms});
1569
- }
1570
-
1571
- /**
1572
- * Sets the amount of time to wait, in milliseconds, for an asynchronous
1573
- * script to finish execution before returning an error. If the timeout is
1574
- * less than or equal to 0, the script will be allowed to run indefinitely.
1575
- *
1576
- * @param {number} ms The amount of time to wait, in milliseconds.
1577
- * @return {!promise.Thenable<void>} A promise that will be resolved
1578
- * when the script timeout has been set.
1579
- * @deprecated Use {@link Options#setTimeouts()
1580
- * driver.manage().setTimeouts({script: ms})}.
1581
- */
1582
- setScriptTimeout(ms) {
1583
- return this.driver_.manage().setTimeouts({script: ms});
1584
- }
1585
-
1586
- /**
1587
- * Sets the amount of time to wait for a page load to complete before
1588
- * returning an error. If the timeout is negative, page loads may be
1589
- * indefinite.
1590
- *
1591
- * @param {number} ms The amount of time to wait, in milliseconds.
1592
- * @return {!promise.Thenable<void>} A promise that will be resolved
1593
- * when the timeout has been set.
1594
- * @deprecated Use {@link Options#setTimeouts()
1595
- * driver.manage().setTimeouts({pageLoad: ms})}.
1596
- */
1597
- pageLoadTimeout(ms) {
1598
- return this.driver_.manage().setTimeouts({pageLoad: ms});
1599
- }
1600
- }
1601
-
1602
-
1603
1400
  /**
1604
1401
  * An interface for managing the current window.
1605
1402
  *
@@ -1622,76 +1419,114 @@ class Window {
1622
1419
  }
1623
1420
 
1624
1421
  /**
1625
- * Retrieves the window's current position, relative to the top left corner of
1626
- * the screen.
1627
- * @return {!promise.Thenable<{x: number, y: number}>} A promise
1628
- * that will be resolved with the window's position in the form of a
1629
- * {x:number, y:number} object literal.
1630
- */
1631
- getPosition() {
1632
- return this.driver_.schedule(
1633
- new command.Command(command.Name.GET_WINDOW_POSITION).
1634
- setParameter('windowHandle', 'current'),
1635
- 'WebDriver.manage().window().getPosition()');
1422
+ * Retrieves the a rect describing the current top-level window's size and
1423
+ * position.
1424
+ *
1425
+ * @return {!Promise<{x: number, y: number, width: number, height: number}>}
1426
+ * A promise that will resolve to the window rect of the current window.
1427
+ */
1428
+ async getRect() {
1429
+ try {
1430
+ return await this.driver_.execute(
1431
+ new command.Command(command.Name.GET_WINDOW_RECT));
1432
+ } catch (ex) {
1433
+ if (ex instanceof error.UnknownCommandError) {
1434
+ let {width, height} =
1435
+ await this.driver_.execute(
1436
+ new command.Command(command.Name.GET_WINDOW_SIZE)
1437
+ .setParameter('windowHandle', 'current'));
1438
+ let {x, y} =
1439
+ await this.driver_.execute(
1440
+ new command.Command(command.Name.GET_WINDOW_POSITION)
1441
+ .setParameter('windowHandle', 'current'));
1442
+ return {x, y, width, height};
1443
+ }
1444
+ throw ex;
1445
+ }
1636
1446
  }
1637
1447
 
1638
1448
  /**
1639
- * Repositions the current window.
1640
- * @param {number} x The desired horizontal position, relative to the left
1641
- * side of the screen.
1642
- * @param {number} y The desired vertical position, relative to the top of the
1643
- * of the screen.
1644
- * @return {!promise.Thenable<void>} A promise that will be resolved
1645
- * when the command has completed.
1646
- */
1647
- setPosition(x, y) {
1648
- return this.driver_.schedule(
1649
- new command.Command(command.Name.SET_WINDOW_POSITION).
1650
- setParameter('windowHandle', 'current').
1651
- setParameter('x', x).
1652
- setParameter('y', y),
1653
- 'WebDriver.manage().window().setPosition(' + x + ', ' + y + ')');
1449
+ * Sets the current top-level window's size and position. You may update just
1450
+ * the size by omitting `x` & `y`, or just the position by omitting
1451
+ * `width` & `height` options.
1452
+ *
1453
+ * @param {{x: (number|undefined),
1454
+ * y: (number|undefined),
1455
+ * width: (number|undefined),
1456
+ * height: (number|undefined)}} options
1457
+ * The desired window size and position.
1458
+ * @return {!Promise<{x: number, y: number, width: number, height: number}>}
1459
+ * A promise that will resolve to the current widnow's updated window
1460
+ * rect.
1461
+ */
1462
+ async setRect({x, y, width, height}) {
1463
+ try {
1464
+ return await this.driver_.execute(
1465
+ new command.Command(command.Name.SET_WINDOW_RECT)
1466
+ .setParameters({x, y, width, height}));
1467
+ } catch (ex) {
1468
+ if (ex instanceof error.UnknownCommandError) {
1469
+ if (typeof x === 'number' && typeof y === 'number') {
1470
+ await this.driver_.execute(
1471
+ new command.Command(command.Name.SET_WINDOW_POSITION)
1472
+ .setParameter('windowHandle', 'current')
1473
+ .setParameter('x', x)
1474
+ .setParameter('y', y));
1475
+ }
1476
+
1477
+ if (typeof width === 'number' && typeof height === 'number') {
1478
+ await this.driver_.execute(
1479
+ new command.Command(command.Name.SET_WINDOW_SIZE)
1480
+ .setParameter('windowHandle', 'current')
1481
+ .setParameter('width', width)
1482
+ .setParameter('height', height));
1483
+ }
1484
+ return this.getRect();
1485
+ }
1486
+ throw ex;
1487
+ }
1654
1488
  }
1655
1489
 
1656
1490
  /**
1657
- * Retrieves the window's current size.
1658
- * @return {!promise.Thenable<{width: number, height: number}>} A
1659
- * promise that will be resolved with the window's size in the form of a
1660
- * {width:number, height:number} object literal.
1491
+ * Maximizes the current window. The exact behavior of this command is
1492
+ * specific to individual window managers, but typically involves increasing
1493
+ * the window to the maximum available size without going full-screen.
1494
+ *
1495
+ * @return {!Promise<void>} A promise that will be resolved when the command
1496
+ * has completed.
1661
1497
  */
1662
- getSize() {
1663
- return this.driver_.schedule(
1664
- new command.Command(command.Name.GET_WINDOW_SIZE).
1665
- setParameter('windowHandle', 'current'),
1666
- 'WebDriver.manage().window().getSize()');
1498
+ maximize() {
1499
+ return this.driver_.execute(
1500
+ new command.Command(command.Name.MAXIMIZE_WINDOW).
1501
+ setParameter('windowHandle', 'current'));
1667
1502
  }
1668
1503
 
1669
1504
  /**
1670
- * Resizes the current window.
1671
- * @param {number} width The desired window width.
1672
- * @param {number} height The desired window height.
1673
- * @return {!promise.Thenable<void>} A promise that will be resolved
1674
- * when the command has completed.
1505
+ * Minimizes the current window. The exact behavior of this command is
1506
+ * specific to individual window managers, but typicallly involves hiding
1507
+ * the window in the system tray.
1508
+ *
1509
+ * @return {!Promise<void>} A promise that will be resolved when the command
1510
+ * has completed.
1675
1511
  */
1676
- setSize(width, height) {
1677
- return this.driver_.schedule(
1678
- new command.Command(command.Name.SET_WINDOW_SIZE).
1679
- setParameter('windowHandle', 'current').
1680
- setParameter('width', width).
1681
- setParameter('height', height),
1682
- 'WebDriver.manage().window().setSize(' + width + ', ' + height + ')');
1512
+ minimize() {
1513
+ return this.driver_.execute(
1514
+ new command.Command(command.Name.MINIMIZE_WINDOW));
1683
1515
  }
1684
1516
 
1685
1517
  /**
1686
- * Maximizes the current window.
1687
- * @return {!promise.Thenable<void>} A promise that will be resolved
1688
- * when the command has completed.
1518
+ * Invokes the "full screen" operation on the current window. The exact
1519
+ * behavior of this command is specific to individual window managers, but
1520
+ * this will typically increase the window size to the size of the physical
1521
+ * display and hide the browser chrome.
1522
+ *
1523
+ * @return {!Promise<void>} A promise that will be resolved when the command
1524
+ * has completed.
1525
+ * @see <https://fullscreen.spec.whatwg.org/#fullscreen-an-element>
1689
1526
  */
1690
- maximize() {
1691
- return this.driver_.schedule(
1692
- new command.Command(command.Name.MAXIMIZE_WINDOW).
1693
- setParameter('windowHandle', 'current'),
1694
- 'WebDriver.manage().window().maximize()');
1527
+ fullscreen() {
1528
+ return this.driver_.execute(
1529
+ new command.Command(command.Name.FULLSCREEN_WINDOW));
1695
1530
  }
1696
1531
  }
1697
1532
 
@@ -1726,15 +1561,14 @@ class Logs {
1726
1561
  * entries since the last call, or from the start of the session.
1727
1562
  *
1728
1563
  * @param {!logging.Type} type The desired log type.
1729
- * @return {!promise.Thenable<!Array.<!logging.Entry>>} A
1564
+ * @return {!Promise<!Array.<!logging.Entry>>} A
1730
1565
  * promise that will resolve to a list of log entries for the specified
1731
1566
  * type.
1732
1567
  */
1733
1568
  get(type) {
1734
1569
  let cmd = new command.Command(command.Name.GET_LOG).
1735
1570
  setParameter('type', type);
1736
- return this.driver_.schedule(
1737
- cmd, 'WebDriver.manage().logs().get(' + type + ')').
1571
+ return this.driver_.execute(cmd).
1738
1572
  then(function(entries) {
1739
1573
  return entries.map(function(entry) {
1740
1574
  if (!(entry instanceof logging.Entry)) {
@@ -1749,13 +1583,12 @@ class Logs {
1749
1583
 
1750
1584
  /**
1751
1585
  * Retrieves the log types available to this driver.
1752
- * @return {!promise.Thenable<!Array<!logging.Type>>} A
1586
+ * @return {!Promise<!Array<!logging.Type>>} A
1753
1587
  * promise that will resolve to a list of available log types.
1754
1588
  */
1755
1589
  getAvailableLogTypes() {
1756
- return this.driver_.schedule(
1757
- new command.Command(command.Name.GET_AVAILABLE_LOG_TYPES),
1758
- 'WebDriver.manage().logs().getAvailableLogTypes()');
1590
+ return this.driver_.execute(
1591
+ new command.Command(command.Name.GET_AVAILABLE_LOG_TYPES));
1759
1592
  }
1760
1593
  }
1761
1594
 
@@ -1781,35 +1614,34 @@ class TargetLocator {
1781
1614
  }
1782
1615
 
1783
1616
  /**
1784
- * Schedules a command retrieve the {@code document.activeElement} element on
1785
- * the current document, or {@code document.body} if activeElement is not
1617
+ * Locates the DOM element on the current page that corresponds to
1618
+ * `document.activeElement` or `document.body` if the active element is not
1786
1619
  * available.
1620
+ *
1787
1621
  * @return {!WebElementPromise} The active element.
1788
1622
  */
1789
1623
  activeElement() {
1790
- var id = this.driver_.schedule(
1791
- new command.Command(command.Name.GET_ACTIVE_ELEMENT),
1792
- 'WebDriver.switchTo().activeElement()');
1624
+ var id = this.driver_.execute(
1625
+ new command.Command(command.Name.GET_ACTIVE_ELEMENT));
1793
1626
  return new WebElementPromise(this.driver_, id);
1794
1627
  }
1795
1628
 
1796
1629
  /**
1797
- * Schedules a command to switch focus of all future commands to the topmost
1798
- * frame on the page.
1799
- * @return {!promise.Thenable<void>} A promise that will be resolved
1630
+ * Switches focus of all future commands to the topmost frame in the current
1631
+ * window.
1632
+ *
1633
+ * @return {!Promise<void>} A promise that will be resolved
1800
1634
  * when the driver has changed focus to the default content.
1801
1635
  */
1802
1636
  defaultContent() {
1803
- return this.driver_.schedule(
1637
+ return this.driver_.execute(
1804
1638
  new command.Command(command.Name.SWITCH_TO_FRAME).
1805
- setParameter('id', null),
1806
- 'WebDriver.switchTo().defaultContent()');
1639
+ setParameter('id', null));
1807
1640
  }
1808
1641
 
1809
1642
  /**
1810
- * Schedules a command to switch the focus of all future commands to another
1811
- * frame on the page. The target frame may be specified as one of the
1812
- * following:
1643
+ * Changes the focus of all future commands to another frame on the page. The
1644
+ * target frame may be specified as one of the following:
1813
1645
  *
1814
1646
  * - A number that specifies a (zero-based) index into [window.frames](
1815
1647
  * https://developer.mozilla.org/en-US/docs/Web/API/Window.frames).
@@ -1822,51 +1654,82 @@ class TargetLocator {
1822
1654
  * rejected with a {@linkplain error.NoSuchFrameError}.
1823
1655
  *
1824
1656
  * @param {(number|WebElement|null)} id The frame locator.
1825
- * @return {!promise.Thenable<void>} A promise that will be resolved
1657
+ * @return {!Promise<void>} A promise that will be resolved
1826
1658
  * when the driver has changed focus to the specified frame.
1827
1659
  */
1828
1660
  frame(id) {
1829
- return this.driver_.schedule(
1661
+ return this.driver_.execute(
1830
1662
  new command.Command(command.Name.SWITCH_TO_FRAME).
1831
- setParameter('id', id),
1832
- 'WebDriver.switchTo().frame(' + id + ')');
1663
+ setParameter('id', id));
1833
1664
  }
1834
1665
 
1835
1666
  /**
1836
- * Schedules a command to switch the focus of all future commands to another
1837
- * window. Windows may be specified by their {@code window.name} attribute or
1838
- * by its handle (as returned by {@link WebDriver#getWindowHandles}).
1667
+ * Changes the focus of all future commands to the parent frame of the
1668
+ * currently selected frame. This command has no effect if the driver is
1669
+ * already focused on the top-level browsing context.
1670
+ *
1671
+ * @return {!Promise<void>} A promise that will be resolved when the command
1672
+ * has completed.
1673
+ */
1674
+ parentFrame() {
1675
+ return this.driver_.execute(
1676
+ new command.Command(command.Name.SWITCH_TO_FRAME_PARENT));
1677
+ }
1678
+
1679
+ /**
1680
+ * Changes the focus of all future commands to another window. Windows may be
1681
+ * specified by their {@code window.name} attribute or by its handle
1682
+ * (as returned by {@link WebDriver#getWindowHandles}).
1839
1683
  *
1840
1684
  * If the specified window cannot be found, the returned promise will be
1841
1685
  * rejected with a {@linkplain error.NoSuchWindowError}.
1842
1686
  *
1843
1687
  * @param {string} nameOrHandle The name or window handle of the window to
1844
1688
  * switch focus to.
1845
- * @return {!promise.Thenable<void>} A promise that will be resolved
1689
+ * @return {!Promise<void>} A promise that will be resolved
1846
1690
  * when the driver has changed focus to the specified window.
1847
1691
  */
1848
1692
  window(nameOrHandle) {
1849
- return this.driver_.schedule(
1693
+ return this.driver_.execute(
1850
1694
  new command.Command(command.Name.SWITCH_TO_WINDOW).
1851
1695
  // "name" supports the legacy drivers. "handle" is the W3C
1852
1696
  // compliant parameter.
1853
1697
  setParameter('name', nameOrHandle).
1854
- setParameter('handle', nameOrHandle),
1855
- 'WebDriver.switchTo().window(' + nameOrHandle + ')');
1698
+ setParameter('handle', nameOrHandle));
1699
+ }
1700
+
1701
+ /**
1702
+ * Creates a new browser window and switches the focus for future
1703
+ * commands of this driver to the new window.
1704
+ *
1705
+ * @param {string} typeHint 'window' or 'tab'. The created window is not
1706
+ * guaranteed to be of the requested type; if the driver does not support
1707
+ * the requested type, a new browser window will be created of whatever type
1708
+ * the driver does support.
1709
+ * @return {!Promise<void>} A promise that will be resolved
1710
+ * when the driver has changed focus to the new window.
1711
+ */
1712
+ newWindow(typeHint) {
1713
+ var driver = this.driver_
1714
+ return this.driver_.execute(
1715
+ new command.Command(command.Name.SWITCH_TO_NEW_WINDOW).
1716
+ setParameter('type', typeHint)
1717
+ ).then(function(response) {
1718
+ return driver.switchTo().window(response.handle);
1719
+ });
1856
1720
  }
1857
1721
 
1858
1722
  /**
1859
- * Schedules a command to change focus to the active modal dialog, such as
1860
- * those opened by `window.alert()`, `window.confirm()`, and
1861
- * `window.prompt()`. The returned promise will be rejected with a
1723
+ * Changes focus to the active modal dialog, such as those opened by
1724
+ * `window.alert()`, `window.confirm()`, and `window.prompt()`. The returned
1725
+ * promise will be rejected with a
1862
1726
  * {@linkplain error.NoSuchAlertError} if there are no open alerts.
1863
1727
  *
1864
1728
  * @return {!AlertPromise} The open alert.
1865
1729
  */
1866
1730
  alert() {
1867
- var text = this.driver_.schedule(
1868
- new command.Command(command.Name.GET_ALERT_TEXT),
1869
- 'WebDriver.switchTo().alert()');
1731
+ var text = this.driver_.execute(
1732
+ new command.Command(command.Name.GET_ALERT_TEXT));
1870
1733
  var driver = this.driver_;
1871
1734
  return new AlertPromise(driver, text.then(function(text) {
1872
1735
  return new Alert(driver, text);
@@ -1906,17 +1769,17 @@ class WebElement {
1906
1769
  /** @private {!WebDriver} */
1907
1770
  this.driver_ = driver;
1908
1771
 
1909
- /** @private {!promise.Thenable<string>} */
1910
- this.id_ = driver.controlFlow().promise(resolve => resolve(id));
1772
+ /** @private {!Promise<string>} */
1773
+ this.id_ = Promise.resolve(id);
1911
1774
  }
1912
1775
 
1913
1776
  /**
1914
1777
  * @param {string} id The raw ID.
1915
- * @param {boolean=} opt_noLegacy Whether to exclude the legacy element key.
1778
+ * @param {boolean=} noLegacy Whether to exclude the legacy element key.
1916
1779
  * @return {!Object} The element ID for use with WebDriver's wire protocol.
1917
1780
  */
1918
- static buildId(id, opt_noLegacy) {
1919
- return opt_noLegacy
1781
+ static buildId(id, noLegacy = false) {
1782
+ return noLegacy
1920
1783
  ? {[ELEMENT_ID_KEY]: id}
1921
1784
  : {[ELEMENT_ID_KEY]: id, [LEGACY_ELEMENT_ID_KEY]: id};
1922
1785
  }
@@ -1954,27 +1817,15 @@ class WebElement {
1954
1817
  *
1955
1818
  * @param {!WebElement} a A WebElement.
1956
1819
  * @param {!WebElement} b A WebElement.
1957
- * @return {!promise.Thenable<boolean>} A promise that will be
1820
+ * @return {!Promise<boolean>} A promise that will be
1958
1821
  * resolved to whether the two WebElements are equal.
1959
1822
  */
1960
- static equals(a, b) {
1823
+ static async equals(a, b) {
1961
1824
  if (a === b) {
1962
- return a.driver_.controlFlow().promise(resolve => resolve(true));
1825
+ return true;
1963
1826
  }
1964
- let ids = [a.getId(), b.getId()];
1965
- return promise.all(ids).then(function(ids) {
1966
- // If the two element's have the same ID, they should be considered
1967
- // equal. Otherwise, they may still be equivalent, but we'll need to
1968
- // ask the server to check for us.
1969
- if (ids[0] === ids[1]) {
1970
- return true;
1971
- }
1972
-
1973
- let cmd = new command.Command(command.Name.ELEMENT_EQUALS);
1974
- cmd.setParameter('id', ids[0]);
1975
- cmd.setParameter('other', ids[1]);
1976
- return a.driver_.schedule(cmd, 'WebElement.equals()');
1977
- });
1827
+ return a.driver_.executeScript(
1828
+ 'return arguments[0] === arguments[1]', a, b);
1978
1829
  }
1979
1830
 
1980
1831
  /** @return {!WebDriver} The parent driver for this instance. */
@@ -1983,7 +1834,7 @@ class WebElement {
1983
1834
  }
1984
1835
 
1985
1836
  /**
1986
- * @return {!promise.Thenable<string>} A promise that resolves to
1837
+ * @return {!Promise<string>} A promise that resolves to
1987
1838
  * the server-assigned opaque ID assigned to this element.
1988
1839
  */
1989
1840
  getId() {
@@ -2003,16 +1854,14 @@ class WebElement {
2003
1854
  * parameters under the "id" key.
2004
1855
  *
2005
1856
  * @param {!command.Command} command The command to schedule.
2006
- * @param {string} description A description of the command for debugging.
2007
- * @return {!promise.Thenable<T>} A promise that will be resolved
2008
- * with the command result.
1857
+ * @return {!Promise<T>} A promise that will be resolved with the result.
2009
1858
  * @template T
2010
1859
  * @see WebDriver#schedule
2011
1860
  * @private
2012
1861
  */
2013
- schedule_(command, description) {
1862
+ execute_(command) {
2014
1863
  command.setParameter('id', this);
2015
- return this.driver_.schedule(command, description);
1864
+ return this.driver_.execute(command);
2016
1865
  }
2017
1866
 
2018
1867
  /**
@@ -2059,48 +1908,46 @@ class WebElement {
2059
1908
  command.Name.FIND_CHILD_ELEMENT).
2060
1909
  setParameter('using', locator.using).
2061
1910
  setParameter('value', locator.value);
2062
- id = this.schedule_(cmd, 'WebElement.findElement(' + locator + ')');
1911
+ id = this.execute_(cmd);
2063
1912
  }
2064
1913
  return new WebElementPromise(this.driver_, id);
2065
1914
  }
2066
1915
 
2067
1916
  /**
2068
- * Schedules a command to find all of the descendants of this element that
2069
- * match the given search criteria.
1917
+ * Locates all of the descendants of this element that match the given search
1918
+ * criteria.
2070
1919
  *
2071
1920
  * @param {!(by.By|Function)} locator The locator strategy to use when
2072
1921
  * searching for the element.
2073
- * @return {!promise.Thenable<!Array<!WebElement>>} A
2074
- * promise that will resolve to an array of WebElements.
1922
+ * @return {!Promise<!Array<!WebElement>>} A promise that will resolve to an
1923
+ * array of WebElements.
2075
1924
  */
2076
- findElements(locator) {
1925
+ async findElements(locator) {
2077
1926
  locator = by.checkedLocator(locator);
2078
1927
  let id;
2079
1928
  if (typeof locator === 'function') {
2080
1929
  return this.driver_.findElementsInternal_(locator, this);
2081
1930
  } else {
2082
- var cmd = new command.Command(
2083
- command.Name.FIND_CHILD_ELEMENTS).
2084
- setParameter('using', locator.using).
2085
- setParameter('value', locator.value);
2086
- return this.schedule_(cmd, 'WebElement.findElements(' + locator + ')');
1931
+ let cmd = new command.Command(command.Name.FIND_CHILD_ELEMENTS)
1932
+ .setParameter('using', locator.using)
1933
+ .setParameter('value', locator.value);
1934
+ let result = await this.execute_(cmd);
1935
+ return Array.isArray(result) ? result : [];
2087
1936
  }
2088
1937
  }
2089
1938
 
2090
1939
  /**
2091
- * Schedules a command to click on this element.
2092
- * @return {!promise.Thenable<void>} A promise that will be resolved
2093
- * when the click command has completed.
1940
+ * Clicks on this element.
1941
+ *
1942
+ * @return {!Promise<void>} A promise that will be resolved when the click
1943
+ * command has completed.
2094
1944
  */
2095
1945
  click() {
2096
- return this.schedule_(
2097
- new command.Command(command.Name.CLICK_ELEMENT),
2098
- 'WebElement.click()');
1946
+ return this.execute_(new command.Command(command.Name.CLICK_ELEMENT));
2099
1947
  }
2100
1948
 
2101
1949
  /**
2102
- * Schedules a command to type a sequence on the DOM element represented by
2103
- * this instance.
1950
+ * Types a key sequence on the DOM element represented by this instance.
2104
1951
  *
2105
1952
  * Modifier keys (SHIFT, CONTROL, ALT, META) are stateful; once a modifier is
2106
1953
  * processed in the key sequence, that key state is toggled until one of the
@@ -2147,97 +1994,80 @@ class WebElement {
2147
1994
  * punctuation keys will be synthesized according to a standard QWERTY en-us
2148
1995
  * keyboard layout.
2149
1996
  *
2150
- * @param {...(number|string|!IThenable<(number|string)>)} var_args The
1997
+ * @param {...(number|string|!IThenable<(number|string)>)} args The
2151
1998
  * sequence of keys to type. Number keys may be referenced numerically or
2152
1999
  * by string (1 or '1'). All arguments will be joined into a single
2153
2000
  * sequence.
2154
- * @return {!promise.Thenable<void>} A promise that will be resolved
2155
- * when all keys have been typed.
2156
- */
2157
- sendKeys(var_args) {
2158
- let keys = Promise.all(Array.prototype.slice.call(arguments, 0)).
2159
- then(keys => {
2160
- let ret = [];
2161
- keys.forEach(key => {
2162
- let type = typeof key;
2163
- if (type === 'number') {
2164
- key = String(key);
2165
- } else if (type !== 'string') {
2166
- throw TypeError(
2167
- 'each key must be a number of string; got ' + type);
2168
- }
2001
+ * @return {!Promise<void>} A promise that will be resolved when all keys
2002
+ * have been typed.
2003
+ */
2004
+ async sendKeys(...args) {
2005
+ let keys = [];
2006
+ (await Promise.all(args)).forEach(key => {
2007
+ let type = typeof key;
2008
+ if (type === 'number') {
2009
+ key = String(key);
2010
+ } else if (type !== 'string') {
2011
+ throw TypeError('each key must be a number of string; got ' + type);
2012
+ }
2169
2013
 
2170
- // The W3C protocol requires keys to be specified as an array where
2171
- // each element is a single key.
2172
- ret.push.apply(ret, key.split(''));
2173
- });
2174
- return ret;
2175
- });
2014
+ // The W3C protocol requires keys to be specified as an array where
2015
+ // each element is a single key.
2016
+ keys.push(...key.split(''));
2017
+ });
2176
2018
 
2177
2019
  if (!this.driver_.fileDetector_) {
2178
- return this.schedule_(
2179
- new command.Command(command.Name.SEND_KEYS_TO_ELEMENT).
2180
- setParameter('text', keys.then(keys => keys.join(''))).
2181
- setParameter('value', keys),
2182
- 'WebElement.sendKeys()');
2020
+ return this.execute_(
2021
+ new command.Command(command.Name.SEND_KEYS_TO_ELEMENT)
2022
+ .setParameter('text', keys.join(''))
2023
+ .setParameter('value', keys));
2183
2024
  }
2184
2025
 
2185
- // Suppress unhandled rejection errors until the flow executes the command.
2186
- keys.catch(function() {});
2187
-
2188
- var element = this;
2189
- return this.getDriver().controlFlow().execute(function() {
2190
- return keys.then(function(keys) {
2191
- return element.driver_.fileDetector_
2192
- .handleFile(element.driver_, keys.join(''));
2193
- }).then(function(keys) {
2194
- return element.schedule_(
2195
- new command.Command(command.Name.SEND_KEYS_TO_ELEMENT).
2196
- setParameter('text', keys).
2197
- setParameter('value', keys.split('')),
2198
- 'WebElement.sendKeys()');
2199
- });
2200
- }, 'WebElement.sendKeys()');
2026
+ keys =
2027
+ await this.driver_.fileDetector_.handleFile(
2028
+ this.driver_, keys.join(''));
2029
+ return this.execute_(
2030
+ new command.Command(command.Name.SEND_KEYS_TO_ELEMENT)
2031
+ .setParameter('text', keys)
2032
+ .setParameter('value', keys.split('')));
2201
2033
  }
2202
2034
 
2203
2035
  /**
2204
- * Schedules a command to query for the tag/node name of this element.
2205
- * @return {!promise.Thenable<string>} A promise that will be
2206
- * resolved with the element's tag name.
2036
+ * Retrieves the element's tag name.
2037
+ *
2038
+ * @return {!Promise<string>} A promise that will be resolved with the
2039
+ * element's tag name.
2207
2040
  */
2208
2041
  getTagName() {
2209
- return this.schedule_(
2210
- new command.Command(command.Name.GET_ELEMENT_TAG_NAME),
2211
- 'WebElement.getTagName()');
2042
+ return this.execute_(
2043
+ new command.Command(command.Name.GET_ELEMENT_TAG_NAME));
2212
2044
  }
2213
2045
 
2214
2046
  /**
2215
- * Schedules a command to query for the computed style of the element
2216
- * represented by this instance. If the element inherits the named style from
2217
- * its parent, the parent will be queried for its value. Where possible, color
2218
- * values will be converted to their hex representation (e.g. #00ff00 instead
2219
- * of rgb(0, 255, 0)).
2047
+ * Retrieves the value of a computed style property for this instance. If
2048
+ * the element inherits the named style from its parent, the parent will be
2049
+ * queried for its value. Where possible, color values will be converted to
2050
+ * their hex representation (e.g. #00ff00 instead of rgb(0, 255, 0)).
2220
2051
  *
2221
2052
  * _Warning:_ the value returned will be as the browser interprets it, so
2222
2053
  * it may be tricky to form a proper assertion.
2223
2054
  *
2224
2055
  * @param {string} cssStyleProperty The name of the CSS style property to look
2225
2056
  * up.
2226
- * @return {!promise.Thenable<string>} A promise that will be
2227
- * resolved with the requested CSS value.
2057
+ * @return {!Promise<string>} A promise that will be resolved with the
2058
+ * requested CSS value.
2228
2059
  */
2229
2060
  getCssValue(cssStyleProperty) {
2230
2061
  var name = command.Name.GET_ELEMENT_VALUE_OF_CSS_PROPERTY;
2231
- return this.schedule_(
2062
+ return this.execute_(
2232
2063
  new command.Command(name).
2233
- setParameter('propertyName', cssStyleProperty),
2234
- 'WebElement.getCssValue(' + cssStyleProperty + ')');
2064
+ setParameter('propertyName', cssStyleProperty));
2235
2065
  }
2236
2066
 
2237
2067
  /**
2238
- * Schedules a command to query for the value of the given attribute of the
2239
- * element. Will return the current value, even if it has been modified after
2240
- * the page has been loaded. More exactly, this method will return the value
2068
+ * Retrieves the current value of the given attribute of this element.
2069
+ * Will return the current value, even if it has been modified after the page
2070
+ * has been loaded. More exactly, this method will return the value
2241
2071
  * of the given attribute, unless that attribute is not present, in which case
2242
2072
  * the value of the property with the same name is returned. If neither value
2243
2073
  * is set, null is returned (for example, the "value" property of a textarea
@@ -2259,131 +2089,122 @@ class WebElement {
2259
2089
  * - "readonly"
2260
2090
  *
2261
2091
  * @param {string} attributeName The name of the attribute to query.
2262
- * @return {!promise.Thenable<?string>} A promise that will be
2092
+ * @return {!Promise<?string>} A promise that will be
2263
2093
  * resolved with the attribute's value. The returned value will always be
2264
2094
  * either a string or null.
2265
2095
  */
2266
2096
  getAttribute(attributeName) {
2267
- return this.schedule_(
2097
+ return this.execute_(
2268
2098
  new command.Command(command.Name.GET_ELEMENT_ATTRIBUTE).
2269
- setParameter('name', attributeName),
2270
- 'WebElement.getAttribute(' + attributeName + ')');
2099
+ setParameter('name', attributeName));
2271
2100
  }
2272
2101
 
2273
2102
  /**
2274
2103
  * Get the visible (i.e. not hidden by CSS) innerText of this element,
2275
2104
  * including sub-elements, without any leading or trailing whitespace.
2276
2105
  *
2277
- * @return {!promise.Thenable<string>} A promise that will be
2106
+ * @return {!Promise<string>} A promise that will be
2278
2107
  * resolved with the element's visible text.
2279
2108
  */
2280
2109
  getText() {
2281
- return this.schedule_(
2282
- new command.Command(command.Name.GET_ELEMENT_TEXT),
2283
- 'WebElement.getText()');
2110
+ return this.execute_(new command.Command(command.Name.GET_ELEMENT_TEXT));
2284
2111
  }
2285
2112
 
2286
2113
  /**
2287
- * Schedules a command to compute the size of this element's bounding box, in
2288
- * pixels.
2289
- * @return {!promise.Thenable<{width: number, height: number}>} A
2290
- * promise that will be resolved with the element's size as a
2291
- * {@code {width:number, height:number}} object.
2292
- */
2293
- getSize() {
2294
- return this.schedule_(
2295
- new command.Command(command.Name.GET_ELEMENT_SIZE),
2296
- 'WebElement.getSize()');
2297
- }
2298
-
2299
- /**
2300
- * Schedules a command to compute the location of this element in page space.
2301
- * @return {!promise.Thenable<{x: number, y: number}>} A promise that
2302
- * will be resolved to the element's location as a
2303
- * {@code {x:number, y:number}} object.
2114
+ * Returns an object describing an element's location, in pixels relative to
2115
+ * the document element, and the element's size in pixels.
2116
+ *
2117
+ * @return {!Promise<{width: number, height: number, x: number, y: number}>}
2118
+ * A promise that will resolve with the element's rect.
2304
2119
  */
2305
- getLocation() {
2306
- return this.schedule_(
2307
- new command.Command(command.Name.GET_ELEMENT_LOCATION),
2308
- 'WebElement.getLocation()');
2120
+ async getRect() {
2121
+ try {
2122
+ return await this.execute_(
2123
+ new command.Command(command.Name.GET_ELEMENT_RECT));
2124
+ } catch (err) {
2125
+ if (err instanceof error.UnknownCommandError) {
2126
+ const {width, height} =
2127
+ await this.execute_(
2128
+ new command.Command(command.Name.GET_ELEMENT_SIZE));
2129
+ const {x, y} =
2130
+ await this.execute_(
2131
+ new command.Command(command.Name.GET_ELEMENT_LOCATION));
2132
+ return {x, y, width, height};
2133
+ }
2134
+ }
2309
2135
  }
2310
2136
 
2311
2137
  /**
2312
- * Schedules a command to query whether the DOM element represented by this
2313
- * instance is enabled, as dictated by the {@code disabled} attribute.
2314
- * @return {!promise.Thenable<boolean>} A promise that will be
2138
+ * Tests whether this element is enabled, as dictated by the `disabled`
2139
+ * attribute.
2140
+ *
2141
+ * @return {!Promise<boolean>} A promise that will be
2315
2142
  * resolved with whether this element is currently enabled.
2316
2143
  */
2317
2144
  isEnabled() {
2318
- return this.schedule_(
2319
- new command.Command(command.Name.IS_ELEMENT_ENABLED),
2320
- 'WebElement.isEnabled()');
2145
+ return this.execute_(new command.Command(command.Name.IS_ELEMENT_ENABLED));
2321
2146
  }
2322
2147
 
2323
2148
  /**
2324
- * Schedules a command to query whether this element is selected.
2325
- * @return {!promise.Thenable<boolean>} A promise that will be
2149
+ * Tests whether this element is selected.
2150
+ *
2151
+ * @return {!Promise<boolean>} A promise that will be
2326
2152
  * resolved with whether this element is currently selected.
2327
2153
  */
2328
2154
  isSelected() {
2329
- return this.schedule_(
2330
- new command.Command(command.Name.IS_ELEMENT_SELECTED),
2331
- 'WebElement.isSelected()');
2155
+ return this.execute_(
2156
+ new command.Command(command.Name.IS_ELEMENT_SELECTED));
2332
2157
  }
2333
2158
 
2334
2159
  /**
2335
- * Schedules a command to submit the form containing this element (or this
2336
- * element if it is a FORM element). This command is a no-op if the element is
2337
- * not contained in a form.
2338
- * @return {!promise.Thenable<void>} A promise that will be resolved
2160
+ * Submits the form containing this element (or this element if it is itself
2161
+ * a FORM element). his command is a no-op if the element is not contained in
2162
+ * a form.
2163
+ *
2164
+ * @return {!Promise<void>} A promise that will be resolved
2339
2165
  * when the form has been submitted.
2340
2166
  */
2341
2167
  submit() {
2342
- return this.schedule_(
2343
- new command.Command(command.Name.SUBMIT_ELEMENT),
2344
- 'WebElement.submit()');
2168
+ return this.execute_(new command.Command(command.Name.SUBMIT_ELEMENT));
2345
2169
  }
2346
2170
 
2347
2171
  /**
2348
- * Schedules a command to clear the `value` of this element. This command has
2349
- * no effect if the underlying DOM element is neither a text INPUT element
2350
- * nor a TEXTAREA element.
2351
- * @return {!promise.Thenable<void>} A promise that will be resolved
2172
+ * Clear the `value` of this element. This command has no effect if the
2173
+ * underlying DOM element is neither a text INPUT element nor a TEXTAREA
2174
+ * element.
2175
+ *
2176
+ * @return {!Promise<void>} A promise that will be resolved
2352
2177
  * when the element has been cleared.
2353
2178
  */
2354
2179
  clear() {
2355
- return this.schedule_(
2356
- new command.Command(command.Name.CLEAR_ELEMENT),
2357
- 'WebElement.clear()');
2180
+ return this.execute_(new command.Command(command.Name.CLEAR_ELEMENT));
2358
2181
  }
2359
2182
 
2360
2183
  /**
2361
- * Schedules a command to test whether this element is currently displayed.
2362
- * @return {!promise.Thenable<boolean>} A promise that will be
2184
+ * Test whether this element is currently displayed.
2185
+ *
2186
+ * @return {!Promise<boolean>} A promise that will be
2363
2187
  * resolved with whether this element is currently visible on the page.
2364
2188
  */
2365
2189
  isDisplayed() {
2366
- return this.schedule_(
2367
- new command.Command(command.Name.IS_ELEMENT_DISPLAYED),
2368
- 'WebElement.isDisplayed()');
2190
+ return this.execute_(
2191
+ new command.Command(command.Name.IS_ELEMENT_DISPLAYED));
2369
2192
  }
2370
2193
 
2371
2194
  /**
2372
2195
  * Take a screenshot of the visible region encompassed by this element's
2373
2196
  * bounding rectangle.
2374
2197
  *
2375
- * @param {boolean=} opt_scroll Optional argument that indicates whether the
2198
+ * @param {boolean=} scroll Optional argument that indicates whether the
2376
2199
  * element should be scrolled into view before taking a screenshot.
2377
2200
  * Defaults to false.
2378
- * @return {!promise.Thenable<string>} A promise that will be
2201
+ * @return {!Promise<string>} A promise that will be
2379
2202
  * resolved to the screenshot as a base-64 encoded PNG.
2380
2203
  */
2381
- takeScreenshot(opt_scroll) {
2382
- var scroll = !!opt_scroll;
2383
- return this.schedule_(
2204
+ takeScreenshot(scroll = false) {
2205
+ return this.execute_(
2384
2206
  new command.Command(command.Name.TAKE_ELEMENT_SCREENSHOT)
2385
- .setParameter('scroll', scroll),
2386
- 'WebElement.takeScreenshot(' + scroll + ')');
2207
+ .setParameter('scroll', scroll));
2387
2208
  }
2388
2209
  }
2389
2210
 
@@ -2400,31 +2221,19 @@ class WebElement {
2400
2221
  * return el.click();
2401
2222
  * });
2402
2223
  *
2403
- * @implements {promise.CancellableThenable<!WebElement>}
2224
+ * @implements {IThenable<!WebElement>}
2404
2225
  * @final
2405
2226
  */
2406
2227
  class WebElementPromise extends WebElement {
2407
2228
  /**
2408
2229
  * @param {!WebDriver} driver The parent WebDriver instance for this
2409
2230
  * element.
2410
- * @param {!promise.Thenable<!WebElement>} el A promise
2231
+ * @param {!Promise<!WebElement>} el A promise
2411
2232
  * that will resolve to the promised element.
2412
2233
  */
2413
2234
  constructor(driver, el) {
2414
2235
  super(driver, 'unused');
2415
2236
 
2416
- /**
2417
- * Cancel operation is only supported if the wrapped thenable is also
2418
- * cancellable.
2419
- * @param {(string|Error)=} opt_reason
2420
- * @override
2421
- */
2422
- this.cancel = function(opt_reason) {
2423
- if (promise.CancellableThenable.isImplementation(el)) {
2424
- /** @type {!promise.CancellableThenable} */(el).cancel(opt_reason);
2425
- }
2426
- };
2427
-
2428
2237
  /** @override */
2429
2238
  this.then = el.then.bind(el);
2430
2239
 
@@ -2443,7 +2252,6 @@ class WebElementPromise extends WebElement {
2443
2252
  };
2444
2253
  }
2445
2254
  }
2446
- promise.CancellableThenable.addImplementation(WebElementPromise);
2447
2255
 
2448
2256
 
2449
2257
  //////////////////////////////////////////////////////////////////////////////
@@ -2469,60 +2277,41 @@ class Alert {
2469
2277
  /** @private {!WebDriver} */
2470
2278
  this.driver_ = driver;
2471
2279
 
2472
- /** @private {!promise.Thenable<string>} */
2473
- this.text_ = driver.controlFlow().promise(resolve => resolve(text));
2280
+ /** @private {!Promise<string>} */
2281
+ this.text_ = Promise.resolve(text);
2474
2282
  }
2475
2283
 
2476
2284
  /**
2477
2285
  * Retrieves the message text displayed with this alert. For instance, if the
2478
2286
  * alert were opened with alert("hello"), then this would return "hello".
2479
2287
  *
2480
- * @return {!promise.Thenable<string>} A promise that will be
2288
+ * @return {!Promise<string>} A promise that will be
2481
2289
  * resolved to the text displayed with this alert.
2482
2290
  */
2483
2291
  getText() {
2484
2292
  return this.text_;
2485
2293
  }
2486
2294
 
2487
- /**
2488
- * Sets the username and password in an alert prompting for credentials (such
2489
- * as a Basic HTTP Auth prompt). This method will implicitly
2490
- * {@linkplain #accept() submit} the dialog.
2491
- *
2492
- * @param {string} username The username to send.
2493
- * @param {string} password The password to send.
2494
- * @return {!promise.Thenable<void>} A promise that will be resolved when this
2495
- * command has completed.
2496
- */
2497
- authenticateAs(username, password) {
2498
- return this.driver_.schedule(
2499
- new command.Command(command.Name.SET_ALERT_CREDENTIALS),
2500
- 'WebDriver.switchTo().alert()'
2501
- + `.authenticateAs("${username}", "${password}")`);
2502
- }
2503
-
2504
2295
  /**
2505
2296
  * Accepts this alert.
2506
2297
  *
2507
- * @return {!promise.Thenable<void>} A promise that will be resolved
2298
+ * @return {!Promise<void>} A promise that will be resolved
2508
2299
  * when this command has completed.
2509
2300
  */
2510
2301
  accept() {
2511
- return this.driver_.schedule(
2512
- new command.Command(command.Name.ACCEPT_ALERT),
2513
- 'WebDriver.switchTo().alert().accept()');
2302
+ return this.driver_.execute(
2303
+ new command.Command(command.Name.ACCEPT_ALERT));
2514
2304
  }
2515
2305
 
2516
2306
  /**
2517
2307
  * Dismisses this alert.
2518
2308
  *
2519
- * @return {!promise.Thenable<void>} A promise that will be resolved
2309
+ * @return {!Promise<void>} A promise that will be resolved
2520
2310
  * when this command has completed.
2521
2311
  */
2522
2312
  dismiss() {
2523
- return this.driver_.schedule(
2524
- new command.Command(command.Name.DISMISS_ALERT),
2525
- 'WebDriver.switchTo().alert().dismiss()');
2313
+ return this.driver_.execute(
2314
+ new command.Command(command.Name.DISMISS_ALERT));
2526
2315
  }
2527
2316
 
2528
2317
  /**
@@ -2531,14 +2320,13 @@ class Alert {
2531
2320
  * window.confirm).
2532
2321
  *
2533
2322
  * @param {string} text The text to set.
2534
- * @return {!promise.Thenable<void>} A promise that will be resolved
2323
+ * @return {!Promise<void>} A promise that will be resolved
2535
2324
  * when this command has completed.
2536
2325
  */
2537
2326
  sendKeys(text) {
2538
- return this.driver_.schedule(
2327
+ return this.driver_.execute(
2539
2328
  new command.Command(command.Name.SET_ALERT_TEXT).
2540
- setParameter('text', text),
2541
- 'WebDriver.switchTo().alert().sendKeys(' + text + ')');
2329
+ setParameter('text', text));
2542
2330
  }
2543
2331
  }
2544
2332
 
@@ -2554,31 +2342,19 @@ class Alert {
2554
2342
  * return alert.dismiss();
2555
2343
  * });
2556
2344
  *
2557
- * @implements {promise.CancellableThenable<!webdriver.Alert>}
2345
+ * @implements {IThenable<!Alert>}
2558
2346
  * @final
2559
2347
  */
2560
2348
  class AlertPromise extends Alert {
2561
2349
  /**
2562
2350
  * @param {!WebDriver} driver The driver controlling the browser this
2563
2351
  * alert is attached to.
2564
- * @param {!promise.Thenable<!Alert>} alert A thenable
2352
+ * @param {!Promise<!Alert>} alert A thenable
2565
2353
  * that will be fulfilled with the promised alert.
2566
2354
  */
2567
2355
  constructor(driver, alert) {
2568
2356
  super(driver, 'unused');
2569
2357
 
2570
- /**
2571
- * Cancel operation is only supported if the wrapped thenable is also
2572
- * cancellable.
2573
- * @param {(string|Error)=} opt_reason
2574
- * @override
2575
- */
2576
- this.cancel = function(opt_reason) {
2577
- if (promise.CancellableThenable.isImplementation(alert)) {
2578
- /** @type {!promise.CancellableThenable} */(alert).cancel(opt_reason);
2579
- }
2580
- };
2581
-
2582
2358
  /** @override */
2583
2359
  this.then = alert.then.bind(alert);
2584
2360
 
@@ -2595,16 +2371,6 @@ class AlertPromise extends Alert {
2595
2371
  });
2596
2372
  };
2597
2373
 
2598
- /**
2599
- * Defers action until the alert has been located.
2600
- * @override
2601
- */
2602
- this.authenticateAs = function(username, password) {
2603
- return alert.then(function(alert) {
2604
- return alert.authenticateAs(username, password);
2605
- });
2606
- };
2607
-
2608
2374
  /**
2609
2375
  * Defers action until the alert has been located.
2610
2376
  * @override
@@ -2636,25 +2402,23 @@ class AlertPromise extends Alert {
2636
2402
  };
2637
2403
  }
2638
2404
  }
2639
- promise.CancellableThenable.addImplementation(AlertPromise);
2640
2405
 
2641
2406
 
2642
2407
  // PUBLIC API
2643
2408
 
2644
2409
 
2645
2410
  module.exports = {
2646
- Alert: Alert,
2647
- AlertPromise: AlertPromise,
2648
- Condition: Condition,
2649
- Logs: Logs,
2650
- Navigation: Navigation,
2651
- Options: Options,
2652
- TargetLocator: TargetLocator,
2653
- Timeouts: Timeouts,
2654
- IWebDriver: IWebDriver,
2655
- WebDriver: WebDriver,
2656
- WebElement: WebElement,
2657
- WebElementCondition: WebElementCondition,
2658
- WebElementPromise: WebElementPromise,
2659
- Window: Window
2411
+ Alert,
2412
+ AlertPromise,
2413
+ Condition,
2414
+ Logs,
2415
+ Navigation,
2416
+ Options,
2417
+ TargetLocator,
2418
+ IWebDriver,
2419
+ WebDriver,
2420
+ WebElement,
2421
+ WebElementCondition,
2422
+ WebElementPromise,
2423
+ Window
2660
2424
  };