lib/watir-webdriver/locators/element_locator.rb in watir-webdriver-0.7.0 vs lib/watir-webdriver/locators/element_locator.rb in watir-webdriver-0.8.0
- old
+ new
@@ -1,6 +1,5 @@
-# encoding: utf-8
module Watir
class ElementLocator
include Watir::Exception
WD_FINDERS = [
@@ -16,10 +15,21 @@
:xpath
]
WILDCARD_ATTRIBUTE = /^(aria|data)_(.+)$/
+ # Regular expressions that can be reliably converted to xpath `contains`
+ # expressions in order to optimize the locator.
+ #
+ CONVERTABLE_REGEXP = %r{
+ \A
+ ([^\[\]\\^$.|?*+()]*) # leading literal characters
+ [^|]*? # do not try to convert expressions with alternates
+ ([^\[\]\\^$.|?*+()]*) # trailing literal characters
+ \z
+ }x
+
def initialize(wd, selector, valid_attributes)
@wd = wd
@selector = selector.dup
@valid_attributes = valid_attributes
end
@@ -98,11 +108,11 @@
end
def find_all_by_multiple
selector = normalized_selector
- if selector.has_key? :index
+ if selector.key? :index
raise ArgumentError, "can't locate all elements by :index"
end
how, what = given_xpath_or_css(selector) || build_wd_selector(selector)
if how
@@ -130,11 +140,11 @@
def wd_find_by_regexp_selector(selector, method = :find)
parent = @wd
rx_selector = delete_regexps_from(selector)
- if rx_selector.has_key?(:label) && should_use_label_element?
+ if rx_selector.key?(:label) && should_use_label_element?
label = label_from_text(rx_selector.delete(:label)) || return
if (id = label.attribute(:for))
selector[:id] = id
else
parent = label
@@ -145,10 +155,19 @@
unless how
raise Error, "internal error: unable to build WebDriver selector from #{selector.inspect}"
end
+ if how == :xpath && can_convert_regexp_to_contains?
+ rx_selector.each do |key, value|
+ next if key == :tag_name || key == :text
+
+ predicates = regexp_selector_to_predicates(key, value)
+ what = "(#{what})[#{predicates.join(' and ')}]" unless predicates.empty?
+ end
+ end
+
elements = parent.find_elements(how, what)
elements.__send__(method) { |el| matches_selector?(el, rx_selector) }
end
VALID_WHATS = [String, Regexp]
@@ -167,11 +186,11 @@
end
def label_from_text(label_exp)
# TODO: this won't work correctly if @wd is a sub-element
@wd.find_elements(:tag_name, 'label').find do |el|
- matches_selector?(el, :text => label_exp)
+ matches_selector?(el, text: label_exp)
end
end
def fetch_value(element, how)
case how
@@ -231,10 +250,26 @@
end
rx_selector
end
+ def regexp_selector_to_predicates(key, re)
+ return [] if re.casefold?
+
+ match = re.source.match(CONVERTABLE_REGEXP)
+ return [] unless match
+
+ lhs = lhs_for(key)
+ match.captures.reject(&:empty?).map do |literals|
+ "contains(#{lhs}, #{XpathSupport.escape(literals)})"
+ end
+ end
+
+ def can_convert_regexp_to_contains?
+ true
+ end
+
def assert_valid_as_attribute(attribute)
unless valid_attribute? attribute or attribute.to_s =~ WILDCARD_ATTRIBUTE
raise MissingWayOfFindingObjectException, "invalid attribute: #{attribute.inspect}"
end
end
@@ -253,11 +288,11 @@
element
end
def all_elements
- @wd.find_elements(:xpath => ".//*")
+ @wd.find_elements(xpath: ".//*")
end
def tag_name_matches?(element_tag_name, tag_name)
tag_name === element_tag_name
end
@@ -285,11 +320,11 @@
# the remaining entries should be attributes
unless selectors.empty?
xpath << "[" << attribute_expression(selectors) << "]"
end
- p :xpath => xpath, :selectors => selectors if $DEBUG
+ p xpath: xpath, selectors: selectors if $DEBUG
[:xpath, xpath]
end
def build_css(selectors)
@@ -325,18 +360,18 @@
end
def use_css?(selectors)
return false unless Watir.prefer_css?
- if selectors.has_key?(:text) || selectors.has_key?(:label) || selectors.has_key?(:index)
+ if selectors.key?(:text) || selectors.key?(:label) || selectors.key?(:index)
return false
end
- if selectors[:tag_name] == 'input' && selectors.has_key?(:type)
+ if selectors[:tag_name] == 'input' && selectors.key?(:type)
return false
end
- if selectors.has_key?(:class) && selectors[:class] !~ /^[\w-]+$/ui
+ if selectors.key?(:class) && selectors[:class] !~ /^[\w-]+$/ui
return false
end
true
end