capybara 3.16.1 → 3.33.0

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