capybara 3.3.0 → 3.40.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (308) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +1 -0
  3. data/History.md +803 -13
  4. data/License.txt +1 -1
  5. data/README.md +257 -84
  6. data/lib/capybara/config.rb +25 -9
  7. data/lib/capybara/cucumber.rb +1 -1
  8. data/lib/capybara/driver/base.rb +17 -3
  9. data/lib/capybara/driver/node.rb +31 -6
  10. data/lib/capybara/dsl.rb +9 -7
  11. data/lib/capybara/helpers.rb +31 -7
  12. data/lib/capybara/minitest/spec.rb +180 -88
  13. data/lib/capybara/minitest.rb +262 -149
  14. data/lib/capybara/node/actions.rb +202 -116
  15. data/lib/capybara/node/base.rb +34 -19
  16. data/lib/capybara/node/document.rb +14 -2
  17. data/lib/capybara/node/document_matchers.rb +10 -12
  18. data/lib/capybara/node/element.rb +269 -115
  19. data/lib/capybara/node/finders.rb +99 -77
  20. data/lib/capybara/node/matchers.rb +327 -151
  21. data/lib/capybara/node/simple.rb +48 -13
  22. data/lib/capybara/node/whitespace_normalizer.rb +81 -0
  23. data/lib/capybara/queries/active_element_query.rb +18 -0
  24. data/lib/capybara/queries/ancestor_query.rb +8 -9
  25. data/lib/capybara/queries/base_query.rb +23 -16
  26. data/lib/capybara/queries/current_path_query.rb +16 -6
  27. data/lib/capybara/queries/match_query.rb +1 -0
  28. data/lib/capybara/queries/selector_query.rb +587 -130
  29. data/lib/capybara/queries/sibling_query.rb +8 -6
  30. data/lib/capybara/queries/style_query.rb +6 -2
  31. data/lib/capybara/queries/text_query.rb +28 -14
  32. data/lib/capybara/queries/title_query.rb +2 -2
  33. data/lib/capybara/rack_test/browser.rb +92 -25
  34. data/lib/capybara/rack_test/driver.rb +16 -7
  35. data/lib/capybara/rack_test/errors.rb +6 -0
  36. data/lib/capybara/rack_test/form.rb +68 -41
  37. data/lib/capybara/rack_test/node.rb +106 -39
  38. data/lib/capybara/rails.rb +1 -1
  39. data/lib/capybara/registration_container.rb +41 -0
  40. data/lib/capybara/registrations/drivers.rb +42 -0
  41. data/lib/capybara/registrations/patches/puma_ssl.rb +29 -0
  42. data/lib/capybara/registrations/servers.rb +66 -0
  43. data/lib/capybara/result.rb +75 -52
  44. data/lib/capybara/rspec/features.rb +7 -7
  45. data/lib/capybara/rspec/matcher_proxies.rb +39 -18
  46. data/lib/capybara/rspec/matchers/base.rb +113 -0
  47. data/lib/capybara/rspec/matchers/become_closed.rb +33 -0
  48. data/lib/capybara/rspec/matchers/compound.rb +88 -0
  49. data/lib/capybara/rspec/matchers/count_sugar.rb +37 -0
  50. data/lib/capybara/rspec/matchers/have_ancestor.rb +28 -0
  51. data/lib/capybara/rspec/matchers/have_current_path.rb +29 -0
  52. data/lib/capybara/rspec/matchers/have_selector.rb +69 -0
  53. data/lib/capybara/rspec/matchers/have_sibling.rb +27 -0
  54. data/lib/capybara/rspec/matchers/have_text.rb +33 -0
  55. data/lib/capybara/rspec/matchers/have_title.rb +29 -0
  56. data/lib/capybara/rspec/matchers/match_selector.rb +27 -0
  57. data/lib/capybara/rspec/matchers/match_style.rb +43 -0
  58. data/lib/capybara/rspec/matchers/spatial_sugar.rb +39 -0
  59. data/lib/capybara/rspec/matchers.rb +141 -339
  60. data/lib/capybara/rspec.rb +2 -0
  61. data/lib/capybara/selector/builders/css_builder.rb +84 -0
  62. data/lib/capybara/selector/builders/xpath_builder.rb +71 -0
  63. data/lib/capybara/selector/css.rb +27 -25
  64. data/lib/capybara/selector/definition/button.rb +68 -0
  65. data/lib/capybara/selector/definition/checkbox.rb +26 -0
  66. data/lib/capybara/selector/definition/css.rb +10 -0
  67. data/lib/capybara/selector/definition/datalist_input.rb +35 -0
  68. data/lib/capybara/selector/definition/datalist_option.rb +25 -0
  69. data/lib/capybara/selector/definition/element.rb +28 -0
  70. data/lib/capybara/selector/definition/field.rb +40 -0
  71. data/lib/capybara/selector/definition/fieldset.rb +14 -0
  72. data/lib/capybara/selector/definition/file_field.rb +13 -0
  73. data/lib/capybara/selector/definition/fillable_field.rb +33 -0
  74. data/lib/capybara/selector/definition/frame.rb +17 -0
  75. data/lib/capybara/selector/definition/id.rb +6 -0
  76. data/lib/capybara/selector/definition/label.rb +62 -0
  77. data/lib/capybara/selector/definition/link.rb +55 -0
  78. data/lib/capybara/selector/definition/link_or_button.rb +16 -0
  79. data/lib/capybara/selector/definition/option.rb +27 -0
  80. data/lib/capybara/selector/definition/radio_button.rb +27 -0
  81. data/lib/capybara/selector/definition/select.rb +81 -0
  82. data/lib/capybara/selector/definition/table.rb +109 -0
  83. data/lib/capybara/selector/definition/table_row.rb +21 -0
  84. data/lib/capybara/selector/definition/xpath.rb +5 -0
  85. data/lib/capybara/selector/definition.rb +280 -0
  86. data/lib/capybara/selector/filter.rb +1 -0
  87. data/lib/capybara/selector/filter_set.rb +73 -25
  88. data/lib/capybara/selector/filters/base.rb +24 -5
  89. data/lib/capybara/selector/filters/expression_filter.rb +3 -3
  90. data/lib/capybara/selector/filters/locator_filter.rb +29 -0
  91. data/lib/capybara/selector/filters/node_filter.rb +16 -2
  92. data/lib/capybara/selector/regexp_disassembler.rb +211 -0
  93. data/lib/capybara/selector/selector.rb +85 -348
  94. data/lib/capybara/selector/xpath_extensions.rb +17 -0
  95. data/lib/capybara/selector.rb +474 -447
  96. data/lib/capybara/selenium/atoms/getAttribute.min.js +1 -0
  97. data/lib/capybara/selenium/atoms/isDisplayed.min.js +1 -0
  98. data/lib/capybara/selenium/atoms/src/getAttribute.js +161 -0
  99. data/lib/capybara/selenium/atoms/src/isDisplayed.js +454 -0
  100. data/lib/capybara/selenium/driver.rb +255 -143
  101. data/lib/capybara/selenium/driver_specializations/chrome_driver.rb +93 -11
  102. data/lib/capybara/selenium/driver_specializations/edge_driver.rb +128 -0
  103. data/lib/capybara/selenium/driver_specializations/firefox_driver.rb +84 -0
  104. data/lib/capybara/selenium/driver_specializations/internet_explorer_driver.rb +26 -0
  105. data/lib/capybara/selenium/driver_specializations/safari_driver.rb +24 -0
  106. data/lib/capybara/selenium/extensions/file_input_click_emulation.rb +34 -0
  107. data/lib/capybara/selenium/extensions/find.rb +110 -0
  108. data/lib/capybara/selenium/extensions/html5_drag.rb +229 -0
  109. data/lib/capybara/selenium/extensions/modifier_keys_stack.rb +28 -0
  110. data/lib/capybara/selenium/extensions/scroll.rb +76 -0
  111. data/lib/capybara/selenium/node.rb +436 -134
  112. data/lib/capybara/selenium/nodes/chrome_node.rb +125 -0
  113. data/lib/capybara/selenium/nodes/edge_node.rb +110 -0
  114. data/lib/capybara/selenium/nodes/firefox_node.rb +136 -0
  115. data/lib/capybara/selenium/nodes/ie_node.rb +22 -0
  116. data/lib/capybara/selenium/nodes/safari_node.rb +118 -0
  117. data/lib/capybara/selenium/patches/atoms.rb +18 -0
  118. data/lib/capybara/selenium/patches/is_displayed.rb +16 -0
  119. data/lib/capybara/selenium/patches/logs.rb +45 -0
  120. data/lib/capybara/selenium/patches/pause_duration_fix.rb +9 -0
  121. data/lib/capybara/selenium/patches/persistent_client.rb +20 -0
  122. data/lib/capybara/server/animation_disabler.rb +56 -19
  123. data/lib/capybara/server/checker.rb +9 -3
  124. data/lib/capybara/server/middleware.rb +28 -12
  125. data/lib/capybara/server.rb +33 -10
  126. data/lib/capybara/session/config.rb +34 -10
  127. data/lib/capybara/session/matchers.rb +23 -16
  128. data/lib/capybara/session.rb +230 -170
  129. data/lib/capybara/spec/public/jquery.js +5 -5
  130. data/lib/capybara/spec/public/offset.js +6 -0
  131. data/lib/capybara/spec/public/test.js +121 -8
  132. data/lib/capybara/spec/session/accept_alert_spec.rb +11 -11
  133. data/lib/capybara/spec/session/accept_confirm_spec.rb +3 -3
  134. data/lib/capybara/spec/session/accept_prompt_spec.rb +9 -10
  135. data/lib/capybara/spec/session/active_element_spec.rb +31 -0
  136. data/lib/capybara/spec/session/all_spec.rb +127 -40
  137. data/lib/capybara/spec/session/ancestor_spec.rb +24 -19
  138. data/lib/capybara/spec/session/assert_all_of_selectors_spec.rb +67 -38
  139. data/lib/capybara/spec/session/assert_current_path_spec.rb +21 -18
  140. data/lib/capybara/spec/session/assert_selector_spec.rb +52 -58
  141. data/lib/capybara/spec/session/assert_style_spec.rb +7 -7
  142. data/lib/capybara/spec/session/assert_text_spec.rb +74 -50
  143. data/lib/capybara/spec/session/assert_title_spec.rb +12 -12
  144. data/lib/capybara/spec/session/attach_file_spec.rb +126 -72
  145. data/lib/capybara/spec/session/body_spec.rb +6 -6
  146. data/lib/capybara/spec/session/check_spec.rb +102 -47
  147. data/lib/capybara/spec/session/choose_spec.rb +58 -32
  148. data/lib/capybara/spec/session/click_button_spec.rb +219 -163
  149. data/lib/capybara/spec/session/click_link_or_button_spec.rb +49 -23
  150. data/lib/capybara/spec/session/click_link_spec.rb +77 -54
  151. data/lib/capybara/spec/session/current_scope_spec.rb +8 -8
  152. data/lib/capybara/spec/session/current_url_spec.rb +38 -29
  153. data/lib/capybara/spec/session/dismiss_confirm_spec.rb +3 -3
  154. data/lib/capybara/spec/session/dismiss_prompt_spec.rb +2 -2
  155. data/lib/capybara/spec/session/element/assert_match_selector_spec.rb +8 -8
  156. data/lib/capybara/spec/session/element/match_css_spec.rb +16 -10
  157. data/lib/capybara/spec/session/element/match_xpath_spec.rb +6 -6
  158. data/lib/capybara/spec/session/element/matches_selector_spec.rb +68 -56
  159. data/lib/capybara/spec/session/evaluate_async_script_spec.rb +7 -7
  160. data/lib/capybara/spec/session/evaluate_script_spec.rb +28 -8
  161. data/lib/capybara/spec/session/execute_script_spec.rb +8 -7
  162. data/lib/capybara/spec/session/fill_in_spec.rb +101 -46
  163. data/lib/capybara/spec/session/find_button_spec.rb +23 -23
  164. data/lib/capybara/spec/session/find_by_id_spec.rb +7 -7
  165. data/lib/capybara/spec/session/find_field_spec.rb +32 -30
  166. data/lib/capybara/spec/session/find_link_spec.rb +31 -21
  167. data/lib/capybara/spec/session/find_spec.rb +244 -141
  168. data/lib/capybara/spec/session/first_spec.rb +43 -43
  169. data/lib/capybara/spec/session/frame/frame_title_spec.rb +5 -5
  170. data/lib/capybara/spec/session/frame/frame_url_spec.rb +5 -5
  171. data/lib/capybara/spec/session/frame/switch_to_frame_spec.rb +30 -18
  172. data/lib/capybara/spec/session/frame/within_frame_spec.rb +45 -18
  173. data/lib/capybara/spec/session/go_back_spec.rb +1 -1
  174. data/lib/capybara/spec/session/go_forward_spec.rb +1 -1
  175. data/lib/capybara/spec/session/has_all_selectors_spec.rb +23 -23
  176. data/lib/capybara/spec/session/has_ancestor_spec.rb +46 -0
  177. data/lib/capybara/spec/session/has_any_selectors_spec.rb +29 -0
  178. data/lib/capybara/spec/session/has_button_spec.rb +94 -13
  179. data/lib/capybara/spec/session/has_css_spec.rb +272 -132
  180. data/lib/capybara/spec/session/has_current_path_spec.rb +50 -35
  181. data/lib/capybara/spec/session/has_element_spec.rb +47 -0
  182. data/lib/capybara/spec/session/has_field_spec.rb +137 -58
  183. data/lib/capybara/spec/session/has_link_spec.rb +44 -4
  184. data/lib/capybara/spec/session/has_none_selectors_spec.rb +31 -31
  185. data/lib/capybara/spec/session/has_select_spec.rb +84 -50
  186. data/lib/capybara/spec/session/has_selector_spec.rb +111 -71
  187. data/lib/capybara/spec/session/has_sibling_spec.rb +50 -0
  188. data/lib/capybara/spec/session/has_table_spec.rb +181 -4
  189. data/lib/capybara/spec/session/has_text_spec.rb +101 -53
  190. data/lib/capybara/spec/session/has_title_spec.rb +19 -14
  191. data/lib/capybara/spec/session/has_xpath_spec.rb +56 -38
  192. data/lib/capybara/spec/session/headers_spec.rb +1 -1
  193. data/lib/capybara/spec/session/html_spec.rb +13 -6
  194. data/lib/capybara/spec/session/matches_style_spec.rb +37 -0
  195. data/lib/capybara/spec/session/node_spec.rb +894 -142
  196. data/lib/capybara/spec/session/node_wrapper_spec.rb +10 -7
  197. data/lib/capybara/spec/session/refresh_spec.rb +9 -7
  198. data/lib/capybara/spec/session/reset_session_spec.rb +63 -35
  199. data/lib/capybara/spec/session/response_code_spec.rb +1 -1
  200. data/lib/capybara/spec/session/save_and_open_page_spec.rb +2 -2
  201. data/lib/capybara/spec/session/save_and_open_screenshot_spec.rb +2 -2
  202. data/lib/capybara/spec/session/save_page_spec.rb +37 -37
  203. data/lib/capybara/spec/session/save_screenshot_spec.rb +10 -10
  204. data/lib/capybara/spec/session/screenshot_spec.rb +2 -2
  205. data/lib/capybara/spec/session/scroll_spec.rb +119 -0
  206. data/lib/capybara/spec/session/select_spec.rb +85 -85
  207. data/lib/capybara/spec/session/selectors_spec.rb +49 -18
  208. data/lib/capybara/spec/session/sibling_spec.rb +9 -9
  209. data/lib/capybara/spec/session/text_spec.rb +25 -24
  210. data/lib/capybara/spec/session/title_spec.rb +7 -6
  211. data/lib/capybara/spec/session/uncheck_spec.rb +33 -21
  212. data/lib/capybara/spec/session/unselect_spec.rb +37 -37
  213. data/lib/capybara/spec/session/visit_spec.rb +68 -49
  214. data/lib/capybara/spec/session/window/become_closed_spec.rb +20 -17
  215. data/lib/capybara/spec/session/window/current_window_spec.rb +1 -1
  216. data/lib/capybara/spec/session/window/switch_to_window_spec.rb +20 -16
  217. data/lib/capybara/spec/session/window/window_opened_by_spec.rb +6 -2
  218. data/lib/capybara/spec/session/window/window_spec.rb +62 -63
  219. data/lib/capybara/spec/session/window/windows_spec.rb +5 -1
  220. data/lib/capybara/spec/session/window/within_window_spec.rb +14 -14
  221. data/lib/capybara/spec/session/within_spec.rb +79 -42
  222. data/lib/capybara/spec/spec_helper.rb +41 -53
  223. data/lib/capybara/spec/test_app.rb +132 -43
  224. data/lib/capybara/spec/views/animated.erb +49 -0
  225. data/lib/capybara/spec/views/form.erb +139 -42
  226. data/lib/capybara/spec/views/frame_child.erb +4 -3
  227. data/lib/capybara/spec/views/frame_one.erb +2 -1
  228. data/lib/capybara/spec/views/frame_parent.erb +1 -1
  229. data/lib/capybara/spec/views/frame_two.erb +1 -1
  230. data/lib/capybara/spec/views/initial_alert.erb +2 -1
  231. data/lib/capybara/spec/views/layout.erb +10 -0
  232. data/lib/capybara/spec/views/obscured.erb +47 -0
  233. data/lib/capybara/spec/views/offset.erb +33 -0
  234. data/lib/capybara/spec/views/path.erb +2 -2
  235. data/lib/capybara/spec/views/popup_one.erb +1 -1
  236. data/lib/capybara/spec/views/popup_two.erb +1 -1
  237. data/lib/capybara/spec/views/react.erb +45 -0
  238. data/lib/capybara/spec/views/scroll.erb +21 -0
  239. data/lib/capybara/spec/views/spatial.erb +31 -0
  240. data/lib/capybara/spec/views/tables.erb +67 -0
  241. data/lib/capybara/spec/views/with_animation.erb +39 -4
  242. data/lib/capybara/spec/views/with_base_tag.erb +2 -2
  243. data/lib/capybara/spec/views/with_dragula.erb +24 -0
  244. data/lib/capybara/spec/views/with_fixed_header_footer.erb +2 -1
  245. data/lib/capybara/spec/views/with_hover.erb +3 -2
  246. data/lib/capybara/spec/views/with_hover1.erb +10 -0
  247. data/lib/capybara/spec/views/with_html.erb +37 -9
  248. data/lib/capybara/spec/views/with_html5_svg.erb +20 -0
  249. data/lib/capybara/spec/views/with_jquery_animation.erb +24 -0
  250. data/lib/capybara/spec/views/with_js.erb +26 -5
  251. data/lib/capybara/spec/views/with_jstree.erb +26 -0
  252. data/lib/capybara/spec/views/with_namespace.erb +1 -0
  253. data/lib/capybara/spec/views/with_scope.erb +2 -2
  254. data/lib/capybara/spec/views/with_scope_other.erb +6 -0
  255. data/lib/capybara/spec/views/with_shadow.erb +31 -0
  256. data/lib/capybara/spec/views/with_slow_unload.erb +2 -1
  257. data/lib/capybara/spec/views/with_sortable_js.erb +21 -0
  258. data/lib/capybara/spec/views/with_unload_alert.erb +1 -0
  259. data/lib/capybara/spec/views/with_windows.erb +1 -1
  260. data/lib/capybara/spec/views/within_frames.erb +1 -1
  261. data/lib/capybara/version.rb +1 -1
  262. data/lib/capybara/window.rb +19 -25
  263. data/lib/capybara.rb +126 -111
  264. data/spec/basic_node_spec.rb +59 -34
  265. data/spec/capybara_spec.rb +56 -44
  266. data/spec/counter_spec.rb +35 -0
  267. data/spec/css_builder_spec.rb +101 -0
  268. data/spec/css_splitter_spec.rb +8 -8
  269. data/spec/dsl_spec.rb +79 -52
  270. data/spec/filter_set_spec.rb +9 -9
  271. data/spec/fixtures/selenium_driver_rspec_failure.rb +4 -4
  272. data/spec/fixtures/selenium_driver_rspec_success.rb +4 -4
  273. data/spec/minitest_spec.rb +45 -7
  274. data/spec/minitest_spec_spec.rb +87 -64
  275. data/spec/per_session_config_spec.rb +6 -6
  276. data/spec/rack_test_spec.rb +172 -116
  277. data/spec/regexp_dissassembler_spec.rb +250 -0
  278. data/spec/result_spec.rb +80 -72
  279. data/spec/rspec/features_spec.rb +21 -16
  280. data/spec/rspec/scenarios_spec.rb +10 -6
  281. data/spec/rspec/shared_spec_matchers.rb +407 -365
  282. data/spec/rspec/views_spec.rb +3 -3
  283. data/spec/rspec_matchers_spec.rb +35 -10
  284. data/spec/rspec_spec.rb +63 -41
  285. data/spec/sauce_spec_chrome.rb +43 -0
  286. data/spec/selector_spec.rb +334 -89
  287. data/spec/selenium_spec_chrome.rb +176 -62
  288. data/spec/selenium_spec_chrome_remote.rb +54 -14
  289. data/spec/selenium_spec_edge.rb +41 -8
  290. data/spec/selenium_spec_firefox.rb +228 -0
  291. data/spec/selenium_spec_firefox_remote.rb +94 -0
  292. data/spec/selenium_spec_ie.rb +129 -11
  293. data/spec/selenium_spec_safari.rb +162 -0
  294. data/spec/server_spec.rb +171 -97
  295. data/spec/session_spec.rb +34 -18
  296. data/spec/shared_selenium_node.rb +79 -0
  297. data/spec/shared_selenium_session.rb +344 -80
  298. data/spec/spec_helper.rb +124 -2
  299. data/spec/whitespace_normalizer_spec.rb +54 -0
  300. data/spec/xpath_builder_spec.rb +93 -0
  301. metadata +326 -28
  302. data/lib/capybara/rspec/compound.rb +0 -94
  303. data/lib/capybara/selenium/driver_specializations/marionette_driver.rb +0 -31
  304. data/lib/capybara/selenium/nodes/marionette_node.rb +0 -31
  305. data/lib/capybara/spec/session/has_style_spec.rb +0 -25
  306. data/lib/capybara/spec/session/source_spec.rb +0 -0
  307. data/lib/capybara/spec/views/with_title.erb +0 -5
  308. data/spec/selenium_spec_marionette.rb +0 -167
@@ -4,53 +4,89 @@ require 'uri'
4
4
  require 'English'
5
5
 
6
6
  class Capybara::Selenium::Driver < Capybara::Driver::Base
7
+ include Capybara::Selenium::Find
8
+
7
9
  DEFAULT_OPTIONS = {
8
10
  browser: :firefox,
9
- clear_local_storage: false,
10
- clear_session_storage: false
11
+ clear_local_storage: nil,
12
+ clear_session_storage: nil
11
13
  }.freeze
12
- SPECIAL_OPTIONS = %i[browser clear_local_storage clear_session_storage].freeze
14
+ SPECIAL_OPTIONS = %i[browser clear_local_storage clear_session_storage timeout native_displayed].freeze
15
+ CAPS_VERSION = Gem::Requirement.new('< 4.8.0')
16
+
13
17
  attr_reader :app, :options
14
18
 
15
- def self.load_selenium
16
- require 'selenium-webdriver'
17
- warn "Warning: You're using an unsupported version of selenium-webdriver, please upgrade." if Gem::Version.new(Selenium::WebDriver::VERSION) < Gem::Version.new('3.5.0')
18
- rescue LoadError => e
19
- raise e if e.message !~ /selenium-webdriver/
20
- raise LoadError, "Capybara's selenium driver is unable to load `selenium-webdriver`, please install the gem and add `gem 'selenium-webdriver'` to your Gemfile if you are using bundler."
19
+ class << self
20
+ attr_reader :selenium_webdriver_version
21
+
22
+ def load_selenium
23
+ require 'selenium-webdriver'
24
+ require 'capybara/selenium/patches/atoms'
25
+ require 'capybara/selenium/patches/is_displayed'
26
+
27
+ # Look up the version of `selenium-webdriver` to
28
+ # see if it's a version we support.
29
+ #
30
+ # By default, we use Gem.loaded_specs to determine
31
+ # the version number. However, in some cases, such
32
+ # as when loading `selenium-webdriver` outside of
33
+ # Rubygems, we fall back to referencing
34
+ # Selenium::WebDriver::VERSION. Ideally we'd
35
+ # use the constant in all cases, but earlier versions
36
+ # of `selenium-webdriver` didn't provide the constant.
37
+ @selenium_webdriver_version =
38
+ if Gem.loaded_specs['selenium-webdriver']
39
+ Gem.loaded_specs['selenium-webdriver'].version
40
+ else
41
+ Gem::Version.new(Selenium::WebDriver::VERSION)
42
+ end
43
+
44
+ unless Gem::Requirement.new('>= 4.8').satisfied_by? @selenium_webdriver_version
45
+ warn "Warning: You're using an unsupported version of selenium-webdriver, please upgrade to 4.8+."
46
+ end
47
+
48
+ @selenium_webdriver_version
49
+ rescue LoadError => e
50
+ raise e unless e.message.include?('selenium-webdriver')
51
+
52
+ raise LoadError, "Capybara's selenium driver is unable to load `selenium-webdriver`, please install the gem and add `gem 'selenium-webdriver'` to your Gemfile if you are using bundler."
53
+ end
54
+
55
+ attr_reader :specializations
56
+
57
+ def register_specialization(browser_name, specialization)
58
+ @specializations ||= {}
59
+ @specializations[browser_name] = specialization
60
+ end
21
61
  end
