capybara 2.0.3 → 2.1.0.beta1

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 (81) hide show
  1. data.tar.gz.sig +0 -0
  2. data/History.md +73 -0
  3. data/README.md +52 -5
  4. data/lib/capybara.rb +44 -62
  5. data/lib/capybara/cucumber.rb +4 -1
  6. data/lib/capybara/driver/base.rb +13 -9
  7. data/lib/capybara/driver/node.rb +18 -6
  8. data/lib/capybara/helpers.rb +111 -22
  9. data/lib/capybara/node/actions.rb +24 -19
  10. data/lib/capybara/node/base.rb +25 -32
  11. data/lib/capybara/node/document.rb +6 -2
  12. data/lib/capybara/node/element.rb +45 -7
  13. data/lib/capybara/node/finders.rb +48 -21
  14. data/lib/capybara/node/matchers.rb +38 -21
  15. data/lib/capybara/node/simple.rb +29 -7
  16. data/lib/capybara/query.rb +72 -26
  17. data/lib/capybara/rack_test/browser.rb +11 -3
  18. data/lib/capybara/rack_test/css_handlers.rb +8 -0
  19. data/lib/capybara/rack_test/driver.rb +11 -3
  20. data/lib/capybara/rack_test/form.rb +8 -1
  21. data/lib/capybara/rack_test/node.rb +92 -36
  22. data/lib/capybara/rails.rb +1 -2
  23. data/lib/capybara/result.rb +21 -37
  24. data/lib/capybara/rspec/matchers.rb +60 -31
  25. data/lib/capybara/selector.rb +46 -13
  26. data/lib/capybara/selenium/driver.rb +42 -6
  27. data/lib/capybara/selenium/node.rb +23 -5
  28. data/lib/capybara/session.rb +46 -11
  29. data/lib/capybara/spec/public/test.js +12 -0
  30. data/lib/capybara/spec/session/all_spec.rb +9 -8
  31. data/lib/capybara/spec/session/attach_file_spec.rb +14 -0
  32. data/lib/capybara/spec/session/check_spec.rb +14 -0
  33. data/lib/capybara/spec/session/choose_spec.rb +14 -0
  34. data/lib/capybara/spec/session/click_button_spec.rb +77 -1
  35. data/lib/capybara/spec/session/click_link_or_button_spec.rb +65 -0
  36. data/lib/capybara/spec/session/click_link_spec.rb +24 -0
  37. data/lib/capybara/spec/session/current_scope_spec.rb +29 -0
  38. data/lib/capybara/spec/session/fill_in_spec.rb +14 -0
  39. data/lib/capybara/spec/session/find_button_spec.rb +12 -0
  40. data/lib/capybara/spec/session/find_by_id_spec.rb +12 -1
  41. data/lib/capybara/spec/session/find_field_spec.rb +30 -0
  42. data/lib/capybara/spec/session/find_link_spec.rb +12 -0
  43. data/lib/capybara/spec/session/find_spec.rb +258 -16
  44. data/lib/capybara/spec/session/first_spec.rb +25 -8
  45. data/lib/capybara/spec/session/has_css_spec.rb +2 -2
  46. data/lib/capybara/spec/session/has_field_spec.rb +10 -2
  47. data/lib/capybara/spec/session/has_selector_spec.rb +9 -0
  48. data/lib/capybara/spec/session/has_text_spec.rb +96 -0
  49. data/lib/capybara/spec/session/has_title_spec.rb +47 -0
  50. data/lib/capybara/spec/session/node_spec.rb +43 -0
  51. data/lib/capybara/spec/session/reset_session_spec.rb +10 -2
  52. data/lib/capybara/spec/session/save_page_spec.rb +30 -0
  53. data/lib/capybara/spec/session/select_spec.rb +65 -0
  54. data/lib/capybara/spec/session/text_spec.rb +31 -0
  55. data/lib/capybara/spec/session/title_spec.rb +16 -0
  56. data/lib/capybara/spec/session/uncheck_spec.rb +14 -0
  57. data/lib/capybara/spec/session/unselect_spec.rb +42 -0
  58. data/lib/capybara/spec/session/visit_spec.rb +3 -3
  59. data/lib/capybara/spec/session/within_frame_spec.rb +14 -0
  60. data/lib/capybara/spec/session/within_spec.rb +3 -3
  61. data/lib/capybara/spec/spec_helper.rb +6 -1
  62. data/lib/capybara/spec/views/form.erb +39 -2
  63. data/lib/capybara/spec/views/frame_child.erb +9 -0
  64. data/lib/capybara/spec/views/frame_parent.erb +8 -0
  65. data/lib/capybara/spec/views/with_base_tag.erb +10 -0
  66. data/lib/capybara/spec/views/with_count.erb +7 -0
  67. data/lib/capybara/spec/views/with_hover.erb +17 -0
  68. data/lib/capybara/spec/views/with_html.erb +21 -2
  69. data/lib/capybara/spec/views/with_js.erb +5 -0
  70. data/lib/capybara/spec/views/with_scope.erb +6 -1
  71. data/lib/capybara/spec/views/with_title.erb +1 -0
  72. data/lib/capybara/spec/views/within_frames.erb +1 -0
  73. data/lib/capybara/version.rb +1 -1
  74. data/spec/basic_node_spec.rb +75 -24
  75. data/spec/dsl_spec.rb +2 -1
  76. data/spec/rack_test_spec.rb +4 -3
  77. data/spec/rspec/matchers_spec.rb +105 -17
  78. data/spec/server_spec.rb +8 -8
  79. data/spec/spec_helper.rb +2 -1
  80. metadata +83 -23
  81. metadata.gz.sig +0 -0
