capybara 3.32.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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