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.
- 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/native.c +40 -0
- data/ext/nokogiri/native.h +51 -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/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/css.rb +6 -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/decorators/hpricot.rb +3 -0
- data/lib/nokogiri/decorators.rb +1 -0
- data/lib/nokogiri/hpricot.rb +47 -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/html.rb +95 -0
- data/lib/nokogiri/version.rb +3 -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/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/document.rb +59 -0
- data/lib/nokogiri/xml/sax/parser.rb +33 -0
- data/lib/nokogiri/xml/sax.rb +9 -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/xml.rb +67 -0
- data/lib/nokogiri/xslt/stylesheet.rb +6 -0
- data/lib/nokogiri/xslt.rb +11 -0
- data/lib/nokogiri.rb +51 -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 +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>
|