@@ -1,31 +1,120 @@
1
1
  # encoding: UTF-8
2
2
 
3
3
  module Capybara
4
+
5
+ # @api private
4
6
  module Helpers
5
- class << self
6
- ##
7
- #
8
- # Normalizes whitespace space by stripping leading and trailing
9
- # whitespace and replacing sequences of whitespace characters
10
- # with a single space.
11
- #
12
- # @param [String] text Text to normalize
13
- # @return [String] Normalized text
14
- #
15
- def normalize_whitespace(text)
16
- text.to_s.gsub(/[[:space:]]+/, ' ').strip
7
+ extend self
8
+
9
+ ##
10
+ #
11
+ # Normalizes whitespace space by stripping leading and trailing
12
+ # whitespace and replacing sequences of whitespace characters
13
+ # with a single space.
14
+ #
15
+ # @param [String] text Text to normalize
16
+ # @return [String] Normalized text
17
+ #
18
+ def normalize_whitespace(text)
19
+ text.to_s.gsub(/[[:space:]]+/, ' ').strip
20
+ end
21
+
22
+ ##
23
+ #
24
+ # Escapes any characters that would have special meaning in a regexp
25
+ # if text is not a regexp
26
+ #
27
+ # @param [String] text Text to escape
28
+ # @return [String] Escaped text
29
+ #
30
+ def to_regexp(text)
31
+ text.is_a?(Regexp) ? text : Regexp.new(Regexp.escape(normalize_whitespace(text)))
32
+ end
33
+
34
+ ##
35
+ #
36
+ # Injects a `<base>` tag into the given HTML code, pointing to
37
+ # `Capybara.asset_host`.
38
+ #
39
+ # @param [String] html HTML code to inject into
40
+ # @param [String] The modified HTML code
41
+ #
42
+ def inject_asset_host(html)
43
+ if Capybara.asset_host
44
+ if Nokogiri::HTML(html).css("base").empty? and match = html.match(/<head[^<]*?>/)
45
+ html.clone.insert match.end(0), "<base href='#{Capybara.asset_host}' />"
46
+ end
47
+ else
48
+ html
17
49
  end
