mirror of
https://github.com/teamcapybara/capybara.git
synced 2022-11-09 12:08:07 -05:00
Use find hints to optimize Selenium style filter
This commit is contained in:
parent
c5f518880c
commit
9ce269c3ba
4 changed files with 59 additions and 24 deletions
|
@ -43,7 +43,6 @@ module Capybara
|
||||||
def scroll_to(*args)
|
def scroll_to(*args)
|
||||||
find(:xpath, '//body').scroll_to(*args)
|
find(:xpath, '//body').scroll_to(*args)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -405,7 +405,7 @@ module Capybara
|
||||||
when Symbol
|
when Symbol
|
||||||
synchronize { base.scroll_to(nil, pos_or_el_or_x) } unless pos_or_el_or_x == :current
|
synchronize { base.scroll_to(nil, pos_or_el_or_x) } unless pos_or_el_or_x == :current
|
||||||
when Capybara::Node::Element
|
when Capybara::Node::Element
|
||||||
synchronize { base.scroll_to(pos_or_el_or_x.base, align)}
|
synchronize { base.scroll_to(pos_or_el_or_x.base, align) }
|
||||||
else
|
else
|
||||||
synchronize { base.scroll_to(nil, nil, [pos_or_el_or_x, y]) }
|
synchronize { base.scroll_to(nil, nil, [pos_or_el_or_x, y]) }
|
||||||
end
|
end
|
||||||
|
|
|
@ -180,19 +180,20 @@ module Capybara
|
||||||
end
|
end
|
||||||
|
|
||||||
def find_nodes_by_selector_format(node, exact)
|
def find_nodes_by_selector_format(node, exact)
|
||||||
options = {}
|
hints = {}
|
||||||
options[:uses_visibility] = true unless visible == :all
|
hints[:uses_visibility] = true unless visible == :all
|
||||||
options[:texts] = text_fragments unless selector.format == :xpath
|
hints[:texts] = text_fragments unless selector.format == :xpath
|
||||||
|
hints[:styles] = options[:style] if use_default_style_filter?
|
||||||
|
|
||||||
if selector.format == :css
|
if selector.format == :css
|
||||||
if node.method(:find_css).arity != 1
|
if node.method(:find_css).arity != 1
|
||||||
node.find_css(css, **options)
|
node.find_css(css, **hints)
|
||||||
else
|
else
|
||||||
node.find_css(css)
|
node.find_css(css)
|
||||||
end
|
end
|
||||||
elsif selector.format == :xpath
|
elsif selector.format == :xpath
|
||||||
if node.method(:find_xpath).arity != 1
|
if node.method(:find_xpath).arity != 1
|
||||||
node.find_xpath(xpath(exact), **options)
|
node.find_xpath(xpath(exact), **hints)
|
||||||
else
|
else
|
||||||
node.find_xpath(xpath(exact))
|
node.find_xpath(xpath(exact))
|
||||||
end
|
end
|
||||||
|
@ -388,7 +389,7 @@ module Capybara
|
||||||
end
|
end
|
||||||
|
|
||||||
def matches_style?(node, styles)
|
def matches_style?(node, styles)
|
||||||
@actual_styles = node.style(*styles.keys)
|
@actual_styles = node.initial_cache[:style] || node.style(*styles.keys)
|
||||||
styles.all? do |style, value|
|
styles.all? do |style, value|
|
||||||
if value.is_a? Regexp
|
if value.is_a? Regexp
|
||||||
@actual_styles[style.to_s] =~ value
|
@actual_styles[style.to_s] =~ value
|
||||||
|
|
|
@ -3,32 +3,63 @@
|
||||||
module Capybara
|
module Capybara
|
||||||
module Selenium
|
module Selenium
|
||||||
module Find
|
module Find
|
||||||
def find_xpath(selector, uses_visibility: false, **_options)
|
def find_xpath(selector, uses_visibility: false, styles: nil, **_options)
|
||||||
find_by(:xpath, selector, uses_visibility: uses_visibility, texts: [])
|
find_by(:xpath, selector, uses_visibility: uses_visibility, texts: [], styles: styles)
|
||||||
end
|
end
|
||||||
|
|
||||||
def find_css(selector, uses_visibility: false, texts: [], **_options)
|
def find_css(selector, uses_visibility: false, texts: [], styles: nil, **_options)
|
||||||
find_by(:css, selector, uses_visibility: uses_visibility, texts: texts)
|
find_by(:css, selector, uses_visibility: uses_visibility, texts: texts, styles: styles)
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def find_by(format, selector, uses_visibility:, texts:)
|
def find_by(format, selector, uses_visibility:, texts:, styles:)
|
||||||
els = find_context.find_elements(format, selector)
|
els = find_context.find_elements(format, selector)
|
||||||
els = filter_by_text(els, texts) if (els.size > 1) && !texts.empty?
|
hints = []
|
||||||
visibilities = uses_visibility && els.size > 1 ? element_visibilities(els) : []
|
if (els.size > 2) && !ENV['DISABLE_CAPYBARA_SELENIUM_OPTIMIZATIONS']
|
||||||
els.map.with_index { |el, idx| build_node(el, visible: visibilities[idx]) }
|
els = filter_by_text(els, texts) unless texts.empty?
|
||||||
|
|
||||||
|
hints_js = +''
|
||||||
|
functions = []
|
||||||
|
if uses_visibility
|
||||||
|
hints_js << <<~VISIBILITY_JS
|
||||||
|
var vis_func = #{is_displayed_atom};
|
||||||
|
VISIBILITY_JS
|
||||||
|
functions << 'vis_func'
|
||||||
end
|
end
|
||||||
|
|
||||||
def element_visibilities(elements)
|
if styles.is_a? Hash
|
||||||
es_context = respond_to?(:execute_script) ? self : driver
|
hints_js << <<~STYLE_JS
|
||||||
es_context.execute_script <<~JS, elements
|
var style_func = function(el){
|
||||||
return arguments[0].map(#{is_displayed_atom})
|
var el_styles = window.getComputedStyle(el);
|
||||||
JS
|
return #{styles.keys.map(&:to_s)}.reduce(function(res, style){
|
||||||
|
res[style] = el_styles[style];
|
||||||
|
return res;
|
||||||
|
}, {});
|
||||||
|
};
|
||||||
|
STYLE_JS
|
||||||
|
functions << 'style_func'
|
||||||
|
end
|
||||||
|
|
||||||
|
unless functions.empty?
|
||||||
|
hints_js << <<~EACH_JS
|
||||||
|
return arguments[0].map(function(el){
|
||||||
|
return [#{functions.join(',')}].map(function(fn){ return fn.call(null, el) }); });
|
||||||
|
EACH_JS
|
||||||
|
|
||||||
|
hints = es_context.execute_script hints_js, els
|
||||||
|
hints.map! do |results|
|
||||||
|
result = {}
|
||||||
|
result[:style] = results.pop if styles.is_a? Hash
|
||||||
|
result[:visible] = results.pop if uses_visibility
|
||||||
|
result
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
els.map.with_index { |el, idx| build_node(el, hints[idx] || {}) }
|
||||||
end
|
end
|
||||||
|
|
||||||
def filter_by_text(elements, texts)
|
def filter_by_text(elements, texts)
|
||||||
es_context = respond_to?(:execute_script) ? self : driver
|
|
||||||
es_context.execute_script <<~JS, elements, texts
|
es_context.execute_script <<~JS, elements, texts
|
||||||
var texts = arguments[1]
|
var texts = arguments[1]
|
||||||
return arguments[0].filter(function(el){
|
return arguments[0].filter(function(el){
|
||||||
|
@ -38,8 +69,12 @@ module Capybara
|
||||||
JS
|
JS
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def es_context
|
||||||
|
respond_to?(:execute_script) ? self : driver
|
||||||
|
end
|
||||||
|
|
||||||
def is_displayed_atom # rubocop:disable Naming/PredicateName
|
def is_displayed_atom # rubocop:disable Naming/PredicateName
|
||||||
@is_displayed_atom ||= browser.send(:bridge).send(:read_atom, 'isDisplayed')
|
@@is_displayed_atom ||= browser.send(:bridge).send(:read_atom, 'isDisplayed')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue