capybara 2.18.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 (316) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +1 -1
  3. data/History.md +945 -12
  4. data/License.txt +1 -1
  5. data/README.md +264 -90
  6. data/lib/capybara/config.rb +29 -57
  7. data/lib/capybara/cucumber.rb +2 -3
  8. data/lib/capybara/driver/base.rb +35 -18
  9. data/lib/capybara/driver/node.rb +40 -10
  10. data/lib/capybara/dsl.rb +10 -7
  11. data/lib/capybara/helpers.rb +70 -31
  12. data/lib/capybara/minitest/spec.rb +185 -83
  13. data/lib/capybara/minitest.rb +232 -112
  14. data/lib/capybara/node/actions.rb +274 -171
  15. data/lib/capybara/node/base.rb +42 -34
  16. data/lib/capybara/node/document.rb +15 -3
  17. data/lib/capybara/node/document_matchers.rb +19 -21
  18. data/lib/capybara/node/element.rb +362 -135
  19. data/lib/capybara/node/finders.rb +151 -137
  20. data/lib/capybara/node/matchers.rb +394 -209
  21. data/lib/capybara/node/simple.rb +59 -26
  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 +12 -9
  25. data/lib/capybara/queries/base_query.rb +39 -28
  26. data/lib/capybara/queries/current_path_query.rb +21 -27
  27. data/lib/capybara/queries/match_query.rb +14 -7
  28. data/lib/capybara/queries/selector_query.rb +659 -149
  29. data/lib/capybara/queries/sibling_query.rb +11 -9
  30. data/lib/capybara/queries/style_query.rb +45 -0
  31. data/lib/capybara/queries/text_query.rb +56 -38
  32. data/lib/capybara/queries/title_query.rb +8 -11
  33. data/lib/capybara/rack_test/browser.rb +114 -42
  34. data/lib/capybara/rack_test/css_handlers.rb +6 -4
  35. data/lib/capybara/rack_test/driver.rb +22 -17
  36. data/lib/capybara/rack_test/errors.rb +6 -0
  37. data/lib/capybara/rack_test/form.rb +93 -58
  38. data/lib/capybara/rack_test/node.rb +184 -81
  39. data/lib/capybara/rails.rb +3 -7
  40. data/lib/capybara/registration_container.rb +41 -0
  41. data/lib/capybara/registrations/drivers.rb +42 -0
  42. data/lib/capybara/registrations/patches/puma_ssl.rb +29 -0
  43. data/lib/capybara/registrations/servers.rb +66 -0
  44. data/lib/capybara/result.rb +97 -63
  45. data/lib/capybara/rspec/features.rb +17 -50
  46. data/lib/capybara/rspec/matcher_proxies.rb +52 -15
  47. data/lib/capybara/rspec/matchers/base.rb +113 -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 +69 -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 +43 -0
  59. data/lib/capybara/rspec/matchers/spatial_sugar.rb +39 -0
  60. data/lib/capybara/rspec/matchers.rb +146 -310
  61. data/lib/capybara/rspec.rb +7 -11
  62. data/lib/capybara/selector/builders/css_builder.rb +84 -0
  63. data/lib/capybara/selector/builders/xpath_builder.rb +71 -0
  64. data/lib/capybara/selector/css.rb +85 -13
  65. data/lib/capybara/selector/definition/button.rb +68 -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 +28 -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 +55 -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/definition.rb +280 -0
  87. data/lib/capybara/selector/filter.rb +2 -17
  88. data/lib/capybara/selector/filter_set.rb +80 -33
  89. data/lib/capybara/selector/filters/base.rb +50 -6
  90. data/lib/capybara/selector/filters/expression_filter.rb +8 -26
  91. data/lib/capybara/selector/filters/locator_filter.rb +29 -0
  92. data/lib/capybara/selector/filters/node_filter.rb +16 -12
  93. data/lib/capybara/selector/regexp_disassembler.rb +211 -0
  94. data/lib/capybara/selector/selector.rb +93 -210
  95. data/lib/capybara/selector/xpath_extensions.rb +17 -0
  96. data/lib/capybara/selector.rb +475 -523
  97. data/lib/capybara/selenium/atoms/getAttribute.min.js +1 -0
  98. data/lib/capybara/selenium/atoms/isDisplayed.min.js +1 -0
  99. data/lib/capybara/selenium/atoms/src/getAttribute.js +161 -0
  100. data/lib/capybara/selenium/atoms/src/isDisplayed.js +454 -0
  101. data/lib/capybara/selenium/driver.rb +298 -267
  102. data/lib/capybara/selenium/driver_specializations/chrome_driver.rb +117 -0
  103. data/lib/capybara/selenium/driver_specializations/edge_driver.rb +128 -0
  104. data/lib/capybara/selenium/driver_specializations/firefox_driver.rb +84 -0
  105. data/lib/capybara/selenium/driver_specializations/internet_explorer_driver.rb +26 -0
  106. data/lib/capybara/selenium/driver_specializations/safari_driver.rb +24 -0
  107. data/lib/capybara/selenium/extensions/file_input_click_emulation.rb +34 -0
  108. data/lib/capybara/selenium/extensions/find.rb +110 -0
  109. data/lib/capybara/selenium/extensions/html5_drag.rb +229 -0
  110. data/lib/capybara/selenium/extensions/modifier_keys_stack.rb +28 -0
  111. data/lib/capybara/selenium/extensions/scroll.rb +76 -0
  112. data/lib/capybara/selenium/node.rb +517 -145
  113. data/lib/capybara/selenium/nodes/chrome_node.rb +125 -0
  114. data/lib/capybara/selenium/nodes/edge_node.rb +110 -0
  115. data/lib/capybara/selenium/nodes/firefox_node.rb +136 -0
  116. data/lib/capybara/selenium/nodes/ie_node.rb +22 -0
  117. data/lib/capybara/selenium/nodes/safari_node.rb +118 -0
  118. data/lib/capybara/selenium/patches/atoms.rb +18 -0
  119. data/lib/capybara/selenium/patches/is_displayed.rb +16 -0
  120. data/lib/capybara/selenium/patches/logs.rb +45 -0
  121. data/lib/capybara/selenium/patches/pause_duration_fix.rb +9 -0
  122. data/lib/capybara/selenium/patches/persistent_client.rb +20 -0
  123. data/lib/capybara/server/animation_disabler.rb +80 -0
  124. data/lib/capybara/server/checker.rb +44 -0
  125. data/lib/capybara/server/middleware.rb +71 -0
  126. data/lib/capybara/server.rb +59 -67
  127. data/lib/capybara/session/config.rb +81 -67
  128. data/lib/capybara/session/matchers.rb +28 -20
  129. data/lib/capybara/session.rb +337 -365
  130. data/lib/capybara/spec/public/jquery.js +5 -5
  131. data/lib/capybara/spec/public/offset.js +6 -0
  132. data/lib/capybara/spec/public/test.js +151 -12
  133. data/lib/capybara/spec/session/accept_alert_spec.rb +12 -11
  134. data/lib/capybara/spec/session/accept_confirm_spec.rb +6 -5
  135. data/lib/capybara/spec/session/accept_prompt_spec.rb +10 -10
  136. data/lib/capybara/spec/session/active_element_spec.rb +31 -0
  137. data/lib/capybara/spec/session/all_spec.rb +161 -57
  138. data/lib/capybara/spec/session/ancestor_spec.rb +27 -24
  139. data/lib/capybara/spec/session/assert_all_of_selectors_spec.rb +68 -38
  140. data/lib/capybara/spec/session/assert_current_path_spec.rb +75 -0
  141. data/lib/capybara/spec/session/assert_selector_spec.rb +143 -0
  142. data/lib/capybara/spec/session/assert_style_spec.rb +26 -0
  143. data/lib/capybara/spec/session/assert_text_spec.rb +258 -0
  144. data/lib/capybara/spec/session/{assert_title.rb → assert_title_spec.rb} +22 -12
  145. data/lib/capybara/spec/session/attach_file_spec.rb +144 -69
  146. data/lib/capybara/spec/session/body_spec.rb +12 -13
  147. data/lib/capybara/spec/session/check_spec.rb +117 -55
  148. data/lib/capybara/spec/session/choose_spec.rb +64 -31
  149. data/lib/capybara/spec/session/click_button_spec.rb +231 -173
  150. data/lib/capybara/spec/session/click_link_or_button_spec.rb +55 -35
  151. data/lib/capybara/spec/session/click_link_spec.rb +93 -58
  152. data/lib/capybara/spec/session/current_scope_spec.rb +12 -11
  153. data/lib/capybara/spec/session/current_url_spec.rb +57 -39
  154. data/lib/capybara/spec/session/dismiss_confirm_spec.rb +4 -4
  155. data/lib/capybara/spec/session/dismiss_prompt_spec.rb +3 -2
  156. data/lib/capybara/spec/session/element/{assert_match_selector.rb → assert_match_selector_spec.rb} +11 -9
  157. data/lib/capybara/spec/session/element/match_css_spec.rb +18 -10
  158. data/lib/capybara/spec/session/element/match_xpath_spec.rb +9 -7
  159. data/lib/capybara/spec/session/element/matches_selector_spec.rb +71 -57
  160. data/lib/capybara/spec/session/evaluate_async_script_spec.rb +8 -7
  161. data/lib/capybara/spec/session/evaluate_script_spec.rb +29 -8
  162. data/lib/capybara/spec/session/execute_script_spec.rb +10 -8
  163. data/lib/capybara/spec/session/fill_in_spec.rb +134 -43
  164. data/lib/capybara/spec/session/find_button_spec.rb +25 -24
  165. data/lib/capybara/spec/session/find_by_id_spec.rb +10 -9
  166. data/lib/capybara/spec/session/find_field_spec.rb +37 -41
  167. data/lib/capybara/spec/session/find_link_spec.rb +46 -17
  168. data/lib/capybara/spec/session/find_spec.rb +260 -145
  169. data/lib/capybara/spec/session/first_spec.rb +80 -52
  170. data/lib/capybara/spec/session/frame/frame_title_spec.rb +23 -0
  171. data/lib/capybara/spec/session/frame/frame_url_spec.rb +23 -0
  172. data/lib/capybara/spec/session/frame/switch_to_frame_spec.rb +33 -20
  173. data/lib/capybara/spec/session/frame/within_frame_spec.rb +52 -32
  174. data/lib/capybara/spec/session/go_back_spec.rb +2 -1
  175. data/lib/capybara/spec/session/go_forward_spec.rb +2 -1
  176. data/lib/capybara/spec/session/has_all_selectors_spec.rb +31 -31
  177. data/lib/capybara/spec/session/has_ancestor_spec.rb +46 -0
  178. data/lib/capybara/spec/session/has_any_selectors_spec.rb +29 -0
  179. data/lib/capybara/spec/session/has_button_spec.rb +100 -13
  180. data/lib/capybara/spec/session/has_css_spec.rb +272 -137
  181. data/lib/capybara/spec/session/has_current_path_spec.rb +60 -61
  182. data/lib/capybara/spec/session/has_element_spec.rb +47 -0
  183. data/lib/capybara/spec/session/has_field_spec.rb +139 -59
  184. data/lib/capybara/spec/session/has_link_spec.rb +47 -6
  185. data/lib/capybara/spec/session/has_none_selectors_spec.rb +42 -40
  186. data/lib/capybara/spec/session/has_select_spec.rb +107 -72
  187. data/lib/capybara/spec/session/has_selector_spec.rb +120 -71
  188. data/lib/capybara/spec/session/has_sibling_spec.rb +50 -0
  189. data/lib/capybara/spec/session/has_table_spec.rb +183 -5
  190. data/lib/capybara/spec/session/has_text_spec.rb +106 -62
  191. data/lib/capybara/spec/session/has_title_spec.rb +20 -14
  192. data/lib/capybara/spec/session/has_xpath_spec.rb +57 -38
  193. data/lib/capybara/spec/session/{headers.rb → headers_spec.rb} +3 -2
  194. data/lib/capybara/spec/session/html_spec.rb +14 -6
  195. data/lib/capybara/spec/session/matches_style_spec.rb +37 -0
  196. data/lib/capybara/spec/session/node_spec.rb +1024 -153
  197. data/lib/capybara/spec/session/node_wrapper_spec.rb +39 -0
  198. data/lib/capybara/spec/session/refresh_spec.rb +12 -6
  199. data/lib/capybara/spec/session/reset_session_spec.rb +82 -35
  200. data/lib/capybara/spec/session/{response_code.rb → response_code_spec.rb} +2 -1
  201. data/lib/capybara/spec/session/save_and_open_page_spec.rb +3 -2
  202. data/lib/capybara/spec/session/save_and_open_screenshot_spec.rb +8 -12
  203. data/lib/capybara/spec/session/save_page_spec.rb +42 -55
  204. data/lib/capybara/spec/session/save_screenshot_spec.rb +16 -14
  205. data/lib/capybara/spec/session/screenshot_spec.rb +2 -2
  206. data/lib/capybara/spec/session/scroll_spec.rb +119 -0
  207. data/lib/capybara/spec/session/select_spec.rb +107 -81
  208. data/lib/capybara/spec/session/selectors_spec.rb +52 -19
  209. data/lib/capybara/spec/session/sibling_spec.rb +10 -10
  210. data/lib/capybara/spec/session/text_spec.rb +37 -21
  211. data/lib/capybara/spec/session/title_spec.rb +17 -5
  212. data/lib/capybara/spec/session/uncheck_spec.rb +43 -23
  213. data/lib/capybara/spec/session/unselect_spec.rb +39 -38
  214. data/lib/capybara/spec/session/visit_spec.rb +85 -53
  215. data/lib/capybara/spec/session/window/become_closed_spec.rb +24 -20
  216. data/lib/capybara/spec/session/window/current_window_spec.rb +5 -3
  217. data/lib/capybara/spec/session/window/open_new_window_spec.rb +5 -3
  218. data/lib/capybara/spec/session/window/switch_to_window_spec.rb +27 -22
  219. data/lib/capybara/spec/session/window/window_opened_by_spec.rb +12 -6
  220. data/lib/capybara/spec/session/window/window_spec.rb +97 -63
  221. data/lib/capybara/spec/session/window/windows_spec.rb +12 -10
  222. data/lib/capybara/spec/session/window/within_window_spec.rb +31 -86
  223. data/lib/capybara/spec/session/within_spec.rb +83 -44
  224. data/lib/capybara/spec/spec_helper.rb +54 -44
  225. data/lib/capybara/spec/test_app.rb +158 -43
  226. data/lib/capybara/spec/views/animated.erb +49 -0
  227. data/lib/capybara/spec/views/form.erb +163 -42
  228. data/lib/capybara/spec/views/frame_child.erb +4 -3
  229. data/lib/capybara/spec/views/frame_one.erb +2 -1
  230. data/lib/capybara/spec/views/frame_parent.erb +1 -1
  231. data/lib/capybara/spec/views/frame_two.erb +1 -1
  232. data/lib/capybara/spec/views/initial_alert.erb +2 -1
  233. data/lib/capybara/spec/views/layout.erb +10 -0
  234. data/lib/capybara/spec/views/obscured.erb +47 -0
  235. data/lib/capybara/spec/views/offset.erb +33 -0
  236. data/lib/capybara/spec/views/path.erb +2 -2
  237. data/lib/capybara/spec/views/popup_one.erb +1 -1
  238. data/lib/capybara/spec/views/popup_two.erb +1 -1
  239. data/lib/capybara/spec/views/react.erb +45 -0
  240. data/lib/capybara/spec/views/scroll.erb +21 -0
  241. data/lib/capybara/spec/views/spatial.erb +31 -0
  242. data/lib/capybara/spec/views/tables.erb +68 -1
  243. data/lib/capybara/spec/views/with_animation.erb +81 -0
  244. data/lib/capybara/spec/views/with_base_tag.erb +2 -2
  245. data/lib/capybara/spec/views/with_dragula.erb +24 -0
  246. data/lib/capybara/spec/views/with_fixed_header_footer.erb +2 -1
  247. data/lib/capybara/spec/views/with_hover.erb +3 -2
  248. data/lib/capybara/spec/views/with_hover1.erb +10 -0
  249. data/lib/capybara/spec/views/with_html.erb +69 -12
  250. data/lib/capybara/spec/views/with_html5_svg.erb +20 -0
  251. data/lib/capybara/spec/views/with_jquery_animation.erb +24 -0
  252. data/lib/capybara/spec/views/with_js.erb +30 -5
  253. data/lib/capybara/spec/views/with_jstree.erb +26 -0
  254. data/lib/capybara/spec/views/with_namespace.erb +21 -0
  255. data/lib/capybara/spec/views/with_scope.erb +2 -2
  256. data/lib/capybara/spec/views/with_scope_other.erb +6 -0
  257. data/lib/capybara/spec/views/with_shadow.erb +31 -0
  258. data/lib/capybara/spec/views/with_slow_unload.erb +2 -1
  259. data/lib/capybara/spec/views/with_sortable_js.erb +21 -0
  260. data/lib/capybara/spec/views/with_unload_alert.erb +1 -0
  261. data/lib/capybara/spec/views/with_windows.erb +1 -1
  262. data/lib/capybara/spec/views/within_frames.erb +5 -2
  263. data/lib/capybara/version.rb +2 -1
  264. data/lib/capybara/window.rb +36 -34
  265. data/lib/capybara.rb +134 -107
  266. data/spec/basic_node_spec.rb +60 -34
  267. data/spec/capybara_spec.rb +63 -88
  268. data/spec/counter_spec.rb +35 -0
  269. data/spec/css_builder_spec.rb +101 -0
  270. data/spec/css_splitter_spec.rb +38 -0
  271. data/spec/dsl_spec.rb +85 -64
  272. data/spec/filter_set_spec.rb +27 -9
  273. data/spec/fixtures/certificate.pem +25 -0
  274. data/spec/fixtures/key.pem +27 -0
  275. data/spec/fixtures/selenium_driver_rspec_failure.rb +6 -5
  276. data/spec/fixtures/selenium_driver_rspec_success.rb +6 -5
  277. data/spec/minitest_spec.rb +52 -7
  278. data/spec/minitest_spec_spec.rb +94 -63
  279. data/spec/per_session_config_spec.rb +14 -13
  280. data/spec/rack_test_spec.rb +194 -125
  281. data/spec/regexp_dissassembler_spec.rb +250 -0
  282. data/spec/result_spec.rb +111 -50
  283. data/spec/rspec/features_spec.rb +37 -31
  284. data/spec/rspec/scenarios_spec.rb +10 -8
  285. data/spec/rspec/shared_spec_matchers.rb +473 -422
  286. data/spec/rspec/views_spec.rb +5 -3
  287. data/spec/rspec_matchers_spec.rb +52 -11
  288. data/spec/rspec_spec.rb +109 -89
  289. data/spec/sauce_spec_chrome.rb +43 -0
  290. data/spec/selector_spec.rb +397 -68
  291. data/spec/selenium_spec_chrome.rb +187 -40
  292. data/spec/selenium_spec_chrome_remote.rb +96 -0
  293. data/spec/selenium_spec_edge.rb +60 -0
  294. data/spec/selenium_spec_firefox.rb +201 -41
  295. data/spec/selenium_spec_firefox_remote.rb +94 -0
  296. data/spec/selenium_spec_ie.rb +149 -0
  297. data/spec/selenium_spec_safari.rb +162 -0
  298. data/spec/server_spec.rb +213 -102
  299. data/spec/session_spec.rb +53 -16
  300. data/spec/shared_selenium_node.rb +79 -0
  301. data/spec/shared_selenium_session.rb +473 -122
  302. data/spec/spec_helper.rb +126 -7
  303. data/spec/whitespace_normalizer_spec.rb +54 -0
  304. data/spec/xpath_builder_spec.rb +93 -0
  305. metadata +355 -73
  306. data/.yard/templates_custom/default/class/html/selectors.erb +0 -38
  307. data/.yard/templates_custom/default/class/html/setup.rb +0 -17
  308. data/.yard/yard_extensions.rb +0 -78
  309. data/lib/capybara/query.rb +0 -7
  310. data/lib/capybara/rspec/compound.rb +0 -95
  311. data/lib/capybara/spec/session/assert_current_path.rb +0 -72
  312. data/lib/capybara/spec/session/assert_selector.rb +0 -148
  313. data/lib/capybara/spec/session/assert_text.rb +0 -234
  314. data/lib/capybara/spec/session/source_spec.rb +0 -0
  315. data/lib/capybara/spec/views/with_title.erb +0 -5
  316. data/spec/selenium_spec_marionette.rb +0 -127
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Capybara
3
4
  module Node
