1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
|
# frozen_string_literal: true
require 'xpath'
module Capybara
class Selector
# @api private
class XPathBuilder
def initialize(expression)
@expression = expression || ''
end
attr_reader :expression
def add_attribute_conditions(**conditions)
@expression = conditions.inject(expression) do |xp, (name, value)|
conditions = name == :class ? class_conditions(value) : attribute_conditions(name => value)
return xp if conditions.nil?
if xp.is_a? XPath::Expression
xp[conditions]
else
"(#{xp})[#{conditions}]"
end
end
end
private
def attribute_conditions(attributes)
attributes.map do |attribute, value|
case value
when XPath::Expression
XPath.attr(attribute)[value]
when Regexp
XPath.attr(attribute)[regexp_to_xpath_conditions(value)]
when true
XPath.attr(attribute)
when false, nil
!XPath.attr(attribute)
else
XPath.attr(attribute) == value.to_s
end
end.reduce(:&)
end
def class_conditions(classes)
case classes
when XPath::Expression, Regexp
attribute_conditions(class: classes)
else
Array(classes).reject { |c| c.is_a? Regexp }.map do |klass|
if klass.match?(/^!(?!!!)/)
!XPath.attr(:class).contains_word(klass.slice(1..))
else
XPath.attr(:class).contains_word(klass.sub(/^!!/, ''))
end
end.reduce(:&)
end
end
def regexp_to_xpath_conditions(regexp)
condition = XPath.current
condition = condition.uppercase if regexp.casefold?
Selector::RegexpDisassembler.new(regexp).alternated_substrings.map do |strs|
strs.map { |str| condition.contains(str) }.reduce(:&)
end.reduce(:|)
end
end
end
end
|