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