File: html_selector.rb

package info (click to toggle)
ruby-rails-dom-testing 2.2.0-1
  • links: PTS, VCS
  • area: main
  • in suites: sid, trixie
  • size: 156 kB
  • sloc: ruby: 974; makefile: 4
file content (132 lines) | stat: -rw-r--r-- 4,880 bytes parent folder | download
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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# frozen_string_literal: true

require "minitest"

require_relative "substitution_context"

module Rails
  module Dom
    module Testing
      module Assertions
        module SelectorAssertions
          class HTMLSelector # :nodoc:
            attr_reader :css_selector, :tests, :message

            include Minitest::Assertions

            def initialize(values, previous_selection = nil, &root_fallback)
              @values = values
              @root = extract_root(previous_selection, root_fallback)
              extract_selectors
              @tests = extract_equality_tests
              @message = @values.shift

              if @message.is_a?(Hash)
                raise ArgumentError, "Last argument was a Hash, which would be used for the assertion message. You probably want this to be a String, or you have the wrong type of arguments."
              end

              if @values.shift
                raise ArgumentError, "Not expecting that last argument, you either have too many arguments, or they're the wrong type"
              end
            end

            def selecting_no_body? # :nodoc:
              # Nokogiri gives the document a body element. Which means we can't
              # run an assertion expecting there to not be a body.
              @selector == "body" && @tests[:count] == 0
            end

            def select
              filter @root.css(@selector, context)
            end

            private
              NO_STRIP = %w{pre script style textarea}

              mattr_reader(:context) { SubstitutionContext.new }

              def filter(matches)
                match_with = tests[:text] || tests[:html]
                return matches if matches.empty? || !match_with

                content_mismatch = nil
                text_matches = tests.has_key?(:text)
                regex_matching = match_with.is_a?(Regexp)

                remaining = matches.reject do |match|
                  # Preserve markup with to_s for html elements
                  content = text_matches ? match.text : match.children.to_s

                  content.strip! unless NO_STRIP.include?(match.name)
                  content.delete_prefix!("\n") if text_matches && match.name == "textarea"

                  next if regex_matching ? (content =~ match_with) : (content == match_with)
                  content_mismatch ||= diff(match_with, content)
                  true
                end

                @message ||= content_mismatch if remaining.empty?
                Nokogiri::XML::NodeSet.new(matches.document, remaining)
              end

              def extract_root(previous_selection, root_fallback)
                possible_root = @values.first

                if possible_root == nil
                  raise ArgumentError, "First argument is either selector or element " \
                    "to select, but nil found. Perhaps you called assert_dom with " \
                    "an element that does not exist?"
                elsif possible_root.respond_to?(:css)
                  @values.shift # remove the root, so selector is the first argument
                  possible_root
                elsif previous_selection
                  previous_selection
                else
                  root_fallback.call
                end
              end

              def extract_selectors
                selector = @values.shift

                unless selector.is_a? String
                  raise ArgumentError, "Expecting a selector as the first argument"
                end

                @css_selector = context.substitute!(selector, @values.dup, true)
                @selector     = context.substitute!(selector, @values)
              end

              def extract_equality_tests
                comparisons = {}
                case comparator = @values.shift
                when Hash
                  comparisons = comparator
                when String, Regexp
                  comparisons[:text] = comparator
                when Integer
                  comparisons[:count] = comparator
                when Range
                  comparisons[:minimum] = comparator.begin
                  comparisons[:maximum] = comparator.end
                when FalseClass
                  comparisons[:count] = 0
                when NilClass, TrueClass
                  comparisons[:minimum] = 1
                else raise ArgumentError, "I don't understand what you're trying to match"
                end

                # By default we're looking for at least one match.
                if comparisons[:count]
                  comparisons[:minimum] = comparisons[:maximum] = comparisons[:count]
                else
                  comparisons[:minimum] ||= 1
                end
                comparisons
              end
          end
        end
      end
    end
  end
end