capybara 3.3.0 → 3.40.0

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 (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