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