4
5
  module Matchers
5
-
6
6
  ##
7
7
  #
8
8
  # Checks if a given selector is on the page or a descendant of the current node.
@@ -19,41 +19,58 @@ module Capybara
19
19
  # This will check if the expression occurs exactly 4 times.
20
20
  #
21
21
  # It also accepts all options that {Capybara::Node::Finders#all} accepts,
22
- # such as :text and :visible.
22
+ # such as `:text` and `:visible`.
23
23
  #
24
24
  # page.has_selector?('li', text: 'Horse', visible: true)
25
25
  #
26
- # has_selector? can also accept XPath expressions generated by the
26
+ # {#has_selector?} can also accept XPath expressions generated by the
27
27
  # XPath gem:
28
28
  #
29
29
  # page.has_selector?(:xpath, XPath.descendant(:p))
30
30
  #
31
31
  # @param (see Capybara::Node::Finders#all)
32
- # @param args
33
- # @option args [Integer] :count (nil) Number of times the text should occur
34
- # @option args [Integer] :minimum (nil) Minimum number of times the text should occur
35
- # @option args [Integer] :maximum (nil) Maximum number of times the text should occur
36
- # @option args [Range] :between (nil) Range of times that should contain number of times text occurs
32
+ # @option options [Integer] :count (nil) Number of matching elements that should exist
33
+ # @option options [Integer] :minimum (nil) Minimum number of matching elements that should exist
34
+ # @option options [Integer] :maximum (nil) Maximum number of matching elements that should exist
35
+ # @option options [Range] :between (nil) Range of number of matching elements that should exist
37
36
  # @return [Boolean] If the expression exists
