capybara 3.3.0 → 3.40.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (308) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +1 -0
  3. data/History.md +803 -13
  4. data/License.txt +1 -1
  5. data/README.md +257 -84
  6. data/lib/capybara/config.rb +25 -9
  7. data/lib/capybara/cucumber.rb +1 -1
  8. data/lib/capybara/driver/base.rb +17 -3
  9. data/lib/capybara/driver/node.rb +31 -6
  10. data/lib/capybara/dsl.rb +9 -7
  11. data/lib/capybara/helpers.rb +31 -7
  12. data/lib/capybara/minitest/spec.rb +180 -88
  13. data/lib/capybara/minitest.rb +262 -149
  14. data/lib/capybara/node/actions.rb +202 -116
  15. data/lib/capybara/node/base.rb +34 -19
  16. data/lib/capybara/node/document.rb +14 -2
  17. data/lib/capybara/node/document_matchers.rb +10 -12
  18. data/lib/capybara/node/element.rb +269 -115
  19. data/lib/capybara/node/finders.rb +99 -77
  20. data/lib/capybara/node/matchers.rb +327 -151
  21. data/lib/capybara/node/simple.rb +48 -13
  22. data/lib/capybara/node/whitespace_normalizer.rb +81 -0
  23. data/lib/capybara/queries/active_element_query.rb +18 -0
  24. data/lib/capybara/queries/ancestor_query.rb +8 -9
  25. data/lib/capybara/queries/base_query.rb +23 -16
  26. data/lib/capybara/queries/current_path_query.rb +16 -6
  27. data/lib/capybara/queries/match_query.rb +1 -0
  28. data/lib/capybara/queries/selector_query.rb +587 -130
  29. data/lib/capybara/queries/sibling_query.rb +8 -6
  30. data/lib/capybara/queries/style_query.rb +6 -2
  31. data/lib/capybara/queries/text_query.rb +28 -14
  32. data/lib/capybara/queries/title_query.rb +2 -2
  33. data/lib/capybara/rack_test/browser.rb +92 -25
  34. data/lib/capybara/rack_test/driver.rb +16 -7
  35. data/lib/capybara/rack_test/errors.rb +6 -0
  36. data/lib/capybara/rack_test/form.rb +68 -41
  37. data/lib/capybara/rack_test/node.rb +106 -39
  38. data/lib/capybara/rails.rb +1 -1
  39. data/lib/capybara/registration_container.rb +41 -0
  40. data/lib/capybara/registrations/drivers.rb +42 -0
  41. data/lib/capybara/registrations/patches/puma_ssl.rb +29 -0
  42. data/lib/capybara/registrations/servers.rb +66 -0
  43. data/lib/capybara/result.rb +75 -52
  44. data/lib/capybara/rspec/features.rb +7 -7
  45. data/lib/capybara/rspec/matcher_proxies.rb +39 -18
  46. data/lib/capybara/rspec/matchers/base.rb +113 -0
  47. data/lib/capybara/rspec/matchers/become_closed.rb +33 -0
  48. data/lib/capybara/rspec/matchers/compound.rb +88 -0
  49. data/lib/capybara/rspec/matchers/count_sugar.rb +37 -0
  50. data/lib/capybara/rspec/matchers/have_ancestor.rb +28 -0
  51. data/lib/capybara/rspec/matchers/have_current_path.rb +29 -0
  52. data/lib/capybara/rspec/matchers/have_selector.rb +69 -0
  53. data/lib/capybara/rspec/matchers/have_sibling.rb +27 -0
  54. data/lib/capybara/rspec/matchers/have_text.rb +33 -0
  55. data/lib/capybara/rspec/matchers/have_title.rb +29 -0
  56. data/lib/capybara/rspec/matchers/match_selector.rb +27 -0
  57. data/lib/capybara/rspec/matchers/match_style.rb +43 -0
  58. data/lib/capybara/rspec/matchers/spatial_sugar.rb +39 -0
  59. data/lib/capybara/rspec/matchers.rb +141 -339
  60. data/lib/capybara/rspec.rb +2 -0
  61. data/lib/capybara/selector/builders/css_builder.rb +84 -0
  62. data/lib/capybara/selector/builders/xpath_builder.rb +71 -0
  63. data/lib/capybara/selector/css.rb +27 -25
  64. data/lib/capybara/selector/definition/button.rb +68 -0
  65. data/lib/capybara/selector/definition/checkbox.rb +26 -0
  66. data/lib/capybara/selector/definition/css.rb +10 -0
  67. data/lib/capybara/selector/definition/datalist_input.rb +35 -0
  68. data/lib/capybara/selector/definition/datalist_option.rb +25 -0
  69. data/lib/capybara/selector/definition/element.rb +28 -0
  70. data/lib/capybara/selector/definition/field.rb +40 -0
  71. data/lib/capybara/selector/definition/fieldset.rb +14 -0
  72. data/lib/capybara/selector/definition/file_field.rb +13 -0
  73. data/lib/capybara/selector/definition/fillable_field.rb +33 -0
  74. data/lib/capybara/selector/definition/frame.rb +17 -0
  75. data/lib/capybara/selector/definition/id.rb +6 -0
  76. data/lib/capybara/selector/definition/label.rb +62 -0
  77. data/lib/capybara/selector/definition/link.rb +55 -0
  78. data/lib/capybara/selector/definition/link_or_button.rb +16 -0
  79. data/lib/capybara/selector/definition/option.rb +27 -0
  80. data/lib/capybara/selector/definition/radio_button.rb +27 -0
  81. data/lib/capybara/selector/definition/select.rb +81 -0
  82. data/lib/capybara/selector/definition/table.rb +109 -0
  83. data/lib/capybara/selector/definition/table_row.rb +21 -0
  84. data/lib/capybara/selector/definition/xpath.rb +5 -0
  85. data/lib/capybara/selector/definition.rb +280 -0
  86. data/lib/capybara/selector/filter.rb +1 -0
  87. data/lib/capybara/selector/filter_set.rb +73 -25
  88. data/lib/capybara/selector/filters/base.rb +24 -5
  89. data/lib/capybara/selector/filters/expression_filter.rb +3 -3
  90. data/lib/capybara/selector/filters/locator_filter.rb +29 -0
  91. data/lib/capybara/selector/filters/node_filter.rb +16 -2
  92. data/lib/capybara/selector/regexp_disassembler.rb +211 -0
  93. data/lib/capybara/selector/selector.rb +85 -348
  94. data/lib/capybara/selector/xpath_extensions.rb +17 -0
  95. data/lib/capybara/selector.rb +474 -447
  96. data/lib/capybara/selenium/atoms/getAttribute.min.js +1 -0
  97. data/lib/capybara/selenium/atoms/isDisplayed.min.js +1 -0
  98. data/lib/capybara/selenium/atoms/src/getAttribute.js +161 -0
  99. data/lib/capybara/selenium/atoms/src/isDisplayed.js +454 -0
  100. data/lib/capybara/selenium/driver.rb +255 -143
  101. data/lib/capybara/selenium/driver_specializations/chrome_driver.rb +93 -11
  102. data/lib/capybara/selenium/driver_specializations/edge_driver.rb +128 -0
  103. data/lib/capybara/selenium/driver_specializations/firefox_driver.rb +84 -0
  104. data/lib/capybara/selenium/driver_specializations/internet_explorer_driver.rb +26 -0
  105. data/lib/capybara/selenium/driver_specializations/safari_driver.rb +24 -0
  106. data/lib/capybara/selenium/extensions/file_input_click_emulation.rb +34 -0
  107. data/lib/capybara/selenium/extensions/find.rb +110 -0
  108. data/lib/capybara/selenium/extensions/html5_drag.rb +229 -0
  109. data/lib/capybara/selenium/extensions/modifier_keys_stack.rb +28 -0
  110. data/lib/capybara/selenium/extensions/scroll.rb +76 -0
  111. data/lib/capybara/selenium/node.rb +436 -134
  112. data/lib/capybara/selenium/nodes/chrome_node.rb +125 -0
  113. data/lib/capybara/selenium/nodes/edge_node.rb +110 -0
  114. data/lib/capybara/selenium/nodes/firefox_node.rb +136 -0
  115. data/lib/capybara/selenium/nodes/ie_node.rb +22 -0
  116. data/lib/capybara/selenium/nodes/safari_node.rb +118 -0
  117. data/lib/capybara/selenium/patches/atoms.rb +18 -0
  118. data/lib/capybara/selenium/patches/is_displayed.rb +16 -0
  119. data/lib/capybara/selenium/patches/logs.rb +45 -0
  120. data/lib/capybara/selenium/patches/pause_duration_fix.rb +9 -0
  121. data/lib/capybara/selenium/patches/persistent_client.rb +20 -0
  122. data/lib/capybara/server/animation_disabler.rb +56 -19
  123. data/lib/capybara/server/checker.rb +9 -3
  124. data/lib/capybara/server/middleware.rb +28 -12
  125. data/lib/capybara/server.rb +33 -10
  126. data/lib/capybara/session/config.rb +34 -10
  127. data/lib/capybara/session/matchers.rb +23 -16
  128. data/lib/capybara/session.rb +230 -170
  129. data/lib/capybara/spec/public/jquery.js +5 -5
  130. data/lib/capybara/spec/public/offset.js +6 -0
  131. data/lib/capybara/spec/public/test.js +121 -8
  132. data/lib/capybara/spec/session/accept_alert_spec.rb +11 -11
  133. data/lib/capybara/spec/session/accept_confirm_spec.rb +3 -3
  134. data/lib/capybara/spec/session/accept_prompt_spec.rb +9 -10
  135. data/lib/capybara/spec/session/active_element_spec.rb +31 -0
  136. data/lib/capybara/spec/session/all_spec.rb +127 -40
  137. data/lib/capybara/spec/session/ancestor_spec.rb +24 -19
  138. data/lib/capybara/spec/session/assert_all_of_selectors_spec.rb +67 -38
  139. data/lib/capybara/spec/session/assert_current_path_spec.rb +21 -18
  140. data/lib/capybara/spec/session/assert_selector_spec.rb +52 -58
  141. data/lib/capybara/spec/session/assert_style_spec.rb +7 -7
  142. data/lib/capybara/spec/session/assert_text_spec.rb +74 -50
  143. data/lib/capybara/spec/session/assert_title_spec.rb +12 -12
  144. data/lib/capybara/spec/session/attach_file_spec.rb +126 -72
  145. data/lib/capybara/spec/session/body_spec.rb +6 -6
  146. data/lib/capybara/spec/session/check_spec.rb +102 -47
  147. data/lib/capybara/spec/session/choose_spec.rb +58 -32
  148. data/lib/capybara/spec/session/click_button_spec.rb +219 -163
  149. data/lib/capybara/spec/session/click_link_or_button_spec.rb +49 -23
  150. data/lib/capybara/spec/session/click_link_spec.rb +77 -54
  151. data/lib/capybara/spec/session/current_scope_spec.rb +8 -8
  152. data/lib/capybara/spec/session/current_url_spec.rb +38 -29
  153. data/lib/capybara/spec/session/dismiss_confirm_spec.rb +3 -3
  154. data/lib/capybara/spec/session/dismiss_prompt_spec.rb +2 -2
  155. data/lib/capybara/spec/session/element/assert_match_selector_spec.rb +8 -8
  156. data/lib/capybara/spec/session/element/match_css_spec.rb +16 -10
  157. data/lib/capybara/spec/session/element/match_xpath_spec.rb +6 -6
  158. data/lib/capybara/spec/session/element/matches_selector_spec.rb +68 -56
  159. data/lib/capybara/spec/session/evaluate_async_script_spec.rb +7 -7
  160. data/lib/capybara/spec/session/evaluate_script_spec.rb +28 -8
  161. data/lib/capybara/spec/session/execute_script_spec.rb +8 -7
  162. data/lib/capybara/spec/session/fill_in_spec.rb +101 -46
  163. data/lib/capybara/spec/session/find_button_spec.rb +23 -23
  164. data/lib/capybara/spec/session/find_by_id_spec.rb +7 -7
  165. data/lib/capybara/spec/session/find_field_spec.rb +32 -30
  166. data/lib/capybara/spec/session/find_link_spec.rb +31 -21
  167. data/lib/capybara/spec/session/find_spec.rb +244 -141
  168. data/lib/capybara/spec/session/first_spec.rb +43 -43
  169. data/lib/capybara/spec/session/frame/frame_title_spec.rb +5 -5
  170. data/lib/capybara/spec/session/frame/frame_url_spec.rb +5 -5
  171. data/lib/capybara/spec/session/frame/switch_to_frame_spec.rb +30 -18
  172. data/lib/capybara/spec/session/frame/within_frame_spec.rb +45 -18
  173. data/lib/capybara/spec/session/go_back_spec.rb +1 -1
  174. data/lib/capybara/spec/session/go_forward_spec.rb +1 -1
  175. data/lib/capybara/spec/session/has_all_selectors_spec.rb +23 -23
  176. data/lib/capybara/spec/session/has_ancestor_spec.rb +46 -0
  177. data/lib/capybara/spec/session/has_any_selectors_spec.rb +29 -0
  178. data/lib/capybara/spec/session/has_button_spec.rb +94 -13
  179. data/lib/capybara/spec/session/has_css_spec.rb +272 -132
  180. data/lib/capybara/spec/session/has_current_path_spec.rb +50 -35
  181. data/lib/capybara/spec/session/has_element_spec.rb +47 -0
  182. data/lib/capybara/spec/session/has_field_spec.rb +137 -58
  183. data/lib/capybara/spec/session/has_link_spec.rb +44 -4
  184. data/lib/capybara/spec/session/has_none_selectors_spec.rb +31 -31
  185. data/lib/capybara/spec/session/has_select_spec.rb +84 -50
  186. data/lib/capybara/spec/session/has_selector_spec.rb +111 -71
  187. data/lib/capybara/spec/session/has_sibling_spec.rb +50 -0
  188. data/lib/capybara/spec/session/has_table_spec.rb +181 -4
  189. data/lib/capybara/spec/session/has_text_spec.rb +101 -53
  190. data/lib/capybara/spec/session/has_title_spec.rb +19 -14
  191. data/lib/capybara/spec/session/has_xpath_spec.rb +56 -38
  192. data/lib/capybara/spec/session/headers_spec.rb +1 -1
  193. data/lib/capybara/spec/session/html_spec.rb +13 -6
  194. data/lib/capybara/spec/session/matches_style_spec.rb +37 -0
  195. data/lib/capybara/spec/session/node_spec.rb +894 -142
  196. data/lib/capybara/spec/session/node_wrapper_spec.rb +10 -7
  197. data/lib/capybara/spec/session/refresh_spec.rb +9 -7
  198. data/lib/capybara/spec/session/reset_session_spec.rb +63 -35
  199. data/lib/capybara/spec/session/response_code_spec.rb +1 -1
  200. data/lib/capybara/spec/session/save_and_open_page_spec.rb +2 -2
  201. data/lib/capybara/spec/session/save_and_open_screenshot_spec.rb +2 -2
  202. data/lib/capybara/spec/session/save_page_spec.rb +37 -37
  203. data/lib/capybara/spec/session/save_screenshot_spec.rb +10 -10
  204. data/lib/capybara/spec/session/screenshot_spec.rb +2 -2
  205. data/lib/capybara/spec/session/scroll_spec.rb +119 -0
  206. data/lib/capybara/spec/session/select_spec.rb +85 -85
  207. data/lib/capybara/spec/session/selectors_spec.rb +49 -18
  208. data/lib/capybara/spec/session/sibling_spec.rb +9 -9
  209. data/lib/capybara/spec/session/text_spec.rb +25 -24
  210. data/lib/capybara/spec/session/title_spec.rb +7 -6
  211. data/lib/capybara/spec/session/uncheck_spec.rb +33 -21
  212. data/lib/capybara/spec/session/unselect_spec.rb +37 -37
  213. data/lib/capybara/spec/session/visit_spec.rb +68 -49
  214. data/lib/capybara/spec/session/window/become_closed_spec.rb +20 -17
  215. data/lib/capybara/spec/session/window/current_window_spec.rb +1 -1
  216. data/lib/capybara/spec/session/window/switch_to_window_spec.rb +20 -16
  217. data/lib/capybara/spec/session/window/window_opened_by_spec.rb +6 -2
  218. data/lib/capybara/spec/session/window/window_spec.rb +62 -63
  219. data/lib/capybara/spec/session/window/windows_spec.rb +5 -1
  220. data/lib/capybara/spec/session/window/within_window_spec.rb +14 -14
  221. data/lib/capybara/spec/session/within_spec.rb +79 -42
  222. data/lib/capybara/spec/spec_helper.rb +41 -53
  223. data/lib/capybara/spec/test_app.rb +132 -43
  224. data/lib/capybara/spec/views/animated.erb +49 -0
  225. data/lib/capybara/spec/views/form.erb +139 -42
  226. data/lib/capybara/spec/views/frame_child.erb +4 -3
  227. data/lib/capybara/spec/views/frame_one.erb +2 -1
  228. data/lib/capybara/spec/views/frame_parent.erb +1 -1
  229. data/lib/capybara/spec/views/frame_two.erb +1 -1
  230. data/lib/capybara/spec/views/initial_alert.erb +2 -1
  231. data/lib/capybara/spec/views/layout.erb +10 -0
  232. data/lib/capybara/spec/views/obscured.erb +47 -0
  233. data/lib/capybara/spec/views/offset.erb +33 -0
  234. data/lib/capybara/spec/views/path.erb +2 -2
  235. data/lib/capybara/spec/views/popup_one.erb +1 -1
  236. data/lib/capybara/spec/views/popup_two.erb +1 -1
  237. data/lib/capybara/spec/views/react.erb +45 -0
  238. data/lib/capybara/spec/views/scroll.erb +21 -0
  239. data/lib/capybara/spec/views/spatial.erb +31 -0
  240. data/lib/capybara/spec/views/tables.erb +67 -0
  241. data/lib/capybara/spec/views/with_animation.erb +39 -4
  242. data/lib/capybara/spec/views/with_base_tag.erb +2 -2
  243. data/lib/capybara/spec/views/with_dragula.erb +24 -0
  244. data/lib/capybara/spec/views/with_fixed_header_footer.erb +2 -1
  245. data/lib/capybara/spec/views/with_hover.erb +3 -2
  246. data/lib/capybara/spec/views/with_hover1.erb +10 -0
  247. data/lib/capybara/spec/views/with_html.erb +37 -9
  248. data/lib/capybara/spec/views/with_html5_svg.erb +20 -0
  249. data/lib/capybara/spec/views/with_jquery_animation.erb +24 -0
  250. data/lib/capybara/spec/views/with_js.erb +26 -5
  251. data/lib/capybara/spec/views/with_jstree.erb +26 -0
  252. data/lib/capybara/spec/views/with_namespace.erb +1 -0
  253. data/lib/capybara/spec/views/with_scope.erb +2 -2
  254. data/lib/capybara/spec/views/with_scope_other.erb +6 -0
  255. data/lib/capybara/spec/views/with_shadow.erb +31 -0
  256. data/lib/capybara/spec/views/with_slow_unload.erb +2 -1
  257. data/lib/capybara/spec/views/with_sortable_js.erb +21 -0
  258. data/lib/capybara/spec/views/with_unload_alert.erb +1 -0
  259. data/lib/capybara/spec/views/with_windows.erb +1 -1
  260. data/lib/capybara/spec/views/within_frames.erb +1 -1
  261. data/lib/capybara/version.rb +1 -1
  262. data/lib/capybara/window.rb +19 -25
  263. data/lib/capybara.rb +126 -111
  264. data/spec/basic_node_spec.rb +59 -34
  265. data/spec/capybara_spec.rb +56 -44
  266. data/spec/counter_spec.rb +35 -0
  267. data/spec/css_builder_spec.rb +101 -0
  268. data/spec/css_splitter_spec.rb +8 -8
  269. data/spec/dsl_spec.rb +79 -52
  270. data/spec/filter_set_spec.rb +9 -9
  271. data/spec/fixtures/selenium_driver_rspec_failure.rb +4 -4
  272. data/spec/fixtures/selenium_driver_rspec_success.rb +4 -4
  273. data/spec/minitest_spec.rb +45 -7
  274. data/spec/minitest_spec_spec.rb +87 -64
  275. data/spec/per_session_config_spec.rb +6 -6
  276. data/spec/rack_test_spec.rb +172 -116
  277. data/spec/regexp_dissassembler_spec.rb +250 -0
  278. data/spec/result_spec.rb +80 -72
  279. data/spec/rspec/features_spec.rb +21 -16
  280. data/spec/rspec/scenarios_spec.rb +10 -6
  281. data/spec/rspec/shared_spec_matchers.rb +407 -365
  282. data/spec/rspec/views_spec.rb +3 -3
  283. data/spec/rspec_matchers_spec.rb +35 -10
  284. data/spec/rspec_spec.rb +63 -41
  285. data/spec/sauce_spec_chrome.rb +43 -0
  286. data/spec/selector_spec.rb +334 -89
  287. data/spec/selenium_spec_chrome.rb +176 -62
  288. data/spec/selenium_spec_chrome_remote.rb +54 -14
  289. data/spec/selenium_spec_edge.rb +41 -8
  290. data/spec/selenium_spec_firefox.rb +228 -0
  291. data/spec/selenium_spec_firefox_remote.rb +94 -0
  292. data/spec/selenium_spec_ie.rb +129 -11
  293. data/spec/selenium_spec_safari.rb +162 -0
  294. data/spec/server_spec.rb +171 -97
  295. data/spec/session_spec.rb +34 -18
  296. data/spec/shared_selenium_node.rb +79 -0
  297. data/spec/shared_selenium_session.rb +344 -80
  298. data/spec/spec_helper.rb +124 -2
  299. data/spec/whitespace_normalizer_spec.rb +54 -0
  300. data/spec/xpath_builder_spec.rb +93 -0
  301. metadata +326 -28
  302. data/lib/capybara/rspec/compound.rb +0 -94
  303. data/lib/capybara/selenium/driver_specializations/marionette_driver.rb +0 -31
  304. data/lib/capybara/selenium/nodes/marionette_node.rb +0 -31
  305. data/lib/capybara/spec/session/has_style_spec.rb +0 -25
  306. data/lib/capybara/spec/session/source_spec.rb +0 -0
  307. data/lib/capybara/spec/views/with_title.erb +0 -5
  308. data/spec/selenium_spec_marionette.rb +0 -167
@@ -3,237 +3,305 @@
3
3
  module Capybara
4
4
  module Node
5
5
  module Actions
6
- ##
7
- #
8
- # Finds a button or link and clicks it. See {Capybara::Node::Actions#click_button} and
9
- # {Capybara::Node::Actions#click_link} for what locator will match against for each type of element
10
6
  # @!macro waiting_behavior
11
- # If the driver is capable of executing JavaScript, +$0+ will wait for a set amount of time
7
+ # If the driver is capable of executing JavaScript, this method will wait for a set amount of time
12
8
  # and continuously retry finding the element until either the element is found or the time
13
- # expires. The length of time +find+ will wait is controlled through {Capybara.default_max_wait_time}
9
+ # expires. The length of time this method will wait is controlled through {Capybara.configure default_max_wait_time}.
14
10
  #
15
- # @option options [false, Numeric] wait (Capybara.default_max_wait_time) Maximum time to wait for matching element to appear.
11
+ # @option options [false, true, Numeric] wait
12
+ # Maximum time to wait for matching element to appear. Defaults to {Capybara.configure default_max_wait_time}.
13
+
14
+ ##
16
15
  #
17
- # @overload click_link_or_button([locator], options)
16
+ # Finds a button or link and clicks it. See {#click_button} and
17
+ # {#click_link} for what locator will match against for each type of element.
18
18
  #
