capybara 3.32.2

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