capybara 3.0.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 (312) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +1 -1
  3. data/History.md +891 -12
  4. data/License.txt +1 -1
  5. data/README.md +257 -84
  6. data/lib/capybara/config.rb +29 -10
  7. data/lib/capybara/cucumber.rb +1 -1
  8. data/lib/capybara/driver/base.rb +22 -4
  9. data/lib/capybara/driver/node.rb +38 -9
  10. data/lib/capybara/dsl.rb +9 -7
  11. data/lib/capybara/helpers.rb +57 -8
  12. data/lib/capybara/minitest/spec.rb +185 -84
  13. data/lib/capybara/minitest.rb +264 -145
  14. data/lib/capybara/node/actions.rb +248 -124
  15. data/lib/capybara/node/base.rb +35 -20
  16. data/lib/capybara/node/document.rb +14 -2
  17. data/lib/capybara/node/document_matchers.rb +13 -15
  18. data/lib/capybara/node/element.rb +350 -113
  19. data/lib/capybara/node/finders.rb +104 -82
  20. data/lib/capybara/node/matchers.rb +363 -157
  21. data/lib/capybara/node/simple.rb +54 -15
  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 +9 -10
  25. data/lib/capybara/queries/base_query.rb +25 -18
  26. data/lib/capybara/queries/current_path_query.rb +16 -6
  27. data/lib/capybara/queries/match_query.rb +11 -0
  28. data/lib/capybara/queries/selector_query.rb +617 -104
  29. data/lib/capybara/queries/sibling_query.rb +9 -7
  30. data/lib/capybara/queries/style_query.rb +45 -0
  31. data/lib/capybara/queries/text_query.rb +40 -22
  32. data/lib/capybara/queries/title_query.rb +2 -2
  33. data/lib/capybara/rack_test/browser.rb +106 -31
  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 +74 -49
  37. data/lib/capybara/rack_test/node.rb +120 -47
  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 +87 -53
  44. data/lib/capybara/rspec/features.rb +8 -10
  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 +142 -315
  60. data/lib/capybara/rspec.rb +3 -2
  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 +85 -8
  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 +72 -28
  88. data/lib/capybara/selector/filters/base.rb +45 -2
  89. data/lib/capybara/selector/filters/expression_filter.rb +5 -6
  90. data/lib/capybara/selector/filters/locator_filter.rb +29 -0
  91. data/lib/capybara/selector/filters/node_filter.rb +18 -4
  92. data/lib/capybara/selector/regexp_disassembler.rb +211 -0
  93. data/lib/capybara/selector/selector.rb +89 -200
  94. data/lib/capybara/selector/xpath_extensions.rb +17 -0
  95. data/lib/capybara/selector.rb +474 -534
  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 +270 -245
  101. data/lib/capybara/selenium/driver_specializations/chrome_driver.rb +117 -0
  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 +460 -170
  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 +80 -0
  123. data/lib/capybara/server/checker.rb +44 -0
  124. data/lib/capybara/server/middleware.rb +71 -0
  125. data/lib/capybara/server.rb +58 -67
  126. data/lib/capybara/session/config.rb +40 -6
  127. data/lib/capybara/session/matchers.rb +26 -19
  128. data/lib/capybara/session.rb +252 -194
  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 +126 -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 +135 -44
  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.rb → assert_current_path_spec.rb} +20 -18
  140. data/lib/capybara/spec/session/assert_selector_spec.rb +143 -0
  141. data/lib/capybara/spec/session/assert_style_spec.rb +26 -0
  142. data/lib/capybara/spec/session/{assert_text.rb → assert_text_spec.rb} +76 -52
  143. data/lib/capybara/spec/session/{assert_title.rb → 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 +11 -13
  146. data/lib/capybara/spec/session/check_spec.rb +112 -51
  147. data/lib/capybara/spec/session/choose_spec.rb +62 -30
  148. data/lib/capybara/spec/session/click_button_spec.rb +227 -161
  149. data/lib/capybara/spec/session/click_link_or_button_spec.rb +49 -30
  150. data/lib/capybara/spec/session/click_link_spec.rb +89 -55
  151. data/lib/capybara/spec/session/current_scope_spec.rb +8 -8
  152. data/lib/capybara/spec/session/current_url_spec.rb +44 -37
  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.rb → assert_match_selector_spec.rb} +11 -9
  156. data/lib/capybara/spec/session/element/match_css_spec.rb +18 -10
  157. data/lib/capybara/spec/session/element/match_xpath_spec.rb +8 -6
  158. data/lib/capybara/spec/session/element/matches_selector_spec.rb +70 -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 +110 -44
  163. data/lib/capybara/spec/session/find_button_spec.rb +23 -23
  164. data/lib/capybara/spec/session/find_by_id_spec.rb +8 -8
  165. data/lib/capybara/spec/session/find_field_spec.rb +33 -31
  166. data/lib/capybara/spec/session/find_link_spec.rb +42 -14
  167. data/lib/capybara/spec/session/find_spec.rb +251 -142
  168. data/lib/capybara/spec/session/first_spec.rb +45 -44
  169. data/lib/capybara/spec/session/frame/frame_title_spec.rb +6 -6
  170. data/lib/capybara/spec/session/frame/frame_url_spec.rb +6 -6
  171. data/lib/capybara/spec/session/frame/switch_to_frame_spec.rb +32 -20
  172. data/lib/capybara/spec/session/frame/within_frame_spec.rb +46 -19
  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 +98 -12
  179. data/lib/capybara/spec/session/has_css_spec.rb +271 -137
  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 +46 -6
  184. data/lib/capybara/spec/session/has_none_selectors_spec.rb +33 -31
  185. data/lib/capybara/spec/session/has_select_spec.rb +84 -50
  186. data/lib/capybara/spec/session/has_selector_spec.rb +117 -69
  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.rb → headers_spec.rb} +1 -1
  193. data/lib/capybara/spec/session/html_spec.rb +13 -6
  194. data/lib/capybara/spec/session/matches_style_spec.rb +37 -0
  195. data/lib/capybara/spec/session/node_spec.rb +958 -122
  196. data/lib/capybara/spec/session/node_wrapper_spec.rb +15 -12
  197. data/lib/capybara/spec/session/refresh_spec.rb +9 -7
  198. data/lib/capybara/spec/session/reset_session_spec.rb +65 -37
  199. data/lib/capybara/spec/session/{response_code.rb → response_code_spec.rb} +1 -1
  200. data/lib/capybara/spec/session/save_and_open_page_spec.rb +2 -2
  201. data/lib/capybara/spec/session/save_and_open_screenshot_spec.rb +5 -4
  202. data/lib/capybara/spec/session/save_page_spec.rb +41 -38
  203. data/lib/capybara/spec/session/save_screenshot_spec.rb +13 -11
  204. data/lib/capybara/spec/session/screenshot_spec.rb +2 -2
  205. data/lib/capybara/spec/session/scroll_spec.rb +119 -0
  206. data/lib/capybara/spec/session/select_spec.rb +102 -76
  207. data/lib/capybara/spec/session/selectors_spec.rb +51 -18
  208. data/lib/capybara/spec/session/sibling_spec.rb +9 -9
  209. data/lib/capybara/spec/session/text_spec.rb +26 -24
  210. data/lib/capybara/spec/session/title_spec.rb +8 -6
  211. data/lib/capybara/spec/session/uncheck_spec.rb +41 -22
  212. data/lib/capybara/spec/session/unselect_spec.rb +37 -37
  213. data/lib/capybara/spec/session/visit_spec.rb +79 -53
  214. data/lib/capybara/spec/session/window/become_closed_spec.rb +22 -19
  215. data/lib/capybara/spec/session/window/current_window_spec.rb +4 -3
  216. data/lib/capybara/spec/session/window/open_new_window_spec.rb +4 -3
  217. data/lib/capybara/spec/session/window/switch_to_window_spec.rb +25 -21
  218. data/lib/capybara/spec/session/window/window_opened_by_spec.rb +10 -5
  219. data/lib/capybara/spec/session/window/window_spec.rb +88 -54
  220. data/lib/capybara/spec/session/window/windows_spec.rb +11 -8
  221. data/lib/capybara/spec/session/window/within_window_spec.rb +17 -16
  222. data/lib/capybara/spec/session/within_spec.rb +82 -44
  223. data/lib/capybara/spec/spec_helper.rb +46 -52
  224. data/lib/capybara/spec/test_app.rb +148 -41
  225. data/lib/capybara/spec/views/animated.erb +49 -0
  226. data/lib/capybara/spec/views/form.erb +156 -42
  227. data/lib/capybara/spec/views/frame_child.erb +4 -3
  228. data/lib/capybara/spec/views/frame_one.erb +2 -1
  229. data/lib/capybara/spec/views/frame_parent.erb +1 -1
  230. data/lib/capybara/spec/views/frame_two.erb +1 -1
  231. data/lib/capybara/spec/views/initial_alert.erb +2 -1
  232. data/lib/capybara/spec/views/layout.erb +10 -0
  233. data/lib/capybara/spec/views/obscured.erb +47 -0
  234. data/lib/capybara/spec/views/offset.erb +33 -0
  235. data/lib/capybara/spec/views/path.erb +2 -2
  236. data/lib/capybara/spec/views/popup_one.erb +1 -1
  237. data/lib/capybara/spec/views/popup_two.erb +1 -1
  238. data/lib/capybara/spec/views/react.erb +45 -0
  239. data/lib/capybara/spec/views/scroll.erb +21 -0
  240. data/lib/capybara/spec/views/spatial.erb +31 -0
  241. data/lib/capybara/spec/views/tables.erb +68 -1
  242. data/lib/capybara/spec/views/with_animation.erb +81 -0
  243. data/lib/capybara/spec/views/with_base_tag.erb +2 -2
  244. data/lib/capybara/spec/views/with_dragula.erb +24 -0
  245. data/lib/capybara/spec/views/with_fixed_header_footer.erb +2 -1
  246. data/lib/capybara/spec/views/with_hover.erb +3 -2
  247. data/lib/capybara/spec/views/with_hover1.erb +10 -0
  248. data/lib/capybara/spec/views/with_html.erb +46 -11
  249. data/lib/capybara/spec/views/with_html5_svg.erb +20 -0
  250. data/lib/capybara/spec/views/with_jquery_animation.erb +24 -0
  251. data/lib/capybara/spec/views/with_js.erb +30 -5
  252. data/lib/capybara/spec/views/with_jstree.erb +26 -0
  253. data/lib/capybara/spec/views/with_namespace.erb +21 -0
  254. data/lib/capybara/spec/views/with_scope.erb +2 -2
  255. data/lib/capybara/spec/views/with_scope_other.erb +6 -0
  256. data/lib/capybara/spec/views/with_shadow.erb +31 -0
  257. data/lib/capybara/spec/views/with_slow_unload.erb +2 -1
  258. data/lib/capybara/spec/views/with_sortable_js.erb +21 -0
  259. data/lib/capybara/spec/views/with_unload_alert.erb +1 -0
  260. data/lib/capybara/spec/views/with_windows.erb +1 -1
  261. data/lib/capybara/spec/views/within_frames.erb +1 -1
  262. data/lib/capybara/version.rb +1 -1
  263. data/lib/capybara/window.rb +32 -26
  264. data/lib/capybara.rb +128 -104
  265. data/spec/basic_node_spec.rb +59 -34
  266. data/spec/capybara_spec.rb +65 -51
  267. data/spec/counter_spec.rb +35 -0
  268. data/spec/css_builder_spec.rb +101 -0
  269. data/spec/css_splitter_spec.rb +38 -0
  270. data/spec/dsl_spec.rb +84 -55
  271. data/spec/filter_set_spec.rb +24 -7
  272. data/spec/fixtures/certificate.pem +25 -0
  273. data/spec/fixtures/key.pem +27 -0
  274. data/spec/fixtures/selenium_driver_rspec_failure.rb +5 -5
  275. data/spec/fixtures/selenium_driver_rspec_success.rb +5 -5
  276. data/spec/minitest_spec.rb +49 -5
  277. data/spec/minitest_spec_spec.rb +92 -62
  278. data/spec/per_session_config_spec.rb +6 -6
  279. data/spec/rack_test_spec.rb +183 -115
  280. data/spec/regexp_dissassembler_spec.rb +250 -0
  281. data/spec/result_spec.rb +99 -39
  282. data/spec/rspec/features_spec.rb +28 -25
  283. data/spec/rspec/scenarios_spec.rb +10 -6
  284. data/spec/rspec/shared_spec_matchers.rb +418 -364
  285. data/spec/rspec/views_spec.rb +4 -3
  286. data/spec/rspec_matchers_spec.rb +35 -10
  287. data/spec/rspec_spec.rb +109 -85
  288. data/spec/sauce_spec_chrome.rb +43 -0
  289. data/spec/selector_spec.rb +392 -62
  290. data/spec/selenium_spec_chrome.rb +183 -41
  291. data/spec/selenium_spec_chrome_remote.rb +96 -0
  292. data/spec/selenium_spec_edge.rb +41 -8
  293. data/spec/selenium_spec_firefox.rb +228 -0
  294. data/spec/selenium_spec_firefox_remote.rb +94 -0
  295. data/spec/selenium_spec_ie.rb +129 -11
  296. data/spec/selenium_spec_safari.rb +162 -0
  297. data/spec/server_spec.rb +192 -81
  298. data/spec/session_spec.rb +52 -16
  299. data/spec/shared_selenium_node.rb +79 -0
  300. data/spec/shared_selenium_session.rb +460 -123
  301. data/spec/spec_helper.rb +124 -2
  302. data/spec/whitespace_normalizer_spec.rb +54 -0
  303. data/spec/xpath_builder_spec.rb +93 -0
  304. metadata +344 -45
  305. data/.yard/templates_custom/default/class/html/selectors.erb +0 -38
  306. data/.yard/templates_custom/default/class/html/setup.rb +0 -17
  307. data/.yard/yard_extensions.rb +0 -78
  308. data/lib/capybara/rspec/compound.rb +0 -90
  309. data/lib/capybara/spec/session/assert_selector.rb +0 -149
  310. data/lib/capybara/spec/session/source_spec.rb +0 -0
  311. data/lib/capybara/spec/views/with_title.erb +0 -5
  312. data/spec/selenium_spec_marionette.rb +0 -143