22
62
 
23
63
  def browser
24
64
  unless @browser
25
- # if firefox?
26
- # options[:desired_capabilities] ||= {}
27
- # options[:desired_capabilities][:unexpectedAlertBehaviour] = "ignore"
28
- # end
29
-
30
- @processed_options = options.reject { |key, _val| SPECIAL_OPTIONS.include?(key) }
31
- @browser = Selenium::WebDriver.for(options[:browser], @processed_options)
32
-
33
- extend ChromeDriver if chrome?
34
- extend MarionetteDriver if marionette?
35
-
36
- main = Process.pid
37
- at_exit do
38
- # Store the exit status of the test run since it goes away after calling the at_exit proc...
39
- @exit_status = $ERROR_INFO.status if $ERROR_INFO.is_a?(SystemExit)
40
- quit if Process.pid == main
41
- exit @exit_status if @exit_status # Force exit with stored status
65
+ options[:http_client] ||= begin
66
+ require 'capybara/selenium/patches/persistent_client'
67
+ if options[:timeout]
68
+ ::Capybara::Selenium::PersistentClient.new(read_timeout: options[:timeout])
69
+ else
70
+ ::Capybara::Selenium::PersistentClient.new
71
+ end
42
72
  end
73
+ processed_options = options.except(*SPECIAL_OPTIONS)
74
+
75
+ @browser = Selenium::WebDriver.for(options[:browser], processed_options)
76
+
77
+ specialize_driver
78
+ setup_exit_handler
43
79
  end
