tenderlove-nokogiri 0.0.0-x86-mswin32-60

Sign up to get free protection for your applications and to get access to all the features.
Files changed (117) hide show
  1. data/History.txt +6 -0
  2. data/Manifest.txt +120 -0
  3. data/README.ja.txt +86 -0
  4. data/README.txt +87 -0
  5. data/Rakefile +264 -0
  6. data/ext/nokogiri/extconf.rb +59 -0
  7. data/ext/nokogiri/html_document.c +83 -0
  8. data/ext/nokogiri/html_document.h +10 -0
  9. data/ext/nokogiri/html_sax_parser.c +32 -0
  10. data/ext/nokogiri/html_sax_parser.h +11 -0
  11. data/ext/nokogiri/native.c +40 -0
  12. data/ext/nokogiri/native.h +51 -0
  13. data/ext/nokogiri/xml_cdata.c +52 -0
  14. data/ext/nokogiri/xml_cdata.h +9 -0
  15. data/ext/nokogiri/xml_document.c +159 -0
  16. data/ext/nokogiri/xml_document.h +10 -0
  17. data/ext/nokogiri/xml_dtd.c +117 -0
  18. data/ext/nokogiri/xml_dtd.h +8 -0
  19. data/ext/nokogiri/xml_node.c +709 -0
  20. data/ext/nokogiri/xml_node.h +15 -0
  21. data/ext/nokogiri/xml_node_set.c +124 -0
  22. data/ext/nokogiri/xml_node_set.h +9 -0
  23. data/ext/nokogiri/xml_reader.c +429 -0
  24. data/ext/nokogiri/xml_reader.h +10 -0
  25. data/ext/nokogiri/xml_sax_parser.c +174 -0
  26. data/ext/nokogiri/xml_sax_parser.h +10 -0
  27. data/ext/nokogiri/xml_syntax_error.c +194 -0
  28. data/ext/nokogiri/xml_syntax_error.h +11 -0
  29. data/ext/nokogiri/xml_text.c +29 -0
  30. data/ext/nokogiri/xml_text.h +9 -0
  31. data/ext/nokogiri/xml_xpath.c +46 -0
  32. data/ext/nokogiri/xml_xpath.h +11 -0
  33. data/ext/nokogiri/xml_xpath_context.c +81 -0
  34. data/ext/nokogiri/xml_xpath_context.h +9 -0
  35. data/ext/nokogiri/xslt_stylesheet.c +108 -0
  36. data/ext/nokogiri/xslt_stylesheet.h +9 -0
  37. data/lib/nokogiri/css/node.rb +95 -0
  38. data/lib/nokogiri/css/parser.rb +24 -0
  39. data/lib/nokogiri/css/parser.y +198 -0
  40. data/lib/nokogiri/css/tokenizer.rb +9 -0
  41. data/lib/nokogiri/css/tokenizer.rex +63 -0
  42. data/lib/nokogiri/css/xpath_visitor.rb +165 -0
  43. data/lib/nokogiri/css.rb +6 -0
  44. data/lib/nokogiri/decorators/hpricot/node.rb +58 -0
  45. data/lib/nokogiri/decorators/hpricot/node_set.rb +14 -0
  46. data/lib/nokogiri/decorators/hpricot/xpath_visitor.rb +17 -0
  47. data/lib/nokogiri/decorators/hpricot.rb +3 -0
  48. data/lib/nokogiri/decorators.rb +1 -0
  49. data/lib/nokogiri/hpricot.rb +47 -0
  50. data/lib/nokogiri/html/builder.rb +9 -0
  51. data/lib/nokogiri/html/document.rb +9 -0
  52. data/lib/nokogiri/html/sax/parser.rb +21 -0
  53. data/lib/nokogiri/html.rb +95 -0
  54. data/lib/nokogiri/version.rb +3 -0
  55. data/lib/nokogiri/xml/after_handler.rb +18 -0
  56. data/lib/nokogiri/xml/before_handler.rb +32 -0
  57. data/lib/nokogiri/xml/builder.rb +79 -0
  58. data/lib/nokogiri/xml/cdata.rb +9 -0
  59. data/lib/nokogiri/xml/document.rb +30 -0
  60. data/lib/nokogiri/xml/dtd.rb +6 -0
  61. data/lib/nokogiri/xml/node.rb +195 -0
  62. data/lib/nokogiri/xml/node_set.rb +183 -0
  63. data/lib/nokogiri/xml/notation.rb +6 -0
  64. data/lib/nokogiri/xml/reader.rb +14 -0
  65. data/lib/nokogiri/xml/sax/document.rb +59 -0
  66. data/lib/nokogiri/xml/sax/parser.rb +33 -0
  67. data/lib/nokogiri/xml/sax.rb +9 -0
  68. data/lib/nokogiri/xml/syntax_error.rb +21 -0
  69. data/lib/nokogiri/xml/text.rb +6 -0
  70. data/lib/nokogiri/xml/xpath.rb +6 -0
  71. data/lib/nokogiri/xml/xpath_context.rb +14 -0
  72. data/lib/nokogiri/xml.rb +67 -0
  73. data/lib/nokogiri/xslt/stylesheet.rb +6 -0
  74. data/lib/nokogiri/xslt.rb +11 -0
  75. data/lib/nokogiri.rb +51 -0
  76. data/nokogiri.gemspec +34 -0
  77. data/test/css/test_nthiness.rb +159 -0
  78. data/test/css/test_parser.rb +224 -0
  79. data/test/css/test_tokenizer.rb +162 -0
  80. data/test/css/test_xpath_visitor.rb +54 -0
  81. data/test/files/staff.xml +59 -0
  82. data/test/files/staff.xslt +32 -0
  83. data/test/files/tlm.html +850 -0
  84. data/test/helper.rb +70 -0
  85. data/test/hpricot/files/basic.xhtml +17 -0
  86. data/test/hpricot/files/boingboing.html +2266 -0
  87. data/test/hpricot/files/cy0.html +3653 -0
  88. data/test/hpricot/files/immob.html +400 -0
  89. data/test/hpricot/files/pace_application.html +1320 -0
  90. data/test/hpricot/files/tenderlove.html +16 -0
  91. data/test/hpricot/files/uswebgen.html +220 -0
  92. data/test/hpricot/files/utf8.html +1054 -0
  93. data/test/hpricot/files/week9.html +1723 -0
  94. data/test/hpricot/files/why.xml +19 -0
  95. data/test/hpricot/load_files.rb +7 -0
  96. data/test/hpricot/test_alter.rb +67 -0
  97. data/test/hpricot/test_builder.rb +27 -0
  98. data/test/hpricot/test_parser.rb +423 -0
  99. data/test/hpricot/test_paths.rb +15 -0
  100. data/test/hpricot/test_preserved.rb +78 -0
  101. data/test/hpricot/test_xml.rb +30 -0
  102. data/test/html/sax/test_parser.rb +27 -0
  103. data/test/html/test_builder.rb +78 -0
  104. data/test/html/test_document.rb +86 -0
  105. data/test/test_convert_xpath.rb +180 -0
  106. data/test/test_nokogiri.rb +36 -0
  107. data/test/test_reader.rb +222 -0
  108. data/test/test_xslt_transforms.rb +29 -0
  109. data/test/xml/sax/test_parser.rb +93 -0
  110. data/test/xml/test_builder.rb +16 -0
  111. data/test/xml/test_cdata.rb +18 -0
  112. data/test/xml/test_document.rb +171 -0
  113. data/test/xml/test_dtd.rb +43 -0
  114. data/test/xml/test_node.rb +223 -0
  115. data/test/xml/test_node_set.rb +116 -0
  116. data/test/xml/test_text.rb +13 -0
  117. metadata +214 -0
