nokogiri 1.5.6.rc3-java → 1.5.7-java

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 (50) hide show
  1. data/CHANGELOG.ja.rdoc +87 -26
  2. data/CHANGELOG.rdoc +94 -32
  3. data/Manifest.txt +1 -0
  4. data/Rakefile +28 -15
  5. data/build_all +13 -5
  6. data/ext/java/nokogiri/NokogiriService.java +8 -1
  7. data/ext/java/nokogiri/XmlDocument.java +4 -4
  8. data/ext/java/nokogiri/XmlDtd.java +13 -2
  9. data/ext/java/nokogiri/XmlElement.java +3 -12
  10. data/ext/java/nokogiri/XmlEntityReference.java +11 -31
  11. data/ext/java/nokogiri/XmlNode.java +76 -32
  12. data/ext/java/nokogiri/XmlReader.java +257 -181
  13. data/ext/java/nokogiri/XmlSaxPushParser.java +17 -2
  14. data/ext/java/nokogiri/internals/NokogiriHelpers.java +23 -16
  15. data/ext/java/nokogiri/internals/NokogiriNamespaceCache.java +18 -1
  16. data/ext/java/nokogiri/internals/NokogiriNonStrictErrorHandler.java +9 -0
  17. data/ext/java/nokogiri/internals/ReaderNode.java +37 -37
  18. data/ext/java/nokogiri/internals/SaveContextVisitor.java +23 -16
  19. data/ext/java/nokogiri/internals/UncloseableInputStream.java +102 -0
  20. data/ext/java/nokogiri/internals/XmlDomParserContext.java +7 -4
  21. data/ext/nokogiri/extconf.rb +1 -0
  22. data/ext/nokogiri/nokogiri.h +4 -0
  23. data/ext/nokogiri/xml_node.c +33 -1
  24. data/ext/nokogiri/xml_reader.c +0 -3
  25. data/ext/nokogiri/xml_sax_parser.c +4 -1
  26. data/lib/nekodtd.jar +0 -0
  27. data/lib/nokogiri.rb +1 -0
  28. data/lib/nokogiri/css/xpath_visitor.rb +1 -1
  29. data/lib/nokogiri/nokogiri.jar +0 -0
  30. data/lib/nokogiri/version.rb +4 -1
  31. data/lib/nokogiri/xml/builder.rb +12 -2
  32. data/lib/nokogiri/xml/document.rb +3 -1
  33. data/lib/nokogiri/xml/sax/parser.rb +1 -0
  34. data/tasks/cross_compile.rb +15 -15
  35. data/test/css/test_parser.rb +9 -9
  36. data/test/css/test_xpath_visitor.rb +1 -1
  37. data/test/helper.rb +1 -0
  38. data/test/html/sax/test_parser.rb +5 -2
  39. data/test/html/test_document_fragment.rb +4 -2
  40. data/test/namespaces/test_namespaces_in_builder_doc.rb +60 -0
  41. data/test/namespaces/test_namespaces_in_created_doc.rb +62 -0
  42. data/test/namespaces/test_namespaces_in_parsed_doc.rb +60 -0
  43. data/test/test_reader.rb +38 -4
  44. data/test/xml/sax/test_parser.rb +10 -1
  45. data/test/xml/test_builder.rb +40 -1
  46. data/test/xml/test_document.rb +50 -2
  47. data/test/xml/test_entity_reference.rb +2 -4
  48. data/test/xml/test_node.rb +30 -1
  49. data/test_all +2 -2
  50. metadata +142 -232