@@ -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
- 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
- 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,76 +108,126 @@ 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, wait: nil, **options, &optional_filter_block)
116
- wait = session_options.default_max_wait_time if wait.nil?
117
- selector = args.first.is_a?(Symbol) ? args.shift : session_options.default_selector
118
- synchronize(wait) do
119
- args.each do |locator|
120
- assert_selector(selector, locator, options, &optional_filter_block)
121
- 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)
122
162
  end
123
163
  end
124
164
 
125
165
  # Asserts that none of the provided selectors are present on the given page
126
166
  # or descendants of the current node. If options are provided, the assertion
127
- # 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`).
128
168
  #
129
- # page.assert_none_of_selectors(:custom, 'Tom', 'Joe', visible: all)
130
- # page.assert_none_of_selectors(:css, '#my_div', 'a.not_clicked')
169
+ # page.assert_none_of_selectors(:custom, 'Tom', 'Joe', visible: all)
170
+ # page.assert_none_of_selectors(:css, '#my_div', 'a.not_clicked')
131
171
  #
132
172
  # It accepts all options that {Capybara::Node::Finders#all} accepts,
133
- # such as :text and :visible.
173
+ # such as `:text` and `:visible`.
134
174
  #
135
- # The :wait option applies to all of the selectors as a group, so none of the locators must be present
136
- # within :wait (Defaults to Capybara.default_max_wait_time) seconds.
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.
137
177
  #
138
- # @overload assert_none_of_selectors([kind = Capybara.default_selector], *locators, options = {})
178
+ # @overload assert_none_of_selectors([kind = Capybara.default_selector], *locators, **options)
139
179
  #
140
- def assert_none_of_selectors(*args, wait: nil, **options, &optional_filter_block)
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`).
189
+ #
190
+ # page.assert_any_of_selectors(:custom, 'Tom', 'Joe', visible: all)
191
+ # page.assert_any_of_selectors(:css, '#my_div', 'a.not_clicked')
192
+ #
193
+ # It accepts all options that {Capybara::Node::Finders#all} accepts,
194
+ # such as `:text` and `:visible`.
195
+ #
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.
198
+ #
199
+ # @overload assert_any_of_selectors([kind = Capybara.default_selector], *locators, **options)
200
+ #
201
+ def assert_any_of_selectors(*args, wait: nil, **options, &optional_filter_block)
141
202
  wait = session_options.default_max_wait_time if wait.nil?
142
- selector = args.first.is_a?(Symbol) ? args.shift : session_options.default_selector
203
+ selector = extract_selector(args)
143
204
  synchronize(wait) do
144
- args.each do |locator|
145
- 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
146
210
  end
211
+ raise Capybara::ExpectationNotMet, res.join(' or ') if res
212
+
213
+ true
147
214
  end
148
215
  end
149
216
 
150
217
  ##
151
218
  #
152
219
  # Asserts that a given selector is not on the page or a descendant of the current node.
153
- # Usage is identical to Capybara::Node::Matchers#assert_selector
220
+ # Usage is identical to {#assert_selector}.
154
221
  #
155
- # Query options such as :count, :minimum, :maximum, and :between are
222
+ # Query options such as `:count`, `:minimum`, `:maximum`, and `:between` are
156
223
  # considered to be an integral part of the selector. This will return
157
- # 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:
158
225
  #
159
226
  # page.assert_no_selector('a', minimum: 1) # Found, raises Capybara::ExpectationNotMet
160
227
  # page.assert_no_selector('a', count: 4) # Found, raises Capybara::ExpectationNotMet
161
228
  # page.assert_no_selector('a', count: 5) # Not Found, returns true
162
229
  #
163
- # @param (see Capybara::Node::Finders#assert_selector)
230
+ # @param (see #assert_selector)
164
231
  # @raise [Capybara::ExpectationNotMet] If the selector exists
165
232
  #
166
233
  def assert_no_selector(*args, &optional_filter_block)
@@ -170,7 +237,6 @@ module Capybara
170
237
  end
171
238
  end
172
239
  end
173
- alias_method :refute_selector, :assert_no_selector
174
240
 
175
241
  ##
176
242
  #
@@ -186,11 +252,11 @@ module Capybara
186
252
  # This will check if the expression occurs exactly 4 times.
187
253
  #
188
254
  # It also accepts all options that {Capybara::Node::Finders#all} accepts,
189
- # such as :text and :visible.
255
+ # such as `:text` and `:visible`.
190
256
  #
191
257
  # page.has_xpath?('.//li', text: 'Horse', visible: true)
192
258
  #
193
- # has_xpath? can also accept XPath expressions generate by the
259
+ # {#has_xpath?} can also accept XPath expressions generated by the
194
260
  # XPath gem:
195
261
  #
196
262
  # xpath = XPath.generate { |x| x.descendant(:p) }
@@ -202,19 +268,19 @@ module Capybara
202
268
  # @return [Boolean] If the expression exists
203
269
  #
204
270
  def has_xpath?(path, **options, &optional_filter_block)
205
- has_selector?(:xpath, path, options, &optional_filter_block)
271
+ has_selector?(:xpath, path, **options, &optional_filter_block)
206
272
  end
207
273
 
208
274
  ##
209
275
  #
210
276
  # Checks if a given XPath expression is not on the page or a descendant of the current node.
211
- # Usage is identical to Capybara::Node::Matchers#has_xpath?
277
+ # Usage is identical to {#has_xpath?}.
212
278
  #
213
- # @param (see Capybara::Node::Finders#has_xpath?)
279
+ # @param (see #has_xpath?)
214
280
  # @return [Boolean]
215
281
  #
216
282
  def has_no_xpath?(path, **options, &optional_filter_block)
217
- has_no_selector?(:xpath, path, options, &optional_filter_block)
283
+ has_no_selector?(:xpath, path, **options, &optional_filter_block)
218
284
  end
219
285
 
220
286
  ##
@@ -231,7 +297,7 @@ module Capybara
231
297
  # This will check if the selector occurs exactly 4 times.
232
298
  #
233
299
  # It also accepts all options that {Capybara::Node::Finders#all} accepts,
234
- # such as :text and :visible.
300
+ # such as `:text` and `:visible`.
235
301
  #
236
302
  # page.has_css?('li', text: 'Horse', visible: true)
237
303
  #
@@ -241,19 +307,44 @@ module Capybara
241
307
  # @return [Boolean] If the selector exists
242
308
  #
243
309
  def has_css?(path, **options, &optional_filter_block)
244
- has_selector?(:css, path, options, &optional_filter_block)
310
+ has_selector?(:css, path, **options, &optional_filter_block)
245
311
  end
246
312
 
247
313
  ##
248
314
  #
249
315
  # Checks if a given CSS selector is not on the page or a descendant of the current node.
250
- # Usage is identical to Capybara::Node::Matchers#has_css?
316
+ # Usage is identical to {#has_css?}.
251
317
  #
252
- # @param (see Capybara::Node::Finders#has_css?)
318
+ # @param (see #has_css?)
253
319
  # @return [Boolean]
254
320
  #
255
321
  def has_no_css?(path, **options, &optional_filter_block)
256
- has_no_selector?(: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)
257
348
  end
258
349
 
259
350
  ##
@@ -262,12 +353,11 @@ module Capybara
262
353
  # text or id.
263
354
  #
264
355
  # @param [String] locator The text or id of a link to check for
265
- # @param options
266
356
  # @option options [String, Regexp] :href The value the href attribute must be
267
357
  # @return [Boolean] Whether it exists
268
358
  #
269
359
  def has_link?(locator = nil, **options, &optional_filter_block)
270
- has_selector?(:link, locator, options, &optional_filter_block)
360
+ has_selector?(:link, locator, **options, &optional_filter_block)
271
361
  end
272
362
 
273
363
  ##
@@ -275,11 +365,11 @@ module Capybara
275
365
  # Checks if the page or current node has no link with the given
276
366
  # text or id.
277
367
  #
278
- # @param (see Capybara::Node::Finders#has_link?)
368
+ # @param (see #has_link?)
279
369
  # @return [Boolean] Whether it doesn't exist
280
370
  #
281
371
  def has_no_link?(locator = nil, **options, &optional_filter_block)
282
- has_no_selector?(:link, locator, options, &optional_filter_block)
372
+ has_no_selector?(:link, locator, **options, &optional_filter_block)
283
373
  end
284
374
 
285
375
  ##
@@ -291,7 +381,7 @@ module Capybara
291
381
  # @return [Boolean] Whether it exists
292
382
  #
293
383
  def has_button?(locator = nil, **options, &optional_filter_block)
294
- has_selector?(:button, locator, options, &optional_filter_block)
384
+ has_selector?(:button, locator, **options, &optional_filter_block)
295
385
  end
296
386
 
297
387
  ##
@@ -303,7 +393,7 @@ module Capybara
303
393
  # @return [Boolean] Whether it doesn't exist
304
394
  #
305
395
  def has_no_button?(locator = nil, **options, &optional_filter_block)
306
- has_no_selector?(:button, locator, options, &optional_filter_block)
396
+ has_no_selector?(:button, locator, **options, &optional_filter_block)
307
397
  end
308
398
 
309
399
  ##
@@ -312,7 +402,7 @@ module Capybara
312
402
  # label, name or id.
313
403
  #
314
404
  # For text fields and other textual fields, such as textareas and
315
- # 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`
316
406
  # option to specify the text the field should contain:
317
407
  #
318
408
  # page.has_field?('Name', with: 'Jonas')
@@ -321,7 +411,7 @@ module Capybara
321
411
  #
322
412
  # page.has_field?('Email', type: 'email')
323
413
  #
324
- # 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.
325
415
  #
326
416
  # @param [String] locator The label, name or id of a field to check for
327
417
  # @option options [String, Regexp] :with The text content of the field or a Regexp to match
@@ -329,13 +419,13 @@ module Capybara
329
419
  # @return [Boolean] Whether it exists
330
420
  #
331
421
  def has_field?(locator = nil, **options, &optional_filter_block)
332
- has_selector?(:field, locator, options, &optional_filter_block)
422
+ has_selector?(:field, locator, **options, &optional_filter_block)
333
423
  end
334
424
 
335
425
  ##
336
426
  #
337
427
  # Checks if the page or current node has no form field with the given
338
- # label, name or id. See {Capybara::Node::Matchers#has_field?}.
428
+ # label, name or id. See {#has_field?}.
339
429
  #
340
430
  # @param [String] locator The label, name or id of a field to check for
341
431
  # @option options [String, Regexp] :with The text content of the field or a Regexp to match
@@ -343,59 +433,59 @@ module Capybara
343
433
  # @return [Boolean] Whether it doesn't exist
