xpath 0.1.2 → 0.1.3

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 CHANGED
@@ -1,6 +1,7 @@
1
1
  = XPath
2
2
 
3
- XPath is a Ruby DSL around a subset of XPath 1.0. It's primary purpose is to facilitate writing complex XPath queries from Ruby code.
3
+ XPath is a Ruby DSL around a subset of XPath 1.0. It's primary purpose is to
4
+ facilitate writing complex XPath queries from Ruby code.
4
5
 
5
6
  == Generating expressions
6
7
 
@@ -8,7 +9,8 @@ To create quick, one of expressions, XPath.generate can be used:
8
9
 
9
10
  XPath.generate { |x| x.descendant(:ul)[x.attr(:id) == 'foo'] }
10
11
 
11
- However for more complex expressions, it is probably ore convenient to include the XPath module into your own class or module:
12
+ However for more complex expressions, it is probably ore convenient to include
13
+ the XPath module into your own class or module:
12
14
 
13
15
  module MyXPaths
14
16
  include XPath
@@ -22,8 +24,33 @@ However for more complex expressions, it is probably ore convenient to include t
22
24
  end
23
25
  end
24
26
 
25
- Both ways return an XPath::Expression instance, which can be further modified. To convert the expression to a string, just call #to_s on it.
27
+ Both ways return an XPath::Expression instance, which can be further modified.
28
+ To convert the expression to a string, just call #to_s on it.
26
29
 
27
30
  == HTML
28
31
 
29
32
  XPath comes with a set of premade XPaths for use with HTML documents.
33
+
34
+ == License
35
+
36
+ (The MIT License)
37
+
38
+ Copyright © 2010 Jonas Nicklas
39
+
40
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
41
+ this software and associated documentation files (the ‘Software’), to deal in
42
+ the Software without restriction, including without limitation the rights to
43
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
44
+ of the Software, and to permit persons to whom the Software is furnished to do
45
+ so, subject to the following conditions:
46
+
47
+ The above copyright notice and this permission notice shall be included in all
48
+ copies or substantial portions of the Software.
49
+
50
+ THE SOFTWARE IS PROVIDED ‘AS IS’, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
51
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
52
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
53
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
54
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
55
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
56
+ SOFTWARE.
@@ -126,14 +126,15 @@ module XPath
126
126
  end
127
127
 
128
128
  def to_xpath(predicate=nil)
129
- @expression = @expression.to_xpath(predicate) unless @expression.is_a?(String)
130
- if @expression.include?("'")
131
- @expression = @expression.split("'", -1).map do |substr|
129
+ string = @expression
130
+ string = @expression.to_xpath(predicate) unless @expression.is_a?(String)
131
+ if string.include?("'")
132
+ string = string.split("'", -1).map do |substr|
132
133
  "'#{substr}'"
133
134
  end.join(%q{,"'",})
134
- "concat(#{@expression})"
135
+ "concat(#{string})"
135
136
  else
136
- "'#{@expression}'"
137
+ "'#{string}'"
137
138
  end
138
139
  end
139
140
  end
data/lib/xpath/html.rb CHANGED
@@ -3,8 +3,9 @@ module XPath
3
3
  include XPath
4
4
  extend self
5
5
 
6
- def link(locator)
7
- link = descendant(:a)[attr(:href)]
6
+ def link(locator, options={})
7
+ href = options[:href]
8
+ link = descendant(:a)[href ? attr(:href).equals(href) : attr(:href)]
8
9
  link[attr(:id).equals(locator) | string.n.is(locator) | attr(:title).is(locator) | descendant(:img)[attr(:alt).is(locator)]]
9
10
  end
10
11
 
@@ -23,7 +24,7 @@ module XPath
23
24
  end
24
25
 
25
26
  def fieldset(locator)
26
- descendant(:fieldset)[attr(:id).equals(locator) | descendant(:legend)[text.is(locator)]]
27
+ descendant(:fieldset)[attr(:id).equals(locator) | descendant(:legend)[string.n.is(locator)]]
27
28
  end
28
29
 
29
30
  def field(locator, options={})
@@ -46,11 +47,11 @@ module XPath
46
47
  xpath = locate_field(descendant(:select), locator)
