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)
|
||||
find(:xpath, '//body').scroll_to(*args)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -405,7 +405,7 @@ module Capybara
|
|||
when Symbol
|
||||
synchronize { base.scroll_to(nil, pos_or_el_or_x) } unless pos_or_el_or_x == :current
|
||||
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
|
||||
synchronize { base.scroll_to(nil, nil, [pos_or_el_or_x, y]) }
|
||||
end
|
||||
|
|
|
@ -180,19 +180,20 @@ module Capybara
|
|||
end
|
||||
|
||||
def find_nodes_by_selector_format(node, exact)
|
||||
options = {}
|
||||
options[:uses_visibility] = true unless visible == :all
|
||||
options[:texts] = text_fragments unless selector.format == :xpath
|
||||
hints = {}
|
||||
hints[:uses_visibility] = true unless visible == :all
|
||||
hints[:texts] = text_fragments unless selector.format == :xpath
|
||||
hints[:styles] = options[:style] if use_default_style_filter?
|
||||
|
||||
if selector.format == :css
|
||||
if node.method(:find_css).arity != 1
|
||||
node.find_css(css, **options)
|
||||
node.find_css(css, **hints)
|
||||
else
|
||||
node.find_css(css)
|
||||
end
|
||||
elsif selector.format == :xpath
|
||||
if node.method(:find_xpath).arity != 1
|
||||
node.find_xpath(xpath(exact), **options)
|
||||
node.find_xpath(xpath(exact), **hints)
|
||||
else
|
||||
node.find_xpath(xpath(exact))
|
||||
end
|
||||
|
@ -388,7 +389,7 @@ module Capybara
|
|||
end
|
||||
|
||||
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|
|
||||
if value.is_a? Regexp
|
||||
@actual_styles[style.to_s] =~ value
|
||||
|
|
|
@ -3,32 +3,63 @@
|
|||
module Capybara
|
||||
module Selenium
|
||||
module Find
|
||||
def find_xpath(selector, uses_visibility: false, **_options)
|
||||
find_by(:xpath, selector, uses_visibility: uses_visibility, texts: [])
|
||||
def find_xpath(selector, uses_visibility: false, styles: nil, **_options)
|
||||
find_by(:xpath, selector, uses_visibility: uses_visibility, texts: [], styles: styles)
|
||||
end
|
||||
|
||||
def find_css(selector, uses_visibility: false, texts: [], **_options)
|
||||
find_by(:css, selector, uses_visibility: uses_visibility, texts: texts)
|
||||
def find_css(selector, uses_visibility: false, texts: [], styles: nil, **_options)
|
||||
find_by(:css, selector, uses_visibility: uses_visibility, texts: texts, styles: styles)
|
||||
end
|
||||
|
||||
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 = filter_by_text(els, texts) if (els.size > 1) && !texts.empty?
|
||||
visibilities = uses_visibility && els.size > 1 ? element_visibilities(els) : []
|
||||
els.map.with_index { |el, idx| build_node(el, visible: visibilities[idx]) }
|
||||
end
|
||||
hints = []
|
||||
if (els.size > 2) && !ENV['DISABLE_CAPYBARA_SELENIUM_OPTIMIZATIONS']
|
||||
els = filter_by_text(els, texts) unless texts.empty?
|
||||
|
||||
def element_visibilities(elements)
|
||||
es_context = respond_to?(:execute_script) ? self : driver
|
||||
es_context.execute_script <<~JS, elements
|
||||
return arguments[0].map(#{is_displayed_atom})
|
||||
JS
|
||||
hints_js = +''
|
||||
functions = []
|
||||
if uses_visibility
|
||||
hints_js << <<~VISIBILITY_JS
|
||||
var vis_func = #{is_displayed_atom};
|
||||
VISIBILITY_JS
|
||||
functions << 'vis_func'
|
||||
end
|
||||
|
||||
if styles.is_a? Hash
|
||||
hints_js << <<~STYLE_JS
|
||||
var style_func = function(el){
|
||||
var el_styles = window.getComputedStyle(el);
|
||||
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
|
||||
|
||||
def filter_by_text(elements, texts)
|
||||
es_context = respond_to?(:execute_script) ? self : driver
|
||||
es_context.execute_script <<~JS, elements, texts
|
||||
var texts = arguments[1]
|
||||
return arguments[0].filter(function(el){
|
||||
|
@ -38,8 +69,12 @@ module Capybara
|
|||
JS
|
||||
end
|
||||
|
||||
def es_context
|
||||
respond_to?(:execute_script) ? self : driver
|
||||
end
|
||||
|
||||
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
|
||||
|
|
Loading…
Reference in a new issue