lib/watir-webdriver/locators/element_locator.rb in watir-webdriver-0.0.7 vs lib/watir-webdriver/locators/element_locator.rb in watir-webdriver-0.0.8

- old
+ new

@@ -28,14 +28,18 @@ if e = by_id return e end if @selector.size == 1 - find_first_by_one + element = find_first_by_one else - find_first_by_multiple + element = find_first_by_multiple end + + # this actually only applies when finding by xpath - browser.text_field(:xpath, "//input[@type='radio']") + # we don't need to validate the element if we built the xpath ourselves. + validate_element(element) if element rescue WebDriver::Error::NoSuchElementError => wde nil end def locate_all @@ -44,29 +48,31 @@ else find_all_by_multiple end end + private + def find_first_by_one - how, what = @selector.shift + how, what = @selector.to_a.first check_type how, what if WD_FINDERS.include?(how) wd_find_first_by(how, what) else - raise NotImplementedError, "find first by attribute/other" + find_first_by_multiple end end def find_all_by_one - how, what = @selector.shift + how, what = @selector.to_a.first check_type how, what if WD_FINDERS.include?(how) wd_find_all_by(how, what) else - raise NotImplementedError, "find all by attribute/other" + find_all_by_multiple end end def find_first_by_multiple selector = normalized_selector @@ -93,11 +99,11 @@ def find_all_by_multiple selector = normalized_selector if selector.has_key? :index - raise Error, "can't locate all elements by :index" + raise ArgumentError, "can't locate all elements by :index" end xpath = selector[:xpath] || build_xpath(selector) if xpath @wd.find_elements(:xpath, xpath) @@ -108,65 +114,75 @@ def wd_find_first_by(how, what) if what.kind_of? String @wd.find_element(how, what) else - all_elements.find { |e| fetch_value(how, e) =~ what } + all_elements.find { |element| fetch_value(element, how) =~ what } end end def wd_find_all_by(how, what) if what.kind_of? String @wd.find_elements(how, what) else - all_elements.select { |e| fetch_value(how, e) =~ what } + all_elements.select { |element| fetch_value(element, how) =~ what } end end def wd_find_by_regexp_selector(selector, method = :find) rx_selector = delete_regexps_from(selector) if rx_selector.has_key?(:label) && should_use_label_element? - label_exp = rx_selector.delete(:label) - label = @wd.find_elements(:tag_name, 'label').find { |e| matches_selector?({:text => label_exp}, e) } || return - - selector[:id] = label.attribute(:for) + selector[:id] = id_from_label(rx_selector.delete(:label)) || return end - xpath = build_xpath(selector) || raise("internal error: unable to build xpath from #{@selector.inspect}") + xpath = build_xpath(selector) || raise("internal error: unable to build xpath from #{selector.inspect}") elements = @wd.find_elements(:xpath, xpath) - elements.send(method) { |e| matches_selector?(rx_selector, e) } + elements.__send__(method) { |el| matches_selector?(el, rx_selector) } end + VALID_WHATS = [String, Regexp] + def check_type(how, what) case how when :index - raise TypeError, "expected Fixnum, got #{what.class}" unless what.kind_of?(Fixnum) + unless what.kind_of?(Fixnum) + raise TypeError, "expected Fixnum, got #{what.inspect}:#{what.class}" + end else - unless [String, Regexp].any? { |t| what.kind_of? t } - raise TypeError, "expected String or Regexp, got #{what.inspect}:#{what.class}" + unless VALID_WHATS.any? { |t| what.kind_of? t } + raise TypeError, "expected one of #{VALID_WHATS.inspect}, got #{what.inspect}:#{what.class}" end end end - def fetch_value(how, element) + def id_from_label(label_exp) + # TODO: this won't work correctly if @wd is a sub-element + label = @wd.find_elements(:tag_name, 'label').find do |el| + matches_selector?(el, :text => label_exp) + end + + label.attribute(:for) if label + end + + def fetch_value(element, how) case how when :text element.text when :tag_name element.tag_name + when :href + (href = element.attribute(:href)) && href.strip else element.attribute(how) end end - def matches_selector?(selector, element) - # p :start => selector + def matches_selector?(element, selector) selector.all? do |how, what| - # p :comparing => [how, what], :to => fetch_value(how, element) - what === fetch_value(how, element) + what === fetch_value(element, how) end end def normalized_selector selector = {} @@ -181,17 +197,16 @@ selector end def normalize_selector(how, what) case how - when :url - [:href, what] when :caption [:text, what] when :class_name [:class, what] - when :tag_name, :text, :xpath, :index, :class # include class since the attribute method is 'class_name' + when :tag_name, :text, :xpath, :index, :class + # include class since the valid attribute is 'class_name' [how, what] else assert_valid_as_attribute how [how, what] end @@ -208,11 +223,11 @@ rx_selector end def assert_valid_as_attribute(attribute) - unless valid_attribute? attribute + unless valid_attribute? attribute or attribute.to_s =~ /^data_.+$/ raise MissingWayOfFindingObjectException, "invalid attribute: #{attribute.inspect}" end end def by_id @@ -270,11 +285,11 @@ end def attribute_expression(selectors) selectors.map do |key, val| if val.kind_of?(Array) - "( " + val.map { |v| equal_pair(key, v) }.join(" or ") + " )" + "(" + val.map { |v| equal_pair(key, v) }.join(" or ") + ")" else equal_pair(key, val) end end.join(" and ") end @@ -296,9 +311,20 @@ # TODO: change this behaviour? 'normalize-space(@href)' else "@#{key.to_s.gsub("_", "-")}" end + end + + def validate_element(element) + tn = @selector[:tag_name] + return if tn && !tag_name_matches?(element, tn) + + if element.tag_name == 'input' + return if @selector[:type] && @selector[:type] != element.attribute(:type) + end + + element end end # ElementLocator end # Watir