code cleanup/refactor

This commit is contained in:
Thomas Walpole 2018-01-11 16:45:50 -08:00
parent 5ca348f7d1
commit 86ede5207a
15 changed files with 152 additions and 151 deletions

View File

@ -14,22 +14,25 @@ Metrics/LineLength:
Enabled: false
Metrics/AbcSize:
Max: 53
Enabled: false
Metrics/ClassLength:
Max: 460
CountComments: false
Enabled: false
Metrics/CyclomaticComplexity:
Max: 17
Enabled: false
Metrics/MethodLength:
Max: 29
CountComments: false
Enabled: false
Metrics/ModuleLength:
Max: 194
Enabled: false
CountComments: false
Metrics/PerceivedComplexity:
Max: 19
Enabled: false
Lint/UnusedMethodArgument:
Exclude:

View File

@ -230,17 +230,8 @@ module Capybara
end
# Allow user to update the CSS style of the file input since they are so often hidden on a page
if make_visible
make_visible = { opacity: 1, display: 'block', visibility: 'visible' } if make_visible == true
ff = find(:file_field, locator, options.merge(visible: :all))
_update_style(ff, make_visible)
raise ExpectationNotMet, "The style changes in :make_visible did not make the file input visible" unless ff.visible?
begin
ff.set(path)
ensure
_reset_style(ff)
end
while_visible(ff, make_visible) { |el| el.set(path) }
else
find(:file_field, locator, options).set(path)
end
@ -248,36 +239,26 @@ module Capybara
private
def _update_style(element, style)
script = <<-JS
var el = arguments[0];
el.capybara_style_cache = el.style.cssText;
var css = arguments[1];
for (var prop in css){
if (css.hasOwnProperty(prop)) {
el.style[prop] = css[prop]
}
}
JS
def while_visible(element, visible_css)
visible_css = { opacity: 1, display: 'block', visibility: 'visible' } if visible_css == true
_update_style(element, visible_css)
raise ExpectationNotMet, "The style changes in :make_visible did not make the file input visible" unless element.visible?
begin
session.execute_script(script, element, style)
rescue Capybara::NotSupportedByDriverError
warn "The :make_visible option is not supported by the current driver - ignoring"
yield element
ensure
_reset_style(element)
end
end
def _update_style(element, style)
session.execute_script(UPDATE_STYLE_SCRIPT, element, style)
rescue Capybara::NotSupportedByDriverError
warn "The :make_visible option is not supported by the current driver - ignoring"
end
def _reset_style(element)
script = <<-JS
var el = arguments[0];
if (el.hasOwnProperty('capybara_style_cache')) {
el.style.cssText = el.capybara_style_cache;
delete el.capybara_style_cache;
}
JS
begin
session.execute_script(script, element)
rescue # swallow extra errors
end
session.execute_script(RESET_STYLE_SCRIPT, element)
rescue # swallow extra errors
end
def _check_with_label(selector, checked, locator, allow_label_click: session_options.automatic_label_click, **options)
@ -289,14 +270,32 @@ module Capybara
raise unless allow_label_click && catch_error?(e)
begin
el ||= find(selector, locator, options.merge(visible: :all))
label = find(:label, for: el, visible: true)
label.click unless el.checked? == checked
find(:label, for: el, visible: true).click unless el.checked? == checked
rescue # swallow extra errors - raise original
raise e
end
end
end
end
UPDATE_STYLE_SCRIPT = <<-'JS'.freeze
var el = arguments[0];
el.capybara_style_cache = el.style.cssText;
var css = arguments[1];
for (var prop in css){
if (css.hasOwnProperty(prop)) {
el.style[prop] = css[prop]
}
}
JS
RESET_STYLE_SCRIPT = <<-'JS'.freeze
var el = arguments[0];
if (el.hasOwnProperty('capybara_style_cache')) {
el.style.cssText = el.capybara_style_cache;
delete el.capybara_style_cache;
}
JS
end
end
end

View File

@ -75,18 +75,16 @@ module Capybara
# @raise [Capybara::FrozenInTime] If the return value of `Time.now` appears stuck
#
def synchronize(seconds = session_options.default_max_wait_time, errors: nil)
start_time = Capybara::Helpers.monotonic_time
if session.synchronized
yield
else
session.synchronized = true
start_time = Capybara::Helpers.monotonic_time
begin
yield
rescue => e
session.raise_server_error!
raise e unless driver.wait?
raise e unless catch_error?(e, errors)
raise e unless driver.wait? && catch_error?(e, errors)
raise e if (Capybara::Helpers.monotonic_time - start_time) >= seconds
sleep(0.05)
raise Capybara::FrozenInTime, "time appears to be frozen, Capybara does not work with libraries which freeze time, consider using time travelling instead" if Capybara::Helpers.monotonic_time == start_time

