nokogiri 1.0.0-x86-mswin32-60
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of nokogiri might be problematic. Click here for more details.
- data/History.txt +6 -0
- data/Manifest.txt +120 -0
- data/README.ja.txt +86 -0
- data/README.txt +87 -0
- data/Rakefile +264 -0
- data/ext/nokogiri/extconf.rb +59 -0
- data/ext/nokogiri/html_document.c +83 -0
- data/ext/nokogiri/html_document.h +10 -0
- data/ext/nokogiri/html_sax_parser.c +32 -0
- data/ext/nokogiri/html_sax_parser.h +11 -0
- data/ext/nokogiri/iconv.dll +0 -0
- data/ext/nokogiri/libexslt.dll +0 -0
- data/ext/nokogiri/libxml2.dll +0 -0
- data/ext/nokogiri/libxslt.dll +0 -0
- data/ext/nokogiri/native.c +40 -0
- data/ext/nokogiri/native.h +51 -0
- data/ext/nokogiri/native.so +0 -0
- data/ext/nokogiri/xml_cdata.c +52 -0
- data/ext/nokogiri/xml_cdata.h +9 -0
- data/ext/nokogiri/xml_document.c +159 -0
- data/ext/nokogiri/xml_document.h +10 -0
- data/ext/nokogiri/xml_dtd.c +117 -0
- data/ext/nokogiri/xml_dtd.h +8 -0
- data/ext/nokogiri/xml_node.c +709 -0
- data/ext/nokogiri/xml_node.h +15 -0
- data/ext/nokogiri/xml_node_set.c +124 -0
- data/ext/nokogiri/xml_node_set.h +9 -0
- data/ext/nokogiri/xml_reader.c +429 -0
- data/ext/nokogiri/xml_reader.h +10 -0
- data/ext/nokogiri/xml_sax_parser.c +174 -0
- data/ext/nokogiri/xml_sax_parser.h +10 -0
- data/ext/nokogiri/xml_syntax_error.c +194 -0
- data/ext/nokogiri/xml_syntax_error.h +11 -0
- data/ext/nokogiri/xml_text.c +29 -0
- data/ext/nokogiri/xml_text.h +9 -0
- data/ext/nokogiri/xml_xpath.c +46 -0
- data/ext/nokogiri/xml_xpath.h +11 -0
- data/ext/nokogiri/xml_xpath_context.c +81 -0
- data/ext/nokogiri/xml_xpath_context.h +9 -0
- data/ext/nokogiri/xslt_stylesheet.c +108 -0
- data/ext/nokogiri/xslt_stylesheet.h +9 -0
- data/ext/nokogiri/zlib1.dll +0 -0
- data/lib/nokogiri.rb +51 -0
- data/lib/nokogiri/css.rb +6 -0
- data/lib/nokogiri/css/generated_parser.rb +653 -0
- data/lib/nokogiri/css/generated_tokenizer.rb +159 -0
- data/lib/nokogiri/css/node.rb +95 -0
- data/lib/nokogiri/css/parser.rb +24 -0
- data/lib/nokogiri/css/parser.y +198 -0
- data/lib/nokogiri/css/tokenizer.rb +9 -0
- data/lib/nokogiri/css/tokenizer.rex +63 -0
- data/lib/nokogiri/css/xpath_visitor.rb +165 -0
- data/lib/nokogiri/decorators.rb +1 -0
- data/lib/nokogiri/decorators/hpricot.rb +3 -0
- data/lib/nokogiri/decorators/hpricot/node.rb +58 -0
- data/lib/nokogiri/decorators/hpricot/node_set.rb +14 -0
- data/lib/nokogiri/decorators/hpricot/xpath_visitor.rb +17 -0
- data/lib/nokogiri/hpricot.rb +47 -0
- data/lib/nokogiri/html.rb +95 -0
- data/lib/nokogiri/html/builder.rb +9 -0
- data/lib/nokogiri/html/document.rb +9 -0
- data/lib/nokogiri/html/sax/parser.rb +21 -0
- data/lib/nokogiri/version.rb +3 -0
- data/lib/nokogiri/xml.rb +67 -0
- data/lib/nokogiri/xml/after_handler.rb +18 -0
- data/lib/nokogiri/xml/before_handler.rb +32 -0
- data/lib/nokogiri/xml/builder.rb +79 -0
- data/lib/nokogiri/xml/cdata.rb +9 -0
- data/lib/nokogiri/xml/document.rb +30 -0
- data/lib/nokogiri/xml/dtd.rb +6 -0
- data/lib/nokogiri/xml/element.rb +6 -0
- data/lib/nokogiri/xml/entity_declaration.rb +9 -0
- data/lib/nokogiri/xml/node.rb +195 -0
- data/lib/nokogiri/xml/node_set.rb +183 -0
- data/lib/nokogiri/xml/notation.rb +6 -0
- data/lib/nokogiri/xml/reader.rb +14 -0
- data/lib/nokogiri/xml/sax.rb +9 -0
- data/lib/nokogiri/xml/sax/document.rb +59 -0
- data/lib/nokogiri/xml/sax/parser.rb +33 -0
- data/lib/nokogiri/xml/syntax_error.rb +21 -0
- data/lib/nokogiri/xml/text.rb +6 -0
- data/lib/nokogiri/xml/xpath.rb +6 -0
- data/lib/nokogiri/xml/xpath_context.rb +14 -0
- data/lib/nokogiri/xslt.rb +11 -0
- data/lib/nokogiri/xslt/stylesheet.rb +6 -0
- data/nokogiri.gemspec +34 -0
- data/test/css/test_nthiness.rb +159 -0
- data/test/css/test_parser.rb +224 -0
- data/test/css/test_tokenizer.rb +162 -0
- data/test/css/test_xpath_visitor.rb +54 -0
- data/test/files/staff.xml +59 -0
- data/test/files/staff.xslt +32 -0
- data/test/files/tlm.html +850 -0
- data/test/helper.rb +70 -0
- data/test/hpricot/files/basic.xhtml +17 -0
- data/test/hpricot/files/boingboing.html +2266 -0
- data/test/hpricot/files/cy0.html +3653 -0
- data/test/hpricot/files/immob.html +400 -0
- data/test/hpricot/files/pace_application.html +1320 -0
- data/test/hpricot/files/tenderlove.html +16 -0
- data/test/hpricot/files/uswebgen.html +220 -0
- data/test/hpricot/files/utf8.html +1054 -0
- data/test/hpricot/files/week9.html +1723 -0
- data/test/hpricot/files/why.xml +19 -0
- data/test/hpricot/load_files.rb +7 -0
- data/test/hpricot/test_alter.rb +67 -0
- data/test/hpricot/test_builder.rb +27 -0
- data/test/hpricot/test_parser.rb +423 -0
- data/test/hpricot/test_paths.rb +15 -0
- data/test/hpricot/test_preserved.rb +78 -0
- data/test/hpricot/test_xml.rb +30 -0
- data/test/html/sax/test_parser.rb +27 -0
- data/test/html/test_builder.rb +78 -0
- data/test/html/test_document.rb +86 -0
- data/test/test_convert_xpath.rb +180 -0
- data/test/test_nokogiri.rb +36 -0
- data/test/test_reader.rb +222 -0
- data/test/test_xslt_transforms.rb +29 -0
- data/test/xml/sax/test_parser.rb +93 -0
- data/test/xml/test_builder.rb +16 -0
- data/test/xml/test_cdata.rb +18 -0
- data/test/xml/test_document.rb +171 -0
- data/test/xml/test_dtd.rb +43 -0
- data/test/xml/test_node.rb +223 -0
- data/test/xml/test_node_set.rb +116 -0
- data/test/xml/test_text.rb +13 -0
- metadata +217 -0
@@ -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>
|