File: css_builder.rb

package info (click to toggle)
ruby-capybara 3.40.0%2Bds-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,368 kB
  • sloc: ruby: 23,988; javascript: 752; makefile: 11
file content (84 lines) | stat: -rw-r--r-- 2,648 bytes parent folder | download | duplicates (2)
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