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
|