19
- # @param [String] locator See {Capybara::Node::Actions#click_button} and {Capybara::Node::Actions#click_link}
19
+ # @overload click_link_or_button([locator], **options)
20
+ # @macro waiting_behavior
21
+ # @param [String] locator See {#click_button} and {#click_link}
20
22
  #
21
23
  # @return [Capybara::Node::Element] The element clicked
22
24
  #
23
25
  def click_link_or_button(locator = nil, **options)
24
- find(:link_or_button, locator, options).click
26
+ find(:link_or_button, locator, **options).click
25
27
  end
26
28
  alias_method :click_on, :click_link_or_button
27
29
 
28
30
  ##
29
31
  #
30
- # Finds a link by id, text or title and clicks it. Also looks at image
32
+ # Finds a link by id, {Capybara.configure test_id} attribute, text or title and clicks it. Also looks at image
31
33
  # alt text inside the link.
32
34
  #
33
- # @macro waiting_behavior
34
- #
35
- # @overload click_link([locator], options)
36
- # @param [String] locator text, id, title or nested image's alt attribute
37
- # @param options See {Capybara::Node::Finders#find_link}
35
+ # @overload click_link([locator], **options)
36
+ # @macro waiting_behavior
37
+ # @param [String] locator text, id, {Capybara.configure test_id} attribute, title or nested image's alt attribute
38
+ # @param [Hash] options See {Capybara::Node::Finders#find_link}
38
39
  #
