capybara 2.5.0 → 2.18.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 (205) hide show
  1. checksums.yaml +5 -5
  2. data/.yard/templates_custom/default/class/html/selectors.erb +38 -0
  3. data/.yard/templates_custom/default/class/html/setup.rb +17 -0
  4. data/.yard/yard_extensions.rb +78 -0
  5. data/.yardopts +1 -0
  6. data/History.md +413 -10
  7. data/License.txt +1 -1
  8. data/README.md +237 -130
  9. data/lib/capybara/config.rb +132 -0
  10. data/lib/capybara/cucumber.rb +3 -1
  11. data/lib/capybara/driver/base.rb +27 -6
  12. data/lib/capybara/driver/node.rb +14 -5
  13. data/lib/capybara/dsl.rb +2 -3
  14. data/lib/capybara/helpers.rb +13 -65
  15. data/lib/capybara/minitest/spec.rb +177 -0
  16. data/lib/capybara/minitest.rb +278 -0
  17. data/lib/capybara/node/actions.rb +180 -24
  18. data/lib/capybara/node/base.rb +17 -5
  19. data/lib/capybara/node/document.rb +5 -0
  20. data/lib/capybara/node/document_matchers.rb +15 -14
  21. data/lib/capybara/node/element.rb +55 -7
  22. data/lib/capybara/node/finders.rb +179 -67
  23. data/lib/capybara/node/matchers.rb +301 -105
  24. data/lib/capybara/node/simple.rb +15 -4
  25. data/lib/capybara/queries/ancestor_query.rb +25 -0
  26. data/lib/capybara/queries/base_query.rb +69 -3
  27. data/lib/capybara/queries/current_path_query.rb +17 -8
  28. data/lib/capybara/queries/match_query.rb +19 -0
  29. data/lib/capybara/queries/selector_query.rb +251 -0
  30. data/lib/capybara/queries/sibling_query.rb +25 -0
  31. data/lib/capybara/queries/text_query.rb +67 -16
  32. data/lib/capybara/queries/title_query.rb +4 -2
  33. data/lib/capybara/query.rb +3 -131
  34. data/lib/capybara/rack_test/browser.rb +14 -5
  35. data/lib/capybara/rack_test/css_handlers.rb +1 -0
  36. data/lib/capybara/rack_test/driver.rb +15 -8
  37. data/lib/capybara/rack_test/form.rb +34 -12
  38. data/lib/capybara/rack_test/node.rb +29 -12
  39. data/lib/capybara/rails.rb +3 -3
  40. data/lib/capybara/result.rb +104 -9
  41. data/lib/capybara/rspec/compound.rb +95 -0
  42. data/lib/capybara/rspec/features.rb +17 -6
  43. data/lib/capybara/rspec/matcher_proxies.rb +45 -0
  44. data/lib/capybara/rspec/matchers.rb +199 -80
  45. data/lib/capybara/rspec.rb +4 -2
  46. data/lib/capybara/selector/css.rb +30 -0
  47. data/lib/capybara/selector/filter.rb +20 -0
  48. data/lib/capybara/selector/filter_set.rb +74 -0
  49. data/lib/capybara/selector/filters/base.rb +33 -0
  50. data/lib/capybara/selector/filters/expression_filter.rb +40 -0
  51. data/lib/capybara/selector/filters/node_filter.rb +27 -0
  52. data/lib/capybara/selector/selector.rb +276 -0
  53. data/lib/capybara/selector.rb +452 -157
  54. data/lib/capybara/selenium/driver.rb +282 -81
  55. data/lib/capybara/selenium/node.rb +144 -46
  56. data/lib/capybara/server.rb +59 -16
  57. data/lib/capybara/session/config.rb +114 -0
  58. data/lib/capybara/session/matchers.rb +29 -19
  59. data/lib/capybara/session.rb +378 -143
  60. data/lib/capybara/spec/fixtures/no_extension +1 -0
  61. data/lib/capybara/spec/public/jquery-ui.js +13 -791
  62. data/lib/capybara/spec/public/jquery.js +4 -9045
  63. data/lib/capybara/spec/public/test.js +45 -11
  64. data/lib/capybara/spec/session/accept_alert_spec.rb +30 -7
  65. data/lib/capybara/spec/session/accept_confirm_spec.rb +14 -2
  66. data/lib/capybara/spec/session/accept_prompt_spec.rb +35 -6
  67. data/lib/capybara/spec/session/all_spec.rb +45 -32
  68. data/lib/capybara/spec/session/ancestor_spec.rb +85 -0
  69. data/lib/capybara/spec/session/assert_all_of_selectors_spec.rb +110 -0
  70. data/lib/capybara/spec/session/assert_current_path.rb +15 -2
  71. data/lib/capybara/spec/session/assert_selector.rb +29 -28
  72. data/lib/capybara/spec/session/assert_text.rb +59 -20
  73. data/lib/capybara/spec/session/assert_title.rb +25 -11
  74. data/lib/capybara/spec/session/attach_file_spec.rb +42 -4
  75. data/lib/capybara/spec/session/body_spec.rb +1 -0
  76. data/lib/capybara/spec/session/check_spec.rb +90 -14
  77. data/lib/capybara/spec/session/choose_spec.rb +31 -5
  78. data/lib/capybara/spec/session/click_button_spec.rb +20 -9
  79. data/lib/capybara/spec/session/click_link_or_button_spec.rb +15 -9
  80. data/lib/capybara/spec/session/click_link_spec.rb +39 -15
  81. data/lib/capybara/spec/session/current_scope_spec.rb +2 -1
  82. data/lib/capybara/spec/session/current_url_spec.rb +12 -3
  83. data/lib/capybara/spec/session/dismiss_confirm_spec.rb +6 -5
  84. data/lib/capybara/spec/session/dismiss_prompt_spec.rb +4 -3
  85. data/lib/capybara/spec/session/element/assert_match_selector.rb +36 -0
  86. data/lib/capybara/spec/session/element/match_css_spec.rb +23 -0
  87. data/lib/capybara/spec/session/element/match_xpath_spec.rb +23 -0
  88. data/lib/capybara/spec/session/element/matches_selector_spec.rb +106 -0
  89. data/lib/capybara/spec/session/evaluate_async_script_spec.rb +22 -0
  90. data/lib/capybara/spec/session/evaluate_script_spec.rb +23 -1
  91. data/lib/capybara/spec/session/execute_script_spec.rb +22 -3
  92. data/lib/capybara/spec/session/fill_in_spec.rb +50 -32
  93. data/lib/capybara/spec/session/find_button_spec.rb +43 -2
  94. data/lib/capybara/spec/session/find_by_id_spec.rb +3 -2
  95. data/lib/capybara/spec/session/find_field_spec.rb +42 -6
  96. data/lib/capybara/spec/session/find_link_spec.rb +22 -3
  97. data/lib/capybara/spec/session/find_spec.rb +103 -57
  98. data/lib/capybara/spec/session/first_spec.rb +34 -18
  99. data/lib/capybara/spec/session/frame/switch_to_frame_spec.rb +103 -0
  100. data/lib/capybara/spec/session/{within_frame_spec.rb → frame/within_frame_spec.rb} +44 -2
  101. data/lib/capybara/spec/session/go_back_spec.rb +2 -1
  102. data/lib/capybara/spec/session/go_forward_spec.rb +2 -1
  103. data/lib/capybara/spec/session/has_all_selectors_spec.rb +69 -0
  104. data/lib/capybara/spec/session/has_button_spec.rb +17 -8
  105. data/lib/capybara/spec/session/has_css_spec.rb +85 -73
  106. data/lib/capybara/spec/session/has_current_path_spec.rb +91 -7
  107. data/lib/capybara/spec/session/has_field_spec.rb +93 -58
  108. data/lib/capybara/spec/session/has_link_spec.rb +9 -8
  109. data/lib/capybara/spec/session/has_none_selectors_spec.rb +76 -0
  110. data/lib/capybara/spec/session/has_select_spec.rb +159 -59
  111. data/lib/capybara/spec/session/has_selector_spec.rb +64 -28
  112. data/lib/capybara/spec/session/has_table_spec.rb +1 -0
  113. data/lib/capybara/spec/session/has_text_spec.rb +27 -12
  114. data/lib/capybara/spec/session/has_title_spec.rb +22 -4
  115. data/lib/capybara/spec/session/has_xpath_spec.rb +32 -29
  116. data/lib/capybara/spec/session/headers.rb +2 -1
  117. data/lib/capybara/spec/session/html_spec.rb +4 -3
  118. data/lib/capybara/spec/session/node_spec.rb +198 -38
  119. data/lib/capybara/spec/session/refresh_spec.rb +28 -0
  120. data/lib/capybara/spec/session/reset_session_spec.rb +46 -5
  121. data/lib/capybara/spec/session/response_code.rb +2 -1
  122. data/lib/capybara/spec/session/save_and_open_page_spec.rb +1 -0
  123. data/lib/capybara/spec/session/save_and_open_screenshot_spec.rb +6 -5
  124. data/lib/capybara/spec/session/save_page_spec.rb +34 -2
  125. data/lib/capybara/spec/session/save_screenshot_spec.rb +31 -1
  126. data/lib/capybara/spec/session/screenshot_spec.rb +4 -2
  127. data/lib/capybara/spec/session/select_spec.rb +34 -32
  128. data/lib/capybara/spec/session/selectors_spec.rb +65 -0
  129. data/lib/capybara/spec/session/sibling_spec.rb +52 -0
  130. data/lib/capybara/spec/session/text_spec.rb +4 -4
  131. data/lib/capybara/spec/session/title_spec.rb +2 -1
  132. data/lib/capybara/spec/session/uncheck_spec.rb +42 -2
  133. data/lib/capybara/spec/session/unselect_spec.rb +17 -16
  134. data/lib/capybara/spec/session/visit_spec.rb +77 -2
  135. data/lib/capybara/spec/session/window/become_closed_spec.rb +12 -11
  136. data/lib/capybara/spec/session/window/current_window_spec.rb +1 -0
  137. data/lib/capybara/spec/session/window/open_new_window_spec.rb +1 -0
  138. data/lib/capybara/spec/session/window/switch_to_window_spec.rb +16 -11
  139. data/lib/capybara/spec/session/window/window_opened_by_spec.rb +7 -4
  140. data/lib/capybara/spec/session/window/window_spec.rb +36 -29
  141. data/lib/capybara/spec/session/window/windows_spec.rb +1 -0
  142. data/lib/capybara/spec/session/window/within_window_spec.rb +31 -7
  143. data/lib/capybara/spec/session/within_spec.rb +14 -6
  144. data/lib/capybara/spec/spec_helper.rb +37 -4
  145. data/lib/capybara/spec/test_app.rb +15 -3
  146. data/lib/capybara/spec/views/buttons.erb +1 -0
  147. data/lib/capybara/spec/views/fieldsets.erb +2 -1
  148. data/lib/capybara/spec/views/form.erb +169 -9
  149. data/lib/capybara/spec/views/frame_child.erb +10 -2
  150. data/lib/capybara/spec/views/frame_one.erb +2 -1
  151. data/lib/capybara/spec/views/frame_parent.erb +3 -2
  152. data/lib/capybara/spec/views/frame_two.erb +2 -1
  153. data/lib/capybara/spec/views/header_links.erb +1 -0
  154. data/lib/capybara/spec/views/host_links.erb +1 -0
  155. data/lib/capybara/spec/views/initial_alert.erb +10 -0
  156. data/lib/capybara/spec/views/path.erb +1 -0
  157. data/lib/capybara/spec/views/popup_one.erb +1 -0
  158. data/lib/capybara/spec/views/popup_two.erb +1 -0
  159. data/lib/capybara/spec/views/postback.erb +2 -1
  160. data/lib/capybara/spec/views/tables.erb +1 -0
  161. data/lib/capybara/spec/views/with_base_tag.erb +1 -0
  162. data/lib/capybara/spec/views/with_count.erb +2 -1
  163. data/lib/capybara/spec/views/with_fixed_header_footer.erb +17 -0
  164. data/lib/capybara/spec/views/with_hover.erb +7 -1
  165. data/lib/capybara/spec/views/with_html.erb +40 -2
  166. data/lib/capybara/spec/views/with_html_entities.erb +1 -0
  167. data/lib/capybara/spec/views/with_js.erb +32 -1
  168. data/lib/capybara/spec/views/with_scope.erb +1 -0
  169. data/lib/capybara/spec/views/with_simple_html.erb +2 -1
  170. data/lib/capybara/spec/views/with_slow_unload.erb +17 -0
  171. data/lib/capybara/spec/views/with_title.erb +2 -1
  172. data/lib/capybara/spec/views/with_unload_alert.erb +14 -0
  173. data/lib/capybara/spec/views/with_windows.erb +7 -0
  174. data/lib/capybara/spec/views/within_frames.erb +3 -2
  175. data/lib/capybara/version.rb +2 -1
  176. data/lib/capybara/window.rb +20 -3
  177. data/lib/capybara.rb +189 -93
  178. data/spec/basic_node_spec.rb +7 -6
  179. data/spec/capybara_spec.rb +90 -4
  180. data/spec/dsl_spec.rb +3 -1
  181. data/spec/filter_set_spec.rb +28 -0
  182. data/spec/fixtures/capybara.csv +1 -0
  183. data/spec/fixtures/selenium_driver_rspec_failure.rb +5 -1
  184. data/spec/fixtures/selenium_driver_rspec_success.rb +5 -1
  185. data/spec/minitest_spec.rb +130 -0
  186. data/spec/minitest_spec_spec.rb +135 -0
  187. data/spec/per_session_config_spec.rb +67 -0
  188. data/spec/rack_test_spec.rb +50 -7
  189. data/spec/result_spec.rb +76 -0
  190. data/spec/rspec/features_spec.rb +21 -8
  191. data/spec/rspec/scenarios_spec.rb +21 -0
  192. data/spec/rspec/{matchers_spec.rb → shared_spec_matchers.rb} +160 -54
  193. data/spec/rspec/views_spec.rb +5 -0
  194. data/spec/rspec_matchers_spec.rb +46 -0
  195. data/spec/rspec_spec.rb +79 -1
  196. data/spec/selector_spec.rb +199 -0
  197. data/spec/selenium_spec_chrome.rb +54 -9
  198. data/spec/selenium_spec_firefox.rb +68 -0
  199. data/spec/selenium_spec_marionette.rb +127 -0
  200. data/spec/server_spec.rb +102 -14
  201. data/spec/session_spec.rb +54 -0
  202. data/spec/shared_selenium_session.rb +215 -0
  203. data/spec/spec_helper.rb +7 -0
  204. metadata +140 -15
  205. data/spec/selenium_spec.rb +0 -128
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Capybara
2
3
  module Node
3
4
  module DocumentMatchers
@@ -10,17 +11,12 @@ module Capybara
10
11
  # @overload $0(regexp, options = {})
11
12
  # @param regexp [Regexp] The regexp that title should match to
12
13
  # @option options [Numeric] :wait (Capybara.default_max_wait_time) Maximum time that Capybara will wait for title to eq/match given string/regexp argument
14
+ # @option options [Boolean] :exact (false) When passed a string should the match be exact or just substring
13
15
  # @raise [Capybara::ExpectationNotMet] if the assertion hasn't succeeded during wait time
14
16
  # @return [true]
15
17
  #
16
18
  def assert_title(title, options = {})
17
- query = Capybara::Queries::TitleQuery.new(title, options)
18
- synchronize(query.wait) do
19
- unless query.resolves_for?(self)
20
- raise Capybara::ExpectationNotMet, query.failure_message
21
- end
22
- end
23
- return true
19
+ _verify_title(title,options) { |query| raise Capybara::ExpectationNotMet, query.failure_message unless query.resolves_for?(self) }
24
20
  end
25
21
 
26
22
  ##
@@ -31,13 +27,7 @@ module Capybara
31
27
  # @return [true]
32
28
  #
33
29
  def assert_no_title(title, options = {})
34
- query = Capybara::Queries::TitleQuery.new(title, options)
35
- synchronize(query.wait) do
36
- if query.resolves_for?(self)
37
- raise Capybara::ExpectationNotMet, query.negative_failure_message
38
- end
39
- end
40
- return true
30
+ _verify_title(title,options) { |query| raise Capybara::ExpectationNotMet, query.negative_failure_message if query.resolves_for?(self) }
41
31
  end
