rxerces 0.1.0 → 0.3.0
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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +4 -0
- data/CHANGES.md +21 -0
- data/Gemfile +7 -0
- data/README.md +6 -2
- data/Rakefile +43 -0
- data/certs/djberg96_pub.pem +26 -0
- data/examples/basic_usage.rb +75 -0
- data/examples/schema_example.rb +107 -0
- data/examples/simple_example.rb +34 -0
- data/examples/xpath_example.rb +108 -0
- data/ext/rxerces/rxerces.cpp +623 -1
- data/lib/rxerces/nokogiri.rb +34 -0
- data/lib/rxerces/version.rb +1 -1
- data/lib/rxerces.rb +0 -31
- data/rxerces.gemspec +33 -0
- data/spec/document_spec.rb +101 -0
- data/spec/element_spec.rb +25 -0
- data/spec/node_spec.rb +472 -0
- data/spec/nodeset_spec.rb +92 -0
- data/spec/nokogiri_compatibility_spec.rb +167 -0
- data/spec/rxerces_shared.rb +10 -0
- data/spec/rxerces_spec.rb +23 -0
- data/spec/schema_spec.rb +76 -0
- data/spec/spec_helper.rb +17 -0
- data/spec/xpath_spec.rb +164 -0
- data.tar.gz.sig +0 -0
- metadata +71 -9
- metadata.gz.sig +2 -0
- data/lib/rxerces/rxerces.bundle +0 -0
data/spec/node_spec.rb
ADDED
|
@@ -0,0 +1,472 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
RSpec.describe RXerces::XML::Node do
|
|
4
|
+
let(:xml) do
|
|
5
|
+
<<-XML
|
|
6
|
+
<root>
|
|
7
|
+
<person id="1" name="Alice">
|
|
8
|
+
<age>30</age>
|
|
9
|
+
<city>New York</city>
|
|
10
|
+
</person>
|
|
11
|
+
<person id="2" name="Bob">
|
|
12
|
+
<age>25</age>
|
|
13
|
+
</person>
|
|
14
|
+
</root>
|
|
15
|
+
XML
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
let(:doc) { RXerces::XML::Document.parse(xml) }
|
|
19
|
+
let(:root) { doc.root }
|
|
20
|
+
|
|
21
|
+
describe "#name" do
|
|
22
|
+
it "returns the node name" do
|
|
23
|
+
expect(root.name).to eq('root')
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it "returns child element names" do
|
|
27
|
+
person = root.children.find { |n| n.is_a?(RXerces::XML::Element) }
|
|
28
|
+
expect(person.name).to eq('person')
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
describe "#text" do
|
|
33
|
+
it "returns text content" do
|
|
34
|
+
person = root.children.find { |n| n.is_a?(RXerces::XML::Element) }
|
|
35
|
+
age = person.children.find { |n| n.name == 'age' }
|
|
36
|
+
expect(age.text.strip).to eq('30')
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it "returns empty string for nodes without text" do
|
|
40
|
+
expect(root.text).to be_a(String)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
describe "#content" do
|
|
45
|
+
it "is an alias for text" do
|
|
46
|
+
person = root.children.find { |n| n.is_a?(RXerces::XML::Element) }
|
|
47
|
+
age = person.children.find { |n| n.name == 'age' }
|
|
48
|
+
expect(age.content).to eq(age.text)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
describe "#text=" do
|
|
53
|
+
it "sets text content" do
|
|
54
|
+
person = root.children.find { |n| n.is_a?(RXerces::XML::Element) }
|
|
55
|
+
age = person.children.find { |n| n.name == 'age' }
|
|
56
|
+
age.text = '35'
|
|
57
|
+
expect(age.text.strip).to eq('35')
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
describe "#[]" do
|
|
62
|
+
it "gets attribute value" do
|
|
63
|
+
person = root.children.find { |n| n.is_a?(RXerces::XML::Element) }
|
|
64
|
+
expect(person['id']).to eq('1')
|
|
65
|
+
expect(person['name']).to eq('Alice')
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
it "returns nil for non-existent attribute" do
|
|
69
|
+
expect(root['nonexistent']).to be_nil
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
describe "#[]=" do
|
|
74
|
+
it "sets attribute value" do
|
|
75
|
+
person = root.children.find { |n| n.is_a?(RXerces::XML::Element) }
|
|
76
|
+
person['id'] = '100'
|
|
77
|
+
expect(person['id']).to eq('100')
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
it "creates new attribute" do
|
|
81
|
+
person = root.children.find { |n| n.is_a?(RXerces::XML::Element) }
|
|
82
|
+
person['email'] = 'alice@example.com'
|
|
83
|
+
expect(person['email']).to eq('alice@example.com')
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
describe "#children" do
|
|
88
|
+
it "returns an array of child nodes" do
|
|
89
|
+
children = root.children
|
|
90
|
+
expect(children).to be_an(Array)
|
|
91
|
+
expect(children.length).to be > 0
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
it "includes element nodes" do
|
|
95
|
+
person_nodes = root.children.select { |n| n.is_a?(RXerces::XML::Element) }
|
|
96
|
+
expect(person_nodes.length).to eq(2)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
it "includes text nodes" do
|
|
100
|
+
text_nodes = root.children.select { |n| n.is_a?(RXerces::XML::Text) }
|
|
101
|
+
expect(text_nodes.length).to be > 0
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
describe "#parent" do
|
|
106
|
+
it "returns the parent node" do
|
|
107
|
+
person = root.children.find { |n| n.is_a?(RXerces::XML::Element) }
|
|
108
|
+
parent = person.parent
|
|
109
|
+
expect(parent).to be_a(RXerces::XML::Element)
|
|
110
|
+
expect(parent.name).to eq('root')
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
it "returns the parent for nested elements" do
|
|
114
|
+
person = root.children.find { |n| n.is_a?(RXerces::XML::Element) }
|
|
115
|
+
age = person.children.find { |n| n.name == 'age' }
|
|
116
|
+
parent = age.parent
|
|
117
|
+
expect(parent).to be_a(RXerces::XML::Element)
|
|
118
|
+
expect(parent.name).to eq('person')
|
|
119
|
+
expect(parent['id']).to eq('1')
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
it "returns the document for root element" do
|
|
123
|
+
parent = root.parent
|
|
124
|
+
expect(parent).not_to be_nil
|
|
125
|
+
expect(parent.name).to eq('#document')
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
it "returns nil for nodes without parent" do
|
|
129
|
+
# This is edge case - all nodes in a parsed document have parents
|
|
130
|
+
# but we test the safety of the method
|
|
131
|
+
expect(root.parent).not_to be_nil
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
describe "#attributes" do
|
|
136
|
+
it "returns a hash of attributes" do
|
|
137
|
+
person = root.children.find { |n| n.is_a?(RXerces::XML::Element) }
|
|
138
|
+
attrs = person.attributes
|
|
139
|
+
expect(attrs).to be_a(Hash)
|
|
140
|
+
expect(attrs['id']).to eq('1')
|
|
141
|
+
expect(attrs['name']).to eq('Alice')
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
it "returns all attributes" do
|
|
145
|
+
person = root.children.find { |n| n.is_a?(RXerces::XML::Element) }
|
|
146
|
+
attrs = person.attributes
|
|
147
|
+
expect(attrs.keys).to match_array(['id', 'name'])
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
it "returns empty hash for elements without attributes" do
|
|
151
|
+
person = root.children.find { |n| n.is_a?(RXerces::XML::Element) }
|
|
152
|
+
age = person.children.find { |n| n.name == 'age' }
|
|
153
|
+
attrs = age.attributes
|
|
154
|
+
expect(attrs).to be_a(Hash)
|
|
155
|
+
expect(attrs).to be_empty
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
it "returns empty hash for text nodes" do
|
|
159
|
+
person = root.children.find { |n| n.is_a?(RXerces::XML::Element) }
|
|
160
|
+
text_node = person.children.find { |n| n.is_a?(RXerces::XML::Text) }
|
|
161
|
+
attrs = text_node.attributes
|
|
162
|
+
expect(attrs).to be_a(Hash)
|
|
163
|
+
expect(attrs).to be_empty
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
describe "#next_sibling" do
|
|
168
|
+
it "returns the next sibling node" do
|
|
169
|
+
people = root.children.select { |n| n.is_a?(RXerces::XML::Element) }
|
|
170
|
+
first_person = people[0]
|
|
171
|
+
next_node = first_person.next_sibling
|
|
172
|
+
|
|
173
|
+
# May be a text node (whitespace) between elements
|
|
174
|
+
# Skip to next element if needed
|
|
175
|
+
while next_node && next_node.is_a?(RXerces::XML::Text)
|
|
176
|
+
next_node = next_node.next_sibling
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
expect(next_node).to be_a(RXerces::XML::Element)
|
|
180
|
+
expect(next_node.name).to eq('person')
|
|
181
|
+
expect(next_node['id']).to eq('2')
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
it "returns nil for last sibling" do
|
|
185
|
+
people = root.children.select { |n| n.is_a?(RXerces::XML::Element) }
|
|
186
|
+
last_person = people.last
|
|
187
|
+
|
|
188
|
+
# Navigate past any trailing whitespace
|
|
189
|
+
next_node = last_person.next_sibling
|
|
190
|
+
while next_node && next_node.is_a?(RXerces::XML::Text)
|
|
191
|
+
next_node = next_node.next_sibling
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
expect(next_node).to be_nil
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
it "can navigate through all siblings" do
|
|
198
|
+
first_element = root.children.find { |n| n.is_a?(RXerces::XML::Element) }
|
|
199
|
+
siblings = []
|
|
200
|
+
current = first_element
|
|
201
|
+
|
|
202
|
+
while current
|
|
203
|
+
siblings << current if current.is_a?(RXerces::XML::Element)
|
|
204
|
+
current = current.next_sibling
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
expect(siblings.length).to eq(2)
|
|
208
|
+
expect(siblings[0]['id']).to eq('1')
|
|
209
|
+
expect(siblings[1]['id']).to eq('2')
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
describe "#previous_sibling" do
|
|
214
|
+
it "returns the previous sibling node" do
|
|
215
|
+
people = root.children.select { |n| n.is_a?(RXerces::XML::Element) }
|
|
216
|
+
second_person = people[1]
|
|
217
|
+
prev_node = second_person.previous_sibling
|
|
218
|
+
|
|
219
|
+
# May be a text node (whitespace) between elements
|
|
220
|
+
# Skip to previous element if needed
|
|
221
|
+
while prev_node && prev_node.is_a?(RXerces::XML::Text)
|
|
222
|
+
prev_node = prev_node.previous_sibling
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
expect(prev_node).to be_a(RXerces::XML::Element)
|
|
226
|
+
expect(prev_node.name).to eq('person')
|
|
227
|
+
expect(prev_node['id']).to eq('1')
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
it "returns nil for first sibling" do
|
|
231
|
+
first_element = root.children.find { |n| n.is_a?(RXerces::XML::Element) }
|
|
232
|
+
|
|
233
|
+
# Navigate past any leading whitespace
|
|
234
|
+
prev_node = first_element.previous_sibling
|
|
235
|
+
while prev_node && prev_node.is_a?(RXerces::XML::Text)
|
|
236
|
+
prev_node = prev_node.previous_sibling
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
expect(prev_node).to be_nil
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
it "can navigate backward through all siblings" do
|
|
243
|
+
last_element = root.children.select { |n| n.is_a?(RXerces::XML::Element) }.last
|
|
244
|
+
siblings = []
|
|
245
|
+
current = last_element
|
|
246
|
+
|
|
247
|
+
while current
|
|
248
|
+
siblings.unshift(current) if current.is_a?(RXerces::XML::Element)
|
|
249
|
+
current = current.previous_sibling
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
expect(siblings.length).to eq(2)
|
|
253
|
+
expect(siblings[0]['id']).to eq('1')
|
|
254
|
+
expect(siblings[1]['id']).to eq('2')
|
|
255
|
+
end
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
describe "#add_child" do
|
|
259
|
+
it "adds a text node from a string" do
|
|
260
|
+
person = root.children.find { |n| n.is_a?(RXerces::XML::Element) }
|
|
261
|
+
initial_count = person.children.length
|
|
262
|
+
|
|
263
|
+
person.add_child("New text content")
|
|
264
|
+
|
|
265
|
+
expect(person.children.length).to eq(initial_count + 1)
|
|
266
|
+
last_child = person.children.last
|
|
267
|
+
expect(last_child).to be_a(RXerces::XML::Text)
|
|
268
|
+
expect(last_child.text).to eq("New text content")
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
it "adds a new element to another element" do
|
|
272
|
+
# Create a simple document to test with
|
|
273
|
+
simple_xml = '<root><parent></parent></root>'
|
|
274
|
+
simple_doc = RXerces::XML::Document.parse(simple_xml)
|
|
275
|
+
parent = simple_doc.root.children.find { |n| n.is_a?(RXerces::XML::Element) }
|
|
276
|
+
|
|
277
|
+
# Add text child
|
|
278
|
+
parent.add_child("Hello World")
|
|
279
|
+
|
|
280
|
+
expect(parent.children.length).to be > 0
|
|
281
|
+
text_child = parent.children.find { |n| n.is_a?(RXerces::XML::Text) }
|
|
282
|
+
expect(text_child.text).to eq("Hello World")
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
it "allows building a document structure" do
|
|
286
|
+
simple_xml = '<root></root>'
|
|
287
|
+
simple_doc = RXerces::XML::Document.parse(simple_xml)
|
|
288
|
+
root = simple_doc.root
|
|
289
|
+
|
|
290
|
+
# Add multiple children
|
|
291
|
+
root.add_child("First text")
|
|
292
|
+
root.add_child("Second text")
|
|
293
|
+
|
|
294
|
+
text_nodes = root.children.select { |n| n.is_a?(RXerces::XML::Text) }
|
|
295
|
+
expect(text_nodes.length).to eq(2)
|
|
296
|
+
expect(text_nodes[0].text).to eq("First text")
|
|
297
|
+
expect(text_nodes[1].text).to eq("Second text")
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
it "modifies the document" do
|
|
301
|
+
simple_xml = '<item></item>'
|
|
302
|
+
simple_doc = RXerces::XML::Document.parse(simple_xml)
|
|
303
|
+
item = simple_doc.root
|
|
304
|
+
|
|
305
|
+
item.add_child("Content")
|
|
306
|
+
|
|
307
|
+
xml_output = simple_doc.to_s
|
|
308
|
+
expect(xml_output).to include("Content")
|
|
309
|
+
end
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
describe "#remove" do
|
|
313
|
+
it "removes a node from its parent" do
|
|
314
|
+
person = root.children.find { |n| n.is_a?(RXerces::XML::Element) }
|
|
315
|
+
initial_count = root.children.select { |n| n.is_a?(RXerces::XML::Element) }.length
|
|
316
|
+
|
|
317
|
+
person.remove
|
|
318
|
+
|
|
319
|
+
remaining = root.children.select { |n| n.is_a?(RXerces::XML::Element) }
|
|
320
|
+
expect(remaining.length).to eq(initial_count - 1)
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
it "removes a child element from parent" do
|
|
324
|
+
person = root.children.find { |n| n.is_a?(RXerces::XML::Element) }
|
|
325
|
+
age = person.children.find { |n| n.name == 'age' }
|
|
326
|
+
initial_count = person.children.select { |n| n.is_a?(RXerces::XML::Element) }.length
|
|
327
|
+
|
|
328
|
+
age.remove
|
|
329
|
+
|
|
330
|
+
remaining = person.children.select { |n| n.is_a?(RXerces::XML::Element) }
|
|
331
|
+
expect(remaining.length).to eq(initial_count - 1)
|
|
332
|
+
expect(person.children.find { |n| n.name == 'age' }).to be_nil
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
it "modifies the document" do
|
|
336
|
+
simple_xml = '<root><item>Remove me</item><keep>Keep me</keep></root>'
|
|
337
|
+
simple_doc = RXerces::XML::Document.parse(simple_xml)
|
|
338
|
+
item = simple_doc.xpath('//item').first
|
|
339
|
+
|
|
340
|
+
item.remove
|
|
341
|
+
|
|
342
|
+
xml_output = simple_doc.to_s
|
|
343
|
+
expect(xml_output).not_to include("Remove me")
|
|
344
|
+
expect(xml_output).to include("Keep me")
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
it "raises error when node has no parent" do
|
|
348
|
+
# The root element's parent is the document, so this should work
|
|
349
|
+
# We'll test with a document node instead
|
|
350
|
+
expect {
|
|
351
|
+
root.parent.remove
|
|
352
|
+
}.to raise_error(RuntimeError, /no parent/)
|
|
353
|
+
end
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
describe "#unlink" do
|
|
357
|
+
it "is an alias for remove" do
|
|
358
|
+
simple_xml = '<root><item>Test</item></root>'
|
|
359
|
+
simple_doc = RXerces::XML::Document.parse(simple_xml)
|
|
360
|
+
item = simple_doc.xpath('//item').first
|
|
361
|
+
|
|
362
|
+
expect(item).to respond_to(:unlink)
|
|
363
|
+
item.unlink
|
|
364
|
+
|
|
365
|
+
xml_output = simple_doc.to_s
|
|
366
|
+
expect(xml_output).not_to include("Test")
|
|
367
|
+
end
|
|
368
|
+
end
|
|
369
|
+
|
|
370
|
+
describe "#xpath" do
|
|
371
|
+
it "returns a NodeSet" do
|
|
372
|
+
result = root.xpath('.//age')
|
|
373
|
+
expect(result).to be_a(RXerces::XML::NodeSet)
|
|
374
|
+
end
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
describe "#inner_html" do
|
|
378
|
+
it "returns the XML content of children without parent tags" do
|
|
379
|
+
person = root.xpath('//person').first
|
|
380
|
+
inner = person.inner_html
|
|
381
|
+
expect(inner).to include('<age>')
|
|
382
|
+
expect(inner).to include('<city>')
|
|
383
|
+
expect(inner).not_to include('<person')
|
|
384
|
+
end
|
|
385
|
+
|
|
386
|
+
it "returns empty string for nodes without children" do
|
|
387
|
+
age = root.xpath('//age').first
|
|
388
|
+
inner = age.inner_html
|
|
389
|
+
expect(inner).to eq('30')
|
|
390
|
+
end
|
|
391
|
+
|
|
392
|
+
it "includes multiple children" do
|
|
393
|
+
person = root.xpath('//person').first
|
|
394
|
+
inner = person.inner_html
|
|
395
|
+
expect(inner).to include('<age>30</age>')
|
|
396
|
+
expect(inner).to include('<city>New York</city>')
|
|
397
|
+
end
|
|
398
|
+
end
|
|
399
|
+
|
|
400
|
+
describe "#inner_xml" do
|
|
401
|
+
it "is an alias for inner_html" do
|
|
402
|
+
person = root.xpath('//person').first
|
|
403
|
+
expect(person.inner_xml).to eq(person.inner_html)
|
|
404
|
+
end
|
|
405
|
+
|
|
406
|
+
it "returns the same XML content" do
|
|
407
|
+
person = root.xpath('//person').first
|
|
408
|
+
inner = person.inner_xml
|
|
409
|
+
expect(inner).to include('<age>')
|
|
410
|
+
expect(inner).to include('<city>')
|
|
411
|
+
end
|
|
412
|
+
end
|
|
413
|
+
|
|
414
|
+
describe "#path" do
|
|
415
|
+
it "returns the XPath to the root element" do
|
|
416
|
+
expect(root.path).to eq('/root[1]')
|
|
417
|
+
end
|
|
418
|
+
|
|
419
|
+
it "returns the XPath to a nested element" do
|
|
420
|
+
person = root.xpath('//person').first
|
|
421
|
+
expect(person.path).to eq('/root[1]/person[1]')
|
|
422
|
+
end
|
|
423
|
+
|
|
424
|
+
it "returns the XPath to the second person element" do
|
|
425
|
+
people = root.xpath('//person')
|
|
426
|
+
second_person = people[1]
|
|
427
|
+
expect(second_person.path).to eq('/root[1]/person[2]')
|
|
428
|
+
end
|
|
429
|
+
|
|
430
|
+
it "returns the XPath to deeply nested elements" do
|
|
431
|
+
age = root.xpath('//age').first
|
|
432
|
+
expect(age.path).to eq('/root[1]/person[1]/age[1]')
|
|
433
|
+
end
|
|
434
|
+
end
|
|
435
|
+
|
|
436
|
+
describe "#blank?" do
|
|
437
|
+
let(:blank_xml) { '<root><empty></empty><whitespace> </whitespace><content>Hello</content></root>' }
|
|
438
|
+
let(:blank_doc) { RXerces::XML::Document.parse(blank_xml) }
|
|
439
|
+
|
|
440
|
+
it "returns false for elements with text content" do
|
|
441
|
+
content = blank_doc.xpath('//content').first
|
|
442
|
+
expect(content.blank?).to be false
|
|
443
|
+
end
|
|
444
|
+
|
|
445
|
+
it "returns true for empty elements" do
|
|
446
|
+
empty = blank_doc.xpath('//empty').first
|
|
447
|
+
expect(empty.blank?).to be true
|
|
448
|
+
end
|
|
449
|
+
|
|
450
|
+
it "returns true for elements with only whitespace" do
|
|
451
|
+
whitespace = blank_doc.xpath('//whitespace').first
|
|
452
|
+
expect(whitespace.blank?).to be true
|
|
453
|
+
end
|
|
454
|
+
|
|
455
|
+
it "returns false for elements with child elements" do
|
|
456
|
+
root = blank_doc.root
|
|
457
|
+
expect(root.blank?).to be false
|
|
458
|
+
end
|
|
459
|
+
|
|
460
|
+
it "returns false for text nodes with content" do
|
|
461
|
+
content = blank_doc.xpath('//content').first
|
|
462
|
+
text_node = content.children.first
|
|
463
|
+
expect(text_node.blank?).to be false
|
|
464
|
+
end
|
|
465
|
+
|
|
466
|
+
it "returns true for text nodes with only whitespace" do
|
|
467
|
+
whitespace = blank_doc.xpath('//whitespace').first
|
|
468
|
+
text_node = whitespace.children.first
|
|
469
|
+
expect(text_node.blank?).to be true
|
|
470
|
+
end
|
|
471
|
+
end
|
|
472
|
+
end
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
RSpec.describe RXerces::XML::NodeSet do
|
|
4
|
+
let(:xml) do
|
|
5
|
+
<<-XML
|
|
6
|
+
<root>
|
|
7
|
+
<item>First</item>
|
|
8
|
+
<item>Second</item>
|
|
9
|
+
<item>Third</item>
|
|
10
|
+
</root>
|
|
11
|
+
XML
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
let(:doc) { RXerces::XML::Document.parse(xml) }
|
|
15
|
+
let(:nodeset) { doc.xpath('//item') }
|
|
16
|
+
let(:empty_nodeset) { doc.xpath('//nonexistent') }
|
|
17
|
+
|
|
18
|
+
describe "#length" do
|
|
19
|
+
it "returns the number of nodes" do
|
|
20
|
+
expect(nodeset.length).to be_a(Integer)
|
|
21
|
+
expect(nodeset.length).to eq(3)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it "returns 0 for empty nodeset" do
|
|
25
|
+
expect(empty_nodeset.length).to eq(0)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
describe "#size" do
|
|
30
|
+
it "is an alias for length" do
|
|
31
|
+
expect(nodeset.size).to eq(nodeset.length)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
describe "#[]" do
|
|
36
|
+
it "returns node at index" do
|
|
37
|
+
item = nodeset[0]
|
|
38
|
+
expect(item).to be_a(RXerces::XML::Element)
|
|
39
|
+
expect(item.text.strip).to eq('First')
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
it "returns nil for out of bounds index" do
|
|
43
|
+
expect(nodeset[999]).to be_nil
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it "returns nil for empty nodeset" do
|
|
47
|
+
expect(empty_nodeset[0]).to be_nil
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
describe "#each" do
|
|
52
|
+
it "is enumerable" do
|
|
53
|
+
expect(nodeset).to respond_to(:each)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
it "returns enumerator when no block given" do
|
|
57
|
+
expect(nodeset.each).to be_a(Enumerator)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
it "yields all items in nodeset" do
|
|
61
|
+
count = 0
|
|
62
|
+
nodeset.each { count += 1 }
|
|
63
|
+
expect(count).to eq(3)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
it "yields nothing for empty nodeset" do
|
|
67
|
+
count = 0
|
|
68
|
+
empty_nodeset.each { count += 1 }
|
|
69
|
+
expect(count).to eq(0)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
it "can iterate and access node properties" do
|
|
73
|
+
texts = []
|
|
74
|
+
nodeset.each do |item|
|
|
75
|
+
texts << item.text.strip
|
|
76
|
+
end
|
|
77
|
+
expect(texts).to eq(['First', 'Second', 'Third'])
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
describe "#to_a" do
|
|
82
|
+
it "converts to array" do
|
|
83
|
+
result = nodeset.to_a
|
|
84
|
+
expect(result).to be_an(Array)
|
|
85
|
+
expect(result.length).to eq(3)
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
it "includes Enumerable" do
|
|
90
|
+
expect(RXerces::XML::NodeSet.ancestors).to include(Enumerable)
|
|
91
|
+
end
|
|
92
|
+
end
|