@@ -0,0 +1,102 @@
1
+ /**
2
+ * (The MIT License)
3
+ *
4
+ * Copyright (c) 2008 - 2012:
5
+ *
6
+ * * {Aaron Patterson}[http://tenderlovemaking.com]
7
+ * * {Mike Dalessio}[http://mike.daless.io]
8
+ * * {Charles Nutter}[http://blog.headius.com]
9
+ * * {Sergio Arbeo}[http://www.serabe.com]
10
+ * * {Patrick Mahoney}[http://polycrystal.org]
11
+ * * {Yoko Harada}[http://yokolet.blogspot.com]
12
+ *
13
+ * Permission is hereby granted, free of charge, to any person obtaining
14
+ * a copy of this software and associated documentation files (the
15
+ * 'Software'), to deal in the Software without restriction, including
16
+ * without limitation the rights to use, copy, modify, merge, publish,
17
+ * distribute, sublicense, and/or sell copies of the Software, and to
18
+ * permit persons to whom the Software is furnished to do so, subject to
19
+ * the following conditions:
20
+ *
21
+ * The above copyright notice and this permission notice shall be
22
+ * included in all copies or substantial portions of the Software.
23
+ *
24
+ * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
25
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
27
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
28
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
29
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
30
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31
+ */
32
+
33
+ package nokogiri.internals;
34
+
35
+ import java.io.IOException;
36
+ import java.io.InputStream;
37
+
38
+ /**
39
+ * Delegates all the methods to another InputStream except the
40
+ * close() method, which is ignored. This is used to fix #495.
41
+ *
42
+ * @author John Shahid <jvshahid@gmail.com>
43
+ */
44
+ public class UncloseableInputStream extends InputStream {
45
+ private final InputStream delegate;
46
+
47
+ /**
48
+ * Create a new uncloseable stream.
49
+ *
50
+ * @param delegate The InputStream to which all methods (except close)
51
+ * will be delegated.
52
+ */
53
+ public UncloseableInputStream(InputStream delegate) {
54
+ this.delegate = delegate;
55
+ }
56
+
57
+ @Override
58
+ public int read() throws IOException {
59
+ return delegate.read();
60
+ }
61
+
62
+ @Override
63
+ public int read(byte []b) throws IOException {
64
+ return delegate.read(b);
65
+ }
66
+
67
+ @Override
68
+ public int read(byte []b, int offset, int len) throws IOException {
69
+ return delegate.read(b, offset, len);
70
+ }
71
+
72
+ @Override
73
+ public long skip(long n) throws IOException {
74
+ return delegate.skip(n);
75
+ }
76
+
77
+ @Override
78
+ public int available() throws IOException {
79
+ return delegate.available();
80
+ }
81
+
82
+ @Override
83
+ public void close() {
84
+ // don't forward this to the InputStream we're delegating from
85
+ // we don't want the InputStream of the RubyIO to be closed
86
+ }
87
+
88
+ @Override
89
+ public void mark(int readlimit) {
90
+ delegate.mark(readlimit);
91
+ }
92
+
93
+ @Override
94
+ public void reset() throws IOException {
95
+ delegate.reset();
96
+ }
97
+
98
+ @Override
99
+ public boolean markSupported() {
100
+ return delegate.markSupported();
101
+ }
102
+ }
@@ -33,7 +33,7 @@
33
33
  package nokogiri.internals;
34
34
 
35
35
  import static nokogiri.internals.NokogiriHelpers.getNokogiriClass;
36
- import java.io.ByteArrayInputStream;
36
+
37
37
  import java.io.IOException;
38
38
  import java.util.ArrayList;
39
39
  import java.util.List;
@@ -53,8 +53,6 @@ import org.jruby.runtime.builtin.IRubyObject;
53
53
  import org.w3c.dom.Document;
54
54
  import org.w3c.dom.Node;
55
55
  import org.w3c.dom.NodeList;
56
- import org.xml.sax.EntityResolver;
57
- import org.xml.sax.InputSource;
58
56
  import org.xml.sax.SAXException;
59
57
 
60
58
  /**
@@ -246,7 +244,12 @@ public class XmlDomParserContext extends ParserContext {
246
244
  }
247
245
 
248
246
  protected Document do_parse() throws SAXException, IOException {
249
- parser.parse(getInputSource());
247
+ try {
248
+ parser.parse(getInputSource());
249
+ } catch (NullPointerException ex) {
250
+ // FIXME: this is really a hack to fix #838. Xerces will throw a NullPointerException
251
+ // if we tried to parse '<? ?>'. We should submit a patch to Xerces.
252
+ }
250
253
  if (options.noBlanks) {
251
254
  List<Node> emptyNodes = new ArrayList<Node>();
252
255
  findEmptyTexts(parser.getDocument(), emptyNodes);
@@ -8,6 +8,7 @@ RbConfig::MAKEFILE_CONFIG['CC'] = ENV['CC'] if ENV['CC']
8
8
 
9
9
  ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..', '..'))
10
10
  LIBDIR = RbConfig::CONFIG['libdir']
11
+ @libdir_basename = "lib" # shrug, ruby 2.0 won't work for me.
11
12
  INCLUDEDIR = RbConfig::CONFIG['includedir']
12
13
 
13
14
  if defined?(RUBY_ENGINE) && RUBY_ENGINE == 'macruby'
@@ -157,4 +157,8 @@ void nokogiri_root_nsdef(xmlNsPtr, xmlDocPtr);
157
157
  # endif
158
158
  #endif
159
159
 
160
+ #define XMLNS_PREFIX "xmlns"
161
+ #define XMLNS_PREFIX_LEN 6 /* including either colon or \0 */
162
+ #define XMLNS_BUFFER_LEN 128
163
+
160
164
  #endif