@@ -0,0 +1,159 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', "helper"))
2
+
3
+ module Nokogiri
4
+ module CSS
5
+ class TestNthiness < Nokogiri::TestCase
6
+ def setup
7
+ doc = <<EOF
8
+ <html>
9
+ <table>
10
+ <tr><td>row1 </td></tr>
11
+ <tr><td>row2 </td></tr>
12
+ <tr><td>row3 </td></tr>
13
+ <tr><td>row4 </td></tr>
14
+ <tr><td>row5 </td></tr>
15
+ <tr><td>row6 </td></tr>
16
+ <tr><td>row7 </td></tr>
17
+ <tr><td>row8 </td></tr>
18
+ <tr><td>row9 </td></tr>
19
+ <tr><td>row10 </td></tr>
20
+ <tr><td>row11 </td></tr>
21
+ <tr><td>row12 </td></tr>
22
+ <tr><td>row13 </td></tr>
23
+ <tr><td>row14 </td></tr>
24
+ </table>
25
+ <div>
26
+ <b>bold1 </b>
27
+ <i>italic1 </i>
28
+ <b>bold2 </b>
29
+ <i>italic2 </i>
30
+ <p>para1 </p>
31
+ <b>bold3 </b>
32
+ </div>
33
+ <div>
34
+ <p>para2 </p>
35
+ <p>para3 </p>
36
+ </div>
37
+ <div>
38
+ <p>para4 </p>
39
+ </div>
40
+ <p class='empty'></p>
41
+ <p class='not-empty'><b></b></p>
42
+ </html>
43
+ EOF
44
+ @parser = Nokogiri.Hpricot doc
45
+ end
46
+
47
+
48
+ def test_even
49
+ assert_result_rows [2,4,6,8,10,12,14], @parser.search("table/tr:nth(even)")
50
+ end
51
+
52
+ def test_odd
53
+ assert_result_rows [1,3,5,7,9,11,13], @parser.search("table/tr:nth(odd)")
54
+ end
55
+
56
+ def test_2n
57
+ assert_equal @parser.search("table/tr:nth(even)").inner_text, @parser.search("table/tr:nth(2n)").inner_text
58
+ end
59
+
60
+ def test_2np1
61
+ assert_equal @parser.search("table/tr:nth(odd)").inner_text, @parser.search("table/tr:nth(2n+1)").inner_text
62
+ end
63
+
64
+ def test_4np3
65
+ assert_result_rows [3,7,11], @parser.search("table/tr:nth(4n+3)")
66
+ end
67
+
68
+ def test_3np4
69
+ assert_result_rows [4,7,10,13], @parser.search("table/tr:nth(3n+4)")
70
+ end
71
+
72
+ def test_mnp3
73
+ assert_result_rows [1,2,3], @parser.search("table/tr:nth(-n+3)")
74
+ end
75
+
76
+ def test_np3
77
+ assert_result_rows [3,4,5,6,7,8,9,10,11,12,13,14], @parser.search("table/tr:nth(n+3)")
78
+ end
79
+
80
+ def test_first
81
+ assert_result_rows [1], @parser.search("table/tr:first")
82
+ assert_result_rows [1], @parser.search("table/tr:first()")
83
+ end
84
+
85
+ def test_last
86
+ assert_result_rows [14], @parser.search("table/tr:last")
87
+ assert_result_rows [14], @parser.search("table/tr:last()")
88
+ end
89
+
90
+ def test_first_child
91
+ assert_result_rows [1], @parser.search("div/b:first-child"), "bold"
92
+ assert_result_rows [1], @parser.search("table/tr:first-child")
93
+ end
94
+
95
+ def test_last_child
96
+ assert_result_rows [3], @parser.search("div/b:last-child"), "bold"
97
+ assert_result_rows [14], @parser.search("table/tr:last-child")
98
+ end
99
+
100
+ def test_first_of_type
101
+ assert_result_rows [1], @parser.search("table/tr:first-of-type")
102
+ assert_result_rows [1], @parser.search("div/b:first-of-type"), "bold"
103
+ end
104
+
105
+ def test_last_of_type
106
+ assert_result_rows [14], @parser.search("table/tr:last-of-type")
107
+ assert_result_rows [3], @parser.search("div/b:last-of-type"), "bold"
108
+ end
109
+
110
+ def test_only_of_type
111
+ assert_result_rows [1,4], @parser.search("div/p:only-of-type"), "para"
112
+ end
113
+
114
+ def test_only_child
115
+ assert_result_rows [4], @parser.search("div/p:only-child"), "para"
116
+ end
117
+
118
+ def test_empty
119
+ result = @parser.search("p:empty")
120
+ assert_equal 1, result.size, "unexpected number of rows returned: '#{result.inner_text}'"
121
+ assert_equal 'empty', result.first['class']
122
+ end
123
+
124
+ def test_parent
125
+ result = @parser.search("p:parent")
126
+ assert_equal 5, result.size
127
+ 0.upto(3) do |j|
128
+ assert_equal "para#{j+1} ", result[j].inner_text
129
+ end
130
+ assert_equal "not-empty", result[4]['class']
131
+ end
132
+
133
+ def test_siblings
134
+ doc = <<-EOF
135
+ <html><body><div>
136
+ <p id="1">p1 </p>
137
+ <p id="2">p2 </p>
138
+ <p id="3">p3 </p>
139
+ <p id="4">p4 </p>
140
+ <p id="5">p5 </p>
141
+ EOF
142
+ parser = Nokogiri.Hpricot doc
143
+
144
+ assert_equal 2, parser.search("#3 ~ p").size
145
+ assert_equal "p4 p5 ", parser.search("#3 ~ p").inner_text
146
+ assert_equal 0, parser.search("#5 ~ p").size
147
+
148
+ assert_equal 1, parser.search("#3 + p").size
149
+ assert_equal "p4 ", parser.search("#3 + p").inner_text
150
+ assert_equal 0, parser.search("#5 + p").size
151
+ end
152
+
153
+ def assert_result_rows intarray, result, word="row"
154
+ assert_equal intarray.size, result.size, "unexpected number of rows returned: '#{result.inner_text}'"
155
+ assert_equal intarray.map{|j| "#{word}#{j}"}.join(' '), result.inner_text.strip, result.inner_text
156
+ end
157
+ end
158
+ end
159
+ end
@@ -0,0 +1,224 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', "helper"))
2
+
3
+ module Nokogiri
4
+ module CSS
5
+ class TestParser < Nokogiri::TestCase
6
+ def setup
7
+ @parser = Nokogiri::CSS::Parser.new
8
+ end
9
+
10
+ def test_find_by_type
11
+ ast = @parser.parse("a:nth-child(2)").first
12
+ matches = ast.find_by_type(
13
+ [:CONDITIONAL_SELECTOR,
14
+ [:ELEMENT_NAME],
15
+ [:PSEUDO_CLASS,
16
+ [:FUNCTION]
17
+ ]
18
+ ]
19
+ )
20
+ assert_equal(1, matches.length)
21
+ assert_equal(ast, matches.first)
22
+ end
23
+
24
+ def test_to_type
25
+ ast = @parser.parse("a:nth-child(2)").first
26
+ assert_equal(
27
+ [:CONDITIONAL_SELECTOR,
28
+ [:ELEMENT_NAME],
29
+ [:PSEUDO_CLASS,
30
+ [:FUNCTION]
31
+ ]
32
+ ], ast.to_type
33
+ )
34
+ end
35
+
36
+ def test_to_a
37
+ asts = @parser.parse("a:nth-child(2)")
38
+ assert_equal(
39
+ [:CONDITIONAL_SELECTOR,
40
+ [:ELEMENT_NAME, ["a"]],
41
+ [:PSEUDO_CLASS,
42
+ [:FUNCTION, ["nth-child("], ["2"]]
43
+ ]
44
+ ], asts.first.to_a
45
+ )
46
+ end
47
+
48
+ def test_function_with_arguments
49
+ assert_xpath "//*[position() = 2 and self::a]",
50
+ @parser.parse("a[2]")
51
+ assert_xpath "//*[position() = 2 and self::a]",
52
+ @parser.parse("a:nth-child(2)")
53
+ end
54
+
55
+ def test_carrot
56
+ assert_xpath "//a[starts-with(@id, 'Boing')]",
57
+ @parser.parse("a[id^='Boing']")
58
+ end
59
+
60
+ def test_attributes_with_at
61
+ ## This is non standard CSS
62
+ assert_xpath "//a[@id = 'Boing']",
63
+ @parser.parse("a[@id='Boing']")
64
+ end
65
+
66
+ def test_not_equal
67
+ ## This is non standard CSS
68
+ assert_xpath "//a[child::text() != 'Boing']",
69
+ @parser.parse("a[text()!='Boing']")
70
+ end
71
+
72
+ def test_function
73
+ ## This is non standard CSS
74
+ assert_xpath "//a[child::text()]",
75
+ @parser.parse("a[text()]")
76
+
77
+ ## This is non standard CSS
78
+ assert_xpath "//child::text()",
79
+ @parser.parse("text()")
80
+
81
+ ## This is non standard CSS
82
+ assert_xpath "//a[contains(child::text(), 'Boing')]",
83
+ @parser.parse("a[text()*='Boing']")
84
+
85
+ ## This is non standard CSS
86
+ assert_xpath "//script//comment()",
87
+ @parser.parse("script comment()")
88
+ end
89
+
90
+ def test_nonstandard_nth_selectors
91
+ ## These are non standard CSS
92
+ assert_xpath '//a[position() = 99]', @parser.parse('a:eq(99)')
93
+ assert_xpath '//a[position() = 1]', @parser.parse('a:first') # no parens
94
+ assert_xpath '//a[position() = last()]', @parser.parse('a:last') # no parens
95
+ assert_xpath '//a[position() = 99]', @parser.parse('a:nth(99)')
96
+ assert_xpath '//a[position() = 1]', @parser.parse('a:first()')
97
+ assert_xpath '//a[position() = last()]', @parser.parse('a:last()')
98
+ assert_xpath '//a[node()]', @parser.parse('a:parent')
99
+ end
100
+
101
+ def test_standard_nth_selectors
102
+ assert_xpath '//a[position() = 99]', @parser.parse('a:nth-of-type(99)')
103
+ assert_xpath '//a[position() = 1]', @parser.parse('a:first-of-type()')
104
+ assert_xpath '//a[position() = last()]', @parser.parse('a:last-of-type()')
105
+ assert_xpath '//a[position() = 1]', @parser.parse('a:first-of-type') # no parens
106
+ assert_xpath '//a[position() = last()]', @parser.parse('a:last-of-type') # no parens
107
+ assert_xpath '//a[position() = last() - 99]', @parser.parse('a:nth-last-of-type(99)')
108
+ assert_xpath '//a[position() = last() - 99]', @parser.parse('a:nth-last-of-type(99)')
109
+ end
110
+
111
+ def test_nth_child_selectors
112
+ assert_xpath '//*[position() = 1 and self::a]', @parser.parse('a:first-child')
113
+ assert_xpath '//*[position() = last() and self::a]', @parser.parse('a:last-child')
114
+ assert_xpath '//*[position() = 99 and self::a]', @parser.parse('a:nth-child(99)')
115
+ assert_xpath '//*[position() = last() - 99 and self::a]', @parser.parse('a:nth-last-child(99)')
116
+ end
117
+
118
+ def test_miscellaneous_selectors
119
+ assert_xpath '//*[last() = 1 and self::a]',
120
+ @parser.parse('a:only-child')
121
+ assert_xpath '//a[last() = 1]', @parser.parse('a:only-of-type')
122
+ assert_xpath '//a[not(node())]', @parser.parse('a:empty')
123
+ end
124
+
125
+ def test_nth_a_n_plus_b
126
+ assert_xpath '//a[(position() mod 2) = 0]', @parser.parse('a:nth-of-type(2n)')
127
+ assert_xpath '//a[(position() >= 1) and (((position()-1) mod 2) = 0)]', @parser.parse('a:nth-of-type(2n+1)')
128
+ assert_xpath '//a[(position() mod 2) = 0]', @parser.parse('a:nth-of-type(even)')
129
+ assert_xpath '//a[(position() >= 1) and (((position()-1) mod 2) = 0)]', @parser.parse('a:nth-of-type(odd)')
130
+ assert_xpath '//a[(position() >= 3) and (((position()-3) mod 4) = 0)]', @parser.parse('a:nth-of-type(4n+3)')
131
+ assert_xpath '//a[(position() <= 3) and (((position()-3) mod 1) = 0)]', @parser.parse('a:nth-of-type(-1n+3)')
132
+ assert_xpath '//a[(position() <= 3) and (((position()-3) mod 1) = 0)]', @parser.parse('a:nth-of-type(-n+3)')
133
+ assert_xpath '//a[(position() >= 3) and (((position()-3) mod 1) = 0)]', @parser.parse('a:nth-of-type(1n+3)')
134
+ assert_xpath '//a[(position() >= 3) and (((position()-3) mod 1) = 0)]', @parser.parse('a:nth-of-type(n+3)')
135
+ end
136
+
137
+ def test_preceding_selector
138
+ assert_xpath "//F[preceding-sibling::E]",
139
+ @parser.parse("E ~ F")
140
+ end
141
+
142
+ def test_direct_preceding_selector
143
+ assert_xpath "//E/following-sibling::*[1]/self::F",
144
+ @parser.parse("E + F")
145
+ end
146
+
147
+ def test_attribute
148
+ assert_xpath "//h1[@a = 'Tender Lovemaking']",
149
+ @parser.parse("h1[a='Tender Lovemaking']")
150
+ end
151
+
152
+ def test_id
153
+ assert_xpath "//*[@id = 'foo']", @parser.parse('#foo')
154
+ end
155
+
156
+ def test_pseudo_class_no_ident
157
+ assert_xpath "//*[1 = 1]", @parser.parse(':link')
158
+ end
159
+
160
+ def test_pseudo_class
161
+ assert_xpath "//a[1 = 1]", @parser.parse('a:link')
162
+ assert_xpath "//a[1 = 1]", @parser.parse('a:visited')
163
+ assert_xpath "//a[1 = 1]", @parser.parse('a:hover')
164
+ assert_xpath "//a[1 = 1]", @parser.parse('a:active')
165
+ assert_xpath "//a[1 = 1 and contains(concat(' ', @class, ' '), ' foo ')]",
166
+ @parser.parse('a:active.foo')
167
+ end
168
+
169
+ def test_star
170
+ assert_xpath "//*", @parser.parse('*')
171
+ assert_xpath "//*[contains(concat(' ', @class, ' '), ' pastoral ')]",
172
+ @parser.parse('*.pastoral')
173
+ end
174
+
175
+ def test_class
176
+ assert_xpath "//*[contains(concat(' ', @class, ' '), ' a ') and contains(concat(' ', @class, ' '), ' b ')]",
177
+ @parser.parse('.a.b')
178
+ assert_xpath "//*[contains(concat(' ', @class, ' '), ' awesome ')]",
179
+ @parser.parse('.awesome')
180
+ assert_xpath "//foo[contains(concat(' ', @class, ' '), ' awesome ')]",
181
+ @parser.parse('foo.awesome')
182
+ assert_xpath "//foo//*[contains(concat(' ', @class, ' '), ' awesome ')]",
183
+ @parser.parse('foo .awesome')
184
+ end
185
+
186
+ def test_ident
187
+ assert_xpath '//x', @parser.parse('x')
188
+ end
189
+
190
+ def test_parse_space
191
+ assert_xpath '//x//y', @parser.parse('x y')
192
+ end
193
+
194
+ def test_parse_descendant
195
+ assert_xpath '//x/y', @parser.parse('x > y')
196
+ end
197
+
198
+ def test_parse_slash
199
+ ## This is non standard CSS
200
+ assert_xpath '//x/y', @parser.parse('x/y')
201
+ end
202
+
203
+ def test_parse_doubleslash
204
+ ## This is non standard CSS
205
+ assert_xpath '//x//y', @parser.parse('x//y')
206
+ end
207
+
208
+ def test_multi_path
209
+ assert_xpath ['//x/y', '//y/z'], @parser.parse('x > y, y > z')
210
+ assert_xpath ['//x/y', '//y/z'], @parser.parse('x > y,y > z')
211
+ ###
212
+ # TODO: should we make this work?
213
+ # assert_xpath ['//x/y', '//y/z'], @parser.parse('x > y | y > z')
214
+ end
215
+
216
+ def assert_xpath expecteds, asts
217
+ expecteds = [expecteds].flatten
218
+ expecteds.zip(asts).each do |expected, actual|
219
+ assert_equal expected, actual.to_xpath
220
+ end
221
+ end
222
+ end
223
+ end
224
+ end
@@ -0,0 +1,162 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', "helper"))
2
+
3
+ module Nokogiri
4
+ module CSS
5
+ class TestTokenizer < Nokogiri::TestCase
6
+ def setup
7
+ @scanner = Nokogiri::CSS::Tokenizer.new
8
+ end
9
+
10
+ def test_not_equal
11
+ @scanner.scan("h1[a!='Tender Lovemaking']")
12
+ assert_tokens([ [:IDENT, 'h1'],
13
+ ['[', '['],
14
+ [:IDENT, 'a'],
15
+ [:NOT_EQUAL, '!='],
16
+ [:STRING, "'Tender Lovemaking'"],
17
+ [']', ']'],
18
+ ], @scanner)
19
+ end
20
+
21
+ def test_function
22
+ @scanner.scan("script comment()")
23
+ assert_tokens([ [:IDENT, 'script'],
24
+ [:S, ' '],
25
+ [:FUNCTION, 'comment('],
26
+ [')', ')'],
27
+ ], @scanner)
28
+ end
29
+
30
+ def test_preceding_selector
31
+ @scanner.scan("E ~ F")
32
+ assert_tokens([ [:IDENT, 'E'],
33
+ [:TILDE, ' ~'],
34
+ [:S, ' '],
35
+ [:IDENT, 'F'],
36
+ ], @scanner)
37
+ end
38
+
39
+ def test_scan_attribute_string
40
+ @scanner.scan("h1[a='Tender Lovemaking']")
41
+ assert_tokens([ [:IDENT, 'h1'],
42
+ ['[', '['],
43
+ [:IDENT, 'a'],
44
+ ['=', '='],
45
+ [:STRING, "'Tender Lovemaking'"],
46
+ [']', ']'],
47
+ ], @scanner)
48
+ @scanner.scan('h1[a="Tender Lovemaking"]')
49
+ assert_tokens([ [:IDENT, 'h1'],
50
+ ['[', '['],
51
+ [:IDENT, 'a'],
52
+ ['=', '='],
53
+ [:STRING, '"Tender Lovemaking"'],
54
+ [']', ']'],
55
+ ], @scanner)
56
+ end
57
+
58
+ def test_scan_id
59
+ @scanner.scan('#foo')
60
+ assert_tokens([ [:HASH, '#foo'] ], @scanner)
61
+ end
62
+
63
+ def test_scan_pseudo
64
+ @scanner.scan('a:visited')
65
+ assert_tokens([ [:IDENT, 'a'],
66
+ [':', ':'],
67
+ [:IDENT, 'visited']
68
+ ], @scanner)
69
+ end
70
+
71
+ def test_scan_star
72
+ @scanner.scan('*')
73
+ assert_tokens([ ['*', '*'], ], @scanner)
74
+ end
75
+
76
+ def test_scan_class
77
+ @scanner.scan('x.awesome')
78
+ assert_tokens([ [:IDENT, 'x'],
79
+ ['.', '.'],
80
+ [:IDENT, 'awesome'],
81
+ ], @scanner)
82
+ end
83
+
84
+ def test_scan_greater
85
+ @scanner.scan('x > y')
86
+ assert_tokens([ [:IDENT, 'x'],
87
+ [:GREATER, ' >'],
88
+ [:S, ' '],
89
+ [:IDENT, 'y']
90
+ ], @scanner)
91
+ end
92
+
93
+ def test_scan_slash
94
+ @scanner.scan('x/y')
95
+ assert_tokens([ [:IDENT, 'x'],
96
+ [:SLASH, '/'],
97
+ [:IDENT, 'y']
98
+ ], @scanner)
99
+ end
100
+
101
+ def test_scan_doubleslash
102
+ @scanner.scan('x//y')
103
+ assert_tokens([ [:IDENT, 'x'],
104
+ [:DOUBLESLASH, '//'],
105
+ [:IDENT, 'y']
106
+ ], @scanner)
107
+ end
108
+
109
+ def test_scan_function_selector
110
+ @scanner.scan('x:eq(0)')
111
+ assert_tokens([ [:IDENT, 'x'],
112
+ [':', ':'],
113
+ [:FUNCTION, 'eq('],
114
+ [:NUMBER, "0"],
115
+ [")", ")"]
116
+ ], @scanner)
117
+ end
118
+
119
+ def test_scan_an_plus_b
120
+ @scanner.scan('x:nth-child(5n+3)')
121
+ assert_tokens([ [:IDENT, 'x'],
122
+ [':', ':'],
123
+ [:FUNCTION, 'nth-child('],
124
+ [:NUMBER, '5'],
125
+ [:IDENT, 'n'],
126
+ [:PLUS, '+'],
127
+ [:NUMBER, '3'],
128
+ [")", ")"]
129
+ ], @scanner)
130
+
131
+ @scanner.scan('x:nth-child(-1n+3)')
132
+ assert_tokens([ [:IDENT, 'x'],
133
+ [':', ':'],
134
+ [:FUNCTION, 'nth-child('],
135
+ [:NUMBER, '-1'],
136
+ [:IDENT, 'n'],
137
+ [:PLUS, '+'],
138
+ [:NUMBER, '3'],
139
+ [")", ")"]
140
+ ], @scanner)
141
+
142
+ @scanner.scan('x:nth-child(-n+3)')
143
+ assert_tokens([ [:IDENT, 'x'],
144
+ [':', ':'],
145
+ [:FUNCTION, 'nth-child('],
146
+ [:IDENT, '-n'],
147
+ [:PLUS, '+'],
148
+ [:NUMBER, '3'],
149
+ [")", ")"]
150
+ ], @scanner)
151
+ end
152
+
153
+ def assert_tokens(tokens, scanner)
154
+ toks = []
155
+ while tok = @scanner.next_token
156
+ toks << tok
157
+ end
158
+ assert_equal(tokens, toks)
159
+ end
160
+ end
161
+ end
162
+ end
@@ -0,0 +1,54 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', "helper"))
2
+
3
+ module Nokogiri
4
+ module CSS
5
+ class TestXPathVisitor < Nokogiri::TestCase
6
+ def setup
7
+ @parser = Nokogiri::CSS::Parser.new
8
+ end
9
+
10
+ def test_class_selectors
11
+ assert_xpath "//*[contains(concat(' ', @class, ' '), ' red ')]",
12
+ @parser.parse(".red")
13
+ end
14
+
15
+ def test_pipe
16
+ assert_xpath "//a[@id = 'Boing' or starts-with(@id, concat('Boing', '-'))]",
17
+ @parser.parse("a[id|='Boing']")
18
+ end
19
+
20
+ def test_custom_functions
21
+ visitor = Class.new(XPathVisitor) do
22
+ attr_accessor :awesome
23
+ def visit_function_aaron node
24
+ @awesome = true
25
+ 'aaron() = 1'
26
+ end
27
+ end.new
28
+ ast = @parser.parse('a:aaron()').first
29
+ assert_equal 'a[aaron() = 1]', visitor.accept(ast)
30
+ assert visitor.awesome
31
+ end
32
+
33
+ def test_custom_psuedo_classes
34
+ visitor = Class.new(XPathVisitor) do
35
+ attr_accessor :awesome
36
+ def visit_pseudo_class_aaron node
37
+ @awesome = true
38
+ 'aaron() = 1'
39
+ end
40
+ end.new
41
+ ast = @parser.parse('a:aaron').first
42
+ assert_equal 'a[aaron() = 1]', visitor.accept(ast)
43
+ assert visitor.awesome
44
+ end
45
+
46
+ def assert_xpath expecteds, asts
47
+ expecteds = [expecteds].flatten
48
+ expecteds.zip(asts).each do |expected, actual|
49
+ assert_equal expected, actual.to_xpath
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,59 @@
1
+ <?xml version="1.0"?><?TEST-STYLE PIDATA?>
2
+ <!DOCTYPE staff SYSTEM "staff.dtd" [
3
+ <!ENTITY ent1 "es">
4
+ <!ENTITY ent2 "1900 Dallas Road">
5
+ <!ENTITY ent3 "Texas">
6
+ <!ENTITY ent4 "<entElement domestic='Yes'>Element data</entElement><?PItarget PIdata?>">
7
+ <!ENTITY ent5 PUBLIC "entityURI" "entityFile" NDATA notation1>
8
+ <!ENTITY ent1 "This entity should be discarded">
9
+ <!ELEMENT br EMPTY>
10
+ <!ATTLIST br width CDATA "0">
11
+ <!NOTATION notation1 PUBLIC "notation1File">
12
+ <!NOTATION notation2 SYSTEM "notation2File">
13
+ ]>
14
+ <!-- This is comment number 1.-->
15
+ <staff>
16
+ <employee>
17
+ <employeeId>EMP0001</employeeId>
18
+ <name>Margaret Martin</name>
19
+ <position>Accountant</position>
20
+ <salary>56,000</salary>
21
+ <gender>Female</gender>
22
+ <address domestic="Yes">1230 North Ave. Dallas, Texas 98551</address>
23
+ </employee>
24
+ <employee>
25
+ <employeeId>EMP0002</employeeId>
26
+ <name>Martha Raynolds<![CDATA[This is a CDATASection with EntityReference number 2 &ent2;]]>
27
+ <![CDATA[This is an adjacent CDATASection with a reference to a tab &tab;]]></name>
28
+ <position>Secretary</position>
29
+ <salary>35,000</salary>
30
+ <gender>Female</gender>
31
+ <address domestic="Yes" street="Yes">&ent2; Dallas, &ent3;
32
+ 98554</address>
33
+ </employee>
34
+ <employee>
35
+ <employeeId>EMP0003</employeeId>
36
+ <name>Roger
37
+ Jones</name>
38
+ <position>Department Manager</position>
39
+ <salary>100,000</salary>
40
+ <gender>&ent4;</gender>
41
+ <address domestic="Yes" street="No">PO Box 27 Irving, texas 98553</address>
42
+ </employee>
43
+ <employee>
44
+ <employeeId>EMP0004</employeeId>
45
+ <name>Jeny Oconnor</name>
46
+ <position>Personnel Director</position>
47
+ <salary>95,000</salary>
48
+ <gender>Female</gender>
49
+ <address domestic="Yes" street="Y&ent1;">27 South Road. Dallas, Texas 98556</address>
50
+ </employee>
51
+ <employee>
52
+ <employeeId>EMP0005</employeeId>
53
+ <name>Robert Myers</name>
54
+ <position>Computer Specialist</position>
55
+ <salary>90,000</salary>
56
+ <gender>male</gender>
57
+ <address street="Yes">1821 Nordic. Road, Irving Texas 98558</address>
58
+ </employee>
59
+ </staff>
@@ -0,0 +1,32 @@
1
+ <?xml version="1.0" encoding="ISO-8859-1"?>
2
+
3
+ <xsl:stylesheet version="1.0"
4
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
5
+
6
+ <xsl:param name="title"/>
7
+
8
+ <xsl:template match="/">
9
+ <html>
10
+ <body>
11
+ <h1><xsl:value-of select="$title"/></h1>
12
+ <table border="1">
13
+ <tr bgcolor="#9acd32">
14
+ <th align="left">Employee ID</th>
15
+ <th align="left">Name</th>
16
+ <th align="left">Position</th>
17
+ <th align="left">Salary</th>
18
+ </tr>
19
+ <xsl:for-each select="staff/employee">
20
+ <tr>
21
+ <td><xsl:value-of select="employeeId"/></td>
22
+ <td><xsl:value-of select="name"/></td>
23
+ <td><xsl:value-of select="position"/></td>
24
+ <td><xsl:value-of select="salary"/></td>
25
+ </tr>
26
+ </xsl:for-each>
27
+ </table>
28
+ </body>
29
+ </html>
30
+ </xsl:template>
31
+
32
+ </xsl:stylesheet>