# 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( # 'Apples', # 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 # strict: true: # # # these assertions will both pass # assert_dom_equal "
\nfoo\n\
", "
foo
", strict: false # assert_dom_not_equal "
\nfoo\n\
", "
foo
", 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 # html_version: :html4 or html_version: :html5 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( # 'Apples', # 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 # strict: true: # # # these assertions will both pass # assert_dom_equal "
\nfoo\n\
", "
foo
", strict: false # assert_dom_not_equal "
\nfoo\n\
", "
foo
", 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 # html_version: :html4 or html_version: :html5 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