class Capybara::Selector

All Selectors below support the listed selector specific filters in addition to the following system-wide filters

* :id (String, Regexp, XPath::Expression) - Matches the id attribute
* :class (String, Array<String | Regexp>, Regexp, XPath::Expression) - Matches the class(es) provided
* :style (String, Regexp, Hash<String, String>) - Match on elements style
* :above (Element) - Match elements above the passed element on the page
* :below (Element) - Match elements below the passed element on the page
* :left_of (Element) - Match elements left of the passed element on the page
* :right_of (Element) - Match elements right of the passed element on the page
* :near (Element) - Match elements near (within 50px) the passed element on the page
* :focused (Boolean) - Match elements with focus (requires driver support)

### Built-in Selectors

Attributes

errors[R]

Public Class Methods

[](name) click to toggle source
# File lib/capybara/selector/selector.rb, line 10
def [](name)
  all.fetch(name.to_sym) { |sel_type| raise ArgumentError, "Unknown selector type (:#{sel_type})" }
end
add(name, **options, &block) click to toggle source
# File lib/capybara/selector/selector.rb, line 14
def add(name, **options, &block)
  all[name.to_sym] = Definition.new(name.to_sym, **options, &block)
end
all() click to toggle source
# File lib/capybara/selector/selector.rb, line 6
def all
  @definitions ||= {} # rubocop:disable Naming/MemoizedInstanceVariableName
end
for(locator) click to toggle source
# File lib/capybara/selector/selector.rb, line 26
def for(locator)
  all.values.find { |sel| sel.match?(locator) }
end
new(definition, config:, format:) click to toggle source
Calls superclass method
# File lib/capybara/selector/selector.rb, line 33
def initialize(definition, config:, format:)
  definition = self.class[definition] unless definition.is_a? Definition
  super(definition)
  @definition = definition
  @config = config
  @format = format
  @errors = []
end
remove(name) click to toggle source
# File lib/capybara/selector/selector.rb, line 22
def remove(name)
  all.delete(name.to_sym)
end
update(name, &block) click to toggle source
# File lib/capybara/selector/selector.rb, line 18
def update(name, &block)
  self[name].instance_eval(&block)
end

Public Instance Methods

add_error(error_msg) click to toggle source
# File lib/capybara/selector/selector.rb, line 73
def add_error(error_msg)
  errors << error_msg
end
builder(expr = nil) click to toggle source

@api private

# File lib/capybara/selector/selector.rb, line 91
def builder(expr = nil)
  case format
  when :css
    Capybara::Selector::CSSBuilder
  when :xpath
    Capybara::Selector::XPathBuilder
  else
    raise NotImplementedError, "No builder exists for selector of type #{default_format}"
  end.new(expr)
end
call(locator, **options) click to toggle source
# File lib/capybara/selector/selector.rb, line 59
def call(locator, **options)
  if format
    raise ArgumentError, "Selector #{@name} does not support #{format}" unless expressions.key?(format)

    instance_exec(locator, **options, &expressions[format])
  else
    warn 'Selector has no format'
  end
ensure
  unless locator_valid?(locator)
    warn "Locator #{locator.class}:#{locator.inspect} for selector #{name.inspect} must #{locator_description}. This will raise an error in a future version of Capybara."
  end
end
current_format()
Alias for: format
enable_aria_label() click to toggle source
# File lib/capybara/selector/selector.rb, line 47
def enable_aria_label
  @config[:enable_aria_label]
end
enable_aria_role() click to toggle source
# File lib/capybara/selector/selector.rb, line 51
def enable_aria_role
  @config[:enable_aria_role]
end
expression_for(name, locator, config: @config, format: current_format, **options) click to toggle source
# File lib/capybara/selector/selector.rb, line 77
def expression_for(name, locator, config: @config, format: current_format, **options)
  Selector.new(name, config: config, format: format).call(locator, **options)
end
format() click to toggle source
# File lib/capybara/selector/selector.rb, line 42
def format
  @format || @definition.default_format
end
Also aliased as: current_format
test_id() click to toggle source
# File lib/capybara/selector/selector.rb, line 55
def test_id
  @config[:test_id]
end
with_filter_errors(errors) { || ... } click to toggle source

@api private

# File lib/capybara/selector/selector.rb, line 82
def with_filter_errors(errors)
  old_errors = @errors
  @errors = errors
  yield
ensure
  @errors = old_errors
end

Private Instance Methods

find_by_attr(attribute, value) click to toggle source
# File lib/capybara/selector/selector.rb, line 142
def find_by_attr(attribute, value)
  finder_name = "find_by_#{attribute}_attr"
  if respond_to?(finder_name, true)
    send(finder_name, value)
  else
    value ? XPath.attr(attribute) == value : nil
  end
end
find_by_class_attr(classes) click to toggle source
# File lib/capybara/selector/selector.rb, line 151
def find_by_class_attr(classes)
  Array(classes).map { |klass| XPath.attr(:class).contains_word(klass) }.reduce(:&)
end
locate_field(xpath, locator, **_options) click to toggle source
# File lib/capybara/selector/selector.rb, line 122
def locate_field(xpath, locator, **_options)
  return xpath if locator.nil?

  locate_xpath = xpath # Need to save original xpath for the label wrap
  locator = locator.to_s
  attr_matchers = [XPath.attr(:id) == locator,
                   XPath.attr(:name) == locator,
                   XPath.attr(:placeholder) == locator,
                   XPath.attr(:id) == XPath.anywhere(:label)[XPath.string.n.is(locator)].attr(:for)].reduce(:|)
  attr_matchers |= XPath.attr(:'aria-label').is(locator) if enable_aria_label
  attr_matchers |= XPath.attr(test_id) == locator if test_id

  locate_xpath = locate_xpath[attr_matchers]
  locate_xpath + locate_label(locator).descendant(xpath)
end
locate_label(locator) click to toggle source
# File lib/capybara/selector/selector.rb, line 138
def locate_label(locator)
  XPath.descendant(:label)[XPath.string.n.is(locator)]
end
locator_description() click to toggle source
# File lib/capybara/selector/selector.rb, line 104
def locator_description
  locator_types.group_by { |lt| lt.is_a? Symbol }.map do |symbol, types_or_methods|
    if symbol
      "respond to #{types_or_methods.join(' or ')}"
    else
      "be an instance of #{types_or_methods.join(' or ')}"
    end
  end.join(' or ')
end
locator_valid?(locator) click to toggle source
# File lib/capybara/selector/selector.rb, line 114
def locator_valid?(locator)
  return true unless locator && locator_types

  locator_types&.any? do |type_or_method|
    type_or_method.is_a?(Symbol) ? locator.respond_to?(type_or_method) : type_or_method === locator # rubocop:disable Style/CaseEquality
  end
end