42
32
 
43
33
  ##
@@ -63,6 +53,17 @@ module Capybara
63
53
  rescue Capybara::ExpectationNotMet
64
54
  return false
65
55
  end
56
+
57
+ private
58
+
59
+ def _verify_title(title, options)
60
+ query = Capybara::Queries::TitleQuery.new(title, options)
61
+ synchronize(query.wait) do
62
+ yield(query)
63
+ end
64
+ return true
65
+ end
66
+
66
67
  end
67
68
  end
68
69
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Capybara
2
3
  module Node
3
4
 
@@ -9,7 +10,7 @@ module Capybara
9
10
  # session = Capybara::Session.new(:rack_test, my_app)
10
11
  #
11
12
  # bar = session.find('#bar') # from Capybara::Node::Finders
12
- # bar.select('Baz', :from => 'Quox') # from Capybara::Node::Actions
13
+ # bar.select('Baz', from: 'Quox') # from Capybara::Node::Actions
13
14
  #
14
15
  # {Capybara::Node::Element} also has access to HTML attributes and other properties of the
15
16
  # element:
@@ -22,10 +23,11 @@ module Capybara
22
23
  #
23
24
  class Element < Base
24
25
 
25
- def initialize(session, base, parent, query)
26
+ def initialize(session, base, query_scope, query)
26
27
  super(session, base)