50
+ end
51
+
52
+ ##
53
+ #
54
+ # Checks if the given count matches the given count options. By default,
55
+ # when no options are given, count should be larger than zero.
56
+ #
57
+ # @param [Integer] count The actual number. Should be coercible via Integer()
58
+ # @option [Range] between Count must be within the given range
59
+ # @option [Integer] count Count must be exactly this
60
+ # @option [Integer] maximum Count must be smaller than or equal to this value
61
+ # @option [Integer] minimum Count must be larger than or equal to this value
62
+ #
63
+ def matches_count?(count, options={})
64
+ case
65
+ when options[:between]
66
+ options[:between] === count
67
+ when options[:count]
68
+ Integer(options[:count]) == count
69
+ when options[:maximum]
70
+ Integer(options[:maximum]) >= count
71
+ when options[:minimum]
72
+ Integer(options[:minimum]) <= count
73
+ else
74
+ count > 0
75
+ end
76
+ end
77
+
78
+ ##
79
+ #
80
+ # Generates a failure message given a description of the query and count
81
+ # options.
82
+ #
83
+ # @param [String] description Description of a query
84
+ # @option [Range] between Count should have been within the given range
85
+ # @option [Integer] count Count should have been exactly this
86
+ # @option [Integer] maximum Count should have been smaller than or equal to this value
87
+ # @option [Integer] minimum Count should have been larger than or equal to this value
88
+ #
89
+ def failure_message(description, options={})
90
+ message = "expected to find #{description}"
91
+ if options[:count]
92
+ message << " #{options[:count]} #{declension('time', 'times', options[:count])}"
93
+ elsif options[:between]
94
+ message << " between #{options[:between].first} and #{options[:between].last} times"
95
+ elsif options[:maximum]
96
+ message << " at most #{options[:maximum]} #{declension('time', 'times', options[:maximum])}"
97
+ elsif options[:minimum]
98
+ message << " at least #{options[:minimum]} #{declension('time', 'times', options[:minimum])}"
99
+ end
100
+ message
101
+ end
18
102
 
19
- ##
20
- #
21
- # Escapes any characters that would have special meaning in a regexp
22
- # if text is not a regexp
23
- #
24
- # @param [String] text Text to escape
25
- # @return [String] Escaped text
26
- #
27
- def to_regexp(text)
28
- text.is_a?(Regexp) ? text : Regexp.escape(normalize_whitespace(text))
103
+ ##
104
+ #
105
+ # A poor man's `pluralize`. Given two declensions, one singular and one
106
+ # plural, as well as a count, this will pick the correct declension. This
107
+ # way we can generate gramatically correct error message.
108
+ #
109
+ # @param [String] singular The singular form of the word
110
+ # @param [String] plural The plural form of the word
111
+ # @param [Integer] count The number of items
112
+ #
113
+ def declension(singular, plural, count)
114
+ if count == 1
115
+ singular
116
+ else
117
+ plural
29
118
  end
30
119
  end
31
120
  end
@@ -9,8 +9,8 @@ module Capybara
9
9
  #
10
10
  # @param [String] locator Text, id or value of link or button
11
11
  #
12
- def click_link_or_button(locator)
13
- find(:link_or_button, locator).click
12
+ def click_link_or_button(locator, options={})
13
+ find(:link_or_button, locator, options).click
14
14
  end
15
15
  alias_method :click_on, :click_link_or_button
16
16
 
@@ -20,9 +20,11 @@ module Capybara
20
20
  # alt text inside the link.
21
21
  #
22
22
  # @param [String] locator Text, id or text of link
23
+ # @param options
24
+ # @option options [String] :href The value the href attribute must be
23
25
  #
24
- def click_link(locator)
25
- find(:link, locator).click
26
+ def click_link(locator, options={})
27
+ find(:link, locator, options).click
26
28
  end
27
29
 
28
30
  ##
@@ -31,8 +33,8 @@ module Capybara
31
33
  #
32
34
  # @param [String] locator Text, id or value of button
33
35
  #
34
- def click_button(locator)
35
- find(:button, locator).click
36
+ def click_button(locator, options={})
37
+ find(:button, locator, options).click
36
38
  end
37
39
 
38
40
  ##
@@ -47,7 +49,8 @@ module Capybara
47
49
  #
48
50
  def fill_in(locator, options={})
49
51
  raise "Must pass a hash containing 'with'" if not options.is_a?(Hash) or not options.has_key?(:with)
50
- find(:fillable_field, locator).set(options[:with])
52
+ with = options.delete(:with)
53
+ find(:fillable_field, locator, options).set(with)
51
54
  end
52
55
 
53
56
  ##
@@ -59,8 +62,8 @@ module Capybara
59
62
  #
60
63
  # @param [String] locator Which radio button to choose
61
64
  #
62
- def choose(locator)
63
- find(:radio_button, locator).set(true)
65
+ def choose(locator, options={})
66
+ find(:radio_button, locator, options).set(true)
64
67
  end
65
68
 
66
69
  ##
@@ -72,8 +75,8 @@ module Capybara
72
75
  #
73
76
  # @param [String] locator Which check box to check
74
77
  #
75
- def check(locator)
76
- find(:checkbox, locator).set(true)
78
+ def check(locator, options={})
79
+ find(:checkbox, locator, options).set(true)
77
80
  end
78
81
 
79
82
  ##
@@ -85,8 +88,8 @@ module Capybara
85
88
  #