44
80
  @browser
45
81
  end
46
82
 
47
83
  def initialize(app, **options)
84
+ super()
48
85
  self.class.load_selenium
49
- @session = nil
50
86
  @app = app
51
87
  @browser = nil
52
88
  @exit_status = nil
53
- @frame_handles = {}
89
+ @frame_handles = Hash.new { |hash, handle| hash[handle] = [] }
54
90
  @options = DEFAULT_OPTIONS.merge(options)
55
91
  @node_class = ::Capybara::Selenium::Node
56
92
  end
@@ -73,6 +109,8 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
73
109
 
74
110
  def html
75
111
  browser.page_source
112
+ rescue Selenium::WebDriver::Error::JavascriptError => e
113
+ raise unless e.message.include?('documentElement is null')
76
114
  end
77
115
 
78
116
  def title
@@ -83,14 +121,6 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
83
121
  browser.current_url
84
122
  end
85
123
 
86
- def find_xpath(selector)
87
- browser.find_elements(:xpath, selector).map(&method(:build_node))
88
- end
89
-
90
- def find_css(selector)
91
- browser.find_elements(:css, selector).map(&method(:build_node))
92
- end
93
-
94
124
  def wait?; true; end
95
125
  def needs_server?; true; end
96
126
 
@@ -109,8 +139,17 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
109
139
  unwrap_script_result(result)
110
140
  end
111
141
 
112
- def save_screenshot(path, **_options)
113
- browser.save_screenshot(path)
142
+ def active_element
143
+ build_node(native_active_element)
144
+ end
145
+
146
+ def send_keys(*args)
147
+ # Should this call the specialized nodes rather than native???
148
+ native_active_element.send_keys(*args)
149
+ end
150
+
151
+ def save_screenshot(path, **options)
152
+ browser.save_screenshot(path, **options)
114
153
  end
115
154
 
116
155
  def reset!
@@ -120,70 +159,45 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
120
159
  navigated = false
121
160
  timer = Capybara::Helpers.timer(expire_in: 10)
122
161
  begin
123
- unless navigated
124
- # Only trigger a navigation if we haven't done it already, otherwise it
125
- # can trigger an endless series of unload modals
126
- begin
127
- @browser.manage.delete_all_cookies
128
- clear_storage
129
- # rescue Selenium::WebDriver::Error::NoSuchAlertError
130
- # # Handle a bug in Firefox/Geckodriver where it thinks it needs an alert modal to exist
131
- # # for no good reason
132
- # retry
133
- rescue Selenium::WebDriver::Error::UnhandledError # rubocop:disable Lint/HandleExceptions
134
- # delete_all_cookies fails when we've previously gone
135
- # to about:blank, so we rescue this error and do nothing
136
- # instead.
137
- end
138
- @browser.navigate.to("about:blank")
139
- end
162
+ # Only trigger a navigation if we haven't done it already, otherwise it
163
+ # can trigger an endless series of unload modals
164
+ reset_browser_state unless navigated
140
165
  navigated = true
141
-
142
166
  # Ensure the page is empty and trigger an UnhandledAlertError for any modals that appear during unload
143
- until find_xpath("/html/body/*").empty?
144
- raise Capybara::ExpectationNotMet, 'Timed out waiting for Selenium session reset' if timer.expired?
145
- sleep 0.05
146
- end
147
- rescue Selenium::WebDriver::Error::UnhandledAlertError, Selenium::WebDriver::Error::UnexpectedAlertOpenError
167
+ wait_for_empty_page(timer)
168
+ rescue *unhandled_alert_errors
148
169
  # This error is thrown if an unhandled alert is on the page
149
170
  # Firefox appears to automatically dismiss this alert, chrome does not
150
171
  # We'll try to accept it
151
- begin
152
- @browser.switch_to.alert.accept
153
- sleep 0.25 # allow time for the modal to be handled
154
- rescue modal_error
155
- # The alert is now gone
156
- if current_url != "about:blank"
157
- begin
158
- # If navigation has not occurred attempt again and accept alert
159
- # since FF may have dismissed the alert at first attempt
160
- @browser.navigate.to("about:blank")
161
- sleep 0.1 # slight wait for alert
162
- @browser.switch_to.alert.accept
163
- rescue modal_error # rubocop:disable Metrics/BlockNesting, Lint/HandleExceptions
164
- # alert now gone, should mean navigation happened
165
- end
166
- end
167
- end
172
+ accept_unhandled_reset_alert
168
173
  # try cleaning up the browser again
169
174
  retry
170
175
  end
171
176
  end
172
177
 
178
+ def frame_obscured_at?(x:, y:)
179
+ frame = @frame_handles[current_window_handle].last
180
+ return false unless frame
181
+
182
+ switch_to_frame(:parent)
183
+ begin
184
+ frame.base.obscured?(x: x, y: y)
185
+ ensure
186
+ switch_to_frame(frame)
187
+ end
188
+ end
189
+
173
190
  def switch_to_frame(frame)
191
+ handles = @frame_handles[current_window_handle]
174
192
  case frame
175
193
  when :top
176
- @frame_handles[browser.window_handle] = []
194
+ handles.clear
177
195
  browser.switch_to.default_content
178
196
  when :parent
179
- # would love to use browser.switch_to.parent_frame here
180
- # but it has an issue if the current frame is removed from within it
181
- @frame_handles[browser.window_handle].pop
182
- browser.switch_to.default_content
183
- @frame_handles[browser.window_handle].each { |fh| browser.switch_to.frame(fh) }
197
+ handles.pop
198
+ browser.switch_to.parent_frame
184
199
  else
185
- @frame_handles[browser.window_handle] ||= []
186
- @frame_handles[browser.window_handle] << frame.native
200
+ handles << frame
187
201
  browser.switch_to.frame(frame.native)
188
202
  end
189
203
  end
@@ -219,7 +233,8 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
219
233
  end
220
234
 
221
235
  def close_window(handle)
222
- raise ArgumentError, "Not allowed to close the primary window" if handle == window_handles.first
236
+ raise ArgumentError, 'Not allowed to close the primary window' if handle == window_handles.first
237
+
223
238
  within_given_window(handle) do
224
239
  browser.close
225
240
  end
@@ -229,7 +244,16 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
229
244
  browser.window_handles
230
245
  end
231
246
 
232
- def open_new_window
247
+ def open_new_window(kind = :tab)
248
+ if browser.switch_to.respond_to?(:new_window)
249
+ handle = current_window_handle
250
+ browser.switch_to.new_window(kind)
251
+ switch_to_window(handle)
252
+ else
253
+ browser.manage.new_window(kind)
254
+ end
255
+ rescue NoMethodError, Selenium::WebDriver::Error::WebDriverError
256
+ # If not supported by the driver or browser default to using JS
233
257
  browser.execute_script('window.open();')
234
258
  end
235
259
 
@@ -239,7 +263,7 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
239
263
 
240
264
  def accept_modal(_type, **options)
241
265
  yield if block_given?
242
- modal = find_modal(options)
266
+ modal = find_modal(**options)
243
267
 
244
268
  modal.send_keys options[:with] if options[:with]
245
269
 
@@ -250,7 +274,7 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
250
274
 
251
275
  def dismiss_modal(_type, **options)
252
276
  yield if block_given?
253
- modal = find_modal(options)
277
+ modal = find_modal(**options)
254
278
  message = modal.text
255
279
  modal.dismiss
256
280
  message
@@ -258,7 +282,8 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
258
282
 
259
283
  def quit
260
284
  @browser&.quit
261
- rescue Selenium::WebDriver::Error::SessionNotCreatedError, Errno::ECONNREFUSED # rubocop:disable Lint/HandleExceptions
285
+ rescue Selenium::WebDriver::Error::SessionNotCreatedError, Errno::ECONNREFUSED,
286
+ Selenium::WebDriver::Error::InvalidSessionIdError
262
287
  # Browser must have already gone
263
288
  rescue Selenium::WebDriver::Error::UnknownError => e
264
289
  unless silenced_unknown_error_message?(e.message) # Most likely already gone
@@ -270,19 +295,19 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
270
295
  end
271
296
 
272
297
  def invalid_element_errors
273
- [
274
- ::Selenium::WebDriver::Error::StaleElementReferenceError,
275
- ::Selenium::WebDriver::Error::UnhandledError,
276
- ::Selenium::WebDriver::Error::ElementNotVisibleError,
277
- ::Selenium::WebDriver::Error::InvalidSelectorError, # Work around a race condition that can occur with chromedriver and #go_back/#go_forward
278
- ::Selenium::WebDriver::Error::ElementNotInteractableError,
279
- ::Selenium::WebDriver::Error::ElementClickInterceptedError,
280
- ::Selenium::WebDriver::Error::InvalidElementStateError,
281
- ::Selenium::WebDriver::Error::ElementNotSelectableError,
282
- ::Selenium::WebDriver::Error::ElementNotSelectableError,
283
- ::Selenium::WebDriver::Error::NoSuchElementError, # IE
284
- ::Selenium::WebDriver::Error::InvalidArgumentError # IE
285
- ]
298
+ @invalid_element_errors ||=
299
+ [
300
+ ::Selenium::WebDriver::Error::StaleElementReferenceError,
301
+ ::Selenium::WebDriver::Error::ElementNotInteractableError,
302
+ ::Selenium::WebDriver::Error::InvalidSelectorError, # Work around chromedriver go_back/go_forward race condition
303
+ ::Selenium::WebDriver::Error::ElementClickInterceptedError,
304
+ ::Selenium::WebDriver::Error::NoSuchElementError, # IE
305
+ ::Selenium::WebDriver::Error::InvalidArgumentError # IE
306
+ ].tap do |errors|
307
+ if defined?(::Selenium::WebDriver::Error::DetachedShadowRootError)
308
+ errors.push(::Selenium::WebDriver::Error::DetachedShadowRootError)
309
+ end
310
+ end
286
311
  end
287
312
 
288
313
  def no_such_window_error
@@ -291,56 +316,78 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
291
316
 
292
317
  private
293
318
 
294
- def w3c?
295
- browser && browser.capabilities.is_a?(Selenium::WebDriver::Remote::W3C::Capabilities)
296
- end
297
-
298
- def marionette?
299
- firefox? && w3c?
319
+ def native_args(args)
320
+ args.map { |arg| arg.is_a?(Capybara::Selenium::Node) ? arg.native : arg }
300
321
  end
301
322
 
302
- def firefox?
303
- browser_name == :firefox
323
+ def native_active_element
324
+ browser.switch_to.active_element
304
325
  end
305
326
 
306
- def chrome?
307
- browser_name == :chrome
327
+ def clear_browser_state
328
+ delete_all_cookies
329
+ clear_storage
330
+ rescue *clear_browser_state_errors
331
+ # delete_all_cookies fails when we've previously gone
332
+ # to about:blank, so we rescue this error and do nothing
333
+ # instead.
308
334
  end
