capybara 3.8.1 → 3.33.0

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