capybara 2.18.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (172) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +55 -1
  3. data/README.md +18 -17
  4. data/lib/capybara/config.rb +11 -58
  5. data/lib/capybara/cucumber.rb +2 -3
  6. data/lib/capybara/driver/base.rb +15 -16
  7. data/lib/capybara/driver/node.rb +5 -4
  8. data/lib/capybara/dsl.rb +1 -0
  9. data/lib/capybara/helpers.rb +19 -29
  10. data/lib/capybara/minitest/spec.rb +15 -14
  11. data/lib/capybara/minitest.rb +139 -138
  12. data/lib/capybara/node/actions.rb +60 -81
  13. data/lib/capybara/node/base.rb +11 -18
  14. data/lib/capybara/node/document.rb +2 -2
  15. data/lib/capybara/node/document_matchers.rb +8 -8
  16. data/lib/capybara/node/element.rb +30 -40
  17. data/lib/capybara/node/finders.rb +62 -70
  18. data/lib/capybara/node/matchers.rb +50 -71
  19. data/lib/capybara/node/simple.rb +11 -17
  20. data/lib/capybara/queries/ancestor_query.rb +11 -7
  21. data/lib/capybara/queries/base_query.rb +22 -18
  22. data/lib/capybara/queries/current_path_query.rb +8 -24
  23. data/lib/capybara/queries/match_query.rb +3 -7
  24. data/lib/capybara/queries/selector_query.rb +92 -95
  25. data/lib/capybara/queries/sibling_query.rb +4 -4
  26. data/lib/capybara/queries/text_query.rb +35 -35
  27. data/lib/capybara/queries/title_query.rb +8 -11
  28. data/lib/capybara/rack_test/browser.rb +15 -18
  29. data/lib/capybara/rack_test/css_handlers.rb +6 -4
  30. data/lib/capybara/rack_test/driver.rb +6 -10
  31. data/lib/capybara/rack_test/form.rb +50 -40
  32. data/lib/capybara/rack_test/node.rb +93 -63
  33. data/lib/capybara/rails.rb +2 -6
  34. data/lib/capybara/result.rb +22 -22
  35. data/lib/capybara/rspec/compound.rb +5 -10
  36. data/lib/capybara/rspec/features.rb +17 -48
  37. data/lib/capybara/rspec/matcher_proxies.rb +31 -15
  38. data/lib/capybara/rspec/matchers.rb +70 -61
  39. data/lib/capybara/rspec.rb +5 -10
  40. data/lib/capybara/selector/css.rb +6 -11
  41. data/lib/capybara/selector/filter.rb +1 -17
  42. data/lib/capybara/selector/filter_set.rb +18 -15
  43. data/lib/capybara/selector/filters/base.rb +7 -6
  44. data/lib/capybara/selector/filters/expression_filter.rb +6 -23
  45. data/lib/capybara/selector/filters/node_filter.rb +2 -12
  46. data/lib/capybara/selector/selector.rb +28 -34
  47. data/lib/capybara/selector.rb +129 -117
  48. data/lib/capybara/selenium/driver.rb +131 -125
  49. data/lib/capybara/selenium/node.rb +197 -115
  50. data/lib/capybara/server.rb +3 -2
  51. data/lib/capybara/session/config.rb +47 -67
  52. data/lib/capybara/session/matchers.rb +8 -7
  53. data/lib/capybara/session.rb +138 -224
  54. data/lib/capybara/spec/public/test.js +25 -4
  55. data/lib/capybara/spec/session/accept_alert_spec.rb +1 -0
  56. data/lib/capybara/spec/session/accept_confirm_spec.rb +3 -2
  57. data/lib/capybara/spec/session/accept_prompt_spec.rb +1 -0
  58. data/lib/capybara/spec/session/all_spec.rb +31 -18
  59. data/lib/capybara/spec/session/ancestor_spec.rb +6 -8
  60. data/lib/capybara/spec/session/assert_all_of_selectors_spec.rb +6 -5
  61. data/lib/capybara/spec/session/assert_current_path.rb +12 -11
  62. data/lib/capybara/spec/session/assert_selector.rb +1 -0
  63. data/lib/capybara/spec/session/assert_text.rb +23 -23
  64. data/lib/capybara/spec/session/assert_title.rb +13 -3
  65. data/lib/capybara/spec/session/attach_file_spec.rb +51 -30
  66. data/lib/capybara/spec/session/body_spec.rb +1 -0
  67. data/lib/capybara/spec/session/check_spec.rb +7 -6
  68. data/lib/capybara/spec/session/choose_spec.rb +5 -4
  69. data/lib/capybara/spec/session/click_button_spec.rb +24 -32
  70. data/lib/capybara/spec/session/click_link_or_button_spec.rb +8 -7
  71. data/lib/capybara/spec/session/click_link_spec.rb +8 -7
  72. data/lib/capybara/spec/session/current_scope_spec.rb +4 -3
  73. data/lib/capybara/spec/session/current_url_spec.rb +17 -6
  74. data/lib/capybara/spec/session/dismiss_confirm_spec.rb +1 -1
  75. data/lib/capybara/spec/session/dismiss_prompt_spec.rb +1 -0
  76. data/lib/capybara/spec/session/element/assert_match_selector.rb +1 -1
  77. data/lib/capybara/spec/session/element/match_xpath_spec.rb +1 -1
  78. data/lib/capybara/spec/session/element/matches_selector_spec.rb +5 -5
  79. data/lib/capybara/spec/session/evaluate_async_script_spec.rb +3 -2
  80. data/lib/capybara/spec/session/evaluate_script_spec.rb +4 -3
  81. data/lib/capybara/spec/session/execute_script_spec.rb +4 -3
  82. data/lib/capybara/spec/session/fill_in_spec.rb +30 -5
  83. data/lib/capybara/spec/session/find_button_spec.rb +4 -3
  84. data/lib/capybara/spec/session/find_by_id_spec.rb +2 -1
  85. data/lib/capybara/spec/session/find_field_spec.rb +9 -15
  86. data/lib/capybara/spec/session/find_link_spec.rb +6 -5
  87. data/lib/capybara/spec/session/find_spec.rb +37 -31
  88. data/lib/capybara/spec/session/first_spec.rb +60 -33
  89. data/lib/capybara/spec/session/frame/frame_title_spec.rb +23 -0
  90. data/lib/capybara/spec/session/frame/frame_url_spec.rb +23 -0
  91. data/lib/capybara/spec/session/frame/switch_to_frame_spec.rb +2 -1
  92. data/lib/capybara/spec/session/frame/within_frame_spec.rb +9 -16
  93. data/lib/capybara/spec/session/go_back_spec.rb +1 -0
  94. data/lib/capybara/spec/session/go_forward_spec.rb +1 -0
  95. data/lib/capybara/spec/session/has_all_selectors_spec.rb +15 -15
  96. data/lib/capybara/spec/session/has_button_spec.rb +2 -1
  97. data/lib/capybara/spec/session/has_css_spec.rb +3 -2
  98. data/lib/capybara/spec/session/has_current_path_spec.rb +12 -28
  99. data/lib/capybara/spec/session/has_field_spec.rb +4 -3
  100. data/lib/capybara/spec/session/has_link_spec.rb +1 -0
  101. data/lib/capybara/spec/session/has_none_selectors_spec.rb +17 -17
  102. data/lib/capybara/spec/session/has_select_spec.rb +30 -29
  103. data/lib/capybara/spec/session/has_selector_spec.rb +5 -4
  104. data/lib/capybara/spec/session/has_table_spec.rb +2 -1
  105. data/lib/capybara/spec/session/has_text_spec.rb +9 -13
  106. data/lib/capybara/spec/session/has_title_spec.rb +1 -0
  107. data/lib/capybara/spec/session/has_xpath_spec.rb +1 -0
  108. data/lib/capybara/spec/session/headers.rb +2 -1
  109. data/lib/capybara/spec/session/html_spec.rb +1 -0
  110. data/lib/capybara/spec/session/node_spec.rb +91 -56
  111. data/lib/capybara/spec/session/node_wrapper_spec.rb +36 -0
  112. data/lib/capybara/spec/session/refresh_spec.rb +6 -2
  113. data/lib/capybara/spec/session/reset_session_spec.rb +19 -0
  114. data/lib/capybara/spec/session/response_code.rb +1 -0
  115. data/lib/capybara/spec/session/save_and_open_page_spec.rb +1 -0
  116. data/lib/capybara/spec/session/save_and_open_screenshot_spec.rb +6 -11
  117. data/lib/capybara/spec/session/save_page_spec.rb +1 -17
  118. data/lib/capybara/spec/session/save_screenshot_spec.rb +3 -3
  119. data/lib/capybara/spec/session/select_spec.rb +20 -20
  120. data/lib/capybara/spec/session/selectors_spec.rb +2 -2
  121. data/lib/capybara/spec/session/sibling_spec.rb +1 -1
  122. data/lib/capybara/spec/session/text_spec.rb +17 -3
  123. data/lib/capybara/spec/session/title_spec.rb +11 -1
  124. data/lib/capybara/spec/session/uncheck_spec.rb +4 -3
  125. data/lib/capybara/spec/session/unselect_spec.rb +6 -5
  126. data/lib/capybara/spec/session/visit_spec.rb +9 -3
  127. data/lib/capybara/spec/session/window/become_closed_spec.rb +2 -1
  128. data/lib/capybara/spec/session/window/current_window_spec.rb +1 -0
  129. data/lib/capybara/spec/session/window/open_new_window_spec.rb +1 -0
  130. data/lib/capybara/spec/session/window/switch_to_window_spec.rb +2 -1
  131. data/lib/capybara/spec/session/window/window_opened_by_spec.rb +2 -1
  132. data/lib/capybara/spec/session/window/window_spec.rb +12 -12
  133. data/lib/capybara/spec/session/window/windows_spec.rb +2 -3
  134. data/lib/capybara/spec/session/window/within_window_spec.rb +15 -71
  135. data/lib/capybara/spec/session/within_spec.rb +1 -0
  136. data/lib/capybara/spec/spec_helper.rb +34 -18
  137. data/lib/capybara/spec/test_app.rb +17 -9
  138. data/lib/capybara/spec/views/form.erb +7 -0
  139. data/lib/capybara/spec/views/with_html.erb +23 -1
  140. data/lib/capybara/spec/views/within_frames.erb +4 -1
  141. data/lib/capybara/version.rb +2 -1
  142. data/lib/capybara/window.rb +6 -10
  143. data/lib/capybara.rb +28 -25
  144. data/spec/basic_node_spec.rb +1 -0
  145. data/spec/capybara_spec.rb +11 -50
  146. data/spec/dsl_spec.rb +5 -13
  147. data/spec/filter_set_spec.rb +5 -4
  148. data/spec/fixtures/selenium_driver_rspec_failure.rb +2 -1
  149. data/spec/fixtures/selenium_driver_rspec_success.rb +3 -2
  150. data/spec/minitest_spec.rb +4 -3
  151. data/spec/minitest_spec_spec.rb +3 -2
  152. data/spec/per_session_config_spec.rb +9 -8
  153. data/spec/rack_test_spec.rb +21 -20
  154. data/spec/result_spec.rb +17 -16
  155. data/spec/rspec/features_spec.rb +17 -14
  156. data/spec/rspec/scenarios_spec.rb +5 -7
  157. data/spec/rspec/shared_spec_matchers.rb +96 -99
  158. data/spec/rspec/views_spec.rb +2 -1
  159. data/spec/rspec_matchers_spec.rb +18 -2
  160. data/spec/rspec_spec.rb +11 -15
  161. data/spec/selector_spec.rb +5 -6
  162. data/spec/selenium_spec_chrome.rb +9 -4
  163. data/spec/selenium_spec_edge.rb +27 -0
  164. data/spec/selenium_spec_ie.rb +31 -0
  165. data/spec/selenium_spec_marionette.rb +28 -12
  166. data/spec/server_spec.rb +33 -33
  167. data/spec/session_spec.rb +2 -1
  168. data/spec/shared_selenium_session.rb +36 -22
  169. data/spec/spec_helper.rb +3 -6
  170. metadata +68 -85
  171. data/lib/capybara/query.rb +0 -7
  172. data/spec/selenium_spec_firefox.rb +0 -68
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Capybara
3
4
  module Node
4
-
5
5
  ##
6
6
  #
7
7
  # A {Capybara::Document} represents an HTML document. Any operation
@@ -20,7 +20,7 @@ module Capybara
20
20
  #
21
21
  # @return [String] The text of the document
22
22
  #
23
- def text(type=nil)
23
+ def text(type = nil)
24
24
  find(:xpath, '/html').text(type)
25
25
  end
26
26
 
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Capybara
3
4
  module Node
4
5
  module DocumentMatchers
@@ -15,8 +16,8 @@ module Capybara
15
16
  # @raise [Capybara::ExpectationNotMet] if the assertion hasn't succeeded during wait time
16
17
  # @return [true]
17
18
  #
18
- def assert_title(title, options = {})
19
- _verify_title(title,options) { |query| raise Capybara::ExpectationNotMet, query.failure_message unless query.resolves_for?(self) }
19
+ def assert_title(title, **options)
20
+ _verify_title(title, options) { |query| raise Capybara::ExpectationNotMet, query.failure_message unless query.resolves_for?(self) }
20
21
  end
21
22
 
22
23
  ##
@@ -26,8 +27,8 @@ module Capybara
26
27
  # @raise [Capybara::ExpectationNotMet] if the assertion hasn't succeeded during wait time
27
28
  # @return [true]