27
- @parent = parent
28
+ @query_scope = query_scope
28
29
  @query = query
30
+ @allow_reload = false
29
31
  end
30
32
 
31
33
  def allow_reload!
@@ -53,7 +55,7 @@ module Capybara
53
55
  # @return [String] The text of the element
54
56
  #
55
57
  def text(type=nil)
56
- type ||= :all unless Capybara.ignore_hidden_elements or Capybara.visible_text_only
58
+ type ||= :all unless session_options.ignore_hidden_elements or session_options.visible_text_only
57
59
  synchronize do
58
60
  if type == :all
59
61
  base.all_text
@@ -91,6 +93,7 @@ module Capybara
91
93
  # @param [String] value The new value
92
94
  # @param [Hash{}] options Driver specific options for how to set the value
93
95
  #
96
+ # @return [Capybara::Node::Element] The element
94
97
  def set(value, options={})
95
98
  options ||= {}
96
99
 
@@ -107,47 +110,58 @@ module Capybara
107
110
  base.set(value)
108
111
  end
109
112
  end
113
+ return self
110
114
  end
111
115
 
112
116
  ##
113
117
  #
114
118
  # Select this node if is an option element inside a select tag
115
119
  #
120
+ # @return [Capybara::Node::Element] The element
116
121
  def select_option
117
122
  warn "Attempt to select disabled option: #{value || text}" if disabled?
118
123
  synchronize { base.select_option }
124
+ return self
119
125
  end
120
126
 
121
127
  ##
122
128
  #
123
129
  # Unselect this node if is an option element inside a multiple select tag
124
130
  #
131
+ # @return [Capybara::Node::Element] The element
125
132
  def unselect_option
126
133
  synchronize { base.unselect_option }
134
+ return self
127
135
  end
128
136
 
129
137
  ##
130
138
  #
131
139
  # Click the Element
132
140
  #
141
+ # @return [Capybara::Node::Element] The element
133
142
  def click
134
143
  synchronize { base.click }
144
+ return self
135
145
  end
136
146
 
137
147
  ##
138
148
  #
139
149
  # Right Click the Element
140
150
  #
151
+ # @return [Capybara::Node::Element] The element
141
152
  def right_click
142
153
  synchronize { base.right_click }
154
+ return self
143
155
  end
144
156
 
145
157
  ##
146
158
  #
147
159
  # Double Click the Element
148
160
  #
161
+ # @return [Capybara::Node::Element] The element
149
162
  def double_click
150
163
  synchronize { base.double_click }
164
+ return self
151
165
  end
152
166
 
153
167
  ##
@@ -220,16 +234,20 @@ module Capybara
220
234
  # :meta
221
235
  # :command - alias of :meta
222
236
  #
237
+ # @return [Capybara::Node::Element] The element
223
238
  def send_keys(*args)
224
239
  synchronize { base.send_keys(*args) }
240
+ return self
225
241
  end
226
242
 
227
243
  ##
228
244
  #
229
245
  # Hover on the Element
230
246
  #
247
+ # @return [Capybara::Node::Element] The element
231
248
  def hover
232
249
  synchronize { base.hover }
250
+ return self
233
251
  end
234
252
 
235
253
  ##
@@ -281,6 +299,26 @@ module Capybara
281
299
  synchronize { base.disabled? }
282
300
  end
283
301
 
302
+ ##
303
+ #
304
+ # Whether or not the element is readonly.
305
+ #
306
+ # @return [Boolean] Whether the element is readonly
307
+ #
308
+ def readonly?
309
+ synchronize { base.readonly? }
310
+ end
311
+
312
+ ##
313
+ #
314
+ # Whether or not the element supports multiple results.
315
+ #
316
+ # @return [Boolean] Whether the element supports multiple results.
317
+ #
318
+ def multiple?
319
+ synchronize { base.multiple? }
320
+ end
321
+
284
322
  ##
285
323
  #
286
324
  # An XPath expression describing where on the page the element can be found
@@ -298,8 +336,10 @@ module Capybara
298
336
  #
299
337
  # @param [String] event The name of the event to trigger
300
338
  #
339
+ # @return [Capybara::Node::Element] The element
301
340
  def trigger(event)
302
341
  synchronize { base.trigger(event) }
342
+ return self
303
343
  end
304
344
 
305
345
  ##
@@ -312,14 +352,16 @@ module Capybara
312
352
  #
313
353
  # @param [Capybara::Node::Element] node The element to drag to
314
354
  #
355
+ # @return [Capybara::Node::Element] The element
315
356
  def drag_to(node)
316
357
  synchronize { base.drag_to(node.base) }
358
+ return self
317
359
  end
318
360
 
319
361
  def reload
320
362
  if @allow_reload
321
363
  begin
322
- reloaded = parent.reload.first(@query.name, @query.locator, @query.options)
364
+ reloaded = query_scope.reload.first(@query.name, @query.locator, @query.options)
323
365
  @base = reloaded.base if reloaded
324
366
  rescue => e
325
367
  raise e unless catch_error?(e)
@@ -329,9 +371,15 @@ module Capybara
329
371
  end
330
372
 
331
373
  def inspect
