xpath 2.0.0 → 3.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/spec/xpath_spec.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
 
3
5
  require 'nokogiri'
@@ -14,23 +16,23 @@ describe XPath do
14
16
  let(:template) { File.read(File.expand_path('fixtures/simple.html', File.dirname(__FILE__))) }
15
17
  let(:doc) { Nokogiri::HTML(template) }
16
18
 
17
- def xpath(type=nil, &block)
19
+ def xpath(type = nil, &block)
18
20
  doc.xpath XPath.generate(&block).to_xpath(type)
19
21
  end
20
22
 
21
- it "should work as a mixin" do
23
+ it 'should work as a mixin' do
22
24
  xpath = Thingy.new.foo_div.to_xpath
23
- doc.xpath(xpath).first[:title].should == 'fooDiv'
25
+ doc.xpath(xpath).first[:title].should eq 'fooDiv'
24
26
  end
25
27
 
26
28
  describe '#descendant' do
27
- it "should find nodes that are nested below the current node" do
29
+ it 'should find nodes that are nested below the current node' do
28
30
  @results = xpath { |x| x.descendant(:p) }
29
- @results[0].text.should == "Blah"
30
- @results[1].text.should == "Bax"
31
+ @results[0].text.should eq 'Blah'
32
+ @results[1].text.should eq 'Bax'
31
33
  end
32
34
 
33
- it "should not find nodes outside the context" do
35
+ it 'should not find nodes outside the context' do
34
36
  @results = xpath do |x|
35
37
  foo_div = x.descendant(:div).where(x.attr(:id) == 'foo')
36
38
  x.descendant(:p).where(x.attr(:id) == foo_div.attr(:title))
@@ -38,312 +40,523 @@ describe XPath do
38
40
  @results[0].should be_nil
39
41
  end
40
42
 
41
- it "should find multiple kinds of nodes" do
43
+ it 'should find multiple kinds of nodes' do
42
44
  @results = xpath { |x| x.descendant(:p, :ul) }
43
- @results[0].text.should == 'Blah'
44
- @results[3].text.should == 'A list'
45
+ @results[0].text.should eq 'Blah'
46
+ @results[3].text.should eq 'A list'
45
47
  end
46
48
 
47
- it "should find all nodes when no arguments given" do
49
+ it 'should find all nodes when no arguments given' do
48
50
  @results = xpath { |x| x.descendant[x.attr(:id) == 'foo'].descendant }
49
- @results[0].text.should == 'Blah'
50
- @results[4].text.should == 'A list'
51
+ @results[0].text.should eq 'Blah'
52
+ @results[4].text.should eq 'A list'
51
53
  end
52
54
  end
53
55
 
54
56
  describe '#child' do
55
- it "should find nodes that are nested directly below the current node" do
57
+ it 'should find nodes that are nested directly below the current node' do
56
58
  @results = xpath { |x| x.descendant(:div).child(:p) }
57
- @results[0].text.should == "Blah"
58
- @results[1].text.should == "Bax"
59
+ @results[0].text.should eq 'Blah'
60
+ @results[1].text.should eq 'Bax'
59
61
  end
60
62
 
61
- it "should not find nodes that are nested further down below the current node" do
63
+ it 'should not find nodes that are nested further down below the current node' do
62
64
  @results = xpath { |x| x.child(:p) }
63
65
  @results[0].should be_nil
64
66
  end
65
67
 
66
- it "should find multiple kinds of nodes" do
68
+ it 'should find multiple kinds of nodes' do
67
69
  @results = xpath { |x| x.descendant(:div).child(:p, :ul) }
68
- @results[0].text.should == 'Blah'
69
- @results[3].text.should == 'A list'
70
+ @results[0].text.should eq 'Blah'
71
+ @results[3].text.should eq 'A list'
70
72
  end
71
73
 
72
- it "should find all nodes when no arguments given" do
74
+ it 'should find all nodes when no arguments given' do
73
75
  @results = xpath { |x| x.descendant[x.attr(:id) == 'foo'].child }
74
- @results[0].text.should == 'Blah'
75
- @results[3].text.should == 'A list'
76
+ @results[0].text.should eq 'Blah'
77
+ @results[3].text.should eq 'A list'
76
78
  end