28
29
  #
29
- def assert_no_title(title, options = {})
30
- _verify_title(title,options) { |query| raise Capybara::ExpectationNotMet, query.negative_failure_message if query.resolves_for?(self) }
30
+ def assert_no_title(title, **options)
31
+ _verify_title(title, options) { |query| raise Capybara::ExpectationNotMet, query.negative_failure_message if query.resolves_for?(self) }
31
32
  end
32
33
 
33
34
  ##
@@ -36,7 +37,7 @@ module Capybara
36
37
  # @macro title_query_params
37
38
  # @return [Boolean]
38
39
  #
39
- def has_title?(title, options = {})
40
+ def has_title?(title, **options)
40
41
  assert_title(title, options)
41
42
  rescue Capybara::ExpectationNotMet
42
43
  return false
@@ -48,13 +49,13 @@ module Capybara
48
49
  # @macro title_query_params
49
50
  # @return [Boolean]
50
51
  #
51
- def has_no_title?(title, options = {})
52
+ def has_no_title?(title, **options)
52
53
  assert_no_title(title, options)
53
54
  rescue Capybara::ExpectationNotMet
54
55
  return false
55
56
  end
56
57
 
57
- private
58
+ private
58
59
 
59
60
  def _verify_title(title, options)
60
61
  query = Capybara::Queries::TitleQuery.new(title, options)
