teamcapybara--capybara/lib/capybara/result.rb

163 lines
3.9 KiB
Ruby
Raw Normal View History

2016-03-08 00:52:19 +00:00
# frozen_string_literal: true
2018-01-08 20:23:54 +00:00
require 'forwardable'
module Capybara
##
# A {Capybara::Result} represents a collection of {Capybara::Node::Element} on the page. It is possible to interact with this
# collection similar to an Array because it implements Enumerable and offers the following Array methods through delegation:
#
2018-10-31 16:07:25 +00:00
# * \[\]
# * each()
# * at()
# * size()
# * count()
# * length()
# * first()
# * last()
# * empty?()
2018-11-01 16:08:35 +00:00
# * values_at()
# * sample()
#
# @see Capybara::Node::Element
#
class Result
include Enumerable
extend Forwardable
def initialize(elements, query)
2012-07-09 12:47:11 +00:00
@elements = elements
@result_cache = []
@filter_errors = []
@results_enum = lazy_select_elements { |node| query.matches_filters?(node, @filter_errors) }
@query = query
end
def_delegators :full_results, :size, :length, :last, :values_at, :inspect, :sample
2018-10-12 23:21:53 +00:00
alias index find_index
def each(&block)
return enum_for(:each) unless block_given?
@result_cache.each(&block)
loop do
next_result = @results_enum.next
@result_cache << next_result
2018-01-08 20:23:54 +00:00
yield next_result
end
self
end
def [](*args)
idx, length = args
max_idx = case idx
when Integer
2018-05-10 20:20:23 +00:00
if !idx.negative?
length.nil? ? idx : idx + length - 1
else
nil
end
when Range
idx.end && idx.max # endless range will have end == nil
end
if max_idx.nil?
full_results[*args]
else
2018-08-17 20:57:12 +00:00
load_up_to(max_idx + 1)
@result_cache[*args]
end
end
alias :at :[]
def empty?
!any?
end
2017-11-14 20:14:24 +00:00
def compare_count
2018-10-11 17:59:17 +00:00
count, min, max, between = @query.options.values_at(:count, :minimum, :maximum, :between)
2016-08-31 21:55:03 +00:00
# Only check filters for as many elements as necessary to determine result
2018-10-11 17:59:17 +00:00
if count && (count = Integer(count))
2018-08-17 20:57:12 +00:00
return load_up_to(count + 1) <=> count
2016-08-31 21:55:03 +00:00
end
2018-10-11 17:59:17 +00:00
if min && (min = Integer(min))
2018-08-17 20:57:12 +00:00
return -1 if load_up_to(min) < min
end
2018-10-11 17:59:17 +00:00
if max && (max = Integer(max))
2018-08-17 20:57:12 +00:00
return 1 if load_up_to(max + 1) > max
end
2018-10-11 17:59:17 +00:00
if between
min, max = between.min, (between.end && between.max)
size = load_up_to(max ? max + 1 : min)
2018-11-18 20:52:39 +00:00
return size <=> min unless between.include?(size)
2016-08-31 21:55:03 +00:00
end
2018-05-14 21:30:34 +00:00
0
end
2017-11-14 20:14:24 +00:00
def matches_count?
2018-01-08 20:23:54 +00:00
compare_count.zero?
2017-11-14 20:14:24 +00:00
end
2012-07-09 11:21:44 +00:00
def failure_message
2016-08-30 18:37:54 +00:00
message = @query.failure_message
2018-01-13 21:06:03 +00:00
if count.zero?
2018-07-10 21:18:39 +00:00
message << ' but there were no matches'
2018-01-13 21:06:03 +00:00
else
message << ", found #{count} #{Capybara::Helpers.declension('match', 'matches', count)}: " \
<< full_results.map(&:text).map(&:inspect).join(', ')
2012-07-09 12:47:11 +00:00
end
unless rest.empty?
2018-07-10 21:18:39 +00:00
elements = rest.map { |el| el.text rescue '<<ERROR>>' }.map(&:inspect).join(', ') # rubocop:disable Style/RescueModifier
message << '. Also found ' << elements << ', which matched the selector but not all filters. '
message << @filter_errors.join('. ') if (rest.size == 1) && count.zero?
2012-07-09 12:47:11 +00:00
end
message
end
def negative_failure_message
failure_message.sub(/(to find)/, 'not \1')
end
def unfiltered_size
@elements.length
end
2018-01-09 22:05:50 +00:00
private
2018-08-17 20:57:12 +00:00
def load_up_to(num)
loop do
break if @result_cache.size >= num
2018-09-24 16:43:46 +00:00
2018-08-17 20:57:12 +00:00
@result_cache << @results_enum.next
end
@result_cache.size
end
def full_results
2016-08-31 21:55:03 +00:00
loop { @result_cache << @results_enum.next }
@result_cache
end
def rest
@rest ||= @elements - full_results
end
def lazy_select_elements(&block)
# JRuby has an issue with lazy enumerators which
# causes a concurrency issue with network requests here
# https://github.com/jruby/jruby/issues/4212
if RUBY_PLATFORM == 'java'
2018-01-09 22:05:50 +00:00
@elements.select(&block).to_enum # non-lazy evaluation
else
@elements.lazy.select(&block)
end
end
end
end