View File

@ -257,10 +257,9 @@ module Capybara
minimum_specified = %i[count minimum between].any? { |k| options.key?(k) }
options = { minimum: 1 }.merge(options) unless minimum_specified
options[:session_options] = session_options
args.push(options)
query = Capybara::Queries::SelectorQuery.new(*args, &optional_filter_block)
query = Capybara::Queries::SelectorQuery.new(*args.push(options), &optional_filter_block)
result = nil
begin
result = nil
synchronize(query.wait) do
result = query.resolve_for(self)
raise Capybara::ExpectationNotMet, result.failure_message unless result.matches_count?
@ -296,22 +295,27 @@ module Capybara
def synced_resolve(query)
synchronize(query.wait) do
if query.match == :smart or query.match == :prefer_exact
if prefer_exact?(query)
result = query.resolve_for(self, true)
result = query.resolve_for(self, false) if result.empty? && query.supports_exact? && !query.exact?
else
result = query.resolve_for(self)
end
if query.match == :one or query.match == :smart and result.size > 1
raise Capybara::Ambiguous, "Ambiguous match, found #{result.size} elements matching #{query.description}"
end
raise Capybara::Ambiguous, "Ambiguous match, found #{result.size} elements matching #{query.description}" if ambiguous?(query, result)
raise Capybara::ElementNotFound, "Unable to find #{query.description}" if result.empty?
result.first
end.tap(&:allow_reload!)
end
def ambiguous?(query, result)
query.match == :one or query.match == :smart and result.size > 1
end
def prefer_exact?(query)
query.match == :smart or query.match == :prefer_exact
end
end
end
end

View File

@ -61,25 +61,7 @@ module Capybara
when :hidden then return false if node.visible?
end
res = node_filters.all? do |name, filter|
if options.key?(name)
filter.matches?(node, options[name])
elsif filter.default?
filter.matches?(node, filter.default)
else
true
end
end
if @filter_block
res &&= if node.respond_to?(:session)
node.session.using_wait_time(0) { @filter_block.call(node) }
else
@filter_block.call(node)
end
end
res
matches_node_filters?(node) && matches_filter_block?(node)
rescue *(node.respond_to?(:session) ? node.session.driver.invalid_element_errors : [])
return false
end
@ -142,6 +124,28 @@ module Capybara
VALID_KEYS + custom_keys
end
def matches_node_filters?(node)
node_filters.all? do |name, filter|
if options.key?(name)
filter.matches?(node, options[name])
elsif filter.default?
filter.matches?(node, filter.default)
else
true
end
end
end
def matches_filter_block?(node)
return true unless @filter_block
if node.respond_to?(:session)
node.session.using_wait_time(0) { @filter_block.call(node) }
else
@filter_block.call(node)
end
end
def node_filters
if options.key?(:filter_set)
::Capybara::Selector::FilterSet.all[options[:filter_set]].node_filters

View File

@ -54,31 +54,31 @@ module Capybara
message << " in #{@actual_text.inspect}"
details_message = []
if @node and !@expected_text.is_a? Regexp
insensitive_regexp = Capybara::Helpers.to_regexp(@expected_text, Regexp::IGNORECASE)
insensitive_count = @actual_text.scan(insensitive_regexp).size
if insensitive_count != @count
details_message << "it was found #{insensitive_count} #{Capybara::Helpers.declension("time", "times", insensitive_count)} using a case insensitive search"
end
end
if @node and check_visible_text? and report_on_invisible
begin
invisible_text = text(@node, :all)
invisible_count = invisible_text.scan(@search_regexp).size
if invisible_count != @count
details_message << "it was found #{invisible_count} #{Capybara::Helpers.declension("time", "times", invisible_count)} including non-visible text"
end
rescue # An error getting the non-visible text (if element goes out of scope) should not affect the response
end
end
details_message << case_insensitive_message if @node and !@expected_text.is_a? Regexp
details_message << invisible_message if @node and check_visible_text? and report_on_invisible
details_message.compact!
message << ". (However, #{details_message.join(' and ')}.)" unless details_message.empty?
message
end
def case_insensitive_message
insensitive_regexp = Capybara::Helpers.to_regexp(@expected_text, Regexp::IGNORECASE)
insensitive_count = @actual_text.scan(insensitive_regexp).size
if insensitive_count != @count
"it was found #{insensitive_count} #{Capybara::Helpers.declension("time", "times", insensitive_count)} using a case insensitive search"
end
end
def invisible_message
invisible_text = text(@node, :all)
invisible_count = invisible_text.scan(@search_regexp).size
if invisible_count != @count
"it was found #{invisible_count} #{Capybara::Helpers.declension("time", "times", invisible_count)} including non-visible text"
end
rescue # An error getting the non-visible text (if element goes out of scope) should not affect the response
end
def valid_keys
COUNT_KEYS + %i[wait exact]
end