39
40
  # @return [Capybara::Node::Element] The element clicked
40
41
  def click_link(locator = nil, **options)
41
- find(:link, locator, options).click
42
+ find(:link, locator, **options).click
42
43
  end
43
44
 
44
45
  ##
45
46
  #
46
47
  # Finds a button on the page and clicks it.
47
- # This can be any \<input> element of type submit, reset, image, button or it can be a
48
- # \<button> element. All buttons can be found by their id, value, or title. \<button> elements can also be found
49
- # by their text content, and image \<input> elements by their alt attribute
50
- #
51
- # @macro waiting_behavior
48
+ # This can be any `<input>` element of type submit, reset, image, button or it can be a
49
+ # `<button>` element. All buttons can be found by their id, name, {Capybara.configure test_id} attribute, value, or title. `<button>` elements can also be found
50
+ # by their text content, and image `<input>` elements by their alt attribute.
52
51
  #
53
52
  # @overload click_button([locator], **options)
53
+ # @macro waiting_behavior
54
54
  # @param [String] locator Which button to find
55
- # @param options See {Capybara::Node::Finders#find_button}
55
+ # @param [Hash] options See {Capybara::Node::Finders#find_button}
56
56
  # @return [Capybara::Node::Element] The element clicked
57
57
  def click_button(locator = nil, **options)
