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/extensions/html5_drag'
4
+ require 'capybara/selenium/extensions/file_input_click_emulation'
5
+
6
+ class Capybara::Selenium::ChromeNode < Capybara::Selenium::Node
7
+ include Html5Drag
8
+ include FileInputClickEmulation
9
+
10
+ def set_text(value, clear: nil, **_unused)
11
+ super.tap do
12
+ # React doesn't see the chromedriver element clear
13
+ send_keys(:space, :backspace) if value.to_s.empty? && clear.nil?
14
+ end
15
+ end
16
+
17
+ def set_file(value) # rubocop:disable Naming/AccessorMethodName
18
+ # In Chrome 75+ files are appended (due to WebDriver spec - why?) so we have to clear here if its multiple and already set
19
+ if browser_version >= 75.0
20
+ driver.execute_script(<<~JS, self)
21
+ if (arguments[0].multiple && arguments[0].files.length){
22
+ arguments[0].value = null;
23
+ }
24
+ JS
25
+ end
26
+ super
27
+ rescue *file_errors => e
28
+ if e.message.match?(/File not found : .+\n.+/m)
29
+ raise ArgumentError, "Selenium < 3.14 with remote Chrome doesn't support multiple file upload"
30
+ end
31
+
32
+ raise
33
+ end
34
+
35
+ def drop(*args)
36
+ html5_drop(*args)
37
+ end
38
+
39
+ def click(*, **)
40
+ super
41
+ rescue ::Selenium::WebDriver::Error::ElementClickInterceptedError
42
+ raise
43
+ rescue ::Selenium::WebDriver::Error::WebDriverError => e
44
+ # chromedriver 74 (at least on mac) raises the wrong error for this
45
+ if e.message.match?(/element click intercepted/)
46
+ raise ::Selenium::WebDriver::Error::ElementClickInterceptedError, e.message
47
+ end
48
+
49
+ raise
50
+ end
51
+
52
+ def disabled?
53
+ driver.evaluate_script("arguments[0].matches(':disabled, select:disabled *')", self)
54
+ end
55
+
56
+ def select_option
57
+ # To optimize to only one check and then click
58
+ selected_or_disabled = driver.evaluate_script(<<~JS, self)
59
+ arguments[0].matches(':disabled, select:disabled *, :checked')
60
+ JS
61
+ click unless selected_or_disabled
62
+ end
63
+
64
+ def visible?
65
+ return super unless native_displayed?
66
+
67
+ begin
68
+ bridge.send(:execute, :is_element_displayed, id: native.ref)
69
+ rescue Selenium::WebDriver::Error::UnknownCommandError
70
+ # If the is_element_displayed command is unknown, no point in trying again
71
+ driver.options[:native_displayed] = false
72
+ super
73
+ end
74
+ end
75
+
76
+ private
77
+
78
+ def perform_legacy_drag(element, drop_modifiers)
79
+ return super if chromedriver_fixed_actions_key_state? || !w3c? || element.obscured?
80
+
81
+ raise ArgumentError, 'Modifier keys are not supported while dragging in this version of Chrome.' unless drop_modifiers.empty?
82
+
83
+ # W3C Chrome/chromedriver < 77 doesn't maintain mouse button state across actions API performs
84
+ # https://bugs.chromium.org/p/chromedriver/issues/detail?id=2981
85
+ browser_action.release.perform
86
+ browser_action.click_and_hold(native).move_to(element.native).release.perform
87
+ end
88
+
89
+ def file_errors
90
+ @file_errors = ::Selenium::WebDriver.logger.suppress_deprecations do
91
+ [::Selenium::WebDriver::Error::ExpectedError]
92
+ end
93
+ end
94
+
95
+ def browser_version(to_float = true)
96
+ caps = capabilities
97
+ ver = (caps[:browser_version] || caps[:version])
98
+ ver = ver.to_f if to_float
99
+ ver
100
+ end
101
+
102
+ def chromedriver_fixed_actions_key_state?
103
+ Gem::Version.new(chromedriver_version) >= Gem::Version.new('76.0.3809.68')
104
+ end
105
+
106
+ def chromedriver_supports_displayed_endpoint?
107
+ Gem::Version.new(chromedriver_version) >= Gem::Version.new('76.0.3809.25')
108
+ end
109
+
110
+ def chromedriver_version
111
+ capabilities['chrome']['chromedriverVersion'].split(' ')[0]
112
+ end
113
+
114
+ def native_displayed?
115
+ (driver.options[:native_displayed] != false) &&
116
+ (w3c? && chromedriver_supports_displayed_endpoint?) &&
117
+ (!ENV['DISABLE_CAPYBARA_SELENIUM_OPTIMIZATIONS'])
118
+ end
119
+ end
@@ -0,0 +1,104 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'capybara/selenium/extensions/html5_drag'
4
+
5
+ class Capybara::Selenium::EdgeNode < Capybara::Selenium::Node
6
+ include Html5Drag
7
+
8
+ def set_text(value, clear: nil, **_unused)
9
+ return super unless chrome_edge?
10
+
11
+ super.tap do
12
+ # React doesn't see the chromedriver element clear
13
+ send_keys(:space, :backspace) if value.to_s.empty? && clear.nil?
14
+ end
15
+ end
16
+
17
+ def set_file(value) # rubocop:disable Naming/AccessorMethodName
18
+ # In Chrome 75+ files are appended (due to WebDriver spec - why?) so we have to clear here if its multiple and already set
19
+ if chrome_edge?
20
+ driver.execute_script(<<~JS, self)
21
+ if (arguments[0].multiple && arguments[0].files.length){
22
+ arguments[0].value = null;
23
+ }
24
+ JS
25
+ end
26
+ super
27
+ rescue *file_errors => e
28
+ if e.message.match?(/File not found : .+\n.+/m)
29
+ raise ArgumentError, "Selenium < 3.14 with remote Chrome doesn't support multiple file upload"
30
+ end
31
+
32
+ raise
33
+ end
34
+
35
+ def drop(*args)
36
+ return super unless chrome_edge?
37
+
38
+ html5_drop(*args)
39
+ end
40
+
41
+ def click(*)
42
+ super
43
+ rescue Selenium::WebDriver::Error::InvalidArgumentError => e
44
+ tag_name, type = attrs(:tagName, :type).map { |val| val&.downcase }
45
+ if tag_name == 'input' && type == 'file'
46
+ raise Selenium::WebDriver::Error::InvalidArgumentError, "EdgeChrome can't click on file inputs.\n#{e.message}"
47
+ end
48
+
49
+ raise
50
+ end
51
+
52
+ def disabled?
53
+ return super unless chrome_edge?
54
+
55
+ driver.evaluate_script("arguments[0].matches(':disabled, select:disabled *')", self)
56
+ end
57
+
58
+ def select_option
59
+ return super unless chrome_edge?
60
+
61
+ # To optimize to only one check and then click
62
+ selected_or_disabled = driver.evaluate_script(<<~JS, self)
63
+ arguments[0].matches(':disabled, select:disabled *, :checked')
64
+ JS
65
+ click unless selected_or_disabled
66
+ end
67
+
68
+ def visible?
69
+ return super unless chrome_edge? && native_displayed?
70
+
71
+ begin
72
+ bridge.send(:execute, :is_element_displayed, id: native.ref)
73
+ rescue Selenium::WebDriver::Error::UnknownCommandError
74
+ # If the is_element_displayed command is unknown, no point in trying again
75
+ driver.options[:native_displayed] = false
76
+ super
77
+ end
78
+ end
79
+
80
+ private
81
+
82
+ def file_errors
83
+ @file_errors = ::Selenium::WebDriver.logger.suppress_deprecations do
84
+ [::Selenium::WebDriver::Error::ExpectedError]
85
+ end
86
+ end
87
+
88
+ def browser_version
89
+ @browser_version ||= begin
90
+ caps = driver.browser.capabilities
91
+ (caps[:browser_version] || caps[:version]).to_f
92
+ end
93
+ end
94
+
95
+ def chrome_edge?
96
+ browser_version >= 75
97
+ end
98
+
99
+ def native_displayed?
100
+ (driver.options[:native_displayed] != false) &&
101
+ # chromedriver_supports_displayed_endpoint? &&
102
+ (!ENV['DISABLE_CAPYBARA_SELENIUM_OPTIMIZATIONS'])
103
+ end
104
+ end
@@ -0,0 +1,131 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'capybara/selenium/extensions/html5_drag'
4
+ require 'capybara/selenium/extensions/file_input_click_emulation'
5
+
6
+ class Capybara::Selenium::FirefoxNode < Capybara::Selenium::Node
7
+ include Html5Drag
8
+ include FileInputClickEmulation
9
+
10
+ def click(keys = [], **options)
11
+ super
12
+ rescue ::Selenium::WebDriver::Error::ElementNotInteractableError
13
+ if tag_name == 'tr'
14
+ warn 'You are attempting to click a table row which has issues in geckodriver/marionette - '\
15
+ 'see https://github.com/mozilla/geckodriver/issues/1228. Your test should probably be '\
16
+ 'clicking on a table cell like a user would. Clicking the first cell in the row instead.'
17
+ return find_css('th:first-child,td:first-child')[0].click(keys, **options)
18
+ end
19
+ raise
20
+ end
21
+
22
+ def disabled?
23
+ driver.evaluate_script("arguments[0].matches(':disabled, select:disabled *')", self)
24
+ end
25
+
26
+ def set_file(value) # rubocop:disable Naming/AccessorMethodName
27
+ # By default files are appended so we have to clear here if its multiple and already set
28
+ driver.execute_script(<<~JS, self)
29
+ if (arguments[0].multiple && arguments[0].files.length){
30
+ arguments[0].value = null;
31
+ }
32
+ JS
33
+ return super if browser_version >= 62.0
34
+
35
+ # Workaround lack of support for multiple upload by uploading one at a time
36
+ path_names = value.to_s.empty? ? [] : Array(value)
37
+ if (fd = bridge.file_detector) && !driver.browser.respond_to?(:upload)
38
+ path_names.map! { |path| upload(fd.call([path])) || path }
39
+ end
40
+ path_names.each { |path| native.send_keys(path) }
41
+ end
42
+
43
+ def send_keys(*args)
44
+ # https://github.com/mozilla/geckodriver/issues/846
45
+ return super(*args.map { |arg| arg == :space ? ' ' : arg }) if args.none? { |arg| arg.is_a? Array }
46
+
47
+ native.click
48
+ _send_keys(args).perform
49
+ end
50
+
51
+ def drop(*args)
52
+ html5_drop(*args)
53
+ end
54
+
55
+ def hover
56
+ return super unless browser_version >= 65.0
57
+
58
+ # work around issue 2156 - https://github.com/teamcapybara/capybara/issues/2156
59
+ scroll_if_needed { browser_action.move_to(native, 0, 0).move_to(native).perform }
60
+ end
61
+
62
+ def select_option
63
+ # To optimize to only one check and then click
64
+ selected_or_disabled = driver.evaluate_script(<<~JS, self)
65
+ arguments[0].matches(':disabled, select:disabled *, :checked')
66
+ JS
67
+ click unless selected_or_disabled
68
+ end
69
+
70
+ def visible?
71
+ return super unless native_displayed?
72
+
73
+ begin
74
+ bridge.send(:execute, :is_element_displayed, id: native.ref)
75
+ rescue Selenium::WebDriver::Error::UnknownCommandError
76
+ # If the is_element_displayed command is unknown, no point in trying again
77
+ driver.options[:native_displayed] = false
78
+ super
79
+ end
80
+ end
81
+
82
+ private
83
+
84
+ def native_displayed?
85
+ (driver.options[:native_displayed] != false) && !ENV['DISABLE_CAPYBARA_SELENIUM_OPTIMIZATIONS']
86
+ end
87
+
88
+ def perform_with_options(click_options)
89
+ # Firefox/marionette has an issue clicking with offset near viewport edge
90
+ # scroll element to middle just in case
91
+ scroll_to_center if click_options.coords?
92
+ super
93
+ end
94
+
95
+ def _send_keys(keys, actions = browser_action, down_keys = ModifierKeysStack.new)
96
+ case keys
97
+ when :control, :left_control, :right_control,
98
+ :alt, :left_alt, :right_alt,
99
+ :shift, :left_shift, :right_shift,
100
+ :meta, :left_meta, :right_meta,
101
+ :command
102
+ down_keys.press(keys)
103
+ actions.key_down(keys)
104
+ when String
105
+ # https://bugzilla.mozilla.org/show_bug.cgi?id=1405370
106
+ keys = keys.upcase if (browser_version < 64.0) && down_keys&.include?(:shift)
107
+ actions.send_keys(keys)
108
+ when Symbol
109
+ actions.send_keys(keys)
110
+ when Array
111
+ down_keys.push
112
+ keys.each { |sub_keys| _send_keys(sub_keys, actions, down_keys) }
113
+ down_keys.pop.reverse_each { |key| actions.key_up(key) }
114
+ else
115
+ raise ArgumentError, 'Unknown keys type'
116
+ end
117
+ actions
118
+ end
119
+
120
+ def upload(local_file)
121
+ return nil unless local_file
122
+ raise ArgumentError, "You may only upload files: #{local_file.inspect}" unless File.file?(local_file)
123
+
124
+ file = ::Selenium::WebDriver::Zipper.zip_file(local_file)
125
+ bridge.http.call(:post, "session/#{bridge.session_id}/file", file: file)['value']
126
+ end
127
+
128
+ def browser_version
129
+ driver.browser.capabilities[:browser_version].to_f
130
+ end
131
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'capybara/selenium/extensions/html5_drag'
4
+
5
+ class Capybara::Selenium::IENode < Capybara::Selenium::Node
6
+ def disabled?
7
+ # super
8
+ # optimize to one script call
9
+ driver.evaluate_script <<~JS.delete("\n"), self
10
+ arguments[0].msMatchesSelector('
11
+ :disabled,
12
+ select:disabled *,
13
+ optgroup:disabled *,
14
+ fieldset[disabled],
15
+ fieldset[disabled] > *:not(legend),
16
+ fieldset[disabled] > *:not(legend) *,
17
+ fieldset[disabled] > legend:nth-of-type(n+2),
18
+ fieldset[disabled] > legend:nth-of-type(n+2) *
19
+ ')
20
+ JS
21
+ end
22
+ end
@@ -0,0 +1,118 @@
1
+ # frozen_string_literal: true
2
+
3
+ # require 'capybara/selenium/extensions/html5_drag'
4
+ require 'capybara/selenium/extensions/modifier_keys_stack'
5
+
6
+ class Capybara::Selenium::SafariNode < Capybara::Selenium::Node
7
+ # include Html5Drag
8
+
9
+ def click(keys = [], **options)
10
+ # driver.execute_script('arguments[0].scrollIntoViewIfNeeded({block: "center"})', self)
11
+ super
12
+ rescue ::Selenium::WebDriver::Error::ElementNotInteractableError
13
+ if tag_name == 'tr'
14
+ warn 'You are attempting to click a table row which has issues in safaridriver - '\
15
+ 'Your test should probably be clicking on a table cell like a user would. '\
16
+ 'Clicking the first cell in the row instead.'
17
+ return find_css('th:first-child,td:first-child')[0].click(keys, options)
18
+ end
19
+ raise
20
+ rescue ::Selenium::WebDriver::Error::WebDriverError => e
21
+ raise unless e.instance_of? ::Selenium::WebDriver::Error::WebDriverError
22
+
23
+ # Safari doesn't return a specific error here - assume it's an ElementNotInteractableError
24
+ raise ::Selenium::WebDriver::Error::ElementNotInteractableError,
25
+ 'Non distinct error raised in #click, translated to ElementNotInteractableError for retry'
26
+ end
27
+
28
+ def select_option
29
+ # To optimize to only one check and then click
30
+ selected_or_disabled = driver.execute_script(<<~JS, self)
31
+ arguments[0].closest('select').scrollIntoView();
32
+ return arguments[0].matches(':disabled, select:disabled *, :checked');
33
+ JS
34
+ click unless selected_or_disabled
35
+ end
36
+
37
+ def unselect_option
38
+ driver.execute_script("arguments[0].closest('select').scrollIntoView()", self)
39
+ super
40
+ end
41
+
42
+ def visible_text
43
+ return '' unless visible?
44
+
45
+ vis_text = driver.execute_script('return arguments[0].innerText', self)
46
+ vis_text.gsub(/\ +/, ' ')
47
+ .gsub(/[\ \n]*\n[\ \n]*/, "\n")
48
+ .gsub(/\A[[:space:]&&[^\u00a0]]+/, '')
49
+ .gsub(/[[:space:]&&[^\u00a0]]+\z/, '')
50
+ .tr("\u00a0", ' ')
51
+ end
52
+
53
+ def disabled?
54
+ driver.evaluate_script("arguments[0].matches(':disabled, select:disabled *')", self)
55
+ end
56
+
57
+ def set_file(value) # rubocop:disable Naming/AccessorMethodName
58
+ # By default files are appended so we have to clear here if its multiple and already set
59
+ native.clear if multiple? && driver.evaluate_script('arguments[0].files', self).any?
60
+ super
61
+ end
62
+
63
+ def send_keys(*args)
64
+ if args.none? { |arg| arg.is_a?(Array) || (arg.is_a?(Symbol) && MODIFIER_KEYS.include?(arg)) }
65
+ return super(*args.map { |arg| arg == :space ? ' ' : arg })
66
+ end
67
+
68
+ native.click
69
+ _send_keys(args).perform
70
+ end
71
+
72
+ def set_text(value, clear: nil, **_unused)
73
+ value = value.to_s
74
+ if clear == :backspace
75
+ # Clear field by sending the correct number of backspace keys.
76
+ backspaces = [:backspace] * self.value.to_s.length
77
+ send_keys(*([[:control, 'e']] + backspaces + [value]))
78
+ else
79
+ super.tap do
80
+ # React doesn't see the safaridriver element clear
81
+ send_keys(:space, :backspace) if value.to_s.empty? && clear.nil?
82
+ end
83
+ end
84
+ end
85
+
86
+ def hover
87
+ # Workaround issue where hover would sometimes fail - possibly due to mouse not having moved
88
+ scroll_if_needed { browser_action.move_to(native, 0, 0).move_to(native).perform }
89
+ end
90
+
91
+ private
92
+
93
+ def _send_keys(keys, actions = browser_action, down_keys = ModifierKeysStack.new)
94
+ case keys
95
+ when *MODIFIER_KEYS
96
+ down_keys.press(keys)
97
+ actions.key_down(keys)
98
+ when String
99
+ keys = keys.upcase if down_keys&.include?(:shift)
100
+ actions.send_keys(keys)
101
+ when Symbol
102
+ actions.send_keys(keys)
103
+ when Array
104
+ down_keys.push
105
+ keys.each { |sub_keys| _send_keys(sub_keys, actions, down_keys) }
106
+ down_keys.pop.reverse_each { |key| actions.key_up(key) }
107
+ else
108
+ raise ArgumentError, 'Unknown keys type'
109
+ end
110
+ actions
111
+ end
112
+
113
+ MODIFIER_KEYS = %i[control left_control right_control
114
+ alt left_alt right_alt
115
+ shift left_shift right_shift
116
+ meta left_meta right_meta
117
+ command].freeze
118
+ end