332
- %(#<Capybara::Node::Element tag="#{tag_name}" path="#{path}">)
374
+ %(#<Capybara::Node::Element tag="#{base.tag_name}" path="#{base.path}">)
333
375
  rescue NotSupportedByDriverError
334
- %(#<Capybara::Node::Element tag="#{tag_name}">)
376
+ %(#<Capybara::Node::Element tag="#{base.tag_name}">)
377
+ 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
335
383
  end
336
384
  end
337
385
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Capybara
2
3
  module Node
3
4
  module Finders
@@ -17,8 +18,8 @@ module Capybara
17
18
  # +find+ takes the same options as +all+.
18
19
  #
19
20
  # page.find('#foo').find('.bar')
20
- # page.find(:xpath, '//div[contains(., "bar")]')
21
- # page.find('li', :text => 'Quox').click_link('Delete')
21
+ # page.find(:xpath, './/div[contains(., "bar")]')
22
+ # page.find('li', text: 'Quox').click_link('Delete')
22
23
  #
23
24
  # @param (see Capybara::Node::Finders#all)
24
25
  #
@@ -27,43 +28,104 @@ module Capybara
27
28
  # @return [Capybara::Node::Element] The found element
28
29
  # @raise [Capybara::ElementNotFound] If the element can't be found before time expires
29
30
  #
30
- def find(*args)
31
- query = Capybara::Query.new(*args)
32
- synchronize(query.wait) do
33
- if query.match == :smart or query.match == :prefer_exact
34
- result = query.resolve_for(self, true)
35
- result = query.resolve_for(self, false) if result.size == 0 && !query.exact?
36
- else
37
- result = query.resolve_for(self)
38
- end
39
- if query.match == :one or query.match == :smart and result.size > 1
40
- raise Capybara::Ambiguous.new("Ambiguous match, found #{result.size} elements matching #{query.description}")
41
- end
42
- if result.size == 0
43
- raise Capybara::ElementNotFound.new("Unable to find #{query.description}")
44
- end
45
- result.first
46
- end.tap(&:allow_reload!)
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)
47
38
  end
48
39
 
49
40
  ##
50
41
  #
51
- # Find a form field on the page. The field can be found by its name, id or label text.
42
+ # 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
43
+ # is not found.
44
+ #
45
+ # +ancestor+ takes the same options as +find+.
46
+ #
47
+ # element.ancestor('#foo').find('.bar')
48
+ # element.ancestor(:xpath, './/div[contains(., "bar")]')
49
+ # element.ancestor('ul', text: 'Quox').click_link('Delete')
50
+ #
51
+ # @param (see Capybara::Node::Finders#find)
52
+ #
53
+ # @!macro waiting_behavior
54
+ #
55
+ # @option options [Boolean] match The matching strategy to use.
56
+ #
57
+ # @return [Capybara::Node::Element] The found element
58
+ # @raise [Capybara::ElementNotFound] If the element can't be found before time expires
59
+ #
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)
67
+ end
68
+
69
+ ##
70
+ #
71
+ # 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
72
+ # is not found.
73
+ #
74
+ #
75
+ # +sibling+ takes the same options as +find+.
76
+ #
77
+ # element.sibling('#foo').find('.bar')
78
+ # element.sibling(:xpath, './/div[contains(., "bar")]')
79
+ # element.sibling('ul', text: 'Quox').click_link('Delete')
80
+ #
81
+ # @param (see Capybara::Node::Finders#find)
52
82
  #
53
83
  # @macro waiting_behavior
54
84
  #
55
- # @param [String] locator Which field to find
85
+ # @option options [Boolean] match The matching strategy to use.
86
+ #
87
+ # @return [Capybara::Node::Element] The found element
88
+ # @raise [Capybara::ElementNotFound] If the element can't be found before time expires
89
+ #
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)
97
+ end
98
+
99
+ ##
100
+ #
101
+ # Find a form field on the page. The field can be found by its name, id or label text.
56
102
  #
57
- # @option options [Boolean] checked Match checked field?
58
- # @option options [Boolean] unchecked Match unchecked field?
59
- # @option options [Boolean] disabled (false) Match disabled field?
60
- # @option options [Boolean] readonly Match readonly field?
61
- # @option options [String] with Value of field to match on
62
- # @option options [String] type Type of field to match on
103
+ # @overload find_field([locator], options={})
104
+ # @param [String] locator name, id, placeholder or text of associated label element
105
+ #
106
+ # @macro waiting_behavior
107
+ #
108
+ #
109
+ # @option options [Boolean] checked Match checked field?
110
+ # @option options [Boolean] unchecked Match unchecked field?
111
+ # @option options [Boolean, Symbol] disabled (false) Match disabled field?
112
+ # * true - only finds a disabled field
113
+ # * false - only finds an enabled field
114
+ # * :all - finds either an enabled or disabled field
115
+ # @option options [Boolean] readonly Match readonly field?
116
+ # @option options [String, Regexp] with Value of field to match on
117
+ # @option options [String] type Type of field to match on
118
+ # @option options [Boolean] multiple Match fields that can have multiple values?
119
+ # @option options [String] id Match fields that match the id attribute
120
+ # @option options [String] name Match fields that match the name attribute
121
+ # @option options [String] placeholder Match fields that match the placeholder attribute
122
+ # @option options [String, Array<String>] Match fields that match the class(es) passed
63
123
  # @return [Capybara::Node::Element] The found element
64
124
  #
65
- def find_field(locator, options={})
66
- find(:field, locator, options)
125
+
126
+ def find_field(locator=nil, options={}, &optional_filter_block)
127
+ locator, options = nil, locator if locator.is_a? Hash
128
+ find(:field, locator, options, &optional_filter_block)
67
129
  end
68
130
  alias_method :field_labeled, :find_field
69
131
 
@@ -71,14 +133,21 @@ module Capybara
71
133
  #
72
134
  # Find a link on the page. The link can be found by its id or text.
73
135
  #
74
- # @macro waiting_behavior
136
+ # @overload find_link([locator], options={})
137
+ # @param [String] locator id, title, text, or alt of enclosed img element
138
+ #
139
+ # @macro waiting_behavior
75
140
  #
76
- # @param [String] locator Which link to find
77
- # @option options [String,Regexp] href Value to match against the links href
141
+ # @option options [String,Regexp,nil] href Value to match against the links href, if nil finds link placeholders (<a> elements with no href attribute)
142
+ # @option options [String] id Match links with the id provided
143
+ # @option options [String] title Match links with the title provided
144
+ # @option options [String] alt Match links with a contained img element whose alt matches
145
+ # @option options [String, Array<String>] class Match links that match the class(es) provided
78
146
  # @return [Capybara::Node::Element] The found element
79
147
  #
80
- def find_link(locator, options={})
81
- find(:link, locator, options)
148
+ def find_link(locator=nil, options={}, &optional_filter_block)
149
+ locator, options = nil, locator if locator.is_a? Hash
150
+ find(:link, locator, options, &optional_filter_block)
82
151
  end
83
152
 
84
153
  ##
@@ -87,15 +156,27 @@ module Capybara
87
156
  # This can be any \<input> element of type submit, reset, image, button or it can be a
88
157
  # \<button> element. All buttons can be found by their id, value, or title. \<button> elements can also be found
89
158
  # by their text content, and image \<input> elements by their alt attribute
90
-
91
- # @macro waiting_behavior
92
159
  #
93
- # @param [String] locator Which button to find
94
- # @option options [Boolean] disabled (false) Match disabled button?
160
+ # @overload find_button([locator], options={})
161
+ # @param [String] locator id, value, title, text content, alt of image
162
+ #
163
+ # @overload find_button(options={})
164
+ #
165
+ # @macro waiting_behavior
166
+ #
167
+ # @option options [Boolean, Symbol] disabled (false) Match disabled button?
168
+ # * true - only finds a disabled button
169
+ # * false - only finds an enabled button
170
+ # * :all - finds either an enabled or disabled button
171
+ # @option options [String] 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
174
+ # @option options [String, Array<String>] class Match buttons that match the class(es) provided
95
175
  # @return [Capybara::Node::Element] The found element
96
176
  #
97
- def find_button(locator, options={})
98
- find(:button, locator, options)
177
+ def find_button(locator=nil, options={}, &optional_filter_block)
178
+ locator, options = nil, locator if locator.is_a? Hash
179
+ find(:button, locator, options, &optional_filter_block)
99
180
  end
100
181
 
101
182
  ##
@@ -104,15 +185,16 @@ module Capybara
104
185
  #
105
186
  # @macro waiting_behavior
106
187
  #
107
- # @param [String] id Which element to find
188
+ # @param [String] id id of element
108
189
  #
109
190
  # @return [Capybara::Node::Element] The found element
110
191
  #
111
- def find_by_id(id, options={})
112
- find(:id, id, options)
192
+ def find_by_id(id, options={}, &optional_filter_block)
193
+ find(:id, id, options, &optional_filter_block)
113
194
  end
114
195
 
115
196
  ##
197
+ # @!method all([kind = Capybara.default_selector], locator = nil, options = {})
116
198
  #
117
199
  # Find all elements on the page matching the given selector
118
200
  # and options.
@@ -122,7 +204,7 @@ module Capybara
122
204
  # following statements are equivalent:
123
205
  #
124
206
  # page.all(:css, 'a#person_123')
125
- # page.all(:xpath, '//a[@id="person_123"]')
207
+ # page.all(:xpath, './/a[@id="person_123"]')
126
208
  #
127
209
  #
128
210
  # If the type of selector is left out, Capybara uses
@@ -131,47 +213,56 @@ module Capybara
131
213
  # page.all("a#person_123")
132
214
  #
133
215
  # Capybara.default_selector = :xpath
134
- # page.all('//a[@id="person_123"]')
216
+ # page.all('.//a[@id="person_123"]')
135
217
  #
136
218
  # The set of found elements can further be restricted by specifying
137
219
  # options. It's possible to select elements by their text or visibility:
138
220
  #
139
- # page.all('a', :text => 'Home')
140
- # page.all('#menu li', :visible => true)
221
+ # page.all('a', text: 'Home')
222
+ # page.all('#menu li', visible: true)
141
223
  #
142
224
  # By default if no elements are found, an empty array is returned;
143
225
  # however, expectations can be set on the number of elements to be found which
144
226
  # will trigger Capybara's waiting behavior for the expectations to match.The
145
227
  # expectations can be set using
146
228
  #
147
- # page.assert_selector('p#foo', :count => 4)
148
- # page.assert_selector('p#foo', :maximum => 10)
149
- # page.assert_selector('p#foo', :minimum => 1)
150
- # page.assert_selector('p#foo', :between => 1..10)
229
+ # page.assert_selector('p#foo', count: 4)
230
+ # page.assert_selector('p#foo', maximum: 10)
231
+ # page.assert_selector('p#foo', minimum: 1)
232
+ # page.assert_selector('p#foo', between: 1..10)
151
233
  #
152
234
  # See {Capybara::Helpers#matches_count?} for additional information about
153
235
  # count matching.
154
236
  #
155
- # @overload all([kind], locator, options)
156
- # @param [:css, :xpath] kind The type of selector
157
- # @param [String] locator The selector
158
- # @option options [String, Regexp] text Only find elements which contain this text or match this regexp
159
- # @option options [Boolean, Symbol] visible Only find elements with the specified visibility:
237
+ # @param [Symbol] kind Optional selector type (:css, :xpath, :field, etc.) - Defaults to Capybara.default_selector
238
+ # @param [String] locator The selector
239
+ # @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
241
+ # @option options [Boolean, Symbol] visible Only find elements with the specified visibility:
160
242
  # * true - only finds visible elements.
161
243
  # * false - finds invisible _and_ visible elements.
162
244
  # * :all - same as false; finds visible and invisible elements.
163
245
  # * :hidden - only finds invisible elements.
164
246
  # * :visible - same as true; only finds visible elements.
165
- # @option options [Integer] count Exact number of matches that are expected to be found
166
- # @option options [Integer] maximum Maximum number of matches that are expected to be found
167
- # @option options [Integer] minimum Minimum number of matches that are expected to be found
168
- # @option options [Range] between Number of matches found must be within the given range
169
- # @option options [Boolean] exact Control whether `is` expressions in the given XPath match exactly or partially
170
- # @option options [Integer] wait (Capybara.default_max_wait_time) The time to wait for element count expectations to become true
247
+ # @option options [Integer] count Exact number of matches that are expected to be found
248
+ # @option options [Integer] maximum Maximum number of matches that are expected to be found
249
+ # @option options [Integer] minimum Minimum number of matches that are expected to be found
250
+ # @option options [Range] between Number of matches found must be within the given range
251
+ # @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
253
+ # @overload all([kind = Capybara.default_selector], locator = nil, options = {})
254
+ # @overload all([kind = Capybara.default_selector], locator = nil, options = {}, &filter_block)
255
+ # @yieldparam element [Capybara::Node::Element] The element being considered for inclusion in the results
256
+ # @yieldreturn [Boolean] Should the element be considered in the results?
171
257
  # @return [Capybara::Result] A collection of found elements
172
258
  #
173
- def all(*args)
174
- query = Capybara::Query.new(*args)
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)
175
266
  synchronize(query.wait) do