@@ -63,7 +64,6 @@ module Capybara
63
64
  end
64
65
  return true
65
66
  end
66
-
67
67
  end
68
68
  end
69
69
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Capybara
3
4
  module Node
4
-
5
5
  ##
6
6
  #
7
7
  # A {Capybara::Node::Element} represents a single element on the page. It is possible
@@ -22,7 +22,6 @@ module Capybara
22
22
  # @see Capybara::Node
23
23
  #
24
24
  class Element < Base
25
-
26
25
  def initialize(session, base, query_scope, query)
27
26
  super(session, base)
28
27
  @query_scope = query_scope
@@ -54,7 +53,7 @@ module Capybara
54
53
  # @param [:all, :visible] type Whether to return only visible or all text
55
54
  # @return [String] The text of the element
56
55
  #
57
- def text(type=nil)
56
+ def text(type = nil)
58
57
  type ||= :all unless session_options.ignore_hidden_elements or session_options.visible_text_only
59
58
  synchronize do
60
59
  if type == :all
@@ -94,23 +93,10 @@ module Capybara
94
93
  # @param [Hash{}] options Driver specific options for how to set the value
95
94
  #
96
95
  # @return [Capybara::Node::Element] The element
97
- def set(value, options={})
98
- options ||= {}
99
-
100
- driver_supports_options = (base.method(:set).arity != 1)
101
-
102
- unless options.empty? || driver_supports_options
103
- warn "Options passed to Capybara::Node#set but the driver doesn't support them"
104
- end
105
-
106
- synchronize do
107
- if driver_supports_options
108
- base.set(value, options)
109
- else
110
- base.set(value)
111
- end
112
- end
113
- return self
96
+ def set(value, **options)
97
+ raise Capybara::ReadOnlyElementError, "Attempt to set readonly element with value: #{value}" if readonly?
98
+ synchronize { base.set(value, options) }
99
+ self
114
100
  end
