capybara 3.3.0 → 3.40.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (308) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +1 -0
  3. data/History.md +803 -13
  4. data/License.txt +1 -1
  5. data/README.md +257 -84
  6. data/lib/capybara/config.rb +25 -9
  7. data/lib/capybara/cucumber.rb +1 -1
  8. data/lib/capybara/driver/base.rb +17 -3
  9. data/lib/capybara/driver/node.rb +31 -6
  10. data/lib/capybara/dsl.rb +9 -7
  11. data/lib/capybara/helpers.rb +31 -7
  12. data/lib/capybara/minitest/spec.rb +180 -88
  13. data/lib/capybara/minitest.rb +262 -149
  14. data/lib/capybara/node/actions.rb +202 -116
  15. data/lib/capybara/node/base.rb +34 -19
  16. data/lib/capybara/node/document.rb +14 -2
  17. data/lib/capybara/node/document_matchers.rb +10 -12
  18. data/lib/capybara/node/element.rb +269 -115
  19. data/lib/capybara/node/finders.rb +99 -77
  20. data/lib/capybara/node/matchers.rb +327 -151
  21. data/lib/capybara/node/simple.rb +48 -13
  22. data/lib/capybara/node/whitespace_normalizer.rb +81 -0
  23. data/lib/capybara/queries/active_element_query.rb +18 -0
  24. data/lib/capybara/queries/ancestor_query.rb +8 -9
  25. data/lib/capybara/queries/base_query.rb +23 -16
  26. data/lib/capybara/queries/current_path_query.rb +16 -6
  27. data/lib/capybara/queries/match_query.rb +1 -0
  28. data/lib/capybara/queries/selector_query.rb +587 -130
  29. data/lib/capybara/queries/sibling_query.rb +8 -6
  30. data/lib/capybara/queries/style_query.rb +6 -2
  31. data/lib/capybara/queries/text_query.rb +28 -14
  32. data/lib/capybara/queries/title_query.rb +2 -2
  33. data/lib/capybara/rack_test/browser.rb +92 -25
  34. data/lib/capybara/rack_test/driver.rb +16 -7
  35. data/lib/capybara/rack_test/errors.rb +6 -0
  36. data/lib/capybara/rack_test/form.rb +68 -41
  37. data/lib/capybara/rack_test/node.rb +106 -39
  38. data/lib/capybara/rails.rb +1 -1
  39. data/lib/capybara/registration_container.rb +41 -0
  40. data/lib/capybara/registrations/drivers.rb +42 -0
  41. data/lib/capybara/registrations/patches/puma_ssl.rb +29 -0
  42. data/lib/capybara/registrations/servers.rb +66 -0
  43. data/lib/capybara/result.rb +75 -52
  44. data/lib/capybara/rspec/features.rb +7 -7
  45. data/lib/capybara/rspec/matcher_proxies.rb +39 -18
  46. data/lib/capybara/rspec/matchers/base.rb +113 -0
  47. data/lib/capybara/rspec/matchers/become_closed.rb +33 -0
  48. data/lib/capybara/rspec/matchers/compound.rb +88 -0
  49. data/lib/capybara/rspec/matchers/count_sugar.rb +37 -0
  50. data/lib/capybara/rspec/matchers/have_ancestor.rb +28 -0
  51. data/lib/capybara/rspec/matchers/have_current_path.rb +29 -0
  52. data/lib/capybara/rspec/matchers/have_selector.rb +69 -0
  53. data/lib/capybara/rspec/matchers/have_sibling.rb +27 -0
  54. data/lib/capybara/rspec/matchers/have_text.rb +33 -0
  55. data/lib/capybara/rspec/matchers/have_title.rb +29 -0
  56. data/lib/capybara/rspec/matchers/match_selector.rb +27 -0
  57. data/lib/capybara/rspec/matchers/match_style.rb +43 -0
  58. data/lib/capybara/rspec/matchers/spatial_sugar.rb +39 -0
  59. data/lib/capybara/rspec/matchers.rb +141 -339
  60. data/lib/capybara/rspec.rb +2 -0
  61. data/lib/capybara/selector/builders/css_builder.rb +84 -0
  62. data/lib/capybara/selector/builders/xpath_builder.rb +71 -0
  63. data/lib/capybara/selector/css.rb +27 -25
  64. data/lib/capybara/selector/definition/button.rb +68 -0
  65. data/lib/capybara/selector/definition/checkbox.rb +26 -0
  66. data/lib/capybara/selector/definition/css.rb +10 -0
  67. data/lib/capybara/selector/definition/datalist_input.rb +35 -0
  68. data/lib/capybara/selector/definition/datalist_option.rb +25 -0
  69. data/lib/capybara/selector/definition/element.rb +28 -0
  70. data/lib/capybara/selector/definition/field.rb +40 -0
  71. data/lib/capybara/selector/definition/fieldset.rb +14 -0
  72. data/lib/capybara/selector/definition/file_field.rb +13 -0
  73. data/lib/capybara/selector/definition/fillable_field.rb +33 -0
  74. data/lib/capybara/selector/definition/frame.rb +17 -0
  75. data/lib/capybara/selector/definition/id.rb +6 -0
  76. data/lib/capybara/selector/definition/label.rb +62 -0
  77. data/lib/capybara/selector/definition/link.rb +55 -0
  78. data/lib/capybara/selector/definition/link_or_button.rb +16 -0
  79. data/lib/capybara/selector/definition/option.rb +27 -0
  80. data/lib/capybara/selector/definition/radio_button.rb +27 -0
  81. data/lib/capybara/selector/definition/select.rb +81 -0
  82. data/lib/capybara/selector/definition/table.rb +109 -0
  83. data/lib/capybara/selector/definition/table_row.rb +21 -0
  84. data/lib/capybara/selector/definition/xpath.rb +5 -0
  85. data/lib/capybara/selector/definition.rb +280 -0
  86. data/lib/capybara/selector/filter.rb +1 -0
  87. data/lib/capybara/selector/filter_set.rb +73 -25
  88. data/lib/capybara/selector/filters/base.rb +24 -5
  89. data/lib/capybara/selector/filters/expression_filter.rb +3 -3
  90. data/lib/capybara/selector/filters/locator_filter.rb +29 -0
  91. data/lib/capybara/selector/filters/node_filter.rb +16 -2
  92. data/lib/capybara/selector/regexp_disassembler.rb +211 -0
  93. data/lib/capybara/selector/selector.rb +85 -348
  94. data/lib/capybara/selector/xpath_extensions.rb +17 -0
  95. data/lib/capybara/selector.rb +474 -447
  96. data/lib/capybara/selenium/atoms/getAttribute.min.js +1 -0
  97. data/lib/capybara/selenium/atoms/isDisplayed.min.js +1 -0
  98. data/lib/capybara/selenium/atoms/src/getAttribute.js +161 -0
  99. data/lib/capybara/selenium/atoms/src/isDisplayed.js +454 -0
  100. data/lib/capybara/selenium/driver.rb +255 -143
  101. data/lib/capybara/selenium/driver_specializations/chrome_driver.rb +93 -11
  102. data/lib/capybara/selenium/driver_specializations/edge_driver.rb +128 -0
  103. data/lib/capybara/selenium/driver_specializations/firefox_driver.rb +84 -0
  104. data/lib/capybara/selenium/driver_specializations/internet_explorer_driver.rb +26 -0
  105. data/lib/capybara/selenium/driver_specializations/safari_driver.rb +24 -0
  106. data/lib/capybara/selenium/extensions/file_input_click_emulation.rb +34 -0
  107. data/lib/capybara/selenium/extensions/find.rb +110 -0
  108. data/lib/capybara/selenium/extensions/html5_drag.rb +229 -0
  109. data/lib/capybara/selenium/extensions/modifier_keys_stack.rb +28 -0
  110. data/lib/capybara/selenium/extensions/scroll.rb +76 -0
  111. data/lib/capybara/selenium/node.rb +436 -134
  112. data/lib/capybara/selenium/nodes/chrome_node.rb +125 -0
  113. data/lib/capybara/selenium/nodes/edge_node.rb +110 -0
  114. data/lib/capybara/selenium/nodes/firefox_node.rb +136 -0
  115. data/lib/capybara/selenium/nodes/ie_node.rb +22 -0
  116. data/lib/capybara/selenium/nodes/safari_node.rb +118 -0
  117. data/lib/capybara/selenium/patches/atoms.rb +18 -0
  118. data/lib/capybara/selenium/patches/is_displayed.rb +16 -0
  119. data/lib/capybara/selenium/patches/logs.rb +45 -0
  120. data/lib/capybara/selenium/patches/pause_duration_fix.rb +9 -0
  121. data/lib/capybara/selenium/patches/persistent_client.rb +20 -0
  122. data/lib/capybara/server/animation_disabler.rb +56 -19
  123. data/lib/capybara/server/checker.rb +9 -3
  124. data/lib/capybara/server/middleware.rb +28 -12
  125. data/lib/capybara/server.rb +33 -10
  126. data/lib/capybara/session/config.rb +34 -10
  127. data/lib/capybara/session/matchers.rb +23 -16
  128. data/lib/capybara/session.rb +230 -170
  129. data/lib/capybara/spec/public/jquery.js +5 -5
  130. data/lib/capybara/spec/public/offset.js +6 -0
  131. data/lib/capybara/spec/public/test.js +121 -8
  132. data/lib/capybara/spec/session/accept_alert_spec.rb +11 -11
  133. data/lib/capybara/spec/session/accept_confirm_spec.rb +3 -3
  134. data/lib/capybara/spec/session/accept_prompt_spec.rb +9 -10
  135. data/lib/capybara/spec/session/active_element_spec.rb +31 -0
  136. data/lib/capybara/spec/session/all_spec.rb +127 -40
  137. data/lib/capybara/spec/session/ancestor_spec.rb +24 -19
  138. data/lib/capybara/spec/session/assert_all_of_selectors_spec.rb +67 -38
  139. data/lib/capybara/spec/session/assert_current_path_spec.rb +21 -18
  140. data/lib/capybara/spec/session/assert_selector_spec.rb +52 -58
  141. data/lib/capybara/spec/session/assert_style_spec.rb +7 -7
  142. data/lib/capybara/spec/session/assert_text_spec.rb +74 -50
  143. data/lib/capybara/spec/session/assert_title_spec.rb +12 -12
  144. data/lib/capybara/spec/session/attach_file_spec.rb +126 -72
  145. data/lib/capybara/spec/session/body_spec.rb +6 -6
  146. data/lib/capybara/spec/session/check_spec.rb +102 -47
  147. data/lib/capybara/spec/session/choose_spec.rb +58 -32
  148. data/lib/capybara/spec/session/click_button_spec.rb +219 -163
  149. data/lib/capybara/spec/session/click_link_or_button_spec.rb +49 -23
  150. data/lib/capybara/spec/session/click_link_spec.rb +77 -54
  151. data/lib/capybara/spec/session/current_scope_spec.rb +8 -8
  152. data/lib/capybara/spec/session/current_url_spec.rb +38 -29
  153. data/lib/capybara/spec/session/dismiss_confirm_spec.rb +3 -3
  154. data/lib/capybara/spec/session/dismiss_prompt_spec.rb +2 -2
  155. data/lib/capybara/spec/session/element/assert_match_selector_spec.rb +8 -8
  156. data/lib/capybara/spec/session/element/match_css_spec.rb +16 -10
  157. data/lib/capybara/spec/session/element/match_xpath_spec.rb +6 -6
  158. data/lib/capybara/spec/session/element/matches_selector_spec.rb +68 -56
  159. data/lib/capybara/spec/session/evaluate_async_script_spec.rb +7 -7
  160. data/lib/capybara/spec/session/evaluate_script_spec.rb +28 -8
  161. data/lib/capybara/spec/session/execute_script_spec.rb +8 -7
  162. data/lib/capybara/spec/session/fill_in_spec.rb +101 -46
  163. data/lib/capybara/spec/session/find_button_spec.rb +23 -23
  164. data/lib/capybara/spec/session/find_by_id_spec.rb +7 -7
  165. data/lib/capybara/spec/session/find_field_spec.rb +32 -30
  166. data/lib/capybara/spec/session/find_link_spec.rb +31 -21
  167. data/lib/capybara/spec/session/find_spec.rb +244 -141
  168. data/lib/capybara/spec/session/first_spec.rb +43 -43
  169. data/lib/capybara/spec/session/frame/frame_title_spec.rb +5 -5
  170. data/lib/capybara/spec/session/frame/frame_url_spec.rb +5 -5
  171. data/lib/capybara/spec/session/frame/switch_to_frame_spec.rb +30 -18
  172. data/lib/capybara/spec/session/frame/within_frame_spec.rb +45 -18
  173. data/lib/capybara/spec/session/go_back_spec.rb +1 -1
  174. data/lib/capybara/spec/session/go_forward_spec.rb +1 -1
  175. data/lib/capybara/spec/session/has_all_selectors_spec.rb +23 -23
  176. data/lib/capybara/spec/session/has_ancestor_spec.rb +46 -0
  177. data/lib/capybara/spec/session/has_any_selectors_spec.rb +29 -0
  178. data/lib/capybara/spec/session/has_button_spec.rb +94 -13
  179. data/lib/capybara/spec/session/has_css_spec.rb +272 -132
  180. data/lib/capybara/spec/session/has_current_path_spec.rb +50 -35
  181. data/lib/capybara/spec/session/has_element_spec.rb +47 -0
  182. data/lib/capybara/spec/session/has_field_spec.rb +137 -58
  183. data/lib/capybara/spec/session/has_link_spec.rb +44 -4
  184. data/lib/capybara/spec/session/has_none_selectors_spec.rb +31 -31
  185. data/lib/capybara/spec/session/has_select_spec.rb +84 -50
  186. data/lib/capybara/spec/session/has_selector_spec.rb +111 -71
  187. data/lib/capybara/spec/session/has_sibling_spec.rb +50 -0
  188. data/lib/capybara/spec/session/has_table_spec.rb +181 -4
  189. data/lib/capybara/spec/session/has_text_spec.rb +101 -53
  190. data/lib/capybara/spec/session/has_title_spec.rb +19 -14
  191. data/lib/capybara/spec/session/has_xpath_spec.rb +56 -38
  192. data/lib/capybara/spec/session/headers_spec.rb +1 -1
  193. data/lib/capybara/spec/session/html_spec.rb +13 -6
  194. data/lib/capybara/spec/session/matches_style_spec.rb +37 -0
  195. data/lib/capybara/spec/session/node_spec.rb +894 -142
  196. data/lib/capybara/spec/session/node_wrapper_spec.rb +10 -7
  197. data/lib/capybara/spec/session/refresh_spec.rb +9 -7
  198. data/lib/capybara/spec/session/reset_session_spec.rb +63 -35
  199. data/lib/capybara/spec/session/response_code_spec.rb +1 -1
  200. data/lib/capybara/spec/session/save_and_open_page_spec.rb +2 -2
  201. data/lib/capybara/spec/session/save_and_open_screenshot_spec.rb +2 -2
  202. data/lib/capybara/spec/session/save_page_spec.rb +37 -37
  203. data/lib/capybara/spec/session/save_screenshot_spec.rb +10 -10
  204. data/lib/capybara/spec/session/screenshot_spec.rb +2 -2
  205. data/lib/capybara/spec/session/scroll_spec.rb +119 -0
  206. data/lib/capybara/spec/session/select_spec.rb +85 -85
  207. data/lib/capybara/spec/session/selectors_spec.rb +49 -18
  208. data/lib/capybara/spec/session/sibling_spec.rb +9 -9
  209. data/lib/capybara/spec/session/text_spec.rb +25 -24
  210. data/lib/capybara/spec/session/title_spec.rb +7 -6
  211. data/lib/capybara/spec/session/uncheck_spec.rb +33 -21
  212. data/lib/capybara/spec/session/unselect_spec.rb +37 -37
  213. data/lib/capybara/spec/session/visit_spec.rb +68 -49
  214. data/lib/capybara/spec/session/window/become_closed_spec.rb +20 -17
  215. data/lib/capybara/spec/session/window/current_window_spec.rb +1 -1
  216. data/lib/capybara/spec/session/window/switch_to_window_spec.rb +20 -16
  217. data/lib/capybara/spec/session/window/window_opened_by_spec.rb +6 -2
  218. data/lib/capybara/spec/session/window/window_spec.rb +62 -63
  219. data/lib/capybara/spec/session/window/windows_spec.rb +5 -1
  220. data/lib/capybara/spec/session/window/within_window_spec.rb +14 -14
  221. data/lib/capybara/spec/session/within_spec.rb +79 -42
  222. data/lib/capybara/spec/spec_helper.rb +41 -53
  223. data/lib/capybara/spec/test_app.rb +132 -43
  224. data/lib/capybara/spec/views/animated.erb +49 -0
  225. data/lib/capybara/spec/views/form.erb +139 -42
  226. data/lib/capybara/spec/views/frame_child.erb +4 -3
  227. data/lib/capybara/spec/views/frame_one.erb +2 -1
  228. data/lib/capybara/spec/views/frame_parent.erb +1 -1
  229. data/lib/capybara/spec/views/frame_two.erb +1 -1
  230. data/lib/capybara/spec/views/initial_alert.erb +2 -1
  231. data/lib/capybara/spec/views/layout.erb +10 -0
  232. data/lib/capybara/spec/views/obscured.erb +47 -0
  233. data/lib/capybara/spec/views/offset.erb +33 -0
  234. data/lib/capybara/spec/views/path.erb +2 -2
  235. data/lib/capybara/spec/views/popup_one.erb +1 -1
  236. data/lib/capybara/spec/views/popup_two.erb +1 -1
  237. data/lib/capybara/spec/views/react.erb +45 -0
  238. data/lib/capybara/spec/views/scroll.erb +21 -0
  239. data/lib/capybara/spec/views/spatial.erb +31 -0
  240. data/lib/capybara/spec/views/tables.erb +67 -0
  241. data/lib/capybara/spec/views/with_animation.erb +39 -4
  242. data/lib/capybara/spec/views/with_base_tag.erb +2 -2
  243. data/lib/capybara/spec/views/with_dragula.erb +24 -0
  244. data/lib/capybara/spec/views/with_fixed_header_footer.erb +2 -1
  245. data/lib/capybara/spec/views/with_hover.erb +3 -2
  246. data/lib/capybara/spec/views/with_hover1.erb +10 -0
  247. data/lib/capybara/spec/views/with_html.erb +37 -9
  248. data/lib/capybara/spec/views/with_html5_svg.erb +20 -0
  249. data/lib/capybara/spec/views/with_jquery_animation.erb +24 -0
  250. data/lib/capybara/spec/views/with_js.erb +26 -5
  251. data/lib/capybara/spec/views/with_jstree.erb +26 -0
  252. data/lib/capybara/spec/views/with_namespace.erb +1 -0
  253. data/lib/capybara/spec/views/with_scope.erb +2 -2
  254. data/lib/capybara/spec/views/with_scope_other.erb +6 -0
  255. data/lib/capybara/spec/views/with_shadow.erb +31 -0
  256. data/lib/capybara/spec/views/with_slow_unload.erb +2 -1
  257. data/lib/capybara/spec/views/with_sortable_js.erb +21 -0
  258. data/lib/capybara/spec/views/with_unload_alert.erb +1 -0
  259. data/lib/capybara/spec/views/with_windows.erb +1 -1
  260. data/lib/capybara/spec/views/within_frames.erb +1 -1
  261. data/lib/capybara/version.rb +1 -1
  262. data/lib/capybara/window.rb +19 -25
  263. data/lib/capybara.rb +126 -111
  264. data/spec/basic_node_spec.rb +59 -34
  265. data/spec/capybara_spec.rb +56 -44
  266. data/spec/counter_spec.rb +35 -0
  267. data/spec/css_builder_spec.rb +101 -0
  268. data/spec/css_splitter_spec.rb +8 -8
  269. data/spec/dsl_spec.rb +79 -52
  270. data/spec/filter_set_spec.rb +9 -9
  271. data/spec/fixtures/selenium_driver_rspec_failure.rb +4 -4
  272. data/spec/fixtures/selenium_driver_rspec_success.rb +4 -4
  273. data/spec/minitest_spec.rb +45 -7
  274. data/spec/minitest_spec_spec.rb +87 -64
  275. data/spec/per_session_config_spec.rb +6 -6
  276. data/spec/rack_test_spec.rb +172 -116
  277. data/spec/regexp_dissassembler_spec.rb +250 -0
  278. data/spec/result_spec.rb +80 -72
  279. data/spec/rspec/features_spec.rb +21 -16
  280. data/spec/rspec/scenarios_spec.rb +10 -6
  281. data/spec/rspec/shared_spec_matchers.rb +407 -365
  282. data/spec/rspec/views_spec.rb +3 -3
  283. data/spec/rspec_matchers_spec.rb +35 -10
  284. data/spec/rspec_spec.rb +63 -41
  285. data/spec/sauce_spec_chrome.rb +43 -0
  286. data/spec/selector_spec.rb +334 -89
  287. data/spec/selenium_spec_chrome.rb +176 -62
  288. data/spec/selenium_spec_chrome_remote.rb +54 -14
  289. data/spec/selenium_spec_edge.rb +41 -8
  290. data/spec/selenium_spec_firefox.rb +228 -0
  291. data/spec/selenium_spec_firefox_remote.rb +94 -0
  292. data/spec/selenium_spec_ie.rb +129 -11
  293. data/spec/selenium_spec_safari.rb +162 -0
  294. data/spec/server_spec.rb +171 -97
  295. data/spec/session_spec.rb +34 -18
  296. data/spec/shared_selenium_node.rb +79 -0
  297. data/spec/shared_selenium_session.rb +344 -80
  298. data/spec/spec_helper.rb +124 -2
  299. data/spec/whitespace_normalizer_spec.rb +54 -0
  300. data/spec/xpath_builder_spec.rb +93 -0
  301. metadata +326 -28
  302. data/lib/capybara/rspec/compound.rb +0 -94
  303. data/lib/capybara/selenium/driver_specializations/marionette_driver.rb +0 -31
  304. data/lib/capybara/selenium/nodes/marionette_node.rb +0 -31
  305. data/lib/capybara/spec/session/has_style_spec.rb +0 -25
  306. data/lib/capybara/spec/session/source_spec.rb +0 -0
  307. data/lib/capybara/spec/views/with_title.erb +0 -5
  308. data/spec/selenium_spec_marionette.rb +0 -167
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Capybara
4
+ module RSpecMatchers
5
+ module SpatialSugar
6
+ def above(el)
7
+ options[:above] = el
8
+ self
9
+ end
10
+
11
+ def below(el)
12
+ options[:below] = el
13
+ self
14
+ end
15
+
16
+ def left_of(el)
17
+ options[:left_of] = el
18
+ self
19
+ end
20
+
21
+ def right_of(el)
22
+ options[:right_of] = el
23
+ self
24
+ end
25
+
26
+ def near(el)
27
+ options[:near] = el
28
+ self
29
+ end
30
+
31
+ private
32
+
33
+ def options
34
+ # (@args.last.is_a?(Hash) ? @args : @args.push({})).last
35
+ @kw_args
36
+ end
37
+ end
38
+ end
39
+ end
@@ -1,405 +1,207 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'capybara/rspec/compound'
3
+ require 'capybara/rspec/matchers/have_selector'
4
+ require 'capybara/rspec/matchers/have_ancestor'
5
+ require 'capybara/rspec/matchers/have_sibling'
6
+ require 'capybara/rspec/matchers/match_selector'
7
+ require 'capybara/rspec/matchers/have_current_path'
8
+ require 'capybara/rspec/matchers/match_style'
9
+ require 'capybara/rspec/matchers/have_text'
10
+ require 'capybara/rspec/matchers/have_title'
11
+ require 'capybara/rspec/matchers/become_closed'
4
12
 
