rails-deprecated_sanitizer 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,107 @@
1
+ require 'strscan'
2
+
3
+ module HTML #: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 HTML #: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.0"
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 = HTML::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,30 @@
1
+ require 'test_helper'
2
+ require 'action_view'
3
+ require 'action_view/helpers/sanitize_helper'
4
+
5
+ class DeprecatedSanitizerTest < ActiveSupport::TestCase
6
+ def sanitize_helper
7
+ ActionView::Helpers::SanitizeHelper
8
+ end
9
+
10
+ test 'Action View sanitizer vendor is set to deprecated sanitizer' do
11
+ assert_equal Rails::DeprecatedSanitizer, sanitize_helper.sanitizer_vendor
12
+ end
13
+
14
+ test 'Action View sanitizer vendor returns constant from HTML module' do
15
+ assert_equal HTML::LinkSanitizer, sanitize_helper.sanitizer_vendor.link_sanitizer
16
+ end
17
+
18
+ test 'setting allowed tags modifies HTML::WhiteListSanitizers allowed tags' do
19
+ sanitize_helper.sanitized_allowed_tags = %w(horse)
20
+ assert_includes HTML::WhiteListSanitizer.allowed_tags, 'horse'
21
+ end
22
+
23
+ test 'setting allowed attributes modifies HTML::WhiteListSanitizers allowed attributes' do
24
+ attrs = %w(for your health)
25
+ sanitize_helper.sanitized_allowed_attributes = attrs
26
+ attrs.each do |attr|
27
+ assert_includes HTML::WhiteListSanitizer.allowed_attributes, attr
28
+ end
29
+ end
30
+ 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 = HTML::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 = HTML::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 = HTML::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 = HTML::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 { HTML::Document.new("<tags><tag/></tags>", true, true) }
64
+ assert_nothing_raised { HTML::Document.new("<outer><link>something</link></outer>", true, true) }
65
+ end
66
+
67
+ def test_parse_document
68
+ doc = HTML::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 = HTML::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 = HTML::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 = HTML::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 = HTML::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 = HTML::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 = HTML::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
+ HTML::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
+ HTML::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
@@ -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 = HTML::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) { HTML::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 = HTML::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 = HTML::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 = HTML::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 = HTML::Node.parse(nil,0,0,s,false) }
74
+ assert_kind_of HTML::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) { HTML::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 = HTML::Node.parse(nil,0,0,s,false) }
87
+ assert_kind_of HTML::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 = HTML::Node.parse(parent,0,0,content)
241
+ parent.children << node if parent
242
+ node
243
+ end
244
+ end