/
css_builder.rb
84 lines (74 loc) · 2.59 KB
/
css_builder.rb
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
72
73
74
75
76
77
78
79
80
81
82
83
84
# frozen_string_literal: true
require 'xpath'
module Capybara
class Selector
# @api private
class CSSBuilder
def initialize(expression)
@expression = expression || ''
end
attr_reader :expression
def add_attribute_conditions(**attributes)
@expression = attributes.inject(expression) do |css, (name, value)|
conditions = if name == :class
class_conditions(value)
elsif value.is_a? Regexp
regexp_conditions(name, value)
else
[attribute_conditions(name => value)]
end
::Capybara::Selector::CSS.split(css).map do |sel|
next sel if conditions.empty?
conditions.map { |cond| sel + cond }.join(', ')
end.join(', ')
end
end
private
def regexp_conditions(name, value)
Selector::RegexpDisassembler.new(value).alternated_substrings.map do |strs|
strs.map do |str|
"[#{name}*='#{str}'#{' i' if value.casefold?}]"
end.join
end
end
def attribute_conditions(attributes)
attributes.map do |attribute, value|
case value
when XPath::Expression
raise ArgumentError, "XPath expressions are not supported for the :#{attribute} filter with CSS based selectors"
when Regexp
Selector::RegexpDisassembler.new(value).substrings.map do |str|
"[#{attribute}*='#{str}'#{' i' if value.casefold?}]"
end.join
when true
"[#{attribute}]"
when false
':not([attribute])'
else
if attribute == :id
"##{::Capybara::Selector::CSS.escape(value)}"
else
"[#{attribute}='#{value}']"
end
end
end.join
end
def class_conditions(classes)
case classes
when XPath::Expression
raise ArgumentError, 'XPath expressions are not supported for the :class filter with CSS based selectors'
when Regexp
Selector::RegexpDisassembler.new(classes).alternated_substrings.map do |strs|
strs.map do |str|
"[class*='#{str}'#{' i' if classes.casefold?}]"
end.join
end
else
cls = Array(classes).reject { |c| c.is_a? Regexp }.group_by { |cl| cl.match?(/^!(?!!!)/) }
[(cls[false].to_a.map { |cl| ".#{Capybara::Selector::CSS.escape(cl.sub(/^!!/, ''))}" } +
cls[true].to_a.map { |cl| ":not(.#{Capybara::Selector::CSS.escape(cl.slice(1..))})" }).join]
end
end
end
end
end