344
434
  #
345
435
  def has_no_field?(locator = nil, **options, &optional_filter_block)
346
- has_no_selector?(:field, locator, options, &optional_filter_block)
436
+ has_no_selector?(:field, locator, **options, &optional_filter_block)
347
437
  end
348
438
 
349
439
  ##
350
440
  #
351
441
  # Checks if the page or current node has a radio button or
352
- # 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
353
443
  # checked.
354
444
  #
355
445
  # @param [String] locator The label, name or id of a checked field
356
446
  # @return [Boolean] Whether it exists
357
447
  #
358
448
  def has_checked_field?(locator = nil, **options, &optional_filter_block)
359
- has_selector?(:field, locator, options.merge(checked: true), &optional_filter_block)
449
+ has_selector?(:field, locator, **options.merge(checked: true), &optional_filter_block)
360
450
  end
361
451
 
362
452
  ##
363
453
  #
364
454
  # Checks if the page or current node has no radio button or
365
- # 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
366
456
  # checked.
367
457
  #
368
458
  # @param [String] locator The label, name or id of a checked field
369
459
  # @return [Boolean] Whether it doesn't exist
370
460
  #
371
461
  def has_no_checked_field?(locator = nil, **options, &optional_filter_block)
372
- has_no_selector?(:field, locator, options.merge(checked: true), &optional_filter_block)
462
+ has_no_selector?(:field, locator, **options.merge(checked: true), &optional_filter_block)
373
463
  end