86
89
  # @param [String] locator Which check box to uncheck
87
90
  #
88
- def uncheck(locator)
89
- find(:checkbox, locator).set(false)
91
+ def uncheck(locator, options={})
92
+ find(:checkbox, locator, options).set(false)
90
93
  end
91
94
 
92
95
  ##
@@ -102,9 +105,10 @@ module Capybara
102
105
  #
103
106
  def select(value, options={})
104
107
  if options.has_key?(:from)
105
- find(:select, options[:from]).find(:option, value).select_option
108
+ from = options.delete(:from)
109
+ find(:select, from, options).find(:option, value, options).select_option
106
110
  else
107
- find(:option, value).select_option
111
+ find(:option, value, options).select_option
108
112
  end
109
113
  end
110
114
 
@@ -121,9 +125,10 @@ module Capybara
121
125
  #
122
126
  def unselect(value, options={})
123
127
  if options.has_key?(:from)
124
- find(:select, options[:from]).find(:option, value).unselect_option
128
+ from = options.delete(:from)
129
+ find(:select, from, options).find(:option, value, options).unselect_option
125
130
  else
126
- find(:option, value).unselect_option
131
+ find(:option, value, options).unselect_option
127
132
  end
128
133
  end
129
134
 
@@ -137,11 +142,11 @@ module Capybara
137
142
  # @param [String] locator Which field to attach the file to
138
143
  # @param [String] path The path of the file that will be attached, or an array of paths
139
144
  #
140
- def attach_file(locator, path)
145
+ def attach_file(locator, path, options={})
141
146
  Array(path).each do |p|
142
147
  raise Capybara::FileNotFound, "cannot attach file, #{p} does not exist" unless File.exist?(p.to_s)
143
148
  end
144
- find(:file_field, locator).set(path)
149
+ find(:file_field, locator, options).set(path)
145
150
  end
146
151
  end
147
152
  end
@@ -31,7 +31,6 @@ module Capybara
31
31
  def initialize(session, base)
32
32
  @session = session
33
33
  @base = base
34
- @unsynchronized = false
35
34
  end
36
35
 
37
36
  # overridden in subclasses, e.g. Capybara::Node::Element
@@ -64,50 +63,44 @@ module Capybara
64
63
  # until a certain amount of time passes. The amount of time defaults to
65
64
  # {Capybara.default_wait_time} and can be overriden through the `seconds`
66
65
  # argument. This time is compared with the system time to see how much
67
- # time has passed. If the return value of {Time.now} is stubbed out,
66
+ # time has passed. If the return value of `Time.now` is stubbed out,
68
67
  # Capybara will raise `Capybara::FrozenInTime`.
69
68
  #
70
- # @param [Integer] seconds Number of seconds to retry this block
69
+ # @param [Integer] seconds Number of seconds to retry this block
71
70
  # @return [Object] The result of the given block
72
- # @raise [Capybara::FrozenInTime] If the return value of {Time.now} appears stuck
71
+ # @raise [Capybara::FrozenInTime] If the return value of `Time.now` appears stuck
73
72
  #
74
73
  def synchronize(seconds=Capybara.default_wait_time)
75
74
  start_time = Time.now
76
75
 
77
- begin
76
+ if session.synchronized
78
77
  yield
79
- rescue => e
80
- raise e if @unsynchronized
81
- raise e unless driver.wait?
82
- raise e unless driver.invalid_element_errors.include?(e.class) || e.is_a?(Capybara::ElementNotFound)
83
- raise e if (Time.now - start_time) >= seconds
84
- sleep(0.05)
85
- raise Capybara::FrozenInTime, "time appears to be frozen, Capybara does not work with libraries which freeze time, consider using time travelling instead" if Time.now == start_time
86
- reload if Capybara.automatic_reload
87
- retry
78
+ else
79
+ session.synchronized = true
80
+ begin
81
+ yield
82
+ rescue => e
83
+ raise e unless driver.wait?
84
+ raise e unless catch_error?(e)
85
+ raise e if (Time.now - start_time) >= seconds
86
+ sleep(0.05)
87
+ raise Capybara::FrozenInTime, "time appears to be frozen, Capybara does not work with libraries which freeze time, consider using time travelling instead" if Time.now == start_time
88
+ reload if Capybara.automatic_reload
89
+ retry
90
+ ensure
91
+ session.synchronized = false
92
+ end
88
93
  end
