nokogiri 1.6.2.rc1-x64-mingw32

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.

Files changed (263) hide show
  1. checksums.yaml +7 -0
  2. data/.autotest +26 -0
  3. data/.editorconfig +17 -0
  4. data/.gemtest +0 -0
  5. data/.travis.yml +25 -0
  6. data/CHANGELOG.ja.rdoc +857 -0
  7. data/CHANGELOG.rdoc +880 -0
  8. data/C_CODING_STYLE.rdoc +33 -0
  9. data/Gemfile +21 -0
  10. data/Manifest.txt +371 -0
  11. data/README.ja.rdoc +112 -0
  12. data/README.rdoc +180 -0
  13. data/ROADMAP.md +89 -0
  14. data/Rakefile +351 -0
  15. data/STANDARD_RESPONSES.md +47 -0
  16. data/Y_U_NO_GEMSPEC.md +155 -0
  17. data/bin/nokogiri +78 -0
  18. data/build_all +130 -0
  19. data/dependencies.yml +4 -0
  20. data/ext/nokogiri/depend +358 -0
  21. data/ext/nokogiri/extconf.rb +453 -0
  22. data/ext/nokogiri/html_document.c +170 -0
  23. data/ext/nokogiri/html_document.h +10 -0
  24. data/ext/nokogiri/html_element_description.c +279 -0
  25. data/ext/nokogiri/html_element_description.h +10 -0
  26. data/ext/nokogiri/html_entity_lookup.c +32 -0
  27. data/ext/nokogiri/html_entity_lookup.h +8 -0
  28. data/ext/nokogiri/html_sax_parser_context.c +116 -0
  29. data/ext/nokogiri/html_sax_parser_context.h +11 -0
  30. data/ext/nokogiri/html_sax_push_parser.c +87 -0
  31. data/ext/nokogiri/html_sax_push_parser.h +9 -0
  32. data/ext/nokogiri/nokogiri.c +148 -0
  33. data/ext/nokogiri/nokogiri.h +164 -0
  34. data/ext/nokogiri/xml_attr.c +94 -0
  35. data/ext/nokogiri/xml_attr.h +9 -0
  36. data/ext/nokogiri/xml_attribute_decl.c +70 -0
  37. data/ext/nokogiri/xml_attribute_decl.h +9 -0
  38. data/ext/nokogiri/xml_cdata.c +56 -0
  39. data/ext/nokogiri/xml_cdata.h +9 -0
  40. data/ext/nokogiri/xml_comment.c +54 -0
  41. data/ext/nokogiri/xml_comment.h +9 -0
  42. data/ext/nokogiri/xml_document.c +577 -0
  43. data/ext/nokogiri/xml_document.h +23 -0
  44. data/ext/nokogiri/xml_document_fragment.c +48 -0
  45. data/ext/nokogiri/xml_document_fragment.h +10 -0
  46. data/ext/nokogiri/xml_dtd.c +202 -0
  47. data/ext/nokogiri/xml_dtd.h +10 -0
  48. data/ext/nokogiri/xml_element_content.c +123 -0
  49. data/ext/nokogiri/xml_element_content.h +10 -0
  50. data/ext/nokogiri/xml_element_decl.c +69 -0
  51. data/ext/nokogiri/xml_element_decl.h +9 -0
  52. data/ext/nokogiri/xml_encoding_handler.c +79 -0
  53. data/ext/nokogiri/xml_encoding_handler.h +8 -0
  54. data/ext/nokogiri/xml_entity_decl.c +110 -0
  55. data/ext/nokogiri/xml_entity_decl.h +10 -0
  56. data/ext/nokogiri/xml_entity_reference.c +52 -0
  57. data/ext/nokogiri/xml_entity_reference.h +9 -0
  58. data/ext/nokogiri/xml_io.c +56 -0
  59. data/ext/nokogiri/xml_io.h +11 -0
  60. data/ext/nokogiri/xml_libxml2_hacks.c +112 -0
  61. data/ext/nokogiri/xml_libxml2_hacks.h +12 -0
  62. data/ext/nokogiri/xml_namespace.c +78 -0
  63. data/ext/nokogiri/xml_namespace.h +13 -0
  64. data/ext/nokogiri/xml_node.c +1541 -0
  65. data/ext/nokogiri/xml_node.h +13 -0
  66. data/ext/nokogiri/xml_node_set.c +467 -0
  67. data/ext/nokogiri/xml_node_set.h +14 -0
  68. data/ext/nokogiri/xml_processing_instruction.c +56 -0
  69. data/ext/nokogiri/xml_processing_instruction.h +9 -0
  70. data/ext/nokogiri/xml_reader.c +681 -0
  71. data/ext/nokogiri/xml_reader.h +10 -0
  72. data/ext/nokogiri/xml_relax_ng.c +161 -0
  73. data/ext/nokogiri/xml_relax_ng.h +9 -0
  74. data/ext/nokogiri/xml_sax_parser.c +312 -0
  75. data/ext/nokogiri/xml_sax_parser.h +39 -0
  76. data/ext/nokogiri/xml_sax_parser_context.c +262 -0
  77. data/ext/nokogiri/xml_sax_parser_context.h +10 -0
  78. data/ext/nokogiri/xml_sax_push_parser.c +115 -0
  79. data/ext/nokogiri/xml_sax_push_parser.h +9 -0
  80. data/ext/nokogiri/xml_schema.c +205 -0
  81. data/ext/nokogiri/xml_schema.h +9 -0
  82. data/ext/nokogiri/xml_syntax_error.c +63 -0
  83. data/ext/nokogiri/xml_syntax_error.h +13 -0
  84. data/ext/nokogiri/xml_text.c +52 -0
  85. data/ext/nokogiri/xml_text.h +9 -0
  86. data/ext/nokogiri/xml_xpath_context.c +307 -0
  87. data/ext/nokogiri/xml_xpath_context.h +10 -0
  88. data/ext/nokogiri/xslt_stylesheet.c +270 -0
  89. data/ext/nokogiri/xslt_stylesheet.h +14 -0
  90. data/lib/nokogiri.rb +137 -0
  91. data/lib/nokogiri/2.0/nokogiri.so +0 -0
  92. data/lib/nokogiri/2.1/nokogiri.so +0 -0
  93. data/lib/nokogiri/css.rb +27 -0
  94. data/lib/nokogiri/css/node.rb +52 -0
  95. data/lib/nokogiri/css/parser.rb +715 -0
  96. data/lib/nokogiri/css/parser.y +249 -0
  97. data/lib/nokogiri/css/parser_extras.rb +91 -0
  98. data/lib/nokogiri/css/syntax_error.rb +7 -0
  99. data/lib/nokogiri/css/tokenizer.rb +152 -0
  100. data/lib/nokogiri/css/tokenizer.rex +55 -0
  101. data/lib/nokogiri/css/xpath_visitor.rb +219 -0
  102. data/lib/nokogiri/decorators/slop.rb +35 -0
  103. data/lib/nokogiri/html.rb +37 -0
  104. data/lib/nokogiri/html/builder.rb +35 -0
  105. data/lib/nokogiri/html/document.rb +333 -0
  106. data/lib/nokogiri/html/document_fragment.rb +41 -0
  107. data/lib/nokogiri/html/element_description.rb +23 -0
  108. data/lib/nokogiri/html/element_description_defaults.rb +671 -0
  109. data/lib/nokogiri/html/entity_lookup.rb +13 -0
  110. data/lib/nokogiri/html/sax/parser.rb +52 -0
  111. data/lib/nokogiri/html/sax/parser_context.rb +16 -0
  112. data/lib/nokogiri/html/sax/push_parser.rb +16 -0
  113. data/lib/nokogiri/syntax_error.rb +4 -0
  114. data/lib/nokogiri/version.rb +106 -0
  115. data/lib/nokogiri/xml.rb +73 -0
  116. data/lib/nokogiri/xml/attr.rb +14 -0
  117. data/lib/nokogiri/xml/attribute_decl.rb +18 -0
  118. data/lib/nokogiri/xml/builder.rb +443 -0
  119. data/lib/nokogiri/xml/cdata.rb +11 -0
  120. data/lib/nokogiri/xml/character_data.rb +7 -0
  121. data/lib/nokogiri/xml/document.rb +279 -0
  122. data/lib/nokogiri/xml/document_fragment.rb +112 -0
  123. data/lib/nokogiri/xml/dtd.rb +32 -0
  124. data/lib/nokogiri/xml/element_content.rb +36 -0
  125. data/lib/nokogiri/xml/element_decl.rb +13 -0
  126. data/lib/nokogiri/xml/entity_decl.rb +19 -0
  127. data/lib/nokogiri/xml/namespace.rb +13 -0
  128. data/lib/nokogiri/xml/node.rb +982 -0
  129. data/lib/nokogiri/xml/node/save_options.rb +61 -0
  130. data/lib/nokogiri/xml/node_set.rb +355 -0
  131. data/lib/nokogiri/xml/notation.rb +6 -0
  132. data/lib/nokogiri/xml/parse_options.rb +98 -0
  133. data/lib/nokogiri/xml/pp.rb +2 -0
  134. data/lib/nokogiri/xml/pp/character_data.rb +18 -0
  135. data/lib/nokogiri/xml/pp/node.rb +56 -0
  136. data/lib/nokogiri/xml/processing_instruction.rb +8 -0
  137. data/lib/nokogiri/xml/reader.rb +112 -0
  138. data/lib/nokogiri/xml/relax_ng.rb +32 -0
  139. data/lib/nokogiri/xml/sax.rb +4 -0
  140. data/lib/nokogiri/xml/sax/document.rb +171 -0
  141. data/lib/nokogiri/xml/sax/parser.rb +123 -0
  142. data/lib/nokogiri/xml/sax/parser_context.rb +16 -0
  143. data/lib/nokogiri/xml/sax/push_parser.rb +60 -0
  144. data/lib/nokogiri/xml/schema.rb +63 -0
  145. data/lib/nokogiri/xml/syntax_error.rb +47 -0
  146. data/lib/nokogiri/xml/text.rb +9 -0
  147. data/lib/nokogiri/xml/xpath.rb +10 -0
  148. data/lib/nokogiri/xml/xpath/syntax_error.rb +11 -0
  149. data/lib/nokogiri/xml/xpath_context.rb +16 -0
  150. data/lib/nokogiri/xslt.rb +56 -0
  151. data/lib/nokogiri/xslt/stylesheet.rb +25 -0
  152. data/lib/xsd/xmlparser/nokogiri.rb +102 -0
  153. data/suppressions/README.txt +1 -0
  154. data/suppressions/nokogiri_ree-1.8.7.358.supp +61 -0
  155. data/suppressions/nokogiri_ruby-1.8.7.370.supp +0 -0
  156. data/suppressions/nokogiri_ruby-1.9.2.320.supp +28 -0
  157. data/suppressions/nokogiri_ruby-1.9.3.327.supp +28 -0
  158. data/tasks/nokogiri.org.rb +24 -0
  159. data/tasks/test.rb +95 -0
  160. data/test/css/test_nthiness.rb +222 -0
  161. data/test/css/test_parser.rb +358 -0
  162. data/test/css/test_tokenizer.rb +198 -0
  163. data/test/css/test_xpath_visitor.rb +96 -0
  164. data/test/decorators/test_slop.rb +16 -0
  165. data/test/files/2ch.html +108 -0
  166. data/test/files/address_book.rlx +12 -0
  167. data/test/files/address_book.xml +10 -0
  168. data/test/files/atom.xml +344 -0
  169. data/test/files/bar/bar.xsd +4 -0
  170. data/test/files/bogus.xml +0 -0
  171. data/test/files/dont_hurt_em_why.xml +422 -0
  172. data/test/files/encoding.html +82 -0
  173. data/test/files/encoding.xhtml +84 -0
  174. data/test/files/exslt.xml +8 -0
  175. data/test/files/exslt.xslt +35 -0
  176. data/test/files/foo/foo.xsd +4 -0
  177. data/test/files/metacharset.html +10 -0
  178. data/test/files/noencoding.html +47 -0
  179. data/test/files/po.xml +32 -0
  180. data/test/files/po.xsd +66 -0
  181. data/test/files/saml/saml20assertion_schema.xsd +283 -0
  182. data/test/files/saml/saml20protocol_schema.xsd +302 -0
  183. data/test/files/saml/xenc_schema.xsd +146 -0
  184. data/test/files/saml/xmldsig_schema.xsd +318 -0
  185. data/test/files/shift_jis.html +10 -0
  186. data/test/files/shift_jis.xml +5 -0
  187. data/test/files/shift_jis_no_charset.html +9 -0
  188. data/test/files/snuggles.xml +3 -0
  189. data/test/files/staff.dtd +10 -0
  190. data/test/files/staff.xml +59 -0
  191. data/test/files/staff.xslt +32 -0
  192. data/test/files/test_document_url/bar.xml +2 -0
  193. data/test/files/test_document_url/document.dtd +4 -0
  194. data/test/files/test_document_url/document.xml +6 -0
  195. data/test/files/tlm.html +850 -0
  196. data/test/files/to_be_xincluded.xml +2 -0
  197. data/test/files/valid_bar.xml +2 -0
  198. data/test/files/xinclude.xml +4 -0
  199. data/test/helper.rb +164 -0
  200. data/test/html/sax/test_parser.rb +141 -0
  201. data/test/html/sax/test_parser_context.rb +46 -0
  202. data/test/html/test_builder.rb +164 -0
  203. data/test/html/test_document.rb +619 -0
  204. data/test/html/test_document_encoding.rb +148 -0
  205. data/test/html/test_document_fragment.rb +261 -0
  206. data/test/html/test_element_description.rb +105 -0
  207. data/test/html/test_named_characters.rb +14 -0
  208. data/test/html/test_node.rb +196 -0
  209. data/test/html/test_node_encoding.rb +27 -0
  210. data/test/namespaces/test_additional_namespaces_in_builder_doc.rb +14 -0
  211. data/test/namespaces/test_namespaces_in_builder_doc.rb +75 -0
  212. data/test/namespaces/test_namespaces_in_cloned_doc.rb +31 -0
  213. data/test/namespaces/test_namespaces_in_created_doc.rb +75 -0
  214. data/test/namespaces/test_namespaces_in_parsed_doc.rb +66 -0
  215. data/test/test_convert_xpath.rb +135 -0
  216. data/test/test_css_cache.rb +45 -0
  217. data/test/test_encoding_handler.rb +46 -0
  218. data/test/test_memory_leak.rb +156 -0
  219. data/test/test_nokogiri.rb +138 -0
  220. data/test/test_reader.rb +558 -0
  221. data/test/test_soap4r_sax.rb +52 -0
  222. data/test/test_xslt_transforms.rb +279 -0
  223. data/test/xml/node/test_save_options.rb +28 -0
  224. data/test/xml/node/test_subclass.rb +44 -0
  225. data/test/xml/sax/test_parser.rb +382 -0
  226. data/test/xml/sax/test_parser_context.rb +115 -0
  227. data/test/xml/sax/test_push_parser.rb +157 -0
  228. data/test/xml/test_attr.rb +64 -0
  229. data/test/xml/test_attribute_decl.rb +86 -0
  230. data/test/xml/test_builder.rb +315 -0
  231. data/test/xml/test_c14n.rb +161 -0
  232. data/test/xml/test_cdata.rb +48 -0
  233. data/test/xml/test_comment.rb +29 -0
  234. data/test/xml/test_document.rb +934 -0
  235. data/test/xml/test_document_encoding.rb +28 -0
  236. data/test/xml/test_document_fragment.rb +228 -0
  237. data/test/xml/test_dtd.rb +187 -0
  238. data/test/xml/test_dtd_encoding.rb +33 -0
  239. data/test/xml/test_element_content.rb +56 -0
  240. data/test/xml/test_element_decl.rb +73 -0
  241. data/test/xml/test_entity_decl.rb +122 -0
  242. data/test/xml/test_entity_reference.rb +245 -0
  243. data/test/xml/test_namespace.rb +95 -0
  244. data/test/xml/test_node.rb +1155 -0
  245. data/test/xml/test_node_attributes.rb +113 -0
  246. data/test/xml/test_node_encoding.rb +107 -0
  247. data/test/xml/test_node_inheritance.rb +32 -0
  248. data/test/xml/test_node_reparenting.rb +374 -0
  249. data/test/xml/test_node_set.rb +755 -0
  250. data/test/xml/test_parse_options.rb +64 -0
  251. data/test/xml/test_processing_instruction.rb +30 -0
  252. data/test/xml/test_reader_encoding.rb +142 -0
  253. data/test/xml/test_relax_ng.rb +60 -0
  254. data/test/xml/test_schema.rb +129 -0
  255. data/test/xml/test_syntax_error.rb +12 -0
  256. data/test/xml/test_text.rb +45 -0
  257. data/test/xml/test_unparented_node.rb +422 -0
  258. data/test/xml/test_xinclude.rb +83 -0
  259. data/test/xml/test_xpath.rb +376 -0
  260. data/test/xslt/test_custom_functions.rb +133 -0
  261. data/test/xslt/test_exception_handling.rb +37 -0
  262. data/test_all +81 -0
  263. metadata +601 -0