5
13
  module Capybara
6
14
  module RSpecMatchers
7
- class Matcher
8
- include ::Capybara::RSpecMatchers::Compound if defined?(::Capybara::RSpecMatchers::Compound)
9
-
10
- attr_reader :failure_message, :failure_message_when_negated
11
-
12
- def wrap(actual)
13
- actual = actual.to_capybara_node if actual.respond_to?(:to_capybara_node)
14
- @context_el = if actual.respond_to?(:has_selector?)
15
- actual
16
- else
17
- Capybara.string(actual.to_s)
18
- end
19
- end
20
-
21
- private
22
-
23
- def wrap_matches?(actual)
24
- yield(wrap(actual))
25
- rescue Capybara::ExpectationNotMet => e
26
- @failure_message = e.message
27
- false
28
- end
29
-
30
- def wrap_does_not_match?(actual)
31
- yield(wrap(actual))
32
- rescue Capybara::ExpectationNotMet => e
33
- @failure_message_when_negated = e.message
34
- false
35
- end
36
-
37
- def session_query_args
38
- if @args.last.is_a? Hash
39
- @args.last[:session_options] = session_options
40
- else
41
- @args.push(session_options: session_options)
42
- end
43
- @args
44
- end
45
-
46
- def session_options
47
- @context_el ||= nil
48
- if @context_el.respond_to? :session_options
49
- @context_el.session_options
50
- elsif @context_el.respond_to? :current_scope
51
- @context_el.current_scope.session_options
52
- else
53
- Capybara.session_options
54
- end
55
- end
56
- end
57
-
58
- class HaveSelector < Matcher
59
- def initialize(*args, &filter_block)
60
- @args = args
61
- @filter_block = filter_block
62
- end
63
-
64
- def matches?(actual)
65
- wrap_matches?(actual) { |el| el.assert_selector(*@args, &@filter_block) }
66
- end
67
-
68
- def does_not_match?(actual)
69
- wrap_does_not_match?(actual) { |el| el.assert_no_selector(*@args, &@filter_block) }
70
- end
71
-
72
- def description
73
- "have #{query.description}"
74
- end
75
-
76
- def query
77
- @query ||= Capybara::Queries::SelectorQuery.new(*session_query_args, &@filter_block)
78
- end
79
- end
80
-
81
- class HaveAllSelectors < Matcher
82
- def initialize(*args, &filter_block)
83
- @args = args
84
- @filter_block = filter_block
85
- end
86
-
87
- def matches?(actual)
88
- wrap_matches?(actual) { |el| el.assert_all_of_selectors(*@args, &@filter_block) }
89
- end
90
-
91
- def does_not_match?(_actual)
92
- raise ArgumentError, "The have_all_selectors matcher does not support use with not_to/should_not"
93
- end
94
-
95
- def description
96
- "have all selectors"
97
- end
15
+ # RSpec matcher for whether the element(s) matching a given selector exist.
16
+ #
17
+ # @see Capybara::Node::Matchers#assert_selector
18
+ def have_selector(...)
19
+ Matchers::HaveSelector.new(...)
98
20
  end