@@ -23,10 +23,34 @@ typedef xmlNodePtr (*pivot_reparentee_func)(xmlNodePtr, xmlNodePtr);
23
23
  /* :nodoc: */
24
24
  static void relink_namespace(xmlNodePtr reparented)
25
25
  {
26
+ xmlChar *name, *prefix;
26
27
  xmlNodePtr child;
28
+ xmlNsPtr ns;
29
+
30
+ if (reparented->type != XML_ATTRIBUTE_NODE &&
31
+ reparented->type != XML_ELEMENT_NODE) return;
32
+
33
+ if (reparented->ns == NULL || reparented->ns->prefix == NULL) {
34
+ name = xmlSplitQName2(reparented->name, &prefix);
35
+
36
+ if(reparented->type == XML_ATTRIBUTE_NODE) {
37
+ if (prefix == NULL || strcmp((char*)prefix, XMLNS_PREFIX) == 0) return;
38
+ }
39
+
40
+ ns = xmlSearchNs(reparented->doc, reparented, prefix);
41
+
42
+ if (ns == NULL && reparented->parent) {
43
+ ns = xmlSearchNs(reparented->doc, reparented->parent, prefix);
44
+ }
45
+
46
+ if (ns != NULL) {
47
+ xmlNodeSetName(reparented, name);
48
+ xmlSetNs(reparented, ns);
49
+ }
50
+ }
27
51
 
28
52
  /* Avoid segv when relinking against unlinked nodes. */
29
- if(!reparented->parent) return;
53
+ if (reparented->type != XML_ELEMENT_NODE || !reparented->parent) return;
30
54
 
31
55
  /* Make sure that our reparented node has the correct namespaces */
32
56
  if(!reparented->ns && reparented->doc != (xmlDocPtr)reparented->parent)
@@ -70,6 +94,14 @@ static void relink_namespace(xmlNodePtr reparented)
70
94
  relink_namespace(child);
71
95
  child = child->next;
72
96
  }
97
+
98
+ if (reparented->type == XML_ELEMENT_NODE) {
99
+ child = (xmlNodePtr)((xmlElementPtr)reparented)->attributes;
100
+ while(NULL != child) {
101
+ relink_namespace(child);
102
+ child = child->next;
103
+ }
104
+ }
73
105
  }
74
106
 
75
107
  /* :nodoc: */
@@ -25,9 +25,6 @@ static int has_attributes(xmlTextReaderPtr reader)
25
25
  return(0);
26
26
  }
27
27
 
28
- #define XMLNS_PREFIX "xmlns"
29
- #define XMLNS_PREFIX_LEN 6 /* including either colon or \0 */
30
- #define XMLNS_BUFFER_LEN 128
31
28
  static void Nokogiri_xml_node_namespaces(xmlNodePtr node, VALUE attr_hash)