38
37
  #
39
- def has_selector?(*args, &optional_filter_block)
40
- assert_selector(*args, &optional_filter_block)
41
- rescue Capybara::ExpectationNotMet
42
- return false
38
+ def has_selector?(*args, **options, &optional_filter_block)
39
+ make_predicate(options) { assert_selector(*args, options, &optional_filter_block) }
43
40
  end
44
41
 
45
42
  ##
46
43
  #
47
44
  # Checks if a given selector is not on the page or a descendant of the current node.
48
- # Usage is identical to Capybara::Node::Matchers#has_selector?
45
+ # Usage is identical to {#has_selector?}.
49
46
  #
50
- # @param (see Capybara::Node::Finders#has_selector?)
47
+ # @param (see #has_selector?)
51
48
  # @return [Boolean]
52
49
  #
53
- def has_no_selector?(*args, &optional_filter_block)
54
- assert_no_selector(*args, &optional_filter_block)
55
- rescue Capybara::ExpectationNotMet
56
- return false
50
+ def has_no_selector?(*args, **options, &optional_filter_block)
51
+ make_predicate(options) { assert_no_selector(*args, options, &optional_filter_block) }
52
+ end
53
+
54
+ ##
55
+ #
56
+ # Checks if a an element has the specified CSS styles.
57
+ #
58
+ # element.matches_style?( 'color' => 'rgb(0,0,255)', 'font-size' => /px/ )
59
+ #
60
+ # @param styles [Hash]
61
+ # @return [Boolean] If the styles match
62
+ #
63
+ def matches_style?(styles = nil, **options)
64
+ styles, options = options, {} if styles.nil?
65
+ make_predicate(options) { assert_matches_style(styles, **options) }
66
+ end
67
+
68
+ ##
69
+ # @deprecated Use {#matches_style?} instead.
70
+ #
71
+ def has_style?(styles = nil, **options)
72
+ Capybara::Helpers.warn "DEPRECATED: has_style? is deprecated, please use matches_style? : #{Capybara::Helpers.filter_backtrace(caller)}"
73
+ matches_style?(styles, **options)
57
74
  end
58
75
 
59
76
  ##
@@ -72,15 +89,15 @@ module Capybara
72
89
  # This will check if the expression occurs exactly 4 times. See
73
90
  # {Capybara::Node::Finders#all} for other available result size options.
74
91
  #
75
- # If a :count of 0 is specified, it will behave like {#assert_no_selector};
92
+ # If a `:count` of 0 is specified, it will behave like {#assert_no_selector};
76
93
  # however, use of that method is preferred over this one.
77
94
  #
78
95
  # It also accepts all options that {Capybara::Node::Finders#all} accepts,
79
- # such as :text and :visible.
96
+ # such as `:text` and `:visible`.
80
97
  #
81
98
  # page.assert_selector('li', text: 'Horse', visible: true)
82
99
  #
83
- # `assert_selector` can also accept XPath expressions generated by the
100
+ # {#assert_selector} can also accept XPath expressions generated by the
84
101
  # XPath gem:
85
102
  #
86
103
  # page.assert_selector(:xpath, XPath.descendant(:p))
@@ -91,88 +108,135 @@ module Capybara
91
108
  #
92
109
  def assert_selector(*args, &optional_filter_block)
93
110
  _verify_selector_result(args, optional_filter_block) do |result, query|
94
- unless result.matches_count? && ((!result.empty?) || query.expects_none?)
111
+ unless result.matches_count? && (result.any? || query.expects_none?)
95
112
  raise Capybara::ExpectationNotMet, result.failure_message
96
113
  end
97
114
  end
98
115
  end
99
116
 