309
335
 
310
- def edge?
311
- browser_name == :edge
336
+ def clear_browser_state_errors
337
+ @clear_browser_state_errors ||= [Selenium::WebDriver::Error::UnknownError]
312
338
  end
313
339
 
314
- def ie?
315
- browser_name == :ie
340
+ def unhandled_alert_errors
341
+ @unhandled_alert_errors ||= [Selenium::WebDriver::Error::UnexpectedAlertOpenError]
316
342
  end
317
343
 
318
- def browser_name
319
- browser.browser
344
+ def delete_all_cookies
345
+ @browser.manage.delete_all_cookies
320
346
  end
321
347
 
322
- def native_args(args)
323
- args.map { |arg| arg.is_a?(Capybara::Selenium::Node) ? arg.native : arg }
348
+ def clear_storage
349
+ clear_session_storage unless options[:clear_session_storage] == false
350
+ clear_local_storage unless options[:clear_local_storage] == false
351
+ rescue Selenium::WebDriver::Error::JavascriptError
352
+ # session/local storage may not be available if on non-http pages (e.g. about:blank)
324
353
  end
325
354
 
326
- def clear_storage
327
- if options[:clear_session_storage]
328
- if @browser.respond_to? :session_storage
329
- @browser.session_storage.clear
330
- else
331
- warn "sessionStorage clear requested but is not available for this driver"
355
+ def clear_session_storage
356
+ if @browser.respond_to? :session_storage
357
+ @browser.session_storage.clear
358
+ else
359
+ begin
360
+ @browser&.execute_script('window.sessionStorage.clear()')
361
+ rescue # rubocop:disable Style/RescueStandardError
362
+ unless options[:clear_session_storage].nil?
363
+ warn 'sessionStorage clear requested but is not supported by this driver'
364
+ end
332
365
  end
333
366
  end
367
+ end
334
368
 
335
- if options[:clear_local_storage]
336
- if @browser.respond_to? :local_storage
337
- @browser.local_storage.clear
338
- else
339
- warn "localStorage clear requested but is not available for this driver"
369
+ def clear_local_storage
370
+ if @browser.respond_to? :local_storage
371
+ @browser.local_storage.clear
372
+ else
373
+ begin
374
+ @browser&.execute_script('window.localStorage.clear()')
375
+ rescue # rubocop:disable Style/RescueStandardError
376
+ unless options[:clear_local_storage].nil?
377
+ warn 'localStorage clear requested but is not supported by this driver'
378
+ end
340
379
  end
341
380
  end
342
381
  end
343
382
 
383
+ def navigate_with_accept(url)
384
+ @browser.navigate.to(url)
385
+ sleep 0.1 # slight wait for alert
386
+ @browser.switch_to.alert.accept
387
+ rescue modal_error
388
+ # alert now gone, should mean navigation happened
389
+ end
390
+
344
391
  def modal_error
345
392
  Selenium::WebDriver::Error::NoSuchAlertError
346
393
  end
@@ -367,16 +414,25 @@ private
367
414
  begin
368
415
  wait.until do
369
416
  alert = @browser.switch_to.alert
370
- regexp = text.is_a?(Regexp) ? text : Regexp.escape(text.to_s)
371
- alert.text.match(regexp) ? alert : nil
417
+ regexp = text.is_a?(Regexp) ? text : Regexp.new(Regexp.escape(text.to_s))
418
+ matched = alert.text.match?(regexp)
419
+ unless matched
420
+ raise Capybara::ModalNotFound, "Unable to find modal dialog with #{text} - found '#{alert.text}' instead."
421
+ end
422
+
423
+ alert
372
424
  end
373
- rescue Selenium::WebDriver::Error::TimeOutError
425
+ rescue *find_modal_errors
374
426
  raise Capybara::ModalNotFound, "Unable to find modal dialog#{" with #{text}" if text}"
375
427
  end
376
428
  end
377
429
 
430
+ def find_modal_errors
431
+ @find_modal_errors ||= [Selenium::WebDriver::Error::TimeoutError]
432
+ end
433
+
378
434
  def silenced_unknown_error_message?(msg)
379
- silenced_unknown_error_messages.any? { |r| msg =~ r }
435
+ silenced_unknown_error_messages.any? { |regex| msg.match? regex }
380
436
  end
381
437
 
382
438
  def silenced_unknown_error_messages
@@ -386,20 +442,76 @@ private
386
442
  def unwrap_script_result(arg)
387
443
  case arg
388
444
  when Array
389
- arg.map { |e| unwrap_script_result(e) }
445
+ arg.map { |arr| unwrap_script_result(arr) }
390
446
  when Hash
391
- arg.each { |k, v| arg[k] = unwrap_script_result(v) }
392
- when Selenium::WebDriver::Element
447
+ arg.transform_values! { |value| unwrap_script_result(value) }
448
+ when Selenium::WebDriver::Element, Selenium::WebDriver::ShadowRoot
393
449
  build_node(arg)
394
450
  else
395
451
  arg
396
452
  end
397
453
  end
398
454
 
399
- def build_node(native_node)
400
- ::Capybara::Selenium::Node.new(self, native_node)
455
+ def find_context
456
+ browser
457
+ end
458
+
459
+ def build_node(native_node, initial_cache = {})
460
+ ::Capybara::Selenium::Node.new(self, native_node, initial_cache)
461
+ end
462
+
463
+ def bridge
464
+ browser.send(:bridge)
465
+ end
466
+
467
+ def specialize_driver
468
+ browser_type = browser.browser
469
+ Capybara::Selenium::Driver.specializations.select { |k, _v| k === browser_type }.each_value do |specialization| # rubocop:disable Style/CaseEquality
470
+ extend specialization
471
+ end
472
+ end
473
+
474
+ def setup_exit_handler
475
+ main = Process.pid
476
+ at_exit do
477
+ # Store the exit status of the test run since it goes away after calling the at_exit proc...
478
+ @exit_status = $ERROR_INFO.status if $ERROR_INFO.is_a?(SystemExit)
479
+ quit if Process.pid == main
480
+ exit @exit_status if @exit_status # Force exit with stored status
481
+ end
482
+ end
483
+
484
+ def reset_browser_state
485
+ clear_browser_state
486
+ @browser.navigate.to('about:blank')
487
+ end
488
+
489
+ def wait_for_empty_page(timer)
490
+ until find_xpath('/html/body/*').empty?
491
+ raise Capybara::ExpectationNotMet, 'Timed out waiting for Selenium session reset' if timer.expired?
492
+
493
+ sleep 0.01
494
+
495
+ # It has been observed that it is possible that asynchronous JS code in
496
+ # the application under test can navigate the browser away from about:blank
497
+ # if the timing is just right. Ensure we are still at about:blank...
498
+ @browser.navigate.to('about:blank') unless current_url == 'about:blank'
499
+ end
500
+ end
501
+
502
+ def accept_unhandled_reset_alert
503
+ @browser.switch_to.alert.accept
504
+ sleep 0.25 # allow time for the modal to be handled
505
+ rescue modal_error
506
+ # The alert is now gone.
507
+ # If navigation has not occurred attempt again and accept alert
508
+ # since FF may have dismissed the alert at first attempt.
509
+ navigate_with_accept('about:blank') if current_url != 'about:blank'
401
510
  end
