xpath 1.0.0.beta1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,53 +1,65 @@
1
- = XPath
1
+ # XPath
2
2
 
3
3
  XPath is a Ruby DSL around a subset of XPath 1.0. Its primary purpose is to
4
4
  facilitate writing complex XPath queries from Ruby code.
5
5
 
6
- {<img src="http://travis-ci.org/jnicklas/xpath.png" />}[http://travis-ci.org/jnicklas/xpath]
6
+ [![Build Status](https://secure.travis-ci.org/jnicklas/xpath.png?branch=master)](http://travis-ci.org/jnicklas/xpath)
7
7
 
8
- == Generating expressions
8
+ ## Generating expressions
9
9
 
10
- To create quick, one-off expressions, +XPath.generate+ can be used:
10
+ To create quick, one-off expressions, `XPath.generate` can be used:
11
11
 
12
- XPath.generate { |x| x.descendant(:ul)[x.attr(:id) == 'foo'] }
12
+ ``` ruby
13
+ XPath.generate { |x| x.descendant(:ul)[x.attr(:id) == 'foo'] }
14
+ ```
13
15
 
14
- You can also call expression methods directly on the XPath module:
16
+ You can also call expression methods directly on the `XPath` module:
15
17
 
16
- XPath.descendant(:ul)[XPath.attr(:id) == 'foo'] }
18
+ ``` ruby
19
+ XPath.descendant(:ul)[XPath.attr(:id) == 'foo'] }
20
+ ```
17
21
 
18
22
  However for more complex expressions, it is probably more convenient to include
19
- the XPath module into your own class or module:
23
+ the `XPath` module into your own class or module:
20
24
 
21
- module MyXPaths
22
- include XPath
25
+ ``` ruby
26
+ module MyXPaths
27
+ include XPath
23
28
 
24
- def foo_ul
25
- descendant(:ul)[attr(:id) == 'foo']
26
- end
29
+ def foo_ul
30
+ descendant(:ul)[attr(:id) == 'foo']
31
+ end
27
32
 
28
- def password_field(id)
29
- descendant(:input)[attr(:type) == 'password'][attr(:id) == id]
30
- end
31
- end
33
+ def password_field(id)
34
+ descendant(:input)[attr(:type) == 'password'][attr(:id) == id]
35
+ end
36
+ end
37
+ ```
32
38
 
33
- Both ways return an XPath::Expression instance, which can be further modified.
34
- To convert the expression to a string, just call #to_s on it. All available
35
- expressions are defined in XPath::DSL.
39
+ Both ways return an
40
+ [`XPath::Expression`](http://rdoc.info/github/jnicklas/xpath/XPath/Expression)
41
+ instance, which can be further modified. To convert the expression to a
42
+ string, just call `#to_s` on it. All available expressions are defined in
43
+ [`XPath::DSL`](http://rdoc.info/github/jnicklas/xpath/XPath/DSL).
36
44
 
37
- == String, Hashes and Symbols
45
+ ## String, Hashes and Symbols
38
46
 
39
47
  When you send a string as an argument to any XPath function, XPath assumes this
40
48
  to be a string literal. On the other hand if you send in Symbol, XPath assumes
41
49
  this to be an XPath literal. Thus the following two statements are not
42
50
  equivalent:
43
51
 
44
- XPath.descendant(:p)[XPath.attr(:id) == 'foo']
45
- XPath.descendant(:p)[XPath.attr(:id) == :foo]
52
+ ``` ruby
53
+ XPath.descendant(:p)[XPath.attr(:id) == 'foo']
54
+ XPath.descendant(:p)[XPath.attr(:id) == :foo]
55
+ ```
46
56
 
47
57
  These are the XPath expressions that these would be translated to:
48
58
 
49
- .//p[@id = 'foo']
50
- .//p[@id = foo]
59
+ ```
60
+ .//p[@id = 'foo']
61
+ .//p[@id = foo]
62
+ ```
51
63
 
52
64
  The second expression would match any p tag whose id attribute matches a 'foo'
53
65
  tag it contains. Most likely this is not what you want.
@@ -55,7 +67,9 @@ tag it contains. Most likely this is not what you want.
55
67
  In fact anything other than a String is treated as a literal. Thus the
56
68
  following works as expected:
57
69
 
58
- XPath.descendant(:p)[1]
70
+ ``` ruby
71
+ XPath.descendant(:p)[1]
72
+ ```
59
73
 
60
74
  Keep in mind that XPath is 1-indexed and not 0-indexed like most other
61
75
  programming languages, including Ruby.
@@ -63,32 +77,41 @@ programming languages, including Ruby.
63
77
  Hashes are automatically converted to equality expressions, so the above
64
78
  example could be written as:
65
79
 
66
- XPath.descendant(:p)[:@id => 'foo']
80
+ ``` ruby
81
+ XPath.descendant(:p)[:@id => 'foo']
82
+ ```
67
83
 
68
84
  Which would generate the same expression:
69
85
 
70
- .//p[@id = 'foo']
86
+ ```
87
+ .//p[@id = 'foo']
88
+ ```
71
89
 
72
90
  Note that the same rules apply here, both the keys and values in the hash are
73
91
  treated the same way as any other expression in XPath. Thus the following are
74
92
  not equivalent:
75
93
 
76
- XPath.descendant(:p)[:@id => 'foo'] # => .//p[@id = 'foo']
77
- XPath.descendant(:p)[:id => 'foo'] # => .//p[id = 'foo']
78
- XPath.descendant(:p)['id' => 'foo'] # => .//p['id' = 'foo']
94
+ ``` ruby
95
+ XPath.descendant(:p)[:@id => 'foo'] # => .//p[@id = 'foo']
96
+ XPath.descendant(:p)[:id => 'foo'] # => .//p[id = 'foo']
97
+ XPath.descendant(:p)['id' => 'foo'] # => .//p['id' = 'foo']
98
+ ```
79
99
 
80
- == HTML
100
+ ## HTML
81
101
 
82
102
  XPath comes with a set of premade XPaths for use with HTML documents.
83
103
 
84
104
  You can generate these like this:
85
105
 
86
- XPath::HTML.link('Home')
87
- XPath::HTML.field('Name')
106
+ ``` ruby
107
+ XPath::HTML.link('Home')
108
+ XPath::HTML.field('Name')
109
+ ```
88
110
 
89
- See XPath::HTML for all available matchers.
111
+ See [`XPath::HTML`](http://rdoc.info/github/jnicklas/xpath/XPath/HTML) for all
112
+ available matchers.
90
113
 
91
- == License
114
+ ## License
92
115
 
93
116
  (The MIT License)
94
117
 
@@ -21,6 +21,14 @@ module XPath
21
21
  Expression.new(:axis, current, name, tag_name)
22
22
  end
23
23
 
24
+ def next_sibling(*expressions)
25
+ Expression.new(:next_sibling, current, expressions)
26
+ end
27
+
28
+ def previous_sibling(*expressions)
29
+ Expression.new(:previous_sibling, current, expressions)
30
+ end
31
+
24
32
  def anywhere(expression)
25
33
  Expression.new(:anywhere, expression)
26
34
  end
@@ -58,10 +66,6 @@ module XPath
58
66
  end
59
67
  alias_method :[], :where
60
68
 
61
- def next_sibling(*expressions)
62
- Expression.new(:next_sibling, current, expressions)
63
- end
64
-
65
69
  def one_of(*expressions)
66
70
  Expression.new(:one_of, current, expressions)
67
71
  end
@@ -9,6 +9,7 @@ module XPath
9
9
  # Text, id, title, or image alt attribute of the link
10
10
  #
11
11
  def link(locator)
12
+ locator = locator.to_s
12
13
  link = descendant(:a)[attr(:href)]
13
14
  link[attr(:id).equals(locator) | string.n.contains(locator) | attr(:title).contains(locator) | descendant(:img)[attr(:alt).contains(locator)]]
14
15
  end
@@ -19,9 +20,10 @@ module XPath
19
20
  # Value, title, id, or image alt attribute of the button
20
21
  #
21
22
  def button(locator)
22
- button = descendant(:input)[attr(:type).one_of('submit', 'reset', 'image', 'button')][attr(:id).equals(locator) | attr(:value).contains(locator) | attr(:title).contains(locator)]
23
- button += descendant(:button)[attr(:id).equals(locator) | attr(:value).contains(locator) | string.n.contains(locator) | attr(:title).contains(locator)]
24
- button += descendant(:input)[attr(:type).equals('image')][attr(:alt).contains(locator)]
23
+ locator = locator.to_s
24
+ button = descendant(:input)[~attr(:disabled)][attr(:type).one_of('submit', 'reset', 'image', 'button')][attr(:id).equals(locator) | attr(:value).contains(locator) | attr(:title).contains(locator)]
25
+ button += descendant(:button)[~attr(:disabled)][attr(:id).equals(locator) | attr(:value).contains(locator) | string.n.contains(locator) | attr(:title).contains(locator)]
26
+ button += descendant(:input)[~attr(:disabled)][attr(:type).equals('image')][attr(:alt).contains(locator)]
25
27
  end
26
28
 
27
29
 
@@ -41,6 +43,7 @@ module XPath
41
43
  # Legend or id of the fieldset
42
44
  #
43
45
  def fieldset(locator)
46
+ locator = locator.to_s
44
47
  descendant(:fieldset)[attr(:id).equals(locator) | child(:legend)[string.n.contains(locator)]]
45
48
  end
46
49
 
@@ -52,6 +55,7 @@ module XPath
52
55
  # Label, id, or name of field to match
53
56
  #
54
57
  def field(locator)
58
+ locator = locator.to_s
55
59
  xpath = descendant(:input, :textarea, :select)[~attr(:type).one_of('submit', 'image', 'hidden')]
56
60
  xpath = locate_field(xpath, locator)
57
61
  xpath
@@ -66,6 +70,7 @@ module XPath
66
70
  # Label, id, or name of field to match
67
71
  #
68
72
  def fillable_field(locator)
73
+ locator = locator.to_s
69
74
  xpath = descendant(:input, :textarea)[~attr(:type).one_of('submit', 'image', 'radio', 'checkbox', 'hidden', 'file')]
70
75
  xpath = locate_field(xpath, locator)
71
76
  xpath
@@ -78,6 +83,7 @@ module XPath
78
83
  # Label, id, or name of the field to match
79
84
  #
80
85
  def select(locator)
86
+ locator = locator.to_s
81
87
  locate_field(descendant(:select), locator)
82
88
  end
83
89
 
@@ -88,6 +94,7 @@ module XPath
88
94
  # Label, id, or name of the checkbox to match
89
95
  #
90
96
  def checkbox(locator)
97
+ locator = locator.to_s
91
98
  locate_field(descendant(:input)[attr(:type).equals('checkbox')], locator)
92
99
  end
93
100
 
@@ -98,6 +105,7 @@ module XPath
98
105
  # Label, id, or name of the radio button to match
99
106
  #
100
107
  def radio_button(locator)
108
+ locator = locator.to_s
101
109
  locate_field(descendant(:input)[attr(:type).equals('radio')], locator)
102
110
  end
103
111
 
@@ -108,6 +116,7 @@ module XPath
108
116
  # Label, id, or name of the file field to match
109
117
  #
110
118
  def file_field(locator)
119
+ locator = locator.to_s
111
120
  locate_field(descendant(:input)[attr(:type).equals('file')], locator)
112
121
  end
113
122
 
@@ -117,8 +126,9 @@ module XPath
117
126
  # @param [String] name
118
127
  # Label for the option group
119
128
  #
120
- def optgroup(name)
121
- descendant(:optgroup)[attr(:label).contains(name)]
129
+ def optgroup(locator)
130
+ locator = locator.to_s
131
+ descendant(:optgroup)[attr(:label).contains(locator)]
122
132
  end
123
133
 
124
134
 
@@ -127,8 +137,9 @@ module XPath
127
137
  # @param [String] name
128
138
  # Visible text of the option
129
139
  #
130
- def option(name)
131
- descendant(:option)[string.n.equals(name)]
140
+ def option(locator)
141
+ locator = locator.to_s
142
+ descendant(:option)[string.n.equals(locator)]
132
143
  end
133
144
 
134
145
 
@@ -140,14 +151,25 @@ module XPath
140
151
  # Content of each cell in each row to match
141
152
  #
142
153
  def table(locator)
154
+ locator = locator.to_s
143
155
  descendant(:table)[attr(:id).equals(locator) | descendant(:caption).contains(locator)]
144
156
  end
145
157
 
158
+ # Match any 'dd' element.
159
+ #
160
+ # @param [String] locator
161
+ # Id of the 'dd' element or text from preciding 'dt' element content
162
+ def definition_description(locator)
163
+ locator = locator.to_s
164
+ descendant(:dd)[attr(:id).equals(locator) | previous_sibling(:dt)[string.n.equals(locator)] ]
165
+ end
166
+
146
167
  protected
147
168
 
148
169
  def locate_field(xpath, locator)
149
170
  locate_field = xpath[attr(:id).equals(locator) | attr(:name).equals(locator) | attr(:placeholder).equals(locator) | attr(:id).equals(anywhere(:label)[string.n.contains(locator)].attr(:for))]
150
171
  locate_field += descendant(:label)[string.n.contains(locator)].descendant(xpath)
172
+ locate_field[~attr(:disabled)]
151
173
  end
152
174
  end
153
175
  end
@@ -135,6 +135,16 @@ module XPath
135
135
  end
136
136
  end
137
137
 
138
+ def previous_sibling(current, element_names)
139
+ if element_names.length == 1
140
+ "#{current}/preceding-sibling::*[1]/self::#{element_names.first}"
141
+ elsif element_names.length > 1
142
+ "#{current}/preceding-sibling::*[1]/self::*[#{element_names.map { |e| "self::#{e}" }.join(" | ")}]"
143
+ else
144
+ "#{current}/preceding-sibling::*[1]/self::*"
145
+ end
146
+ end
147
+
138
148
  def inverse(current)
139
149
  "not(#{current})"
140
150
  end
@@ -1,3 +1,3 @@
1
1
  module XPath
2
- VERSION = '1.0.0.beta1'
2
+ VERSION = '1.0.0'
3
3
  end
@@ -28,7 +28,7 @@
28
28
  <input type="submit" title="My submit title" value="submit-with-title" data="title-submit">
29
29
  <input type="submit" title="Exact submit title" value="exact title submit" data="exact-title-submit">
30
30
  <input type="submit" title="Not Exact submit title" value="exact title submit" data="not-exact-title-submit">
31
-
31
+
32
32
  <input type="reset" id="reset-with-id" data="id-reset" value="Has ID"/>
33
33
  <input type="reset" value="reset-with-value" data="value-reset"/>
34
34
  <input type="reset" value="not exact value reset" data="not-exact-value-reset"/>
@@ -36,7 +36,7 @@
36
36
  <input type="reset" title="My reset title" value="reset-with-title" data="title-reset">
37
37
  <input type="reset" title="Exact reset title" value="exact title reset" data="exact-title-reset">
38
38
  <input type="reset" title="Not Exact reset title" value="exact title reset" data="not-exact-title-reset">
39
-
39
+
40
40
  <input type="button" id="button-with-id" data="id-button" value="Has ID"/>
41
41
  <input type="button" value="button-with-value" data="value-button"/>
42
42
  <input type="button" value="not exact value button" data="not-exact-value-button"/>
@@ -77,6 +77,8 @@
77
77
  <input type="schmoo" value="schmoo button" data="schmoo"/>
78
78
  <label for="text-with-id">Label text <input type="text" id="text-with-id" data="id-text" value="monkey"/></label>
79
79
  <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>
80
+
81
+ <input disabled type="submit" id="disabled-submit" data="submit-disabled" value=""/>
80
82
  </p>
81
83
 
82
84
  <p>
@@ -185,4 +187,15 @@
185
187
  <label>Input hidden with parent label<input type="hidden" data="input-hidden-with-parent-label-data"/></label>
186
188
  <label>Input checkbox with parent label<input type="checkbox" data="input-checkbox-with-parent-label-data"/></label>
187
189
  <label>Input radio with parent label<input type="radio" data="input-radio-with-parent-label-data"/></label>
190
+
191
+ <h4>Disabled</h4>
192
+ <input disabled id="input-disabled" value="correct-value" data="input-data"/>
193
+ <input disabled type="text" id="input-text-disabled" data="input-text-data"/>
194
+ <input disabled type="file" id="input-file-disabled" data="input-file-data"/>
195
+ <input disabled type="password" id="input-password-disabled" data="input-password-data"/>
196
+ <input disabled type="custom" id="input-custom-disabled" data="input-custom-data"/>
197
+ <textarea disabled id="textarea-with-id-data-disabled">Correct value</textarea>
198
+ <select disabled id="select-with-id-data-disabled"></select>
199
+ <input disabled type="checkbox" id="input-checkbox-disabled" data="input-checkbox-data"/>
200
+ <input disabled type="radio" id="input-radio-disabled" data="input-radio-data"/>
188
201
  </p>
@@ -11,6 +11,13 @@
11
11
  <ul><li>A list</li></ul>
12
12
  </div>
13
13
 
14
+ <div id="woo" title="wooDiv" data="id">
15
+ <ul><li>A list</li></ul>
16
+ <p title="gorilla">Bax</p>
17
+ <p>Bax</p>
18
+ <p id="wooDiv">Blah</p>
19
+ </div>
20
+
14
21
  <div id="baz" title="bazDiv"></div>
15
22
 
16
23
  <div id="preference">
@@ -41,3 +41,14 @@
41
41
  <div id="hidden_via_ancestor">Inside element with hidden ancestor</div>
42
42
  <a href="/with_simple_html" title="awesome title" class="simple">hidden link</a>
43
43
  </div>
44
+
45
+ <div>
46
+ <dl>
47
+ <dt>Coffee</dt>
48
+ <dd id="latte" data="with-id">black hot drink</dd>
49
+
50
+ <dt>Milk</dt>
51
+ <dd data="with-dt">white cold drink</dd>
52
+ </dl>
53
+ </div>
54
+
@@ -28,6 +28,7 @@ describe XPath::HTML do
28
28
  it("finds links by image's alt attribute") { get('Alt link').should == 'link-img' }
29
29
  it("finds links by image's approximate alt attribute") { get('Alt').should == 'link-img' }
30
30
  it("does not find links without href attriutes") { get('Wrong Link').should be_nil }
31
+ it("casts to string") { get(:'some-id').should == 'link-id' }
31
32
  end
32
33
 
33
34
  describe '#button' do
@@ -82,6 +83,9 @@ describe XPath::HTML do
82
83
  context "with unkown type" do
83
84
  it("does not find the button") { get('schmoo button').should be_nil }
84
85
  end
86
+
87
+ it("") { get('disabled-submit').should be_nil }
88
+ it("casts to string") { get(:'tag-with-tex').should == 'text-btag' }
85
89
  end
86
90
 
87
91
  describe '#fieldset' do
@@ -92,6 +96,7 @@ describe XPath::HTML do
92
96
  it("finds fieldsets by legend child tags") { get('Span Legend').should == 'fieldset-legend-span' }
93
97
  it("accepts approximate legends") { get('Legend').should == 'fieldset-legend' }
94
98
  it("finds nested fieldsets by legend") { get('Inner legend').should == 'fieldset-inner' }
99
+ it("casts to string") { get(:'Inner legend').should == 'fieldset-inner' }
95
100
  end
96
101
 
97
102
  describe '#field' do
@@ -153,6 +158,17 @@ describe XPath::HTML do
153
158
  it("does not find image buttons") { get('Input image with parent label').should be_nil }
154
159
  it("does not find hidden fields") { get('Input hidden with parent label').should be_nil }
155
160
  end
161
+
162
+ context "is diabled" do
163
+ it("does not find inputs with no type") { get('input-disabled').should be_nil }
164
+ it("does not find inputs with text type") { get('input-text-disabled').should be_nil }
165
+ it("does not find inputs with password type") { get('input-password-disabled').should be_nil }
166
+ it("does not find inputs with custom type") { get('input-custom-disabled').should be_nil }
167
+ it("does not find textareas") { get('textarea-disabled').should be_nil }
168
+ it("does not find select boxes") { get('select-disabled').should be_nil }
169
+ end
170
+
171
+ it("casts to string") { get(:'select-with-id').should == 'select-with-id-data' }
156
172
  end
157
173
 
158
174
  describe '#fillable_field' do
@@ -170,48 +186,65 @@ describe XPath::HTML do
170
186
  it("finds selects by name") { get('select-with-name').should == 'select-with-name-data' }
171
187
  it("finds selects by label") { get('Select with label').should == 'select-with-label-data' }
172
188
  it("finds selects by parent label") { get('Select with parent label').should == 'select-with-parent-label-data' }
189
+ it("does not find disabled selects") { get('select-disabled').should be_nil }
190
+ it("casts to string") { get(:'Select with parent label').should == 'select-with-parent-label-data' }
173
191
  end
174
192
 
175
193
  describe '#checkbox' do
176
194
  subject{ :checkbox }
177
- it("finds checkboxes by id") { get('input-checkbox-with-id').should == 'input-checkbox-with-id-data' }
178
- it("finds checkboxes by name") { get('input-checkbox-with-name').should == 'input-checkbox-with-name-data' }
179
- it("finds checkboxes by label") { get('Input checkbox with label').should == 'input-checkbox-with-label-data' }
195
+ it("finds checkboxes by id") { get('input-checkbox-with-id').should == 'input-checkbox-with-id-data' }
196
+ it("finds checkboxes by name") { get('input-checkbox-with-name').should == 'input-checkbox-with-name-data' }
197
+ it("finds checkboxes by label") { get('Input checkbox with label').should == 'input-checkbox-with-label-data' }
180
198
  it("finds checkboxes by parent label") { get('Input checkbox with parent label').should == 'input-checkbox-with-parent-label-data' }
199
+ it("does not find disabled") { get('input-checkbox-disabled').should be_nil }
200
+ it("casts to string") { get(:'Input checkbox with parent label').should == 'input-checkbox-with-parent-label-data' }
181
201
  end
182
202
 
183
203
  describe '#radio_button' do
184
204
  subject{ :radio_button }
185
- it("finds radio buttons by id") { get('input-radio-with-id').should == 'input-radio-with-id-data' }
186
- it("finds radio buttons by name") { get('input-radio-with-name').should == 'input-radio-with-name-data' }
187
- it("finds radio buttons by label") { get('Input radio with label').should == 'input-radio-with-label-data' }
205
+ it("finds radio buttons by id") { get('input-radio-with-id').should == 'input-radio-with-id-data' }
206
+ it("finds radio buttons by name") { get('input-radio-with-name').should == 'input-radio-with-name-data' }
207
+ it("finds radio buttons by label") { get('Input radio with label').should == 'input-radio-with-label-data' }
188
208
  it("finds radio buttons by parent label") { get('Input radio with parent label').should == 'input-radio-with-parent-label-data' }
189
-
209
+ it("does not find disabled") { get('input-radio-disabled').should be_nil }
210
+ it("casts to string") { get(:'Input radio with parent label').should == 'input-radio-with-parent-label-data' }
190
211
  end
191
212
 
192
213
  describe '#file_field' do
193
214
  subject{ :file_field }
194
- it("finds file fields by id") { get('input-file-with-id').should == 'input-file-with-id-data' }
195
- it("finds file fields by name") { get('input-file-with-name').should == 'input-file-with-name-data' }
196
- it("finds file fields by label") { get('Input file with label').should == 'input-file-with-label-data' }
215
+ it("finds file fields by id") { get('input-file-with-id').should == 'input-file-with-id-data' }
216
+ it("finds file fields by name") { get('input-file-with-name').should == 'input-file-with-name-data' }
217
+ it("finds file fields by label") { get('Input file with label').should == 'input-file-with-label-data' }
197
218
  it("finds file fields by parent label") { get('Input file with parent label').should == 'input-file-with-parent-label-data' }
198
- end
199
-
200
- describe '#option' do
201
- subject{ :option }
202
- it("finds options by text") { get('Option with text').should == 'option-with-text-data' }
203
- it("does not find option by partial text") { get('Option with').should be_nil }
219
+ it("does not find disabled") { get('input-file-disabled').should be_nil }
220
+ it("casts to string") { get(:'Input file with parent label').should == 'input-file-with-parent-label-data' }
204
221
  end
205
222
 
206
223
  describe "#optgroup" do
207
224
  subject { :optgroup }
208
225
  it("finds optgroups by label") { get('Group A').should == 'optgroup-a' }
226
+ it("casts to string") { get(:'Group A').should == 'optgroup-a' }
227
+ end
228
+
229
+ describe '#option' do
230
+ subject{ :option }
231
+ it("finds options by text") { get('Option with text').should == 'option-with-text-data' }
232
+ it("does not find option by partial text") { get('Option with').should be_nil }
233
+ it("casts to string") { get(:'Option with text').should == 'option-with-text-data' }
209
234
  end
210
235
 
211
236
  describe "#table" do
212
237
  subject {:table}
213
- it("finds tables by id") { get('table-with-id').should == 'table-with-id-data' }
238
+ it("finds tables by id") { get('table-with-id').should == 'table-with-id-data' }
214
239
  it("finds tables by caption") { get('Table with caption').should == 'table-with-caption-data' }
240
+ it("casts to string") { get(:'Table with caption').should == 'table-with-caption-data' }
215
241
  end
216
242
 
243
+ describe "#definition_description" do
244
+ subject {:definition_description}
245
+ let(:template) {'stuff'}
246
+ it("find definition description by id") { get('latte').should == "with-id" }
247
+ it("find definition description by term") { get("Milk").should == "with-dt" }
248
+ it("casts to string") { get(:"Milk").should == "with-dt" }
249
+ end
217
250
  end
@@ -98,6 +98,16 @@ describe XPath do
98
98
  end
99
99
  end
100
100
 
101
+ 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'
106
+ 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'
108
+ end
109
+ end
110
+
101
111
  describe '#anywhere' do
102
112
  it "should find nodes regardless of the context" do
103
113
  @results = xpath do |x|
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: xpath
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0.beta1
5
- prerelease: 6
4
+ version: 1.0.0
5
+ prerelease:
6
6
  platform: ruby
7
7
  authors:
8
8
  - Jonas Nicklas
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-07-13 00:00:00.000000000 Z
12
+ date: 2012-11-14 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: nokogiri
@@ -81,7 +81,7 @@ email:
81
81
  executables: []
82
82
  extensions: []
83
83
  extra_rdoc_files:
84
- - README.rdoc
84
+ - README.md
85
85
  files:
86
86
  - lib/xpath/dsl.rb
87
87
  - lib/xpath/expression.rb
@@ -98,13 +98,13 @@ files:
98
98
  - spec/spec_helper.rb
99
99
  - spec/union_spec.rb
100
100
  - spec/xpath_spec.rb
101
- - README.rdoc
101
+ - README.md
102
102
  homepage: http://github.com/jnicklas/xpath
103
103
  licenses: []
104
104
  post_install_message:
105
105
  rdoc_options:
106
106
  - --main
107
- - README.rdoc
107
+ - README.md
108
108
  require_paths:
109
109
  - lib
110
110
  required_ruby_version: !ruby/object:Gem::Requirement
@@ -116,12 +116,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
116
116
  required_rubygems_version: !ruby/object:Gem::Requirement
117
117
  none: false
118
118
  requirements:
119
- - - ! '>'
119
+ - - ! '>='
120
120
  - !ruby/object:Gem::Version
121
- version: 1.3.1
121
+ version: '0'
122
122
  requirements: []
123
123
  rubyforge_project: xpath
124
- rubygems_version: 1.8.21
124
+ rubygems_version: 1.8.24
125
125
  signing_key:
126
126
  specification_version: 3
127
127
  summary: Generate XPath expressions from Ruby