115
101
 
116
102
  ##
@@ -121,7 +107,7 @@ module Capybara
121
107
  def select_option
122
108
  warn "Attempt to select disabled option: #{value || text}" if disabled?
123
109
  synchronize { base.select_option }
124
- return self
110
+ self
125
111
  end
126
112
 
127
113
  ##
@@ -131,37 +117,43 @@ module Capybara
131
117
  # @return [Capybara::Node::Element] The element
132
118
  def unselect_option
133
119
  synchronize { base.unselect_option }
134
- return self
120
+ self
135
121
  end
136
122
 
137
123
  ##
138
124
  #
139
125
  # Click the Element
140
126
  #
127
+ # @!macro click_modifiers
128
+ # @overload $0(*key_modifiers=[], offset={x: nil, y: nil})
129
+ # @param [Array<:alt, :control, :meta, :shift>] *key_modifiers Keys to be held down when clicking
130
+ # @param [Hash] offset x and y coordinates to offset the click location from the top left corner of the element. If not specified will click the middle of the element.
141
131
  # @return [Capybara::Node::Element] The element
142
- def click
143
- synchronize { base.click }
144
- return self
132
+ def click(*keys, **offset)
133
+ synchronize { base.click(keys, offset) }
134
+ self
145
135
  end
146
136
 
147
137
  ##
148
138
  #
149
139
  # Right Click the Element
150
140
  #
141
+ # @macro click_modifiers
151
142
  # @return [Capybara::Node::Element] The element
152
- def right_click
153
- synchronize { base.right_click }
154
- return self
143
+ def right_click(*keys, **offset)
144
+ synchronize { base.right_click(keys, offset) }
145
+ self
155
146
  end
156
147
 
157
148
  ##
158
149
  #
159
150
  # Double Click the Element
160
151
  #
152
+ # @macro click_modifiers
161
153
  # @return [Capybara::Node::Element] The element
162
- def double_click
163
- synchronize { base.double_click }
164
- return self
154
+ def double_click(*keys, **offset)
155
+ synchronize { base.double_click(keys, offset) }
156
+ self
165
157
  end
166
158
 
167
159
  ##
@@ -237,7 +229,7 @@ module Capybara
237
229
  # @return [Capybara::Node::Element] The element
238
230
  def send_keys(*args)
239
231
  synchronize { base.send_keys(*args) }
240
- return self
232
+ self
241
233
  end
242
234
 
243
235
  ##
@@ -247,7 +239,7 @@ module Capybara
247
239
  # @return [Capybara::Node::Element] The element
248
240
  def hover
249
241
  synchronize { base.hover }
250
- return self
242
+ self
251
243
  end
252
244
 
253
245
  ##
@@ -339,7 +331,7 @@ module Capybara
339
331
  # @return [Capybara::Node::Element] The element
340
332
  def trigger(event)
341
333
  synchronize { base.trigger(event) }
342
- return self
334
+ self
343
335
  end
344
336
 
345
337
  ##
@@ -355,7 +347,7 @@ module Capybara
355
347
  # @return [Capybara::Node::Element] The element
356
348
  def drag_to(node)
357
349
  synchronize { base.drag_to(node.base) }
358
- return self
350
+ self
359
351
  end
360
352
 
361
353
  def reload
@@ -375,11 +367,9 @@ module Capybara
375
367
  rescue NotSupportedByDriverError
