minor refactor

This commit is contained in:
Thomas Walpole 2018-05-15 09:17:09 -07:00
parent 952d373026
commit a6e289f6cc
3 changed files with 47 additions and 43 deletions

View File

@ -174,41 +174,12 @@ module Capybara
# #
# @return [Capybara::Node::Element] The option element selected # @return [Capybara::Node::Element] The option element selected
def select(value = nil, from: nil, **options) def select(value = nil, from: nil, **options)
scope = if from el = from ? find_select_or_datalist_input(from, options) : self
synchronize(Capybara::Queries::BaseQuery.wait(options, session_options.default_max_wait_time)) do
begin
find(:select, from, options)
rescue Capybara::ElementNotFound => select_error
raise if %i[selected with_selected multiple].any? { |option| options.key?(option) }
begin
find(:datalist_input, from, options)
rescue Capybara::ElementNotFound => dlinput_error
raise Capybara::ElementNotFound, "#{select_error.message} and #{dlinput_error.message}"
end
end
end
else
self
end
if scope.respond_to?(:tag_name) && scope.tag_name == "input" if el.respond_to?(:tag_name) && (el.tag_name == "input")
begin select_datalist_option(el, value)
# TODO: this is a more efficient but won't work with non-JS drivers
# datalist_options = session.evaluate_script('Array.prototype.slice.call((arguments[0].list||{}).options || []).filter(function(el){ return !el.disabled }).map(function(el){ return { "value": el.value, "label": el.label} })', scope)
datalist_options = session.evaluate_script(DATALIST_OPTIONS_SCRIPT, scope)
if (option = datalist_options.find { |o| o['value'] == value || o['label'] == value })
scope.set(option["value"])
else
raise ::Capybara::ElementNotFound, "Unable to find datalist option \"#{value}\""
end
rescue ::Capybara::NotSupportedByDriverError
# Implement for drivers that don't support JS
datalist = find(:xpath, XPath.descendant(:datalist)[XPath.attr(:id) == scope[:list]], visible: false)
option = datalist.find(:datalist_option, value, disabled: false)
scope.set(option.value)
end
else else
scope.find(:option, value, options).select_option el.find(:option, value, options).select_option
end end
end end
@ -267,6 +238,35 @@ module Capybara
private private
def find_select_or_datalist_input(from, options)
synchronize(Capybara::Queries::BaseQuery.wait(options, session_options.default_max_wait_time)) do
begin
find(:select, from, options)
rescue Capybara::ElementNotFound => select_error
raise if %i[selected with_selected multiple].any? { |option| options.key?(option) }
begin
find(:datalist_input, from, options)
rescue Capybara::ElementNotFound => dlinput_error
raise Capybara::ElementNotFound, "#{select_error.message} and #{dlinput_error.message}"
end
end
end
end
def select_datalist_option(input, value)
datalist_options = session.evaluate_script(DATALIST_OPTIONS_SCRIPT, input)
if (option = datalist_options.find { |o| o['value'] == value || o['label'] == value })
input.set(option["value"])
else
raise ::Capybara::ElementNotFound, "Unable to find datalist option \"#{value}\""
end
rescue ::Capybara::NotSupportedByDriverError
# Implement for drivers that don't support JS
datalist = find(:xpath, XPath.descendant(:datalist)[XPath.attr(:id) == input[:list]], visible: false)
option = datalist.find(:datalist_option, value, disabled: false)
input.set(option.value)
end
def while_visible(element, visible_css) def while_visible(element, visible_css)
visible_css = { opacity: 1, display: 'block', visibility: 'visible' } if visible_css == true visible_css = { opacity: 1, display: 'block', visibility: 'visible' } if visible_css == true
_update_style(element, visible_css) _update_style(element, visible_css)

View File

@ -245,7 +245,7 @@ module Capybara
minimum_specified = options_include_minimum?(options) minimum_specified = options_include_minimum?(options)
options = { minimum: 1 }.merge(options) unless minimum_specified options = { minimum: 1 }.merge(options) unless minimum_specified
options[:session_options] = session_options options[:session_options] = session_options
query = Capybara::Queries::SelectorQuery.new(*args.push(options), &optional_filter_block) query = Capybara::Queries::SelectorQuery.new(*args, options, &optional_filter_block)
result = nil result = nil
begin begin
synchronize(query.wait) do synchronize(query.wait) do
@ -298,11 +298,11 @@ module Capybara
end end
def ambiguous?(query, result) def ambiguous?(query, result)
((query.match == :one) || (query.match == :smart)) && (result.size > 1) %i[one smart].include?(query.match) && (result.size > 1)
end end
def prefer_exact?(query) def prefer_exact?(query)
(query.match == :smart) || (query.match == :prefer_exact) %i[smart prefer_exact].include?(query.match)
end end
def options_include_minimum?(opts) def options_include_minimum?(opts)

View File

@ -93,11 +93,7 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
rescue StandardError => e rescue StandardError => e
if e.is_a?(::Selenium::WebDriver::Error::ElementClickInterceptedError) || if e.is_a?(::Selenium::WebDriver::Error::ElementClickInterceptedError) ||
e.message =~ /Other element would receive the click/ e.message =~ /Other element would receive the click/
begin scroll_to_center
driver.execute_script("arguments[0].scrollIntoView({behavior: 'instant', block: 'center', inline: 'center'})", self)
rescue StandardError # rubocop:disable Lint/HandleExceptions
# Swallow error if scrollIntoView with options isn't supported
end
end end
raise e raise e
end end
@ -230,6 +226,11 @@ private
def scroll_if_needed def scroll_if_needed
yield yield
rescue ::Selenium::WebDriver::Error::MoveTargetOutOfBoundsError rescue ::Selenium::WebDriver::Error::MoveTargetOutOfBoundsError
scroll_to_center
yield
end
def scroll_to_center
script = <<-'JS' script = <<-'JS'
try { try {
arguments[0].scrollIntoView({behavior: 'instant', block: 'center', inline: 'center'}); arguments[0].scrollIntoView({behavior: 'instant', block: 'center', inline: 'center'});
@ -237,8 +238,11 @@ private
arguments[0].scrollIntoView(true); arguments[0].scrollIntoView(true);
} }
JS JS
driver.execute_script(script, self) begin
yield driver.execute_script(script, self)
rescue StandardError # rubocop:disable Lint/HandleExceptions
# Swallow error if scrollIntoView with options isn't supported
end
end end
def set_date(value) # rubocop:disable Naming/AccessorMethodName def set_date(value) # rubocop:disable Naming/AccessorMethodName