99
21
 
100
- class HaveNoSelectors < Matcher
101
- def initialize(*args, &filter_block)
102
- @args = args
103
- @filter_block = filter_block
104
- end
105
-
106
- def matches?(actual)
107
- wrap_matches?(actual) { |el| el.assert_none_of_selectors(*@args, &@filter_block) }
108
- end
109
-
110
- def does_not_match?(_actual)
111
- raise ArgumentError, "The have_none_of_selectors matcher does not support use with not_to/should_not"
112
- end
113
-
114
- def description
115
- "have no selectors"
116
- end
22
+ # RSpec matcher for whether the element(s) matching a group of selectors exist.
23
+ #
24
+ # @see Capybara::Node::Matchers#assert_all_of_selectors
25
+ def have_all_of_selectors(...)
26
+ Matchers::HaveAllSelectors.new(...)
117
27
  end
118
28
 
119
- class MatchSelector < HaveSelector
120
- def matches?(actual)
121
- wrap_matches?(actual) { |el| el.assert_matches_selector(*@args, &@filter_block) }
122
- end
123
-
124
- def does_not_match?(actual)
125
- wrap_does_not_match?(actual) { |el| el.assert_not_matches_selector(*@args, &@filter_block) }
126
- end
127
-
128
- def description
129
- "match #{query.description}"
130
- end
131
-
132
- def query
133
- @query ||= Capybara::Queries::MatchQuery.new(*session_query_args, &@filter_block)
134
- end
29
+ # RSpec matcher for whether no element(s) matching a group of selectors exist.
30
+ #
31
+ # @see Capybara::Node::Matchers#assert_none_of_selectors
32
+ def have_none_of_selectors(...)
33
+ Matchers::HaveNoSelectors.new(...)
135
34
  end