32
29
  {
33
30
  xmlNsPtr ns;
@@ -239,14 +239,17 @@ static void cdata_block(void * ctx, const xmlChar * value, int len)
239
239
 
240
240
  static void processing_instruction(void * ctx, const xmlChar * name, const xmlChar * content)
241
241
  {
242
+ VALUE rb_content;
242
243
  VALUE self = NOKOGIRI_SAX_SELF(ctx);
243
244
  VALUE doc = rb_iv_get(self, "@document");
244
245
 
246
+ rb_content = content ? NOKOGIRI_STR_NEW2(content) : Qnil;
247
+
245
248
  rb_funcall( doc,
246
249
  id_processing_instruction,
247
250
  2,
248
251
  NOKOGIRI_STR_NEW2(name),
249
- NOKOGIRI_STR_NEW2(content)
252
+ rb_content
250
253
  );
251
254
  }
252
255
 
data/lib/nekodtd.jar CHANGED
Binary file
data/lib/nokogiri.rb CHANGED
@@ -16,6 +16,7 @@ if defined?(RUBY_ENGINE) && RUBY_ENGINE == "jruby"
16
16
  # should skip loading xml jars. This is because those are in WEB-INF/lib and
17
17
  # already set in the classpath.
18
18
  unless $LOAD_PATH.to_s.include?("appengine-rack")
19
+ require 'stringio'
19
20
  require 'isorelax.jar'
20
21
  require 'jing.jar'
21
22
  require 'nekohtml.jar'
@@ -120,7 +120,7 @@ module Nokogiri
120
120
  end
121
121
 
122
122
  def visit_class_condition node
123
- "contains(concat(' ', @class, ' '), ' #{node.value.first} ')"
123
+ "contains(concat(' ', normalize-space(@class), ' '), ' #{node.value.first} ')"
124
124
  end
125
125
 
126
126
  {
Binary file
@@ -1,6 +1,6 @@
1
1
  module Nokogiri
2
2
  # The version of Nokogiri you are using
3
- VERSION = '1.5.6.rc3'
3
+ VERSION = '1.5.7'
4
4
 
5
5
  class VersionInfo # :nodoc:
6
6
  def jruby?
@@ -52,6 +52,9 @@ module Nokogiri
52
52
  hash_info['libxml']['compiled'] = compiled_parser_version
53
53
  hash_info['libxml']['loaded'] = loaded_parser_version
54
54
  hash_info['warnings'] = warnings
55
+ elsif jruby?
56
+ hash_info['xerces'] = Nokogiri::XERCES_VERSION
57
+ hash_info['nekohtml'] = Nokogiri::NEKO_VERSION
55
58
  end
56
59
 
57
60
  hash_info
@@ -327,7 +327,8 @@ module Nokogiri
327
327
  return self if @ns
328
328
  end
329
329
 
330
- raise ArgumentError, "Namespace #{ns} has not been defined"
330
+ @ns = { :pending => ns.to_s }
331
+ return self
331
332
  end
332
333
 
333
334
  ###
@@ -355,11 +356,20 @@ module Nokogiri
355
356
  else
356
357
  node = @doc.create_element(method.to_s.sub(/[_!]$/, ''),*args) { |n|
357
358
  # Set up the namespace
358
- if @ns
359
+ if @ns.is_a? Nokogiri::XML::Namespace
359
360
  n.namespace = @ns
360
361
  @ns = nil
361
362
  end
362
363
  }
364
+
365
+ if @ns.is_a? Hash
366
+ node.namespace = node.namespace_definitions.find { |x| x.prefix == @ns[:pending] }
367
+ if node.namespace.nil?
368
+ raise ArgumentError, "Namespace #{@ns[:pending]} has not been defined"
369
+ end
370
+ @ns = nil
371
+ end
372
+
363
373
  insert(node, &block)
364
374
  end
365
375
  end
@@ -87,7 +87,6 @@ module Nokogiri
87
87
  if key =~ NCNAME_RE
88
88
  ns_name = key.split(":", 2)[1]
89
89
  elm.add_namespace_definition ns_name, v
90
- next
91
90
  end
92
91
  elm[k.to_s] = v.to_s
93
92
  }
@@ -95,6 +94,9 @@ module Nokogiri
95
94
  elm.content = arg
96
95
  end
97
96
  end
97
+ if ns = elm.namespace_definitions.find { |n| n.prefix.nil? or n.prefix == '' }
98
+ elm.namespace = ns
99
+ end
98
100
  elm
99
101
  end
100
102
 
@@ -114,6 +114,7 @@ module Nokogiri
114
114
 
115
115
  private
116
116
  def check_encoding(encoding)
117
+ encoding.upcase!
117
118
  raise ArgumentError.new("'#{encoding}' is not a valid encoding") unless ENCODINGS[encoding]
118
119
  end
119
120
  end
@@ -4,10 +4,10 @@ HOST = Rake::ExtensionCompiler.mingw_host
4
4
 
5
5
  require 'mini_portile'
6
6
  $recipes = {}
7
- $recipes[:zlib] = MiniPortile.new "zlib", "1.2.7"
8
- $recipes[:libiconv] = MiniPortile.new "libiconv", "1.13.1"
9
- $recipes[:libxml2] = MiniPortile.new "libxml2", "2.7.7"
10
- $recipes[:libxslt] = MiniPortile.new "libxslt", "1.1.26"
7
+ $recipes["zlib"] = MiniPortile.new "zlib", "1.2.7"
8
+ $recipes["libiconv"] = MiniPortile.new "libiconv", "1.13.1"
9
+ $recipes["libxml2"] = MiniPortile.new "libxml2", "2.7.7"
10
+ $recipes["libxslt"] = MiniPortile.new "libxslt", "1.1.26"
11
11
  $recipes.each { |_, recipe| recipe.host = HOST }
12
12
 
13
13
  file "lib/nokogiri/nokogiri.rb" do
@@ -18,7 +18,7 @@ end
18
18
 
19
19
  namespace :cross do
20
20
  task :zlib do
21
- recipe = $recipes[:zlib]
21
+ recipe = $recipes["zlib"]
22
22
  recipe.files = ["http://zlib.net/#{recipe.name}-#{recipe.version}.tar.gz"]
23
23
  class << recipe
24
24
  def configure
@@ -48,7 +48,7 @@ namespace :cross do
48
48
  end
49
49
  end
50
50
 
51
- checkpoint = "#{CROSS_DIR}/#{recipe.name}-#{recipe.version}.installed"
51
+ checkpoint = "#{CROSS_DIR}/#{recipe.name}-#{recipe.version}-#{recipe.host}.installed"
52
52
  unless File.exist?(checkpoint)
53
53
  recipe.cook
54
54
  touch checkpoint
@@ -57,7 +57,7 @@ namespace :cross do
57
57
  end
58
58
 
59
59
  task :libiconv do
60
- recipe = $recipes[:libiconv]
60
+ recipe = $recipes["libiconv"]
61
61
  recipe.files = ["http://ftp.gnu.org/pub/gnu/libiconv/#{recipe.name}-#{recipe.version}.tar.gz"]
62
62
  recipe.configure_options = [
63
63
  "--host=#{HOST}",
@@ -69,7 +69,7 @@ namespace :cross do
69
69
  "LDFLAGS=-mno-cygwin"
70
70
  ]
71
71
 
72
- checkpoint = "#{CROSS_DIR}/#{recipe.name}-#{recipe.version}.installed"
72
+ checkpoint = "#{CROSS_DIR}/#{recipe.name}-#{recipe.version}-#{recipe.host}.installed"
73
73
  unless File.exist?(checkpoint)
74
74
  recipe.cook
75
75
  touch checkpoint
@@ -78,14 +78,14 @@ namespace :cross do
78
78
  end
79
79
 
80
80
  task :libxml2 => ["cross:zlib", "cross:libiconv"] do
81
- recipe = $recipes[:libxml2]
81
+ recipe = $recipes["libxml2"]
82
82
  recipe.files = ["ftp://ftp.xmlsoft.org/libxml2/#{recipe.name}-#{recipe.version}.tar.gz"]
83
83
  recipe.configure_options = [
84
84
  "--host=#{HOST}",
85
85
  "--enable-static",
86
86
  "--disable-shared",
87
87
  "--with-zlib=#{CROSS_DIR}",
88
- "--with-iconv=#{$recipes[:libiconv].path}",
88
+ "--with-iconv=#{$recipes["libiconv"].path}",
89
89
  "--without-python",
90
90
  "--without-readline",
91
91
  "CFLAGS='-DIN_LIBXML'"
@@ -100,7 +100,7 @@ namespace :cross do
100
100
  end
101
101
  end
102
102
 
103
- checkpoint = "#{CROSS_DIR}/#{recipe.name}-#{recipe.version}.installed"
103
+ checkpoint = "#{CROSS_DIR}/#{recipe.name}-#{recipe.version}-#{recipe.host}.installed"
104
104
  unless File.exist?(checkpoint)
105
105
  recipe.cook
106
106
  touch checkpoint
@@ -109,13 +109,13 @@ namespace :cross do
109
109
  end
110
110
 
111
111
  task :libxslt => ['cross:libxml2'] do
112
- recipe = $recipes[:libxslt]
112
+ recipe = $recipes["libxslt"]
113
113
  recipe.files = ["ftp://ftp.xmlsoft.org/libxml2/#{recipe.name}-#{recipe.version}.tar.gz"]
114
114
  recipe.configure_options = [
115
115
  "--host=#{HOST}",
116
116
  "--enable-static",
117
117
  "--disable-shared",
118
- "--with-libxml-prefix=#{$recipes[:libxml2].path}",
118
+ "--with-libxml-prefix=#{$recipes["libxml2"].path}",
119
119
  "--without-python",
120
120
  "--without-crypto",
121
121
  "CFLAGS='-DIN_LIBXML'"
@@ -130,7 +130,7 @@ namespace :cross do
130
130
  end
131
131
  end
132
132
 
133
- checkpoint = "#{CROSS_DIR}/#{recipe.name}-#{recipe.version}.installed"
133
+ checkpoint = "#{CROSS_DIR}/#{recipe.name}-#{recipe.version}-#{recipe.host}.installed"
134
134
  unless File.exist?(checkpoint)
135
135
  recipe.cook
136
136
  touch checkpoint
@@ -140,7 +140,7 @@ namespace :cross do
140
140
 
141
141
  task :file_list do
142
142
  HOE.spec.files += Dir["lib/nokogiri/nokogiri.rb"]
143
- HOE.spec.files += Dir["lib/nokogiri/1.{8,9}/nokogiri.so"]
143
+ HOE.spec.files += Dir["lib/nokogiri/{1.8,1.9,2.0}/nokogiri.so"]
144
144
  end
145
145
  end
146
146
 
@@ -263,33 +263,33 @@ module Nokogiri
263
263
  assert_xpath "//a[visited(.)]", @parser.parse('a:visited')
264
264
  assert_xpath "//a[hover(.)]", @parser.parse('a:hover')
265
265
  assert_xpath "//a[active(.)]", @parser.parse('a:active')
266
- assert_xpath "//a[active(.) and contains(concat(' ', @class, ' '), ' foo ')]",
266
+ assert_xpath "//a[active(.) and contains(concat(' ', normalize-space(@class), ' '), ' foo ')]",
267
267
  @parser.parse('a:active.foo')
268
268
  end
269
269
 
270
270
  def test_star
271
271
  assert_xpath "//*", @parser.parse('*')
272
- assert_xpath "//*[contains(concat(' ', @class, ' '), ' pastoral ')]",
272
+ assert_xpath "//*[contains(concat(' ', normalize-space(@class), ' '), ' pastoral ')]",
273
273
  @parser.parse('*.pastoral')
274
274
  end
275
275
 
276
276
  def test_class
277
- assert_xpath "//*[contains(concat(' ', @class, ' '), ' a ') and contains(concat(' ', @class, ' '), ' b ')]",
277
+ assert_xpath "//*[contains(concat(' ', normalize-space(@class), ' '), ' a ') and contains(concat(' ', normalize-space(@class), ' '), ' b ')]",
278
278
  @parser.parse('.a.b')
279
- assert_xpath "//*[contains(concat(' ', @class, ' '), ' awesome ')]",
279
+ assert_xpath "//*[contains(concat(' ', normalize-space(@class), ' '), ' awesome ')]",
280
280
  @parser.parse('.awesome')
281
- assert_xpath "//foo[contains(concat(' ', @class, ' '), ' awesome ')]",
281
+ assert_xpath "//foo[contains(concat(' ', normalize-space(@class), ' '), ' awesome ')]",
282
282
  @parser.parse('foo.awesome')
283
- assert_xpath "//foo//*[contains(concat(' ', @class, ' '), ' awesome ')]",
283
+ assert_xpath "//foo//*[contains(concat(' ', normalize-space(@class), ' '), ' awesome ')]",
284
284
  @parser.parse('foo .awesome')
285
285
  end
286
286
 
287
287
  def test_not_so_simple_not
288
- assert_xpath "//*[@id = 'p' and not(contains(concat(' ', @class, ' '), ' a '))]",
288
+ assert_xpath "//*[@id = 'p' and not(contains(concat(' ', normalize-space(@class), ' '), ' a '))]",
289
289
  @parser.parse('#p:not(.a)')
290
- assert_xpath "//p[contains(concat(' ', @class, ' '), ' a ') and not(contains(concat(' ', @class, ' '), ' b '))]",
290
+ assert_xpath "//p[contains(concat(' ', normalize-space(@class), ' '), ' a ') and not(contains(concat(' ', normalize-space(@class), ' '), ' b '))]",
291
291
  @parser.parse('p.a:not(.b)')
292
- assert_xpath "//p[@a = 'foo' and not(contains(concat(' ', @class, ' '), ' b '))]",
292
+ assert_xpath "//p[@a = 'foo' and not(contains(concat(' ', normalize-space(@class), ' '), ' b '))]",
293
293
  @parser.parse("p[a='foo']:not(.b)")
294
294
  end
295
295