374
464
 
375
465
  ##
376
466
  #
377
467
  # Checks if the page or current node has a radio button or
378
- # 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
379
469
  # unchecked.
380
470
  #
381
471
  # @param [String] locator The label, name or id of an unchecked field
382
472
  # @return [Boolean] Whether it exists
383
473
  #
384
474
  def has_unchecked_field?(locator = nil, **options, &optional_filter_block)
385
- has_selector?(:field, locator, options.merge(unchecked: true), &optional_filter_block)
475
+ has_selector?(:field, locator, **options.merge(unchecked: true), &optional_filter_block)
386
476
  end
387
477
 
388
478
  ##
389
479
  #
390
480
  # Checks if the page or current node has no radio button or
391
- # 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
392
482
  # unchecked.
393
483
  #
394
484
  # @param [String] locator The label, name or id of an unchecked field
395
485
  # @return [Boolean] Whether it doesn't exist
396
486
  #
397
487
  def has_no_unchecked_field?(locator = nil, **options, &optional_filter_block)
398
- has_no_selector?(:field, locator, options.merge(unchecked: true), &optional_filter_block)
488
+ has_no_selector?(:field, locator, **options.merge(unchecked: true), &optional_filter_block)
399
489
  end
400
490
 
401
491
  ##
@@ -428,19 +518,19 @@ module Capybara
428
518
  # @return [Boolean] Whether it exists
429
519
  #
430
520
  def has_select?(locator = nil, **options, &optional_filter_block)
431
- has_selector?(:select, locator, options, &optional_filter_block)
521
+ has_selector?(:select, locator, **options, &optional_filter_block)
432
522
  end
433
523
 
434
524
  ##
435
525
  #
436
526
  # Checks if the page or current node has no select field with the
437
- # given label, name or id. See {Capybara::Node::Matchers#has_select?}.
527
+ # given label, name or id. See {#has_select?}.
438
528
  #
439
- # @param (see Capybara::Node::Matchers#has_select?)
529
+ # @param (see #has_select?)
440
530
  # @return [Boolean] Whether it doesn't exist
441
531
  #
442
532
  def has_no_select?(locator = nil, **options, &optional_filter_block)
443
- has_no_selector?(:select, locator, options, &optional_filter_block)
533
+ has_no_selector?(:select, locator, **options, &optional_filter_block)
444
534
  end
445
535
 
446
536
  ##
@@ -450,35 +540,43 @@ module Capybara
450
540
  #
451
541
  # page.has_table?('People')
452
542
  #
453
- # @param [String] locator The id or caption of a table
454
- # @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
455
553
  #
456
554
  def has_table?(locator = nil, **options, &optional_filter_block)
457
- has_selector?(:table, locator, options, &optional_filter_block)
555
+ has_selector?(:table, locator, **options, &optional_filter_block)
458
556
  end
459
557
 
460
558
  ##
461
559
  #
462
560
  # Checks if the page or current node has no table with the given id
463
- # or caption. See {Capybara::Node::Matchers#has_table?}.
561
+ # or caption. See {#has_table?}.
464
562
  #
465
- # @param (see Capybara::Node::Matchers#has_table?)
563
+ # @param (see #has_table?)
466
564
  # @return [Boolean] Whether it doesn't exist
467
565
  #
468
566
  def has_no_table?(locator = nil, **options, &optional_filter_block)
469
- has_no_selector?(:table, locator, options, &optional_filter_block)
567
+ has_no_selector?(:table, locator, **options, &optional_filter_block)
470
568
  end
471
569
 
472
570
  ##
473
571
  #
