rails-deprecated_sanitizer-no-registration 1.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,107 @@
1
+ require 'strscan'
2
+
3
+ module HTMLDeprecated #:nodoc:
4
+
5
+ # A simple HTML tokenizer. It simply breaks a stream of text into tokens, where each
6
+ # token is a string. Each string represents either "text", or an HTML element.
7
+ #
8
+ # This currently assumes valid XHTML, which means no free < or > characters.
9
+ #
10
+ # Usage:
11
+ #
12
+ # tokenizer = HTML::Tokenizer.new(text)
13
+ # while token = tokenizer.next
14
+ # p token
15
+ # end
16
+ class Tokenizer #:nodoc:
17
+
18
+ # The current (byte) position in the text
19
+ attr_reader :position
20
+
21
+ # The current line number
22
+ attr_reader :line
23
+
24
+ # Create a new Tokenizer for the given text.
25
+ def initialize(text)
26
+ text.encode!
27
+ @scanner = StringScanner.new(text)
28
+ @position = 0
29
+ @line = 0
30
+ @current_line = 1
31
+ end
32
+
33
+ # Returns the next token in the sequence, or +nil+ if there are no more tokens in
34
+ # the stream.
35
+ def next
36
+ return nil if @scanner.eos?
37
+ @position = @scanner.pos
38
+ @line = @current_line
39
+ if @scanner.check(/<\S/)
40
+ update_current_line(scan_tag)
41
+ else
42
+ update_current_line(scan_text)
43
+ end
44
+ end
45
+
46
+ private
47
+
48
+ # Treat the text at the current position as a tag, and scan it. Supports
49
+ # comments, doctype tags, and regular tags, and ignores less-than and
50
+ # greater-than characters within quoted strings.
51
+ def scan_tag
52
+ tag = @scanner.getch
53
+ if @scanner.scan(/!--/) # comment
54
+ tag << @scanner.matched
55
+ tag << (@scanner.scan_until(/--\s*>/) || @scanner.scan_until(/\Z/))
56
+ elsif @scanner.scan(/!\[CDATA\[/)
57
+ tag << @scanner.matched
58
+ tag << (@scanner.scan_until(/\]\]>/) || @scanner.scan_until(/\Z/))
59
+ elsif @scanner.scan(/!/) # doctype
60
+ tag << @scanner.matched
61
+ tag << consume_quoted_regions
62
+ else
63
+ tag << consume_quoted_regions
64
+ end
65
+ tag
66
+ end
67
+
68
+ # Scan all text up to the next < character and return it.
69
+ def scan_text
70
+ "#{@scanner.getch}#{@scanner.scan(/[^<]*/)}"
71
+ end
72
+
73
+ # Counts the number of newlines in the text and updates the current line
74
+ # accordingly.
75
+ def update_current_line(text)
76
+ text.scan(/\r?\n/) { @current_line += 1 }
77
+ end
78
+
79
+ # Skips over quoted strings, so that less-than and greater-than characters
80
+ # within the strings are ignored.
81
+ def consume_quoted_regions
82
+ text = ""
83
+ loop do
84
+ match = @scanner.scan_until(/['"<>]/) or break
85
+
86
+ delim = @scanner.matched
87
+ if delim == "<"
88
+ match = match.chop
89
+ @scanner.pos -= 1
90
+ end
91
+
92
+ text << match
93
+ break if delim == "<" || delim == ">"
94
+
95
+ # consume the quoted region
96
+ while match = @scanner.scan_until(/[\\#{delim}]/)
97
+ text << match
98
+ break if @scanner.matched == delim
99
+ break if @scanner.eos?
100
+ text << @scanner.getch # skip the escaped character
101
+ end
102
+ end
103
+ text
104
+ end
105
+ end
106
+
107
+ end
@@ -0,0 +1,11 @@
1
+ module HTMLDeprecated #:nodoc:
2
+ module Version #:nodoc:
3
+
4
+ MAJOR = 0
5
+ MINOR = 5
6
+ TINY = 3
7
+
8
+ STRING = [ MAJOR, MINOR, TINY ].join(".")
9
+
10
+ end
11
+ end
@@ -0,0 +1,5 @@
1
+ module Rails
2
+ module DeprecatedSanitizer
3
+ VERSION = "1.0.4"
4
+ end
5
+ end
@@ -0,0 +1,16 @@
1
+ require 'test_helper'
2
+ require 'rails/deprecated_sanitizer/html-scanner/html/node'
3
+
4
+ class CDATANodeTest < ActiveSupport::TestCase
5
+ def setup
6
+ @node = HTMLDeprecated::CDATA.new(nil, 0, 0, "<p>howdy</p>")
7
+ end
8
+
9
+ def test_to_s
10
+ assert_equal "<![CDATA[<p>howdy</p>]]>", @node.to_s
11
+ end
12
+
13
+ def test_content
14
+ assert_equal "<p>howdy</p>", @node.content
15
+ end
16
+ end
@@ -0,0 +1,149 @@
1
+ require 'test_helper'
2
+ require 'rails/deprecated_sanitizer/html-scanner'
3
+
4
+ class DocumentTest < ActiveSupport::TestCase
5
+ def test_handle_doctype
6
+ doc = nil
7
+ assert_nothing_raised do
8
+ doc = HTMLDeprecated::Document.new <<-HTML.strip
9
+ <!DOCTYPE "blah" "blah" "blah">
10
+ <html>
11
+ </html>
12
+ HTML
13
+ end
14
+ assert_equal 3, doc.root.children.length
15
+ assert_equal %{<!DOCTYPE "blah" "blah" "blah">}, doc.root.children[0].content
16
+ assert_match %r{\s+}m, doc.root.children[1].content
17
+ assert_equal "html", doc.root.children[2].name
18
+ end
19
+
20
+ def test_find_img
21
+ doc = HTMLDeprecated::Document.new <<-HTML.strip
22
+ <html>
23
+ <body>
24
+ <p><img src="hello.gif"></p>
25
+ </body>
26
+ </html>
27
+ HTML
28
+ assert doc.find(:tag=>"img", :attributes=>{"src"=>"hello.gif"})
29
+ end
30
+
31
+ def test_find_all
32
+ doc = HTMLDeprecated::Document.new <<-HTML.strip
33
+ <html>
34
+ <body>
35
+ <p class="test"><img src="hello.gif"></p>
36
+ <div class="foo">
37
+ <p class="test">something</p>
38
+ <p>here is <em class="test">more</em></p>
39
+ </div>
40
+ </body>
41
+ </html>
42
+ HTML
43
+ all = doc.find_all :attributes => { :class => "test" }
44
+ assert_equal 3, all.length
45
+ assert_equal [ "p", "p", "em" ], all.map { |n| n.name }
46
+ end
47
+
48
+ def test_find_with_text
49
+ doc = HTMLDeprecated::Document.new <<-HTML.strip
50
+ <html>
51
+ <body>
52
+ <p>Some text</p>
53
+ </body>
54
+ </html>
55
+ HTML
56
+ assert doc.find(:content => "Some text")
57
+ assert doc.find(:tag => "p", :child => { :content => "Some text" })
58
+ assert doc.find(:tag => "p", :child => "Some text")
59
+ assert doc.find(:tag => "p", :content => "Some text")
60
+ end
61
+
62
+ def test_parse_xml
63
+ assert_nothing_raised { HTMLDeprecated::Document.new("<tags><tag/></tags>", true, true) }
64
+ assert_nothing_raised { HTMLDeprecated::Document.new("<outer><link>something</link></outer>", true, true) }
65
+ end
66
+
67
+ def test_parse_document
68
+ doc = HTMLDeprecated::Document.new(<<-HTML)
69
+ <div>
70
+ <h2>blah</h2>
71
+ <table>
72
+ </table>
73
+ </div>
74
+ HTML
75
+ assert_not_nil doc.find(:tag => "div", :children => { :count => 1, :only => { :tag => "table" } })
76
+ end
77
+
78
+ def test_tag_nesting_nothing_to_s
79
+ doc = HTMLDeprecated::Document.new("<tag></tag>")
80
+ assert_equal "<tag></tag>", doc.root.to_s
81
+ end
82
+
83
+ def test_tag_nesting_space_to_s
84
+ doc = HTMLDeprecated::Document.new("<tag> </tag>")
85
+ assert_equal "<tag> </tag>", doc.root.to_s
86
+ end
87
+
88
+ def test_tag_nesting_text_to_s
89
+ doc = HTMLDeprecated::Document.new("<tag>text</tag>")
90
+ assert_equal "<tag>text</tag>", doc.root.to_s
91
+ end
92
+
93
+ def test_tag_nesting_tag_to_s
94
+ doc = HTMLDeprecated::Document.new("<tag><nested /></tag>")
95
+ assert_equal "<tag><nested /></tag>", doc.root.to_s
96
+ end
97
+
98
+ def test_parse_cdata
99
+ doc = HTMLDeprecated::Document.new(<<-HTML)
100
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
101
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
102
+ <html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
103
+ <head>
104
+ <title><![CDATA[<br>]]></title>
105
+ </head>
106
+ <body>
107
+ <p>this document has &lt;br&gt; for a title</p>
108
+ </body>
109
+ </html>
110
+ HTML
111
+
112
+ assert_nil doc.find(:tag => "title", :descendant => { :tag => "br" })
113
+ assert doc.find(:tag => "title", :child => "<br>")
114
+ end
115
+
116
+ def test_find_empty_tag
117
+ doc = HTMLDeprecated::Document.new("<div id='map'></div>")
118
+ assert_nil doc.find(:tag => "div", :attributes => { :id => "map" }, :content => /./)
119
+ assert doc.find(:tag => "div", :attributes => { :id => "map" }, :content => /\A\Z/)
120
+ assert doc.find(:tag => "div", :attributes => { :id => "map" }, :content => /^$/)
121
+ assert doc.find(:tag => "div", :attributes => { :id => "map" }, :content => "")
122
+ assert doc.find(:tag => "div", :attributes => { :id => "map" }, :content => nil)
123
+ end
124
+
125
+ def test_parse_invalid_document
126
+ assert_nothing_raised do
127
+ HTMLDeprecated::Document.new("<html>
128
+ <table>
129
+ <tr>
130
+ <td style=\"color: #FFFFFF; height: 17px; onclick=\"window.location.href='http://www.rmeinc.com/about_rme.aspx'\" style=\"cursor:pointer; height: 17px;\"; nowrap onclick=\"window.location.href='http://www.rmeinc.com/about_rme.aspx'\" onmouseout=\"this.bgColor='#0066cc'; this.style.color='#FFFFFF'\" onmouseover=\"this.bgColor='#ffffff'; this.style.color='#0033cc'\">About Us</td>
131
+ </tr>
132
+ </table>
133
+ </html>")
134
+ end
135
+ end
136
+
137
+ def test_invalid_document_raises_exception_when_strict
138
+ assert_raise RuntimeError do
139
+ HTMLDeprecated::Document.new("<html>
140
+ <table>
141
+ <tr>
142
+ <td style=\"color: #FFFFFF; height: 17px; onclick=\"window.location.href='http://www.rmeinc.com/about_rme.aspx'\" style=\"cursor:pointer; height: 17px;\"; nowrap onclick=\"window.location.href='http://www.rmeinc.com/about_rme.aspx'\" onmouseout=\"this.bgColor='#0066cc'; this.style.color='#FFFFFF'\" onmouseover=\"this.bgColor='#ffffff'; this.style.color='#0033cc'\">About Us</td>
143
+ </tr>
144
+ </table>
145
+ </html>", true)
146
+ end
147
+ end
148
+
149
+ end
data/test/node_test.rb ADDED
@@ -0,0 +1,90 @@
1
+ require 'test_helper'
2
+ require 'rails/deprecated_sanitizer/html-scanner/html/node'
3
+
4
+ class NodeTest < ActiveSupport::TestCase
5
+
6
+ class MockNode
7
+ def initialize(matched, value)
8
+ @matched = matched
9
+ @value = value
10
+ end
11
+
12
+ def find(conditions)
13
+ @matched && self
14
+ end
15
+
16
+ def to_s
17
+ @value.to_s
18
+ end
19
+ end
20
+
21
+ def setup
22
+ @node = HTMLDeprecated::Node.new("parent")
23
+ @node.children.concat [MockNode.new(false,1), MockNode.new(true,"two"), MockNode.new(false,:three)]
24
+ end
25
+
26
+ def test_match
27
+ assert !@node.match("foo")
28
+ end
29
+
30
+ def test_tag
31
+ assert !@node.tag?
32
+ end
33
+
34
+ def test_to_s
35
+ assert_equal "1twothree", @node.to_s
36
+ end
37
+
38
+ def test_find
39
+ assert_equal "two", @node.find('blah').to_s
40
+ end
41
+
42
+ def test_parse_strict
43
+ s = "<b foo='hello'' bar='baz'>"
44
+ assert_raise(RuntimeError) { HTMLDeprecated::Node.parse(nil, 0, 0, s) }
45
+ end
46
+
47
+ def test_parse_relaxed
48
+ s = "<b foo='hello'' bar='baz'>"
49
+ node = nil
50
+ assert_nothing_raised { node = HTMLDeprecated::Node.parse(nil, 0, 0, s, false) }
51
+ assert node.attributes.has_key?("foo")
52
+ assert !node.attributes.has_key?("bar")
53
+ end
54
+
55
+ def test_to_s_with_boolean_attrs
56
+ s = "<b foo bar>"
57
+ node = HTMLDeprecated::Node.parse(nil, 0, 0, s)
58
+ assert node.attributes.has_key?("foo")
59
+ assert node.attributes.has_key?("bar")
60
+ assert "<b foo bar>", node.to_s
61
+ end
62
+
63
+ def test_parse_with_unclosed_tag
64
+ s = "<span onmouseover='bang'"
65
+ node = nil
66
+ assert_nothing_raised { node = HTMLDeprecated::Node.parse(nil, 0, 0, s, false) }
67
+ assert node.attributes.has_key?("onmouseover")
68
+ end
69
+
70
+ def test_parse_with_valid_cdata_section
71
+ s = "<![CDATA[<span>contents</span>]]>"
72
+ node = nil
73
+ assert_nothing_raised { node = HTMLDeprecated::Node.parse(nil, 0, 0, s, false) }
74
+ assert_kind_of HTMLDeprecated::CDATA, node
75
+ assert_equal '<span>contents</span>', node.content
76
+ end
77
+
78
+ def test_parse_strict_with_unterminated_cdata_section
79
+ s = "<![CDATA[neverending..."
80
+ assert_raise(RuntimeError) { HTMLDeprecated::Node.parse(nil, 0, 0, s) }
81
+ end
82
+
83
+ def test_parse_relaxed_with_unterminated_cdata_section
84
+ s = "<![CDATA[neverending..."
85
+ node = nil
86
+ assert_nothing_raised { node = HTMLDeprecated::Node.parse(nil, 0, 0, s, false) }
87
+ assert_kind_of HTMLDeprecated::CDATA, node
88
+ assert_equal 'neverending...', node.content
89
+ end
90
+ end
@@ -0,0 +1,244 @@
1
+ require 'test_helper'
2
+ require 'rails/deprecated_sanitizer/html-scanner/html/node'
3
+
4
+ class TagNodeTest < ActiveSupport::TestCase
5
+ def test_open_without_attributes
6
+ node = tag("<tag>")
7
+ assert_equal "tag", node.name
8
+ assert_equal Hash.new, node.attributes
9
+ assert_nil node.closing
10
+ end
11
+
12
+ def test_open_with_attributes
13
+ node = tag("<TAG1 foo=hey_ho x:bar=\"blah blah\" BAZ='blah blah blah' >")
14
+ assert_equal "tag1", node.name
15
+ assert_equal "hey_ho", node["foo"]
16
+ assert_equal "blah blah", node["x:bar"]
17
+ assert_equal "blah blah blah", node["baz"]
18
+ end
19
+
20
+ def test_self_closing_without_attributes
21
+ node = tag("<tag/>")
22
+ assert_equal "tag", node.name
23
+ assert_equal Hash.new, node.attributes
24
+ assert_equal :self, node.closing
25
+ end
26
+
27
+ def test_self_closing_with_attributes
28
+ node = tag("<tag a=b/>")
29
+ assert_equal "tag", node.name
30
+ assert_equal( { "a" => "b" }, node.attributes )
31
+ assert_equal :self, node.closing
32
+ end
33
+
34
+ def test_closing_without_attributes
35
+ node = tag("</tag>")
36
+ assert_equal "tag", node.name
37
+ assert_nil node.attributes
38
+ assert_equal :close, node.closing
39
+ end
40
+
41
+ def test_bracket_op_when_no_attributes
42
+ node = tag("</tag>")
43
+ assert_nil node["foo"]
44
+ end
45
+
46
+ def test_bracket_op_when_attributes
47
+ node = tag("<tag a=b/>")
48
+ assert_equal "b", node["a"]
49
+ end
50
+
51
+ def test_attributes_with_escaped_quotes
52
+ node = tag("<tag a='b\\'c' b=\"bob \\\"float\\\"\">")
53
+ assert_equal "b\\'c", node["a"]
54
+ assert_equal "bob \\\"float\\\"", node["b"]
55
+ end
56
+
57
+ def test_to_s
58
+ node = tag("<a b=c d='f' g=\"h 'i'\" />")
59
+ node = node.to_s
60
+ assert node.include?('a')
61
+ assert node.include?('b="c"')
62
+ assert node.include?('d="f"')
63
+ assert node.include?('g="h')
64
+ assert node.include?('i')
65
+ end
66
+
67
+ def test_tag
68
+ assert tag("<tag>").tag?
69
+ end
70
+
71
+ def test_match_tag_as_string
72
+ assert tag("<tag>").match(:tag => "tag")
73
+ assert !tag("<tag>").match(:tag => "b")
74
+ end
75
+
76
+ def test_match_tag_as_regexp
77
+ assert tag("<tag>").match(:tag => /t.g/)
78
+ assert !tag("<tag>").match(:tag => /t[bqs]g/)
79
+ end
80
+
81
+ def test_match_attributes_as_string
82
+ t = tag("<tag a=something b=else />")
83
+ assert t.match(:attributes => {"a" => "something"})
84
+ assert t.match(:attributes => {"b" => "else"})
85
+ end
86
+
87
+ def test_match_attributes_as_regexp
88
+ t = tag("<tag a=something b=else />")
89
+ assert t.match(:attributes => {"a" => /^something$/})
90
+ assert t.match(:attributes => {"b" => /e.*e/})
91
+ assert t.match(:attributes => {"a" => /me..i/, "b" => /.ls.$/})
92
+ end
93
+
94
+ def test_match_attributes_as_number
95
+ t = tag("<tag a=15 b=3.1415 />")
96
+ assert t.match(:attributes => {"a" => 15})
97
+ assert t.match(:attributes => {"b" => 3.1415})
98
+ assert t.match(:attributes => {"a" => 15, "b" => 3.1415})
99
+ end
100
+
101
+ def test_match_attributes_exist
102
+ t = tag("<tag a=15 b=3.1415 />")
103
+ assert t.match(:attributes => {"a" => true})
104
+ assert t.match(:attributes => {"b" => true})
105
+ assert t.match(:attributes => {"a" => true, "b" => true})
106
+ end
107
+
108
+ def test_match_attributes_not_exist
109
+ t = tag("<tag a=15 b=3.1415 />")
110
+ assert t.match(:attributes => {"c" => false})
111
+ assert t.match(:attributes => {"c" => nil})
112
+ assert t.match(:attributes => {"a" => true, "c" => false})
113
+ end
114
+
115
+ def test_match_parent_success
116
+ t = tag("<tag a=15 b='hello'>", tag("<foo k='value'>"))
117
+ assert t.match(:parent => {:tag => "foo", :attributes => {"k" => /v.l/, "j" => false}})
118
+ end
119
+
120
+ def test_match_parent_fail
121
+ t = tag("<tag a=15 b='hello'>", tag("<foo k='value'>"))
122
+ assert !t.match(:parent => {:tag => /kafka/})
123
+ end
124
+
125
+ def test_match_child_success
126
+ t = tag("<tag x:k='something'>")
127
+ tag("<child v=john a=kelly>", t)
128
+ tag("<sib m=vaughn v=james>", t)
129
+ assert t.match(:child => { :tag => "sib", :attributes => {"v" => /j/}})
130
+ assert t.match(:child => { :attributes => {"a" => "kelly"}})
131
+ end
132
+
133
+ def test_match_child_fail
134
+ t = tag("<tag x:k='something'>")
135
+ tag("<child v=john a=kelly>", t)
136
+ tag("<sib m=vaughn v=james>", t)
137
+ assert !t.match(:child => { :tag => "sib", :attributes => {"v" => /r/}})
138
+ assert !t.match(:child => { :attributes => {"v" => false}})
139
+ end
140
+
141
+ def test_match_ancestor_success
142
+ t = tag("<tag x:k='something'>", tag("<parent v=john a=kelly>", tag("<grandparent m=vaughn v=james>")))
143
+ assert t.match(:ancestor => {:tag => "parent", :attributes => {"a" => /ll/}})
144
+ assert t.match(:ancestor => {:attributes => {"m" => "vaughn"}})
145
+ end
146
+
147
+ def test_match_ancestor_fail
148
+ t = tag("<tag x:k='something'>", tag("<parent v=john a=kelly>", tag("<grandparent m=vaughn v=james>")))
149
+ assert !t.match(:ancestor => {:tag => /^parent/, :attributes => {"v" => /m/}})
150
+ assert !t.match(:ancestor => {:attributes => {"v" => false}})
151
+ end
152
+
153
+ def test_match_descendant_success
154
+ tag("<grandchild m=vaughn v=james>", tag("<child v=john a=kelly>", t = tag("<tag x:k='something'>")))
155
+ assert t.match(:descendant => {:tag => "child", :attributes => {"a" => /ll/}})
156
+ assert t.match(:descendant => {:attributes => {"m" => "vaughn"}})
157
+ end
158
+
159
+ def test_match_descendant_fail
160
+ tag("<grandchild m=vaughn v=james>", tag("<child v=john a=kelly>", t = tag("<tag x:k='something'>")))
161
+ assert !t.match(:descendant => {:tag => /^child/, :attributes => {"v" => /m/}})
162
+ assert !t.match(:descendant => {:attributes => {"v" => false}})
163
+ end
164
+
165
+ def test_match_child_count
166
+ t = tag("<tag x:k='something'>")
167
+ tag("hello", t)
168
+ tag("<child v=john a=kelly>", t)
169
+ tag("<sib m=vaughn v=james>", t)
170
+ assert t.match(:children => { :count => 2 })
171
+ assert t.match(:children => { :count => 2..4 })
172
+ assert t.match(:children => { :less_than => 4 })
173
+ assert t.match(:children => { :greater_than => 1 })
174
+ assert !t.match(:children => { :count => 3 })
175
+ end
176
+
177
+ def test_conditions_as_strings
178
+ t = tag("<tag x:k='something'>")
179
+ assert t.match("tag" => "tag")
180
+ assert t.match("attributes" => { "x:k" => "something" })
181
+ assert !t.match("tag" => "gat")
182
+ assert !t.match("attributes" => { "x:j" => "something" })
183
+ end
184
+
185
+ def test_attributes_as_symbols
186
+ t = tag("<child v=john a=kelly>")
187
+ assert t.match(:attributes => { :v => /oh/ })
188
+ assert t.match(:attributes => { :a => /ll/ })
189
+ end
190
+
191
+ def test_match_sibling
192
+ t = tag("<tag x:k='something'>")
193
+ tag("hello", t)
194
+ tag("<span a=b>", t)
195
+ tag("world", t)
196
+ m = tag("<span k=r>", t)
197
+ tag("<span m=l>", t)
198
+
199
+ assert m.match(:sibling => {:tag => "span", :attributes => {:a => true}})
200
+ assert m.match(:sibling => {:tag => "span", :attributes => {:m => true}})
201
+ assert !m.match(:sibling => {:tag => "span", :attributes => {:k => true}})
202
+ end
203
+
204
+ def test_match_sibling_before
205
+ t = tag("<tag x:k='something'>")
206
+ tag("hello", t)
207
+ tag("<span a=b>", t)
208
+ tag("world", t)
209
+ m = tag("<span k=r>", t)
210
+ tag("<span m=l>", t)
211
+
212
+ assert m.match(:before => {:tag => "span", :attributes => {:m => true}})
213
+ assert !m.match(:before => {:tag => "span", :attributes => {:a => true}})
214
+ assert !m.match(:before => {:tag => "span", :attributes => {:k => true}})
215
+ end
216
+
217
+ def test_match_sibling_after
218
+ t = tag("<tag x:k='something'>")
219
+ tag("hello", t)
220
+ tag("<span a=b>", t)
221
+ tag("world", t)
222
+ m = tag("<span k=r>", t)
223
+ tag("<span m=l>", t)
224
+
225
+ assert m.match(:after => {:tag => "span", :attributes => {:a => true}})
226
+ assert !m.match(:after => {:tag => "span", :attributes => {:m => true}})
227
+ assert !m.match(:after => {:tag => "span", :attributes => {:k => true}})
228
+ end
229
+
230
+ def test_tag_to_s
231
+ t = tag("<b x='foo'>")
232
+ tag("hello", t)
233
+ tag("<hr />", t)
234
+ assert_equal %(<b x="foo">hello<hr /></b>), t.to_s
235
+ end
236
+
237
+ private
238
+
239
+ def tag(content, parent=nil)
240
+ node = HTMLDeprecated::Node.parse(parent, 0, 0, content)
241
+ parent.children << node if parent
242
+ node
243
+ end
244
+ end