117
+ ##
118
+ #
119
+ # Asserts that an element has the specified CSS styles.
120
+ #
121
+ # element.assert_matches_style( 'color' => 'rgb(0,0,255)', 'font-size' => /px/ )
122
+ #
123
+ # @param styles [Hash]
124
+ # @raise [Capybara::ExpectationNotMet] If the element doesn't have the specified styles
125
+ #
126
+ def assert_matches_style(styles = nil, **options)
127
+ styles, options = options, {} if styles.nil?
128
+ query_args, query_opts = _set_query_session_options(styles, options)
129
+ query = Capybara::Queries::StyleQuery.new(*query_args, **query_opts)
130
+ synchronize(query.wait) do
131
+ raise Capybara::ExpectationNotMet, query.failure_message unless query.resolves_for?(self)
132
+ end
133
+ true
134
+ end
135
+
136
+ ##
137
+ # @deprecated Use {#assert_matches_style} instead.
138
+ #
139
+ def assert_style(styles = nil, **options)
140
+ warn 'assert_style is deprecated, please use assert_matches_style instead'
141
+ assert_matches_style(styles, **options)
142
+ end
143
+
100
144
  # Asserts that all of the provided selectors are present on the given page
101
145
  # or descendants of the current node. If options are provided, the assertion
102
- # will check that each locator is present with those options as well (other than :wait).
146
+ # will check that each locator is present with those options as well (other than `:wait`).
103
147
  #
104
- # page.assert_all_of_selectors(:custom, 'Tom', 'Joe', visible: all)
105
- # page.assert_all_of_selectors(:css, '#my_div', 'a.not_clicked')
148
+ # page.assert_all_of_selectors(:custom, 'Tom', 'Joe', visible: all)
149
+ # page.assert_all_of_selectors(:css, '#my_div', 'a.not_clicked')
106
150
  #
107
151
  # It accepts all options that {Capybara::Node::Finders#all} accepts,
108
- # such as :text and :visible.
152
+ # such as `:text` and `:visible`.
109
153
  #
110
- # The :wait option applies to all of the selectors as a group, so all of the locators must be present
111
- # within :wait (Defaults to Capybara.default_max_wait_time) seconds.
154
+ # The `:wait` option applies to all of the selectors as a group, so all of the locators must be present
155
+ # within `:wait` (defaults to {Capybara.configure default_max_wait_time}) seconds.
112
156
  #
113
- # @overload assert_all_of_selectors([kind = Capybara.default_selector], *locators, options = {})
157
+ # @overload assert_all_of_selectors([kind = Capybara.default_selector], *locators, **options)
114
158
  #
115
- def assert_all_of_selectors(*args, &optional_filter_block)
116
- options = if args.last.is_a?(Hash) then args.pop.dup else {} end
117
- selector = if args.first.is_a?(Symbol) then args.shift else session_options.default_selector end
118
- wait = options.fetch(:wait, session_options.default_max_wait_time)
119
- synchronize(wait) do
120
- args.each do |locator|
121
- assert_selector(selector, locator, options, &optional_filter_block)
122
- end
159
+ def assert_all_of_selectors(*args, **options, &optional_filter_block)
160
+ _verify_multiple(*args, **options) do |selector, locator, opts|
161
+ assert_selector(selector, locator, opts, &optional_filter_block)
123
162
  end
124
163
  end
125
164
 
126
165
  # Asserts that none of the provided selectors are present on the given page
127
166
  # or descendants of the current node. If options are provided, the assertion
128
- # will check that each locator is present with those options as well (other than :wait).
167
+ # will check that each locator is not present with those options as well (other than `:wait`).
168
+ #
169
+ # page.assert_none_of_selectors(:custom, 'Tom', 'Joe', visible: all)
170
+ # page.assert_none_of_selectors(:css, '#my_div', 'a.not_clicked')
171
+ #
172
+ # It accepts all options that {Capybara::Node::Finders#all} accepts,
173
+ # such as `:text` and `:visible`.
174
+ #
175
+ # The `:wait` option applies to all of the selectors as a group, so none of the locators must be present
176
+ # within `:wait` (defaults to {Capybara.configure default_max_wait_time}) seconds.
177
+ #
178
+ # @overload assert_none_of_selectors([kind = Capybara.default_selector], *locators, **options)
179
+ #
180
+ def assert_none_of_selectors(*args, **options, &optional_filter_block)
181
+ _verify_multiple(*args, **options) do |selector, locator, opts|
182
+ assert_no_selector(selector, locator, opts, &optional_filter_block)
183
+ end
184
+ end
185
+
186
+ # Asserts that any of the provided selectors are present on the given page
187
+ # or descendants of the current node. If options are provided, the assertion
188
+ # will check that each locator is present with those options as well (other than `:wait`).
129
189
  #
130
- # page.assert_none_of_selectors(:custom, 'Tom', 'Joe', visible: all)
131
- # page.assert_none_of_selectors(:css, '#my_div', 'a.not_clicked')
190
+ # page.assert_any_of_selectors(:custom, 'Tom', 'Joe', visible: all)
191
+ # page.assert_any_of_selectors(:css, '#my_div', 'a.not_clicked')
132
192
  #
133
193
  # It accepts all options that {Capybara::Node::Finders#all} accepts,
134
- # such as :text and :visible.
194
+ # such as `:text` and `:visible`.
135
195
  #
136
- # The :wait option applies to all of the selectors as a group, so none of the locators must be present
137
- # within :wait (Defaults to Capybara.default_max_wait_time) seconds.
196
+ # The `:wait` option applies to all of the selectors as a group, so any of the locators must be present
197
+ # within `:wait` (defaults to {Capybara.configure default_max_wait_time}) seconds.
138
198
  #
139
- # @overload assert_none_of_selectors([kind = Capybara.default_selector], *locators, options = {})
199
+ # @overload assert_any_of_selectors([kind = Capybara.default_selector], *locators, **options)
140
200
  #
141
- def assert_none_of_selectors(*args, &optional_filter_block)
142
- options = if args.last.is_a?(Hash) then args.pop.dup else {} end
143
- selector = if args.first.is_a?(Symbol) then args.shift else session_options.default_selector end
144
- wait = options.fetch(:wait, session_options.default_max_wait_time)
201
+ def assert_any_of_selectors(*args, wait: nil, **options, &optional_filter_block)
202
+ wait = session_options.default_max_wait_time if wait.nil?
203
+ selector = extract_selector(args)
145
204
  synchronize(wait) do
146
- args.each do |locator|
147
- assert_no_selector(selector, locator, options, &optional_filter_block)
205
+ res = args.map do |locator|
206
+ assert_selector(selector, locator, options, &optional_filter_block)
207
+ break nil
208
+ rescue Capybara::ExpectationNotMet => e
209
+ e.message
148
210
  end
211
+ raise Capybara::ExpectationNotMet, res.join(' or ') if res
212
+
213
+ true
149
214
  end
150
215
  end
151
216
 
152
217
  ##
153
218
  #
154
219
  # Asserts that a given selector is not on the page or a descendant of the current node.
155
- # Usage is identical to Capybara::Node::Matchers#assert_selector
220
+ # Usage is identical to {#assert_selector}.
156
221
  #
157
- # Query options such as :count, :minimum, :maximum, and :between are
222
+ # Query options such as `:count`, `:minimum`, `:maximum`, and `:between` are
158
223
  # considered to be an integral part of the selector. This will return
159
- # true, for example, if a page contains 4 anchors but the query expects 5:
224
+ # `true`, for example, if a page contains 4 anchors but the query expects 5:
160
225
  #
161
226
  # page.assert_no_selector('a', minimum: 1) # Found, raises Capybara::ExpectationNotMet
162
227
  # page.assert_no_selector('a', count: 4) # Found, raises Capybara::ExpectationNotMet
163
228
  # page.assert_no_selector('a', count: 5) # Not Found, returns true
164
229
  #
165
- # @param (see Capybara::Node::Finders#assert_selector)
230
+ # @param (see #assert_selector)
166
231
  # @raise [Capybara::ExpectationNotMet] If the selector exists
167
232
  #
168
233
  def assert_no_selector(*args, &optional_filter_block)
169
234
  _verify_selector_result(args, optional_filter_block) do |result, query|
170
- if result.matches_count? && ((!result.empty?) || query.expects_none?)
235
+ if result.matches_count? && (!result.empty? || query.expects_none?)
171
236
  raise Capybara::ExpectationNotMet, result.negative_failure_message
172
237
  end
173
238
  end
174
239
  end
175
- alias_method :refute_selector, :assert_no_selector
176
240
 
177
241
  ##
178
242
  #
@@ -188,11 +252,11 @@ module Capybara
188
252
  # This will check if the expression occurs exactly 4 times.
189
253
  #
190
254
  # It also accepts all options that {Capybara::Node::Finders#all} accepts,
191
- # such as :text and :visible.
255
+ # such as `:text` and `:visible`.
192
256
  #
193
257
  # page.has_xpath?('.//li', text: 'Horse', visible: true)
194
258
  #
195
- # has_xpath? can also accept XPath expressions generate by the
259
+ # {#has_xpath?} can also accept XPath expressions generated by the
196
260
  # XPath gem:
197
261
  #
198
262
  # xpath = XPath.generate { |x| x.descendant(:p) }
@@ -203,20 +267,20 @@ module Capybara
203
267
  # @option options [Integer] :count (nil) Number of times the expression should occur
204
268
  # @return [Boolean] If the expression exists
205
269
  #
206
- def has_xpath?(path, options={}, &optional_filter_block)
207
- has_selector?(:xpath, path, options, &optional_filter_block)
270
+ def has_xpath?(path, **options, &optional_filter_block)
271
+ has_selector?(:xpath, path, **options, &optional_filter_block)
208
272
  end
209
273
 
210
274
  ##
211
275
  #
212
276
  # Checks if a given XPath expression is not on the page or a descendant of the current node.
213
- # Usage is identical to Capybara::Node::Matchers#has_xpath?
277
+ # Usage is identical to {#has_xpath?}.
214
278
  #
215
- # @param (see Capybara::Node::Finders#has_xpath?)
279
+ # @param (see #has_xpath?)
216
280
  # @return [Boolean]
217
281
  #
218
- def has_no_xpath?(path, options={}, &optional_filter_block)
219
- has_no_selector?(:xpath, path, options, &optional_filter_block)
282
+ def has_no_xpath?(path, **options, &optional_filter_block)
283
+ has_no_selector?(:xpath, path, **options, &optional_filter_block)
220
284
  end
221
285
 
222
286
  ##
@@ -233,7 +297,7 @@ module Capybara
233
297
  # This will check if the selector occurs exactly 4 times.
234
298
  #
235
299
  # It also accepts all options that {Capybara::Node::Finders#all} accepts,
236
- # such as :text and :visible.
300
+ # such as `:text` and `:visible`.
237
301
  #
238
302
  # page.has_css?('li', text: 'Horse', visible: true)
239
303
  #
@@ -242,20 +306,45 @@ module Capybara
242
306
  # @option options [Integer] :count (nil) Number of times the selector should occur
243
307
  # @return [Boolean] If the selector exists
244
308
  #
245
- def has_css?(path, options={}, &optional_filter_block)
246
- has_selector?(:css, path, options, &optional_filter_block)
309
+ def has_css?(path, **options, &optional_filter_block)
310
+ has_selector?(:css, path, **options, &optional_filter_block)
247
311
  end
248
312
 
249
313
  ##
250
314
  #
251
315
  # Checks if a given CSS selector is not on the page or a descendant of the current node.
252
- # Usage is identical to Capybara::Node::Matchers#has_css?
316
+ # Usage is identical to {#has_css?}.
253
317
  #
254
- # @param (see Capybara::Node::Finders#has_css?)
318
+ # @param (see #has_css?)
255
319
  # @return [Boolean]
256
320
  #
257
- def has_no_css?(path, options={}, &optional_filter_block)
258
- has_no_selector?(:css, path, options, &optional_filter_block)
321
+ def has_no_css?(path, **options, &optional_filter_block)
322
+ has_no_selector?(:css, path, **options, &optional_filter_block)
323
+ end
324
+
325
+ ##
326
+ #
327
+ # Checks if the page or current node has a element with the given
328
+ # local name.
329
+ #
330
+ # @param [String] locator The local name of a element to check for
331
+ # @option options [String, Regexp] The attributes values of matching elements
332
+ # @return [Boolean] Whether it exists
333
+ #
334
+ def has_element?(locator = nil, **options, &optional_filter_block)
335
+ has_selector?(:element, locator, **options, &optional_filter_block)
336
+ end
337
+
338
+ ##
339
+ #
340
+ # Checks if the page or current node has no element with the given
341
+ # local name.
342
+ #
343
+ # @param (see #has_element?)
344
+ # @return [Boolean] Whether it doesn't exist
345
+ #
346
+ def has_no_element?(locator = nil, **options, &optional_filter_block)
347
+ has_no_selector?(:element, locator, **options, &optional_filter_block)
259
348
  end
260
349
 
261
350
  ##
@@ -264,13 +353,11 @@ module Capybara
264
353
  # text or id.
265
354
  #
266
355
  # @param [String] locator The text or id of a link to check for
267
- # @param options
268
356
  # @option options [String, Regexp] :href The value the href attribute must be
269
357
  # @return [Boolean] Whether it exists
270
358
  #
271
- def has_link?(locator=nil, options={}, &optional_filter_block)
272
- locator, options = nil, locator if locator.is_a? Hash
273
- has_selector?(:link, locator, options, &optional_filter_block)
359
+ def has_link?(locator = nil, **options, &optional_filter_block)
360
+ has_selector?(:link, locator, **options, &optional_filter_block)
274
361
  end
275
362
 
276
363
  ##
@@ -278,12 +365,11 @@ module Capybara
278
365
  # Checks if the page or current node has no link with the given
279
366
  # text or id.
280
367
  #
281
- # @param (see Capybara::Node::Finders#has_link?)
368
+ # @param (see #has_link?)
282
369
  # @return [Boolean] Whether it doesn't exist
283
370
  #
284
- def has_no_link?(locator=nil, options={}, &optional_filter_block)
285
- locator, options = nil, locator if locator.is_a? Hash
286
- has_no_selector?(:link, locator, options, &optional_filter_block)
371
+ def has_no_link?(locator = nil, **options, &optional_filter_block)
372
+ has_no_selector?(:link, locator, **options, &optional_filter_block)
287
373
  end
288
374
 
289
375
  ##
@@ -294,9 +380,8 @@ module Capybara
294
380
  # @param [String] locator The text, value or id of a button to check for
295
381
  # @return [Boolean] Whether it exists
296
382
  #
297
- def has_button?(locator=nil, options={}, &optional_filter_block)
298
- locator, options = nil, locator if locator.is_a? Hash
299
- has_selector?(:button, locator, options, &optional_filter_block)
383
+ def has_button?(locator = nil, **options, &optional_filter_block)
384
+ has_selector?(:button, locator, **options, &optional_filter_block)
300
385
  end
301
386
 
302
387
  ##
@@ -307,9 +392,8 @@ module Capybara
307
392
  # @param [String] locator The text, value or id of a button to check for
308
393
  # @return [Boolean] Whether it doesn't exist
309
394
  #
310
- def has_no_button?(locator=nil, options={}, &optional_filter_block)
311
- locator, options = nil, locator if locator.is_a? Hash
312
- has_no_selector?(:button, locator, options, &optional_filter_block)
395
+ def has_no_button?(locator = nil, **options, &optional_filter_block)
396
+ has_no_selector?(:button, locator, **options, &optional_filter_block)
313
397
  end
314
398
 
315
399
  ##
@@ -318,7 +402,7 @@ module Capybara
318
402
  # label, name or id.
319
403
  #
320
404
  # For text fields and other textual fields, such as textareas and
321
- # HTML5 email/url/etc. fields, it's possible to specify a :with
405
+ # HTML5 email/url/etc. fields, it's possible to specify a `:with`
322
406
  # option to specify the text the field should contain:
323
407
  #
324
408
  # page.has_field?('Name', with: 'Jonas')
@@ -327,87 +411,81 @@ module Capybara
327
411
  #
328
412
  # page.has_field?('Email', type: 'email')
329
413
  #
330
- # Note: 'textarea' and 'select' are valid type values, matching the associated tag names.
414
+ # NOTE: 'textarea' and 'select' are valid type values, matching the associated tag names.
331
415
  #
332
416
  # @param [String] locator The label, name or id of a field to check for
333
417
  # @option options [String, Regexp] :with The text content of the field or a Regexp to match
334
418
  # @option options [String] :type The type attribute of the field
335
419
  # @return [Boolean] Whether it exists
336
420
  #
337
- def has_field?(locator=nil, options={}, &optional_filter_block)
338
- locator, options = nil, locator if locator.is_a? Hash
339
- has_selector?(:field, locator, options, &optional_filter_block)
421
+ def has_field?(locator = nil, **options, &optional_filter_block)
422
+ has_selector?(:field, locator, **options, &optional_filter_block)
340
423
  end
341
424
 
342
425
  ##
343
426
  #
344
427
  # Checks if the page or current node has no form field with the given
345
- # label, name or id. See {Capybara::Node::Matchers#has_field?}.
428
+ # label, name or id. See {#has_field?}.
346
429
  #
347
430
  # @param [String] locator The label, name or id of a field to check for
348
431
  # @option options [String, Regexp] :with The text content of the field or a Regexp to match
349
432
  # @option options [String] :type The type attribute of the field
350
433
  # @return [Boolean] Whether it doesn't exist
351
434
  #
352
- def has_no_field?(locator=nil, options={}, &optional_filter_block)
353
- locator, options = nil, locator if locator.is_a? Hash
354
- has_no_selector?(:field, locator, options, &optional_filter_block)
435
+ def has_no_field?(locator = nil, **options, &optional_filter_block)
436
+ has_no_selector?(:field, locator, **options, &optional_filter_block)
355
437
  end
356
438
 
