Code cleanup
This commit is contained in:
parent
2f8f15e11e
commit
744e9907a7
|
@ -214,8 +214,8 @@ module Capybara
|
||||||
# @overload attach_file([locator], paths, **options)
|
# @overload attach_file([locator], paths, **options)
|
||||||
# @macro waiting_behavior
|
# @macro waiting_behavior
|
||||||
#
|
#
|
||||||
# @param [String] locator Which field to attach the file to
|
# @param [String] locator Which field to attach the file to
|
||||||
# @param [String, Array<String>] paths The path(s) of the file(s) that will be attached, or an array of paths
|
# @param [String, Array<String>] paths The path(s) of the file(s) that will be attached
|
||||||
#
|
#
|
||||||
# @option options [Symbol] match (Capybara.match) The matching strategy to use (:one, :first, :prefer_exact, :smart).
|
# @option options [Symbol] match (Capybara.match) The matching strategy to use (:one, :first, :prefer_exact, :smart).
|
||||||
# @option options [Boolean] exact (Capybara.exact) Match the exact label name/contents or accept a partial match.
|
# @option options [Boolean] exact (Capybara.exact) Match the exact label name/contents or accept a partial match.
|
||||||
|
|
|
@ -55,9 +55,7 @@ module Capybara
|
||||||
|
|
||||||
def _verify_title(title, options)
|
def _verify_title(title, options)
|
||||||
query = Capybara::Queries::TitleQuery.new(title, options)
|
query = Capybara::Queries::TitleQuery.new(title, options)
|
||||||
synchronize(query.wait) do
|
synchronize(query.wait) { yield(query) }
|
||||||
yield(query)
|
|
||||||
end
|
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,8 +3,7 @@
|
||||||
module Capybara
|
module Capybara
|
||||||
module Queries
|
module Queries
|
||||||
class SelectorQuery < Queries::BaseQuery
|
class SelectorQuery < Queries::BaseQuery
|
||||||
attr_accessor :selector, :locator, :options, :expression, :find, :negative
|
attr_reader :expression, :selector, :locator, :options
|
||||||
|
|
||||||
VALID_KEYS = COUNT_KEYS + %i[text id class visible exact exact_text match wait filter_set]
|
VALID_KEYS = COUNT_KEYS + %i[text id class visible exact exact_text match wait filter_set]
|
||||||
VALID_MATCH = %i[first smart prefer_exact one].freeze
|
VALID_MATCH = %i[first smart prefer_exact one].freeze
|
||||||
|
|
||||||
|
@ -25,7 +24,7 @@ module Capybara
|
||||||
|
|
||||||
raise ArgumentError, "Unused parameters passed to #{self.class.name} : #{args}" unless args.empty?
|
raise ArgumentError, "Unused parameters passed to #{self.class.name} : #{args}" unless args.empty?
|
||||||
|
|
||||||
@expression = @selector.call(@locator, @options.merge(selector_config: { enable_aria_label: enable_aria_label, test_id: test_id }))
|
@expression = selector.call(@locator, @options.merge(selector_config: { enable_aria_label: enable_aria_label, test_id: test_id }))
|
||||||
|
|
||||||
warn_exact_usage
|
warn_exact_usage
|
||||||
|
|
||||||
|
@ -36,22 +35,22 @@ module Capybara
|
||||||
def label; selector.label || selector.name; end
|
def label; selector.label || selector.name; end
|
||||||
|
|
||||||
def description(applied = false)
|
def description(applied = false)
|
||||||
@description = +''
|
desc = +''
|
||||||
if !applied || @applied_filters
|
if !applied || applied_filters
|
||||||
@description << 'visible ' if visible == :visible
|
desc << 'visible ' if visible == :visible
|
||||||
@description << 'non-visible ' if visible == :hidden
|
desc << 'non-visible ' if visible == :hidden
|
||||||
end
|
end
|
||||||
@description << "#{label} #{locator.inspect}"
|
desc << "#{label} #{locator.inspect}"
|
||||||
if !applied || @applied_filters
|
if !applied || applied_filters
|
||||||
@description << " with#{' exact' if exact_text == true} text #{options[:text].inspect}" if options[:text]
|
desc << " with#{' exact' if exact_text == true} text #{options[:text].inspect}" if options[:text]
|
||||||
@description << " with exact text #{exact_text}" if exact_text.is_a?(String)
|
desc << " with exact text #{exact_text}" if exact_text.is_a?(String)
|
||||||
end
|
end
|
||||||
@description << " with id #{options[:id]}" if options[:id]
|
desc << " with id #{options[:id]}" if options[:id]
|
||||||
@description << " with classes [#{Array(options[:class]).join(',')}]" if options[:class]
|
desc << " with classes [#{Array(options[:class]).join(',')}]" if options[:class]
|
||||||
@description << selector.description(node_filters: !applied || (@applied_filters == :node), **options)
|
desc << selector.description(node_filters: !applied || (applied_filters == :node), **options)
|
||||||
@description << ' that also matches the custom filter block' if @filter_block && (!applied || (@applied_filters == :node))
|
desc << ' that also matches the custom filter block' if @filter_block && (!applied || (applied_filters == :node))
|
||||||
@description << " within #{@resolved_node.inspect}" if describe_within?
|
desc << " within #{@resolved_node.inspect}" if describe_within?
|
||||||
@description
|
desc
|
||||||
end
|
end
|
||||||
|
|
||||||
def applied_description
|
def applied_description
|
||||||
|
@ -136,6 +135,10 @@ module Capybara
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def applied_filters
|
||||||
|
@applied_filters ||= false
|
||||||
|
end
|
||||||
|
|
||||||
def find_selector(locator)
|
def find_selector(locator)
|
||||||
selector = if locator.is_a?(Symbol)
|
selector = if locator.is_a?(Symbol)
|
||||||
Selector.all.fetch(locator) { |sel_type| raise ArgumentError, "Unknown selector type (:#{sel_type})" }
|
Selector.all.fetch(locator) { |sel_type| raise ArgumentError, "Unknown selector type (:#{sel_type})" }
|
||||||
|
@ -171,7 +174,6 @@ module Capybara
|
||||||
|
|
||||||
def matches_filter_block?(node)
|
def matches_filter_block?(node)
|
||||||
return true unless @filter_block
|
return true unless @filter_block
|
||||||
|
|
||||||
if node.respond_to?(:session)
|
if node.respond_to?(:session)
|
||||||
node.session.using_wait_time(0) { @filter_block.call(node) }
|
node.session.using_wait_time(0) { @filter_block.call(node) }
|
||||||
else
|
else
|
||||||
|
@ -201,9 +203,9 @@ module Capybara
|
||||||
unless VALID_MATCH.include?(match)
|
unless VALID_MATCH.include?(match)
|
||||||
raise ArgumentError, "invalid option #{match.inspect} for :match, should be one of #{VALID_MATCH.map(&:inspect).join(', ')}"
|
raise ArgumentError, "invalid option #{match.inspect} for :match, should be one of #{VALID_MATCH.map(&:inspect).join(', ')}"
|
||||||
end
|
end
|
||||||
unhandled_options = @options.keys - valid_keys
|
unhandled_options = @options.keys.reject do |option_name|
|
||||||
unhandled_options -= @options.keys.select do |option_name|
|
valid_keys.include?(option_name) ||
|
||||||
expression_filters.any? { |_nmae, ef| ef.handles_option? option_name } ||
|
expression_filters.any? { |_name, ef| ef.handles_option? option_name } ||
|
||||||
node_filters.any? { |_name, nf| nf.handles_option? option_name }
|
node_filters.any? { |_name, nf| nf.handles_option? option_name }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -266,22 +268,21 @@ module Capybara
|
||||||
classes[true].to_a.map { |cl| ":not(.#{Capybara::Selector::CSS.escape(cl.slice(1))})" }).join
|
classes[true].to_a.map { |cl| ":not(.#{Capybara::Selector::CSS.escape(cl.slice(1))})" }).join
|
||||||
end
|
end
|
||||||
|
|
||||||
def apply_expression_filters(expr)
|
def apply_expression_filters(expression)
|
||||||
unapplied_options = options.keys - valid_keys
|
unapplied_options = options.keys - valid_keys
|
||||||
expression_filters.inject(expr) do |memo, (name, ef)|
|
expression_filters.inject(expression) do |expr, (name, ef)|
|
||||||
if ef.matcher?
|
if ef.matcher?
|
||||||
unapplied_options.select { |option_name| ef.handles_option?(option_name) }.each do |option_name|
|
unapplied_options.select { |option_name| ef.handles_option?(option_name) }.inject(expr) do |memo, option_name|
|
||||||
unapplied_options.delete(option_name)
|
unapplied_options.delete(option_name)
|
||||||
memo = ef.apply_filter(memo, option_name, options[option_name])
|
ef.apply_filter(memo, option_name, options[option_name])
|
||||||
end
|
end
|
||||||
memo
|
|
||||||
elsif options.key?(name)
|
elsif options.key?(name)
|
||||||
unapplied_options.delete(name)
|
unapplied_options.delete(name)
|
||||||
ef.apply_filter(memo, name, options[name])
|
ef.apply_filter(expr, name, options[name])
|
||||||
elsif ef.default?
|
elsif ef.default?
|
||||||
ef.apply_filter(memo, name, ef.default)
|
ef.apply_filter(expr, name, ef.default)
|
||||||
else
|
else
|
||||||
memo
|
expr
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -62,10 +62,7 @@ module Capybara
|
||||||
if max_idx.nil?
|
if max_idx.nil?
|
||||||
full_results[*args]
|
full_results[*args]
|
||||||
else
|
else
|
||||||
loop do
|
load_up_to(max_idx + 1)
|
||||||
break if @result_cache.size > max_idx
|
|
||||||
@result_cache << @results_enum.next
|
|
||||||
end
|
|
||||||
@result_cache[*args]
|
@result_cache[*args]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -77,40 +74,26 @@ module Capybara
|
||||||
|
|
||||||
def compare_count
|
def compare_count
|
||||||
# Only check filters for as many elements as necessary to determine result
|
# Only check filters for as many elements as necessary to determine result
|
||||||
if @query.options[:count]
|
if (count = @query.options[:count])
|
||||||
count_opt = Integer(@query.options[:count])
|
count = Integer(count)
|
||||||
loop do
|
return load_up_to(count + 1) <=> count
|
||||||
break if @result_cache.size > count_opt
|
|
||||||
@result_cache << @results_enum.next
|
|
||||||
end
|
|
||||||
return @result_cache.size <=> count_opt
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if @query.options[:minimum]
|
if (min = @query.options[:minimum])
|
||||||
min_opt = Integer(@query.options[:minimum])
|
min = Integer(min)
|
||||||
begin
|
return -1 if load_up_to(min) < min
|
||||||
@result_cache << @results_enum.next while @result_cache.size < min_opt
|
|
||||||
rescue StopIteration
|
|
||||||
return -1
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if @query.options[:maximum]
|
if (max = @query.options[:maximum])
|
||||||
max_opt = Integer(@query.options[:maximum])
|
max = Integer(max)
|
||||||
loop do
|
return 1 if load_up_to(max + 1) > max
|
||||||
return 1 if @result_cache.size > max_opt
|
|
||||||
@result_cache << @results_enum.next
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if @query.options[:between]
|
if (between = @query.options[:between])
|
||||||
min, max = @query.options[:between].minmax
|
min, max = between.minmax
|
||||||
loop do
|
size = load_up_to(max + 1)
|
||||||
break if @result_cache.size > max
|
return 0 if between.include? size
|
||||||
@result_cache << @results_enum.next
|
return size <=> min
|
||||||
end
|
|
||||||
return 0 if @query.options[:between].include? @result_cache.size
|
|
||||||
return @result_cache.size <=> min
|
|
||||||
end
|
end
|
||||||
|
|
||||||
0
|
0
|
||||||
|
@ -144,6 +127,14 @@ module Capybara
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def load_up_to(num)
|
||||||
|
loop do
|
||||||
|
break if @result_cache.size >= num
|
||||||
|
@result_cache << @results_enum.next
|
||||||
|
end
|
||||||
|
@result_cache.size
|
||||||
|
end
|
||||||
|
|
||||||
def full_results
|
def full_results
|
||||||
loop { @result_cache << @results_enum.next }
|
loop { @result_cache << @results_enum.next }
|
||||||
@result_cache
|
@result_cache
|
||||||
|
|
|
@ -185,7 +185,6 @@ module Capybara
|
||||||
@match = nil
|
@match = nil
|
||||||
@label = nil
|
@label = nil
|
||||||
@failure_message = nil
|
@failure_message = nil
|
||||||
@description = nil
|
|
||||||
@format = nil
|
@format = nil
|
||||||
@expression = nil
|
@expression = nil
|
||||||
@expression_filters = {}
|
@expression_filters = {}
|
||||||
|
|
|
@ -49,7 +49,7 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
||||||
@app = app
|
@app = app
|
||||||
@browser = nil
|
@browser = nil
|
||||||
@exit_status = nil
|
@exit_status = nil
|
||||||
@frame_handles = {}
|
@frame_handles = Hash.new { |hash, handle| hash[handle] = [] }
|
||||||
@options = DEFAULT_OPTIONS.merge(options)
|
@options = DEFAULT_OPTIONS.merge(options)
|
||||||
@node_class = ::Capybara::Selenium::Node
|
@node_class = ::Capybara::Selenium::Node
|
||||||
end
|
end
|
||||||
|
@ -170,19 +170,19 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
||||||
end
|
end
|
||||||
|
|
||||||
def switch_to_frame(frame)
|
def switch_to_frame(frame)
|
||||||
|
handles = @frame_handles[current_window_handle]
|
||||||
case frame
|
case frame
|
||||||
when :top
|
when :top
|
||||||
@frame_handles[browser.window_handle] = []
|
handles.clear
|
||||||
browser.switch_to.default_content
|
browser.switch_to.default_content
|
||||||
when :parent
|
when :parent
|
||||||
# would love to use browser.switch_to.parent_frame here
|
# would love to use browser.switch_to.parent_frame here
|
||||||
# but it has an issue if the current frame is removed from within it
|
# but it has an issue if the current frame is removed from within it
|
||||||
@frame_handles[browser.window_handle].pop
|
handles.pop
|
||||||
browser.switch_to.default_content
|
browser.switch_to.default_content
|
||||||
@frame_handles[browser.window_handle].each { |fh| browser.switch_to.frame(fh) }
|
handles.each { |fh| browser.switch_to.frame(fh) }
|
||||||
else
|
else
|
||||||
@frame_handles[browser.window_handle] ||= []
|
handles << frame.native
|
||||||
@frame_handles[browser.window_handle] << frame.native
|
|
||||||
browser.switch_to.frame(frame.native)
|
browser.switch_to.frame(frame.native)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# Selenium specific implementation of the Capybara::Driver::Node API
|
||||||
class Capybara::Selenium::Node < Capybara::Driver::Node
|
class Capybara::Selenium::Node < Capybara::Driver::Node
|
||||||
def visible_text
|
def visible_text
|
||||||
native.text
|
native.text
|
||||||
|
@ -82,13 +83,11 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
|
||||||
end
|
end
|
||||||
|
|
||||||
def click(keys = [], **options)
|
def click(keys = [], **options)
|
||||||
if keys.empty? && !coords?(options)
|
click_options = ClickOptions.new(keys, options)
|
||||||
native.click
|
return native.click if click_options.empty?
|
||||||
else
|
scroll_if_needed do
|
||||||
scroll_if_needed do
|
action_with_modifiers(click_options) do |action|
|
||||||
action_with_modifiers(keys, options) do |action|
|
click_options.coords? ? action.click : action.click(native)
|
||||||
coords?(options) ? action.click : action.click(native)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
rescue StandardError => err
|
rescue StandardError => err
|
||||||
|
@ -101,17 +100,19 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
|
||||||
end
|
end
|
||||||
|
|
||||||
def right_click(keys = [], **options)
|
def right_click(keys = [], **options)
|
||||||
|
click_options = ClickOptions.new(keys, options)
|
||||||
scroll_if_needed do
|
scroll_if_needed do
|
||||||
action_with_modifiers(keys, options) do |action|
|
action_with_modifiers(click_options) do |action|
|
||||||
coords?(options) ? action.context_click : action.context_click(native)
|
click_options.coords? ? action.context_click : action.context_click(native)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def double_click(keys = [], **options)
|
def double_click(keys = [], **options)
|
||||||
|
click_options = ClickOptions.new(keys, options)
|
||||||
scroll_if_needed do
|
scroll_if_needed do
|
||||||
action_with_modifiers(keys, options) do |action|
|
action_with_modifiers(click_options) do |action|
|
||||||
coords?(options) ? action.double_click : action.double_click(native)
|
click_options.coords? ? action.double_click : action.double_click(native)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -121,11 +122,11 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
|
||||||
end
|
end
|
||||||
|
|
||||||
def hover
|
def hover
|
||||||
scroll_if_needed { driver.browser.action.move_to(native).perform }
|
scroll_if_needed { browser_action.move_to(native).perform }
|
||||||
end
|
end
|
||||||
|
|
||||||
def drag_to(element)
|
def drag_to(element)
|
||||||
scroll_if_needed { driver.browser.action.drag_and_drop(native, element.native).perform }
|
scroll_if_needed { browser_action.drag_and_drop(native, element.native).perform }
|
||||||
end
|
end
|
||||||
|
|
||||||
def tag_name
|
def tag_name
|
||||||
|
@ -192,10 +193,6 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def coords?(options)
|
|
||||||
options[:x] && options[:y]
|
|
||||||
end
|
|
||||||
|
|
||||||
def boolean_attr(val)
|
def boolean_attr(val)
|
||||||
val && (val != 'false')
|
val && (val != 'false')
|
||||||
end
|
end
|
||||||
|
@ -206,22 +203,23 @@ private
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_text(value, clear: nil, **_unused)
|
def set_text(value, clear: nil, **_unused)
|
||||||
if value.to_s.empty? && clear.nil?
|
value = value.to_s
|
||||||
|
if value.empty? && clear.nil?
|
||||||
native.clear
|
native.clear
|
||||||
elsif clear == :backspace
|
elsif clear == :backspace
|
||||||
# Clear field by sending the correct number of backspace keys.
|
# Clear field by sending the correct number of backspace keys.
|
||||||
backspaces = [:backspace] * self.value.to_s.length
|
backspaces = [:backspace] * self.value.to_s.length
|
||||||
send_keys(*([:end] + backspaces + [value.to_s]))
|
send_keys(*([:end] + backspaces + [value]))
|
||||||
elsif clear == :none
|
elsif clear == :none
|
||||||
send_keys(value.to_s)
|
send_keys(value)
|
||||||
elsif clear.is_a? Array
|
elsif clear.is_a? Array
|
||||||
send_keys(*clear, value.to_s)
|
send_keys(*clear, value)
|
||||||
else
|
else
|
||||||
# Clear field by JavaScript assignment of the value property.
|
# Clear field by JavaScript assignment of the value property.
|
||||||
# Script can change a readonly element which user input cannot, so
|
# Script can change a readonly element which user input cannot, so
|
||||||
# don't execute if readonly.
|
# don't execute if readonly.
|
||||||
driver.execute_script "arguments[0].value = ''", self
|
driver.execute_script "arguments[0].value = ''", self
|
||||||
send_keys(value.to_s)
|
send_keys(value)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -248,21 +246,24 @@ private
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_date(value) # rubocop:disable Naming/AccessorMethodName
|
def set_date(value) # rubocop:disable Naming/AccessorMethodName
|
||||||
return set_text(value) if value.is_a?(String) || !value.respond_to?(:to_date)
|
value = SettableValue.new(value)
|
||||||
|
return set_text(value) unless value.dateable?
|
||||||
# TODO: this would be better if locale can be detected and correct keystrokes sent
|
# TODO: this would be better if locale can be detected and correct keystrokes sent
|
||||||
update_value_js(value.to_date.strftime('%Y-%m-%d'))
|
update_value_js(value.to_date_str)
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_time(value) # rubocop:disable Naming/AccessorMethodName
|
def set_time(value) # rubocop:disable Naming/AccessorMethodName
|
||||||
return set_text(value) if value.is_a?(String) || !value.respond_to?(:to_time)
|
value = SettableValue.new(value)
|
||||||
|
return set_text(value) unless value.timeable?
|
||||||
# TODO: this would be better if locale can be detected and correct keystrokes sent
|
# TODO: this would be better if locale can be detected and correct keystrokes sent
|
||||||
update_value_js(value.to_time.strftime('%H:%M'))
|
update_value_js(value.to_time_str)
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_datetime_local(value) # rubocop:disable Naming/AccessorMethodName
|
def set_datetime_local(value) # rubocop:disable Naming/AccessorMethodName
|
||||||
return set_text(value) if value.is_a?(String) || !value.respond_to?(:to_time)
|
value = SettableValue.new(value)
|
||||||
|
return set_text(value) unless value.timeable?
|
||||||
# TODO: this would be better if locale can be detected and correct keystrokes sent
|
# TODO: this would be better if locale can be detected and correct keystrokes sent
|
||||||
update_value_js(value.to_time.strftime('%Y-%m-%dT%H:%M'))
|
update_value_js(value.to_datetime_str)
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_value_js(value)
|
def update_value_js(value)
|
||||||
|
@ -301,34 +302,33 @@ private
|
||||||
# if we use the faster direct send_keys. For now just send_keys to the element
|
# if we use the faster direct send_keys. For now just send_keys to the element
|
||||||
# we've already focused.
|
# we've already focused.
|
||||||
# native.send_keys(value.to_s)
|
# native.send_keys(value.to_s)
|
||||||
driver.browser.action.send_keys(value.to_s).perform
|
browser_action.send_keys(value.to_s).perform
|
||||||
end
|
end
|
||||||
|
|
||||||
def action_with_modifiers(keys, x: nil, y: nil)
|
def action_with_modifiers(click_options)
|
||||||
actions = driver.browser.action
|
actions = browser_action.move_to(native, *click_options.coords)
|
||||||
actions.move_to(native, x, y)
|
modifiers_down(actions, click_options.keys)
|
||||||
modifiers_down(actions, keys)
|
|
||||||
yield actions
|
yield actions
|
||||||
modifiers_up(actions, keys)
|
modifiers_up(actions, click_options.keys)
|
||||||
actions.perform
|
actions.perform
|
||||||
ensure
|
ensure
|
||||||
act = driver.browser.action
|
act = browser_action
|
||||||
act.release_actions if act.respond_to?(:release_actions)
|
act.release_actions if act.respond_to?(:release_actions)
|
||||||
end
|
end
|
||||||
|
|
||||||
def modifiers_down(actions, keys)
|
def modifiers_down(actions, keys)
|
||||||
keys.each do |key|
|
each_key(keys) { |key| actions.key_down(key) }
|
||||||
key = case key
|
|
||||||
when :ctrl then :control
|
|
||||||
when :command, :cmd then :meta
|
|
||||||
else
|
|
||||||
key
|
|
||||||
end
|
|
||||||
actions.key_down(key)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def modifiers_up(actions, keys)
|
def modifiers_up(actions, keys)
|
||||||
|
each_key(keys) { |key| actions.key_up(key) }
|
||||||
|
end
|
||||||
|
|
||||||
|
def browser_action
|
||||||
|
driver.browser.action
|
||||||
|
end
|
||||||
|
|
||||||
|
def each_key(keys)
|
||||||
keys.each do |key|
|
keys.each do |key|
|
||||||
key = case key
|
key = case key
|
||||||
when :ctrl then :control
|
when :ctrl then :control
|
||||||
|
@ -336,7 +336,60 @@ private
|
||||||
else
|
else
|
||||||
key
|
key
|
||||||
end
|
end
|
||||||
actions.key_up(key)
|
yield key
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# SettableValue encapsulates time/date field formatting
|
||||||
|
class SettableValue
|
||||||
|
attr_reader :value
|
||||||
|
|
||||||
|
def initialize(value)
|
||||||
|
@value = value
|
||||||
|
end
|
||||||
|
|
||||||
|
def dateable?
|
||||||
|
!value.is_a?(String) && value.respond_to?(:to_date)
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_date_str
|
||||||
|
value.to_date.strftime('%Y-%m-%d')
|
||||||
|
end
|
||||||
|
|
||||||
|
def timeable?
|
||||||
|
!value.is_a?(String) && value.respond_to?(:to_time)
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_time_str
|
||||||
|
value.to_time.strftime('%H:%M')
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_datetime_str
|
||||||
|
value.to_time.strftime('%Y-%m-%dT%H:%M')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
private_constant :SettableValue
|
||||||
|
|
||||||
|
# ClickOptions encapsulates click option logic
|
||||||
|
class ClickOptions
|
||||||
|
attr_reader :keys, :options
|
||||||
|
|
||||||
|
def initialize(keys, options)
|
||||||
|
@keys = keys
|
||||||
|
@options = options
|
||||||
|
end
|
||||||
|
|
||||||
|
def coords?
|
||||||
|
options[:x] && options[:y]
|
||||||
|
end
|
||||||
|
|
||||||
|
def coords
|
||||||
|
[options[:x], options[:y]]
|
||||||
|
end
|
||||||
|
|
||||||
|
def empty?
|
||||||
|
keys.empty? && !coords?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
private_constant :ClickOptions
|
||||||
end
|
end
|
||||||
|
|
|
@ -13,7 +13,7 @@ class Capybara::Selenium::ChromeNode < Capybara::Selenium::Node
|
||||||
def drag_to(element)
|
def drag_to(element)
|
||||||
return super unless self[:draggable] == 'true'
|
return super unless self[:draggable] == 'true'
|
||||||
|
|
||||||
scroll_if_needed { driver.browser.action.click_and_hold(native).perform }
|
scroll_if_needed { browser_action.click_and_hold(native).perform }
|
||||||
driver.execute_script HTML5_DRAG_DROP_SCRIPT, self, element
|
driver.execute_script HTML5_DRAG_DROP_SCRIPT, self, element
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -47,17 +47,15 @@ class Capybara::Selenium::MarionetteNode < Capybara::Selenium::Node
|
||||||
return super(*args.map { |arg| arg == :space ? ' ' : arg }) if args.none? { |arg| arg.is_a? Array }
|
return super(*args.map { |arg| arg == :space ? ' ' : arg }) if args.none? { |arg| arg.is_a? Array }
|
||||||
|
|
||||||
native.click
|
native.click
|
||||||
actions = driver.browser.action
|
args.each_with_object(browser_action) do |keys, actions|
|
||||||
args.each do |keys|
|
|
||||||
_send_keys(keys, actions)
|
_send_keys(keys, actions)
|
||||||
end
|
end.perform
|
||||||
actions.perform
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def drag_to(element)
|
def drag_to(element)
|
||||||
return super unless (browser_version >= 62.0) && (self[:draggable] == 'true')
|
return super unless (browser_version >= 62.0) && (self[:draggable] == 'true')
|
||||||
|
|
||||||
scroll_if_needed { driver.browser.action.click_and_hold(native).perform }
|
scroll_if_needed { browser_action.click_and_hold(native).perform }
|
||||||
driver.execute_script HTML5_DRAG_DROP_SCRIPT, self, element
|
driver.execute_script HTML5_DRAG_DROP_SCRIPT, self, element
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,7 @@ module Capybara
|
||||||
end
|
end
|
||||||
|
|
||||||
def reset_error!
|
def reset_error!
|
||||||
middleware.error = nil
|
middleware.clear_error
|
||||||
end
|
end
|
||||||
|
|
||||||
def error
|
def error
|
||||||
|
|
|
@ -20,7 +20,7 @@ module Capybara
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
attr_accessor :error
|
attr_reader :error
|
||||||
|
|
||||||
def initialize(app, server_errors, extra_middleware = [])
|
def initialize(app, server_errors, extra_middleware = [])
|
||||||
@app = app
|
@app = app
|
||||||
|
@ -35,6 +35,10 @@ module Capybara
|
||||||
@counter.value.positive?
|
@counter.value.positive?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def clear_error
|
||||||
|
@error = nil
|
||||||
|
end
|
||||||
|
|
||||||
def call(env)
|
def call(env)
|
||||||
if env['PATH_INFO'] == '/__identify__'
|
if env['PATH_INFO'] == '/__identify__'
|
||||||
[200, {}, [@app.object_id.to_s]]
|
[200, {}, [@app.object_id.to_s]]
|
||||||
|
|
|
@ -137,7 +137,7 @@ module Capybara
|
||||||
# Raise errors encountered in the server
|
# Raise errors encountered in the server
|
||||||
#
|
#
|
||||||
def raise_server_error!
|
def raise_server_error!
|
||||||
return if @server.nil? || !@server.error
|
return unless @server&.error
|
||||||
# Force an explanation for the error being raised as the exception cause
|
# Force an explanation for the error being raised as the exception cause
|
||||||
begin
|
begin
|
||||||
if config.raise_server_errors
|
if config.raise_server_errors
|
||||||
|
|
|
@ -323,7 +323,7 @@ RSpec.shared_examples 'Capybara::Session' do |session, mode|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'Capybara#disable_animation', :focus_ do
|
describe 'Capybara#disable_animation' do
|
||||||
context 'when set to `true`' do
|
context 'when set to `true`' do
|
||||||
before(:context) do # rubocop:disable RSpec/BeforeAfterAll
|
before(:context) do # rubocop:disable RSpec/BeforeAfterAll
|
||||||
# NOTE: Although Capybara.SpecHelper.reset! sets Capybara.disable_animation to false,
|
# NOTE: Although Capybara.SpecHelper.reset! sets Capybara.disable_animation to false,
|
||||||
|
|
Loading…
Reference in New Issue