77
79
  end
78
80
 
79
81
  describe '#axis' do
80
- it "should find nodes given the xpath axis" do
82
+ it 'should find nodes given the xpath axis' do
81
83
  @results = xpath { |x| x.axis(:descendant, :p) }
82
- @results[0].text.should == "Blah"
84
+ @results[0].text.should eq 'Blah'
83
85
  end
84
86
 
85
- it "should find nodes given the xpath axis without a specific tag" do
87
+ it 'should find nodes given the xpath axis without a specific tag' do
86
88
  @results = xpath { |x| x.descendant(:div)[x.attr(:id) == 'foo'].axis(:descendant) }
87
- @results[0][:id].should == "fooDiv"
89
+ @results[0][:id].should eq 'fooDiv'
88
90
  end
89
91
  end
90
92
 
91
93
  describe '#next_sibling' do
92
- it "should find nodes which are immediate siblings of the current node" do
93
- xpath { |x| x.descendant(:p)[x.attr(:id) == 'fooDiv'].next_sibling(:p) }.first.text.should == 'Bax'
94
- xpath { |x| x.descendant(:p)[x.attr(:id) == 'fooDiv'].next_sibling(:ul, :p) }.first.text.should == 'Bax'
95
- xpath { |x| x.descendant(:p)[x.attr(:title) == 'monkey'].next_sibling(:ul, :p) }.first.text.should == 'A list'
94
+ it 'should find nodes which are immediate siblings of the current node' do
95
+ xpath { |x| x.descendant(:p)[x.attr(:id) == 'fooDiv'].next_sibling(:p) }.first.text.should eq 'Bax'
96
+ xpath { |x| x.descendant(:p)[x.attr(:id) == 'fooDiv'].next_sibling(:ul, :p) }.first.text.should eq 'Bax'
97
+ xpath { |x| x.descendant(:p)[x.attr(:title) == 'monkey'].next_sibling(:ul, :p) }.first.text.should eq 'A list'
96
98
  xpath { |x| x.descendant(:p)[x.attr(:id) == 'fooDiv'].next_sibling(:ul, :li) }.first.should be_nil
97
- xpath { |x| x.descendant(:p)[x.attr(:id) == 'fooDiv'].next_sibling }.first.text.should == 'Bax'
99
+ xpath { |x| x.descendant(:p)[x.attr(:id) == 'fooDiv'].next_sibling }.first.text.should eq 'Bax'
98
100
  end
99
101
  end
100
102
 
101
103
  describe '#previous_sibling' do
102
- it "should find nodes which are exactly preceding the current node" do
103
- xpath { |x| x.descendant(:p)[x.attr(:id) == 'wooDiv'].previous_sibling(:p) }.first.text.should == 'Bax'
104
- xpath { |x| x.descendant(:p)[x.attr(:id) == 'wooDiv'].previous_sibling(:ul, :p) }.first.text.should == 'Bax'
105
- xpath { |x| x.descendant(:p)[x.attr(:title) == 'gorilla'].previous_sibling(:ul, :p) }.first.text.should == 'A list'
104
+ it 'should find nodes which are exactly preceding the current node' do
105
+ xpath { |x| x.descendant(:p)[x.attr(:id) == 'wooDiv'].previous_sibling(:p) }.first.text.should eq 'Bax'
106
+ xpath { |x| x.descendant(:p)[x.attr(:id) == 'wooDiv'].previous_sibling(:ul, :p) }.first.text.should eq 'Bax'
107
+ xpath { |x| x.descendant(:p)[x.attr(:title) == 'gorilla'].previous_sibling(:ul, :p) }.first.text.should eq 'A list'
106
108
  xpath { |x| x.descendant(:p)[x.attr(:id) == 'wooDiv'].previous_sibling(:ul, :li) }.first.should be_nil
107
- xpath { |x| x.descendant(:p)[x.attr(:id) == 'wooDiv'].previous_sibling }.first.text.should == 'Bax'
109
+ xpath { |x| x.descendant(:p)[x.attr(:id) == 'wooDiv'].previous_sibling }.first.text.should eq 'Bax'
108
110
  end