47
48
 
48
49
  options[:options].each do |option|
49
- xpath = xpath[descendant(:option).text.equals(option)]
50
+ xpath = xpath[descendant(:option).equals(option)]
50
51
  end if options[:options]
51
52
 
52
53
  [options[:selected]].flatten.each do |option|
53
- xpath = xpath[descendant(:option)[attr(:selected)].text.equals(option)]
54
+ xpath = xpath[descendant(:option)[attr(:selected)].equals(option)]
54
55
  end if options[:selected]
55
56
 
56
57
  xpath
@@ -68,8 +69,12 @@ module XPath
68
69
  locate_field(descendant(:input)[attr(:type).equals('file')], locator)
69
70
  end
70
71
 
72
+ def optgroup(name)
73
+ descendant(:optgroup)[attr(:label).is(name)]
74
+ end
75
+
71
76
  def option(name)
72
- descendant(:option)[text.n.is(name)]
77
+ descendant(:option)[string.n.is(name)]
73
78
  end
74
79
 
75
80
  def table(locator, options={})
@@ -87,9 +92,9 @@ module XPath
87
92
  end
88
93
 
89
94
  def table_row(cells)
90
- cell_conditions = child(:td, :th)[text.equals(cells.first)]
95
+ cell_conditions = child(:td, :th)[string.n.equals(cells.first)]
91
96
  cells.drop(1).each do |cell|
92
- cell_conditions = cell_conditions.next_sibling(:td, :th)[text.equals(cell)]
97
+ cell_conditions = cell_conditions.next_sibling(:td, :th)[string.n.equals(cell)]
93
98
  end
94
99
  cell_conditions
95
100
  end
@@ -97,12 +102,12 @@ module XPath
97
102
  protected
98
103
 
99
104
  def locate_field(xpath, locator)
100
- locate_field = xpath[attr(:id).equals(locator) | attr(:name).equals(locator) | attr(:id).equals(anywhere(:label)[text.is(locator)].attr(:for))]
101
- locate_field += descendant(:label)[text.is(locator)].descendant(xpath)
105
+ locate_field = xpath[attr(:id).equals(locator) | attr(:name).equals(locator) | attr(:id).equals(anywhere(:label)[string.n.is(locator)].attr(:for))]
106
+ locate_field += descendant(:label)[string.n.is(locator)].descendant(xpath)
102
107
  end
103
108
 
104
109
  def field_value(value)
105
- (text.is(value) & tag(:textarea)) | (attr(:value).equals(value) & ~tag(:textarea))
110
+ (string.n.is(value) & tag(:textarea)) | (attr(:value).equals(value) & ~tag(:textarea))
106
111
  end
107
112
 
108
113
  end
data/lib/xpath/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module XPath
2
- VERSION = '0.1.2'
2
+ VERSION = '0.1.3'
3
3
  end
@@ -9,6 +9,7 @@
9
9
  <a title="This title" data="link-exact" href="#foo">A link</a>
10
10
  <a href="#bar" data="link-img-fuzzy"><img src="foo.png" alt="An image that is beautiful"/></a>
11
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>
12
13
  <a>Wrong Link</a>
13
14
  <a href="#spacey" data="link-whitespace"> My
14
15
 
@@ -54,15 +55,32 @@
54
55
  </button>
55
56
 
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
+ <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>
57
60
  </p>
58
61
 
59
62
  <p>
60
63
  <fieldset id="some-fieldset-id" data="fieldset-id"></fieldset>
61
64
  <fieldset data="fieldset-legend"><legend>Some Legend</legend></fieldset>
65
+ <fieldset data="fieldset-legend-span"><legend><span>Span Legend</span></legend></fieldset>
62
66
  <fieldset data="fieldset-fuzzy"><legend>Long legend yo</legend></fieldset>
63
67
  <fieldset data="fieldset-exact"><legend>Long legend</legend></fieldset>
64
68
  </p>
65
69
 
66
70
  <p>
71
+ <select>
72
+ <optgroup label="Group A" data="optgroup-a"></optgroup>
73
+ <optgroup label="Group B" data="optgroup-b"></optgroup>
74
+ </select>
75
+ </p>
67
76
 
