capybara 3.3.0 → 3.40.0

Sign up to get free protection for your applications and to get access to all the features.
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