capybara 2.7.1 → 2.8.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.
- checksums.yaml +4 -4
- data/History.md +22 -0
- data/README.md +27 -3
- data/lib/capybara.rb +19 -4
- data/lib/capybara/driver/base.rb +6 -2
- data/lib/capybara/driver/node.rb +13 -5
- data/lib/capybara/helpers.rb +2 -2
- data/lib/capybara/node/actions.rb +116 -17
- data/lib/capybara/node/base.rb +7 -1
- data/lib/capybara/node/element.rb +23 -3
- data/lib/capybara/node/finders.rb +45 -29
- data/lib/capybara/node/matchers.rb +13 -15
- data/lib/capybara/queries/selector_query.rb +22 -5
- data/lib/capybara/queries/text_query.rb +42 -6
- data/lib/capybara/rack_test/node.rb +13 -1
- data/lib/capybara/result.rb +80 -8
- data/lib/capybara/rspec/features.rb +13 -6
- data/lib/capybara/selector.rb +98 -71
- data/lib/capybara/selector/filter_set.rb +46 -0
- data/lib/capybara/selenium/driver.rb +22 -23
- data/lib/capybara/selenium/node.rb +14 -6
- data/lib/capybara/server.rb +20 -10
- data/lib/capybara/session.rb +44 -8
- data/lib/capybara/spec/session/all_spec.rb +4 -4
- data/lib/capybara/spec/session/assert_text.rb +23 -0
- data/lib/capybara/spec/session/check_spec.rb +66 -8
- data/lib/capybara/spec/session/choose_spec.rb +20 -0
- data/lib/capybara/spec/session/click_button_spec.rb +0 -3
- data/lib/capybara/spec/session/click_link_spec.rb +7 -0
- data/lib/capybara/spec/session/execute_script_spec.rb +6 -1
- data/lib/capybara/spec/session/find_button_spec.rb +19 -1
- data/lib/capybara/spec/session/find_field_spec.rb +21 -1
- data/lib/capybara/spec/session/find_link_spec.rb +19 -1
- data/lib/capybara/spec/session/find_spec.rb +32 -4
- data/lib/capybara/spec/session/first_spec.rb +4 -4
- data/lib/capybara/spec/session/has_field_spec.rb +4 -0
- data/lib/capybara/spec/session/has_text_spec.rb +2 -2
- data/lib/capybara/spec/session/node_spec.rb +24 -3
- data/lib/capybara/spec/session/reset_session_spec.rb +7 -0
- data/lib/capybara/spec/session/selectors_spec.rb +14 -0
- data/lib/capybara/spec/session/uncheck_spec.rb +39 -0
- data/lib/capybara/spec/session/window/switch_to_window_spec.rb +4 -2
- data/lib/capybara/spec/session/window/window_opened_by_spec.rb +2 -1
- data/lib/capybara/spec/session/window/window_spec.rb +36 -22
- data/lib/capybara/spec/session/within_frame_spec.rb +19 -0
- data/lib/capybara/spec/spec_helper.rb +3 -0
- data/lib/capybara/spec/views/form.erb +34 -6
- data/lib/capybara/spec/views/with_html.erb +5 -1
- data/lib/capybara/spec/views/with_unload_alert.erb +3 -1
- data/lib/capybara/spec/views/with_windows.erb +2 -0
- data/lib/capybara/version.rb +1 -1
- data/spec/capybara_spec.rb +34 -0
- data/spec/rack_test_spec.rb +24 -0
- data/spec/result_spec.rb +25 -0
- data/spec/rspec/features_spec.rb +3 -3
- data/spec/selenium_spec.rb +6 -3
- data/spec/server_spec.rb +2 -2
- metadata +18 -4
data/lib/capybara/node/base.rb
CHANGED
@@ -23,7 +23,7 @@ module Capybara
|
|
23
23
|
# session.has_css?('#foobar') # from Capybara::Node::Matchers
|
24
24
|
#
|
25
25
|
class Base
|
26
|
-
attr_reader :session, :base, :
|
26
|
+
attr_reader :session, :base, :query_scope
|
27
27
|
|
28
28
|
include Capybara::Node::Finders
|
29
29
|
include Capybara::Node::Actions
|
@@ -108,6 +108,12 @@ module Capybara
|
|
108
108
|
base.find_xpath(xpath)
|
109
109
|
end
|
110
110
|
|
111
|
+
# @deprecated Use query_scope instead
|
112
|
+
def parent
|
113
|
+
warn "DEPRECATED: #parent is deprecated in favor of #query_scope - Note: #parent was not the elements parent in the document so it's most likely not what you wanted anyway"
|
114
|
+
query_scope
|
115
|
+
end
|
116
|
+
|
111
117
|
protected
|
112
118
|
|
113
119
|
def catch_error?(error, errors = nil)
|
@@ -23,9 +23,9 @@ module Capybara
|
|
23
23
|
#
|
24
24
|
class Element < Base
|
25
25
|
|
26
|
-
def initialize(session, base,
|
26
|
+
def initialize(session, base, query_scope, query)
|
27
27
|
super(session, base)
|
28
|
-
@
|
28
|
+
@query_scope = query_scope
|
29
29
|
@query = query
|
30
30
|
end
|
31
31
|
|
@@ -282,6 +282,26 @@ module Capybara
|
|
282
282
|
synchronize { base.disabled? }
|
283
283
|
end
|
284
284
|
|
285
|
+
##
|
286
|
+
#
|
287
|
+
# Whether or not the element is readonly.
|
288
|
+
#
|
289
|
+
# @return [Boolean] Whether the element is readonly
|
290
|
+
#
|
291
|
+
def readonly?
|
292
|
+
synchronize { base.readonly? }
|
293
|
+
end
|
294
|
+
|
295
|
+
##
|
296
|
+
#
|
297
|
+
# Whether or not the element supports multiple results.
|
298
|
+
#
|
299
|
+
# @return [Boolean] Whether the element supports multiple results.
|
300
|
+
#
|
301
|
+
def multiple?
|
302
|
+
synchronize { base.multiple? }
|
303
|
+
end
|
304
|
+
|
285
305
|
##
|
286
306
|
#
|
287
307
|
# An XPath expression describing where on the page the element can be found
|
@@ -320,7 +340,7 @@ module Capybara
|
|
320
340
|
def reload
|
321
341
|
if @allow_reload
|
322
342
|
begin
|
323
|
-
reloaded =
|
343
|
+
reloaded = query_scope.reload.first(@query.name, @query.locator, @query.options)
|
324
344
|
@base = reloaded.base if reloaded
|
325
345
|
rescue => e
|
326
346
|
raise e unless catch_error?(e)
|
@@ -33,14 +33,14 @@ module Capybara
|
|
33
33
|
synchronize(query.wait) do
|
34
34
|
if query.match == :smart or query.match == :prefer_exact
|
35
35
|
result = query.resolve_for(self, true)
|
36
|
-
result = query.resolve_for(self, false) if result.
|
36
|
+
result = query.resolve_for(self, false) if result.empty? && !query.exact?
|
37
37
|
else
|
38
38
|
result = query.resolve_for(self)
|
39
39
|
end
|
40
40
|
if query.match == :one or query.match == :smart and result.size > 1
|
41
41
|
raise Capybara::Ambiguous.new("Ambiguous match, found #{result.size} elements matching #{query.description}")
|
42
42
|
end
|
43
|
-
if result.
|
43
|
+
if result.empty?
|
44
44
|
raise Capybara::ElementNotFound.new("Unable to find #{query.description}")
|
45
45
|
end
|
46
46
|
result.first
|
@@ -51,22 +51,30 @@ module Capybara
|
|
51
51
|
#
|
52
52
|
# Find a form field on the page. The field can be found by its name, id or label text.
|
53
53
|
#
|
54
|
-
# @
|
55
|
-
#
|
56
|
-
#
|
57
|
-
#
|
58
|
-
#
|
59
|
-
#
|
60
|
-
#
|
61
|
-
#
|
62
|
-
#
|
63
|
-
#
|
64
|
-
#
|
65
|
-
#
|
66
|
-
#
|
54
|
+
# @overload find_field([locator], options={})
|
55
|
+
# @param [String] locator name, id, placeholder or text of associated label element
|
56
|
+
#
|
57
|
+
# @macro waiting_behavior
|
58
|
+
#
|
59
|
+
#
|
60
|
+
# @option options [Boolean] checked Match checked field?
|
61
|
+
# @option options [Boolean] unchecked Match unchecked field?
|
62
|
+
# @option options [Boolean, Symbol] disabled (false) Match disabled field?
|
63
|
+
# * true - only finds a disabled field
|
64
|
+
# * false - only finds an enabled field
|
65
|
+
# * :all - finds either an enabled or disabled field
|
66
|
+
# @option options [Boolean] readonly Match readonly field?
|
67
|
+
# @option options [String, Regexp] with Value of field to match on
|
68
|
+
# @option options [String] type Type of field to match on
|
69
|
+
# @option options [Boolean] multiple Match fields that can have multiple values?
|
70
|
+
# @option options [String] id Match fields that match the id attribute
|
71
|
+
# @option options [String] name Match fields that match the name attribute
|
72
|
+
# @option options [String] placeholder Match fields that match the placeholder attribute
|
67
73
|
# @return [Capybara::Node::Element] The found element
|
68
74
|
#
|
69
|
-
|
75
|
+
|
76
|
+
def find_field(locator=nil, options={})
|
77
|
+
locator, options = nil, locator if locator.is_a? Hash
|
70
78
|
find(:field, locator, options)
|
71
79
|
end
|
72
80
|
alias_method :field_labeled, :find_field
|
@@ -75,13 +83,16 @@ module Capybara
|
|
75
83
|
#
|
76
84
|
# Find a link on the page. The link can be found by its id or text.
|
77
85
|
#
|
78
|
-
# @
|
86
|
+
# @overload find_link([locator], options={})
|
87
|
+
# @param [String] locator id, title, text, or alt of enclosed img element
|
88
|
+
#
|
89
|
+
# @macro waiting_behavior
|
79
90
|
#
|
80
|
-
#
|
81
|
-
# @option options [String,Regexp] href Value to match against the links href
|
91
|
+
# @option options [String,Regexp] href Value to match against the links href
|
82
92
|
# @return [Capybara::Node::Element] The found element
|
83
93
|
#
|
84
|
-
def find_link(locator, options={})
|
94
|
+
def find_link(locator=nil, options={})
|
95
|
+
locator, options = nil, locator if locator.is_a? Hash
|
85
96
|
find(:link, locator, options)
|
86
97
|
end
|
87
98
|
|
@@ -91,17 +102,22 @@ module Capybara
|
|
91
102
|
# This can be any \<input> element of type submit, reset, image, button or it can be a
|
92
103
|
# \<button> element. All buttons can be found by their id, value, or title. \<button> elements can also be found
|
93
104
|
# by their text content, and image \<input> elements by their alt attribute
|
94
|
-
|
95
|
-
# @macro waiting_behavior
|
96
105
|
#
|
97
|
-
# @
|
98
|
-
#
|
99
|
-
#
|
100
|
-
#
|
101
|
-
#
|
106
|
+
# @overload find_button([locator], options={})
|
107
|
+
# @param [String] locator id, value, title, text content, alt of image
|
108
|
+
#
|
109
|
+
# @overload find_button(options={})
|
110
|
+
#
|
111
|
+
# @macro waiting_behavior
|
112
|
+
#
|
113
|
+
# @option options [Boolean, Symbol] disabled (false) Match disabled button?
|
114
|
+
# * true - only finds a disabled button
|
115
|
+
# * false - only finds an enabled button
|
116
|
+
# * :all - finds either an enabled or disabled button
|
102
117
|
# @return [Capybara::Node::Element] The found element
|
103
118
|
#
|
104
|
-
def find_button(locator, options={})
|
119
|
+
def find_button(locator=nil, options={})
|
120
|
+
locator, options = nil, locator if locator.is_a? Hash
|
105
121
|
find(:button, locator, options)
|
106
122
|
end
|
107
123
|
|
@@ -111,7 +127,7 @@ module Capybara
|
|
111
127
|
#
|
112
128
|
# @macro waiting_behavior
|
113
129
|
#
|
114
|
-
# @param [String] id
|
130
|
+
# @param [String] id id of element
|
115
131
|
#
|
116
132
|
# @return [Capybara::Node::Element] The found element
|
117
133
|
#
|
@@ -123,8 +123,7 @@ module Capybara
|
|
123
123
|
query = Capybara::Queries::SelectorQuery.new(*args)
|
124
124
|
synchronize(query.wait) do
|
125
125
|
result = query.resolve_for(self)
|
126
|
-
matches_count
|
127
|
-
unless matches_count && ((result.size > 0) || Capybara::Helpers.expects_none?(query.options))
|
126
|
+
unless result.matches_count? && ((!result.empty?) || Capybara::Helpers.expects_none?(query.options))
|
128
127
|
raise Capybara::ExpectationNotMet, result.failure_message
|
129
128
|
end
|
130
129
|
end
|
@@ -151,8 +150,7 @@ module Capybara
|
|
151
150
|
query = Capybara::Queries::SelectorQuery.new(*args)
|
152
151
|
synchronize(query.wait) do
|
153
152
|
result = query.resolve_for(self)
|
154
|
-
matches_count
|
155
|
-
if matches_count && ((result.size > 0) || Capybara::Helpers.expects_none?(query.options))
|
153
|
+
if result.matches_count? && ((!result.empty?) || Capybara::Helpers.expects_none?(query.options))
|
156
154
|
raise Capybara::ExpectationNotMet, result.negative_failure_message
|
157
155
|
end
|
158
156
|
end
|
@@ -179,7 +177,7 @@ module Capybara
|
|
179
177
|
def assert_matches_selector(*args)
|
180
178
|
query = Capybara::Queries::MatchQuery.new(*args)
|
181
179
|
synchronize(query.wait) do
|
182
|
-
result = query.resolve_for(self.
|
180
|
+
result = query.resolve_for(self.query_scope)
|
183
181
|
unless result.include? self
|
184
182
|
raise Capybara::ExpectationNotMet, "Item does not match the provided selector"
|
185
183
|
end
|
@@ -190,7 +188,7 @@ module Capybara
|
|
190
188
|
def assert_not_matches_selector(*args)
|
191
189
|
query = Capybara::Queries::MatchQuery.new(*args)
|
192
190
|
synchronize(query.wait) do
|
193
|
-
result = query.resolve_for(self.
|
191
|
+
result = query.resolve_for(self.query_scope)
|
194
192
|
if result.include? self
|
195
193
|
raise Capybara::ExpectationNotMet, 'Item matched the provided selector'
|
196
194
|
end
|
@@ -350,10 +348,10 @@ module Capybara
|
|
350
348
|
#
|
351
349
|
# Note: 'textarea' and 'select' are valid type values, matching the associated tag names.
|
352
350
|
#
|
353
|
-
# @param [String] locator
|
354
|
-
# @option options [String] :with
|
355
|
-
# @option options [String] :type
|
356
|
-
# @return [Boolean]
|
351
|
+
# @param [String] locator The label, name or id of a field to check for
|
352
|
+
# @option options [String, Regexp] :with The text content of the field or a Regexp to match
|
353
|
+
# @option options [String] :type The type attribute of the field
|
354
|
+
# @return [Boolean] Whether it exists
|
357
355
|
#
|
358
356
|
def has_field?(locator, options={})
|
359
357
|
has_selector?(:field, locator, options)
|
@@ -364,10 +362,10 @@ module Capybara
|
|
364
362
|
# Checks if the page or current node has no form field with the given
|
365
363
|
# label, name or id. See {Capybara::Node::Matchers#has_field?}.
|
366
364
|
#
|
367
|
-
# @param [String] locator
|
368
|
-
# @option options [String] :with
|
369
|
-
# @option options [String] :type
|
370
|
-
# @return [Boolean]
|
365
|
+
# @param [String] locator The label, name or id of a field to check for
|
366
|
+
# @option options [String, Regexp] :with The text content of the field or a Regexp to match
|
367
|
+
# @option options [String] :type The type attribute of the field
|
368
|
+
# @return [Boolean] Whether it doesn't exist
|
371
369
|
#
|
372
370
|
def has_no_field?(locator, options={})
|
373
371
|
has_no_selector?(:field, locator, options)
|
@@ -501,7 +499,7 @@ module Capybara
|
|
501
499
|
#
|
502
500
|
# @!macro text_query_params
|
503
501
|
# @overload $0(type, text, options = {})
|
504
|
-
# @param [:all, :visible] type Whether to check for only visible or all text
|
502
|
+
# @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`.
|
505
503
|
# @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.
|
506
504
|
# @option options [Integer] :count (nil) Number of times the text is expected to occur
|
507
505
|
# @option options [Integer] :minimum (nil) Minimum number of times the text is expected to occur
|
@@ -4,7 +4,7 @@ module Capybara
|
|
4
4
|
class SelectorQuery < Queries::BaseQuery
|
5
5
|
attr_accessor :selector, :locator, :options, :expression, :find, :negative
|
6
6
|
|
7
|
-
VALID_KEYS = [:text, :visible, :between, :count, :maximum, :minimum, :exact, :match, :wait]
|
7
|
+
VALID_KEYS = [:text, :visible, :between, :count, :maximum, :minimum, :exact, :match, :wait, :filter_set]
|
8
8
|
VALID_MATCH = [:first, :smart, :prefer_exact, :one]
|
9
9
|
|
10
10
|
def initialize(*args)
|
@@ -45,15 +45,19 @@ module Capybara
|
|
45
45
|
regexp = options[:text].is_a?(Regexp) ? options[:text] : Regexp.escape(options[:text].to_s)
|
46
46
|
return false if not node.text(visible).match(regexp)
|
47
47
|
end
|
48
|
+
|
48
49
|
case visible
|
49
50
|
when :visible then return false unless node.visible?
|
50
51
|
when :hidden then return false if node.visible?
|
51
52
|
end
|
52
|
-
|
53
|
+
|
54
|
+
query_filters.all? do |name, filter|
|
53
55
|
if options.has_key?(name)
|
54
|
-
|
56
|
+
filter.matches?(node, options[name])
|
55
57
|
elsif filter.default?
|
56
|
-
|
58
|
+
filter.matches?(node, filter.default)
|
59
|
+
else
|
60
|
+
true
|
57
61
|
end
|
58
62
|
end
|
59
63
|
end
|
@@ -124,7 +128,20 @@ module Capybara
|
|
124
128
|
private
|
125
129
|
|
126
130
|
def valid_keys
|
127
|
-
|
131
|
+
vk = COUNT_KEYS + [:text, :visible, :exact, :match, :wait, :filter_set]
|
132
|
+
vk + custom_keys
|
133
|
+
end
|
134
|
+
|
135
|
+
def query_filters
|
136
|
+
if options.has_key?(:filter_set)
|
137
|
+
Capybara::Selector::FilterSet.all[options[:filter_set]].filters
|
138
|
+
else
|
139
|
+
@selector.custom_filters
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def custom_keys
|
144
|
+
query_filters.keys
|
128
145
|
end
|
129
146
|
|
130
147
|
def assert_valid_keys
|
@@ -5,6 +5,7 @@ module Capybara
|
|
5
5
|
class TextQuery < BaseQuery
|
6
6
|
def initialize(*args)
|
7
7
|
@type = (args.first.is_a?(Symbol) || args.first.nil?) ? args.shift : nil
|
8
|
+
@type = (Capybara.ignore_hidden_elements or Capybara.visible_text_only) ? :visible : :all if @type.nil?
|
8
9
|
@expected_text, @options = args
|
9
10
|
unless @expected_text.is_a?(Regexp)
|
10
11
|
@expected_text = Capybara::Helpers.normalize_whitespace(@expected_text)
|
@@ -15,11 +16,22 @@ module Capybara
|
|
15
16
|
end
|
16
17
|
|
17
18
|
def resolve_for(node)
|
18
|
-
@
|
19
|
+
@node = node
|
20
|
+
@actual_text = text(node, @type)
|
19
21
|
@count = @actual_text.scan(@search_regexp).size
|
20
22
|
end
|
21
23
|
|
22
24
|
def failure_message
|
25
|
+
build_message(true)
|
26
|
+
end
|
27
|
+
|
28
|
+
def negative_failure_message
|
29
|
+
build_message(false).sub(/(to find)/, 'not \1')
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def build_message(report_on_invisible)
|
23
35
|
description =
|
24
36
|
if @expected_text.is_a?(Regexp)
|
25
37
|
"text matching #{@expected_text.inspect}"
|
@@ -32,17 +44,41 @@ module Capybara
|
|
32
44
|
message << " but found #{@count} #{Capybara::Helpers.declension('time', 'times', @count)}"
|
33
45
|
end
|
34
46
|
message << " in #{@actual_text.inspect}"
|
35
|
-
end
|
36
47
|
|
37
|
-
|
38
|
-
failure_message.sub(/(to find)/, 'not \1')
|
39
|
-
end
|
48
|
+
details_message = []
|
40
49
|
|
41
|
-
|
50
|
+
if @node and !@expected_text.is_a? Regexp
|
51
|
+
insensitive_regexp = Regexp.new(@expected_text, Regexp::IGNORECASE)
|
52
|
+
insensitive_count = @actual_text.scan(insensitive_regexp).size
|
53
|
+
if insensitive_count != @count
|
54
|
+
details_message << "it was found #{insensitive_count} #{Capybara::Helpers.declension("time", "times", insensitive_count)} using a case insensitive search"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
if @node and check_visible_text? and report_on_invisible
|
59
|
+
invisible_text = text(@node, :all)
|
60
|
+
invisible_count = invisible_text.scan(@search_regexp).size
|
61
|
+
if invisible_count != @count
|
62
|
+
details_message << ". it was found #{invisible_count} #{Capybara::Helpers.declension("time", "times", invisible_count)} including non-visible text"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
message << ". (However, #{details_message.join(' and ')}.)" unless details_message.empty?
|
67
|
+
|
68
|
+
message
|
69
|
+
end
|
42
70
|
|
43
71
|
def valid_keys
|
44
72
|
COUNT_KEYS + [:wait]
|
45
73
|
end
|
74
|
+
|
75
|
+
def check_visible_text?
|
76
|
+
@type == :visible
|
77
|
+
end
|
78
|
+
|
79
|
+
def text(node, query_type)
|
80
|
+
Capybara::Helpers.normalize_whitespace(node.text(query_type))
|
81
|
+
end
|
46
82
|
end
|
47
83
|
end
|
48
84
|
end
|
@@ -17,7 +17,7 @@ class Capybara::RackTest::Node < Capybara::Driver::Node
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def set(value)
|
20
|
-
if (Array === value) && !
|
20
|
+
if (Array === value) && !multiple?
|
21
21
|
raise ArgumentError.new "Value cannot be an Array when 'multiple' attribute is not present. Not a #{value.class}"
|
22
22
|
end
|
23
23
|
|
@@ -60,6 +60,16 @@ class Capybara::RackTest::Node < Capybara::Driver::Node
|
|
60
60
|
((tag_name == 'button') and type.nil? or type == "submit")
|
61
61
|
associated_form = form
|
62
62
|
Capybara::RackTest::Form.new(driver, associated_form).submit(self) if associated_form
|
63
|
+
elsif (tag_name == 'label')
|
64
|
+
labelled_control = if native[:for]
|
65
|
+
find_xpath("//input[@id='#{native[:for]}']").first
|
66
|
+
else
|
67
|
+
find_xpath(".//input").first
|
68
|
+
end
|
69
|
+
|
70
|
+
if labelled_control && (labelled_control.checkbox? || labelled_control.radio?)
|
71
|
+
labelled_control.set(!labelled_control.checked?)
|
72
|
+
end
|
63
73
|
end
|
64
74
|
end
|
65
75
|
|
@@ -183,6 +193,8 @@ private
|
|
183
193
|
self[attribute] && !self[attribute].empty?
|
184
194
|
end
|
185
195
|
|
196
|
+
protected
|
197
|
+
|
186
198
|
def checkbox?
|
187
199
|
input_field? && type == 'checkbox'
|
188
200
|
end
|
data/lib/capybara/result.rb
CHANGED
@@ -25,27 +25,74 @@ module Capybara
|
|
25
25
|
|
26
26
|
def initialize(elements, query)
|
27
27
|
@elements = elements
|
28
|
-
@
|
29
|
-
@
|
28
|
+
@result_cache = []
|
29
|
+
@results_enum = lazy_select_elements { |node| query.matches_filters?(node) }
|
30
30
|
@query = query
|
31
31
|
end
|
32
32
|
|
33
|
-
def_delegators
|
34
|
-
|
33
|
+
def_delegators :full_results, :size, :length, :last, :values_at, :inspect, :sample
|
34
|
+
|
35
|
+
alias :index :find_index
|
36
|
+
|
37
|
+
def each(&block)
|
38
|
+
@result_cache.each(&block)
|
39
|
+
loop do
|
40
|
+
next_result = @results_enum.next
|
41
|
+
@result_cache << next_result
|
42
|
+
block.call(next_result)
|
43
|
+
end
|
44
|
+
self
|
45
|
+
end
|
46
|
+
|
47
|
+
def [](*args)
|
48
|
+
if (args.size == 1) && ((idx = args[0]).is_a? Integer) && (idx > 0)
|
49
|
+
@result_cache << @results_enum.next while @result_cache.size <= idx
|
50
|
+
@result_cache[idx]
|
51
|
+
else
|
52
|
+
full_results[*args]
|
53
|
+
end
|
54
|
+
rescue StopIteration
|
55
|
+
return nil
|
56
|
+
end
|
57
|
+
alias :at :[]
|
58
|
+
|
59
|
+
def empty?
|
60
|
+
!any?
|
61
|
+
end
|
35
62
|
|
36
63
|
def matches_count?
|
37
|
-
|
64
|
+
return Integer(@query.options[:count]) == count if @query.options[:count]
|
65
|
+
|
66
|
+
return false if @query.options[:between] && !(@query.options[:between] === count)
|
67
|
+
|
68
|
+
if @query.options[:minimum]
|
69
|
+
begin
|
70
|
+
@result_cache << @results_enum.next while @result_cache.size < Integer(@query.options[:minimum])
|
71
|
+
rescue StopIteration
|
72
|
+
return false
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
if @query.options[:maximum]
|
77
|
+
begin
|
78
|
+
@result_cache << @results_enum.next while @result_cache.size <= Integer(@query.options[:maximum])
|
79
|
+
return false
|
80
|
+
rescue StopIteration
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
return true
|
38
85
|
end
|
39
86
|
|
40
87
|
def failure_message
|
41
88
|
message = Capybara::Helpers.failure_message(@query.description, @query.options)
|
42
89
|
if count > 0
|
43
|
-
message << ", found #{count} #{Capybara::Helpers.declension("match", "matches", count)}: " <<
|
90
|
+
message << ", found #{count} #{Capybara::Helpers.declension("match", "matches", count)}: " << full_results.map(&:text).map(&:inspect).join(", ")
|
44
91
|
else
|
45
92
|
message << " but there were no matches"
|
46
93
|
end
|
47
|
-
unless
|
48
|
-
elements =
|
94
|
+
unless rest.empty?
|
95
|
+
elements = rest.map(&:text).map(&:inspect).join(", ")
|
49
96
|
message << ". Also found " << elements << ", which matched the selector but not all filters."
|
50
97
|
end
|
51
98
|
message
|
@@ -54,5 +101,30 @@ module Capybara
|
|
54
101
|
def negative_failure_message
|
55
102
|
failure_message.sub(/(to find)/, 'not \1')
|
56
103
|
end
|
104
|
+
|
105
|
+
private
|
106
|
+
|
107
|
+
def full_results
|
108
|
+
loop do
|
109
|
+
@result_cache << @results_enum.next
|
110
|
+
end
|
111
|
+
@result_cache
|
112
|
+
end
|
113
|
+
|
114
|
+
def rest
|
115
|
+
@rest ||= @elements - full_results
|
116
|
+
end
|
117
|
+
|
118
|
+
def lazy_select_elements(&block)
|
119
|
+
if @elements.respond_to? :lazy #Ruby 2.0+
|
120
|
+
@elements.lazy.select &block
|
121
|
+
else
|
122
|
+
Enumerator.new do |yielder|
|
123
|
+
@elements.each do |val|
|
124
|
+
yielder.yield(val) if block.call(val)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
57
129
|
end
|
58
130
|
end
|