more code cleanup
This commit is contained in:
parent
63a12e7ba0
commit
dfaf958ea7
|
@ -34,12 +34,9 @@ module Capybara
|
|||
#
|
||||
# Return the proc that Capybara will call to run the Rack application.
|
||||
# The block returned receives a rack app, port, and host/ip and should run a Rack handler
|
||||
# By default, Capybara will try to run webrick.
|
||||
# By default, Capybara will try to use puma.
|
||||
#
|
||||
def server
|
||||
raise ArgumentError, "Capybara#server no longer accepts a block" if block_given?
|
||||
@server
|
||||
end
|
||||
attr_reader :server
|
||||
|
||||
##
|
||||
#
|
||||
|
|
|
@ -27,13 +27,11 @@ module Capybara
|
|||
# @return [String] Escaped text
|
||||
#
|
||||
def to_regexp(text, regexp_options = nil, exact = false)
|
||||
if text.is_a?(Regexp)
|
||||
text
|
||||
else
|
||||
escaped = Regexp.escape(normalize_whitespace(text))
|
||||
escaped = "\\A#{escaped}\\z" if exact
|
||||
Regexp.new(escaped, regexp_options)
|
||||
end
|
||||
return text if text.is_a?(Regexp)
|
||||
|
||||
escaped = Regexp.escape(normalize_whitespace(text))
|
||||
escaped = "\\A#{escaped}\\z" if exact
|
||||
Regexp.new(escaped, regexp_options)
|
||||
end
|
||||
|
||||
##
|
||||
|
@ -47,11 +45,8 @@ module Capybara
|
|||
def inject_asset_host(html, asset_host = Capybara.asset_host)
|
||||
if asset_host && Nokogiri::HTML(html).css("base").empty?
|
||||
match = html.match(/<head[^<]*?>/)
|
||||
if match
|
||||
return html.clone.insert match.end(0), "<base href='#{asset_host}' />"
|
||||
end
|
||||
return html.clone.insert match.end(0), "<base href='#{asset_host}' />" if match
|
||||
end
|
||||
|
||||
html
|
||||
end
|
||||
|
||||
|
@ -66,11 +61,7 @@ module Capybara
|
|||
# @param [Integer] count The number of items
|
||||
#
|
||||
def declension(singular, plural, count)
|
||||
if count == 1
|
||||
singular
|
||||
else
|
||||
plural
|
||||
end
|
||||
count == 1 ? singular : plural
|
||||
end
|
||||
|
||||
if defined?(Process::CLOCK_MONOTONIC)
|
||||
|
|
|
@ -8,17 +8,14 @@ module Capybara
|
|||
@child_node = node
|
||||
node.synchronize do
|
||||
match_results = super(node.session.current_scope, exact)
|
||||
node.all(:xpath, XPath.ancestor) do |el|
|
||||
match_results.include?(el)
|
||||
end
|
||||
node.all(:xpath, XPath.ancestor) { |el| match_results.include?(el) }
|
||||
end
|
||||
end
|
||||
|
||||
def description
|
||||
child_query = @child_node && @child_node.instance_variable_get(:@query)
|
||||
desc = super
|
||||
if @child_node && (child_query = @child_node.instance_variable_get(:@query))
|
||||
desc += " that is an ancestor of #{child_query.description}"
|
||||
end
|
||||
desc += " that is an ancestor of #{child_query.description}" if child_query
|
||||
desc
|
||||
end
|
||||
end
|
||||
|
|
|
@ -31,11 +31,7 @@ module Capybara
|
|||
# Returns false if query does not have any count options specified.
|
||||
#
|
||||
def expects_none?
|
||||
if COUNT_KEYS.any? { |k| options.key? k }
|
||||
matches_count?(0)
|
||||
else
|
||||
false
|
||||
end
|
||||
count_specified? ? matches_count?(0) : false
|
||||
end
|
||||
|
||||
##
|
||||
|
@ -52,7 +48,7 @@ module Capybara
|
|||
return false if options[:maximum] && (Integer(options[:maximum]) < count)
|
||||
return false if options[:minimum] && (Integer(options[:minimum]) > count)
|
||||
return false if options[:between] && !options[:between].include?(count)
|
||||
return true
|
||||
true
|
||||
end
|
||||
|
||||
##
|
||||
|
@ -69,6 +65,10 @@ module Capybara
|
|||
|
||||
private
|
||||
|
||||
def count_specified?
|
||||
COUNT_KEYS.any? { |k| options.key? k }
|
||||
end
|
||||
|
||||
def count_message
|
||||
message = "".dup
|
||||
if options[:count]
|
||||
|
|
|
@ -19,11 +19,7 @@ module Capybara
|
|||
def resolves_for?(session)
|
||||
uri = ::Addressable::URI.parse(session.current_url)
|
||||
uri.query = nil if uri && options[:ignore_query]
|
||||
@actual_path = if options[:url]
|
||||
uri.to_s
|
||||
else
|
||||
uri && uri.request_uri
|
||||
end
|
||||
@actual_path = options[:url] ? uri.to_s : uri && uri.request_uri
|
||||
|
||||
if @expected_path.is_a? Regexp
|
||||
@actual_path.to_s.match(@expected_path)
|
||||
|
|
|
@ -2,11 +2,7 @@ module Capybara
|
|||
module Queries
|
||||
class MatchQuery < Capybara::Queries::SelectorQuery
|
||||
def visible
|
||||
if options.key?(:visible)
|
||||
super
|
||||
else
|
||||
:all
|
||||
end
|
||||
options.key?(:visible) ? super : :all
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
@ -13,17 +13,10 @@ module Capybara
|
|||
@options = options.dup
|
||||
super(@options)
|
||||
self.session_options = session_options
|
||||
@filter_block = filter_block
|
||||
|
||||
@selector = if args[0].is_a?(Symbol)
|
||||
Selector.all.fetch(args.shift) do |selector_type|
|
||||
raise ArgumentError, "Unknown selector type (:#{selector_type})"
|
||||
end
|
||||
else
|
||||
Selector.all.values.find { |s| s.match?(args[0]) }
|
||||
end
|
||||
@selector = find_selector(args[0].is_a?(Symbol) ? args.shift : args[0])
|
||||
@locator = args.shift
|
||||
@selector ||= Selector.all[session_options.default_selector]
|
||||
@filter_block = filter_block
|
||||
|
||||
warn "Unused parameters passed to #{self.class.name} : #{args}" unless args.empty?
|
||||
|
||||
|
@ -35,7 +28,7 @@ module Capybara
|
|||
end
|
||||
|
||||
def name; selector.name; end
|
||||
def label; selector.label or selector.name; end
|
||||
def label; selector.label || selector.name; end
|
||||
|
||||
def description
|
||||
@description = "".dup
|
||||
|
@ -75,8 +68,7 @@ module Capybara
|
|||
end
|
||||
|
||||
def exact?
|
||||
return false unless supports_exact?
|
||||
options.fetch(:exact, session_options.exact)
|
||||
supports_exact? ? options.fetch(:exact, session_options.exact) : false
|
||||
end
|
||||
|
||||
def match
|
||||
|
@ -120,6 +112,15 @@ module Capybara
|
|||
|
||||
private
|
||||
|
||||
def find_selector(locator)
|
||||
selector = if locator.is_a?(Symbol)
|
||||
Selector.all.fetch(locator) { |sel_type| raise ArgumentError, "Unknown selector type (:#{sel_type})" }
|
||||
else
|
||||
Selector.all.values.find { |s| s.match?(locator) }
|
||||
end
|
||||
selector || Selector.all[session_options.default_selector]
|
||||
end
|
||||
|
||||
def valid_keys
|
||||
VALID_KEYS + custom_keys
|
||||
end
|
||||
|
|
|
@ -8,7 +8,7 @@ module Capybara
|
|||
@sibling_node = node
|
||||
node.synchronize do
|
||||
match_results = super(node.session.current_scope, exact)
|
||||
node.all(:xpath, XPath.preceding_sibling.union(XPath.following_sibling)) do |el|
|
||||
node.all(:xpath, XPath.preceding_sibling + XPath.following_sibling) do |el|
|
||||
match_results.include?(el)
|
||||
end
|
||||
end
|
||||
|
@ -16,9 +16,8 @@ module Capybara
|
|||
|
||||
def description
|
||||
desc = super
|
||||
if @sibling_node && (sibling_query = @sibling_node.instance_variable_get(:@query))
|
||||
desc += " that is a sibling of #{sibling_query.description}"
|
||||
end
|
||||
sibling_query = @sibling_node && @sibling_node.instance_variable_get(:@query)
|
||||
desc += " that is a sibling of #{sibling_query.description}" if sibling_query
|
||||
desc
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,16 +5,22 @@ module Capybara
|
|||
module Queries
|
||||
class TextQuery < BaseQuery
|
||||
def initialize(type = nil, expected_text, session_options:, **options) # rubocop:disable Style/OptionalArguments
|
||||
@type = type
|
||||
@type = Capybara.ignore_hidden_elements || Capybara.visible_text_only ? :visible : :all if @type.nil?
|
||||
@expected_text = expected_text
|
||||
@type = if type.nil?
|
||||
Capybara.ignore_hidden_elements || Capybara.visible_text_only ? :visible : :all
|
||||
else
|
||||
type
|
||||
end
|
||||
@expected_text = if expected_text.is_a?(Regexp)
|
||||
expected_text
|
||||
else
|
||||
Capybara::Helpers.normalize_whitespace(expected_text)
|
||||
end
|
||||
@options = options
|
||||
super(@options)
|
||||
self.session_options = session_options
|
||||
unless @expected_text.is_a?(Regexp)
|
||||
@expected_text = Capybara::Helpers.normalize_whitespace(@expected_text)
|
||||
end
|
||||
|
||||
@search_regexp = Capybara::Helpers.to_regexp(@expected_text, nil, exact?)
|
||||
|
||||
assert_valid_keys
|
||||
end
|
||||
|
||||
|
|
|
@ -5,19 +5,15 @@ module Capybara
|
|||
module Queries
|
||||
class TitleQuery < BaseQuery
|
||||
def initialize(expected_title, **options)
|
||||
@expected_title = expected_title
|
||||
@expected_title = expected_title.is_a?(Regexp) ? expected_title : Capybara::Helpers.normalize_whitespace(expected_title)
|
||||
@options = options
|
||||
super(@options)
|
||||
unless @expected_title.is_a?(Regexp)
|
||||
@expected_title = Capybara::Helpers.normalize_whitespace(@expected_title)
|
||||
end
|
||||
@search_regexp = Capybara::Helpers.to_regexp(@expected_title, nil, options.fetch(:exact, false))
|
||||
assert_valid_keys
|
||||
end
|
||||
|
||||
def resolves_for?(node)
|
||||
@actual_title = node.title
|
||||
@actual_title.match(@search_regexp)
|
||||
(@actual_title = node.title).match(@search_regexp)
|
||||
end
|
||||
|
||||
def failure_message
|
||||
|
|
|
@ -23,9 +23,9 @@ class Capybara::RackTest::Form < Capybara::RackTest::Node
|
|||
|
||||
form_element_types = %i[input select textarea]
|
||||
form_elements_xpath = XPath.generate do |x|
|
||||
xpath = x.descendant(*form_element_types).where(~x.attr(:form))
|
||||
xpath = xpath.union(x.anywhere(*form_element_types).where(x.attr(:form) == native[:id])) if native[:id]
|
||||
xpath.where(~x.attr(:disabled))
|
||||
xpath = x.descendant(*form_element_types).where(!x.attr(:form))
|
||||
xpath += x.anywhere(*form_element_types).where(x.attr(:form) == native[:id]) if native[:id]
|
||||
xpath.where(!x.attr(:disabled))
|
||||
end.to_s
|
||||
|
||||
native.xpath(form_elements_xpath).map do |field|
|
||||
|
@ -88,17 +88,16 @@ private
|
|||
merge_param!(params, field['name'].to_s, node.value.to_s)
|
||||
end
|
||||
elsif %w[submit image].include? field['type']
|
||||
# TO DO identify the click button here (in document order, rather
|
||||
# TODO: identify the click button here (in document order, rather
|
||||
# than leaving until the end of the params)
|
||||
elsif field['type'] == 'file'
|
||||
if multipart?
|
||||
file = \
|
||||
if (value = field['value']).to_s.empty?
|
||||
NilUploadedFile.new
|
||||
else
|
||||
mime_info = MiniMime.lookup_by_filename(value)
|
||||
Rack::Test::UploadedFile.new(value, (mime_info && mime_info.content_type).to_s)
|
||||
end
|
||||
file = if (value = field['value']).to_s.empty?
|
||||
NilUploadedFile.new
|
||||
else
|
||||
mime_info = MiniMime.lookup_by_filename(value)
|
||||
Rack::Test::UploadedFile.new(value, (mime_info && mime_info.content_type).to_s)
|
||||
end
|
||||
merge_param!(params, field['name'].to_s, file)
|
||||
else
|
||||
merge_param!(params, field['name'].to_s, File.basename(field['value'].to_s))
|
||||
|
@ -110,13 +109,11 @@ private
|
|||
|
||||
def add_select_param(field, params)
|
||||
if field['multiple'] == 'multiple'
|
||||
options = field.xpath(".//option[@selected]")
|
||||
options.each do |option|
|
||||
field.xpath(".//option[@selected]").each do |option|
|
||||
merge_param!(params, field['name'].to_s, (option['value'] || option.text).to_s)
|
||||
end
|
||||
else
|
||||
option = field.xpath(".//option[@selected]").first
|
||||
option ||= field.xpath('.//option').first
|
||||
option = field.xpath('.//option[@selected]').first || field.xpath('.//option').first
|
||||
merge_param!(params, field['name'].to_s, (option['value'] || option.text).to_s) if option
|
||||
end
|
||||
end
|
||||
|
|
|
@ -26,42 +26,33 @@ class Capybara::RackTest::Node < Capybara::Driver::Node
|
|||
raise TypeError, "Value cannot be an Array when 'multiple' attribute is not present. Not a #{value.class}"
|
||||
end
|
||||
|
||||
if radio?
|
||||
set_radio(value)
|
||||
elsif checkbox?
|
||||
set_checkbox(value)
|
||||
elsif input_field?
|
||||
set_input(value)
|
||||
elsif textarea?
|
||||
native['_capybara_raw_value'] = value.to_s
|
||||
if radio? then set_radio(value)
|
||||
elsif checkbox? then set_checkbox(value)
|
||||
elsif input_field? then set_input(value)
|
||||
elsif textarea? then native['_capybara_raw_value'] = value.to_s
|
||||
end
|
||||
end
|
||||
|
||||
def select_option
|
||||
return if disabled?
|
||||
if select_node['multiple'] != 'multiple'
|
||||
select_node.find_xpath(".//option[@selected]").each { |node| node.native.remove_attribute("selected") }
|
||||
end
|
||||
deselect_options unless select_node.multiple?
|
||||
native["selected"] = 'selected'
|
||||
end
|
||||
|
||||
def unselect_option
|
||||
if select_node['multiple'] != 'multiple'
|
||||
raise Capybara::UnselectNotAllowed, "Cannot unselect option from single select box."
|
||||
end
|
||||
raise Capybara::UnselectNotAllowed, "Cannot unselect option from single select box." unless select_node.multiple?
|
||||
native.remove_attribute('selected')
|
||||
end
|
||||
|
||||
def click(keys = [], offset = {})
|
||||
raise ArgumentError, "The RackTest driver does not support click options" unless keys.empty? && offset.empty?
|
||||
|
||||
if tag_name == 'a' && !self[:href].nil?
|
||||
if link?
|
||||
follow_link
|
||||
elsif (tag_name == 'input' and %w[submit image].include?(type)) or
|
||||
(tag_name == 'button' and [nil, "submit"].include?(type))
|
||||
elsif submits?
|
||||
associated_form = form
|
||||
Capybara::RackTest::Form.new(driver, associated_form).submit(self) if associated_form
|
||||
elsif tag_name == 'input' and %w[checkbox radio].include?(type)
|
||||
elsif checkable?
|
||||
set(!checked?)
|
||||
elsif tag_name == 'label'
|
||||
click_label
|
||||
|
@ -85,10 +76,12 @@ class Capybara::RackTest::Node < Capybara::Driver::Node
|
|||
end
|
||||
|
||||
def disabled?
|
||||
return true if string_node.disabled?
|
||||
|
||||
if %w[option optgroup].include? tag_name
|
||||
string_node.disabled? || find_xpath("parent::*[self::optgroup or self::select]")[0].disabled?
|
||||
find_xpath("parent::*[self::optgroup or self::select]")[0].disabled?
|
||||
else
|
||||
string_node.disabled? || !find_xpath("parent::fieldset[@disabled] | ancestor::*[not(self::legend) or preceding-sibling::legend][parent::fieldset[@disabled]]").empty?
|
||||
!find_xpath("parent::fieldset[@disabled] | ancestor::*[not(self::legend) or preceding-sibling::legend][parent::fieldset[@disabled]]").empty?
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -126,6 +119,10 @@ protected
|
|||
|
||||
private
|
||||
|
||||
def deselect_options
|
||||
select_node.find_xpath(".//option[@selected]").each { |node| node.native.remove_attribute("selected") }
|
||||
end
|
||||
|
||||
def string_node
|
||||
@string_node ||= Capybara::Node::Simple.new(native)
|
||||
end
|
||||
|
@ -141,10 +138,10 @@ private
|
|||
|
||||
def form
|
||||
if native[:form]
|
||||
native.xpath("//form[@id='#{native[:form]}']").first
|
||||
native.xpath("//form[@id='#{native[:form]}']")
|
||||
else
|
||||
native.ancestors('form').first
|
||||
end
|
||||
native.ancestors('form')
|
||||
end.first
|
||||
end
|
||||
|
||||
def set_radio(_value) # rubocop:disable Naming/AccessorMethodName
|
||||
|
@ -192,16 +189,28 @@ private
|
|||
|
||||
def click_label
|
||||
labelled_control = if native[:for]
|
||||
find_xpath("//input[@id='#{native[:for]}']").first
|
||||
find_xpath("//input[@id='#{native[:for]}']")
|
||||
else
|
||||
find_xpath(".//input").first
|
||||
end
|
||||
find_xpath(".//input")
|
||||
end.first
|
||||
|
||||
if labelled_control && (labelled_control.checkbox? || labelled_control.radio?)
|
||||
labelled_control.set(!labelled_control.checked?)
|
||||
end
|
||||
end
|
||||
|
||||
def link?
|
||||
tag_name == 'a' && !self[:href].nil?
|
||||
end
|
||||
|
||||
def submits?
|
||||
(tag_name == 'input' and %w[submit image].include?(type)) || (tag_name == 'button' and [nil, "submit"].include?(type))
|
||||
end
|
||||
|
||||
def checkable?
|
||||
tag_name == 'input' and %w[checkbox radio].include?(type)
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def checkbox?
|
||||
|
|
|
@ -92,8 +92,7 @@ module Capybara
|
|||
end
|
||||
|
||||
if @query.options[:between]
|
||||
max = Integer(@query.options[:between].max)
|
||||
min = Integer(@query.options[:between].min)
|
||||
min, max = @query.options[:between].minmax
|
||||
loop do
|
||||
break if @result_cache.size > max
|
||||
@result_cache << @results_enum.next
|
||||
|
@ -112,10 +111,10 @@ module Capybara
|
|||
|
||||
def failure_message
|
||||
message = @query.failure_message
|
||||
if count > 0
|
||||
message << ", found #{count} #{Capybara::Helpers.declension("match", "matches", count)}: " << full_results.map(&:text).map(&:inspect).join(", ")
|
||||
else
|
||||
if count.zero?
|
||||
message << " but there were no matches"
|
||||
else
|
||||
message << ", found #{count} #{Capybara::Helpers.declension("match", "matches", count)}: " << full_results.map(&:text).map(&:inspect).join(", ")
|
||||
end
|
||||
unless rest.empty?
|
||||
elements = rest.map { |el| el.text rescue "<<ERROR>>" }.map(&:inspect).join(", ")
|
||||
|
|
|
@ -16,10 +16,8 @@ module Capybara
|
|||
end
|
||||
|
||||
class CapybaraEvaluator
|
||||
def initialize(actual, matcher1, matcher2)
|
||||
def initialize(actual)
|
||||
@actual = actual
|
||||
@matcher1 = matcher1
|
||||
@matcher2 = matcher2
|
||||
@match_results = Hash.new { |h, matcher| h[matcher] = matcher.matches?(@actual) }
|
||||
end
|
||||
|
||||
|
@ -36,7 +34,7 @@ module Capybara
|
|||
private
|
||||
|
||||
def match(_expected, actual)
|
||||
@evaluator = CapybaraEvaluator.new(actual, matcher_1, matcher_2)
|
||||
@evaluator = CapybaraEvaluator.new(actual)
|
||||
syncer = sync_element(actual)
|
||||
begin
|
||||
syncer.synchronize do
|
||||
|
@ -64,7 +62,7 @@ module Capybara
|
|||
private
|
||||
|
||||
def match(_expected, actual)
|
||||
@evaluator = CapybaraEvaluator.new(actual, matcher_1, matcher_2)
|
||||
@evaluator = CapybaraEvaluator.new(actual)
|
||||
syncer = sync_element(actual)
|
||||
begin
|
||||
syncer.synchronize do
|
||||
|
|
|
@ -135,15 +135,9 @@ module Capybara
|
|||
end
|
||||
|
||||
class HaveText < Matcher
|
||||
attr_reader :type, :content, :options
|
||||
|
||||
def initialize(*args)
|
||||
@args = args.dup
|
||||
|
||||
# are set just for backwards compatability
|
||||
@type = args.shift if args.first.is_a?(Symbol)
|
||||
@content = args.shift
|
||||
@options = args.first.is_a?(Hash) ? args.first : {}
|
||||
@content = args[0].is_a?(Symbol) ? args[1] : args[0]
|
||||
end
|
||||
|
||||
def matches?(actual)
|
||||
|
@ -155,7 +149,7 @@ module Capybara
|
|||
end
|
||||
|
||||
def description
|
||||
"text #{format(content)}"
|
||||
"text #{format(@content)}"
|
||||
end
|
||||
|
||||
def format(content)
|
||||
|
@ -165,8 +159,6 @@ module Capybara
|
|||
end
|
||||
|
||||
class HaveTitle < Matcher
|
||||
attr_reader :title
|
||||
|
||||
def initialize(*args)
|
||||
@args = args
|
||||
|
||||
|
@ -183,17 +175,13 @@ module Capybara
|
|||
end
|
||||
|
||||
def description
|
||||
"have title #{title.inspect}"
|
||||
"have title #{@title.inspect}"
|
||||
end
|
||||
end
|
||||
|
||||
class HaveCurrentPath < Matcher
|
||||
attr_reader :current_path
|
||||
|
||||
def initialize(*args)
|
||||
@args = args
|
||||
|
||||
# are set just for backwards compatability
|
||||
@current_path = args.first
|
||||
end
|
||||
|
||||
|
@ -206,7 +194,7 @@ module Capybara
|
|||
end
|
||||
|
||||
def description
|
||||
"have current path #{current_path.inspect}"
|
||||
"have current path #{@current_path.inspect}"
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -2,8 +2,8 @@ module Capybara
|
|||
class Selector
|
||||
class CSS
|
||||
def self.escape(str)
|
||||
out = "".dup
|
||||
value = str.dup
|
||||
out = "".dup
|
||||
out << value.slice!(0...1) if value =~ /^[-_]/
|
||||
out << (value[0] =~ NMSTART ? value.slice!(0...1) : escape_char(value.slice!(0...1)))
|
||||
out << value.gsub(/[^a-zA-Z0-9_-]/) { |c| escape_char c }
|
||||
|
|
|
@ -26,13 +26,9 @@ module Capybara
|
|||
end
|
||||
|
||||
def description(**options)
|
||||
options_with_defaults = options.dup
|
||||
filters.each do |name, filter|
|
||||
options_with_defaults[name] = filter.default if filter.default? && !options_with_defaults.key?(name)
|
||||
end
|
||||
|
||||
opts = options_with_defaults(options)
|
||||
@descriptions.map do |desc|
|
||||
desc.call(options_with_defaults).to_s
|
||||
desc.call(opts).to_s
|
||||
end.join
|
||||
end
|
||||
|
||||
|
@ -64,6 +60,14 @@ module Capybara
|
|||
|
||||
private
|
||||
|
||||
def options_with_defaults(options)
|
||||
options = options.dup
|
||||
filters.each do |name, filter|
|
||||
options[name] = filter.default if filter.default? && !options.key?(name)
|
||||
end
|
||||
options
|
||||
end
|
||||
|
||||
def add_filter(name, filter_class, *types, **options, &block)
|
||||
types.each { |k| options[k] = true }
|
||||
filters[name] = filter_class.new(name, block, options)
|
||||
|
|
|
@ -8,32 +8,15 @@ module Capybara
|
|||
class ExpressionFilter < Base
|
||||
def apply_filter(expr, value)
|
||||
return expr if skip?(value)
|
||||
|
||||
unless valid_value?(value)
|
||||
msg = "Invalid value #{value.inspect} passed to expression filter #{@name} - "
|
||||
if default?
|
||||
warn msg + "defaulting to #{default}"
|
||||
value = default
|
||||
else
|
||||
warn msg + "skipping"
|
||||
return expr
|
||||
end
|
||||
end
|
||||
|
||||
raise "ArgumentError", "Invalid value #{value.inspect} passed to expression filter #{@name}" unless valid_value?(value)
|
||||
@block.call(expr, value)
|
||||
end
|
||||
end
|
||||
|
||||
class IdentityExpressionFilter < ExpressionFilter
|
||||
def initialize; end
|
||||
|
||||
def default?
|
||||
false
|
||||
end
|
||||
|
||||
def apply_filter(expr, _value)
|
||||
return expr
|
||||
end
|
||||
def default?; false; end
|
||||
def apply_filter(expr, _value); expr; end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -8,18 +8,7 @@ module Capybara
|
|||
class NodeFilter < Base
|
||||
def matches?(node, value)
|
||||
return true if skip?(value)
|
||||
|
||||
unless valid_value?(value)
|
||||
msg = "Invalid value #{value.inspect} passed to filter #{@name} - "
|
||||
if default?
|
||||
warn msg + "defaulting to #{default}"
|
||||
value = default
|
||||
else
|
||||
warn msg + "skipping"
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
raise ArgumentError, "Invalid value #{value.inspect} passed to filter #{@name}" unless valid_value?(value)
|
||||
@block.call(node, value)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -152,7 +152,6 @@ module Capybara
|
|||
|
||||
def call(locator, **options)
|
||||
if format
|
||||
# @expression.call(locator, options.select {|k,v| @expression_filters.include?(k)})
|
||||
@expression.call(locator, options)
|
||||
else
|
||||
warn "Selector has no format"
|
||||
|
@ -265,13 +264,7 @@ module Capybara
|
|||
end
|
||||
|
||||
def find_by_class_attr(classes)
|
||||
if classes
|
||||
Array(classes).map do |klass|
|
||||
XPath.attr(:class).contains_word(klass)
|
||||
end.reduce(:&)
|
||||
else
|
||||
nil
|
||||
end
|
||||
Array(classes).map { |klass| XPath.attr(:class).contains_word(klass) }.reduce(:&)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -38,12 +38,10 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
|
|||
# Array => an array of keys to send before the value being set, e.g. [[:command, 'a'], :backspace]
|
||||
def set(value, **options)
|
||||
raise ArgumentError, "Value cannot be an Array when 'multiple' attribute is not present. Not a #{value.class}" if value.is_a?(Array) && !multiple?
|
||||
tag_name = self.tag_name
|
||||
type = self[:type]
|
||||
|
||||
case tag_name
|
||||
when 'input'
|
||||
case type
|
||||
case self[:type]
|
||||
when 'radio'
|
||||
click
|
||||
when 'checkbox'
|
||||
|
@ -123,55 +121,38 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
|
|||
end
|
||||
|
||||
def hover
|
||||
scroll_if_needed do
|
||||
driver.browser.action.move_to(native).perform
|
||||
end
|
||||
scroll_if_needed { driver.browser.action.move_to(native).perform }
|
||||
end
|
||||
|
||||
def drag_to(element)
|
||||
scroll_if_needed do
|
||||
driver.browser.action.drag_and_drop(native, element.native).perform
|
||||
end
|
||||
scroll_if_needed { driver.browser.action.drag_and_drop(native, element.native).perform }
|
||||
end
|
||||
|
||||
def tag_name
|
||||
native.tag_name.downcase
|
||||
end
|
||||
|
||||
def visible?
|
||||
displayed = native.displayed?
|
||||
displayed and displayed != "false"
|
||||
end
|
||||
|
||||
def selected?
|
||||
selected = native.selected?
|
||||
selected and selected != "false"
|
||||
end
|
||||
def visible?; boolean_attr(native.displayed?); end
|
||||
def readonly?; boolean_attr(self[:readonly]); end
|
||||
def multiple?; boolean_attr(self[:multiple]); end
|
||||
def selected?; boolean_attr(native.selected?); end
|
||||
alias :checked? :selected?
|
||||
|
||||
def disabled?
|
||||
return true unless native.enabled?
|
||||
|
||||
# workaround for selenium-webdriver/geckodriver reporting elements as enabled when they are nested in disabling elements
|
||||
if driver.marionette?
|
||||
if %w[option optgroup].include? tag_name
|
||||
!native.enabled? || find_xpath("parent::*[self::optgroup or self::select]")[0].disabled?
|
||||
find_xpath("parent::*[self::optgroup or self::select]")[0].disabled?
|
||||
else
|
||||
!native.enabled? || !find_xpath("parent::fieldset[@disabled] | ancestor::*[not(self::legend) or preceding-sibling::legend][parent::fieldset[@disabled]]").empty?
|
||||
!find_xpath("parent::fieldset[@disabled] | ancestor::*[not(self::legend) or preceding-sibling::legend][parent::fieldset[@disabled]]").empty?
|
||||
end
|
||||
else
|
||||
!native.enabled?
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def readonly?
|
||||
readonly = self[:readonly]
|
||||
readonly and readonly != "false"
|
||||
end
|
||||
|
||||
def multiple?
|
||||
multiple = self[:multiple]
|
||||
multiple and multiple != "false"
|
||||
end
|
||||
|
||||
def content_editable?
|
||||
native.attribute('isContentEditable')
|
||||
end
|
||||
|
@ -189,34 +170,31 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
|
|||
end
|
||||
|
||||
def path
|
||||
path = find_xpath('ancestor::*').reverse
|
||||
path.unshift self
|
||||
path = find_xpath(XPath.ancestor_or_self).reverse
|
||||
|
||||
result = []
|
||||
while (node = path.shift)
|
||||
parent = path.first
|
||||
|
||||
selector = node.tag_name
|
||||
if parent
|
||||
siblings = parent.find_xpath(node.tag_name)
|
||||
if siblings.size == 1
|
||||
result.unshift node.tag_name
|
||||
else
|
||||
index = siblings.index(node)
|
||||
result.unshift "#{node.tag_name}[#{index + 1}]"
|
||||
end
|
||||
else
|
||||
result.unshift node.tag_name
|
||||
selector += "[#{siblings.index(node) + 1}]" unless siblings.size == 1
|
||||
end
|
||||
result.push selector
|
||||
end
|
||||
|
||||
'/' + result.join('/')
|
||||
'/' + result.reverse.join('/')
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def boolean_attr(val)
|
||||
val and val != "false"
|
||||
end
|
||||
|
||||
# a reference to the select node if this is an option node
|
||||
def select_node
|
||||
find_xpath('./ancestor::select[1]').first
|
||||
find_xpath(XPath.ancestor(:select)[1]).first
|
||||
end
|
||||
|
||||
def set_text(value, clear: nil, **)
|
||||
|
@ -242,7 +220,7 @@ private
|
|||
def scroll_if_needed
|
||||
yield
|
||||
rescue ::Selenium::WebDriver::Error::MoveTargetOutOfBoundsError
|
||||
script = <<-JS
|
||||
script = <<-'JS'
|
||||
try {
|
||||
arguments[0].scrollIntoView({behavior: 'instant', block: 'center', inline: 'center'});
|
||||
} catch(e) {
|
||||
|
|
|
@ -81,7 +81,7 @@ module Capybara
|
|||
@app = app
|
||||
if block_given?
|
||||
raise "A configuration block is only accepted when Capybara.threadsafe == true" unless Capybara.threadsafe
|
||||
yield config if block_given?
|
||||
yield config
|
||||
end
|
||||
@server = if config.run_server and @app and driver.needs_server?
|
||||
Capybara::Server.new(@app, config.server_port, config.server_host, config.server_errors).boot
|
||||
|
@ -195,6 +195,7 @@ module Capybara
|
|||
|
||||
# Addressable doesn't support opaque URIs - we want nil here
|
||||
return nil if uri.scheme == "about"
|
||||
|
||||
path = uri.path
|
||||
path if path && !path.empty?
|
||||
end
|
||||
|
@ -351,9 +352,7 @@ module Capybara
|
|||
# @param [String] locator Id or legend of the fieldset
|
||||
#
|
||||
def within_fieldset(locator)
|
||||
within :fieldset, locator do
|
||||
yield
|
||||
end
|
||||
within(:fieldset, locator) { yield }
|
||||
end
|
||||
|
||||
##
|
||||
|
@ -363,9 +362,7 @@ module Capybara
|
|||
# @param [String] locator Id or caption of the table
|
||||
#
|
||||
def within_table(locator)
|
||||
within :table, locator do
|
||||
yield
|
||||
end
|
||||
within(:table, locator) { yield }
|
||||
end
|
||||
|
||||
##
|
||||
|
@ -682,9 +679,9 @@ module Capybara
|
|||
# @return [String] the path to which the file was saved
|
||||
#
|
||||
def save_page(path = nil)
|
||||
path = prepare_path(path, 'html')
|
||||
File.write(path, Capybara::Helpers.inject_asset_host(body, config.asset_host), mode: 'wb')
|
||||
path
|
||||
prepare_path(path, 'html').tap do |p|
|
||||
File.write(p, Capybara::Helpers.inject_asset_host(body, config.asset_host), mode: 'wb')
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
|
@ -698,8 +695,7 @@ module Capybara
|
|||
# @param [String] path the path to where it should be saved
|
||||
#
|
||||
def save_and_open_page(path = nil)
|
||||
path = save_page(path)
|
||||
open_file(path)
|
||||
save_page(path).tap { |p| open_file(p) }
|
||||
end
|
||||
|
||||
##
|
||||
|
@ -714,9 +710,7 @@ module Capybara
|
|||
# @param [Hash] options a customizable set of options
|
||||
# @return [String] the path to which the file was saved
|
||||
def save_screenshot(path = nil, **options)
|
||||
path = prepare_path(path, 'png')
|
||||
driver.save_screenshot(path, options)
|
||||
path
|
||||
prepare_path(path, 'png').tap { |p| driver.save_screenshot(p, options) }
|
||||
end
|
||||
|
||||
##
|
||||
|
@ -732,9 +726,8 @@ module Capybara
|
|||
#
|
||||
def save_and_open_screenshot(path = nil, **options)
|
||||
# rubocop:disable Lint/Debugger
|
||||
path = save_screenshot(path, options)
|
||||
save_screenshot(path, options).tap { |p| open_file(p) }
|
||||
# rubocop:enable Lint/Debugger
|
||||
open_file(path)
|
||||
end
|
||||
|
||||
def document
|
||||
|
@ -760,8 +753,7 @@ module Capybara
|
|||
|
||||
def current_scope
|
||||
scope = scopes.last
|
||||
scope = document if [nil, :frame].include? scope
|
||||
scope
|
||||
[nil, :frame].include?(scope) ? document : scope
|
||||
end
|
||||
|
||||
##
|
||||
|
@ -826,14 +818,11 @@ module Capybara
|
|||
require "launchy"
|
||||
Launchy.open(path)
|
||||
rescue LoadError
|
||||
warn "File saved to #{path}."
|
||||
warn "Please install the launchy gem to open the file automatically."
|
||||
warn "File saved to #{path}.\nPlease install the launchy gem to open the file automatically."
|
||||
end
|
||||
|
||||
def prepare_path(path, extension)
|
||||
path = File.expand_path(path || default_fn(extension), config.save_path)
|
||||
FileUtils.mkdir_p(File.dirname(path))
|
||||
path
|
||||
File.expand_path(path || default_fn(extension), config.save_path).tap { |p| FileUtils.mkdir_p(File.dirname(p)) }
|
||||
end
|
||||
|
||||
def default_fn(extension)
|
||||
|
|
|
@ -35,10 +35,10 @@ Capybara::SpecHelper.spec '#find_field' do
|
|||
end.to raise_error(Capybara::ElementNotFound)
|
||||
end
|
||||
|
||||
it "should warn if filter option is invalid" do
|
||||
expect_any_instance_of(Kernel).to receive(:warn).
|
||||
with('Invalid value nil passed to filter disabled - defaulting to false')
|
||||
@session.find_field('Dog', disabled: nil)
|
||||
it "should raise error if filter option is invalid" do
|
||||
expect do
|
||||
@session.find_field('Dog', disabled: nil)
|
||||
end.to raise_error ArgumentError, "Invalid value nil passed to filter disabled"
|
||||
end
|
||||
|
||||
context "with :exact option" do
|
||||
|
|
|
@ -34,14 +34,8 @@ Capybara::SpecHelper.spec '#save_and_open_screenshot' do
|
|||
it 'prints out a correct warning message', requires: [:screenshot] do
|
||||
file_path = File.join(Dir.tmpdir, 'test.png')
|
||||
allow(@session).to receive(:require).with('launchy').and_raise(LoadError)
|
||||
allow(@session).to receive(:warn)
|
||||
|
||||
expect(@session).to receive(:warn).with("File saved to #{file_path}.\nPlease install the launchy gem to open the file automatically.")
|
||||
@session.save_and_open_screenshot(file_path)
|
||||
|
||||
expect(@session).to have_received(:warn).
|
||||
with("File saved to #{file_path}.")
|
||||
expect(@session).to have_received(:warn).
|
||||
with('Please install the launchy gem to open the file automatically.')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue