Optimize wirecalls in selenium driver

This commit is contained in:
Thomas Walpole 2019-04-30 09:49:01 -07:00
parent 32297d027c
commit 72ec06b0c4
6 changed files with 43 additions and 22 deletions

View File

@ -126,7 +126,6 @@ module Capybara
# #
# @return [Capybara::Node::Element] The element # @return [Capybara::Node::Element] The element
def select_option(wait: nil) def select_option(wait: nil)
warn "Attempt to select disabled option: #{value || text}" if disabled?
synchronize(wait) { base.select_option } synchronize(wait) { base.select_option }
self self
end end
@ -276,7 +275,8 @@ module Capybara
# @return [String] The tag name of the element # @return [String] The tag name of the element
# #
def tag_name def tag_name
synchronize { base.tag_name } # Element type is immutable so cache it
@tag_name ||= synchronize { base.tag_name }
end end
## ##

View File

@ -56,10 +56,12 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
def set(value, **options) 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? 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, type = attrs(:tagName, :type) tag_name, type = attrs(:tagName, :type).map { |val| val&.downcase }
case tag_name.downcase @tag_name ||= tag_name
case tag_name
when 'input' when 'input'
case type.downcase case type
when 'radio' when 'radio'
click click
when 'checkbox' when 'checkbox'
@ -78,7 +80,7 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
when 'textarea' when 'textarea'
set_text(value, options) set_text(value, options)
else else
set_content_editable(value) if content_editable? set_content_editable(value)
end end
end end
@ -136,7 +138,7 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
end end
def tag_name def tag_name
native.tag_name.downcase @tag_name ||= native.tag_name.downcase
end end
def visible?; boolean_attr(native.displayed?); end def visible?; boolean_attr(native.displayed?); end
@ -291,20 +293,24 @@ private
# Ensure we are focused on the element # Ensure we are focused on the element
click click
driver.execute_script <<-JS, self editable = driver.execute_script <<-JS, self
var range = document.createRange(); if (arguments[0].isContentEditable) {
var sel = window.getSelection(); var range = document.createRange();
arguments[0].focus(); var sel = window.getSelection();
range.selectNodeContents(arguments[0]); arguments[0].focus();
sel.removeAllRanges(); range.selectNodeContents(arguments[0]);
sel.addRange(range); sel.removeAllRanges();
sel.addRange(range);
return true;
}
return false;
JS JS
# The action api has a speed problem but both chrome and firefox 58 raise errors # The action api has a speed problem but both chrome and firefox 58 raise errors
# 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)
browser_action.send_keys(value.to_s).perform browser_action.send_keys(value.to_s).perform if editable
end end
def action_with_modifiers(click_options) def action_with_modifiers(click_options)

View File

@ -39,6 +39,14 @@ class Capybara::Selenium::ChromeNode < Capybara::Selenium::Node
driver.evaluate_script("arguments[0].matches(':disabled, select:disabled *')", self) driver.evaluate_script("arguments[0].matches(':disabled, select:disabled *')", self)
end end
def select_option
# To optimize to only one check and then click
selected_or_disabled = driver.evaluate_script(<<~JS, self)
arguments[0].matches(':disabled, select:disabled *, :checked')
JS
click unless selected_or_disabled
end
private private
def file_errors def file_errors

View File

@ -55,6 +55,14 @@ class Capybara::Selenium::FirefoxNode < Capybara::Selenium::Node
scroll_if_needed { browser_action.move_to(native, 0, 0).move_to(native).perform } scroll_if_needed { browser_action.move_to(native, 0, 0).move_to(native).perform }
end end
def select_option
# To optimize to only one check and then click
selected_or_disabled = driver.evaluate_script(<<~JS, self)
arguments[0].matches(':disabled, select:disabled *, :checked')
JS
click unless selected_or_disabled
end
private private
def click_with_options(click_options) def click_with_options(click_options)

View File

@ -19,8 +19,12 @@ class Capybara::Selenium::SafariNode < Capybara::Selenium::Node
end end
def select_option def select_option
driver.execute_script("arguments[0].closest('select').scrollIntoView()", self) # To optimize to only one check and then click
super selected_or_disabled = driver.execute_script(<<~JS, self)
arguments[0].closest('select').scrollIntoView();
return arguments[0].matches(':disabled, select:disabled *, :checked');
JS
click unless selected_or_disabled
end end
def unselect_option def unselect_option

View File

@ -141,11 +141,6 @@ Capybara::SpecHelper.spec '#select' do
@session.select('Other', from: 'form_title') @session.select('Other', from: 'form_title')
expect(@session.find_field('form_title').value).not_to eq 'Other' expect(@session.find_field('form_title').value).not_to eq 'Other'
end end
it 'should warn' do
expect { @session.select('Other', from: 'form_title') }.to \
output(/^Attempt to select disabled option: Other/).to_stderr
end
end end
context 'with multiple select' do context 'with multiple select' do