89
94
  end
90
95
 
91
- ##
92
- #
93
- # Within the given block, prevent synchronize from having any effect.
94
- #
95
- # This is an internal method which should not be called unless you are
96
- # absolutely sure of what you're doing.
97
- #
98
- # @api private
99
- # @return [Object] The result of the given block
100
- #
101
- def unsynchronized
102
- orig = @unsynchronized
103
- @unsynchronized = true
104
- yield
105
- ensure
106
- @unsynchronized = orig
107
- end
108
-
109
96
  protected
110
97
 
98
+ def catch_error?(error)
99
+ (driver.invalid_element_errors + [Capybara::ElementNotFound]).any? do |type|
100
+ error.is_a?(type)
101
+ end
102
+ end
103
+
111
104
  def driver
112
105
  session.driver
113
106
  end
@@ -17,8 +17,12 @@ module Capybara
17
17
  #
18
18
  # @return [String] The text of the document
19
19
  #
20
- def text
21
- find(:xpath, '/html').text
20
+ def text(type=nil)
21
+ find(:xpath, '/html').text(type)
22
+ end
23
+
24
+ def title
25
+ session.driver.title
22
26
  end
23
27
  end
24
28
  end
@@ -42,10 +42,26 @@ module Capybara
42
42
 
43
43
  ##
44
44
  #
45
- # @return [String] The text of the element
46
- #
47
- def text
48
- synchronize { base.text }
45
+ # Retrieve the text of the element. If `Capybara.ignore_hidden_elements`
46
+ # is `true`, which it is by default, then this will return only text
47
+ # which is visible. The exact semantics of this may differ between
48
+ # drivers, but generally any text within elements with `display:none` is
49
+ # ignored. This behaviour can be overridden by passing `:all` to this
50
+ # method.
51
+ #
52
+ # @param [:all, :visible] Whether to return only visible or all text
53
+ #
54
+ # @return [String] The text of the element
55
+ #
56
+ def text(type=nil)
57
+ type ||= :all unless Capybara.ignore_hidden_elements or Capybara.visible_text_only
58
+ synchronize do
59
+ if type == :all
60
+ base.all_text
61
+ else
62
+ base.visible_text
63
+ end
64
+ end
49
65
  end
50
66
 
51
67
  ##
@@ -103,6 +119,14 @@ module Capybara
103
119
  synchronize { base.click }
104
120
  end
105
121
 
122
+ ##
123
+ #
124
+ # Hover on the Element
125
+ #
126
+ def hover
127
+ synchronize { base.hover }
128
+ end
129
+
106
130
  ##
107
131
  #
108
132
  # @return [String] The tag name of the element
@@ -142,6 +166,16 @@ module Capybara
142
166
  synchronize { base.selected? }
143
167
  end
144
168
 
169
+ ##
170
+ #
171
+ # Whether or not the element is disabled.
172
+ #
173
+ # @return [Boolean] Whether the element is disabled
174
+ #
175
+ def disabled?
176
+ synchronize { base.disabled? }
177
+ end
178
+
145
179
  ##
146
180
  #
147
181
  # An XPath expression describing where on the page the element can be found
@@ -179,15 +213,19 @@ module Capybara
179
213
 
180
214
  def reload
181
215
  if @allow_reload
182
- reloaded = parent.reload.first(@query.name, @query.locator, @query.options)
183
- @base = reloaded.base if reloaded
216
+ begin
217
+ reloaded = parent.reload.first(@query.name, @query.locator, @query.options)
218
+ @base = reloaded.base if reloaded
219
+ rescue => e
220
+ raise e unless catch_error?(e)
221
+ end
184
222
  end
185
223
  self
186
224
  end
187
225
 
188
226
  def inspect