58
- find(:button, locator, options).click
58
+ find(:button, locator, **options).click
59
59
  end
60
60
 
61
61
  ##
62
62
  #
63
- # Locate a text field or text area and fill it in with the given text
64
- # The field can be found via its name, id or label text.
63
+ # Locate a text field or text area and fill it in with the given text.
64
+ # The field can be found via its name, id, {Capybara.configure test_id} attribute, placeholder, or label text.
65
+ # If no locator is provided this will operate on self or a descendant.
65
66
  #
67
+ # # will fill in a descendant fillable field with name, id, or label text matching 'Name'
66
68
  # page.fill_in 'Name', with: 'Bob'
67
69
  #
70
+ # # will fill in `el` if it's a fillable field
71
+ # el.fill_in with: 'Tom'
72
+ #
68
73
  #
69
74
  # @overload fill_in([locator], with:, **options)
70
75
  # @param [String] locator Which field to fill in
71
76
  # @param [Hash] options
72
- # @param with: [String] The value to fill_in
77
+ # @param with: [String] The value to fill in
73
78
  # @macro waiting_behavior
74
79
  # @option options [String] currently_with The current value property of the field to fill in
75
80
  # @option options [Boolean] multiple Match fields that can have multiple values?
76
- # @option options [String] id Match fields that match the id attribute
81
+ # @option options [String, Regexp] id Match fields that match the id attribute
77
82
  # @option options [String] name Match fields that match the name attribute
78
83
  # @option options [String] placeholder Match fields that match the placeholder attribute
79
- # @option options [String, Array<String>] class Match fields that match the class(es) provided
80
- # @option options [Hash] fill_options Driver specific options regarding how to fill fields (Defaults come from Capybara.default_set_options)
81
- #
82
- # @return [Capybara::Node::Element] The element filled_in
83
- def fill_in(locator = nil, with:, fill_options: {}, **options)
84
- options[:with] = options.delete(:currently_with) if options.key?(:currently_with)
85
- find(:fillable_field, locator, options).set(with, fill_options)
84
+ # @option options [String, Array<String>, Regexp] class Match fields that match the class(es) provided
85
+ # @option options [Hash] fill_options Driver specific options regarding how to fill fields (Defaults come from {Capybara.configure default_set_options})
86
+ #
87
+ # @return [Capybara::Node::Element] The element filled in
88
+ def fill_in(locator = nil, with:, currently_with: nil, fill_options: {}, **find_options)
89
+ find_options[:with] = currently_with if currently_with
90
+ find_options[:allow_self] = true if locator.nil?
91
+ find(:fillable_field, locator, **find_options).set(with, **fill_options)
86
92
  end
87
93
 
88
94
  # @!macro label_click
89
- # @option options [Boolean] allow_label_click (Capybara.automatic_label_click) Attempt to click the label to toggle state if element is non-visible.
95
+ # @option options [Boolean, Hash] allow_label_click
96
+ # Attempt to click the label to toggle state if element is non-visible. Defaults to {Capybara.configure automatic_label_click}.
97
+ # If set to a Hash it is passed as options to the `click` on the label
90
98
 
91
99
  ##
92
100
  #
93
- # Find a radio button and mark it as checked. The radio button can be found
94
- # via name, id or label text.
101
+ # Find a descendant radio button and mark it as checked. The radio button can be found
102
+ # via name, id, {Capybara.configure test_id} attribute or label text. If no locator is
103
+ # provided this will match against self or a descendant.
95
104
  #
105
+ # # will choose a descendant radio button with a name, id, or label text matching 'Male'
96
106
  # page.choose('Male')
97
107
  #
108
+ # # will choose `el` if it's a radio button element
109
+ # el.choose()
110
+ #
98
111
  # @overload choose([locator], **options)
99
112
  # @param [String] locator Which radio button to choose
100
113
  #
101
114
  # @option options [String] option Value of the radio_button to choose
102
- # @option options [String] id Match fields that match the id attribute
115
+ # @option options [String, Regexp] id Match fields that match the id attribute
103
116
  # @option options [String] name Match fields that match the name attribute
104
- # @option options [String, Array<String>] class Match fields that match the class(es) provided
117
+ # @option options [String, Array<String>, Regexp] class Match fields that match the class(es) provided
105
118
  # @macro waiting_behavior
106
119
  # @macro label_click
107
120
  #
108
121
  # @return [Capybara::Node::Element] The element chosen or the label clicked
109
122
  def choose(locator = nil, **options)
110
- _check_with_label(:radio_button, true, locator, options)
123
+ _check_with_label(:radio_button, true, locator, **options)
111
124
  end
112
125
 
113
126
  ##
114
127
  #
115
- # Find a check box and mark it as checked. The check box can be found
116
- # via name, id or label text.
128
+ # Find a descendant check box and mark it as checked. The check box can be found
129
+ # via name, id, {Capybara.configure test_id} attribute, or label text. If no locator
130
+ # is provided this will match against self or a descendant.
117
131
  #
132
+ # # will check a descendant checkbox with a name, id, or label text matching 'German'
118
133
  # page.check('German')
119
134
  #
135
+ # # will check `el` if it's a checkbox element
136
+ # el.check()
137
+ #
120
138
  #
121
139
  # @overload check([locator], **options)
122
140
  # @param [String] locator Which check box to check
123
141
  #
124
142
  # @option options [String] option Value of the checkbox to select
125
- # @option options [String] id Match fields that match the id attribute
143
+ # @option options [String, Regexp] id Match fields that match the id attribute
126
144
  # @option options [String] name Match fields that match the name attribute
127
- # @option options [String, Array<String>] class Match fields that match the class(es) provided
145
+ # @option options [String, Array<String>, Regexp] class Match fields that match the class(es) provided
128
146
  # @macro label_click
129
147
  # @macro waiting_behavior
130
148
  #
131
149
  # @return [Capybara::Node::Element] The element checked or the label clicked
132
150
  def check(locator = nil, **options)
133
- _check_with_label(:checkbox, true, locator, options)
151
+ _check_with_label(:checkbox, true, locator, **options)
134
152
  end
135
153
 
136
154
  ##
137
155
  #
138
- # Find a check box and mark uncheck it. The check box can be found
139
- # via name, id or label text.
156
+ # Find a descendant check box and uncheck it. The check box can be found
157
+ # via name, id, {Capybara.configure test_id} attribute, or label text. If
158
+ # no locator is provided this will match against self or a descendant.
140
159
  #
160
+ # # will uncheck a descendant checkbox with a name, id, or label text matching 'German'
141
161
  # page.uncheck('German')
