File: dom_assertions.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 (140 lines) | stat: -rw-r--r-- 6,067 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
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