wgibbs-xpath 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.rdoc +56 -0
- data/lib/xpath.rb +68 -0
- data/lib/xpath/expression.rb +310 -0
- data/lib/xpath/html.rb +114 -0
- data/lib/xpath/union.rb +27 -0
- data/lib/xpath/version.rb +3 -0
- data/spec/fixtures/form.html +87 -0
- data/spec/fixtures/simple.html +32 -0
- data/spec/fixtures/stuff.html +43 -0
- data/spec/html_spec.rb +187 -0
- data/spec/spec_helper.rb +4 -0
- data/spec/union_spec.rb +63 -0
- data/spec/xpath_spec.rb +353 -0
- metadata +129 -0
data/lib/xpath/union.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
module XPath
|
2
|
+
class Union
|
3
|
+
include Enumerable
|
4
|
+
|
5
|
+
attr_reader :expressions
|
6
|
+
|
7
|
+
def initialize(*expressions)
|
8
|
+
@expressions = expressions
|
9
|
+
end
|
10
|
+
|
11
|
+
def each(&block)
|
12
|
+
expressions.each(&block)
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_xpath(predicate=nil)
|
16
|
+
expressions.map { |e| e.to_xpath(predicate) }.join(' | ')
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_xpaths
|
20
|
+
[to_xpath(:exact), to_xpath(:fuzzy)].uniq
|
21
|
+
end
|
22
|
+
|
23
|
+
def method_missing(*args)
|
24
|
+
XPath::Union.new(*expressions.map { |e| e.send(*args) })
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
<h1>Form</h1>
|
2
|
+
|
3
|
+
<p>
|
4
|
+
<a id="awesome-link" data="link-text" href="#link">An awesome link</a>
|
5
|
+
<a id="some-id" data="link-id" href="#id">With id</a>
|
6
|
+
<a title="My title" data="link-title" href="#title">A cool title</a>
|
7
|
+
<a href="#image" data="link-img"><img src="foo.png" alt="Alt link"/></a>
|
8
|
+
<a title="This title is too long" data="link-fuzzy" href="#bar">A link at a time</a>
|
9
|
+
<a title="This title" data="link-exact" href="#foo">A link</a>
|
10
|
+
<a href="#bar" data="link-img-fuzzy"><img src="foo.png" alt="An image that is beautiful"/></a>
|
11
|
+
<a href="#foo" data="link-img-exact"><img src="foo.ong" alt="An image"/></a>
|
12
|
+
<a href="http://www.example.com" data="link-href">Href-ed link</a>
|
13
|
+
<a>Wrong Link</a>
|
14
|
+
<a href="#spacey" data="link-whitespace"> My
|
15
|
+
|
16
|
+
whitespaced
|
17
|
+
link</a>
|
18
|
+
<a href="#has-children" data="link-children">
|
19
|
+
An <em>emphatic</em> link with some children
|
20
|
+
</a>
|
21
|
+
</p>
|
22
|
+
|
23
|
+
<p>
|
24
|
+
<input type="submit" id="submit-with-id" data="id-submit" value="Has ID"/>
|
25
|
+
<input type="submit" value="submit-with-value" data="value-submit"/>
|
26
|
+
<input type="submit" value="not exact value submit" data="not-exact-value-submit"/>
|
27
|
+
<input type="submit" value="exact value submit" data="exact-value-submit"/>
|
28
|
+
|
29
|
+
<input type="button" id="button-with-id" data="id-button" value="Has ID"/>
|
30
|
+
<input type="button" value="button-with-value" data="value-button"/>
|
31
|
+
<input type="button" value="not exact value button" data="not-exact-value-button"/>
|
32
|
+
<input type="button" value="exact value button" data="exact-value-button"/>
|
33
|
+
|
34
|
+
<input type="image" id="imgbut-with-id" data="id-imgbut" value="Has ID"/>
|
35
|
+
<input type="image" value="imgbut-with-value" data="value-imgbut"/>
|
36
|
+
<input type="image" alt="imgbut-with-alt" data="alt-imgbut"/>
|
37
|
+
<input type="image" value="not exact value imgbut" data="not-exact-value-imgbut"/>
|
38
|
+
<input type="image" value="exact value imgbut" data="exact-value-imgbut"/>
|
39
|
+
|
40
|
+
<button id="btag-with-id" data="id-btag" value="Has ID"/>
|
41
|
+
<button value="btag-with-value" data="value-btag"/>
|
42
|
+
<button value="not exact value btag" data="not-exact-value-btag"/>
|
43
|
+
<button value="exact value btag" data="exact-value-btag"/>
|
44
|
+
|
45
|
+
<button data="text-btag">btag-with-text</button>
|
46
|
+
<button data="not-exact-text-btag">not exact text btag</button>
|
47
|
+
<button data="exact-text-btag">exact text btag</button>
|
48
|
+
|
49
|
+
<button data="btag-with-whitespace"> My
|
50
|
+
|
51
|
+
whitespaced
|
52
|
+
button</button>
|
53
|
+
<button data="btag-with-children">
|
54
|
+
An <em>emphatic</em> button with some children
|
55
|
+
</button>
|
56
|
+
|
57
|
+
<input type="schmoo" value="schmoo button" data="schmoo"/>
|
58
|
+
<label for="text-with-id">Label text <input type="text" id="text-with-id" data="id-text" value="monkey"/></label>
|
59
|
+
<input type="text" id="text-with-placeholder" data="placeholder-text" value="monkey" placeholder="Placeholder text"/>
|
60
|
+
<label for="problem-text-with-id">Label text's got an apostrophe <input type="text" id="problem-text-with-id" data="id-problem-text" value="monkey"/></label>
|
61
|
+
</p>
|
62
|
+
|
63
|
+
<p>
|
64
|
+
<fieldset id="some-fieldset-id" data="fieldset-id"></fieldset>
|
65
|
+
<fieldset data="fieldset-legend"><legend>Some Legend</legend></fieldset>
|
66
|
+
<fieldset data="fieldset-legend-span"><legend><span>Span Legend</span></legend></fieldset>
|
67
|
+
<fieldset data="fieldset-fuzzy"><legend>Long legend yo</legend></fieldset>
|
68
|
+
<fieldset data="fieldset-exact"><legend>Long legend</legend></fieldset>
|
69
|
+
</p>
|
70
|
+
|
71
|
+
<p>
|
72
|
+
<select>
|
73
|
+
<optgroup label="Group A" data="optgroup-a"></optgroup>
|
74
|
+
<optgroup label="Group B" data="optgroup-b"></optgroup>
|
75
|
+
</select>
|
76
|
+
</p>
|
77
|
+
|
78
|
+
<p>
|
79
|
+
<table id="whitespaced-table" data="table-with-whitespace">
|
80
|
+
<tr>
|
81
|
+
<td data="cell-whitespaced">I have
|
82
|
+
<span>nested whitespace</span>
|
83
|
+
</td>
|
84
|
+
<td>I don't</td>
|
85
|
+
</tr>
|
86
|
+
</table>
|
87
|
+
</p>
|
@@ -0,0 +1,32 @@
|
|
1
|
+
<div id="bar" title="barDiv">
|
2
|
+
|
3
|
+
</div>
|
4
|
+
|
5
|
+
<div title="noId"></div>
|
6
|
+
|
7
|
+
<div id="foo" title="fooDiv" data="id">
|
8
|
+
<p id="fooDiv">Blah</p>
|
9
|
+
<p>Bax</p>
|
10
|
+
<p title="monkey">Bax</p>
|
11
|
+
<ul><li>A list</li></ul>
|
12
|
+
</div>
|
13
|
+
|
14
|
+
<div id="baz" title="bazDiv"></div>
|
15
|
+
|
16
|
+
<div id="preference">
|
17
|
+
<p id="is-fuzzy">allamas</p>
|
18
|
+
<p id="is-exact">llama</p>
|
19
|
+
</div>
|
20
|
+
|
21
|
+
<p id="whitespace">
|
22
|
+
A lot
|
23
|
+
|
24
|
+
of
|
25
|
+
whitespace
|
26
|
+
</p>
|
27
|
+
|
28
|
+
<div id="moar">
|
29
|
+
<p id="impchay">chimp</p>
|
30
|
+
<div id="elephantay">elephant</div>
|
31
|
+
<p id="amingoflay">flamingo</p>
|
32
|
+
</div>
|
@@ -0,0 +1,43 @@
|
|
1
|
+
<h1>This is a test</h1>
|
2
|
+
|
3
|
+
<p id="first">
|
4
|
+
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
|
5
|
+
tempor incididunt ut <a href="/with_simple_html" title="awesome title" class="simple">labore</a>
|
6
|
+
et dolore magna aliqua. Ut enim ad minim veniam,
|
7
|
+
quis nostrud exercitation <a href="/foo" id="foo">ullamco</a> laboris nisi
|
8
|
+
ut aliquip ex ea commodo consequat.
|
9
|
+
<a href="/with_simple_html"><img src="http://www.foobar.sun/dummy_image.jpg" width="20" height="20" alt="awesome image" /></a>
|
10
|
+
</p>
|
11
|
+
|
12
|
+
<p id="second">
|
13
|
+
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum
|
14
|
+
dolore eu fugiat <a href="/redirect" id="red">Redirect</a> pariatur. Excepteur sint occaecat cupidatat non proident,
|
15
|
+
sunt in culpa qui officia
|
16
|
+
text with
|
17
|
+
whitespace
|
18
|
+
id est laborum.
|
19
|
+
</p>
|
20
|
+
|
21
|
+
<p>
|
22
|
+
<input type="text" id="test_field" value="monkey"/>
|
23
|
+
<textarea>banana</textarea>
|
24
|
+
<a href="/redirect_back">BackToMyself</a>
|
25
|
+
<a title="twas a fine link" href="/redirect">A link came first</a>
|
26
|
+
<a title="a fine link" href="/with_simple_html">A link</a>
|
27
|
+
<a title="a fine link with data method" data-method="delete" href="/delete">A link with data-method</a>
|
28
|
+
<a>No Href</a>
|
29
|
+
<a href="">Blank Href</a>
|
30
|
+
<a href="#">Blank Anchor</a>
|
31
|
+
<a href="#anchor">Anchor</a>
|
32
|
+
<a href="/with_simple_html#anchor">Anchor on different page</a>
|
33
|
+
<a href="/with_html#anchor">Anchor on same page</a>
|
34
|
+
<input type="text" value="" id="test_field">
|
35
|
+
<input type="text" checked="checked" id="checked_field">
|
36
|
+
<a href="/redirect"><img src="http://www.foobar.sun/dummy_image.jpg" width="20" height="20" alt="very fine image" /></a>
|
37
|
+
<a href="/with_simple_html"><img src="http://www.foobar.sun/dummy_image.jpg" width="20" height="20" alt="fine image" /></a>
|
38
|
+
</p>
|
39
|
+
|
40
|
+
<div id="hidden" style="display: none;">
|
41
|
+
<div id="hidden_via_ancestor">Inside element with hidden ancestor</div>
|
42
|
+
<a href="/with_simple_html" title="awesome title" class="simple">hidden link</a>
|
43
|
+
</div>
|
data/spec/html_spec.rb
ADDED
@@ -0,0 +1,187 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'nokogiri'
|
3
|
+
|
4
|
+
describe XPath::HTML do
|
5
|
+
let(:template) { 'form' }
|
6
|
+
let(:template_path) { File.read(File.expand_path("fixtures/#{template}.html", File.dirname(__FILE__))) }
|
7
|
+
let(:doc) { Nokogiri::HTML(template_path) }
|
8
|
+
|
9
|
+
def get(*args)
|
10
|
+
all(*args).first
|
11
|
+
end
|
12
|
+
|
13
|
+
def all(*args)
|
14
|
+
XPath::HTML.send(subject, *args).to_xpaths.map do |xpath|
|
15
|
+
doc.xpath(xpath)
|
16
|
+
end.flatten.uniq.map { |node| node[:data] }
|
17
|
+
end
|
18
|
+
|
19
|
+
describe '#link' do
|
20
|
+
subject { :link }
|
21
|
+
|
22
|
+
it("finds links by id") { get('some-id').should == 'link-id' }
|
23
|
+
it("finds links by content") { get('An awesome link').should == 'link-text' }
|
24
|
+
it("finds links by content regardless of whitespace") { get('My whitespaced link').should == 'link-whitespace' }
|
25
|
+
it("finds links with child tags by content") { get('An emphatic link').should == 'link-children' }
|
26
|
+
it("finds links by the content of theur child tags") { get('emphatic').should == 'link-children' }
|
27
|
+
it("finds links by approximate content") { get('awesome').should == 'link-text' }
|
28
|
+
it("prefers exact matches of content") { all('A link').should == ['link-exact', 'link-fuzzy'] }
|
29
|
+
it("finds links by title") { get('My title').should == 'link-title' }
|
30
|
+
it("finds links by approximate title") { get('title').should == 'link-title' }
|
31
|
+
it("prefers exact matches of title") { all('This title').should == ['link-exact', 'link-fuzzy'] }
|
32
|
+
it("finds links by image's alt attribute") { get('Alt link').should == 'link-img' }
|
33
|
+
it("finds links by image's approximate alt attribute") { get('Alt').should == 'link-img' }
|
34
|
+
it("prefers exact matches of image's alt attribute") { all('An image').should == ['link-img-exact', 'link-img-fuzzy'] }
|
35
|
+
it("does not find links without href attriutes") { get('Wrong Link').should be_nil }
|
36
|
+
it("finds links with an href") { get("Href-ed link", :href => 'http://www.example.com').should == 'link-href' }
|
37
|
+
it("does not find links with an incorrect href") { get("Href-ed link", :href => 'http://www.somewhere.com').should be_nil }
|
38
|
+
end
|
39
|
+
|
40
|
+
describe '#button' do
|
41
|
+
subject { :button }
|
42
|
+
|
43
|
+
context "with submit type" do
|
44
|
+
it("finds buttons by id") { get('submit-with-id').should == 'id-submit' }
|
45
|
+
it("finds buttons by value") { get('submit-with-value').should == 'value-submit' }
|
46
|
+
it("finds buttons by approximate value") { get('mit-with-val').should == 'value-submit' }
|
47
|
+
it("prefers buttons with exact value") { all('exact value submit').should == ['exact-value-submit', 'not-exact-value-submit'] }
|
48
|
+
end
|
49
|
+
|
50
|
+
context "with button type" do
|
51
|
+
it("finds buttons by id") { get('button-with-id').should == 'id-button' }
|
52
|
+
it("finds buttons by value") { get('button-with-value').should == 'value-button' }
|
53
|
+
it("finds buttons by approximate value") { get('ton-with-val').should == 'value-button' }
|
54
|
+
it("prefers buttons with exact value") { all('exact value button').should == ['exact-value-button', 'not-exact-value-button'] }
|
55
|
+
end
|
56
|
+
|
57
|
+
context "with image type" do
|
58
|
+
it("finds buttons by id") { get('imgbut-with-id').should == 'id-imgbut' }
|
59
|
+
it("finds buttons by value") { get('imgbut-with-value').should == 'value-imgbut' }
|
60
|
+
it("finds buttons by approximate value") { get('gbut-with-val').should == 'value-imgbut' }
|
61
|
+
it("finds buttons by alt attribute") { get('imgbut-with-alt').should == 'alt-imgbut' }
|
62
|
+
it("prefers buttons with exact value") { all('exact value imgbut').should == ['exact-value-imgbut', 'not-exact-value-imgbut'] }
|
63
|
+
end
|
64
|
+
|
65
|
+
context "with button tag" do
|
66
|
+
it("finds buttons by id") { get('btag-with-id').should == 'id-btag' }
|
67
|
+
it("finds buttons by value") { get('btag-with-value').should == 'value-btag' }
|
68
|
+
it("finds buttons by approximate value") { get('tag-with-val').should == 'value-btag' }
|
69
|
+
it("finds prefers buttons with exact value") { all('exact value btag').should == ['exact-value-btag', 'not-exact-value-btag'] }
|
70
|
+
it("finds buttons by text") { get('btag-with-text').should == 'text-btag' }
|
71
|
+
it("finds buttons by text ignoring whitespace") { get('My whitespaced button').should == 'btag-with-whitespace' }
|
72
|
+
it("finds buttons by approximate text ") { get('tag-with-tex').should == 'text-btag' }
|
73
|
+
it("finds buttons with child tags by text") { get('An emphatic button').should == 'btag-with-children' }
|
74
|
+
it("finds buttons by text of their children") { get('emphatic').should == 'btag-with-children' }
|
75
|
+
it("prefers buttons with exact text") { all('exact text btag').should == ['exact-text-btag', 'not-exact-text-btag'] }
|
76
|
+
end
|
77
|
+
|
78
|
+
context "with unkown type" do
|
79
|
+
it("does not find the button") { get('schmoo button').should be_nil }
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
describe '#fieldset' do
|
84
|
+
subject { :fieldset }
|
85
|
+
|
86
|
+
it("finds fieldsets by id") { get('some-fieldset-id').should == 'fieldset-id' }
|
87
|
+
it("finds fieldsets by legend") { get('Some Legend').should == 'fieldset-legend' }
|
88
|
+
it("finds fieldsets by legend child tags") { get('Span Legend').should == 'fieldset-legend-span' }
|
89
|
+
it("accepts approximate legends") { get('Legend').should == 'fieldset-legend' }
|
90
|
+
it("prefers exact legend") { all('Long legend').should == ['fieldset-exact', 'fieldset-fuzzy'] }
|
91
|
+
end
|
92
|
+
|
93
|
+
describe '#field' do
|
94
|
+
subject { :field }
|
95
|
+
|
96
|
+
context "by id" do
|
97
|
+
it("finds inputs with text type") {}
|
98
|
+
it("finds inputs with password type") {}
|
99
|
+
it("finds inputs with custom type") {}
|
100
|
+
it("finds textareas") {}
|
101
|
+
it("finds select boxes") {}
|
102
|
+
it("does not find submit buttons") {}
|
103
|
+
it("does not find image buttons") {}
|
104
|
+
it("does not find hidden fields") {}
|
105
|
+
end
|
106
|
+
|
107
|
+
context "by name" do
|
108
|
+
it("finds inputs with text type") {}
|
109
|
+
it("finds inputs with password type") {}
|
110
|
+
it("finds inputs with custom type") {}
|
111
|
+
it("finds textareas") {}
|
112
|
+
it("finds select boxes") {}
|
113
|
+
it("does not find submit buttons") {}
|
114
|
+
it("does not find image buttons") {}
|
115
|
+
it("does not find hidden fields") {}
|
116
|
+
end
|
117
|
+
|
118
|
+
context "by referenced label" do
|
119
|
+
it("finds inputs with text type") {}
|
120
|
+
it("finds inputs with password type") {}
|
121
|
+
it("finds inputs with custom type") {}
|
122
|
+
it("finds textareas") {}
|
123
|
+
it("finds select boxes") {}
|
124
|
+
it("does not find submit buttons") {}
|
125
|
+
it("does not find image buttons") {}
|
126
|
+
it("does not find hidden fields") {}
|
127
|
+
end
|
128
|
+
|
129
|
+
context "by parent label" do
|
130
|
+
it("finds inputs with text type") { get('Label text').should == 'id-text' }
|
131
|
+
it("finds inputs where label has problem chars") { get("Label text's got an apostrophe").should == 'id-problem-text' }
|
132
|
+
it("finds inputs with password type") {}
|
133
|
+
it("finds inputs with custom type") {}
|
134
|
+
it("finds textareas") {}
|
135
|
+
it("finds select boxes") {}
|
136
|
+
it("does not find submit buttons") {}
|
137
|
+
it("does not find image buttons") {}
|
138
|
+
it("does not find hidden fields") {}
|
139
|
+
end
|
140
|
+
|
141
|
+
context "with :with option" do
|
142
|
+
it("finds inputs that match option") {}
|
143
|
+
it("omits inputs that don't match option") {}
|
144
|
+
it("finds textareas that match option") {}
|
145
|
+
it("omits textareas that don't match option") {}
|
146
|
+
end
|
147
|
+
|
148
|
+
context "with :checked option" do
|
149
|
+
context "when true" do
|
150
|
+
it("finds checked fields") {}
|
151
|
+
it("omits unchecked fields") {}
|
152
|
+
end
|
153
|
+
context "when false" do
|
154
|
+
it("finds unchecked fields") {}
|
155
|
+
it("omits checked fields") {}
|
156
|
+
end
|
157
|
+
context "when ommitted" do
|
158
|
+
it("finds unchecked fields") {}
|
159
|
+
it("finds checked fields") {}
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
describe '#fillable_field' do
|
165
|
+
subject{ :fillable_field }
|
166
|
+
context "by parent label" do
|
167
|
+
it("finds inputs with text type") { get('Label text').should == 'id-text' }
|
168
|
+
it("finds inputs where label has problem chars") { get("Label text's got an apostrophe").should == 'id-problem-text' }
|
169
|
+
end
|
170
|
+
context "by placeholder" do
|
171
|
+
it("finds inputs with placeholder text") { get('Placeholder text').should == 'placeholder-text' }
|
172
|
+
end
|
173
|
+
|
174
|
+
end
|
175
|
+
|
176
|
+
describe "#optgroup" do
|
177
|
+
subject { :optgroup }
|
178
|
+
|
179
|
+
it("finds optgroups by label") { get('Group A').should == 'optgroup-a' }
|
180
|
+
end
|
181
|
+
|
182
|
+
describe "#table" do
|
183
|
+
subject {:table}
|
184
|
+
|
185
|
+
it("finds cell content regardless of whitespace") {get('whitespaced-table', :rows => [["I have nested whitespace", "I don't"]]).should == 'table-with-whitespace'}
|
186
|
+
end
|
187
|
+
end
|
data/spec/spec_helper.rb
ADDED
data/spec/union_spec.rb
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe XPath::Union do
|
4
|
+
let(:template) { File.read(File.expand_path('fixtures/simple.html', File.dirname(__FILE__))) }
|
5
|
+
let(:doc) { Nokogiri::HTML(template) }
|
6
|
+
|
7
|
+
describe '#expressions' do
|
8
|
+
it "should return the expressions" do
|
9
|
+
@expr1 = XPath.generate { |x| x.descendant(:p) }
|
10
|
+
@expr2 = XPath.generate { |x| x.descendant(:div) }
|
11
|
+
@collection = XPath::Union.new(@expr1, @expr2)
|
12
|
+
@collection.expressions.should == [@expr1, @expr2]
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe '#each' do
|
17
|
+
it "should iterate through the expressions" do
|
18
|
+
@expr1 = XPath.generate { |x| x.descendant(:p) }
|
19
|
+
@expr2 = XPath.generate { |x| x.descendant(:div) }
|
20
|
+
@collection = XPath::Union.new(@expr1, @expr2)
|
21
|
+
exprs = []
|
22
|
+
@collection.each { |expr| exprs << expr }
|
23
|
+
exprs.should == [@expr1, @expr2]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe '#map' do
|
28
|
+
it "should map the expressions" do
|
29
|
+
@expr1 = XPath.generate { |x| x.descendant(:p) }
|
30
|
+
@expr2 = XPath.generate { |x| x.descendant(:div) }
|
31
|
+
@collection = XPath::Union.new(@expr1, @expr2)
|
32
|
+
@collection.map { |expr| expr.class }.should == [XPath::Expression::Descendant, XPath::Expression::Descendant]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe '#to_xpath' do
|
37
|
+
it "should create a valid xpath expression" do
|
38
|
+
@expr1 = XPath.generate { |x| x.descendant(:p) }
|
39
|
+
@expr2 = XPath.generate { |x| x.descendant(:div).where(x.attr(:id) == 'foo') }
|
40
|
+
@collection = XPath::Union.new(@expr1, @expr2)
|
41
|
+
@results = doc.xpath(@collection.to_xpath)
|
42
|
+
@results[0][:title].should == 'fooDiv'
|
43
|
+
@results[1].text.should == 'Blah'
|
44
|
+
@results[2].text.should == 'Bax'
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe '#where, #apply and others' do
|
49
|
+
it "should be delegated to the individual expressions" do
|
50
|
+
@expr1 = XPath.generate { |x| x.descendant(:p) }
|
51
|
+
@expr2 = XPath.generate { |x| x.descendant(:div) }
|
52
|
+
@collection = XPath::Union.new(@expr1, @expr2)
|
53
|
+
@xpath1 = @collection.where(XPath.attr(:id) == 'foo').to_xpath
|
54
|
+
@xpath2 = @collection.where(XPath.attr(:id) == XPath.varstring(:id)).apply(:id => 'fooDiv').to_xpath
|
55
|
+
@results = doc.xpath(@xpath1)
|
56
|
+
@results[0][:title].should == 'fooDiv'
|
57
|
+
@results = doc.xpath(@xpath2)
|
58
|
+
@results[0][:id].should == 'fooDiv'
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
|