142
162
  #
163
+ # # will uncheck `el` if it's a checkbox element
164
+ # el.uncheck()
165
+ #
143
166
  #
144
167
  # @overload uncheck([locator], **options)
145
168
  # @param [String] locator Which check box to uncheck
146
169
  #
147
170
  # @option options [String] option Value of the checkbox to deselect
148
- # @option options [String] id Match fields that match the id attribute
171
+ # @option options [String, Regexp] id Match fields that match the id attribute
149
172
  # @option options [String] name Match fields that match the name attribute
150
- # @option options [String, Array<String>] class Match fields that match the class(es) provided
173
+ # @option options [String, Array<String>, Regexp] class Match fields that match the class(es) provided
151
174
  # @macro label_click
152
175
  # @macro waiting_behavior
153
176
  #
154
177
  # @return [Capybara::Node::Element] The element unchecked or the label clicked
155
178
  def uncheck(locator = nil, **options)
156
- _check_with_label(:checkbox, false, locator, options)
179
+ _check_with_label(:checkbox, false, locator, **options)
157
180
  end
158
181
 
159
182
  ##
160
183
  #
161
- # If `:from` option is present, `select` finds a select box, or text input with associated datalist,
184
+ # If `from` option is present, {#select} finds a select box, or text input with associated datalist,
162
185
  # on the page and selects a particular option from it.
163
186
  # Otherwise it finds an option inside current scope and selects it.
164
- # If the select box is a multiple select, +select+ can be called multiple times to select more than
187
+ # If the select box is a multiple select, {#select} can be called multiple times to select more than
165
188
  # one option.
166
- # The select box can be found via its name, id or label text. The option can be found by its text.
189
+ # The select box can be found via its name, id, {Capybara.configure test_id} attribute, or label text.
190
+ # The option can be found by its text.
167
191
  #
168
192
  # page.select 'March', from: 'Month'
169
193
  #
170
- # @macro waiting_behavior
194
+ # @overload select(value = nil, from: nil, **options)
195
+ # @macro waiting_behavior
171
196
  #
172
- # @param value [String] Which option to select
173
- # @param from: [String] The id, name or label of the select box
197
+ # @param value [String] Which option to select
198
+ # @param from [String] The id, {Capybara.configure test_id} attribute, name or label of the select box
174
199
  #
175
200
  # @return [Capybara::Node::Element] The option element selected
176
201
  def select(value = nil, from: nil, **options)
202
+ raise ArgumentError, 'The :from option does not take an element' if from.is_a? Capybara::Node::Element
203
+
177
204
  el = from ? find_select_or_datalist_input(from, options) : self
178
205
 
179
- if el.respond_to?(:tag_name) && (el.tag_name == "input")
206
+ if el.respond_to?(:tag_name) && (el.tag_name == 'input')
180
207
  select_datalist_option(el, value)
181
208
  else
182
- el.find(:option, value, options).select_option
209
+ el.find(:option, value, **options).select_option
183
210
  end
184
211
  end
185
212
 
186
213
  ##
187
214
  #
188
215
  # Find a select box on the page and unselect a particular option from it. If the select
189
- # box is a multiple select, +unselect+ can be called multiple times to unselect more than
190
- # one option. The select box can be found via its name, id or label text.
216
+ # box is a multiple select, {#unselect} can be called multiple times to unselect more than
217
+ # one option. The select box can be found via its name, id, {Capybara.configure test_id} attribute,
218
+ # or label text.
191
219
  #
192
220
  # page.unselect 'March', from: 'Month'
193
221
  #
194
- # @macro waiting_behavior
222
+ # @overload unselect(value = nil, from: nil, **options)
223
+ # @macro waiting_behavior
224
+ #
225
+ # @param value [String] Which option to unselect
226
+ # @param from [String] The id, {Capybara.configure test_id} attribute, name or label of the select box
195
227
  #
196
- # @param value [String] Which option to unselect
197
- # @param from: [String] The id, name or label of the select box
198
228
  #
199
229
  # @return [Capybara::Node::Element] The option element unselected
200
230
  def unselect(value = nil, from: nil, **options)
201
- scope = from ? find(:select, from, options) : self
202
- scope.find(:option, value, options).unselect_option
231
+ raise ArgumentError, 'The :from option does not take an element' if from.is_a? Capybara::Node::Element
232
+
233
+ scope = from ? find(:select, from, **options) : self
234
+ scope.find(:option, value, **options).unselect_option
203
235
  end
204
236
 
205
237
  ##
206
238
  #
207
- # Find a file field on the page and attach a file given its path. The file field can
208
- # be found via its name, id or label text.
239
+ # Find a descendant file field on the page and attach a file given its path. There are two ways to use
240
+ # {#attach_file}, in the first method the file field can be found via its name, id,
241
+ # {Capybara.configure test_id} attribute, or label text. In the case of the file field being hidden for
242
+ # styling reasons the `make_visible` option can be used to temporarily change the CSS of
243
+ # the file field, attach the file, and then revert the CSS back to original. If no locator is
244
+ # passed this will match self or a descendant.
245
+ # The second method, which is currently in beta and may be changed/removed, involves passing a block
246
+ # which performs whatever actions would trigger the file chooser to appear.
247
+ #
248
+ # # will attach file to a descendant file input element that has a name, id, or label_text matching 'My File'
249
+ # page.attach_file('My File', '/path/to/file.png')
209
250
  #
210
- # page.attach_file(locator, '/path/to/file.png')
251
+ # # will attach file to el if it's a file input element
252
+ # el.attach_file('/path/to/file.png')
211
253
  #
212
- # @overload attach_file([locator], path, **options)
254
+ # # will attach file to whatever file input is triggered by the block
255
+ # page.attach_file('/path/to/file.png') do
256
+ # page.find('#upload_button').click
257
+ # end
258
+ #
259
+ # @overload attach_file([locator], paths, **options)
213
260
  # @macro waiting_behavior
214
261
  #
215
- # @param [String] locator Which field to attach the file to
216
- # @param [String] path The path of the file that will be attached, or an array of paths
262
+ # @param [String] locator Which field to attach the file to
263
+ # @param [String, Array<String>] paths The path(s) of the file(s) that will be attached
217
264
  #
218
- # @option options [Symbol] match (Capybara.match) The matching strategy to use (:one, :first, :prefer_exact, :smart).
219
- # @option options [Boolean] exact (Capybara.exact) Match the exact label name/contents or accept a partial match.
265
+ # @option options [Symbol] match
266
+ # The matching strategy to use (:one, :first, :prefer_exact, :smart). Defaults to {Capybara.configure match}.
267
+ # @option options [Boolean] exact
268
+ # Match the exact label name/contents or accept a partial match. Defaults to {Capybara.configure exact}.
220
269
  # @option options [Boolean] multiple Match field which allows multiple file selection
