tenderlove-nokogiri 0.0.0-x86-mswin32-60

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.
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>