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 133 134 135 136 137 138 139 140
|
# frozen_string_literal: true
module Rails
module Dom
module Testing
module Assertions
module DomAssertions
# \Test two HTML strings for equivalency (e.g., equal even when attributes are in another order)
#
# # assert that the referenced method generates the appropriate HTML string
# assert_dom_equal(
# '<a href="http://www.example.com">Apples</a>',
# link_to("Apples", "http://www.example.com"),
# )
#
# By default, the matcher will not pay attention to whitespace in text nodes (e.g., spaces
# and newlines). If you want stricter matching with exact matching for whitespace, pass
# <tt>strict: true</tt>:
#
# # these assertions will both pass
# assert_dom_equal "<div>\nfoo\n\</div>", "<div>foo</div>", strict: false
# assert_dom_not_equal "<div>\nfoo\n\</div>", "<div>foo</div>", strict: true
#
# The DOMs are created using an HTML parser specified by
# Rails::Dom::Testing.default_html_version (either :html4 or :html5).
#
# When testing in a Rails application, the parser default can also be set by setting
# +Rails.application.config.dom_testing_default_html_version+.
#
# If you want to specify the HTML parser just for a particular assertion, pass
# <tt>html_version: :html4</tt> or <tt>html_version: :html5</tt> keyword arguments:
#
# assert_dom_equal expected, actual, html_version: :html5
#
def assert_dom_equal(expected, actual, message = nil, strict: false, html_version: nil)
expected_dom, actual_dom = fragment(expected, html_version: html_version), fragment(actual, html_version: html_version)
message ||= "Expected: #{expected}\nActual: #{actual}"
assert compare_doms(expected_dom, actual_dom, strict), message
end
# The negated form of +assert_dom_equal+.
#
# # assert that the referenced method does not generate the specified HTML string
# assert_dom_not_equal(
# '<a href="http://www.example.com">Apples</a>',
# link_to("Oranges", "http://www.example.com"),
# )
#
# By default, the matcher will not pay attention to whitespace in text nodes (e.g., spaces
# and newlines). If you want stricter matching with exact matching for whitespace, pass
# <tt>strict: true</tt>:
#
# # these assertions will both pass
# assert_dom_equal "<div>\nfoo\n\</div>", "<div>foo</div>", strict: false
# assert_dom_not_equal "<div>\nfoo\n\</div>", "<div>foo</div>", strict: true
#
# The DOMs are created using an HTML parser specified by
# Rails::Dom::Testing.default_html_version (either :html4 or :html5).
#
# When testing in a Rails application, the parser default can also be set by setting
# +Rails.application.config.dom_testing_default_html_version+.
#
# If you want to specify the HTML parser just for a particular assertion, pass
# <tt>html_version: :html4</tt> or <tt>html_version: :html5</tt> keyword arguments:
#
# assert_dom_not_equal expected, actual, html_version: :html5
#
def assert_dom_not_equal(expected, actual, message = nil, strict: false, html_version: nil)
expected_dom, actual_dom = fragment(expected, html_version: html_version), fragment(actual, html_version: html_version)
message ||= "Expected: #{expected}\nActual: #{actual}"
assert_not compare_doms(expected_dom, actual_dom, strict), message
end
protected
def compare_doms(expected, actual, strict)
expected_children = extract_children(expected, strict)
actual_children = extract_children(actual, strict)
return false unless expected_children.size == actual_children.size
expected_children.each_with_index do |child, i|
return false unless equal_children?(child, actual_children[i], strict)
end
true
end
def extract_children(node, strict)
if strict
node.children
else
node.children.reject { |n| n.text? && n.text.blank? }
end
end
def equal_children?(child, other_child, strict)
return false unless child.type == other_child.type
if child.element?
child.name == other_child.name &&
equal_attribute_nodes?(child.attribute_nodes, other_child.attribute_nodes) &&
compare_doms(child, other_child, strict)
else
equal_child?(child, other_child, strict)
end
end
def equal_child?(child, other_child, strict)
if strict
child.to_s == other_child.to_s
else
child.to_s.split == other_child.to_s.split
end
end
def equal_attribute_nodes?(nodes, other_nodes)
return false unless nodes.size == other_nodes.size
nodes = nodes.sort_by(&:name)
other_nodes = other_nodes.sort_by(&:name)
nodes.each_with_index do |attr, i|
return false unless equal_attribute?(attr, other_nodes[i])
end
true
end
def equal_attribute?(attr, other_attr)
attr.name == other_attr.name && attr.value == other_attr.value
end
private
def fragment(text, html_version: nil)
Rails::Dom::Testing.html_document_fragment(html_version: html_version).parse(text)
end
end
end
end
end
end
|