77
+ <p>
78
+ <table id="whitespaced-table" data="table-with-whitespace">
79
+ <tr>
80
+ <td data="cell-whitespaced">I have
81
+ <span>nested whitespace</span>
82
+ </td>
83
+ <td>I don't</td>
84
+ </tr>
85
+ </table>
68
86
  </p>
@@ -27,6 +27,6 @@ A lot
27
27
 
28
28
  <div id="moar">
29
29
  <p id="impchay">chimp</p>
30
- <p id="amingoflay">flamingo</p>
31
30
  <div id="elephantay">elephant</div>
31
+ <p id="amingoflay">flamingo</p>
32
32
  </div>
data/spec/html_spec.rb CHANGED
@@ -33,6 +33,8 @@ describe XPath::HTML do
33
33
  it("finds links by image's approximate alt attribute") { get('Alt').should == 'link-img' }
34
34
  it("prefers exact matches of image's alt attribute") { all('An image').should == ['link-img-exact', 'link-img-fuzzy'] }
35
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 }
36
38
  end
37
39
 
38
40
  describe '#button' do
@@ -81,10 +83,11 @@ describe XPath::HTML do
81
83
  describe '#fieldset' do
82
84
  subject { :fieldset }
83
85
 
84
- it("finds fieldsets by id") { get('some-fieldset-id').should == 'fieldset-id' }
85
- it("finds fieldsets by legend") { get('Some Legend').should == 'fieldset-legend' }
86
- it("accepts approximate legends") { get('Legend').should == 'fieldset-legend' }
87
- it("prefers exact legend") { all('Long legend').should == ['fieldset-exact', 'fieldset-fuzzy'] }
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'] }
88
91
  end
89
92
 
90
93
  describe '#field' do
@@ -124,14 +127,15 @@ describe XPath::HTML do
124
127
  end
125
128
 
126
129
  context "by parent label" do
127
- it("finds inputs with text type") {}
128
- it("finds inputs with password type") {}
129
- it("finds inputs with custom type") {}
130
- it("finds textareas") {}
131
- it("finds select boxes") {}
132
- it("does not find submit buttons") {}
133
- it("does not find image buttons") {}
134
- it("does not find hidden fields") {}
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") {}
135
139
  end
136
140
 
137
141
  context "with :with option" do
@@ -158,6 +162,23 @@ describe XPath::HTML do
158
162
  end
159
163
 
160
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
+
171
+ end
172
+
173
+ describe "#optgroup" do
174
+ subject { :optgroup }
175
+
176
+ it("finds optgroups by label") { get('Group A').should == 'optgroup-a' }
177
+ end
178
+
179
+ describe "#table" do
180
+ subject {:table}
161
181
 
182
+ it("finds cell content regardless of whitespace") {get('whitespaced-table', :rows => [["I have nested whitespace", "I don't"]]).should == 'table-with-whitespace'}
162
183
  end
163
184
  end
data/spec/xpath_spec.rb CHANGED
@@ -265,11 +265,17 @@ describe XPath do
265
265
  @results[1].text.should == 'flamingo'
266
266
  end
267
267
 
268
+ it "should be composable" do
269
+ @results = xpath { |x| x.css('#moar').descendant(:p) }
270
+ @results[0].text.should == 'chimp'
271
+ @results[1].text.should == 'flamingo'
272
+ end
273
+
268
274
  it "should allow comma separated selectors" do
269
275
  @results = xpath { |x| x.descendant[x.attr(:id) == 'moar'].css('div, p') }
270
276
  @results[0].text.should == 'chimp'
271
- @results[1].text.should == 'flamingo'
272
- @results[2].text.should == 'elephant'
277
+ @results[1].text.should == 'elephant'
278
+ @results[2].text.should == 'flamingo'
273
279
  end
274
280
  end
275
281
 
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 1
8
- - 2
9
- version: 0.1.2
8
+ - 3
9
+ version: 0.1.3
10
10
  platform: ruby
11
11
  authors:
12
12
  - Jonas Nicklas
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-10-08 00:00:00 +02:00
17
+ date: 2011-01-09 00:00:00 +01:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency