capybara 3.32.2

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 (313) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +1 -0
  3. data/History.md +1813 -0
  4. data/License.txt +22 -0
  5. data/README.md +1099 -0
  6. data/lib/capybara.rb +511 -0
  7. data/lib/capybara/config.rb +94 -0
  8. data/lib/capybara/cucumber.rb +27 -0
  9. data/lib/capybara/driver/base.rb +170 -0
  10. data/lib/capybara/driver/node.rb +139 -0
  11. data/lib/capybara/dsl.rb +65 -0
  12. data/lib/capybara/helpers.rb +108 -0
  13. data/lib/capybara/minitest.rb +386 -0
  14. data/lib/capybara/minitest/spec.rb +264 -0
  15. data/lib/capybara/node/actions.rb +420 -0
  16. data/lib/capybara/node/base.rb +143 -0
  17. data/lib/capybara/node/document.rb +48 -0
  18. data/lib/capybara/node/document_matchers.rb +67 -0
  19. data/lib/capybara/node/element.rb +606 -0
  20. data/lib/capybara/node/finders.rb +325 -0
  21. data/lib/capybara/node/matchers.rb +883 -0
  22. data/lib/capybara/node/simple.rb +208 -0
  23. data/lib/capybara/queries/ancestor_query.rb +27 -0
  24. data/lib/capybara/queries/base_query.rb +106 -0
  25. data/lib/capybara/queries/current_path_query.rb +51 -0
  26. data/lib/capybara/queries/match_query.rb +26 -0
  27. data/lib/capybara/queries/selector_query.rb +710 -0
  28. data/lib/capybara/queries/sibling_query.rb +26 -0
  29. data/lib/capybara/queries/style_query.rb +45 -0
  30. data/lib/capybara/queries/text_query.rb +110 -0
  31. data/lib/capybara/queries/title_query.rb +39 -0
  32. data/lib/capybara/rack_test/browser.rb +140 -0
  33. data/lib/capybara/rack_test/css_handlers.rb +13 -0
  34. data/lib/capybara/rack_test/driver.rb +109 -0
  35. data/lib/capybara/rack_test/errors.rb +6 -0
  36. data/lib/capybara/rack_test/form.rb +127 -0
  37. data/lib/capybara/rack_test/node.rb +325 -0
  38. data/lib/capybara/rails.rb +16 -0
  39. data/lib/capybara/registrations/drivers.rb +36 -0
  40. data/lib/capybara/registrations/patches/puma_ssl.rb +27 -0
  41. data/lib/capybara/registrations/servers.rb +44 -0
  42. data/lib/capybara/result.rb +190 -0
  43. data/lib/capybara/rspec.rb +29 -0
  44. data/lib/capybara/rspec/features.rb +23 -0
  45. data/lib/capybara/rspec/matcher_proxies.rb +82 -0
  46. data/lib/capybara/rspec/matchers.rb +201 -0
  47. data/lib/capybara/rspec/matchers/base.rb +111 -0
  48. data/lib/capybara/rspec/matchers/become_closed.rb +33 -0
  49. data/lib/capybara/rspec/matchers/compound.rb +88 -0
  50. data/lib/capybara/rspec/matchers/count_sugar.rb +37 -0
  51. data/lib/capybara/rspec/matchers/have_ancestor.rb +28 -0
  52. data/lib/capybara/rspec/matchers/have_current_path.rb +29 -0
  53. data/lib/capybara/rspec/matchers/have_selector.rb +77 -0
  54. data/lib/capybara/rspec/matchers/have_sibling.rb +27 -0
  55. data/lib/capybara/rspec/matchers/have_text.rb +33 -0
  56. data/lib/capybara/rspec/matchers/have_title.rb +29 -0
  57. data/lib/capybara/rspec/matchers/match_selector.rb +27 -0
  58. data/lib/capybara/rspec/matchers/match_style.rb +38 -0
  59. data/lib/capybara/rspec/matchers/spatial_sugar.rb +39 -0
  60. data/lib/capybara/selector.rb +233 -0
  61. data/lib/capybara/selector/builders/css_builder.rb +84 -0
  62. data/lib/capybara/selector/builders/xpath_builder.rb +69 -0
  63. data/lib/capybara/selector/css.rb +102 -0
  64. data/lib/capybara/selector/definition.rb +276 -0
  65. data/lib/capybara/selector/definition/button.rb +51 -0
  66. data/lib/capybara/selector/definition/checkbox.rb +26 -0
  67. data/lib/capybara/selector/definition/css.rb +10 -0
  68. data/lib/capybara/selector/definition/datalist_input.rb +35 -0
  69. data/lib/capybara/selector/definition/datalist_option.rb +25 -0
  70. data/lib/capybara/selector/definition/element.rb +27 -0
  71. data/lib/capybara/selector/definition/field.rb +40 -0
  72. data/lib/capybara/selector/definition/fieldset.rb +14 -0
  73. data/lib/capybara/selector/definition/file_field.rb +13 -0
  74. data/lib/capybara/selector/definition/fillable_field.rb +33 -0
  75. data/lib/capybara/selector/definition/frame.rb +17 -0
  76. data/lib/capybara/selector/definition/id.rb +6 -0
  77. data/lib/capybara/selector/definition/label.rb +62 -0
  78. data/lib/capybara/selector/definition/link.rb +46 -0
  79. data/lib/capybara/selector/definition/link_or_button.rb +16 -0
  80. data/lib/capybara/selector/definition/option.rb +27 -0
  81. data/lib/capybara/selector/definition/radio_button.rb +27 -0
  82. data/lib/capybara/selector/definition/select.rb +81 -0
  83. data/lib/capybara/selector/definition/table.rb +109 -0
  84. data/lib/capybara/selector/definition/table_row.rb +21 -0
  85. data/lib/capybara/selector/definition/xpath.rb +5 -0
  86. data/lib/capybara/selector/filter.rb +5 -0
  87. data/lib/capybara/selector/filter_set.rb +124 -0
  88. data/lib/capybara/selector/filters/base.rb +77 -0
  89. data/lib/capybara/selector/filters/expression_filter.rb +22 -0
  90. data/lib/capybara/selector/filters/locator_filter.rb +29 -0
  91. data/lib/capybara/selector/filters/node_filter.rb +31 -0
  92. data/lib/capybara/selector/regexp_disassembler.rb +214 -0
  93. data/lib/capybara/selector/selector.rb +147 -0
  94. data/lib/capybara/selector/xpath_extensions.rb +17 -0
  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 +496 -0
  100. data/lib/capybara/selenium/driver_specializations/chrome_driver.rb +119 -0
  101. data/lib/capybara/selenium/driver_specializations/edge_driver.rb +126 -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 +78 -0
  110. data/lib/capybara/selenium/logger_suppressor.rb +34 -0
  111. data/lib/capybara/selenium/node.rb +610 -0
  112. data/lib/capybara/selenium/nodes/chrome_node.rb +119 -0
  113. data/lib/capybara/selenium/nodes/edge_node.rb +104 -0
  114. data/lib/capybara/selenium/nodes/firefox_node.rb +131 -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 +47 -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.rb +126 -0
  124. data/lib/capybara/server/animation_disabler.rb +58 -0
  125. data/lib/capybara/server/checker.rb +44 -0
  126. data/lib/capybara/server/middleware.rb +69 -0
  127. data/lib/capybara/session.rb +942 -0
  128. data/lib/capybara/session/config.rb +124 -0
  129. data/lib/capybara/session/matchers.rb +87 -0
  130. data/lib/capybara/spec/fixtures/another_test_file.txt +1 -0
  131. data/lib/capybara/spec/fixtures/capybara.jpg +3 -0
  132. data/lib/capybara/spec/fixtures/no_extension +1 -0
  133. data/lib/capybara/spec/fixtures/test_file.txt +1 -0
  134. data/lib/capybara/spec/public/jquery-ui.js +13 -0
  135. data/lib/capybara/spec/public/jquery.js +5 -0
  136. data/lib/capybara/spec/public/offset.js +6 -0
  137. data/lib/capybara/spec/public/test.js +268 -0
  138. data/lib/capybara/spec/session/accept_alert_spec.rb +81 -0
  139. data/lib/capybara/spec/session/accept_confirm_spec.rb +32 -0
  140. data/lib/capybara/spec/session/accept_prompt_spec.rb +78 -0
  141. data/lib/capybara/spec/session/all_spec.rb +278 -0
  142. data/lib/capybara/spec/session/ancestor_spec.rb +88 -0
  143. data/lib/capybara/spec/session/assert_all_of_selectors_spec.rb +140 -0
  144. data/lib/capybara/spec/session/assert_current_path_spec.rb +75 -0
  145. data/lib/capybara/spec/session/assert_selector_spec.rb +143 -0
  146. data/lib/capybara/spec/session/assert_style_spec.rb +26 -0
  147. data/lib/capybara/spec/session/assert_text_spec.rb +258 -0
  148. data/lib/capybara/spec/session/assert_title_spec.rb +93 -0
  149. data/lib/capybara/spec/session/attach_file_spec.rb +216 -0
  150. data/lib/capybara/spec/session/body_spec.rb +23 -0
  151. data/lib/capybara/spec/session/check_spec.rb +235 -0
  152. data/lib/capybara/spec/session/choose_spec.rb +121 -0
  153. data/lib/capybara/spec/session/click_button_spec.rb +506 -0
  154. data/lib/capybara/spec/session/click_link_or_button_spec.rb +129 -0
  155. data/lib/capybara/spec/session/click_link_spec.rb +229 -0
  156. data/lib/capybara/spec/session/current_scope_spec.rb +31 -0
  157. data/lib/capybara/spec/session/current_url_spec.rb +115 -0
  158. data/lib/capybara/spec/session/dismiss_confirm_spec.rb +36 -0
  159. data/lib/capybara/spec/session/dismiss_prompt_spec.rb +21 -0
  160. data/lib/capybara/spec/session/element/assert_match_selector_spec.rb +38 -0
  161. data/lib/capybara/spec/session/element/match_css_spec.rb +31 -0
  162. data/lib/capybara/spec/session/element/match_xpath_spec.rb +25 -0
  163. data/lib/capybara/spec/session/element/matches_selector_spec.rb +120 -0
  164. data/lib/capybara/spec/session/evaluate_async_script_spec.rb +23 -0
  165. data/lib/capybara/spec/session/evaluate_script_spec.rb +49 -0
  166. data/lib/capybara/spec/session/execute_script_spec.rb +28 -0
  167. data/lib/capybara/spec/session/fill_in_spec.rb +286 -0
  168. data/lib/capybara/spec/session/find_button_spec.rb +74 -0
  169. data/lib/capybara/spec/session/find_by_id_spec.rb +33 -0
  170. data/lib/capybara/spec/session/find_field_spec.rb +113 -0
  171. data/lib/capybara/spec/session/find_link_spec.rb +70 -0
  172. data/lib/capybara/spec/session/find_spec.rb +531 -0
  173. data/lib/capybara/spec/session/first_spec.rb +156 -0
  174. data/lib/capybara/spec/session/frame/frame_title_spec.rb +23 -0
  175. data/lib/capybara/spec/session/frame/frame_url_spec.rb +23 -0
  176. data/lib/capybara/spec/session/frame/switch_to_frame_spec.rb +116 -0
  177. data/lib/capybara/spec/session/frame/within_frame_spec.rb +112 -0
  178. data/lib/capybara/spec/session/go_back_spec.rb +12 -0
  179. data/lib/capybara/spec/session/go_forward_spec.rb +14 -0
  180. data/lib/capybara/spec/session/has_all_selectors_spec.rb +69 -0
  181. data/lib/capybara/spec/session/has_ancestor_spec.rb +46 -0
  182. data/lib/capybara/spec/session/has_any_selectors_spec.rb +25 -0
  183. data/lib/capybara/spec/session/has_button_spec.rb +69 -0
  184. data/lib/capybara/spec/session/has_css_spec.rb +374 -0
  185. data/lib/capybara/spec/session/has_current_path_spec.rb +138 -0
  186. data/lib/capybara/spec/session/has_field_spec.rb +349 -0
  187. data/lib/capybara/spec/session/has_link_spec.rb +39 -0
  188. data/lib/capybara/spec/session/has_none_selectors_spec.rb +78 -0
  189. data/lib/capybara/spec/session/has_select_spec.rb +310 -0
  190. data/lib/capybara/spec/session/has_selector_spec.rb +202 -0
  191. data/lib/capybara/spec/session/has_sibling_spec.rb +50 -0
  192. data/lib/capybara/spec/session/has_table_spec.rb +198 -0
  193. data/lib/capybara/spec/session/has_text_spec.rb +394 -0
  194. data/lib/capybara/spec/session/has_title_spec.rb +71 -0
  195. data/lib/capybara/spec/session/has_xpath_spec.rb +149 -0
  196. data/lib/capybara/spec/session/headers_spec.rb +8 -0
  197. data/lib/capybara/spec/session/html_spec.rb +47 -0
  198. data/lib/capybara/spec/session/matches_style_spec.rb +35 -0
  199. data/lib/capybara/spec/session/node_spec.rb +1292 -0
  200. data/lib/capybara/spec/session/node_wrapper_spec.rb +39 -0
  201. data/lib/capybara/spec/session/refresh_spec.rb +33 -0
  202. data/lib/capybara/spec/session/reset_session_spec.rb +148 -0
  203. data/lib/capybara/spec/session/response_code_spec.rb +8 -0
  204. data/lib/capybara/spec/session/save_and_open_page_spec.rb +21 -0
  205. data/lib/capybara/spec/session/save_and_open_screenshot_spec.rb +43 -0
  206. data/lib/capybara/spec/session/save_page_spec.rb +110 -0
  207. data/lib/capybara/spec/session/save_screenshot_spec.rb +55 -0
  208. data/lib/capybara/spec/session/screenshot_spec.rb +18 -0
  209. data/lib/capybara/spec/session/scroll_spec.rb +117 -0
  210. data/lib/capybara/spec/session/select_spec.rb +229 -0
  211. data/lib/capybara/spec/session/selectors_spec.rb +98 -0
  212. data/lib/capybara/spec/session/sibling_spec.rb +52 -0
  213. data/lib/capybara/spec/session/source_spec.rb +0 -0
  214. data/lib/capybara/spec/session/text_spec.rb +74 -0
  215. data/lib/capybara/spec/session/title_spec.rb +29 -0
  216. data/lib/capybara/spec/session/uncheck_spec.rb +100 -0
  217. data/lib/capybara/spec/session/unselect_spec.rb +116 -0
  218. data/lib/capybara/spec/session/visit_spec.rb +204 -0
  219. data/lib/capybara/spec/session/window/become_closed_spec.rb +89 -0
  220. data/lib/capybara/spec/session/window/current_window_spec.rb +28 -0
  221. data/lib/capybara/spec/session/window/open_new_window_spec.rb +31 -0
  222. data/lib/capybara/spec/session/window/switch_to_window_spec.rb +132 -0
  223. data/lib/capybara/spec/session/window/window_opened_by_spec.rb +99 -0
  224. data/lib/capybara/spec/session/window/window_spec.rb +203 -0
  225. data/lib/capybara/spec/session/window/windows_spec.rb +34 -0
  226. data/lib/capybara/spec/session/window/within_window_spec.rb +157 -0
  227. data/lib/capybara/spec/session/within_spec.rb +199 -0
  228. data/lib/capybara/spec/spec_helper.rb +134 -0
  229. data/lib/capybara/spec/test_app.rb +226 -0
  230. data/lib/capybara/spec/views/animated.erb +49 -0
  231. data/lib/capybara/spec/views/buttons.erb +5 -0
  232. data/lib/capybara/spec/views/fieldsets.erb +30 -0
  233. data/lib/capybara/spec/views/form.erb +685 -0
  234. data/lib/capybara/spec/views/frame_child.erb +18 -0
  235. data/lib/capybara/spec/views/frame_one.erb +10 -0
  236. data/lib/capybara/spec/views/frame_parent.erb +9 -0
  237. data/lib/capybara/spec/views/frame_two.erb +9 -0
  238. data/lib/capybara/spec/views/header_links.erb +8 -0
  239. data/lib/capybara/spec/views/host_links.erb +13 -0
  240. data/lib/capybara/spec/views/initial_alert.erb +10 -0
  241. data/lib/capybara/spec/views/obscured.erb +47 -0
  242. data/lib/capybara/spec/views/offset.erb +32 -0
  243. data/lib/capybara/spec/views/path.erb +13 -0
  244. data/lib/capybara/spec/views/popup_one.erb +9 -0
  245. data/lib/capybara/spec/views/popup_two.erb +9 -0
  246. data/lib/capybara/spec/views/postback.erb +14 -0
  247. data/lib/capybara/spec/views/react.erb +45 -0
  248. data/lib/capybara/spec/views/scroll.erb +20 -0
  249. data/lib/capybara/spec/views/spatial.erb +31 -0
  250. data/lib/capybara/spec/views/tables.erb +130 -0
  251. data/lib/capybara/spec/views/with_animation.erb +74 -0
  252. data/lib/capybara/spec/views/with_base_tag.erb +11 -0
  253. data/lib/capybara/spec/views/with_count.erb +8 -0
  254. data/lib/capybara/spec/views/with_dragula.erb +22 -0
  255. data/lib/capybara/spec/views/with_fixed_header_footer.erb +17 -0
  256. data/lib/capybara/spec/views/with_hover.erb +24 -0
  257. data/lib/capybara/spec/views/with_hover1.erb +10 -0
  258. data/lib/capybara/spec/views/with_html.erb +208 -0
  259. data/lib/capybara/spec/views/with_html5_svg.erb +20 -0
  260. data/lib/capybara/spec/views/with_html_entities.erb +2 -0
  261. data/lib/capybara/spec/views/with_js.erb +160 -0
  262. data/lib/capybara/spec/views/with_jstree.erb +26 -0
  263. data/lib/capybara/spec/views/with_namespace.erb +20 -0
  264. data/lib/capybara/spec/views/with_scope.erb +42 -0
  265. data/lib/capybara/spec/views/with_scope_other.erb +6 -0
  266. data/lib/capybara/spec/views/with_simple_html.erb +2 -0
  267. data/lib/capybara/spec/views/with_slow_unload.erb +17 -0
  268. data/lib/capybara/spec/views/with_sortable_js.erb +21 -0
  269. data/lib/capybara/spec/views/with_title.erb +5 -0
  270. data/lib/capybara/spec/views/with_unload_alert.erb +14 -0
  271. data/lib/capybara/spec/views/with_windows.erb +54 -0
  272. data/lib/capybara/spec/views/within_frames.erb +15 -0
  273. data/lib/capybara/version.rb +5 -0
  274. data/lib/capybara/window.rb +146 -0
  275. data/spec/basic_node_spec.rb +154 -0
  276. data/spec/capybara_spec.rb +112 -0
  277. data/spec/css_builder_spec.rb +101 -0
  278. data/spec/css_splitter_spec.rb +38 -0
  279. data/spec/dsl_spec.rb +276 -0
  280. data/spec/filter_set_spec.rb +46 -0
  281. data/spec/fixtures/capybara.csv +1 -0
  282. data/spec/fixtures/certificate.pem +25 -0
  283. data/spec/fixtures/key.pem +27 -0
  284. data/spec/fixtures/selenium_driver_rspec_failure.rb +13 -0
  285. data/spec/fixtures/selenium_driver_rspec_success.rb +13 -0
  286. data/spec/minitest_spec.rb +163 -0
  287. data/spec/minitest_spec_spec.rb +162 -0
  288. data/spec/per_session_config_spec.rb +68 -0
  289. data/spec/rack_test_spec.rb +268 -0
  290. data/spec/regexp_dissassembler_spec.rb +250 -0
  291. data/spec/result_spec.rb +196 -0
  292. data/spec/rspec/features_spec.rb +99 -0
  293. data/spec/rspec/scenarios_spec.rb +19 -0
  294. data/spec/rspec/shared_spec_matchers.rb +947 -0
  295. data/spec/rspec/views_spec.rb +14 -0
  296. data/spec/rspec_matchers_spec.rb +62 -0
  297. data/spec/rspec_spec.rb +145 -0
  298. data/spec/sauce_spec_chrome.rb +43 -0
  299. data/spec/selector_spec.rb +513 -0
  300. data/spec/selenium_spec_chrome.rb +188 -0
  301. data/spec/selenium_spec_chrome_remote.rb +96 -0
  302. data/spec/selenium_spec_edge.rb +47 -0
  303. data/spec/selenium_spec_firefox.rb +208 -0
  304. data/spec/selenium_spec_firefox_remote.rb +80 -0
  305. data/spec/selenium_spec_ie.rb +150 -0
  306. data/spec/selenium_spec_safari.rb +148 -0
  307. data/spec/server_spec.rb +292 -0
  308. data/spec/session_spec.rb +91 -0
  309. data/spec/shared_selenium_node.rb +83 -0
  310. data/spec/shared_selenium_session.rb +476 -0
  311. data/spec/spec_helper.rb +100 -0
  312. data/spec/xpath_builder_spec.rb +93 -0
  313. metadata +753 -0
@@ -0,0 +1,119 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'capybara/selenium/nodes/chrome_node'
4
+ require 'capybara/selenium/patches/logs'
5
+
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.commands(:is_element_displayed)
11
+ base.options[:native_displayed] = false if base.options[:native_displayed].nil?
12
+ end
13
+
14
+ def fullscreen_window(handle)
15
+ within_given_window(handle) do
16
+ begin
17
+ super
18
+ rescue NoMethodError => e
19
+ raise unless e.message.match?(/full_screen_window/)
20
+
21
+ result = bridge.http.call(:post, "session/#{bridge.session_id}/window/fullscreen", {})
22
+ result['value']
23
+ end
24
+ end
25
+ end
26
+
27
+ def resize_window_to(handle, width, height)
28
+ super
29
+ rescue Selenium::WebDriver::Error::UnknownError => e
30
+ raise unless e.message.match?(/failed to change window state/)
31
+
32
+ # Chromedriver doesn't wait long enough for state to change when coming out of fullscreen
33
+ # and raises unnecessary error. Wait a bit and try again.
34
+ sleep 0.25
35
+ super
36
+ end
37
+
38
+ def reset!
39
+ # Use instance variable directly so we avoid starting the browser just to reset the session
40
+ return unless @browser
41
+
42
+ switch_to_window(window_handles.first)
43
+ window_handles.slice(1..-1).each { |win| close_window(win) }
44
+ return super if chromedriver_version < 73
45
+
46
+ timer = Capybara::Helpers.timer(expire_in: 10)
47
+ begin
48
+ clear_storage unless uniform_storage_clear?
49
+ @browser.navigate.to('about:blank')
50
+ wait_for_empty_page(timer)
51
+ rescue *unhandled_alert_errors
52
+ accept_unhandled_reset_alert
53
+ retry
54
+ end
55
+
56
+ execute_cdp('Storage.clearDataForOrigin', origin: '*', storageTypes: storage_types_to_clear)
57
+ end
58
+
59
+ private
60
+
61
+ def storage_types_to_clear
62
+ types = ['cookies']
63
+ types << 'local_storage' if clear_all_storage?
64
+ types.join(',')
65
+ end
66
+
67
+ def clear_all_storage?
68
+ storage_clears.none? { |s| s == false }
69
+ end
70
+
71
+ def uniform_storage_clear?
72
+ storage_clears.uniq { |s| s == false }.length <= 1
73
+ end
74
+
75
+ def storage_clears
76
+ options.values_at(:clear_session_storage, :clear_local_storage)
77
+ end
78
+
79
+ def clear_storage
80
+ # Chrome errors if attempt to clear storage on about:blank
81
+ # In W3C mode it crashes chromedriver
82
+ url = current_url
83
+ super unless url.nil? || url.start_with?('about:')
84
+ end
85
+
86
+ def delete_all_cookies
87
+ execute_cdp('Network.clearBrowserCookies')
88
+ rescue *cdp_unsupported_errors
89
+ # If the CDP clear isn't supported do original limited clear
90
+ super
91
+ end
92
+
93
+ def cdp_unsupported_errors
94
+ @cdp_unsupported_errors ||= [Selenium::WebDriver::Error::WebDriverError]
95
+ end
96
+
97
+ def execute_cdp(cmd, params = {})
98
+ if browser.respond_to? :execute_cdp
99
+ browser.execute_cdp(cmd, **params)
100
+ else
101
+ args = { cmd: cmd, params: params }
102
+ result = bridge.http.call(:post, "session/#{bridge.session_id}/goog/cdp/execute", args)
103
+ result['value']
104
+ end
105
+ end
106
+
107
+ def build_node(native_node, initial_cache = {})
108
+ ::Capybara::Selenium::ChromeNode.new(self, native_node, initial_cache)
109
+ end
110
+
111
+ def chromedriver_version
112
+ @chromedriver_version ||= begin
113
+ caps = browser.capabilities
114
+ caps['chrome']&.fetch('chromedriverVersion', nil).to_f
115
+ end
116
+ end
117
+ end
118
+
119
+ Capybara::Selenium::Driver.register_specialization :chrome, Capybara::Selenium::Driver::ChromeDriver
@@ -0,0 +1,126 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'capybara/selenium/nodes/edge_node'
4
+
5
+ module Capybara::Selenium::Driver::EdgeDriver
6
+ def self.extended(base)
7
+ bridge = base.send(:bridge)
8
+ bridge.extend Capybara::Selenium::IsDisplayed unless bridge.commands(:is_element_displayed)
9
+ base.options[:native_displayed] = false if base.options[:native_displayed].nil?
10
+ end
11
+
12
+ def fullscreen_window(handle)
13
+ return super if edgedriver_version < 75
14
+
15
+ within_given_window(handle) do
16
+ begin
17
+ super
18
+ rescue NoMethodError => e
19
+ raise unless e.message.match?(/full_screen_window/)
20
+
21
+ result = bridge.http.call(:post, "session/#{bridge.session_id}/window/fullscreen", {})
22
+ result['value']
23
+ end
24
+ end
25
+ end
26
+
27
+ def resize_window_to(handle, width, height)
28
+ super
29
+ rescue Selenium::WebDriver::Error::UnknownError => e
30
+ raise unless e.message.match?(/failed to change window state/)
31
+
32
+ # Chromedriver doesn't wait long enough for state to change when coming out of fullscreen
33
+ # and raises unnecessary error. Wait a bit and try again.
34
+ sleep 0.25
35
+ super
36
+ end
37
+
38
+ def reset!
39
+ return super if edgedriver_version < 75
40
+ # Use instance variable directly so we avoid starting the browser just to reset the session
41
+ return unless @browser
42
+
43
+ switch_to_window(window_handles.first)
44
+ window_handles.slice(1..-1).each { |win| close_window(win) }
45
+
46
+ timer = Capybara::Helpers.timer(expire_in: 10)
47
+ begin
48
+ clear_storage unless uniform_storage_clear?
49
+ @browser.navigate.to('about:blank')
50
+ wait_for_empty_page(timer)
51
+ rescue *unhandled_alert_errors
52
+ accept_unhandled_reset_alert
53
+ retry
54
+ end
55
+
56
+ execute_cdp('Storage.clearDataForOrigin', origin: '*', storageTypes: storage_types_to_clear)
57
+ end
58
+
59
+ def download_path=(path)
60
+ if @browser.respond_to?(:download_path=)
61
+ @browser.download_path = path
62
+ else
63
+ # Not yet implemented in seleniun-webdriver for edge so do it ourselves
64
+ execute_cdp('Page.setDownloadBehavior', behavior: 'allow', downloadPath: path)
65
+ end
66
+ end
67
+
68
+ private
69
+
70
+ def storage_types_to_clear
71
+ types = ['cookies']
72
+ types << 'local_storage' if clear_all_storage?
73
+ types.join(',')
74
+ end
75
+
76
+ def clear_all_storage?
77
+ storage_clears.none? { |s| s == false }
78
+ end
79
+
80
+ def uniform_storage_clear?
81
+ storage_clears.uniq { |s| s == false }.length <= 1
82
+ end
83
+
84
+ def storage_clears
85
+ options.values_at(:clear_session_storage, :clear_local_storage)
86
+ end
87
+
88
+ def clear_storage
89
+ # Edgedriver crashes if attempt to clear storage on about:blank
90
+ url = current_url
91
+ super unless url.nil? || url.start_with?('about:')
92
+ end
93
+
94
+ def delete_all_cookies
95
+ return super if edgedriver_version < 75
96
+
97
+ execute_cdp('Network.clearBrowserCookies')
98
+ rescue *cdp_unsupported_errors
99
+ # If the CDP clear isn't supported do original limited clear
100
+ super
101
+ end
102
+
103
+ def cdp_unsupported_errors
104
+ @cdp_unsupported_errors ||= [Selenium::WebDriver::Error::WebDriverError]
105
+ end
106
+
107
+ def execute_cdp(cmd, params = {})
108
+ args = { cmd: cmd, params: params }
109
+ result = bridge.http.call(:post, "session/#{bridge.session_id}/goog/cdp/execute", args)
110
+ result['value']
111
+ end
112
+
113
+ def build_node(native_node, initial_cache = {})
114
+ ::Capybara::Selenium::EdgeNode.new(self, native_node, initial_cache)
115
+ end
116
+
117
+ def edgedriver_version
118
+ @edgedriver_version ||= begin
119
+ caps = browser.capabilities
120
+ caps['chrome']&.fetch('chromedriverVersion', nil).to_f
121
+ end
122
+ end
123
+ end
124
+
125
+ Capybara::Selenium::Driver.register_specialization :edge, Capybara::Selenium::Driver::EdgeDriver
126
+ Capybara::Selenium::Driver.register_specialization :edge_chrome, Capybara::Selenium::Driver::EdgeDriver
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'capybara/selenium/nodes/firefox_node'
4
+
5
+ module Capybara::Selenium::Driver::FirefoxDriver
6
+ def self.extended(driver)
7
+ driver.extend Capybara::Selenium::Driver::W3CFirefoxDriver if w3c?(driver)
8
+ bridge = driver.send(:bridge)
9
+ bridge.extend Capybara::Selenium::IsDisplayed unless bridge.commands(:is_element_displayed)
10
+ end
11
+
12
+ def self.w3c?(driver)
13
+ (defined?(Selenium::WebDriver::VERSION) && (Selenium::WebDriver::VERSION.to_f >= 4)) ||
14
+ driver.browser.capabilities.is_a?(::Selenium::WebDriver::Remote::W3C::Capabilities)
15
+ end
16
+ end
17
+
18
+ module Capybara::Selenium::Driver::W3CFirefoxDriver
19
+ class << self
20
+ def extended(driver)
21
+ require 'capybara/selenium/patches/pause_duration_fix' if pause_broken?(driver.browser)
22
+ driver.options[:native_displayed] = false if driver.options[:native_displayed].nil?
23
+ end
24
+
25
+ def pause_broken?(sel_driver)
26
+ sel_driver.capabilities['moz:geckodriverVersion']&.start_with?('0.22.')
27
+ end
28
+ end
29
+
30
+ def resize_window_to(handle, width, height)
31
+ within_given_window(handle) do
32
+ # Don't set the size if already set - See https://github.com/mozilla/geckodriver/issues/643
33
+ if window_size(handle) == [width, height]
34
+ {}
35
+ else
36
+ super
37
+ end
38
+ end
39
+ end
40
+
41
+ def reset!
42
+ # Use instance variable directly so we avoid starting the browser just to reset the session
43
+ return unless @browser
44
+
45
+ if browser_version >= 68
46
+ begin
47
+ # Firefox 68 hangs if we try to switch windows while a modal is visible
48
+ browser.switch_to.alert&.dismiss
49
+ rescue Selenium::WebDriver::Error::NoSuchAlertError
50
+ # Swallow
51
+ end
52
+ end
53
+
54
+ switch_to_window(window_handles.first)
55
+ window_handles.slice(1..-1).each { |win| close_window(win) }
56
+ super
57
+ end
58
+
59
+ def refresh
60
+ # Accept any "will repost content" confirmation that occurs
61
+ accept_modal :confirm, wait: 0.1 do
62
+ super
63
+ end
64
+ rescue Capybara::ModalNotFound
65
+ # No modal was opened - page has refreshed - ignore
66
+ end
67
+
68
+ def switch_to_frame(frame)
69
+ return super unless frame == :parent
70
+
71
+ # geckodriver/firefox has an issue if the current frame is removed from within it
72
+ # so we have to move to the default_content and iterate back through the frames
73
+ handles = @frame_handles[current_window_handle]
74
+ browser.switch_to.default_content
75
+ handles.tap(&:pop).each { |fh| browser.switch_to.frame(fh.native) }
76
+ end
77
+
78
+ private
79
+
80
+ def build_node(native_node, initial_cache = {})
81
+ ::Capybara::Selenium::FirefoxNode.new(self, native_node, initial_cache)
82
+ end
83
+
84
+ def browser_version
85
+ browser.capabilities[:browser_version].to_f
86
+ end
87
+ end
88
+
89
+ Capybara::Selenium::Driver.register_specialization :firefox, Capybara::Selenium::Driver::FirefoxDriver
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'capybara/selenium/nodes/ie_node'
4
+
5
+ module Capybara::Selenium::Driver::InternetExplorerDriver
6
+ def switch_to_frame(frame)
7
+ return super unless frame == :parent
8
+
9
+ # iedriverserver has an issue if the current frame is removed from within it
10
+ # so we have to move to the default_content and iterate back through the frames
11
+ handles = @frame_handles[current_window_handle]
12
+ browser.switch_to.default_content
13
+ handles.tap(&:pop).each { |fh| browser.switch_to.frame(fh.native) }
14
+ end
15
+
16
+ private
17
+
18
+ def build_node(native_node, initial_cache = {})
19
+ ::Capybara::Selenium::IENode.new(self, native_node, initial_cache)
20
+ end
21
+ end
22
+
23
+ module Capybara::Selenium
24
+ Driver.register_specialization :ie, Driver::InternetExplorerDriver
25
+ Driver.register_specialization :internet_explorer, Driver::InternetExplorerDriver
26
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'capybara/selenium/nodes/safari_node'
4
+
5
+ module Capybara::Selenium::Driver::SafariDriver
6
+ def switch_to_frame(frame)
7
+ return super unless frame == :parent
8
+
9
+ # safaridriver/safari has an issue where switch_to_frame(:parent)
10
+ # behaves like switch_to_frame(:top)
11
+ handles = @frame_handles[current_window_handle]
12
+ browser.switch_to.default_content
13
+ handles.tap(&:pop).each { |fh| browser.switch_to.frame(fh.native) }
14
+ end
15
+
16
+ private
17
+
18
+ def build_node(native_node, initial_cache = {})
19
+ ::Capybara::Selenium::SafariNode.new(self, native_node, initial_cache)
20
+ end
21
+ end
22
+
23
+ Capybara::Selenium::Driver.register_specialization(/^(safari|Safari_Technology_Preview)$/,
24
+ Capybara::Selenium::Driver::SafariDriver)
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Capybara::Selenium::Node
4
+ module FileInputClickEmulation
5
+ def click(keys = [], **options)
6
+ super
7
+ rescue Selenium::WebDriver::Error::InvalidArgumentError
8
+ return emulate_click if attaching_file? && visible_file_field?
9
+
10
+ raise
11
+ end
12
+
13
+ private
14
+
15
+ def visible_file_field?
16
+ (attrs(:tagName, :type).map { |val| val&.downcase } == %w[input file]) && visible?
17
+ end
18
+
19
+ def attaching_file?
20
+ caller_locations.any? { |cl| cl.base_label == 'attach_file' }
21
+ end
22
+
23
+ def emulate_click
24
+ driver.execute_script(<<~JS, self)
25
+ arguments[0].dispatchEvent(
26
+ new MouseEvent('click', {
27
+ view: window,
28
+ bubbles: true,
29
+ cancelable: true
30
+ }));
31
+ JS
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,110 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Capybara
4
+ module Selenium
5
+ module Find
6
+ def find_xpath(selector, uses_visibility: false, styles: nil, position: false, **_options)
7
+ find_by(:xpath, selector, uses_visibility: uses_visibility, texts: [], styles: styles, position: position)
8
+ end
9
+
10
+ def find_css(selector, uses_visibility: false, texts: [], styles: nil, position: false, **_options)
11
+ find_by(:css, selector, uses_visibility: uses_visibility, texts: texts, styles: styles, position: position)
12
+ end
13
+
14
+ private
15
+
16
+ def find_by(format, selector, uses_visibility:, texts:, styles:, position:)
17
+ els = find_context.find_elements(format, selector)
18
+ hints = []
19
+
20
+ if (els.size > 2) && !ENV['DISABLE_CAPYBARA_SELENIUM_OPTIMIZATIONS']
21
+ els = filter_by_text(els, texts) unless texts.empty?
22
+ hints = gather_hints(els, uses_visibility: uses_visibility, styles: styles, position: position)
23
+ end
24
+ els.map.with_index { |el, idx| build_node(el, hints[idx] || {}) }
25
+ end
26
+
27
+ def gather_hints(elements, uses_visibility:, styles:, position:)
28
+ hints_js, functions = build_hints_js(uses_visibility, styles, position)
29
+ return [] unless functions.any?
30
+
31
+ es_context.execute_script(hints_js, elements).map! do |results|
32
+ hint = {}
33
+ hint[:style] = results.pop if functions.include?(:style_func)
34
+ hint[:position] = results.pop if functions.include?(:position_func)
35
+ hint[:visible] = results.pop if functions.include?(:vis_func)
36
+ hint
37
+ end
38
+ rescue ::Selenium::WebDriver::Error::StaleElementReferenceError,
39
+ ::Capybara::NotSupportedByDriverError
40
+ # warn 'Unexpected Stale Element Error - skipping optimization'
41
+ []
42
+ end
43
+
44
+ def filter_by_text(elements, texts)
45
+ es_context.execute_script <<~JS, elements, texts
46
+ var texts = arguments[1];
47
+ return arguments[0].filter(function(el){
48
+ var content = el.textContent.toLowerCase();
49
+ return texts.every(function(txt){ return content.indexOf(txt.toLowerCase()) != -1 });
50
+ })
51
+ JS
52
+ end
53
+
54
+ def build_hints_js(uses_visibility, styles, position)
55
+ functions = []
56
+ hints_js = +''
57
+
58
+ if uses_visibility && !is_displayed_atom.empty?
59
+ hints_js << <<~VISIBILITY_JS
60
+ var vis_func = #{is_displayed_atom};
61
+ VISIBILITY_JS
62
+ functions << :vis_func
63
+ end
64
+
65
+ if position
66
+ hints_js << <<~POSITION_JS
67
+ var position_func = function(el){
68
+ return el.getBoundingClientRect();
69
+ };
70
+ POSITION_JS
71
+ functions << :position_func
72
+ end
73
+
74
+ if styles.is_a? Hash
75
+ hints_js << <<~STYLE_JS
76
+ var style_func = function(el){
77
+ var el_styles = window.getComputedStyle(el);
78
+ return #{styles.keys.map(&:to_s)}.reduce(function(res, style){
79
+ res[style] = el_styles[style];
80
+ return res;
81
+ }, {});
82
+ };
83
+ STYLE_JS
84
+ functions << :style_func
85
+ end
86
+
87
+ hints_js << <<~EACH_JS
88
+ return arguments[0].map(function(el){
89
+ return [#{functions.join(',')}].map(function(fn){ return fn.call(null, el) });
90
+ });
91
+ EACH_JS
92
+
93
+ [hints_js, functions]
94
+ end
95
+
96
+ def es_context
97
+ respond_to?(:execute_script) ? self : driver
98
+ end
99
+
100
+ def is_displayed_atom # rubocop:disable Naming/PredicateName
101
+ @@is_displayed_atom ||= begin # rubocop:disable Style/ClassVars
102
+ browser.send(:bridge).send(:read_atom, 'isDisplayed')
103
+ rescue StandardError
104
+ # If the atom doesn't exist or other error
105
+ ''
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end