capybara 3.6.0 → 3.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/History.md +16 -0
- data/README.md +5 -1
- data/lib/capybara.rb +2 -0
- data/lib/capybara/minitest/spec.rb +1 -1
- data/lib/capybara/node/actions.rb +34 -25
- data/lib/capybara/node/base.rb +15 -17
- data/lib/capybara/node/document_matchers.rb +1 -3
- data/lib/capybara/node/element.rb +11 -12
- data/lib/capybara/node/finders.rb +2 -1
- data/lib/capybara/node/simple.rb +13 -6
- data/lib/capybara/queries/base_query.rb +4 -4
- data/lib/capybara/queries/selector_query.rb +119 -94
- data/lib/capybara/queries/text_query.rb +2 -1
- data/lib/capybara/rack_test/form.rb +4 -4
- data/lib/capybara/rack_test/node.rb +5 -5
- data/lib/capybara/result.rb +23 -32
- data/lib/capybara/rspec/compound.rb +1 -1
- data/lib/capybara/rspec/matchers.rb +63 -61
- data/lib/capybara/selector.rb +28 -10
- data/lib/capybara/selector/css.rb +17 -17
- data/lib/capybara/selector/filter_set.rb +9 -9
- data/lib/capybara/selector/selector.rb +3 -4
- data/lib/capybara/selenium/driver.rb +73 -95
- data/lib/capybara/selenium/driver_specializations/chrome_driver.rb +4 -4
- data/lib/capybara/selenium/driver_specializations/marionette_driver.rb +9 -0
- data/lib/capybara/selenium/node.rb +127 -67
- data/lib/capybara/selenium/nodes/chrome_node.rb +3 -3
- data/lib/capybara/selenium/nodes/marionette_node.rb +14 -8
- data/lib/capybara/server.rb +2 -2
- data/lib/capybara/server/animation_disabler.rb +17 -3
- data/lib/capybara/server/middleware.rb +8 -4
- data/lib/capybara/session.rb +43 -37
- data/lib/capybara/session/config.rb +8 -6
- data/lib/capybara/spec/session/assert_text_spec.rb +14 -0
- data/lib/capybara/spec/session/attach_file_spec.rb +7 -0
- data/lib/capybara/spec/session/check_spec.rb +21 -0
- data/lib/capybara/spec/session/choose_spec.rb +15 -1
- data/lib/capybara/spec/session/fill_in_spec.rb +7 -0
- data/lib/capybara/spec/session/find_spec.rb +2 -1
- data/lib/capybara/spec/session/has_selector_spec.rb +18 -0
- data/lib/capybara/spec/session/has_text_spec.rb +14 -0
- data/lib/capybara/spec/session/node_spec.rb +2 -1
- data/lib/capybara/spec/session/reset_session_spec.rb +4 -4
- data/lib/capybara/spec/session/text_spec.rb +2 -1
- data/lib/capybara/spec/session/title_spec.rb +2 -1
- data/lib/capybara/spec/session/uncheck_spec.rb +8 -0
- data/lib/capybara/spec/session/within_spec.rb +2 -1
- data/lib/capybara/spec/spec_helper.rb +1 -32
- data/lib/capybara/spec/views/with_js.erb +3 -4
- data/lib/capybara/version.rb +1 -1
- data/spec/minitest_spec.rb +4 -0
- data/spec/minitest_spec_spec.rb +4 -0
- data/spec/rack_test_spec.rb +4 -4
- data/spec/rspec/shared_spec_matchers.rb +4 -2
- data/spec/selector_spec.rb +15 -1
- data/spec/selenium_spec_chrome.rb +1 -6
- data/spec/selenium_spec_chrome_remote.rb +1 -1
- data/spec/selenium_spec_firefox_remote.rb +2 -5
- data/spec/selenium_spec_ie.rb +41 -4
- data/spec/selenium_spec_marionette.rb +1 -25
- data/spec/shared_selenium_session.rb +74 -16
- data/spec/spec_helper.rb +41 -0
- metadata +2 -2
@@ -3,9 +3,8 @@
|
|
3
3
|
module Capybara
|
4
4
|
module Queries
|
5
5
|
class SelectorQuery < Queries::BaseQuery
|
6
|
-
|
7
|
-
|
8
|
-
VALID_KEYS = COUNT_KEYS + %i[text id class visible exact exact_text match wait filter_set]
|
6
|
+
attr_reader :expression, :selector, :locator, :options
|
7
|
+
VALID_KEYS = COUNT_KEYS + %i[text id class visible exact exact_text normalize_ws match wait filter_set]
|
9
8
|
VALID_MATCH = %i[first smart prefer_exact one].freeze
|
10
9
|
|
11
10
|
def initialize(*args,
|
@@ -25,7 +24,7 @@ module Capybara
|
|
25
24
|
|
26
25
|
raise ArgumentError, "Unused parameters passed to #{self.class.name} : #{args}" unless args.empty?
|
27
26
|
|
28
|
-
@expression =
|
27
|
+
@expression = selector.call(@locator, @options.merge(selector_config: { enable_aria_label: enable_aria_label, test_id: test_id }))
|
29
28
|
|
30
29
|
warn_exact_usage
|
31
30
|
|
@@ -36,22 +35,22 @@ module Capybara
|
|
36
35
|
def label; selector.label || selector.name; end
|
37
36
|
|
38
37
|
def description(applied = false)
|
39
|
-
|
40
|
-
if !applied ||
|
41
|
-
|
42
|
-
|
38
|
+
desc = +''
|
39
|
+
if !applied || applied_filters
|
40
|
+
desc << 'visible ' if visible == :visible
|
41
|
+
desc << 'non-visible ' if visible == :hidden
|
43
42
|
end
|
44
|
-
|
45
|
-
if !applied ||
|
46
|
-
|
47
|
-
|
43
|
+
desc << "#{label} #{locator.inspect}"
|
44
|
+
if !applied || applied_filters
|
45
|
+
desc << " with#{' exact' if exact_text == true} text #{options[:text].inspect}" if options[:text]
|
46
|
+
desc << " with exact text #{exact_text}" if exact_text.is_a?(String)
|
48
47
|
end
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
48
|
+
desc << " with id #{options[:id]}" if options[:id]
|
49
|
+
desc << " with classes [#{Array(options[:class]).join(',')}]" if options[:class]
|
50
|
+
desc << selector.description(node_filters: !applied || (applied_filters == :node), **options)
|
51
|
+
desc << ' that also matches the custom filter block' if @filter_block && (!applied || (applied_filters == :node))
|
52
|
+
desc << " within #{@resolved_node.inspect}" if describe_within?
|
53
|
+
desc
|
55
54
|
end
|
56
55
|
|
57
56
|
def applied_description
|
@@ -59,15 +58,9 @@ module Capybara
|
|
59
58
|
end
|
60
59
|
|
61
60
|
def matches_filters?(node)
|
61
|
+
return true if (@resolved_node&.== node) && options[:allow_self]
|
62
62
|
@applied_filters ||= :system
|
63
|
-
return false
|
64
|
-
return false if exact_text.is_a?(String) && !matches_exact_text_filter(node, exact_text)
|
65
|
-
|
66
|
-
case visible
|
67
|
-
when :visible then return false unless node.visible?
|
68
|
-
when :hidden then return false if node.visible?
|
69
|
-
end
|
70
|
-
|
63
|
+
return false unless matches_text_filter?(node) && matches_exact_text_filter?(node) && matches_visible_filter?(node)
|
71
64
|
@applied_filters = :node
|
72
65
|
matches_node_filters?(node) && matches_filter_block?(node)
|
73
66
|
rescue *(node.respond_to?(:session) ? node.session.driver.invalid_element_errors : [])
|
@@ -106,17 +99,7 @@ module Capybara
|
|
106
99
|
@applied_filters = false
|
107
100
|
@resolved_node = node
|
108
101
|
node.synchronize do
|
109
|
-
children =
|
110
|
-
node.find_css(css)
|
111
|
-
else
|
112
|
-
node.find_xpath(xpath(exact))
|
113
|
-
end.map do |child|
|
114
|
-
if node.is_a?(Capybara::Node::Base)
|
115
|
-
Capybara::Node::Element.new(node.session, child, node, self)
|
116
|
-
else
|
117
|
-
Capybara::Node::Simple.new(child)
|
118
|
-
end
|
119
|
-
end
|
102
|
+
children = find_nodes_by_selector_format(node, exact).map(&method(:to_element))
|
120
103
|
Capybara::Result.new(children, self)
|
121
104
|
end
|
122
105
|
end
|
@@ -136,15 +119,35 @@ module Capybara
|
|
136
119
|
|
137
120
|
private
|
138
121
|
|
122
|
+
def applied_filters
|
123
|
+
@applied_filters ||= false
|
124
|
+
end
|
125
|
+
|
139
126
|
def find_selector(locator)
|
140
127
|
selector = if locator.is_a?(Symbol)
|
141
128
|
Selector.all.fetch(locator) { |sel_type| raise ArgumentError, "Unknown selector type (:#{sel_type})" }
|
142
129
|
else
|
143
|
-
Selector.all.values.find { |
|
130
|
+
Selector.all.values.find { |sel| sel.match?(locator) }
|
144
131
|
end
|
145
132
|
selector || Selector.all[session_options.default_selector]
|
146
133
|
end
|
147
134
|
|
135
|
+
def find_nodes_by_selector_format(node, exact)
|
136
|
+
if selector.format == :css
|
137
|
+
node.find_css(css)
|
138
|
+
else
|
139
|
+
node.find_xpath(xpath(exact))
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def to_element(node)
|
144
|
+
if @resolved_node.is_a?(Capybara::Node::Base)
|
145
|
+
Capybara::Node::Element.new(@resolved_node.session, node, @resolved_node, self)
|
146
|
+
else
|
147
|
+
Capybara::Node::Simple.new(node)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
148
151
|
def valid_keys
|
149
152
|
VALID_KEYS + custom_keys
|
150
153
|
end
|
@@ -171,7 +174,6 @@ module Capybara
|
|
171
174
|
|
172
175
|
def matches_filter_block?(node)
|
173
176
|
return true unless @filter_block
|
174
|
-
|
175
177
|
if node.respond_to?(:session)
|
176
178
|
node.session.using_wait_time(0) { @filter_block.call(node) }
|
177
179
|
else
|
@@ -201,87 +203,91 @@ module Capybara
|
|
201
203
|
unless VALID_MATCH.include?(match)
|
202
204
|
raise ArgumentError, "invalid option #{match.inspect} for :match, should be one of #{VALID_MATCH.map(&:inspect).join(', ')}"
|
203
205
|
end
|
204
|
-
unhandled_options = @options.keys
|
205
|
-
|
206
|
-
|
206
|
+
unhandled_options = @options.keys.reject do |option_name|
|
207
|
+
valid_keys.include?(option_name) ||
|
208
|
+
expression_filters.any? { |_name, ef| ef.handles_option? option_name } ||
|
207
209
|
node_filters.any? { |_name, nf| nf.handles_option? option_name }
|
208
210
|
end
|
209
211
|
|
210
212
|
return if unhandled_options.empty?
|
211
213
|
invalid_names = unhandled_options.map(&:inspect).join(', ')
|
212
|
-
valid_names = valid_keys.map(&:inspect).join(', ')
|
214
|
+
valid_names = (valid_keys - [:allow_self]).map(&:inspect).join(', ')
|
213
215
|
raise ArgumentError, "invalid keys #{invalid_names}, should be one of #{valid_names}"
|
214
216
|
end
|
215
217
|
|
216
218
|
def filtered_xpath(expr)
|
217
|
-
if
|
218
|
-
|
219
|
-
|
219
|
+
if use_default_id_filter?
|
220
|
+
id_xpath = if options[:id].is_a? XPath::Expression
|
221
|
+
XPath.attr(:id)[options[:id]]
|
220
222
|
else
|
221
|
-
|
223
|
+
XPath.attr(:id) == options[:id]
|
222
224
|
end
|
225
|
+
expr = "(#{expr})[#{id_xpath}]"
|
223
226
|
end
|
224
|
-
|
225
|
-
class_xpath = if options[:class].is_a?(XPath::Expression)
|
226
|
-
XPath.attr(:class)[options[:class]]
|
227
|
-
else
|
228
|
-
Array(options[:class]).map do |klass|
|
229
|
-
if klass.start_with?('!')
|
230
|
-
!XPath.attr(:class).contains_word(klass.slice(1))
|
231
|
-
else
|
232
|
-
XPath.attr(:class).contains_word(klass)
|
233
|
-
end
|
234
|
-
end.reduce(:&)
|
235
|
-
end
|
236
|
-
expr = "(#{expr})[#{class_xpath}]"
|
237
|
-
end
|
227
|
+
expr = "(#{expr})[#{xpath_from_classes}]" if use_default_class_filter?
|
238
228
|
expr
|
239
229
|
end
|
240
230
|
|
241
231
|
def filtered_css(expr)
|
242
|
-
|
243
|
-
|
232
|
+
::Capybara::Selector::CSS.split(expr).map do |sel|
|
233
|
+
sel += css_from_id if use_default_id_filter?
|
234
|
+
sel += css_from_classes if use_default_class_filter?
|
235
|
+
sel
|
236
|
+
end.join(', ')
|
237
|
+
end
|
244
238
|
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
239
|
+
def use_default_id_filter?
|
240
|
+
options.key?(:id) && !custom_keys.include?(:id)
|
241
|
+
end
|
242
|
+
|
243
|
+
def use_default_class_filter?
|
244
|
+
options.key?(:class) && !custom_keys.include?(:class)
|
245
|
+
end
|
246
|
+
|
247
|
+
def css_from_classes
|
248
|
+
if options[:class].is_a?(XPath::Expression)
|
249
249
|
raise ArgumentError, 'XPath expressions are not supported for the :class filter with CSS based selectors'
|
250
250
|
end
|
251
251
|
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
sel
|
257
|
-
end.join(', ')
|
258
|
-
end
|
252
|
+
classes = Array(options[:class]).group_by { |cl| cl.start_with? '!' }
|
253
|
+
(classes[false].to_a.map { |cl| ".#{Capybara::Selector::CSS.escape(cl)}" } +
|
254
|
+
classes[true].to_a.map { |cl| ":not(.#{Capybara::Selector::CSS.escape(cl.slice(1))})" }).join
|
255
|
+
end
|
259
256
|
|
260
|
-
|
257
|
+
def css_from_id
|
258
|
+
if options[:id].is_a?(XPath::Expression)
|
259
|
+
raise ArgumentError, 'XPath expressions are not supported for the :id filter with CSS based selectors'
|
260
|
+
end
|
261
|
+
"##{::Capybara::Selector::CSS.escape(options[:id])}"
|
261
262
|
end
|
262
263
|
|
263
|
-
def
|
264
|
-
|
265
|
-
|
266
|
-
|
264
|
+
def xpath_from_classes
|
265
|
+
return XPath.attr(:class)[options[:class]] if options[:class].is_a?(XPath::Expression)
|
266
|
+
|
267
|
+
Array(options[:class]).map do |klass|
|
268
|
+
if klass.start_with?('!')
|
269
|
+
!XPath.attr(:class).contains_word(klass.slice(1))
|
270
|
+
else
|
271
|
+
XPath.attr(:class).contains_word(klass)
|
272
|
+
end
|
273
|
+
end.reduce(:&)
|
267
274
|
end
|
268
275
|
|
269
|
-
def apply_expression_filters(
|
276
|
+
def apply_expression_filters(expression)
|
270
277
|
unapplied_options = options.keys - valid_keys
|
271
|
-
expression_filters.inject(
|
278
|
+
expression_filters.inject(expression) do |expr, (name, ef)|
|
272
279
|
if ef.matcher?
|
273
|
-
unapplied_options.select { |option_name| ef.handles_option?(option_name) }.
|
280
|
+
unapplied_options.select { |option_name| ef.handles_option?(option_name) }.inject(expr) do |memo, option_name|
|
274
281
|
unapplied_options.delete(option_name)
|
275
|
-
|
282
|
+
ef.apply_filter(memo, option_name, options[option_name])
|
276
283
|
end
|
277
|
-
memo
|
278
284
|
elsif options.key?(name)
|
279
285
|
unapplied_options.delete(name)
|
280
|
-
ef.apply_filter(
|
286
|
+
ef.apply_filter(expr, name, options[name])
|
281
287
|
elsif ef.default?
|
282
|
-
ef.apply_filter(
|
288
|
+
ef.apply_filter(expr, name, ef.default)
|
283
289
|
else
|
284
|
-
|
290
|
+
expr
|
285
291
|
end
|
286
292
|
end
|
287
293
|
end
|
@@ -307,21 +313,40 @@ module Capybara
|
|
307
313
|
node.is_a?(::Capybara::Node::Simple) && node.path == '/'
|
308
314
|
end
|
309
315
|
|
310
|
-
def matches_text_filter(node
|
311
|
-
|
316
|
+
def matches_text_filter?(node)
|
317
|
+
value = options[:text]
|
318
|
+
return true unless value
|
319
|
+
return matches_text_exactly?(node, value) if exact_text == true
|
312
320
|
regexp = value.is_a?(Regexp) ? value : Regexp.escape(value.to_s)
|
313
|
-
matches_text_regexp(node, regexp)
|
321
|
+
matches_text_regexp?(node, regexp)
|
314
322
|
end
|
315
323
|
|
316
|
-
def matches_exact_text_filter(node
|
324
|
+
def matches_exact_text_filter?(node)
|
325
|
+
return true unless exact_text.is_a?(String)
|
326
|
+
matches_text_exactly?(node, exact_text)
|
327
|
+
end
|
328
|
+
|
329
|
+
def matches_visible_filter?(node)
|
330
|
+
case visible
|
331
|
+
when :visible then node.visible?
|
332
|
+
when :hidden then !node.visible?
|
333
|
+
else true
|
334
|
+
end
|
335
|
+
end
|
336
|
+
|
337
|
+
def matches_text_exactly?(node, value)
|
317
338
|
regexp = value.is_a?(Regexp) ? value : /\A#{Regexp.escape(value.to_s)}\z/
|
318
|
-
matches_text_regexp(node, regexp)
|
339
|
+
matches_text_regexp?(node, regexp)
|
340
|
+
end
|
341
|
+
|
342
|
+
def normalize_ws
|
343
|
+
options.fetch(:normalize_ws, session_options.default_normalize_ws)
|
319
344
|
end
|
320
345
|
|
321
|
-
def matches_text_regexp(node, regexp)
|
346
|
+
def matches_text_regexp?(node, regexp)
|
322
347
|
text_visible = visible
|
323
348
|
text_visible = :all if text_visible == :hidden
|
324
|
-
node.text(text_visible).match(regexp)
|
349
|
+
!!node.text(text_visible, normalize_ws: normalize_ws).match(regexp)
|
325
350
|
end
|
326
351
|
end
|
327
352
|
end
|
@@ -90,7 +90,8 @@ module Capybara
|
|
90
90
|
end
|
91
91
|
|
92
92
|
def text(node: @node, query_type: @type)
|
93
|
-
|
93
|
+
normalize_ws = options.fetch(:normalize_ws, session_options.default_normalize_ws)
|
94
|
+
node.text(query_type, normalize_ws: normalize_ws)
|
94
95
|
end
|
95
96
|
|
96
97
|
def default_type
|
@@ -22,10 +22,10 @@ class Capybara::RackTest::Form < Capybara::RackTest::Node
|
|
22
22
|
params = make_params
|
23
23
|
|
24
24
|
form_element_types = %i[input select textarea]
|
25
|
-
form_elements_xpath = XPath.generate do |
|
26
|
-
xpath =
|
27
|
-
xpath +=
|
28
|
-
xpath.where(!
|
25
|
+
form_elements_xpath = XPath.generate do |xp|
|
26
|
+
xpath = xp.descendant(*form_element_types).where(!xp.attr(:form))
|
27
|
+
xpath += xp.anywhere(*form_element_types).where(xp.attr(:form) == native[:id]) if native[:id]
|
28
|
+
xpath.where(!xp.attr(:disabled))
|
29
29
|
end.to_s
|
30
30
|
|
31
31
|
native.xpath(form_elements_xpath).map do |field|
|
@@ -105,11 +105,11 @@ class Capybara::RackTest::Node < Capybara::Driver::Node
|
|
105
105
|
end
|
106
106
|
|
107
107
|
def find_xpath(locator)
|
108
|
-
native.xpath(locator).map { |
|
108
|
+
native.xpath(locator).map { |el| self.class.new(driver, el) }
|
109
109
|
end
|
110
110
|
|
111
111
|
def find_css(locator)
|
112
|
-
native.css(locator, Capybara::RackTest::CSSHandlers.new).map { |
|
112
|
+
native.css(locator, Capybara::RackTest::CSSHandlers.new).map { |el| self.class.new(driver, el) }
|
113
113
|
end
|
114
114
|
|
115
115
|
def ==(other)
|
@@ -165,7 +165,7 @@ private
|
|
165
165
|
end
|
166
166
|
|
167
167
|
def set_radio(_value) # rubocop:disable Naming/AccessorMethodName
|
168
|
-
other_radios_xpath = XPath.generate { |
|
168
|
+
other_radios_xpath = XPath.generate { |xp| xp.anywhere(:input)[xp.attr(:name) == self[:name]] }.to_s
|
169
169
|
driver.dom.xpath(other_radios_xpath).each { |node| node.remove_attribute('checked') }
|
170
170
|
native['checked'] = 'checked'
|
171
171
|
end
|
@@ -185,11 +185,11 @@ private
|
|
185
185
|
value = value.to_s[0...self[:maxlength].to_i]
|
186
186
|
end
|
187
187
|
if value.is_a?(Array) # Assert multiple attribute is present
|
188
|
-
value.each do |
|
188
|
+
value.each do |val|
|
189
189
|
new_native = native.clone
|
190
190
|
new_native.remove_attribute('value')
|
191
191
|
native.add_next_sibling(new_native)
|
192
|
-
new_native['value'] =
|
192
|
+
new_native['value'] = val.to_s
|
193
193
|
end
|
194
194
|
native.remove
|
195
195
|
else
|
data/lib/capybara/result.rb
CHANGED
@@ -62,10 +62,7 @@ module Capybara
|
|
62
62
|
if max_idx.nil?
|
63
63
|
full_results[*args]
|
64
64
|
else
|
65
|
-
|
66
|
-
break if @result_cache.size > max_idx
|
67
|
-
@result_cache << @results_enum.next
|
68
|
-
end
|
65
|
+
load_up_to(max_idx + 1)
|
69
66
|
@result_cache[*args]
|
70
67
|
end
|
71
68
|
end
|
@@ -77,40 +74,26 @@ module Capybara
|
|
77
74
|
|
78
75
|
def compare_count
|
79
76
|
# Only check filters for as many elements as necessary to determine result
|
80
|
-
if @query.options[:count]
|
81
|
-
|
82
|
-
|
83
|
-
break if @result_cache.size > count_opt
|
84
|
-
@result_cache << @results_enum.next
|
85
|
-
end
|
86
|
-
return @result_cache.size <=> count_opt
|
77
|
+
if (count = @query.options[:count])
|
78
|
+
count = Integer(count)
|
79
|
+
return load_up_to(count + 1) <=> count
|
87
80
|
end
|
88
81
|
|
89
|
-
if @query.options[:minimum]
|
90
|
-
|
91
|
-
|
92
|
-
@result_cache << @results_enum.next while @result_cache.size < min_opt
|
93
|
-
rescue StopIteration
|
94
|
-
return -1
|
95
|
-
end
|
82
|
+
if (min = @query.options[:minimum])
|
83
|
+
min = Integer(min)
|
84
|
+
return -1 if load_up_to(min) < min
|
96
85
|
end
|
97
86
|
|
98
|
-
if @query.options[:maximum]
|
99
|
-
|
100
|
-
|
101
|
-
return 1 if @result_cache.size > max_opt
|
102
|
-
@result_cache << @results_enum.next
|
103
|
-
end
|
87
|
+
if (max = @query.options[:maximum])
|
88
|
+
max = Integer(max)
|
89
|
+
return 1 if load_up_to(max + 1) > max
|
104
90
|
end
|
105
91
|
|
106
|
-
if @query.options[:between]
|
107
|
-
min, max =
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
end
|
112
|
-
return 0 if @query.options[:between].include? @result_cache.size
|
113
|
-
return @result_cache.size <=> min
|
92
|
+
if (between = @query.options[:between])
|
93
|
+
min, max = between.minmax
|
94
|
+
size = load_up_to(max + 1)
|
95
|
+
return 0 if between.include? size
|
96
|
+
return size <=> min
|
114
97
|
end
|
115
98
|
|
116
99
|
0
|
@@ -144,6 +127,14 @@ module Capybara
|
|
144
127
|
|
145
128
|
private
|
146
129
|
|
130
|
+
def load_up_to(num)
|
131
|
+
loop do
|
132
|
+
break if @result_cache.size >= num
|
133
|
+
@result_cache << @results_enum.next
|
134
|
+
end
|
135
|
+
@result_cache.size
|
136
|
+
end
|
137
|
+
|
147
138
|
def full_results
|
148
139
|
loop { @result_cache << @results_enum.next }
|
149
140
|
@result_cache
|