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