474
- # Asserts that the current_node matches a given selector
572
+ # Asserts that the current node matches a given selector.
475
573
  #
476
574
  # node.assert_matches_selector('p#foo')
477
575
  # node.assert_matches_selector(:xpath, '//p[@id="foo"]')
478
576
  # node.assert_matches_selector(:foo)
479
577
  #
480
578
  # It also accepts all options that {Capybara::Node::Finders#all} accepts,
481
- # such as :text and :visible.
579
+ # such as `:text` and `:visible`.
482
580
  #
483
581
  # node.assert_matches_selector('li', text: 'Horse', visible: true)
484
582
  #
@@ -487,86 +585,89 @@ module Capybara
487
585
  #
488
586
  def assert_matches_selector(*args, &optional_filter_block)
489
587
  _verify_match_result(args, optional_filter_block) do |result|
490
- 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
491
589
  end
492
590
  end
493
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
+ #
494
600
  def assert_not_matches_selector(*args, &optional_filter_block)
495
601
  _verify_match_result(args, optional_filter_block) do |result|
496
602
  raise Capybara::ExpectationNotMet, 'Item matched the provided selector' if result.include? self
497
603
  end
498
604
  end
499
- alias_method :refute_matches_selector, :assert_not_matches_selector
500
605
 
501
606
  ##
502
607
  #
503
- # Checks if the current node matches given selector
608
+ # Checks if the current node matches given selector.
504
609
  #
505
- # @param (see Capybara::Node::Finders#has_selector?)
610
+ # @param (see #has_selector?)
506
611
  # @return [Boolean]
507
612
  #
508
- def matches_selector?(*args, &optional_filter_block)
509
- assert_matches_selector(*args, &optional_filter_block)
510
- rescue Capybara::ExpectationNotMet
511
- return false
613
+ def matches_selector?(*args, **options, &optional_filter_block)
614
+ make_predicate(options) { assert_matches_selector(*args, options, &optional_filter_block) }
512
615
  end
513
616
 
514
617
  ##
515
618
  #
516
- # Checks if the current node matches given XPath expression
619
+ # Checks if the current node matches given XPath expression.
517
620
  #
518
621
  # @param [String, XPath::Expression] xpath The XPath expression to match against the current code
519
622
  # @return [Boolean]
520
623
  #
521
624
  def matches_xpath?(xpath, **options, &optional_filter_block)
522
- matches_selector?(:xpath, xpath, options, &optional_filter_block)
625
+ matches_selector?(:xpath, xpath, **options, &optional_filter_block)
523
626
  end
524
627
 
525
628
  ##
526
629
  #
527
- # Checks if the current node matches given CSS selector
630
+ # Checks if the current node matches given CSS selector.
528
631
  #
529
632
  # @param [String] css The CSS selector to match against the current code
530
633
  # @return [Boolean]
531
634
  #
532
635
  def matches_css?(css, **options, &optional_filter_block)
533
- matches_selector?(:css, css, options, &optional_filter_block)
636
+ matches_selector?(:css, css, **options, &optional_filter_block)
534
637
  end
535
638
 
536
639
  ##
537
640
  #
538
- # Checks if the current node does not match given selector
539
- # 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?}.
540
643
  #
541
- # @param (see Capybara::Node::Finders#has_selector?)
644
+ # @param (see #has_selector?)
542
645
  # @return [Boolean]
543
646
  #
544
- def not_matches_selector?(*args, &optional_filter_block)
545
- assert_not_matches_selector(*args, &optional_filter_block)
546
- rescue Capybara::ExpectationNotMet
547
- 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) }
548
649
  end
549
650
 
550
651
  ##
551
652
  #
552
- # Checks if the current node does not match given XPath expression
653
+ # Checks if the current node does not match given XPath expression.
553
654
  #
554
655
  # @param [String, XPath::Expression] xpath The XPath expression to match against the current code
555
656
  # @return [Boolean]
556
657
  #
557
658
  def not_matches_xpath?(xpath, **options, &optional_filter_block)
558
- not_matches_selector?(:xpath, xpath, options, &optional_filter_block)
659
+ not_matches_selector?(:xpath, xpath, **options, &optional_filter_block)
559
660
  end
560
661
 
561
662
  ##
562
663
  #
563
- # Checks if the current node does not match given CSS selector
664
+ # Checks if the current node does not match given CSS selector.
564
665
  #
565
666
  # @param [String] css The CSS selector to match against the current code
566
667
  # @return [Boolean]
567
668
  #
568
669
  def not_matches_css?(css, **options, &optional_filter_block)
569
- not_matches_selector?(:css, css, options, &optional_filter_block)
670
+ not_matches_selector?(:css, css, **options, &optional_filter_block)
570
671
  end
571
672
 
572
673
  ##
@@ -574,29 +675,31 @@ module Capybara
574
675
  # ignoring any HTML tags.
575
676
  #
576
677
  # @!macro text_query_params
577
- # @overload $0(type, text, options = {})
578
- # @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`.
579
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.
580
681
  # @option options [Integer] :count (nil) Number of times the text is expected to occur
