lib/watir/locators/element/selector_builder.rb in watir-6.15.1 vs lib/watir/locators/element/selector_builder.rb in watir-6.16.0
- old
+ new
@@ -1,48 +1,68 @@
module Watir
module Locators
class Element
class SelectorBuilder
include Exception
- attr_reader :custom_attributes
+ attr_reader :custom_attributes, :built
WILDCARD_ATTRIBUTE = /^(aria|data)_(.+)$/.freeze
INTEGER_CLASS = Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.4') ? Fixnum : Integer
VALID_WHATS = Hash.new([String, Regexp, TrueClass, FalseClass]).merge(adjacent: [::Symbol],
xpath: [String],
css: [String],
index: [INTEGER_CLASS],
visible: [TrueClass, FalseClass],
tag_name: [String, Regexp, ::Symbol],
visible_text: [String, Regexp],
+ scope: [Hash],
text: [String, Regexp]).freeze
- def initialize(valid_attributes)
+ def initialize(valid_attributes, query_scope)
@valid_attributes = valid_attributes
@custom_attributes = []
+ @query_scope = query_scope
end
def build(selector)
- inspected = selector.inspect
@selector = selector
+
+ deprecated_locators
normalize_selector
+ inspected = selector.inspect
+ scope = @query_scope unless @selector.key?(:scope) || @query_scope.is_a?(Watir::Browser)
- xpath_css = @selector.select { |key, _value| %i[xpath css].include? key }
+ @built = wd_locator(@selector.keys).nil? ? build_wd_selector(@selector) : @selector
+ @built.delete(:index) if @built[:index]&.zero?
+ @built[:scope] = scope if scope
- raise LocatorException, ":xpath and :css cannot be combined (#{xpath_css})" if xpath_css.size > 1
+ Watir.logger.info "Converted #{inspected} to #{@built.inspect}"
+ @built
+ end
- built = xpath_css.empty? ? build_wd_selector(@selector) : @selector
-
- built.delete(:index) if built[:index]&.zero?
-
- Watir.logger.debug "Converted #{inspected} to #{built}, with #{@selector.inspect} to match"
- built
+ def wd_locator(keys)
+ (Watir::Locators::W3C_FINDERS & keys).first
end
+ private
+
def normalize_selector
- if @selector.key?(:class) && @selector.key?(:class_name)
- raise LocatorException, 'Can not use both :class and :class_name locators'
+ wd_locators = @selector.keys & Watir::Locators::W3C_FINDERS
+ raise LocatorException, "Can not locate element with #{wd_locators}" if wd_locators.size > 1
+
+ @selector[:scope] = @query_scope.selector_builder.built if use_scope?
+
+ if @selector.key?(:class) || @selector.key?(:class_name)
+ classes = ([@selector[:class]].flatten + [@selector.delete(:class_name)].flatten).compact
+
+ classes.each do |class_name|
+ next unless class_name.is_a?(String) && class_name.strip.include?(' ')
+
+ deprecate_class_array(class_name)
+ end
+
+ @selector[:class] = classes
end
if @selector[:adjacent] == :ancestor && @selector.key?(:text)
raise LocatorException, 'Can not find parent element with text locator'
end
@@ -53,45 +73,59 @@
how, what = normalize_locator(key, @selector.delete(key))
@selector[how] = what
end
end
- def check_type(how, what)
- if %i[class class_name].include?(how)
- classes = [what].flatten
- raise LocatorException, "Can not locate elements with an empty Array for :#{how}" if classes.empty?
+ def use_scope?
+ return false if @query_scope.is_a?(Browser)
- classes.each { |value| raise_unless(value, VALID_WHATS[how]) }
+ !@selector.key?(:adjacent) &&
+ (Watir::Locators::W3C_FINDERS & @selector.keys).empty? &&
+ !(@query_scope.is_a?(Watir::IFrame) || @query_scope.is_a?(Watir::Radio)) &&
+ @query_scope.selector_builder.built&.size == 1
+ end
+
+ def deprecate_class_array(class_name)
+ dep = "Using the :class locator to locate multiple classes with a String value (i.e. \"#{class_name}\")"
+ Watir.logger.deprecate dep,
+ "Array (e.g. #{class_name.split})",
+ ids: [:class_array]
+ end
+
+ def check_type(how, what)
+ if %i[class class_name].include? how
+ [what].flatten.each { |value| raise_unless(value, VALID_WHATS[how]) }
else
raise_unless(what, VALID_WHATS[how])
end
end
def should_use_label_element?
!valid_attribute?(:label)
end
- private
-
def normalize_locator(how, what)
case how
when 'text'
Watir.logger.deprecate "String 'text' as a locator", 'Symbol :text', ids: [:text_string]
[:text, what]
when :tag_name
what = what.to_s if what.is_a?(::Symbol)
[how, what]
- when :text, :xpath, :index, :class, :css, :visible, :visible_text, :adjacent
+ when :text, :xpath, :index, :css, :visible, :visible_text, :adjacent
[how, what]
+ when :class
+ what = false if what.tap { |arr| arr.delete('') }.empty?
+ [how, what]
+ when :link
+ [:link_text, what]
when :label, :visible_label
if should_use_label_element?
["#{how}_element".to_sym, what]
else
[how, what]
end
- when :class_name
- [:class, what]
when :caption
# This allows any element to be located with 'caption' instead of 'text'
Watir.logger.deprecate('Locating elements with :caption', ':text locator', ids: [:caption])
[:text, what]
else
@@ -129,9 +163,21 @@
def raise_unless(what, types)
return if types.include?(what.class)
raise TypeError, "expected one of #{types}, got #{what.inspect}:#{what.class}"
+ end
+
+ def deprecated_locators
+ %i[partial_link_text link_text link].each do |locator|
+ next unless @selector.key?(locator)
+
+ Watir.logger.deprecate(":#{locator} locator", ':visible_text', ids: [:link_text])
+ tag = @selector[:tag_name]
+ next if tag.nil? || tag == 'a'
+
+ raise LocatorException, "Can not use #{locator} locator to find a #{tag} element"
+ end
end
end
end
end
end