357
439
  ##
358
440
  #
359
441
  # Checks if the page or current node has a radio button or
360
- # checkbox with the given label, value or id, that is currently
442
+ # checkbox with the given label, value, id, or {Capybara.configure test_id} attribute that is currently
361
443
  # checked.
362
444
  #
363
445
  # @param [String] locator The label, name or id of a checked field
364
446
  # @return [Boolean] Whether it exists
365
447
  #
366
- def has_checked_field?(locator=nil, options={}, &optional_filter_block)
367
- locator, options = nil, locator if locator.is_a? Hash
368
- has_selector?(:field, locator, options.merge(checked: true), &optional_filter_block)
448
+ def has_checked_field?(locator = nil, **options, &optional_filter_block)
449
+ has_selector?(:field, locator, **options.merge(checked: true), &optional_filter_block)
369
450
  end
370
451
 
371
452
  ##
372
453
  #
373
454
  # Checks if the page or current node has no radio button or
374
- # checkbox with the given label, value or id, that is currently
455
+ # checkbox with the given label, value or id, or {Capybara.configure test_id} attribute that is currently
375
456
  # checked.
376
457
  #
377
458
  # @param [String] locator The label, name or id of a checked field
378
459
  # @return [Boolean] Whether it doesn't exist
379
460
  #
380
- def has_no_checked_field?(locator=nil, options={}, &optional_filter_block)
381
- locator, options = nil, locator if locator.is_a? Hash
382
- has_no_selector?(:field, locator, options.merge(checked: true), &optional_filter_block)
461
+ def has_no_checked_field?(locator = nil, **options, &optional_filter_block)
462
+ has_no_selector?(:field, locator, **options.merge(checked: true), &optional_filter_block)
383
463
  end
384
464
 
385
465
  ##
386
466
  #
387
467
  # Checks if the page or current node has a radio button or
388
- # checkbox with the given label, value or id, that is currently
468
+ # checkbox with the given label, value or id, or {Capybara.configure test_id} attribute that is currently
389
469
  # unchecked.
390
470
  #
391
471
  # @param [String] locator The label, name or id of an unchecked field
392
472
  # @return [Boolean] Whether it exists
393
473
  #
394
- def has_unchecked_field?(locator=nil, options={}, &optional_filter_block)
395
- locator, options = nil, locator if locator.is_a? Hash
396
- has_selector?(:field, locator, options.merge(unchecked: true), &optional_filter_block)
474
+ def has_unchecked_field?(locator = nil, **options, &optional_filter_block)
475
+ has_selector?(:field, locator, **options.merge(unchecked: true), &optional_filter_block)
397
476
  end
398
477
 
399
478
  ##
400
479
  #
401
480
  # Checks if the page or current node has no radio button or
402
- # checkbox with the given label, value or id, that is currently
481
+ # checkbox with the given label, value or id, or {Capybara.configure test_id} attribute that is currently
403
482
  # unchecked.
404
483
  #
405
484
  # @param [String] locator The label, name or id of an unchecked field
406
485
  # @return [Boolean] Whether it doesn't exist
407
486
  #
408
- def has_no_unchecked_field?(locator=nil, options={}, &optional_filter_block)
409
- locator, options = nil, locator if locator.is_a? Hash
410
- has_no_selector?(:field, locator, options.merge(unchecked: true), &optional_filter_block)
487
+ def has_no_unchecked_field?(locator = nil, **options, &optional_filter_block)
488
+ has_no_selector?(:field, locator, **options.merge(unchecked: true), &optional_filter_block)
411
489
  end
412
490
 
413
491
  ##
@@ -439,22 +517,20 @@ module Capybara
439
517
  # @option options [String, Array] :with_selected Partial set of options which should minimally be selected
440
518
  # @return [Boolean] Whether it exists
441
519
  #
442
- def has_select?(locator=nil, options={}, &optional_filter_block)
443
- locator, options = nil, locator if locator.is_a? Hash
444
- has_selector?(:select, locator, options, &optional_filter_block)
520
+ def has_select?(locator = nil, **options, &optional_filter_block)
521
+ has_selector?(:select, locator, **options, &optional_filter_block)
445
522
  end
446
523
 
447
524
  ##
448
525
  #
449
526
  # Checks if the page or current node has no select field with the
450
- # given label, name or id. See {Capybara::Node::Matchers#has_select?}.
527
+ # given label, name or id. See {#has_select?}.
451
528
  #
452
- # @param (see Capybara::Node::Matchers#has_select?)
529
+ # @param (see #has_select?)
453
530
  # @return [Boolean] Whether it doesn't exist
454
531
  #
455
- def has_no_select?(locator=nil, options={}, &optional_filter_block)
456
- locator, options = nil, locator if locator.is_a? Hash
457
- has_no_selector?(:select, locator, options, &optional_filter_block)
532
+ def has_no_select?(locator = nil, **options, &optional_filter_block)
533
+ has_no_selector?(:select, locator, **options, &optional_filter_block)
458
534
  end
459
535
 
460
536
  ##
@@ -464,37 +540,43 @@ module Capybara
464
540
  #
465
541
  # page.has_table?('People')
466
542
  #
467
- # @param [String] locator The id or caption of a table
468
- # @return [Boolean] Whether it exist
543
+ # @param [String] locator The id or caption of a table
544
+ # @option options [Array<Array<String>>] :rows
545
+ # Text which should be contained in the tables `<td>` elements organized by row (`<td>` visibility is not considered)
546
+ # @option options [Array<Array<String>>, Array<Hash<String,String>>] :with_rows
547
+ # Partial set of text which should be contained in the tables `<td>` elements organized by row (`<td>` visibility is not considered)
548
+ # @option options [Array<Array<String>>] :cols
549
+ # Text which should be contained in the tables `<td>` elements organized by column (`<td>` visibility is not considered)
550
+ # @option options [Array<Array<String>>, Array<Hash<String,String>>] :with_cols
551
+ # Partial set of text which should be contained in the tables `<td>` elements organized by column (`<td>` visibility is not considered)
552
+ # @return [Boolean] Whether it exists
469
553
  #
470
- def has_table?(locator=nil, options={}, &optional_filter_block)
471
- locator, options = nil, locator if locator.is_a? Hash
472
- has_selector?(:table, locator, options, &optional_filter_block)
554
+ def has_table?(locator = nil, **options, &optional_filter_block)
555
+ has_selector?(:table, locator, **options, &optional_filter_block)
473
556
  end
474
557
 
475
558
  ##
476
559
  #
477
560
  # Checks if the page or current node has no table with the given id
478
- # or caption. See {Capybara::Node::Matchers#has_table?}.
561
+ # or caption. See {#has_table?}.
479
562
  #
480
- # @param (see Capybara::Node::Matchers#has_table?)
563
+ # @param (see #has_table?)
481
564
  # @return [Boolean] Whether it doesn't exist
482
565
  #
483
- def has_no_table?(locator=nil, options={}, &optional_filter_block)
484
- locator, options = nil, locator if locator.is_a? Hash
485
- has_no_selector?(:table, locator, options, &optional_filter_block)
566
+ def has_no_table?(locator = nil, **options, &optional_filter_block)
567
+ has_no_selector?(:table, locator, **options, &optional_filter_block)
486
568
  end
487
569
 
488
570
  ##
489
571
  #
490
- # Asserts that the current_node matches a given selector
572
+ # Asserts that the current node matches a given selector.
491
573
  #
492
574
  # node.assert_matches_selector('p#foo')
493
575
  # node.assert_matches_selector(:xpath, '//p[@id="foo"]')
494
576
  # node.assert_matches_selector(:foo)
495
577
  #
496
578
  # It also accepts all options that {Capybara::Node::Finders#all} accepts,
497
- # such as :text and :visible.
579
+ # such as `:text` and `:visible`.
498
580
  #
499
581
  # node.assert_matches_selector('li', text: 'Horse', visible: true)
500
582
  #
@@ -503,117 +585,121 @@ module Capybara
503
585
  #
504
586
  def assert_matches_selector(*args, &optional_filter_block)
505
587
  _verify_match_result(args, optional_filter_block) do |result|
506
- raise Capybara::ExpectationNotMet, "Item does not match the provided selector" unless result.include? self
588
+ raise Capybara::ExpectationNotMet, 'Item does not match the provided selector' unless result.include? self
507
589
  end
508
590
  end
509
591
 
592
+ ##
593
+ #
594
+ # Asserts that the current node does not match a given selector.
595
+ # Usage is identical to {#assert_matches_selector}.
596
+ #
597
+ # @param (see #assert_matches_selector)
598
+ # @raise [Capybara::ExpectationNotMet] If the selector matches
599
+ #
510
600
  def assert_not_matches_selector(*args, &optional_filter_block)
511
601
  _verify_match_result(args, optional_filter_block) do |result|
512
602
  raise Capybara::ExpectationNotMet, 'Item matched the provided selector' if result.include? self