176
267
  result = query.resolve_for(self)
177
268
  raise Capybara::ExpectationNotMet, result.failure_message unless result.matches_count?
@@ -195,15 +286,36 @@ module Capybara
195
286
  # @param [Hash] options Additional options; see {#all}
196
287
  # @return [Capybara::Node::Element] The found element or nil
197
288
  #
198
- def first(*args)
199
- if Capybara.wait_on_first_by_default
289
+ def first(*args, &optional_filter_block)
290
+ if session_options.wait_on_first_by_default
200
291
  options = if args.last.is_a?(Hash) then args.pop.dup else {} end
201
292
  args.push({minimum: 1}.merge(options))
202
293
  end
203
- all(*args).first
294
+ all(*args, &optional_filter_block).first
204
295
  rescue Capybara::ExpectationNotMet
205
296
  nil
206
297
  end
298
+
299
+ private
300
+
301
+ def synced_resolve(query)
302
+ synchronize(query.wait) do
303
+ if (query.match == :smart or query.match == :prefer_exact)
304
+ result = query.resolve_for(self, true)
305
+ result = query.resolve_for(self, false) if result.empty? && query.supports_exact? && !query.exact?
306
+ else
307
+ result = query.resolve_for(self)
308
+ end
309
+
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
316
+ result.first
317
+ end.tap(&:allow_reload!)
318
+ end
207
319
  end
208
320
  end
209
321
  end