221
- # @option options [String] id Match fields that match the id attribute
270
+ # @option options [String, Regexp] id Match fields that match the id attribute
222
271
  # @option options [String] name Match fields that match the name attribute
223
- # @option options [String, Array<String>] class Match fields that match the class(es) provided
224
- # @option options [true, Hash] make_visible A Hash of CSS styles to change before attempting to attach the file, if `true` { opacity: 1, display: 'block', visibility: 'visible' } is used (may not be supported by all drivers)
225
- #
226
- # @return [Capybara::Node::Element] The file field element
227
- def attach_file(locator = nil, path, make_visible: nil, **options) # rubocop:disable Style/OptionalArguments
228
- Array(path).each do |p|
229
- raise Capybara::FileNotFound, "cannot attach file, #{p} does not exist" unless File.exist?(p.to_s)
272
+ # @option options [String, Array<String>, Regexp] class Match fields that match the class(es) provided
273
+ # @option options [true, Hash] make_visible
274
+ # A Hash of CSS styles to change before attempting to attach the file, if `true`, `{ opacity: 1, display: 'block', visibility: 'visible' }` is used (may not be supported by all drivers).
275
+ # @overload attach_file(paths, &blk)
276
+ # @param [String, Array<String>] paths The path(s) of the file(s) that will be attached
277
+ # @yield Block whose actions will trigger the system file chooser to be shown
278
+ # @return [Capybara::Node::Element] The file field element
279
+ def attach_file(locator = nil, paths, make_visible: nil, **options) # rubocop:disable Style/OptionalArguments
280
+ if locator && block_given?
281
+ raise ArgumentError, '`#attach_file` does not support passing both a locator and a block'
282
+ end
283
+
284
+ Array(paths).each do |path|
285
+ raise Capybara::FileNotFound, "cannot attach file, #{path} does not exist" unless File.exist?(path.to_s)
286
+ end
287
+ options[:allow_self] = true if locator.nil?
288
+
289
+ if block_given?
290
+ begin
291
+ execute_script CAPTURE_FILE_ELEMENT_SCRIPT
292
+ yield
293
+ file_field = evaluate_script 'window._capybara_clicked_file_input'
294
+ raise ArgumentError, "Capybara was unable to determine the file input you're attaching to" unless file_field
295
+ rescue ::Capybara::NotSupportedByDriverError
296
+ warn 'Block mode of `#attach_file` is not supported by the current driver - ignoring.'
297
+ end
230
298
  end
231
299
  # Allow user to update the CSS style of the file input since they are so often hidden on a page
232
300
  if make_visible
233
- ff = find(:file_field, locator, options.merge(visible: :all))
234
- while_visible(ff, make_visible) { |el| el.set(path) }
301
+ ff = file_field || find(:file_field, locator, **options.merge(visible: :all))
302
+ while_visible(ff, make_visible) { |el| el.set(paths) }
235
303
  else
236
- find(:file_field, locator, options).set(path)
304
+ (file_field || find(:file_field, locator, **options)).set(paths)
237
305
  end
238
306
  end
239
307
 
@@ -241,26 +309,24 @@ module Capybara
241
309
 
242
310
  def find_select_or_datalist_input(from, options)
243
311
  synchronize(Capybara::Queries::BaseQuery.wait(options, session_options.default_max_wait_time)) do
312
+ find(:select, from, **options)
313
+ rescue Capybara::ElementNotFound => select_error # rubocop:disable Naming/RescuedExceptionsVariableName
314
+ raise if %i[selected with_selected multiple].any? { |option| options.key?(option) }
315
+
244
316
  begin
245
- find(:select, from, options)
246
- rescue Capybara::ElementNotFound => select_error
247
- raise if %i[selected with_selected multiple].any? { |option| options.key?(option) }
248
- begin
249
- find(:datalist_input, from, options)
250
- rescue Capybara::ElementNotFound => dlinput_error
251
- raise Capybara::ElementNotFound, "#{select_error.message} and #{dlinput_error.message}"
252
- end
317
+ find(:datalist_input, from, **options)
318
+ rescue Capybara::ElementNotFound => dlinput_error
319
+ raise Capybara::ElementNotFound, "#{select_error.message} and #{dlinput_error.message}"
253
320
  end
254
321
  end
255
322
  end
256
323
 
257
324
  def select_datalist_option(input, value)
258
325
  datalist_options = input.evaluate_script(DATALIST_OPTIONS_SCRIPT)
259
- if (option = datalist_options.find { |o| o['value'] == value || o['label'] == value })
260
- input.set(option['value'])
261
- else
262
- raise ::Capybara::ElementNotFound, %(Unable to find datalist option "#{value}")
263
- end
326
+ option = datalist_options.find { |opt| opt.values_at('value', 'label').include?(value) }
327
+ raise ::Capybara::ElementNotFound, %(Unable to find datalist option "#{value}") unless option
328
+
329
+ input.set(option['value'])
264
330
  rescue ::Capybara::NotSupportedByDriverError
265
331
  # Implement for drivers that don't support JS
266
332
  datalist = find(:xpath, XPath.descendant(:datalist)[XPath.attr(:id) == input[:list]], visible: false)
@@ -269,9 +335,14 @@ module Capybara
269
335
  end
270
336
 
271
337
  def while_visible(element, visible_css)
272
- visible_css = { opacity: 1, display: 'block', visibility: 'visible' } if visible_css == true
338
+ if visible_css == true
339
+ visible_css = { opacity: 1, display: 'block', visibility: 'visible', width: 'auto', height: 'auto' }
340
+ end
273
341
  _update_style(element, visible_css)
274
- raise ExpectationNotMet, "The style changes in :make_visible did not make the file input visible" unless element.visible?
342
+ unless element.visible?
343
+ raise ExpectationNotMet, 'The style changes in :make_visible did not make the file input visible'
344
+ end
345
+
275
346
  begin
276
347
  yield element
277
348
  ensure
@@ -282,53 +353,68 @@ module Capybara
282
353
  def _update_style(element, style)
283
354
  element.execute_script(UPDATE_STYLE_SCRIPT, style)
284
355
  rescue Capybara::NotSupportedByDriverError
285
- warn "The :make_visible option is not supported by the current driver - ignoring"
356
+ warn 'The :make_visible option is not supported by the current driver - ignoring'
286
357
  end
287
358
 
288
359
  def _reset_style(element)
289
360
  element.execute_script(RESET_STYLE_SCRIPT)
290
- rescue StandardError # rubocop:disable Lint/HandleExceptions swallow extra errors
361
+ rescue StandardError # rubocop:disable Lint/SuppressedException swallow extra errors
291
362
  end