513
603
  end
514
604
  end
515
- alias_method :refute_matches_selector, :assert_not_matches_selector
516
605
 
517
606
  ##
518
607
  #
519
- # Checks if the current node matches given selector
608
+ # Checks if the current node matches given selector.
520
609
  #
521
- # @param (see Capybara::Node::Finders#has_selector?)
610
+ # @param (see #has_selector?)
522
611
  # @return [Boolean]
523
612
  #
524
- def matches_selector?(*args, &optional_filter_block)
525
- assert_matches_selector(*args, &optional_filter_block)
526
- rescue Capybara::ExpectationNotMet
527
- return false
613
+ def matches_selector?(*args, **options, &optional_filter_block)
614
+ make_predicate(options) { assert_matches_selector(*args, options, &optional_filter_block) }
528
615
  end
529
616
 
530
617
  ##
531
618
  #
532
- # Checks if the current node matches given XPath expression
619
+ # Checks if the current node matches given XPath expression.
533
620
  #
534
621
  # @param [String, XPath::Expression] xpath The XPath expression to match against the current code
535
622
  # @return [Boolean]
536
623
  #
537
- def matches_xpath?(xpath, options={}, &optional_filter_block)
538
- matches_selector?(:xpath, xpath, options, &optional_filter_block)
624
+ def matches_xpath?(xpath, **options, &optional_filter_block)
625
+ matches_selector?(:xpath, xpath, **options, &optional_filter_block)
539
626
  end
540
627
 
541
628
  ##
542
629
  #
543
- # Checks if the current node matches given CSS selector
630
+ # Checks if the current node matches given CSS selector.
544
631
  #
545
632
  # @param [String] css The CSS selector to match against the current code
546
633
  # @return [Boolean]
547
634
  #
548
- def matches_css?(css, options={}, &optional_filter_block)
549
- matches_selector?(:css, css, options, &optional_filter_block)
635
+ def matches_css?(css, **options, &optional_filter_block)
636
+ matches_selector?(:css, css, **options, &optional_filter_block)
550
637
  end
551
638
 
552
639
  ##
553
640
  #
554
- # Checks if the current node does not match given selector
555
- # Usage is identical to Capybara::Node::Matchers#has_selector?
641
+ # Checks if the current node does not match given selector.
642
+ # Usage is identical to {#has_selector?}.
556
643
  #
557
- # @param (see Capybara::Node::Finders#has_selector?)
644
+ # @param (see #has_selector?)
558
645
  # @return [Boolean]
559
646
  #
560
- def not_matches_selector?(*args, &optional_filter_block)
561
- assert_not_matches_selector(*args, &optional_filter_block)
562
- rescue Capybara::ExpectationNotMet
563
- return false
647
+ def not_matches_selector?(*args, **options, &optional_filter_block)
648
+ make_predicate(options) { assert_not_matches_selector(*args, options, &optional_filter_block) }
564
649
  end
565
650
 
566
651
  ##
567
652
  #
568
- # Checks if the current node does not match given XPath expression
653
+ # Checks if the current node does not match given XPath expression.
569
654
  #
570
655
  # @param [String, XPath::Expression] xpath The XPath expression to match against the current code
571
656
  # @return [Boolean]
572
657
  #
573
- def not_matches_xpath?(xpath, options={}, &optional_filter_block)
574
- not_matches_selector?(:xpath, xpath, options, &optional_filter_block)
658
+ def not_matches_xpath?(xpath, **options, &optional_filter_block)
659
+ not_matches_selector?(:xpath, xpath, **options, &optional_filter_block)
575
660
  end
576
661
 
577
662
  ##
578
663
  #
579
- # Checks if the current node does not match given CSS selector
664
+ # Checks if the current node does not match given CSS selector.
580
665
  #
581
666
  # @param [String] css The CSS selector to match against the current code
582
667
  # @return [Boolean]
583
668
  #
584
- def not_matches_css?(css, options={}, &optional_filter_block)
585
- not_matches_selector?(:css, css, options, &optional_filter_block)
669
+ def not_matches_css?(css, **options, &optional_filter_block)
670
+ not_matches_selector?(:css, css, **options, &optional_filter_block)
586
671
  end
587
672
 
588
-
589
673
  ##
590
674
  # Asserts that the page or current node has the given text content,
591
675
  # ignoring any HTML tags.
592
676
  #
593
677
  # @!macro text_query_params
594
- # @overload $0(type, text, options = {})
595
- # @param [:all, :visible] type Whether to check for only visible or all text. If this parameter is missing or nil then we use the value of `Capybara.ignore_hidden_elements`, which defaults to `true`, corresponding to `:visible`.
678
+ # @overload $0(type, text, **options)
679
+ # @param [:all, :visible] type Whether to check for only visible or all text. If this parameter is missing or nil then we use the value of {Capybara.configure ignore_hidden_elements}, which defaults to `true`, corresponding to `:visible`.
596
680
  # @param [String, Regexp] text The string/regexp to check for. If it's a string, text is expected to include it. If it's a regexp, text is expected to match it.
597
681
  # @option options [Integer] :count (nil) Number of times the text is expected to occur
598
682
  # @option options [Integer] :minimum (nil) Minimum number of times the text is expected to occur
599
683
  # @option options [Integer] :maximum (nil) Maximum number of times the text is expected to occur
600
684
  # @option options [Range] :between (nil) Range of times that is expected to contain number of times text occurs
601
- # @option options [Numeric] :wait (Capybara.default_max_wait_time) Maximum time that Capybara will wait for text to eq/match given string/regexp argument
602
- # @option options [Boolean] :exact (Capybara.exact_text) Whether text must be an exact match or just substring
603
- # @overload $0(text, options = {})
685
+ # @option options [Numeric] :wait Maximum time that Capybara will wait for text to eq/match given string/regexp argument. Defaults to {Capybara.configure default_max_wait_time}.
686
+ # @option options [Boolean] :exact Whether text must be an exact match or just substring. Defaults to {Capybara.configure exact_text}.
687
+ # @option options [Boolean] :normalize_ws (false) When `true` replace all whitespace with standard spaces and collapse consecutive whitespace to a single space
688
+ # @overload $0(text, **options)
604
689
  # @param [String, Regexp] text The string/regexp to check for. If it's a string, text is expected to include it. If it's a regexp, text is expected to match it.
605
690
  # @option options [Integer] :count (nil) Number of times the text is expected to occur
606
691
  # @option options [Integer] :minimum (nil) Minimum number of times the text is expected to occur
607
692
  # @option options [Integer] :maximum (nil) Maximum number of times the text is expected to occur
608
693
  # @option options [Range] :between (nil) Range of times that is expected to contain number of times text occurs
609
- # @option options [Numeric] :wait (Capybara.default_max_wait_time) Maximum time that Capybara will wait for text to eq/match given string/regexp argument
610
- # @option options [Boolean] :exact (Capybara.exact_text) Whether text must be an exact match or just substring
694
+ # @option options [Numeric] :wait Maximum time that Capybara will wait for text to eq/match given string/regexp argument. Defaults to {Capybara.configure default_max_wait_time}.
695
+ # @option options [Boolean] :exact Whether text must be an exact match or just substring. Defaults to {Capybara.configure exact_text}.
696
+ # @option options [Boolean] :normalize_ws (false) When `true` replace all whitespace with standard spaces and collapse consecutive whitespace to a single space
611
697
  # @raise [Capybara::ExpectationNotMet] if the assertion hasn't succeeded during wait time
612
698
  # @return [true]
613
699
  #
614
- def assert_text(*args)
615
- _verify_text(args) do |count, query|
616
- unless query.matches_count?(count) && ((count > 0) || query.expects_none?)
700
+ def assert_text(type_or_text, *args, **opts)
701
+ _verify_text(type_or_text, *args, **opts) do |count, query|
702
+ unless query.matches_count?(count) && (count.positive? || query.expects_none?)
617
703
  raise Capybara::ExpectationNotMet, query.failure_message
618
704
  end
619
705
  end
@@ -627,9 +713,9 @@ module Capybara
627
713
  # @raise [Capybara::ExpectationNotMet] if the assertion hasn't succeeded during wait time
628
714
  # @return [true]
629
715
  #
630
- def assert_no_text(*args)
631
- _verify_text(args) do |count, query|
632
- if query.matches_count?(count) && ((count > 0) || query.expects_none?)
716
+ def assert_no_text(type_or_text, *args, **opts)
717
+ _verify_text(type_or_text, *args, **opts) do |count, query|
718
+ if query.matches_count?(count) && (count.positive? || query.expects_none?)
633
719
  raise Capybara::ExpectationNotMet, query.negative_failure_message
634
720
  end
635
721
  end
@@ -639,10 +725,6 @@ module Capybara
639
725
  # Checks if the page or current node has the given text content,
640
726
  # ignoring any HTML tags.