581
682
  # @option options [Integer] :minimum (nil) Minimum number of times the text is expected to occur
582
683
  # @option options [Integer] :maximum (nil) Maximum number of times the text is expected to occur
583
684
  # @option options [Range] :between (nil) Range of times that is expected to contain number of times text occurs
584
- # @option options [Numeric] :wait (Capybara.default_max_wait_time) Maximum time that Capybara will wait for text to eq/match given string/regexp argument
585
- # @option options [Boolean] :exact (Capybara.exact_text) Whether text must be an exact match or just substring
586
- # @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)
587
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.
588
690
  # @option options [Integer] :count (nil) Number of times the text is expected to occur
589
691
  # @option options [Integer] :minimum (nil) Minimum number of times the text is expected to occur
590
692
  # @option options [Integer] :maximum (nil) Maximum number of times the text is expected to occur
591
693
  # @option options [Range] :between (nil) Range of times that is expected to contain number of times text occurs
592
- # @option options [Numeric] :wait (Capybara.default_max_wait_time) Maximum time that Capybara will wait for text to eq/match given string/regexp argument
593
- # @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
594
697
  # @raise [Capybara::ExpectationNotMet] if the assertion hasn't succeeded during wait time
595
698
  # @return [true]
596
699
  #
597
- def assert_text(*args)
598
- _verify_text(args) do |count, query|
599
- 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?)
600
703
  raise Capybara::ExpectationNotMet, query.failure_message
601
704
  end
602
705
  end
@@ -610,9 +713,9 @@ module Capybara
610
713
  # @raise [Capybara::ExpectationNotMet] if the assertion hasn't succeeded during wait time
611
714
  # @return [true]
612
715
  #
613
- def assert_no_text(*args)
614
- _verify_text(args) do |count, query|
615
- 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?)
616
719
  raise Capybara::ExpectationNotMet, query.negative_failure_message
617
720
  end
618
721
  end
@@ -622,10 +725,6 @@ module Capybara
622
725
  # Checks if the page or current node has the given text content,
623
726
  # ignoring any HTML tags.
624
727
  #
625
- # Whitespaces are normalized in both node's text and passed text parameter.
626
- # Note that whitespace isn't normalized in passed regexp as normalizing whitespace
627
- # in regexp isn't easy and doesn't seem to be worth it.
628
- #
629
728
  # By default it will check if the text occurs at least once,
630
729
  # but a different number can be specified.
631
730
  #
@@ -636,10 +735,8 @@ module Capybara
636
735
  # @macro text_query_params
637
736
  # @return [Boolean] Whether it exists
638
737
  #
639
- def has_text?(*args)
640
- assert_text(*args)
641
- rescue Capybara::ExpectationNotMet
642
- false
738
+ def has_text?(*args, **options)
739
+ make_predicate(options) { assert_text(*args, **options) }
643
740
  end
644
741
  alias_method :has_content?, :has_text?
645
742
 
@@ -650,52 +747,161 @@ module Capybara
650
747
  # @macro text_query_params
651
748
  # @return [Boolean] Whether it doesn't exist
652
749
  #
653
- def has_no_text?(*args)
654
- assert_no_text(*args)
655
- rescue Capybara::ExpectationNotMet
656
- false
750
+ def has_no_text?(*args, **options)
751
+ make_predicate(options) { assert_no_text(*args, **options) }
657
752
  end
658
753
  alias_method :has_no_content?, :has_no_text?
659
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
+
660
841
  def ==(other)
661
842
  eql?(other) || (other.respond_to?(:base) && base == other.base)
662
843
  end
663
844
 
664
845
  private
665
846
 
666
- def _verify_selector_result(query_args, optional_filter_block)
667
- query_args = _set_query_session_options(*query_args)
668
- query = Capybara::Queries::SelectorQuery.new(*query_args, &optional_filter_block)
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) }
856
+ end
857
+ end
858
+
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)
669
868
  synchronize(query.wait) do
670
- result = query.resolve_for(self)
671
- yield result, query
869
+ yield query.resolve_for(self), query
672
870
  end
673
871
  true
674
872
  end
675
873
 
676
874
  def _verify_match_result(query_args, optional_filter_block)
677
- query_args = _set_query_session_options(*query_args)
678
- query = Capybara::Queries::MatchQuery.new(*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)
679
877
  synchronize(query.wait) do
680
- result = query.resolve_for(query_scope)
681
- yield result
878
+ yield query.resolve_for(parent || session&.document || query_scope)
682
879
  end
683
880
  true
684
881
  end
685
882
 
686
- def _verify_text(query_args)
687
- query_args = _set_query_session_options(*query_args)
688
- query = Capybara::Queries::TextQuery.new(*query_args)
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)
689
886
  synchronize(query.wait) do
690
- count = query.resolve_for(self)
691
- yield(count, query)
887
+ yield query.resolve_for(self), query
692
888
  end
693
889
  true
694
890
  end
695
891
 
696
- def _set_query_session_options(*query_args, **query_options)
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)
697
896
  query_options[:session_options] = session_options
698
- query_args.push(query_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
699
905
  end
700
906
  end
701
907
  end