376
368
  %(#<Capybara::Node::Element tag="#{base.tag_name}">)
377
369
  rescue => e
378
- if session.driver.invalid_element_errors.any? { |et| e.is_a?(et)}
379
- %(Obsolete #<Capybara::Node::Element>)
380
- else
381
- raise
382
- end
370
+ raise unless session.driver.invalid_element_errors.any? { |et| e.is_a?(et) }
371
+
372
+ %(Obsolete #<Capybara::Node::Element>)
383
373
  end
384
374
  end
385
375
  end
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Capybara
3
4
  module Node
4
5
  module Finders
5
-
6
6
  ##
7
7
  #
8
8
  # Find an {Capybara::Node::Element} based on the given arguments. +find+ will raise an error if the element
@@ -28,13 +28,9 @@ module Capybara
28
28
  # @return [Capybara::Node::Element] The found element
29
29
  # @raise [Capybara::ElementNotFound] If the element can't be found before time expires
30
30
  #
31
- def find(*args, &optional_filter_block)
32
- if args.last.is_a? Hash
33
- args.last[:session_options] = session_options
34
- else
35
- args.push(session_options: session_options)
36
- end
37
- synced_resolve Capybara::Queries::SelectorQuery.new(*args, &optional_filter_block)
31
+ def find(*args, **options, &optional_filter_block)
32
+ options[:session_options] = session_options
33
+ synced_resolve Capybara::Queries::SelectorQuery.new(*args, options, &optional_filter_block)
38
34
  end
39
35
 
40
36
  ##
@@ -57,13 +53,9 @@ module Capybara
57
53
  # @return [Capybara::Node::Element] The found element
58
54
  # @raise [Capybara::ElementNotFound] If the element can't be found before time expires
59
55
  #
60
- def ancestor(*args, &optional_filter_block)
61
- if args.last.is_a? Hash
62
- args.last[:session_options] = session_options
63
- else
64
- args.push(session_options: session_options)
65
- end
66
- synced_resolve Capybara::Queries::AncestorQuery.new(*args, &optional_filter_block)
56
+ def ancestor(*args, **options, &optional_filter_block)
57
+ options[:session_options] = session_options
58
+ synced_resolve Capybara::Queries::AncestorQuery.new(*args, **options, &optional_filter_block)
67
59
  end
68
60
 
69
61
  ##
@@ -87,13 +79,9 @@ module Capybara
87
79
  # @return [Capybara::Node::Element] The found element
88
80
  # @raise [Capybara::ElementNotFound] If the element can't be found before time expires
89
81
  #
90
- def sibling(*args, &optional_filter_block)
91
- if args.last.is_a? Hash
92
- args.last[:session_options] = session_options
93
- else
94
- args.push(session_options: session_options)
95
- end
96
- synced_resolve Capybara::Queries::SiblingQuery.new(*args, &optional_filter_block)
82
+ def sibling(*args, **options, &optional_filter_block)
83
+ options[:session_options] = session_options
84
+ synced_resolve Capybara::Queries::SiblingQuery.new(*args, **options, &optional_filter_block)
97
85
  end
98
86
 
99
87
  ##
@@ -123,11 +111,9 @@ module Capybara
123
111
  # @return [Capybara::Node::Element] The found element
124
112
  #
125
113
 
126
- def find_field(locator=nil, options={}, &optional_filter_block)
127
- locator, options = nil, locator if locator.is_a? Hash
114
+ def find_field(locator = nil, **options, &optional_filter_block)
128
115
  find(:field, locator, options, &optional_filter_block)
129
116
  end
130
- alias_method :field_labeled, :find_field
131
117
 
132
118
  ##
133
119
  #
@@ -145,8 +131,7 @@ module Capybara
145
131
  # @option options [String, Array<String>] class Match links that match the class(es) provided
146
132
  # @return [Capybara::Node::Element] The found element
147
133
  #
148
- def find_link(locator=nil, options={}, &optional_filter_block)
149
- locator, options = nil, locator if locator.is_a? Hash
134
+ def find_link(locator = nil, **options, &optional_filter_block)
150
135
  find(:link, locator, options, &optional_filter_block)
151
136
  end
152
137
 
@@ -174,8 +159,7 @@ module Capybara
174
159
  # @option options [String, Array<String>] class Match buttons that match the class(es) provided
175
160
  # @return [Capybara::Node::Element] The found element
176
161
  #
177
- def find_button(locator=nil, options={}, &optional_filter_block)
178
- locator, options = nil, locator if locator.is_a? Hash
162
+ def find_button(locator = nil, **options, &optional_filter_block)
179
163
  find(:button, locator, options, &optional_filter_block)
180
164
  end
181
165
 
@@ -189,7 +173,7 @@ module Capybara
189
173
  #
190
174
  # @return [Capybara::Node::Element] The found element
191
175
  #
192
- def find_by_id(id, options={}, &optional_filter_block)
176
+ def find_by_id(id, **options, &optional_filter_block)
193
177
  find(:id, id, options, &optional_filter_block)
194
178
  end
195
179
 
@@ -221,10 +205,11 @@ module Capybara
221
205
  # page.all('a', text: 'Home')
222
206
  # page.all('#menu li', visible: true)
223
207
  #
224
- # By default if no elements are found, an empty array is returned;
225
- # however, expectations can be set on the number of elements to be found which
226
- # will trigger Capybara's waiting behavior for the expectations to match.The
227
- # expectations can be set using
208
+ # By default Capybara's waiting behavior will wait up to `Capybara.default_max_wait_time`
209
+ # seconds for matching elements to be available and then return an empty result if none
210
+ # are available. It is possible to set expectations on the number of results located and
211
+ # Capybara will raise an exception if the number of elements located don't satisfy the
212
+ # specified conditions. The expectations can be set using
228
213
  #
229
214
  # page.assert_selector('p#foo', count: 4)
230
215
  # page.assert_selector('p#foo', maximum: 10)
@@ -235,9 +220,9 @@ module Capybara
235
220
  # count matching.
236
221
  #
237
222
  # @param [Symbol] kind Optional selector type (:css, :xpath, :field, etc.) - Defaults to Capybara.default_selector
238
- # @param [String] locator The selector
223
+ # @param [String] locator The locator for the specified selector
239
224
  # @option options [String, Regexp] text Only find elements which contain this text or match this regexp
240
- # @option options [String, Boolean] exact_text (Capybara.exact_text) When String the string the elements contained text must match exactly, when Boolean controls whether the :text option must match exactly
225
+ # @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
241
226
  # @option options [Boolean, Symbol] visible Only find elements with the specified visibility:
242
227
  # * true - only finds visible elements.
243
228
  # * false - finds invisible _and_ visible elements.
@@ -249,24 +234,28 @@ module Capybara
249
234
  # @option options [Integer] minimum Minimum number of matches that are expected to be found
250
235
  # @option options [Range] between Number of matches found must be within the given range
251
236
  # @option options [Boolean] exact Control whether `is` expressions in the given XPath match exactly or partially
252
- # @option options [Integer] wait (Capybara.default_max_wait_time) The time to wait for element count expectations to become true
237
+ # @option options [Integer, false] wait (Capybara.default_max_wait_time) The time to wait for matching elements to become available
253
238
  # @overload all([kind = Capybara.default_selector], locator = nil, options = {})
254
239
  # @overload all([kind = Capybara.default_selector], locator = nil, options = {}, &filter_block)
255
240
  # @yieldparam element [Capybara::Node::Element] The element being considered for inclusion in the results
256
241
  # @yieldreturn [Boolean] Should the element be considered in the results?
257
242
  # @return [Capybara::Result] A collection of found elements
258
- #
259
- def all(*args, &optional_filter_block)
260
- if args.last.is_a? Hash
261
- args.last[:session_options] = session_options
262
- else
263
- args.push(session_options: session_options)
264
- end
265
- query = Capybara::Queries::SelectorQuery.new(*args, &optional_filter_block)
266
- synchronize(query.wait) do
267
- result = query.resolve_for(self)
268
- raise Capybara::ExpectationNotMet, result.failure_message unless result.matches_count?
269
- result
243
+ # @raise [Capybara::ExpectationNotMet] The number of elements found doesn't match the specified conditions
244
+ def all(*args, **options, &optional_filter_block)
245
+ minimum_specified = options_include_minimum?(options)
246
+ options = { minimum: 1 }.merge(options) unless minimum_specified
247
+ options[:session_options] = session_options
248
+ query = Capybara::Queries::SelectorQuery.new(*args.push(options), &optional_filter_block)
249
+ result = nil
250
+ begin
251
+ synchronize(query.wait) do
252
+ result = query.resolve_for(self)
253
+ raise Capybara::ExpectationNotMet, result.failure_message unless result.matches_count?
254
+ result
255
+ end
256
+ rescue Capybara::ExpectationNotMet
257
+ raise if minimum_specified || (result.compare_count == 1)
258
+ Result.new([], nil)
270
259
  end
271
260
  end
272
261
  alias_method :find_all, :all
@@ -274,48 +263,51 @@ module Capybara
274
263
  ##
275
264
  #
276
265
  # Find the first element on the page matching the given selector
277
- # and options, or nil if no element matches. By default no waiting
278
- # behavior occurs, however if {Capybara.wait_on_first_by_default} is set to true
279
- # it will trigger Capybara's waiting behavior for a minimum of 1 matching element to be found and
280
- # return the first. Waiting behavior can also be triggered by passing in any of the count
281
- # expectation options.
266
+ # and options. By default `first` will wait up to `Capybara.default_max_wait_time`
267
+ # seconds for matching elements to appear and then raise an error if no matching
268
+ # element is found, or `nil` if the provided count options allow for empty results.
282
269
  #
283
270
  # @overload first([kind], locator, options)
284
271
  # @param [:css, :xpath] kind The type of selector
285
272
  # @param [String] locator The selector
286
273
  # @param [Hash] options Additional options; see {#all}
287
274
  # @return [Capybara::Node::Element] The found element or nil
275
+ # @raise [Capybara::ElementNotFound] If element(s) matching the provided options can't be found before time expires
288
276
  #
289
- def first(*args, &optional_filter_block)
290
- if session_options.wait_on_first_by_default
291
- options = if args.last.is_a?(Hash) then args.pop.dup else {} end
292
- args.push({minimum: 1}.merge(options))
293
- end
294
- all(*args, &optional_filter_block).first
295
- rescue Capybara::ExpectationNotMet
296
- nil
277
+ def first(*args, **options, &optional_filter_block)
278
+ options = { minimum: 1 }.merge(options) unless options_include_minimum?(options)
279
+ all(*args, **options, &optional_filter_block).first
297
280
  end
298
281
 
299
- private
282
+ private
300
283
 
301
284
  def synced_resolve(query)
302
285
  synchronize(query.wait) do
303
- if (query.match == :smart or query.match == :prefer_exact)
286
+ if prefer_exact?(query)
304
287
  result = query.resolve_for(self, true)
305
288
  result = query.resolve_for(self, false) if result.empty? && query.supports_exact? && !query.exact?
306
289
  else
307
290
  result = query.resolve_for(self)
308
291
  end
309
292
 
310
- if query.match == :one or query.match == :smart and result.size > 1
311
- raise Capybara::Ambiguous.new("Ambiguous match, found #{result.size} elements matching #{query.description}")
312
- end
313
- if result.empty?
314
- raise Capybara::ElementNotFound.new("Unable to find #{query.description}")
315
- end
293
+ raise Capybara::Ambiguous, "Ambiguous match, found #{result.size} elements matching #{query.description}" if ambiguous?(query, result)
294
+ raise Capybara::ElementNotFound, "Unable to find #{query.description}" if result.empty?
295
+
316
296
  result.first
317
297
  end.tap(&:allow_reload!)
318
298
  end
299
+
300
+ def ambiguous?(query, result)
301
+ query.match == :one or query.match == :smart and result.size > 1
302
+ end
303
+
304
+ def prefer_exact?(query)
305
+ query.match == :smart or query.match == :prefer_exact
306
+ end
307
+
308
+ def options_include_minimum?(opts)
309
+ %i[count minimum between].any? { |k| opts.key?(k) }
310
+ end
319
311
  end
320
312
  end
321
313
  end