189
227
  %(#<Capybara::Element tag="#{tag_name}" path="#{path}">)
190
- rescue NotSupportedByDriverError
228
+ rescue NotSupportedByDriverError, 'Capybara::Node::Element#inspect'
191
229
  %(#<Capybara::Element tag="#{tag_name}">)
192
230
  end
193
231
  end
@@ -19,11 +19,29 @@ module Capybara
19
19
  # page.find('li', :text => 'Quox').click_link('Delete')
20
20
  #
21
21
  # @param (see Capybara::Node::Finders#all)
22
- # @return [Capybara::Element] The found element
23
- # @raise [Capybara::ElementNotFound] If the element can't be found before time expires
22
+ # @option options [Boolean] match The matching strategy to use.
23
+ # @option options [false, Numeric] wait How long to wait for the element to appear.
24
+ #
25
+ # @return [Capybara::Element] The found element
26
+ # @raise [Capybara::ElementNotFound] If the element can't be found before time expires
24
27
  #
25
28
  def find(*args)
26
- synchronize { all(*args).find! }.tap(&:allow_reload!)
29
+ query = Capybara::Query.new(*args)
30
+ synchronize(query.wait) do
31
+ if query.match == :smart or query.match == :prefer_exact
32
+ result = resolve_query(query, true)
33
+ result = resolve_query(query, false) if result.size == 0 and not query.exact?
34
+ else
35
+ result = resolve_query(query)
36
+ end
37
+ if query.match == :one or query.match == :smart and result.size > 1
38
+ raise Capybara::Ambiguous.new("Ambiguous match, found #{result.size} elements matching #{query.description}")
39
+ end
40
+ if result.size == 0
41
+ raise Capybara::ElementNotFound.new("Unable to find #{query.description}")
42
+ end
43
+ result.first
44
+ end.tap(&:allow_reload!)
27
45
  end
28
46
 
29
47
  ##
@@ -33,8 +51,8 @@ module Capybara
33
51
  # @param [String] locator Which field to find
34
52
  # @return [Capybara::Element] The found element
35
53
  #
36
- def find_field(locator)
37
- find(:field, locator)
54
+ def find_field(locator, options={})
55
+ find(:field, locator, options)
38
56
  end
39
57
  alias_method :field_labeled, :find_field
40
58
 
@@ -45,8 +63,8 @@ module Capybara
45
63
  # @param [String] locator Which link to find
46
64
  # @return [Capybara::Element] The found element
47
65
  #
48
- def find_link(locator)
49
- find(:link, locator)
66
+ def find_link(locator, options={})
67
+ find(:link, locator, options)
50
68
  end
51
69
 
52
70
  ##
@@ -56,8 +74,8 @@ module Capybara
56
74
  # @param [String] locator Which button to find
57
75
  # @return [Capybara::Element] The found element
58
76
  #
59
- def find_button(locator)
60
- find(:button, locator)
77
+ def find_button(locator, options={})
78
+ find(:button, locator, options)
61
79
  end
62
80
 
63
81
  ##
@@ -67,8 +85,8 @@ module Capybara
67
85
  # @param [String] id Which element to find
68
86
  # @return [Capybara::Element] The found element
69
87
  #
70
- def find_by_id(id)
71
- find(:id, id)
88
+ def find_by_id(id, options={})
89
+ find(:id, id, options)
72
90
  end
73
91
 
74
92
  ##
@@ -103,18 +121,12 @@ module Capybara
103
121
  # @param [String] locator The selector
104
122
  # @option options [String, Regexp] text Only find elements which contain this text or match this regexp
105
123
  # @option options [Boolean] visible Only find elements that are visible on the page. Setting this to false
106
- # (the default, unless Capybara.ignore_hidden_elements = true), finds
107
- # invisible _and_ visible elements.
108
- # @return [Array[Capybara::Element]] The found elements
124
+ # finds invisible _and_ visible elements.
125
+ # @option options [Boolean] exact Control whether `is` expressions in the given XPath match exactly or partially
126
+ # @return [Capybara::Result] A collection of found elements
109
127
  #
110
128
  def all(*args)
111
- query = Capybara::Query.new(*args)
112
- elements = synchronize do
113
- base.find(query.xpath).map do |node|
114
- Capybara::Node::Element.new(session, node, self, query)
115
- end
116
- end
117
- Capybara::Result.new(elements, query)
129
+ resolve_query(Capybara::Query.new(*args))
118
130
  end
119
131
 
120
132
  ##
@@ -131,6 +143,21 @@ module Capybara
131
143
  def first(*args)
132
144
  all(*args).first
133
145
  end
146
+
147
+ private
148
+
149
+ def resolve_query(query, exact=nil)
150
+ elements = synchronize do
151
+ if query.selector.format==:css
152
+ base.find_css(query.css)
153
+ else
154
+ base.find_xpath(query.xpath(exact))
155
+ end.map do |node|
156
+ Capybara::Node::Element.new(session, node, self, query)
157
+ end
158
+ end
159
+ Capybara::Result.new(elements, query)
160
+ end
134
161
  end
135
162
  end
136
163
  end