641
727
  #
642
- # Whitespaces are normalized in both node's text and passed text parameter.
643
- # Note that whitespace isn't normalized in passed regexp as normalizing whitespace
644
- # in regexp isn't easy and doesn't seem to be worth it.
645
- #
646
728
  # By default it will check if the text occurs at least once,
647
729
  # but a different number can be specified.
648
730
  #
@@ -653,10 +735,8 @@ module Capybara
653
735
  # @macro text_query_params
654
736
  # @return [Boolean] Whether it exists
655
737
  #
656
- def has_text?(*args)
657
- assert_text(*args)
658
- rescue Capybara::ExpectationNotMet
659
- return false
738
+ def has_text?(*args, **options)
739
+ make_predicate(options) { assert_text(*args, **options) }
660
740
  end
661
741
  alias_method :has_content?, :has_text?
662
742
 
@@ -667,56 +747,161 @@ module Capybara
667
747
  # @macro text_query_params
668
748
  # @return [Boolean] Whether it doesn't exist
669
749
  #
670
- def has_no_text?(*args)
671
- assert_no_text(*args)
672
- rescue Capybara::ExpectationNotMet
673
- return false
750
+ def has_no_text?(*args, **options)
751
+ make_predicate(options) { assert_no_text(*args, **options) }
674
752
  end
675
753
  alias_method :has_no_content?, :has_no_text?
676
754
 
755
+ ##
756
+ #
757
+ # Asserts that a given selector matches an ancestor of the current node.
758
+ #
759
+ # element.assert_ancestor('p#foo')
760
+ #
761
+ # Accepts the same options as {#assert_selector}
762
+ #
763
+ # @param (see Capybara::Node::Finders#find)
764
+ # @raise [Capybara::ExpectationNotMet] If the selector does not exist
765
+ #
766
+ def assert_ancestor(*args, &optional_filter_block)
767
+ _verify_selector_result(args, optional_filter_block, Capybara::Queries::AncestorQuery) do |result, query|
768
+ unless result.matches_count? && (result.any? || query.expects_none?)
769
+ raise Capybara::ExpectationNotMet, result.failure_message
770
+ end
771
+ end
772
+ end
773
+
774
+ def assert_no_ancestor(*args, &optional_filter_block)
775
+ _verify_selector_result(args, optional_filter_block, Capybara::Queries::AncestorQuery) do |result, query|
776
+ if result.matches_count? && (!result.empty? || query.expects_none?)
777
+ raise Capybara::ExpectationNotMet, result.negative_failure_message
778
+ end
779
+ end
780
+ end
781
+
782
+ ##
783
+ #
784
+ # Predicate version of {#assert_ancestor}
785
+ #
786
+ def has_ancestor?(*args, **options, &optional_filter_block)
787
+ make_predicate(options) { assert_ancestor(*args, options, &optional_filter_block) }
788
+ end
789
+
790
+ ##
791
+ #
792
+ # Predicate version of {#assert_no_ancestor}
793
+ #
794
+ def has_no_ancestor?(*args, **options, &optional_filter_block)
795
+ make_predicate(options) { assert_no_ancestor(*args, options, &optional_filter_block) }
796
+ end
797
+
798
+ ##
799
+ #
800
+ # Asserts that a given selector matches a sibling of the current node.
801
+ #
802
+ # element.assert_sibling('p#foo')
803
+ #
804
+ # Accepts the same options as {#assert_selector}
805
+ #
806
+ # @param (see Capybara::Node::Finders#find)
807
+ # @raise [Capybara::ExpectationNotMet] If the selector does not exist
808
+ #
809
+ def assert_sibling(*args, &optional_filter_block)
810
+ _verify_selector_result(args, optional_filter_block, Capybara::Queries::SiblingQuery) do |result, query|
811
+ unless result.matches_count? && (result.any? || query.expects_none?)
812
+ raise Capybara::ExpectationNotMet, result.failure_message
813
+ end
814
+ end
815
+ end
816
+
817
+ def assert_no_sibling(*args, &optional_filter_block)
818
+ _verify_selector_result(args, optional_filter_block, Capybara::Queries::SiblingQuery) do |result, query|
819
+ if result.matches_count? && (!result.empty? || query.expects_none?)
820
+ raise Capybara::ExpectationNotMet, result.negative_failure_message
821
+ end
822
+ end
823
+ end
824
+
825
+ ##
826
+ #
827
+ # Predicate version of {#assert_sibling}
828
+ #
829
+ def has_sibling?(*args, **options, &optional_filter_block)
830
+ make_predicate(options) { assert_sibling(*args, options, &optional_filter_block) }
831
+ end
832
+
833
+ ##
834
+ #
835
+ # Predicate version of {#assert_no_sibling}
836
+ #
837
+ def has_no_sibling?(*args, **options, &optional_filter_block)
838
+ make_predicate(options) { assert_no_sibling(*args, options, &optional_filter_block) }
839
+ end
840
+
677
841
  def ==(other)
678
- self.eql?(other) || (other.respond_to?(:base) && base == other.base)
842
+ eql?(other) || (other.respond_to?(:base) && base == other.base)
679
843
  end
680
844
 
681
845
  private
682
846
 
683
- def _verify_selector_result(query_args, optional_filter_block, &result_block)
684
- _set_query_session_options(query_args)
685
- query = Capybara::Queries::SelectorQuery.new(*query_args, &optional_filter_block)
686
- synchronize(query.wait) do
687
- result = query.resolve_for(self)
688
- result_block.call(result, query)
847
+ def extract_selector(args)
848
+ args.first.is_a?(Symbol) ? args.shift : session_options.default_selector
849
+ end
850
+
851
+ def _verify_multiple(*args, wait: nil, **options)
852
+ wait = session_options.default_max_wait_time if wait.nil?
853
+ selector = extract_selector(args)
854
+ synchronize(wait) do
855
+ args.each { |locator| yield(selector, locator, options) }
689
856
  end
690
- return true
691
857
  end
692
858
 
693
- def _verify_match_result(query_args, optional_filter_block, &result_block)
694
- _set_query_session_options(query_args)
695
- query = Capybara::Queries::MatchQuery.new(*query_args, &optional_filter_block)
859
+ def _verify_selector_result(query_args, optional_filter_block, query_type = Capybara::Queries::SelectorQuery)
860
+ # query_args, query_opts = if query_args[0].is_a? Symbol
861
+ # a,o = _set_query_session_options(*query_args.slice(2..))
862
+ # [query_args.slice(0..1).concat(a), o]
863
+ # else
864
+ # _set_query_session_options(*query_args)
865
+ # end
866
+ query_args, query_opts = _set_query_session_options(*query_args)
867
+ query = query_type.new(*query_args, **query_opts, &optional_filter_block)
696
868
  synchronize(query.wait) do
697
- result = query.resolve_for(self.query_scope)
698
- result_block.call(result)
869
+ yield query.resolve_for(self), query
699
870
  end
700
- return true
871
+ true
701
872
  end
702
873
 
703
- def _verify_text(query_args)
704
- _set_query_session_options(query_args)
705
- query = Capybara::Queries::TextQuery.new(*query_args)
874
+ def _verify_match_result(query_args, optional_filter_block)
875
+ query_args, query_opts = _set_query_session_options(*query_args)
876
+ query = Capybara::Queries::MatchQuery.new(*query_args, **query_opts, &optional_filter_block)
706
877
  synchronize(query.wait) do
707
- count = query.resolve_for(self)
708
- yield(count, query)
878
+ yield query.resolve_for(parent || session&.document || query_scope)
709
879
  end
710
- return true
880
+ true
711
881
  end
712
882
 
713
- def _set_query_session_options(query_args)
714
- if query_args.last.is_a? Hash
715
- query_args.last[:session_options] = session_options
716
- else
717
- query_args.push(session_options: session_options)
883
+ def _verify_text(type = nil, expected_text, **query_options) # rubocop:disable Style/OptionalArguments
884
+ query_options[:session_options] = session_options
885
+ query = Capybara::Queries::TextQuery.new(type, expected_text, **query_options)
886
+ synchronize(query.wait) do
887
+ yield query.resolve_for(self), query
718
888
  end
719
- query_args
889
+ true
890
+ end
891
+
892
+ def _set_query_session_options(*query_args)
893
+ query_args, query_options = query_args.dup, {}
894
+ # query_options = query_args.pop if query_options.empty? && query_args.last.is_a?(Hash)
895
+ query_options = query_args.pop if query_args.last.is_a?(Hash)
896
+ query_options[:session_options] = session_options
897
+ [query_args, query_options]
898
+ end
899
+
900
+ def make_predicate(options)
901
+ options[:wait] = 0 unless options.key?(:wait) || session_options.predicates_wait
902
+ yield
903
+ rescue Capybara::ExpectationNotMet
904
+ false
720
905
  end
721
906
  end
722
907
  end