402
511
  end
403
512
 
404
513
  require 'capybara/selenium/driver_specializations/chrome_driver'
405
- require 'capybara/selenium/driver_specializations/marionette_driver'
514
+ require 'capybara/selenium/driver_specializations/firefox_driver'
515
+ require 'capybara/selenium/driver_specializations/internet_explorer_driver'
516
+ require 'capybara/selenium/driver_specializations/safari_driver'
517
+ require 'capybara/selenium/driver_specializations/edge_driver'
@@ -1,26 +1,35 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'capybara/selenium/nodes/chrome_node'
4
+ require 'capybara/selenium/patches/logs'
5
+
3
6
  module Capybara::Selenium::Driver::ChromeDriver
7
+ def self.extended(base)
8
+ bridge = base.send(:bridge)
9
+ bridge.extend Capybara::Selenium::ChromeLogs unless bridge.respond_to?(:log)
10
+ bridge.extend Capybara::Selenium::IsDisplayed unless bridge.send(:commands, :is_element_displayed)
11
+ base.options[:native_displayed] = false if base.options[:native_displayed].nil?
12
+ end
13
+
4
14
  def fullscreen_window(handle)
5
15
  within_given_window(handle) do
6
- begin
7
- super
8
- rescue NoMethodError => e
9
- raise unless e.message =~ /full_screen_window/
10
- bridge = browser.send(:bridge)
11
- result = bridge.http.call(:post, "session/#{bridge.session_id}/window/fullscreen", {})
12
- result['value']
13
- end
16
+ super
17
+ rescue NoMethodError => e
18
+ raise unless e.message.include?('full_screen_window')
19
+
20
+ result = bridge.http.call(:post, "session/#{bridge.session_id}/window/fullscreen", {})
21
+ result['value']
14
22
  end
15
23
  end
16
24
 
17
25
  def resize_window_to(handle, width, height)
18
26
  super
19
27
  rescue Selenium::WebDriver::Error::UnknownError => e
20
- raise unless e.message =~ /failed to change window state/
28
+ raise unless e.message.include?('failed to change window state')
29
+
21
30
  # Chromedriver doesn't wait long enough for state to change when coming out of fullscreen
22
31
  # and raises unnecessary error. Wait a bit and try again.
23
- sleep 0.5
32
+ sleep 0.25
24
33
  super
25
34
  end
26
35
 
@@ -29,7 +38,80 @@ module Capybara::Selenium::Driver::ChromeDriver
29
38
  return unless @browser
30
39
 
31
40
  switch_to_window(window_handles.first)
32
- window_handles.slice(1..-1).each { |win| close_window(win) }
41
+ window_handles.slice(1..).each { |win| close_window(win) }
42
+ return super if chromedriver_version < 73
43
+
44
+ timer = Capybara::Helpers.timer(expire_in: 10)
45
+ begin
46
+ clear_storage unless uniform_storage_clear?
47
+ @browser.navigate.to('about:blank')
48
+ wait_for_empty_page(timer)
49
+ rescue *unhandled_alert_errors
50
+ accept_unhandled_reset_alert
51
+ retry
52
+ end
53
+
54
+ execute_cdp('Storage.clearDataForOrigin', origin: '*', storageTypes: storage_types_to_clear)
55
+ end
56
+
57
+ private
58
+
59
+ def storage_types_to_clear
60
+ types = ['cookies']
61
+ types << 'local_storage' if clear_all_storage?
62
+ types.join(',')
63
+ end
64
+
65
+ def clear_all_storage?
66
+ storage_clears.none? false
67
+ end
68
+
69
+ def uniform_storage_clear?
70
+ storage_clears.uniq { |s| s == false }.length <= 1
71
+ end
72
+
73
+ def storage_clears
74
+ options.values_at(:clear_session_storage, :clear_local_storage)
75
+ end
76
+
77
+ def clear_storage
78
+ # Chrome errors if attempt to clear storage on about:blank
79
+ # In W3C mode it crashes chromedriver
80
+ url = current_url
81
+ super unless url.nil? || url.start_with?('about:')
82
+ end
83
+
84
+ def delete_all_cookies
85
+ execute_cdp('Network.clearBrowserCookies')
86
+ rescue *cdp_unsupported_errors
87
+ # If the CDP clear isn't supported do original limited clear
33
88
  super
34
89
  end
90
+
91
+ def cdp_unsupported_errors
92
+ @cdp_unsupported_errors ||= [Selenium::WebDriver::Error::WebDriverError]
93
+ end
94
+
95
+ def execute_cdp(cmd, params = {})
96
+ if browser.respond_to? :execute_cdp
97
+ browser.execute_cdp(cmd, **params)
98
+ else
99
+ args = { cmd: cmd, params: params }
100
+ result = bridge.http.call(:post, "session/#{bridge.session_id}/goog/cdp/execute", args)
101
+ result['value']
102
+ end
103
+ end
104
+
105
+ def build_node(native_node, initial_cache = {})
106
+ ::Capybara::Selenium::ChromeNode.new(self, native_node, initial_cache)
107
+ end
108
+
109
+ def chromedriver_version
110
+ @chromedriver_version ||= begin
111
+ caps = browser.capabilities
112
+ caps['chrome']&.fetch('chromedriverVersion', nil).to_f
113
+ end
114
+ end
35
115
  end
116
+
117
+ Capybara::Selenium::Driver.register_specialization :chrome, Capybara::Selenium::Driver::ChromeDriver