292
363
 
293
- def _check_with_label(selector, checked, locator, allow_label_click: session_options.automatic_label_click, **options)
364
+ def _check_with_label(selector, checked, locator,
365
+ allow_label_click: session_options.automatic_label_click, **options)
366
+ options[:allow_self] = true if locator.nil?
294
367
  synchronize(Capybara::Queries::BaseQuery.wait(options, session_options.default_max_wait_time)) do
368
+ el = find(selector, locator, **options)
369
+ el.set(checked)
370
+ rescue StandardError => e
371
+ raise unless allow_label_click && catch_error?(e)
372
+
295
373
  begin
296
- el = find(selector, locator, options)
297
- el.set(checked)
298
- rescue StandardError => e
299
- raise unless allow_label_click && catch_error?(e)
300
- begin
301
- el ||= find(selector, locator, options.merge(visible: :all))
302
- find(:label, for: el, visible: true).click unless el.checked? == checked
303
- rescue StandardError # swallow extra errors - raise original
304
- raise e
374
+ el ||= find(selector, locator, **options.merge(visible: :all))
375
+ unless el.checked? == checked
376
+ el.session
377
+ .find(:label, for: el, visible: true, match: :first)
378
+ .click(**(Hash.try_convert(allow_label_click) || {}))
305
379
  end
380
+ rescue StandardError # swallow extra errors - raise original
381
+ raise e
306
382
  end
307
383
  end
308
384
  end
309
385
 
310
- UPDATE_STYLE_SCRIPT = <<~'JS'
386
+ UPDATE_STYLE_SCRIPT = <<~JS
311
387
  this.capybara_style_cache = this.style.cssText;
312
388
  var css = arguments[0];
313
389
  for (var prop in css){
314
390
  if (css.hasOwnProperty(prop)) {
315
- this.style[prop] = css[prop]
391
+ this.style.setProperty(prop, css[prop], "important");
316
392
  }
317
393
  }
318
394
  JS
319
395
 
320
- RESET_STYLE_SCRIPT = <<~'JS'
396
+ RESET_STYLE_SCRIPT = <<~JS
321
397
  if (this.hasOwnProperty('capybara_style_cache')) {
322
398
  this.style.cssText = this.capybara_style_cache;
323
399
  delete this.capybara_style_cache;
324
400
  }
325
401
  JS
326
402
 
327
- DATALIST_OPTIONS_SCRIPT = <<~'JS'
403
+ DATALIST_OPTIONS_SCRIPT = <<~JS
328
404
  Array.prototype.slice.call((this.list||{}).options || []).
329
405
  filter(function(el){ return !el.disabled }).
330
406
  map(function(el){ return { "value": el.value, "label": el.label} })
331
407
  JS
408
+
409
+ CAPTURE_FILE_ELEMENT_SCRIPT = <<~JS
410
+ document.addEventListener('click', function file_catcher(e){
411
+ if (e.target.matches("input[type='file']")) {
412
+ window._capybara_clicked_file_input = e.target;
413
+ this.removeEventListener('click', file_catcher);
414
+ e.preventDefault();
415
+ }
416
+ }, {capture: true})
417
+ JS
332
418
  end
333
419
  end
334
420
  end
@@ -73,36 +73,51 @@ module Capybara
73
73
  # @return [Object] The result of the given block
74
74
  # @raise [Capybara::FrozenInTime] If the return value of `Time.now` appears stuck
75
75
  #
76
- def synchronize(seconds = session_options.default_max_wait_time, errors: nil)
77
- if session.synchronized
76
+ def synchronize(seconds = nil, errors: nil)
77
+ return yield if session.synchronized
78
+
79
+ seconds = session_options.default_max_wait_time if [nil, true].include? seconds
80
+ interval = session_options.default_retry_interval
81
+ session.synchronized = true
82
+ timer = Capybara::Helpers.timer(expire_in: seconds)
83
+ begin
78
84
  yield
79
- else
80
- session.synchronized = true
81
- timer = Capybara::Helpers.timer(expire_in: seconds)
82
- begin
83
- yield
84
- rescue StandardError => e
85
- session.raise_server_error!
86
- raise e unless driver.wait? && catch_error?(e, errors)
85
+ rescue StandardError => e
86
+ session.raise_server_error!
87
+ raise e unless catch_error?(e, errors)
88
+
89
+ if driver.wait?
87
90
  raise e if timer.expired?
88
- sleep(0.05)
89
- raise Capybara::FrozenInTime, "Time appears to be frozen. Capybara does not work with libraries which freeze time, consider using time travelling instead" if timer.stalled?
91
+
92
+ sleep interval
93
+ reload if session_options.automatic_reload
94
+ else
95
+ old_base = @base
90
96
  reload if session_options.automatic_reload
91
- retry
92
- ensure
93
- session.synchronized = false
97
+ raise e if old_base == @base
94
98
  end
99
+ retry
100
+ ensure
101
+ session.synchronized = false
95
102
  end
96
103
  end
97
104
 
98
105
  # @api private
99
- def find_css(css)
100
- base.find_css(css)
106
+ def find_css(css, **options)
107
+ if base.method(:find_css).arity == 1
108
+ base.find_css(css)
109
+ else
110
+ base.find_css(css, **options)
111
+ end
101
112
  end
102
113
 
103
114
  # @api private
104
- def find_xpath(xpath)
105
- base.find_xpath(xpath)
115
+ def find_xpath(xpath, **options)
116
+ if base.method(:find_xpath).arity == 1
117
+ base.find_xpath(xpath)
118
+ else
119
+ base.find_xpath(xpath, **options)
120
+ end
106
121
  end
107
122
 
108
123
  # @api private
@@ -20,8 +20,8 @@ module Capybara
20
20
  #
21
21
  # @return [String] The text of the document
22
22
  #
23
- def text(type = nil)
24
- find(:xpath, '/html').text(type)
23
+ def text(type = nil, normalize_ws: false)
24
+ find(:xpath, '/html').text(type, normalize_ws: normalize_ws)
25
25
  end
26
26
 
27
27
  ##
@@ -31,6 +31,18 @@ module Capybara
31
31
  def title
32
32
  session.driver.title
33
33
  end
34
+
35
+ def execute_script(*args)
36
+ find(:xpath, '/html').execute_script(*args)
37
+ end
38
+
39
+ def evaluate_script(*args)
40
+ find(:xpath, '/html').evaluate_script(*args)
41
+ end
42
+
43
+ def scroll_to(*args, quirks: false, **options)
44
+ find(:xpath, quirks ? '//body' : '/html').scroll_to(*args, **options)
45
+ end
34
46
  end
35
47
  end
36
48
  end