136
35
 
137
- class HaveText < Matcher
138
- def initialize(*args)
139
- @args = args.dup
140
- @content = args[0].is_a?(Symbol) ? args[1] : args[0]
141
- end
142
-
143
- def matches?(actual)
144
- wrap_matches?(actual) { |el| el.assert_text(*@args) }
145
- end
146
-
147
- def does_not_match?(actual)
148
- wrap_does_not_match?(actual) { |el| el.assert_no_text(*@args) }
149
- end
150
-
151
- def description
152
- "text #{format(@content)}"
153
- end
154
-
155
- def format(content)
156
- content.inspect
157
- end
36
+ # RSpec matcher for whether the element(s) matching any of a group of selectors exist.
37
+ #
38
+ # @see Capybara::Node::Matchers#assert_any_of_selectors
39
+ def have_any_of_selectors(...)
40
+ Matchers::HaveAnySelectors.new(...)
158
41
  end
159
42
 
160
- class HaveTitle < Matcher
161
- def initialize(*args)
162
- @args = args
163
-
164
- # are set just for backwards compatability
165
- @title = args.first
166
- end
167
-
168
- def matches?(actual)
169
- wrap_matches?(actual) { |el| el.assert_title(*@args) }
170
- end
171
-
172
- def does_not_match?(actual)
173
- wrap_does_not_match?(actual) { |el| el.assert_no_title(*@args) }
174
- end
175
-
176
- def description
177
- "have title #{@title.inspect}"
178
- end
43
+ # RSpec matcher for whether the current element matches a given selector.
44
+ #
45
+ # @see Capybara::Node::Matchers#assert_matches_selector
46
+ def match_selector(...)
47
+ Matchers::MatchSelector.new(...)
179
48
  end
180
49
 
181
- class HaveCurrentPath < Matcher
182
- def initialize(*args)
183
- @args = args
184
- @current_path = args.first
185
- end
186
-
187
- def matches?(actual)
188
- wrap_matches?(actual) { |el| el.assert_current_path(*@args) }
50
+ %i[css xpath].each do |selector|
51
+ define_method "have_#{selector}" do |expr, **options, &optional_filter_block|
52
+ Matchers::HaveSelector.new(selector, expr, **options, &optional_filter_block)
189
53
  end
190
54
 
191
- def does_not_match?(actual)
192
- wrap_does_not_match?(actual) { |el| el.assert_no_current_path(*@args) }
193
- end
194
-
195
- def description
196
- "have current path #{@current_path.inspect}"
55
+ define_method "match_#{selector}" do |expr, **options, &optional_filter_block|
56
+ Matchers::MatchSelector.new(selector, expr, **options, &optional_filter_block)
197
57
  end
198
58
  end
199
59
 
200
- class NegatedMatcher
201
- include ::Capybara::RSpecMatchers::Compound if defined?(::Capybara::RSpecMatchers::Compound)
202
-
203
- def initialize(matcher)
204
- @matcher = matcher
205
- end
60
+ # @!method have_xpath(xpath, **options, &optional_filter_block)
61
+ # RSpec matcher for whether elements(s) matching a given xpath selector exist.
62
+ #
63
+ # @see Capybara::Node::Matchers#has_xpath?
206
64
 