109
111
  end
110
112
 
111
113
  describe '#anywhere' do
112
- it "should find nodes regardless of the context" do
114
+ it 'should find nodes regardless of the context' do
113
115
  @results = xpath do |x|
114
116
  foo_div = x.anywhere(:div).where(x.attr(:id) == 'foo')
115
117
  x.descendant(:p).where(x.attr(:id) == foo_div.attr(:title))
116
118
  end
117
- @results[0].text.should == "Blah"
119
+ @results[0].text.should eq 'Blah'
118
120
  end
119
121
 
120
- it "should find multiple kinds of nodes regardless of the context" do
122
+ it 'should find multiple kinds of nodes regardless of the context' do
121
123
  @results = xpath do |x|
122
- context=x.descendant(:div).where(x.attr(:id)=='woo')
124
+ context = x.descendant(:div).where(x.attr(:id) == 'woo')
123
125
  context.anywhere(:p, :ul)
124
126
  end
125
127
 
126
- @results[0].text.should == 'Blah'
127
- @results[3].text.should == 'A list'
128
- @results[4].text.should == 'A list'
129
- @results[6].text.should == 'Bax'
128
+ @results[0].text.should eq 'Blah'
129
+ @results[3].text.should eq 'A list'
130
+ @results[4].text.should eq 'A list'
131
+ @results[6].text.should eq 'Bax'
130
132
  end
131
133
 
132
- it "should find all nodes when no arguments given regardless of the context" do
134
+ it 'should find all nodes when no arguments given regardless of the context' do
133
135
  @results = xpath do |x|
134
- context=x.descendant(:div).where(x.attr(:id)=='woo')
136
+ context = x.descendant(:div).where(x.attr(:id) == 'woo')
135
137
  context.anywhere
136
138
  end
137
- @results[0].name.should == 'html'
138
- @results[1].name.should == 'head'
139
- @results[2].name.should == 'body'
140
- @results[6].text.should == 'Blah'
141
- @results[10].text.should == 'A list'
142
- @results[13].text.should == 'A list'
143
- @results[15].text.should == 'Bax'
139
+ @results[0].name.should eq 'html'
140
+ @results[1].name.should eq 'head'
141
+ @results[2].name.should eq 'body'
142
+ @results[6].text.should eq 'Blah'
143
+ @results[10].text.should eq 'A list'
144
+ @results[13].text.should eq 'A list'
145
+ @results[15].text.should eq 'Bax'
144
146
  end
145
-
146
147
  end
147
148
 
148
149
  describe '#contains' do
149
- it "should find nodes that contain the given string" do
150
+ it 'should find nodes that contain the given string' do
150
151
  @results = xpath do |x|
151
152
  x.descendant(:div).where(x.attr(:title).contains('ooD'))
152
153
  end
153
- @results[0][:id].should == "foo"
154
+ @results[0][:id].should eq 'foo'
154
155
  end
155
156
 
156
- it "should find nodes that contain the given expression" do
157
+ it 'should find nodes that contain the given expression' do
157
158
  @results = xpath do |x|
158
159
  expression = x.anywhere(:div).where(x.attr(:title) == 'fooDiv').attr(:id)
159
160
  x.descendant(:div).where(x.attr(:title).contains(expression))
160
161
  end
161
- @results[0][:id].should == "foo"
162
+ @results[0][:id].should eq 'foo'
163
+ end
164
+ end
165
+
166
+ describe '#contains_word' do
167
+ it 'should find nodes that contain the given word in its entirety' do
168
+ @results = xpath do |x|
169
+ x.descendant.where(x.attr(:class).contains_word('fish'))
170
+ end
171
+ @results[0].text.should eq 'Bax'
172
+ @results[1].text.should eq 'llama'
173
+ @results.length.should eq 2
162
174
  end
163
175
  end
164
176
 
165
177
  describe '#starts_with' do
166
- it "should find nodes that begin with the given string" do
178
+ it 'should find nodes that begin with the given string' do
167
179
  @results = xpath do |x|
168
180
  x.descendant(:*).where(x.attr(:id).starts_with('foo'))
169
181
  end
170
- @results.size.should == 2
171
- @results[0][:id].should == "foo"
172
- @results[1][:id].should == "fooDiv"
182
+ @results.size.should eq 2
183
+ @results[0][:id].should eq 'foo'
184
+ @results[1][:id].should eq 'fooDiv'
173
185
  end
174
186
 
175
- it "should find nodes that contain the given expression" do
187
+ it 'should find nodes that contain the given expression' do
176
188
  @results = xpath do |x|
177
189
  expression = x.anywhere(:div).where(x.attr(:title) == 'fooDiv').attr(:id)
178
190
  x.descendant(:div).where(x.attr(:title).starts_with(expression))
179
191
  end
180
- @results[0][:id].should == "foo"
192
+ @results[0][:id].should eq 'foo'
193
+ end
194
+ end
195
+
196
+ describe '#ends_with' do
197
+ it 'should find nodes that end with the given string' do
198
+ @results = xpath do |x|
199
+ x.descendant(:*).where(x.attr(:id).ends_with('oof'))
200
+ end
201
+ @results.size.should eq 2
202
+ @results[0][:id].should eq 'oof'
203
+ @results[1][:id].should eq 'viDoof'
204
+ end
205
+
206
+ it 'should find nodes that contain the given expression' do
207
+ @results = xpath do |x|
208
+ expression = x.anywhere(:div).where(x.attr(:title) == 'viDoof').attr(:id)
209
+ x.descendant(:div).where(x.attr(:title).ends_with(expression))
210
+ end
211
+ @results[0][:id].should eq 'oof'
212
+ end
213
+ end
214
+
215
+ describe '#uppercase' do
216
+ it 'should match uppercased text' do
217
+ @results = xpath do |x|
218
+ x.descendant(:div).where(x.attr(:title).uppercase == 'VIDOOF')
219
+ end
220
+ @results[0][:id].should eq 'oof'
221
+ end
222
+ end
223
+
224
+ describe '#lowercase' do
225
+ it 'should match lowercased text' do
226
+ @results = xpath do |x|
227
+ x.descendant(:div).where(x.attr(:title).lowercase == 'vidoof')
228
+ end
229
+ @results[0][:id].should eq 'oof'
181
230
  end
182
231
  end
183
232
 
184
233
  describe '#text' do
185
234
  it "should select a node's text" do
186
235
  @results = xpath { |x| x.descendant(:p).where(x.text == 'Bax') }
187
- @results[0].text.should == 'Bax'
188
- @results[1][:title].should == 'monkey'
236
+ @results[0].text.should eq 'Bax'
237
+ @results[1][:title].should eq 'monkey'
189
238
  @results = xpath { |x| x.descendant(:div).where(x.descendant(:p).text == 'Bax') }
190
- @results[0][:title].should == 'fooDiv'
239
+ @results[0][:title].should eq 'fooDiv'
240
+ end
241
+ end
242
+
243
+ describe '#substring' do
244
+ context 'when called with one argument' do
245
+ it 'should select the part of a string after the specified character' do
246
+ @results = xpath { |x| x.descendant(:span).where(x.attr(:id) == 'substring').text.substring(7) }
247
+ @results.should eq 'there'
248
+ end
249
+ end
250
+
251
+ context 'when called with two arguments' do
252
+ it 'should select the part of a string after the specified character, up to the given length' do
253
+ @results = xpath { |x| x.descendant(:span).where(x.attr(:id) == 'substring').text.substring(2, 4) }
254
+ @results.should eq 'ello'
255
+ end
256
+ end
257
+ end
258
+
259
+ describe '#function' do
260
+ it 'should call the given xpath function' do
261
+ @results = xpath { |x| x.function(:boolean, x.function(:true) == x.function(:false)) }
262
+ @results.should eq false
263
+ end
264
+ end
265
+
266
+ describe '#method' do
267
+ it 'should call the given xpath function with the current node as the first argument' do
268
+ @results = xpath { |x| x.descendant(:span).where(x.attr(:id) == 'string-length').text.method(:"string-length") }
269
+ @results.should eq 11
270
+ end
271
+ end
272
+
273
+ describe '#string_length' do
274
+ it 'should return the length of a string' do
275
+ @results = xpath { |x| x.descendant(:span).where(x.attr(:id) == 'string-length').text.string_length }
276
+ @results.should eq 11
191
277
  end
192
278
  end
193
279
 
194
280
  describe '#where' do
195
- it "should limit the expression to find only certain nodes" do
196
- xpath { |x| x.descendant(:div).where(:"@id = 'foo'") }.first[:title].should == "fooDiv"
281
+ it 'should limit the expression to find only certain nodes' do
282
+ xpath { |x| x.descendant(:div).where(:"@id = 'foo'") }.first[:title].should eq 'fooDiv'
197
283
  end
198
284
 
199
- it "should be aliased as []" do
200
- xpath { |x| x.descendant(:div)[:"@id = 'foo'"] }.first[:title].should == "fooDiv"
285
+ it 'should be aliased as []' do
286
+ xpath { |x| x.descendant(:div)[:"@id = 'foo'"] }.first[:title].should eq 'fooDiv'
287
+ end
288
+
289
+ it 'should be a no-op when nil condition is passed' do
290
+ XPath.descendant(:div).where(nil).to_s.should eq './/div'
201
291
  end
202
292
  end
203
293
 
204
294
  describe '#inverse' do
205
- it "should invert the expression" do
206
- xpath { |x| x.descendant(:p).where(x.attr(:id).equals('fooDiv').inverse) }.first.text.should == 'Bax'
295
+ it 'should invert the expression' do
296
+ xpath { |x| x.descendant(:p).where(x.attr(:id).equals('fooDiv').inverse) }.first.text.should eq 'Bax'
207
297
  end
208
298
 
209
- it "should be aliased as the unary tilde" do
210
- xpath { |x| x.descendant(:p).where(~x.attr(:id).equals('fooDiv')) }.first.text.should == 'Bax'
299
+ it 'should be aliased as the unary tilde' do
300
+ xpath { |x| x.descendant(:p).where(~x.attr(:id).equals('fooDiv')) }.first.text.should eq 'Bax'
301
+ end
302
+
303
+ it 'should be aliased as the unary bang' do
304
+ xpath { |x| x.descendant(:p).where(!x.attr(:id).equals('fooDiv')) }.first.text.should eq 'Bax'
211
305
  end
212
306
  end
213
307
 
214
308
  describe '#equals' do
215
- it "should limit the expression to find only certain nodes" do
216
- xpath { |x| x.descendant(:div).where(x.attr(:id).equals('foo')) }.first[:title].should == "fooDiv"
309
+ it 'should limit the expression to find only certain nodes' do
310
+ xpath { |x| x.descendant(:div).where(x.attr(:id).equals('foo')) }.first[:title].should eq 'fooDiv'
311
+ end
312
+
313
+ it 'should be aliased as ==' do
314
+ xpath { |x| x.descendant(:div).where(x.attr(:id) == 'foo') }.first[:title].should eq 'fooDiv'
315
+ end
316
+ end
317
+
318
+ describe '#not_equals' do
319
+ it 'should match only when not equal' do
320
+ xpath { |x| x.descendant(:div).where(x.attr(:id).not_equals('bar')) }.first[:title].should eq 'fooDiv'
217
321
  end
218
322
 
219
- it "should be aliased as ==" do
220
- xpath { |x| x.descendant(:div).where(x.attr(:id) == 'foo') }.first[:title].should == "fooDiv"
323
+ it 'should be aliased as !=' do
324
+ xpath { |x| x.descendant(:div).where(x.attr(:id) != 'bar') }.first[:title].should eq 'fooDiv'
221
325
  end
222
326
  end
223
327
 
224
328
  describe '#is' do
225
- it "uses equality when :exact given" do
226
- xpath(:exact) { |x| x.descendant(:div).where(x.attr(:id).is('foo')) }.first[:title].should == "fooDiv"
329
+ it 'uses equality when :exact given' do
330
+ xpath(:exact) { |x| x.descendant(:div).where(x.attr(:id).is('foo')) }.first[:title].should eq 'fooDiv'
227
331
  xpath(:exact) { |x| x.descendant(:div).where(x.attr(:id).is('oo')) }.first.should be_nil
228
332
  end
229
333
 
230
- it "uses substring matching otherwise" do
231
- xpath { |x| x.descendant(:div).where(x.attr(:id).is('foo')) }.first[:title].should == "fooDiv"
232
- xpath { |x| x.descendant(:div).where(x.attr(:id).is('oo')) }.first[:title].should == "fooDiv"
334
+ it 'uses substring matching otherwise' do
335
+ xpath { |x| x.descendant(:div).where(x.attr(:id).is('foo')) }.first[:title].should eq 'fooDiv'
336
+ xpath { |x| x.descendant(:div).where(x.attr(:id).is('oo')) }.first[:title].should eq 'fooDiv'
233
337
  end
234
338
  end
235
339
 
236
340
  describe '#one_of' do
237
- it "should return all nodes where the condition matches" do
341
+ it 'should return all nodes where the condition matches' do
238
342
  @results = xpath do |x|
239
343
  p = x.anywhere(:div).where(x.attr(:id) == 'foo').attr(:title)
240
344
  x.descendant(:*).where(x.attr(:id).one_of('foo', p, 'baz'))
241
345
  end
242
- @results[0][:title].should == "fooDiv"
243
- @results[1].text.should == "Blah"
244
- @results[2][:title].should == "bazDiv"
346
+ @results[0][:title].should eq 'fooDiv'
347
+ @results[1].text.should eq 'Blah'
348
+ @results[2][:title].should eq 'bazDiv'
245
349
  end
246
350
  end
247
351
 
248
352
  describe '#and' do
249
- it "should find all nodes in both expression" do
353
+ it 'should find all nodes in both expression' do
250
354
  @results = xpath do |x|
251
355
  x.descendant(:*).where(x.contains('Bax').and(x.attr(:title).equals('monkey')))
252
356
  end
253
- @results[0][:title].should == "monkey"
357
+ @results[0][:title].should eq 'monkey'
254
358
  end
255
359
 
256
- it "should be aliased as ampersand (&)" do
360
+ it 'should be aliased as ampersand (&)' do
257
361
  @results = xpath do |x|
258
362
  x.descendant(:*).where(x.contains('Bax') & x.attr(:title).equals('monkey'))
259
363
  end
260
- @results[0][:title].should == "monkey"
364
+ @results[0][:title].should eq 'monkey'
261
365
  end
262
366
  end
263
367
 
264
368
  describe '#or' do
265
- it "should find all nodes in either expression" do
369
+ it 'should find all nodes in either expression' do
266
370
  @results = xpath do |x|
267
371
  x.descendant(:*).where(x.attr(:id).equals('foo').or(x.attr(:id).equals('fooDiv')))
268
372
  end
269
- @results[0][:title].should == "fooDiv"
270
- @results[1].text.should == "Blah"
373
+ @results[0][:title].should eq 'fooDiv'
374
+ @results[1].text.should eq 'Blah'
271
375
  end
272
376
 
273
- it "should be aliased as pipe (|)" do
377
+ it 'should be aliased as pipe (|)' do
274
378
  @results = xpath do |x|
275
379
  x.descendant(:*).where(x.attr(:id).equals('foo') | x.attr(:id).equals('fooDiv'))
276
380
  end
277
- @results[0][:title].should == "fooDiv"
278
- @results[1].text.should == "Blah"
381
+ @results[0][:title].should eq 'fooDiv'
382
+ @results[1].text.should eq 'Blah'
279
383
  end
280
384
  end
281
385
 
282
386
  describe '#attr' do
283
- it "should be an attribute" do
387
+ it 'should be an attribute' do
284
388
  @results = xpath { |x| x.descendant(:div).where(x.attr(:id)) }
285
- @results[0][:title].should == "barDiv"
286
- @results[1][:title].should == "fooDiv"
389
+ @results[0][:title].should eq 'barDiv'
390
+ @results[1][:title].should eq 'fooDiv'
287
391
  end
288
392
  end
289
393
 
290
394
  describe '#css' do
291
- it "should find nodes by the given CSS selector" do
395
+ it 'should find nodes by the given CSS selector' do
292
396
  @results = xpath { |x| x.css('#preference p') }
