class Watir::Locators::Element::SelectorBuilder

Constants

VALID_WHATS
WILDCARD_ATTRIBUTE

Attributes

built[R]
custom_attributes[R]

Public Class Methods

new(valid_attributes, query_scope) click to toggle source
# File lib/watir/locators/element/selector_builder.rb, line 19
def initialize(valid_attributes, query_scope)
  @valid_attributes = valid_attributes
  @custom_attributes = []
  @query_scope = query_scope
end

Public Instance Methods

build(selector) click to toggle source
# File lib/watir/locators/element/selector_builder.rb, line 25
def build(selector)
  @selector = selector

  deprecated_locators
  normalize_selector
  inspected = selector.inspect
  scope = @query_scope unless @selector.key?(:scope) || @query_scope.is_a?(Watir::Browser)

  @built = wd_locators.empty? ? build_wd_selector(@selector) : @selector
  @built.delete(:index) if @built[:index]&.zero?
  @built[:scope] = scope if scope

  Watir.logger.info "Converted #{inspected} to #{@built.inspect}"
  @built
end
wd_locators() click to toggle source
# File lib/watir/locators/element/selector_builder.rb, line 41
def wd_locators
  Watir::Locators::W3C_FINDERS & @selector.keys
end

Private Instance Methods

build_wd_selector(selector) click to toggle source
# File lib/watir/locators/element/selector_builder.rb, line 148
def build_wd_selector(selector)
  implementation_class.new.build(selector)
end
check_custom_attribute(attribute) click to toggle source
# File lib/watir/locators/element/selector_builder.rb, line 137
def check_custom_attribute(attribute)
  return if valid_attribute?(attribute) || attribute.to_s =~ WILDCARD_ATTRIBUTE

  @custom_attributes << attribute.to_s
end
check_type(how, what) click to toggle source
# File lib/watir/locators/element/selector_builder.rb, line 92
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
combine_with_xpath_or_css?(selector) click to toggle source
# File lib/watir/locators/element/selector_builder.rb, line 160
def combine_with_xpath_or_css?(selector)
  keys = selector.keys
  keys.reject! { |key| %i[visible visible_text index].include? key }
  if (keys - [:tag_name]).empty?
    true
  elsif selector[:tag_name] == 'input' && keys == %i[tag_name type]
    true
  else
    false
  end
end
deprecate_class_array(class_array) click to toggle source
# File lib/watir/locators/element/selector_builder.rb, line 81
def deprecate_class_array(class_array)
  class_array.each do |class_name|
    next unless class_name.is_a?(String) && class_name.strip.include?(' ')

    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
end
deprecated_locators() click to toggle source
# File lib/watir/locators/element/selector_builder.rb, line 178
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
implementation_class() click to toggle source

Extensions implement this method when creating a different selector builder

# File lib/watir/locators/element/selector_builder.rb, line 144
def implementation_class
  Watir.const_get("#{self.class.name}::XPath")
end
merge_scope?() click to toggle source
# File lib/watir/locators/element/selector_builder.rb, line 72
def merge_scope?
  return false unless (Watir::Locators::W3C_FINDERS + [:adjacent] & @selector.keys).empty?

  return false if [Watir::Browser, Watir::IFrame].any? { |k| @query_scope.is_a?(k) }

  scope_invalid_locators = @query_scope.selector_builder.built.keys.reject { |key| key == wd_locator }
  scope_invalid_locators.empty?
end
normalize_locator(how, what) click to toggle source
# File lib/watir/locators/element/selector_builder.rb, line 104
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, :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 :caption
    # This allows any element to be located with 'caption' instead of 'text'
    # It is deprecated because caption is a valid attribute on a Table
    # It is also a valid Element, so it also needs to be removed from the Table attributes list
    Watir.logger.deprecate('Locating elements with :caption', ':text locator', ids: [:caption])
    [:text, what]
  else
    check_custom_attribute how
    [how, what]
  end
end
normalize_selector() click to toggle source
# File lib/watir/locators/element/selector_builder.rb, line 47
def normalize_selector
  raise LocatorException, "Can not locate element with #{wd_locators}" if wd_locators.size > 1

  @selector[:scope] = @query_scope.selector_builder.built if merge_scope?

  if @selector.key?(:class) || @selector.key?(:class_name)
    classes = ([@selector[:class]].flatten + [@selector.delete(:class_name)].flatten).compact

    deprecate_class_array(classes)

    @selector[:class] = classes
  end

  if @selector[:adjacent] == :ancestor && @selector.key?(:text)
    raise LocatorException, 'Can not find parent element with text locator'
  end

  @selector.keys.each do |key|
    check_type(key, @selector[key])

    how, what = normalize_locator(key, @selector.delete(key))
    @selector[how] = what
  end
end
raise_unless(what, types) click to toggle source
# File lib/watir/locators/element/selector_builder.rb, line 172
def raise_unless(what, types)
  return if types.include?(what.class)

  raise TypeError, "expected one of #{types}, got #{what.inspect}:#{what.class}"
end
should_use_label_element?() click to toggle source
# File lib/watir/locators/element/selector_builder.rb, line 100
def should_use_label_element?
  !valid_attribute?(:label)
end
valid_attribute?(attribute) click to toggle source
# File lib/watir/locators/element/selector_builder.rb, line 156
def valid_attribute?(attribute)
  @valid_attributes&.include?(attribute)
end
wd_locator() click to toggle source
# File lib/watir/locators/element/selector_builder.rb, line 152
def wd_locator
  implementation_class::LOCATOR
end