207
- def matches?(actual)
208
- @matcher.does_not_match?(actual)
209
- end
210
-
211
- def does_not_match?(actual)
212
- @matcher.matches?(actual)
213
- end
65
+ # @!method have_css(css, **options, &optional_filter_block)
66
+ # RSpec matcher for whether elements(s) matching a given css selector exist
67
+ #
68
+ # @see Capybara::Node::Matchers#has_css?
214
69
 
215
- def description
216
- "not #{@matcher.description}"
217
- end
70
+ # @!method match_xpath(xpath, **options, &optional_filter_block)
71
+ # RSpec matcher for whether the current element matches a given xpath selector.
72
+ #
73
+ # @see Capybara::Node::Matchers#matches_xpath?
218
74
 
219
- def failure_message
220
- @matcher.failure_message_when_negated
221
- end
75
+ # @!method match_css(css, **options, &optional_filter_block)
76
+ # RSpec matcher for whether the current element matches a given css selector.
77
+ #
78
+ # @see Capybara::Node::Matchers#matches_css?
222
79
 
223
- def failure_message_when_negated
224
- @matcher.failure_message
80
+ %i[link button field select table element].each do |selector|
81
+ define_method "have_#{selector}" do |locator = nil, **options, &optional_filter_block|
82
+ Matchers::HaveSelector.new(selector, locator, **options, &optional_filter_block)
225
83
  end
226
84
  end
227
85
 
228
- class HaveStyle < Matcher
229
- def initialize(*args)
230
- @args = args
231
- end
86
+ # @!method have_element(locator = nil, **options, &optional_filter_block)
87
+ # RSpec matcher for elements.
88
+ #
89
+ # @see Capybara::Node::Matchers#has_element?
232
90
 
233
- def matches?(actual)
234
- wrap_matches?(actual) { |el| el.assert_style(*@args) }
235
- end
91
+ # @!method have_link(locator = nil, **options, &optional_filter_block)
92
+ # RSpec matcher for links.
93
+ #
94
+ # @see Capybara::Node::Matchers#has_link?
236
95
 
237
- def does_not_match?(_actual)
238
- raise ArgumentError, "The have_style matcher does not support use with not_to/should_not"
239
- end
240
-
241
- def description
242
- "have style"
243
- end
244
- end
96
+ # @!method have_button(locator = nil, **options, &optional_filter_block)
97
+ # RSpec matcher for buttons.
98
+ #
99
+ # @see Capybara::Node::Matchers#has_button?
245
100
 
246
- class BecomeClosed
247
- def initialize(options)
248
- @options = options
249
- end
101
+ # @!method have_field(locator = nil, **options, &optional_filter_block)
102
+ # RSpec matcher for form fields.
103
+ #
104
+ # @see Capybara::Node::Matchers#has_field?
250
105
 
251
- def matches?(window)
252
- @window = window
253
- @wait_time = Capybara::Queries::BaseQuery.wait(@options, window.session.config.default_max_wait_time)
254
- timer = Capybara::Helpers.timer(expire_in: @wait_time)
255
- while window.exists?
256
- return false if timer.expired?
257
- sleep 0.05
258
- end
259
- true
260
- end
106
+ # @!method have_select(locator = nil, **options, &optional_filter_block)
107
+ # RSpec matcher for select elements.
108
+ #
109
+ # @see Capybara::Node::Matchers#has_select?
261
110
 
262
- def failure_message
263
- "expected #{@window.inspect} to become closed after #{@wait_time} seconds"
264
- end
111
+ # @!method have_table(locator = nil, **options, &optional_filter_block)
112
+ # RSpec matcher for table elements.
113
+ #
114
+ # @see Capybara::Node::Matchers#has_table?
265
115
 
266
- def failure_message_when_negated
267
- "expected #{@window.inspect} not to become closed after #{@wait_time} seconds"
116
+ %i[checked unchecked].each do |state|
117
+ define_method "have_#{state}_field" do |locator = nil, **options, &optional_filter_block|
118
+ Matchers::HaveSelector.new(:field, locator, **options.merge(state => true), &optional_filter_block)
268
119
  end
269
120
  end
270
121
 
271
- # RSpec matcher for whether the element(s) matching a given selector exist
272
- # See {Capybara::Node::Matcher#assert_selector}
273
- def have_selector(*args, &optional_filter_block)
274
- HaveSelector.new(*args, &optional_filter_block)
275
- end
276
-
277
- # RSpec matcher for whether the element(s) matching a group of selectors exist
278
- # See {Capybara::Node::Matcher#assert_all_of_selectors}
279
- def have_all_of_selectors(*args, &optional_filter_block)
280
- HaveAllSelectors.new(*args, &optional_filter_block)
281
- end
282
-
283
- # RSpec matcher for whether no element(s) matching a group of selectors exist
284
- # See {Capybara::Node::Matcher#assert_none_of_selectors}
285
- def have_none_of_selectors(*args, &optional_filter_block)
286
- HaveNoSelectors.new(*args, &optional_filter_block)
287
- end
288
-
289
- # RSpec matcher for whether the current element matches a given selector
290
- # See {Capybara::Node::Matchers#assert_matches_selector}
291
- def match_selector(*args, &optional_filter_block)
292
- MatchSelector.new(*args, &optional_filter_block)
293
- end
294
-
295
- # RSpec matcher for whether elements(s) matching a given xpath selector exist
296
- # See {Capybara::Node::Matchers#has_xpath?}
297
- def have_xpath(xpath, **options, &optional_filter_block)
298
- HaveSelector.new(:xpath, xpath, options, &optional_filter_block)
299
- end
300
-
301
- # RSpec matcher for whether the current element matches a given xpath selector
302
- def match_xpath(xpath, **options, &optional_filter_block)
303
- MatchSelector.new(:xpath, xpath, options, &optional_filter_block)
304
- end
305
-
306
- # RSpec matcher for whether elements(s) matching a given css selector exist
307
- # See {Capybara::Node::Matchers#has_css?}
308
- def have_css(css, **options, &optional_filter_block)
309
- HaveSelector.new(:css, css, options, &optional_filter_block)
310
- end
122
+ # @!method have_checked_field(locator = nil, **options, &optional_filter_block)
123
+ # RSpec matcher for checked fields.
124
+ #
125
+ # @see Capybara::Node::Matchers#has_checked_field?
311
126
 
312
- # RSpec matcher for whether the current element matches a given css selector
313
- def match_css(css, **options, &optional_filter_block)
314
- MatchSelector.new(:css, css, options, &optional_filter_block)
315
- end
127
+ # @!method have_unchecked_field(locator = nil, **options, &optional_filter_block)
128
+ # RSpec matcher for unchecked fields.
129
+ #
130
+ # @see Capybara::Node::Matchers#has_unchecked_field?
316
131
 
317
- # RSpec matcher for text content
318
- # See {Capybara::SessionMatchers#assert_text}
319
- def have_text(*args)
320
- HaveText.new(*args)
132
+ # RSpec matcher for text content.
133
+ #
134
+ # @see Capybara::Node::Matchers#assert_text
135
+ def have_text(text_or_type, *args, **options)
136
+ Matchers::HaveText.new(text_or_type, *args, **options)
321
137
  end
322
138
  alias_method :have_content, :have_text
323
139
 
324
140
  def have_title(title, **options)
325
- HaveTitle.new(title, options)
326
- end
327
-
328
- # RSpec matcher for the current path
329
- # See {Capybara::SessionMatchers#assert_current_path}
330
- def have_current_path(path, **options)
331
- HaveCurrentPath.new(path, options)
332
- end
333
-
334
- # RSpec matcher for links
335
- # See {Capybara::Node::Matchers#has_link?}
336
- def have_link(locator = nil, **options, &optional_filter_block)
337
- HaveSelector.new(:link, locator, options, &optional_filter_block)
338
- end
339
-
340
- # RSpec matcher for buttons
341
- # See {Capybara::Node::Matchers#has_button?}
342
- def have_button(locator = nil, **options, &optional_filter_block)
343
- HaveSelector.new(:button, locator, options, &optional_filter_block)
344
- end
345
-
346
- # RSpec matcher for links
347
- # See {Capybara::Node::Matchers#has_field?}
348
- def have_field(locator = nil, **options, &optional_filter_block)
349
- HaveSelector.new(:field, locator, options, &optional_filter_block)
350
- end
351
-
352
- # RSpec matcher for checked fields
353
- # See {Capybara::Node::Matchers#has_checked_field?}
354
- def have_checked_field(locator = nil, **options, &optional_filter_block)
355
- HaveSelector.new(:field, locator, options.merge(checked: true), &optional_filter_block)
141
+ Matchers::HaveTitle.new(title, **options)
356
142
  end
357
143
 
358
- # RSpec matcher for unchecked fields
359
- # See {Capybara::Node::Matchers#has_unchecked_field?}
360
- def have_unchecked_field(locator = nil, **options, &optional_filter_block)
361
- HaveSelector.new(:field, locator, options.merge(unchecked: true), &optional_filter_block)
144
+ # RSpec matcher for the current path.
145
+ #
146
+ # @see Capybara::SessionMatchers#assert_current_path
147
+ def have_current_path(path, **options, &optional_filter_block)
148
+ Matchers::HaveCurrentPath.new(path, **options, &optional_filter_block)
362
149
  end
363
150
 
364
- # RSpec matcher for select elements
365
- # See {Capybara::Node::Matchers#has_select?}
366
- def have_select(locator = nil, **options, &optional_filter_block)
367
- HaveSelector.new(:select, locator, options, &optional_filter_block)
151
+ # RSpec matcher for element style.
152
+ #
153
+ # @see Capybara::Node::Matchers#matches_style?
154
+ def match_style(styles = nil, **options)
155
+ styles, options = options, {} if styles.nil?
156
+ Matchers::MatchStyle.new(styles, **options)
368
157
  end
369
158
 
370
- # RSpec matcher for table elements
371
- # See {Capybara::Node::Matchers#has_table?}
372
- def have_table(locator = nil, **options, &optional_filter_block)
373
- HaveSelector.new(:table, locator, options, &optional_filter_block)
374
- end
375
-
376
- # RSpec matcher for element style
377
- # See {Capybara::Node::Matchers#has_style?}
378
- def have_style(styles, **options)
379
- HaveStyle.new(styles, options)
159
+ ##
160
+ # @deprecated
161
+ #
162
+ def have_style(styles = nil, **options)
163
+ Capybara::Helpers.warn "DEPRECATED: have_style is deprecated, please use match_style : #{Capybara::Helpers.filter_backtrace(caller)}"
164
+ match_style(styles, **options)
380
165
  end
381
166
 
382
- %w[selector css xpath text title current_path link button field checked_field unchecked_field select table].each do |matcher_type|
383
- define_method "have_no_#{matcher_type}" do |*args, &optional_filter_block|
384
- NegatedMatcher.new(send("have_#{matcher_type}", *args, &optional_filter_block))
167
+ %w[selector css xpath text title current_path link button
168
+ field checked_field unchecked_field select table
169
+ sibling ancestor element].each do |matcher_type|
170
+ define_method "have_no_#{matcher_type}" do |*args, **kw_args, &optional_filter_block|
171
+ Matchers::NegatedMatcher.new(send("have_#{matcher_type}", *args, **kw_args, &optional_filter_block))
385
172
  end
386
173
  end
387
174
  alias_method :have_no_content, :have_no_text
388
175
 
389
176
  %w[selector css xpath].each do |matcher_type|
390
- define_method "not_match_#{matcher_type}" do |*args, &optional_filter_block|
391
- NegatedMatcher.new(send("match_#{matcher_type}", *args, &optional_filter_block))
177
+ define_method "not_match_#{matcher_type}" do |*args, **kw_args, &optional_filter_block|
178
+ Matchers::NegatedMatcher.new(send("match_#{matcher_type}", *args, **kw_args, &optional_filter_block))
392
179
  end
393
180
  end
394
181
 
182
+ # RSpec matcher for whether sibling element(s) matching a given selector exist.
183
+ #
184
+ # @see Capybara::Node::Matchers#assert_sibling
185
+ def have_sibling(...)
186
+ Matchers::HaveSibling.new(...)
187
+ end
188
+
189
+ # RSpec matcher for whether ancestor element(s) matching a given selector exist.
190
+ #
191
+ # @see Capybara::Node::Matchers#assert_ancestor
192
+ def have_ancestor(...)
193
+ Matchers::HaveAncestor.new(...)
194
+ end
195
+
395
196
  ##
396
197
  # Wait for window to become closed.
198
+ #
397
199
  # @example
398
200
  # expect(window).to become_closed(wait: 0.8)
399
- # @param options [Hash] optional param
400
- # @option options [Numeric] :wait (Capybara.default_max_wait_time) Maximum wait time
201
+ #
202
+ # @option options [Numeric] :wait Maximum wait time. Defaults to {Capybara.configure default_max_wait_time}
401
203
  def become_closed(**options)
402
- BecomeClosed.new(options)
204
+ Matchers::BecomeClosed.new(options)
403
205
  end
404
206
  end
405
207
  end
@@ -9,6 +9,8 @@ require 'capybara/rspec/matcher_proxies'
9
9
  RSpec.configure do |config|
10
10
  config.include Capybara::DSL, type: :feature
11
11
  config.include Capybara::RSpecMatchers, type: :feature
12
+ config.include Capybara::DSL, type: :system
13
+ config.include Capybara::RSpecMatchers, type: :system
12
14
  config.include Capybara::RSpecMatchers, type: :view
13
15
 
14
16
  # The before and after blocks must run instantaneously, because Capybara
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'xpath'
4
+
5
+ module Capybara
6
+ class Selector
7
+ # @api private
8
+ class CSSBuilder
9
+ def initialize(expression)
10
+ @expression = expression || ''
11
+ end
12
+
13
+ attr_reader :expression
14
+
15
+ def add_attribute_conditions(**attributes)
16
+ @expression = attributes.inject(expression) do |css, (name, value)|
17
+ conditions = if name == :class
18
+ class_conditions(value)
19
+ elsif value.is_a? Regexp
20
+ regexp_conditions(name, value)
21
+ else
22
+ [attribute_conditions(name => value)]
23
+ end
24
+
25
+ ::Capybara::Selector::CSS.split(css).map do |sel|
26
+ next sel if conditions.empty?
27
+
28
+ conditions.map { |cond| sel + cond }.join(', ')
29
+ end.join(', ')
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ def regexp_conditions(name, value)
36
+ Selector::RegexpDisassembler.new(value).alternated_substrings.map do |strs|
37
+ strs.map do |str|
38
+ "[#{name}*='#{str}'#{' i' if value.casefold?}]"
39
+ end.join
40
+ end
41
+ end
42
+
43
+ def attribute_conditions(attributes)
44
+ attributes.map do |attribute, value|
45
+ case value
46
+ when XPath::Expression
47
+ raise ArgumentError, "XPath expressions are not supported for the :#{attribute} filter with CSS based selectors"
48
+ when Regexp
49
+ Selector::RegexpDisassembler.new(value).substrings.map do |str|
50
+ "[#{attribute}*='#{str}'#{' i' if value.casefold?}]"
51
+ end.join
52
+ when true
53
+ "[#{attribute}]"
54
+ when false
55
+ ':not([attribute])'
56
+ else
57
+ if attribute == :id
58
+ "##{::Capybara::Selector::CSS.escape(value)}"
59
+ else
60
+ "[#{attribute}='#{value}']"
61
+ end
62
+ end
63
+ end.join
64
+ end
65
+
66
+ def class_conditions(classes)
67
+ case classes
68
+ when XPath::Expression
69
+ raise ArgumentError, 'XPath expressions are not supported for the :class filter with CSS based selectors'
70
+ when Regexp
71
+ Selector::RegexpDisassembler.new(classes).alternated_substrings.map do |strs|
72
+ strs.map do |str|
73
+ "[class*='#{str}'#{' i' if classes.casefold?}]"
74
+ end.join
75
+ end
76
+ else
77
+ cls = Array(classes).reject { |c| c.is_a? Regexp }.group_by { |cl| cl.match?(/^!(?!!!)/) }
78
+ [(cls[false].to_a.map { |cl| ".#{Capybara::Selector::CSS.escape(cl.sub(/^!!/, ''))}" } +
79
+ cls[true].to_a.map { |cl| ":not(.#{Capybara::Selector::CSS.escape(cl.slice(1..))})" }).join]
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end