293
- @results[0].text.should == 'allamas'
294
- @results[1].text.should == 'llama'
397
+ @results[0].text.should eq 'allamas'
398
+ @results[1].text.should eq 'llama'
295
399
  end
296
400
 
297
- it "should respect previous expression" do
401
+ it 'should respect previous expression' do
298
402
  @results = xpath { |x| x.descendant[x.attr(:id) == 'moar'].css('p') }
299
- @results[0].text.should == 'chimp'
300
- @results[1].text.should == 'flamingo'
403
+ @results[0].text.should eq 'chimp'
404
+ @results[1].text.should eq 'flamingo'
301
405
  end
302
406
 
303
- it "should be composable" do
407
+ it 'should be composable' do
304
408
  @results = xpath { |x| x.css('#moar').descendant(:p) }
305
- @results[0].text.should == 'chimp'
306
- @results[1].text.should == 'flamingo'
409
+ @results[0].text.should eq 'chimp'
410
+ @results[1].text.should eq 'flamingo'
307
411
  end
308
412
 
309
- it "should allow comma separated selectors" do
413
+ it 'should allow comma separated selectors' do
310
414
  @results = xpath { |x| x.descendant[x.attr(:id) == 'moar'].css('div, p') }
311
- @results[0].text.should == 'chimp'
312
- @results[1].text.should == 'elephant'
313
- @results[2].text.should == 'flamingo'
415
+ @results[0].text.should eq 'chimp'
416
+ @results[1].text.should eq 'elephant'
417
+ @results[2].text.should eq 'flamingo'
314
418
  end
315
419
  end
316
420
 
317
- describe '#name' do
421
+ describe '#qname' do
318
422
  it "should match the node's name" do
319
- xpath { |x| x.descendant(:*).where(x.name == 'ul') }.first.text.should == "A list"
423
+ xpath { |x| x.descendant(:*).where(x.qname == 'ul') }.first.text.should eq 'A list'
320
424
  end
321
425
  end
322
426
 
323
427
  describe '#union' do
324
- it "should create a union expression" do
428
+ it 'should create a union expression' do
325
429
  @expr1 = XPath.generate { |x| x.descendant(:p) }
326
430
  @expr2 = XPath.generate { |x| x.descendant(:div) }
327
431
  @collection = @expr1.union(@expr2)
328
432
  @xpath1 = @collection.where(XPath.attr(:id) == 'foo').to_xpath
329
433
  @xpath2 = @collection.where(XPath.attr(:id) == 'fooDiv').to_xpath
330
434
  @results = doc.xpath(@xpath1)
331
- @results[0][:title].should == 'fooDiv'
435
+ @results[0][:title].should eq 'fooDiv'
332
436
  @results = doc.xpath(@xpath2)
333
- @results[0][:id].should == 'fooDiv'
437
+ @results[0][:id].should eq 'fooDiv'
334
438
  end
335
439
 
336
- it "should be aliased as +" do
440
+ it 'should be aliased as +' do
337
441
  @expr1 = XPath.generate { |x| x.descendant(:p) }
338
442
  @expr2 = XPath.generate { |x| x.descendant(:div) }
339
443
  @collection = @expr1 + @expr2
340
444
  @xpath1 = @collection.where(XPath.attr(:id) == 'foo').to_xpath
341
445
  @xpath2 = @collection.where(XPath.attr(:id) == 'fooDiv').to_xpath
342
446
  @results = doc.xpath(@xpath1)
343
- @results[0][:title].should == 'fooDiv'
447
+ @results[0][:title].should eq 'fooDiv'
344
448
  @results = doc.xpath(@xpath2)
345
- @results[0][:id].should == 'fooDiv'
449
+ @results[0][:id].should eq 'fooDiv'
450
+ end
451
+ end
452
+
453
+ describe '#last' do
454
+ it 'returns the number of elements in the context' do
455
+ @results = xpath { |x| x.descendant(:p)[XPath.position() == XPath.last()] }
456
+ @results[0].text.should eq 'Bax'
457
+ @results[1].text.should eq 'Blah'
458
+ @results[2].text.should eq 'llama'
459
+ end
460
+ end
461
+
462
+ describe '#position' do
463
+ it 'returns the position of elements in the context' do
464
+ @results = xpath { |x| x.descendant(:p)[XPath.position() == 2] }
465
+ @results[0].text.should eq 'Bax'
466
+ @results[1].text.should eq 'Bax'
467
+ end
468
+ end
469
+
470
+ describe '#count' do
471
+ it 'counts the number of occurrences' do
472
+ @results = xpath { |x| x.descendant(:div)[x.descendant(:p).count == 2] }
473
+ @results[0][:id].should eq 'preference'
474
+ end
475
+ end
476
+
477
+ describe '#lte' do
478
+ it 'checks lesser than or equal' do
479
+ @results = xpath { |x| x.descendant(:p)[XPath.position() <= 2] }
480
+ @results[0].text.should eq 'Blah'
481
+ @results[1].text.should eq 'Bax'
482
+ @results[2][:title].should eq 'gorilla'
483
+ @results[3].text.should eq 'Bax'
484
+ end
485
+ end
486
+
487
+ describe '#lt' do
488
+ it 'checks lesser than' do
489
+ @results = xpath { |x| x.descendant(:p)[XPath.position() < 2] }
490
+ @results[0].text.should eq 'Blah'
491
+ @results[1][:title].should eq 'gorilla'
492
+ end
493
+ end
494
+
495
+ describe '#gte' do
496
+ it 'checks greater than or equal' do
497
+ @results = xpath { |x| x.descendant(:p)[XPath.position() >= 2] }
498
+ @results[0].text.should eq 'Bax'
499
+ @results[1][:title].should eq 'monkey'
500
+ @results[2].text.should eq 'Bax'
501
+ @results[3].text.should eq 'Blah'
502
+ end
503
+ end
504
+
505
+ describe '#gt' do
506
+ it 'checks greater than' do
507
+ @results = xpath { |x| x.descendant(:p)[XPath.position() > 2] }
508
+ @results[0][:title].should eq 'monkey'
509
+ @results[1].text.should eq 'Blah'
510
+ end
511
+ end
512
+
513
+ describe '#plus' do
514
+ it 'adds stuff' do
515
+ @results = xpath { |x| x.descendant(:p)[XPath.position().plus(1) == 2] }
516
+ @results[0][:id].should eq 'fooDiv'
517
+ @results[1][:title].should eq 'gorilla'
518
+ end
519
+ end
520
+
521
+ describe '#minus' do
522
+ it 'subtracts stuff' do
523
+ @results = xpath { |x| x.descendant(:p)[XPath.position().minus(1) == 0] }
524
+ @results[0][:id].should eq 'fooDiv'
525
+ @results[1][:title].should eq 'gorilla'
346
526
  end
347
527
  end
348
528
 
529
+ describe '#multiply' do
530
+ it 'multiplies stuff' do
531
+ @results = xpath { |x| x.descendant(:p)[XPath.position() * 3 == 3] }
532
+ @results[0][:id].should eq 'fooDiv'
533
+ @results[1][:title].should eq 'gorilla'
534
+ end
535
+ end
536
+
537
+ describe '#divide' do
538
+ it 'divides stuff' do
539
+ @results = xpath { |x| x.descendant(:p)[XPath.position() / 2 == 1] }
540
+ @results[0].text.should eq 'Bax'
541
+ @results[1].text.should eq 'Bax'
542
+ end
543
+ end
544
+
545
+ describe '#mod' do
546
+ it 'take modulo' do
547
+ @results = xpath { |x| x.descendant(:p)[XPath.position() % 2 == 1] }
548
+ @results[0].text.should eq 'Blah'
549
+ @results[1][:title].should eq 'monkey'
550
+ @results[2][:title].should eq 'gorilla'
551
+ end
552
+ end
553
+
554
+ describe '#ancestor' do
555
+ it 'finds ancestor nodes' do
556
+ @results = xpath { |x| x.descendant(:p)[1].ancestor }
557
+ @results[0].node_name.should eq 'html'
558
+ @results[1].node_name.should eq 'body'
559
+ @results[2][:id].should eq 'foo'
560
+ end
561
+ end
349
562
  end