View File

@ -56,9 +56,7 @@ class Capybara::RackTest::Node < Capybara::Driver::Node
raise ArgumentError, "The RackTest driver does not support click options" unless keys.empty? && offset.empty?
if tag_name == 'a' && !self[:href].nil?
method = self["data-method"] if driver.options[:respect_data_method]
method ||= :get
driver.follow(method, self[:href].to_s)
follow_link
elsif (tag_name == 'input' and %w[submit image].include?(type)) or
(tag_name == 'button' and [nil, "submit"].include?(type))
associated_form = form
@ -66,15 +64,7 @@ class Capybara::RackTest::Node < Capybara::Driver::Node
elsif tag_name == 'input' and %w[checkbox radio].include?(type)
set(!checked?)
elsif tag_name == 'label'
labelled_control = if native[:for]
find_xpath("//input[@id='#{native[:for]}']").first
else
find_xpath(".//input").first
end
if labelled_control && (labelled_control.checkbox? || labelled_control.radio?)
labelled_control.set(!labelled_control.checked?)
end
click_label
end
end
@ -194,6 +184,24 @@ private
self[attribute] && !self[attribute].empty?
end
def follow_link
method = self["data-method"] if driver.options[:respect_data_method]
method ||= :get
driver.follow(method, self[:href].to_s)
end
def click_label
labelled_control = if native[:for]
find_xpath("//input[@id='#{native[:for]}']").first
else
find_xpath(".//input").first
end
if labelled_control && (labelled_control.checkbox? || labelled_control.radio?)
labelled_control.set(!labelled_control.checked?)
end
end
protected
def checkbox?

View File

@ -62,7 +62,6 @@ module Capybara
!any?
end
# rubocop:disable Metrics/MethodLength
def compare_count
# Only check filters for as many elements as necessary to determine result
if @query.options[:count]
@ -106,7 +105,6 @@ module Capybara
return 0
end
# rubocop:enable Metrics/MethodLength
def matches_count?
compare_count.zero?

View File