@@ -0,0 +1,28 @@
1
+ {
2
+ <insert_a_suppression_name_here>
3
+ Memcheck:Addr4
4
+ fun:glob_helper
5
+ fun:ruby_glob0
6
+ fun:rb_push_glob
7
+ fun:vm_call_method
8
+ fun:vm_exec_core
9
+ fun:vm_exec
10
+ fun:rb_yield
11
+ fun:rb_ary_each
12
+ fun:vm_call_method
13
+ fun:vm_exec_core
14
+ fun:vm_exec
15
+ fun:vm_call0
16
+ fun:rb_iterate
17
+ fun:rb_block_call
18
+ fun:enum_find_all
19
+ fun:vm_call_method
20
+ fun:vm_exec_core
21
+ fun:vm_exec
22
+ fun:vm_call0
23
+ fun:rb_class_new_instance
24
+ fun:vm_call_method
25
+ fun:vm_exec_core
26
+ fun:vm_exec
27
+ fun:vm_call0
28
+ }
@@ -0,0 +1,28 @@
1
+ {
2
+ <insert_a_suppression_name_here>
3
+ Memcheck:Addr4
4
+ fun:glob_helper
5
+ fun:ruby_glob0
6
+ fun:rb_push_glob
7
+ fun:vm_call_method
8
+ fun:vm_exec_core
9
+ fun:vm_exec
10
+ fun:rb_yield
11
+ fun:rb_ary_each
12
+ fun:vm_call_method
13
+ fun:vm_exec_core
14
+ fun:vm_exec
15
+ fun:vm_call0
16
+ fun:rb_iterate
17
+ fun:rb_block_call
18
+ fun:enum_find_all
19
+ fun:vm_call_method
20
+ fun:vm_exec_core
21
+ fun:vm_exec
22
+ fun:vm_call0
23
+ fun:rb_class_new_instance
24
+ fun:vm_call_method
25
+ fun:vm_exec_core
26
+ fun:vm_exec
27
+ fun:vm_call0
28
+ }
@@ -0,0 +1,24 @@
1
+ #
2
+ # note that this file will only work if you've got the `nokogiri.org`
3
+ # repo checked out, and you've got an rvm gemset "1.8.7@nokogiri"
4
+ # bundled with both nokogiri's and nokogiri.org's gems.
5
+ #
6
+ namespace :docs do
7
+ desc "generate HTML docs for nokogiri.org"
8
+ task :website do
9
+ system 'rvm use 1.8.7@nokogiri' # see above
10
+ title = "#{HOE.name}-#{HOE.version} Documentation"
11
+
12
+ options = []
13
+ options << "--main=#{HOE.readme_file}"
14
+ options << '--format=activerecord'
15
+ options << '--threads=1'
16
+ options << "--title=#{title.inspect}"
17
+
18
+ options += HOE.spec.require_paths
19
+ options += HOE.spec.extra_rdoc_files
20
+ require 'rdoc/rdoc'
21
+ ENV['RAILS_ROOT'] ||= File.expand_path(File.join('..', 'nokogiri_ws'))
22
+ RDoc::RDoc.new.document options
23
+ end
24
+ end
@@ -0,0 +1,95 @@
1
+ namespace :test do
2
+ desc "run test suite with aggressive GC"
3
+ task :gc => :build do
4
+ ENV['NOKOGIRI_GC'] = "true"
5
+ Rake::Task["test"].invoke
6
+ end
7
+
8
+ desc "find call-seq in the rdoc"
9
+ task :rdoc_call_seq => 'docs' do
10
+ Dir['doc/**/*.html'].each { |docfile|
11
+ next if docfile =~ /\.src/
12
+ puts "FAIL: #{docfile}" if File.read(docfile) =~ /call-seq/
13
+ }
14
+ end
15
+
16
+ desc "find all undocumented things"
17
+ task :rdoc => 'docs' do
18
+ base = File.expand_path(File.join(File.dirname(__FILE__), '..', 'doc'))
19
+ require 'test/unit'
20
+ test = Class.new(Test::Unit::TestCase)
21
+ Dir["#{base}/**/*.html"].each { |docfile|
22
+ test.class_eval(<<-eotest)
23
+ def test_#{docfile.sub("#{base}/", '').gsub(/[\/\.-]/, '_')}
24
+ assert_no_match(
25
+ /Not documented/,
26
+ File.read('#{docfile}'),
27
+ '#{docfile} has undocumented things'
28
+ )
29
+ end
30
+ eotest
31
+ }
32
+ end
33
+
34
+ desc "Test against multiple versions of libxml2 (MULTIXML2_DIR=directory)"
35
+ task :multixml2 do
36
+ MULTI_XML = File.join(ENV['HOME'], '.multixml2')
37
+ unless File.exists?(MULTI_XML)
38
+ %w{ versions install build }.each { |x|
39
+ FileUtils.mkdir_p(File.join(MULTI_XML, x))
40
+ }
41
+ Dir.chdir File.join(MULTI_XML, 'versions') do
42
+ require 'net/ftp'
43
+ puts "Contacting xmlsoft.org ..."
44
+ ftp = Net::FTP.new('xmlsoft.org')
45
+ ftp.login('anonymous', 'anonymous')
46
+ ftp.chdir('libxml2')
47
+ ftp.list('libxml2-2.*.tar.gz').each do |x|
48
+ file = x[/[^\s]*$/]
49
+ puts "Downloading #{file}"
50
+ ftp.getbinaryfile(file)
51
+ end
52
+ end
53
+ end
54
+
55
+ # Build any libxml2 versions in $HOME/.multixml2/versions that
56
+ # haven't been built yet
57
+ Dir[File.join(MULTI_XML, 'versions','*.tar.gz')].each do |f|
58
+ filename = File.basename(f, '.tar.gz')
59
+
60
+ install_dir = File.join(MULTI_XML, 'install', filename)
61
+ next if File.exists?(install_dir)
62
+
63
+ Dir.chdir File.join(MULTI_XML, 'versions') do
64
+ system "tar zxvf #{f} -C #{File.join(MULTI_XML, 'build')}"
65
+ end
66
+
67
+ Dir.chdir File.join(MULTI_XML, 'build', filename) do
68
+ system "./configure --without-http --prefix=#{install_dir}"
69
+ system "make && make install"
70
+ end
71
+ end
72
+
73
+ test_results = {}
74
+ libxslt = Dir[File.join(MULTI_XML, 'install', 'libxslt*')].first
75
+
76
+ directories = ENV['MULTIXML2_DIR'] ? [ENV['MULTIXML2_DIR']] : Dir[File.join(MULTI_XML, 'install', '*')]
77
+ directories.sort.reverse_each do |xml2_version|
78
+ next unless xml2_version =~ /libxml2/
79
+ extopts = "--with-xml2-include=#{xml2_version}/include/libxml2 --with-xml2-lib=#{xml2_version}/lib --with-xslt-dir=#{libxslt} --with-iconv-dir=/usr"
80
+ cmd = "#{$0} clean test EXTOPTS='#{extopts}' LD_LIBRARY_PATH='#{xml2_version}/lib'"
81
+
82
+ version = File.basename(xml2_version)
83
+ result = system(cmd)
84
+ test_results[version] = {
85
+ :result => result,
86
+ :cmd => cmd
87
+ }
88
+ end
89
+ test_results.sort_by { |k,v| k }.each do |k,v|
90
+ passed = v[:result]
91
+ puts "#{k}: #{passed ? 'PASS' : 'FAIL'}"
92
+ puts "repro: #{v[:cmd]}" unless passed
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,222 @@
1
+ require "helper"
2
+
3
+ module Nokogiri
4
+ module CSS
5
+ class TestNthiness < Nokogiri::TestCase
6
+ def setup
7
+ super
8
+ doc = <<EOF
9
+ <html>
10
+ <table>
11
+ <tr><td>row1 </td></tr>
12
+ <tr><td>row2 </td></tr>
13
+ <tr><td>row3 </td></tr>
14
+ <tr><td>row4 </td></tr>
15
+ <tr><td>row5 </td></tr>
16
+ <tr><td>row6 </td></tr>
17
+ <tr><td>row7 </td></tr>
18
+ <tr><td>row8 </td></tr>
19
+ <tr><td>row9 </td></tr>
20
+ <tr><td>row10 </td></tr>
21
+ <tr><td>row11 </td></tr>
22
+ <tr><td>row12 </td></tr>
23
+ <tr><td>row13 </td></tr>
24
+ <tr><td>row14 </td></tr>
25
+ </table>
26
+ <div>
27
+ <b>bold1 </b>
28
+ <i>italic1 </i>
29
+ <b class="a">bold2 </b>
30
+ <em class="a">emphasis1 </em>
31
+ <i>italic2 </i>
32
+ <p>para1 </p>
33
+ <b class="a">bold3 </b>
34
+ </div>
35
+ <div>
36
+ <i class="b">italic3 </i>
37
+ <em>emphasis2 </em>
38
+ <i class="b">italic4 </i>
39
+ <em>emphasis3 </em>
40
+ <i class="c">italic5 </i>
41
+ <span><i class="b">italic6 </i></span>
42
+ <i>italic7 </i>
43
+ </div>
44
+ <div>
45
+ <p>para2 </p>
46
+ <p>para3 </p>
47
+ </div>
48
+ <div>
49
+ <p>para4 </p>
50
+ </div>
51
+
52
+ <div>
53
+ <h2></h2>
54
+ <h1 class='c'>header1 </h1>
55
+ <h2></h2>
56
+ </div>
57
+ <div>
58
+ <h1 class='c'>header2 </h1>
59
+ <h1 class='c'>header3 </h1>
60
+ </div>
61
+ <div>
62
+ <h1 class='c'>header4</h1>
63
+ </div>
64
+
65
+ <p class='empty'></p>
66
+ <p class='not-empty'><b></b></p>
67
+ </html>
68
+ EOF
69
+ @parser = Nokogiri.HTML doc
70
+ end
71
+
72
+
73
+ def test_even
74
+ assert_result_rows [2,4,6,8,10,12,14], @parser.search("table/tr:nth(even)")
75
+ end
76
+
77
+ def test_odd
78
+ assert_result_rows [1,3,5,7,9,11,13], @parser.search("table/tr:nth(odd)")
79
+ end
80
+
81
+ def test_2n
82
+ assert_equal @parser.search("table/tr:nth(even)").inner_text, @parser.search("table/tr:nth(2n)").inner_text
83
+ end
84
+
85
+ def test_2np1
86
+ assert_equal @parser.search("table/tr:nth(odd)").inner_text, @parser.search("table/tr:nth(2n+1)").inner_text
87
+ end
88
+
89
+ def test_4np3
90
+ assert_result_rows [3,7,11], @parser.search("table/tr:nth(4n+3)")
91
+ end
92
+
93
+ def test_3np4
94
+ assert_result_rows [4,7,10,13], @parser.search("table/tr:nth(3n+4)")
95
+ end
96
+
97
+ def test_mnp3
98
+ assert_result_rows [1,2,3], @parser.search("table/tr:nth(-n+3)")
99
+ end
100
+
101
+ def test_4nm1
102
+ assert_result_rows [3,7,11], @parser.search("table/tr:nth(4n-1)")
103
+ end
104
+
105
+ def test_np3
106
+ assert_result_rows [3,4,5,6,7,8,9,10,11,12,13,14], @parser.search("table/tr:nth(n+3)")
107
+ end
108
+
109
+ def test_first
110
+ assert_result_rows [1], @parser.search("table/tr:first")
111
+ assert_result_rows [1], @parser.search("table/tr:first()")
112
+ end
113
+
114
+ def test_last
115
+ assert_result_rows [14], @parser.search("table/tr:last")
116
+ assert_result_rows [14], @parser.search("table/tr:last()")
117
+ end
118
+
119
+ def test_first_child
120
+ assert_result_rows [1], @parser.search("div/b:first-child"), "bold"
121
+ assert_result_rows [1], @parser.search("table/tr:first-child")
122
+ assert_result_rows [2,4], @parser.search("div/h1.c:first-child"), "header"
123
+ end
124
+
125
+ def test_last_child
126
+ assert_result_rows [3], @parser.search("div/b:last-child"), "bold"
127
+ assert_result_rows [14], @parser.search("table/tr:last-child")
128
+ assert_result_rows [3,4], @parser.search("div/h1.c:last-child"), "header"
129
+ end
130
+
131
+ def test_nth_child
132
+ assert_result_rows [2], @parser.search("div/b:nth-child(3)"), "bold"
133
+ assert_result_rows [5], @parser.search("table/tr:nth-child(5)")
134
+ assert_result_rows [1,3], @parser.search("div/h1.c:nth-child(2)"), "header"
135
+ assert_result_rows [3,4], @parser.search("div/i.b:nth-child(2n+1)"), "italic"
136
+ end
137
+
138
+ def test_first_of_type
139
+ assert_result_rows [1], @parser.search("table/tr:first-of-type")
140
+ assert_result_rows [1], @parser.search("div/b:first-of-type"), "bold"
141
+ assert_result_rows [2], @parser.search("div/b.a:first-of-type"), "bold"
142
+ assert_result_rows [3], @parser.search("div/i.b:first-of-type"), "italic"
143
+ end
144
+
145
+ def test_last_of_type
146
+ assert_result_rows [14], @parser.search("table/tr:last-of-type")
147
+ assert_result_rows [3], @parser.search("div/b:last-of-type"), "bold"
148
+ assert_result_rows [2,7], @parser.search("div/i:last-of-type"), "italic"
149
+ assert_result_rows [2,6,7], @parser.search("div i:last-of-type"), "italic"
150
+ assert_result_rows [4], @parser.search("div/i.b:last-of-type"), "italic"
151
+ end
152
+
153
+ def test_nth_of_type
154
+ assert_result_rows [1], @parser.search("div/b:nth-of-type(1)"), "bold"
155
+ assert_result_rows [2], @parser.search("div/b:nth-of-type(2)"), "bold"
156
+ assert_result_rows [2], @parser.search("div/.a:nth-of-type(1)"), "bold"
157
+ assert_result_rows [2,4,7], @parser.search("div i:nth-of-type(2n)"), "italic"
158
+ assert_result_rows [1,3,5,6], @parser.search("div i:nth-of-type(2n+1)"), "italic"
159
+ assert_result_rows [1], @parser.search("div .a:nth-of-type(2n)"), "emphasis"
160
+ assert_result_rows [2,3], @parser.search("div .a:nth-of-type(2n+1)"), "bold"
161
+ end
162
+
163
+ def test_nth_last_of_type
164
+ assert_result_rows [14], @parser.search("table/tr:nth-last-of-type(1)")
165
+ assert_result_rows [12], @parser.search("table/tr:nth-last-of-type(3)")
166
+ assert_result_rows [2,6,7], @parser.search("div i:nth-last-of-type(1)"), "italic"
167
+ assert_result_rows [1,5], @parser.search("div i:nth-last-of-type(2)"), "italic"
168
+ assert_result_rows [4], @parser.search("div/i.b:nth-last-of-type(1)"), "italic"
169
+ assert_result_rows [3], @parser.search("div/i.b:nth-last-of-type(2)"), "italic"
170
+ end
171
+
172
+ def test_only_of_type
173
+ assert_result_rows [1,4], @parser.search("div/p:only-of-type"), "para"
174
+ assert_result_rows [5], @parser.search("div/i.c:only-of-type"), "italic"
175
+ end
176
+
177
+ def test_only_child
178
+ assert_result_rows [4], @parser.search("div/p:only-child"), "para"
179
+ assert_result_rows [4], @parser.search("div/h1.c:only-child"), "header"
180
+ end
181
+
182
+ def test_empty
183
+ result = @parser.search("p:empty")
184
+ assert_equal 1, result.size, "unexpected number of rows returned: '#{result.inner_text}'"
185
+ assert_equal 'empty', result.first['class']
186
+ end
187
+
188
+ def test_parent
189
+ result = @parser.search("p:parent")
190
+ assert_equal 5, result.size
191
+ 0.upto(3) do |j|
192
+ assert_equal "para#{j+1} ", result[j].inner_text
193
+ end
194
+ assert_equal "not-empty", result[4]['class']
195
+ end
196
+
197
+ def test_siblings
198
+ doc = <<-EOF
199
+ <html><body><div>
200
+ <p id="1">p1 </p>
201
+ <p id="2">p2 </p>
202
+ <p id="3">p3 </p>
203
+ <p id="4">p4 </p>
204
+ <p id="5">p5 </p>
205
+ EOF
206
+ parser = Nokogiri.HTML doc
207
+ assert_equal 2, parser.search("#3 ~ p").size
208
+ assert_equal "p4 p5 ", parser.search("#3 ~ p").inner_text
209
+ assert_equal 0, parser.search("#5 ~ p").size
210
+
211
+ assert_equal 1, parser.search("#3 + p").size
212
+ assert_equal "p4 ", parser.search("#3 + p").inner_text
213
+ assert_equal 0, parser.search("#5 + p").size
214
+ end
215
+
216
+ def assert_result_rows intarray, result, word="row"
217
+ assert_equal intarray.size, result.size, "unexpected number of rows returned: '#{result.inner_text}'"
218
+ assert_equal intarray.map{|j| "#{word}#{j}"}.join(' '), result.inner_text.strip, result.inner_text
219
+ end
220
+ end
221
+ end
222
+ end
@@ -0,0 +1,358 @@
1
+ require "helper"
2
+
3
+ module Nokogiri
4
+ module CSS
5
+ class TestParser < Nokogiri::TestCase
6
+ def setup
7
+ super
8
+ @parser = Nokogiri::CSS::Parser.new
9
+ @parser_with_ns = Nokogiri::CSS::Parser.new({
10
+ "xmlns" => "http://default.example.com/",
11
+ "hoge" => "http://hoge.example.com/",
12
+ })
13
+ end
14
+
15
+ def test_extra_single_quote
16
+ assert_raises(CSS::SyntaxError) { @parser.parse("'") }
17
+ end
18
+
19
+ def test_syntax_error_raised
20
+ assert_raises(CSS::SyntaxError) { @parser.parse("a[x=]") }
21
+ end
22
+
23
+ def test_function_and_pseudo
24
+ assert_xpath '//child::text()[position() = 99]', @parser.parse('text():nth-of-type(99)')
25
+ end
26
+
27
+ def test_find_by_type
28
+ ast = @parser.parse("a:nth-child(2)").first
29
+ matches = ast.find_by_type(
30
+ [:CONDITIONAL_SELECTOR,
31
+ [:ELEMENT_NAME],
32
+ [:PSEUDO_CLASS,
33
+ [:FUNCTION]
34
+ ]
35
+ ]
36
+ )
37
+ assert_equal(1, matches.length)
38
+ assert_equal(ast, matches.first)
39
+ end
40
+
41
+ def test_to_type
42
+ ast = @parser.parse("a:nth-child(2)").first
43
+ assert_equal(
44
+ [:CONDITIONAL_SELECTOR,
45
+ [:ELEMENT_NAME],
46
+ [:PSEUDO_CLASS,
47
+ [:FUNCTION]
48
+ ]
49
+ ], ast.to_type
50
+ )
51
+ end
52
+
53
+ def test_to_a
54
+ asts = @parser.parse("a:nth-child(2)")
55
+ assert_equal(
56
+ [:CONDITIONAL_SELECTOR,
57
+ [:ELEMENT_NAME, ["a"]],
58
+ [:PSEUDO_CLASS,
59
+ [:FUNCTION, ["nth-child("], ["2"]]
60
+ ]
61
+ ], asts.first.to_a
62
+ )
63
+ end
64
+
65
+ def test_has
66
+ assert_xpath "//a[b]", @parser.parse("a:has(b)")
67
+ assert_xpath "//a[b/c]", @parser.parse("a:has(b > c)")
68
+ end
69
+
70
+ def test_dashmatch
71
+ assert_xpath "//a[@class = 'bar' or starts-with(@class, concat('bar', '-'))]",
72
+ @parser.parse("a[@class|='bar']")
73
+ assert_xpath "//a[@class = 'bar' or starts-with(@class, concat('bar', '-'))]",
74
+ @parser.parse("a[@class |= 'bar']")
75
+ end
76
+
77
+ def test_includes
78
+ assert_xpath "//a[contains(concat(\" \", @class, \" \"),concat(\" \", 'bar', \" \"))]",
79
+ @parser.parse("a[@class~='bar']")
80
+ assert_xpath "//a[contains(concat(\" \", @class, \" \"),concat(\" \", 'bar', \" \"))]",
81
+ @parser.parse("a[@class ~= 'bar']")
82
+ end
83
+
84
+ def test_function_with_arguments
85
+ assert_xpath "//a[count(preceding-sibling::*) = 1]",
86
+ @parser.parse("a[2]")
87
+ assert_xpath "//a[count(preceding-sibling::*) = 1]",
88
+ @parser.parse("a:nth-child(2)")
89
+ end
90
+
91
+ def test_carrot
92
+ assert_xpath "//a[starts-with(@id, 'Boing')]",
93
+ @parser.parse("a[id^='Boing']")
94
+ assert_xpath "//a[starts-with(@id, 'Boing')]",
95
+ @parser.parse("a[id ^= 'Boing']")
96
+ end
97
+
98
+ def test_suffix_match
99
+ assert_xpath "//a[substring(@id, string-length(@id) - string-length('Boing') + 1, string-length('Boing')) = 'Boing']",
100
+ @parser.parse("a[id$='Boing']")
101
+ assert_xpath "//a[substring(@id, string-length(@id) - string-length('Boing') + 1, string-length('Boing')) = 'Boing']",
102
+ @parser.parse("a[id $= 'Boing']")
103
+ end
104
+
105
+ def test_attributes_with_at
106
+ ## This is non standard CSS
107
+ assert_xpath "//a[@id = 'Boing']",
108
+ @parser.parse("a[@id='Boing']")
109
+ assert_xpath "//a[@id = 'Boing']",
110
+ @parser.parse("a[@id = 'Boing']")
111
+ end
112
+
113
+ def test_attributes_with_at_and_stuff
114
+ ## This is non standard CSS
115
+ assert_xpath "//a[@id = 'Boing']//div",
116
+ @parser.parse("a[@id='Boing'] div")
117
+ end
118
+
119
+ def test_not_equal
120
+ ## This is non standard CSS
121
+ assert_xpath "//a[child::text() != 'Boing']",
122
+ @parser.parse("a[text()!='Boing']")
123
+ assert_xpath "//a[child::text() != 'Boing']",
124
+ @parser.parse("a[text() != 'Boing']")
125
+ end
126
+
127
+ def test_function
128
+ ## This is non standard CSS
129
+ assert_xpath "//a[child::text()]",
130
+ @parser.parse("a[text()]")
131
+
132
+ ## This is non standard CSS
133
+ assert_xpath "//child::text()",
134
+ @parser.parse("text()")
135
+
136
+ ## This is non standard CSS
137
+ assert_xpath "//a[contains(child::text(), 'Boing')]",
138
+ @parser.parse("a[text()*='Boing']")
139
+ assert_xpath "//a[contains(child::text(), 'Boing')]",
140
+ @parser.parse("a[text() *= 'Boing']")
141
+
142
+ ## This is non standard CSS
143
+ assert_xpath "//script//comment()",
144
+ @parser.parse("script comment()")
145
+ end
146
+
147
+ def test_nonstandard_nth_selectors
148
+ ## These are non standard CSS
149
+ assert_xpath '//a[position() = 1]', @parser.parse('a:first()')
150
+ assert_xpath '//a[position() = 1]', @parser.parse('a:first') # no parens
151
+ assert_xpath '//a[position() = 99]', @parser.parse('a:eq(99)')
152
+ assert_xpath '//a[position() = 99]', @parser.parse('a:nth(99)')
153
+ assert_xpath '//a[position() = last()]', @parser.parse('a:last()')
154
+ assert_xpath '//a[position() = last()]', @parser.parse('a:last') # no parens
155
+ assert_xpath '//a[node()]', @parser.parse('a:parent')
156
+ end
157
+
158
+ def test_standard_nth_selectors
159
+ assert_xpath '//a[position() = 1]', @parser.parse('a:first-of-type()')
160
+ assert_xpath '//a[position() = 1]', @parser.parse('a:first-of-type') # no parens
161
+ assert_xpath "//a[contains(concat(' ', normalize-space(@class), ' '), ' b ')][position() = 1]",
162
+ @parser.parse('a.b:first-of-type') # no parens
163
+ assert_xpath '//a[position() = 99]', @parser.parse('a:nth-of-type(99)')
164
+ assert_xpath "//a[contains(concat(' ', normalize-space(@class), ' '), ' b ')][position() = 99]",
165
+ @parser.parse('a.b:nth-of-type(99)')
166
+ assert_xpath '//a[position() = last()]', @parser.parse('a:last-of-type()')
167
+ assert_xpath '//a[position() = last()]', @parser.parse('a:last-of-type') # no parens
168
+ assert_xpath "//a[contains(concat(' ', normalize-space(@class), ' '), ' b ')][position() = last()]",
169
+ @parser.parse('a.b:last-of-type') # no parens
170
+ assert_xpath '//a[position() = last()]', @parser.parse('a:nth-last-of-type(1)')
171
+ assert_xpath '//a[position() = last() - 98]', @parser.parse('a:nth-last-of-type(99)')
172
+ assert_xpath "//a[contains(concat(' ', normalize-space(@class), ' '), ' b ')][position() = last() - 98]",
173
+ @parser.parse('a.b:nth-last-of-type(99)')
174
+ end
175
+
176
+ def test_nth_child_selectors
177
+ assert_xpath '//a[count(preceding-sibling::*) = 0]', @parser.parse('a:first-child')
178
+ assert_xpath '//a[count(preceding-sibling::*) = 98]', @parser.parse('a:nth-child(99)')
179
+ assert_xpath '//a[count(following-sibling::*) = 0]', @parser.parse('a:last-child')
180
+ assert_xpath '//a[count(following-sbiling::*) = 0]', @parser.parse('a:nth-last-child(1)')
181
+ assert_xpath '//a[count(following-sbiling::*) = 98]', @parser.parse('a:nth-last-child(99)')
182
+ end
183
+
184
+ def test_miscellaneous_selectors
185
+ assert_xpath '//a[count(preceding-sibling::*) = 0 and count(following-sibling::*) = 0]', @parser.parse('a:only-child')
186
+ assert_xpath '//a[last() = 1]', @parser.parse('a:only-of-type')
187
+ assert_xpath '//a[not(node())]', @parser.parse('a:empty')
188
+ end
189
+
190
+ def test_nth_a_n_plus_b
191
+ assert_xpath '//a[(position() mod 2) = 0]', @parser.parse('a:nth-of-type(2n)')
192
+ assert_xpath '//a[(position() >= 1) and (((position()-1) mod 2) = 0)]', @parser.parse('a:nth-of-type(2n+1)')
193
+ assert_xpath '//a[(position() mod 2) = 0]', @parser.parse('a:nth-of-type(even)')
194
+ assert_xpath '//a[(position() >= 1) and (((position()-1) mod 2) = 0)]', @parser.parse('a:nth-of-type(odd)')
195
+ assert_xpath '//a[(position() >= 3) and (((position()-3) mod 4) = 0)]', @parser.parse('a:nth-of-type(4n+3)')
196
+ assert_xpath '//a[(position() <= 3) and (((position()-3) mod 1) = 0)]', @parser.parse('a:nth-of-type(-1n+3)')
197
+ assert_xpath '//a[(position() <= 3) and (((position()-3) mod 1) = 0)]', @parser.parse('a:nth-of-type(-n+3)')
198
+ assert_xpath '//a[(position() >= 3) and (((position()-3) mod 1) = 0)]', @parser.parse('a:nth-of-type(1n+3)')
199
+ assert_xpath '//a[(position() >= 3) and (((position()-3) mod 1) = 0)]', @parser.parse('a:nth-of-type(n+3)')
200
+
201
+ assert_xpath '//a[((last()-position()+1) mod 2) = 0]', @parser.parse('a:nth-last-of-type(2n)')
202
+ assert_xpath '//a[((last()-position()+1) >= 1) and ((((last()-position()+1)-1) mod 2) = 0)]', @parser.parse('a:nth-last-of-type(2n+1)')
203
+ assert_xpath '//a[((last()-position()+1) mod 2) = 0]', @parser.parse('a:nth-last-of-type(even)')
204
+ assert_xpath '//a[((last()-position()+1) >= 1) and ((((last()-position()+1)-1) mod 2) = 0)]', @parser.parse('a:nth-last-of-type(odd)')
205
+ assert_xpath '//a[((last()-position()+1) >= 3) and ((((last()-position()+1)-3) mod 4) = 0)]', @parser.parse('a:nth-last-of-type(4n+3)')
206
+ assert_xpath '//a[((last()-position()+1) <= 3) and ((((last()-position()+1)-3) mod 1) = 0)]', @parser.parse('a:nth-last-of-type(-1n+3)')
207
+ assert_xpath '//a[((last()-position()+1) <= 3) and ((((last()-position()+1)-3) mod 1) = 0)]', @parser.parse('a:nth-last-of-type(-n+3)')
208
+ assert_xpath '//a[((last()-position()+1) >= 3) and ((((last()-position()+1)-3) mod 1) = 0)]', @parser.parse('a:nth-last-of-type(1n+3)')
209
+ assert_xpath '//a[((last()-position()+1) >= 3) and ((((last()-position()+1)-3) mod 1) = 0)]', @parser.parse('a:nth-last-of-type(n+3)')
210
+ end
211
+
212
+ def test_preceding_selector
213
+ assert_xpath "//E/following-sibling::F",
214
+ @parser.parse("E ~ F")
215
+
216
+ assert_xpath "//E/following-sibling::F//G",
217
+ @parser.parse("E ~ F G")
218
+ end
219
+
220
+ def test_direct_preceding_selector
221
+ assert_xpath "//E/following-sibling::*[1]/self::F",
222
+ @parser.parse("E + F")
223
+
224
+ assert_xpath "//E/following-sibling::*[1]/self::F//G",
225
+ @parser.parse("E + F G")
226
+ end
227
+
228
+ def test_child_selector
229
+ assert_xpath("//a//b/i", @parser.parse('a b>i'))
230
+ assert_xpath("//a//b/i", @parser.parse('a b > i'))
231
+ assert_xpath("//a/b/i", @parser.parse('a > b > i'))
232
+ end
233
+
234
+ def test_prefixless_child_selector
235
+ assert_xpath("./a", @parser.parse('>a'))
236
+ assert_xpath("./a", @parser.parse('> a'))
237
+ assert_xpath("./a//b/i", @parser.parse('>a b>i'))
238
+ assert_xpath("./a/b/i", @parser.parse('> a > b > i'))
239
+ end
240
+
241
+ def test_prefixless_preceding_sibling_selector
242
+ assert_xpath("./following-sibling::a", @parser.parse('~a'))
243
+ assert_xpath("./following-sibling::a", @parser.parse('~ a'))
244
+ assert_xpath("./following-sibling::a//b/following-sibling::i", @parser.parse('~a b~i'))
245
+ assert_xpath("./following-sibling::a//b/following-sibling::i", @parser.parse('~ a b ~ i'))
246
+ end
247
+
248
+ def test_prefixless_direct_adjacent_selector
249
+ assert_xpath("./following-sibling::*[1]/self::a", @parser.parse('+a'))
250
+ assert_xpath("./following-sibling::*[1]/self::a", @parser.parse('+ a'))
251
+ assert_xpath("./following-sibling::*[1]/self::a/following-sibling::*[1]/self::b", @parser.parse('+a+b'))
252
+ assert_xpath("./following-sibling::*[1]/self::a/following-sibling::*[1]/self::b", @parser.parse('+ a + b'))
253
+ end
254
+
255
+ def test_attribute
256
+ assert_xpath "//h1[@a = 'Tender Lovemaking']",
257
+ @parser.parse("h1[a='Tender Lovemaking']")
258
+ end
259
+
260
+ def test_id
261
+ assert_xpath "//*[@id = 'foo']", @parser.parse('#foo')
262
+ end
263
+
264
+ def test_pseudo_class_no_ident
265
+ assert_xpath "//*[link(.)]", @parser.parse(':link')
266
+ end
267
+
268
+ def test_pseudo_class
269
+ assert_xpath "//a[link(.)]", @parser.parse('a:link')
270
+ assert_xpath "//a[visited(.)]", @parser.parse('a:visited')
271
+ assert_xpath "//a[hover(.)]", @parser.parse('a:hover')
272
+ assert_xpath "//a[active(.)]", @parser.parse('a:active')
273
+ assert_xpath "//a[active(.) and contains(concat(' ', normalize-space(@class), ' '), ' foo ')]",
274
+ @parser.parse('a:active.foo')
275
+ end
276
+
277
+ def test_star
278
+ assert_xpath "//*", @parser.parse('*')
279
+ assert_xpath "//*[contains(concat(' ', normalize-space(@class), ' '), ' pastoral ')]",
280
+ @parser.parse('*.pastoral')
281
+ end
282
+
283
+ def test_class
284
+ assert_xpath "//*[contains(concat(' ', normalize-space(@class), ' '), ' a ') and contains(concat(' ', normalize-space(@class), ' '), ' b ')]",
285
+ @parser.parse('.a.b')
286
+ assert_xpath "//*[contains(concat(' ', normalize-space(@class), ' '), ' awesome ')]",
287
+ @parser.parse('.awesome')
288
+ assert_xpath "//foo[contains(concat(' ', normalize-space(@class), ' '), ' awesome ')]",
289
+ @parser.parse('foo.awesome')
290
+ assert_xpath "//foo//*[contains(concat(' ', normalize-space(@class), ' '), ' awesome ')]",
291
+ @parser.parse('foo .awesome')
292
+ end
293
+
294
+ def test_bare_not
295
+ assert_xpath "//*[not(contains(concat(' ', normalize-space(@class), ' '), ' a '))]",
296
+ @parser.parse(':not(.a)')
297
+ end
298
+
299
+ def test_not_so_simple_not
300
+ assert_xpath "//*[@id = 'p' and not(contains(concat(' ', normalize-space(@class), ' '), ' a '))]",
301
+ @parser.parse('#p:not(.a)')
302
+ assert_xpath "//p[contains(concat(' ', normalize-space(@class), ' '), ' a ') and not(contains(concat(' ', normalize-space(@class), ' '), ' b '))]",
303
+ @parser.parse('p.a:not(.b)')
304
+ assert_xpath "//p[@a = 'foo' and not(contains(concat(' ', normalize-space(@class), ' '), ' b '))]",
305
+ @parser.parse("p[a='foo']:not(.b)")
306
+ end
307
+
308
+ def test_multiple_not
309
+ assert_xpath "//p[not(contains(concat(' ', normalize-space(@class), ' '), ' a ')) and not(contains(concat(' ', normalize-space(@class), ' '), ' b ')) and not(contains(concat(' ', normalize-space(@class), ' '), ' c '))]",
310
+ @parser.parse("p:not(.a):not(.b):not(.c)")
311
+ end
312
+
313
+ def test_ident
314
+ assert_xpath '//x', @parser.parse('x')
315
+ end
316
+
317
+ def test_parse_space
318
+ assert_xpath '//x//y', @parser.parse('x y')
319
+ end
320
+
321
+ def test_parse_descendant
322
+ assert_xpath '//x/y', @parser.parse('x > y')
323
+ end
324
+
325
+ def test_parse_slash
326
+ ## This is non standard CSS
327
+ assert_xpath '//x/y', @parser.parse('x/y')
328
+ end
329
+
330
+ def test_parse_doubleslash
331
+ ## This is non standard CSS
332
+ assert_xpath '//x//y', @parser.parse('x//y')
333
+ end
334
+
335
+ def test_multi_path
336
+ assert_xpath ['//x/y', '//y/z'], @parser.parse('x > y, y > z')
337
+ assert_xpath ['//x/y', '//y/z'], @parser.parse('x > y,y > z')
338
+ ###
339
+ # TODO: should we make this work?
340
+ # assert_xpath ['//x/y', '//y/z'], @parser.parse('x > y | y > z')
341
+ end
342
+
343
+ def test_attributes_with_namespace
344
+ ## Default namespace is not applied to attributes.
345
+ ## So this must be @class, not @xmlns:class.
346
+ assert_xpath "//xmlns:a[@class = 'bar']", @parser_with_ns.parse("a[class='bar']")
347
+ assert_xpath "//xmlns:a[@hoge:class = 'bar']", @parser_with_ns.parse("a[hoge|class='bar']")
348
+ end
349
+
350
+ def assert_xpath expecteds, asts
351
+ expecteds = [expecteds].flatten
352
+ expecteds.zip(asts).each do |expected, actual|
353
+ assert_equal expected, actual.to_xpath
354
+ end
355
+ end
356
+ end
357
+ end
358
+ end