@ -143,11 +143,11 @@ Capybara.add_selector(:link) do
end
unless locator.nil?
locator = locator.to_s
matchers = [XPath.attr(:id).equals(locator),
matchers = [XPath.attr(:id) == locator,
XPath.string.n.is(locator),
XPath.attr(:title).is(locator),
XPath.descendant(:img)[XPath.attr(:alt).is(locator)]].reduce(:|)
matchers = matchers.or XPath.attr(:'aria-label').is(locator) if enable_aria_label
matchers |= XPath.attr(:'aria-label').is(locator) if enable_aria_label
xpath = xpath[matchers]
end
xpath = xpath[find_by_attr(:title, title)]
@ -193,14 +193,14 @@ Capybara.add_selector(:button) do
unless locator.nil?
locator = locator.to_s
locator_matches = XPath.attr(:id).equals(locator) | XPath.attr(:value).is(locator) | XPath.attr(:title).is(locator)
locator_matches = locator_matches.or XPath.attr(:'aria-label').is(locator) if options[:enable_aria_label]
locator_matches |= XPath.attr(:'aria-label').is(locator) if options[:enable_aria_label]
input_btn_xpath = input_btn_xpath[locator_matches]
btn_xpath = btn_xpath[locator_matches | XPath.string.n.is(locator) | XPath.descendant(:img)[XPath.attr(:alt).is(locator)]]
alt_matches = XPath.attr(:alt).is(locator)
alt_matches = alt_matches.or XPath.attr(:'aria-label').is(locator) if options[:enable_aria_label]
alt_matches |= XPath.attr(:'aria-label').is(locator) if options[:enable_aria_label]
image_btn_xpath = image_btn_xpath[alt_matches]
end
@ -473,7 +473,7 @@ Capybara.add_selector(:label) do
xpath = XPath.descendant(:label)
xpath = xpath[XPath.string.n.is(locator.to_s) | (XPath.attr(:id) == locator.to_s)] unless locator.nil?
if options.key?(:for) && !options[:for].is_a?(Capybara::Node::Element)
with_attr = XPath.attr(:for).equals(options[:for].to_s)
with_attr = XPath.attr(:for) == options[:for].to_s
labelable_elements = %i[button input keygen meter output progress select textarea]
wrapped = !XPath.attr(:for) &
XPath.descendant(*labelable_elements)[XPath.attr(:id) == options[:for].to_s]

View File

@ -237,10 +237,10 @@ module Capybara
locate_xpath = xpath # Need to save original xpath for the label wrap
if locator
locator = locator.to_s
attr_matchers = [XPath.attr(:id).equals(locator),
XPath.attr(:name).equals(locator),
XPath.attr(:placeholder).equals(locator),
XPath.attr(:id).equals(XPath.anywhere(:label)[XPath.string.n.is(locator)].attr(:for))].reduce(:|)
attr_matchers = [XPath.attr(:id) == locator,
XPath.attr(:name) == locator,
XPath.attr(:placeholder) == locator,
XPath.attr(:id) == XPath.anywhere(:label)[XPath.string.n.is(locator)].attr(:for)].reduce(:|)
attr_matchers |= XPath.attr(:'aria-label').is(locator) if enable_aria_label
locate_xpath = locate_xpath[attr_matchers]
@ -260,7 +260,7 @@ module Capybara
if respond_to?(finder_name, true)
send(finder_name, value)
else
value ? XPath.attr(attribute).equals(value) : nil
value ? XPath.attr(attribute) == value : nil
end
end

View File

@ -335,7 +335,6 @@ private
end
end
# rubocop:disable Metrics/MethodLength
def insert_modal_handlers(accept, response_text)
prompt_response = if accept
if response_text.nil?
@ -389,7 +388,6 @@ private
JS
execute_script script
end
# rubocop:enable Metrics/MethodLength
def within_given_window(handle)
original_handle = current_window_handle

View File

@ -37,20 +37,17 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
# :backspace => send backspace keystrokes to clear the field <br/>
# 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]
if value.is_a?(Array) && !multiple?
raise ArgumentError, "Value cannot be an Array when 'multiple' attribute is not present. Not a #{value.class}"
end
case tag_name
when 'input'
case type
when 'radio'
click
when 'checkbox'
click if value ^ native.attribute('checked').to_s.eql?("true")
click if value ^ checked?
when 'file'
set_file(value)
else

View File

@ -511,35 +511,26 @@ module Capybara
# @raise [Capybara::ScopeError] if this method is invoked inside `within_frame` method
# @return value returned by the block
#
def within_window(window_or_handle)
if window_or_handle.instance_of?(Capybara::Window)
original = current_window
scopes << nil
begin
_switch_to_window(window_or_handle) unless original == window_or_handle
begin
yield
ensure
_switch_to_window(original) unless original == window_or_handle
end
ensure
scopes.pop
def within_window(window_or_proc)
original = current_window
scopes << nil
begin
case window_or_proc
when Capybara::Window
_switch_to_window(window_or_proc) unless original == window_or_proc
when Proc
_switch_to_window { window_or_proc.call }
else
raise ArgumentError("`#within_window` requires a `Capybara::Window` instance or a lambda")
end
elsif window_or_handle.is_a?(Proc)
original = current_window
scopes << nil
begin
_switch_to_window { window_or_handle.call }
begin
yield
ensure
_switch_to_window(original)
end
yield
ensure
scopes.pop
_switch_to_window(original) unless original == window_or_proc
end
else
raise ArgumentError("`#within_window` requires a `Capybara::Window` instance or a lambda")
ensure
scopes.pop
end
end

View File

@ -69,7 +69,7 @@ Capybara::SpecHelper.spec "#uncheck" do
end
it "should raise original error when no label available" do
expect { @session.uncheck('form_cars_ariel') }.to raise_error(Capybara::ElementNotFound, 'Unable to find visible checkbox "form_cars_ariel" that is not disabled')
expect { @session.uncheck('form_cars_porsche') }.to raise_error(Capybara::ElementNotFound, 'Unable to find visible checkbox "form_cars_porsche" that is not disabled')
end
it "should raise error if not allowed to click label" do

View File

@ -186,6 +186,7 @@ New line after and before textarea tag
<input type="checkbox" value="pagani" name="form[cars][]" id="form_cars_pagani" style="position: absolute; left: -9999px"/>
<label for="form_cars_pagani">Pagani</label>
<input type="checkbox" value="ariel" name="form[cars][]" id="form_cars_ariel" style="display: none"/>
<input type="checkbox" value="porsche" name="form[cars][]" id="form_cars_porsche" checked="checked" style="display: none"/>
<label>
McLaren
<input type="checkbox" value="mclaren" name="form[cars][]" id="form_cars_mclaren" style="display: none"/>