dill 0.4.4 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/dill.rb +3 -0
- data/lib/dill/auto_table.rb +3 -3
- data/lib/dill/checkpoint.rb +19 -64
- data/lib/dill/dsl.rb +2 -2
- data/lib/dill/field_group.rb +3 -3
- data/lib/dill/list.rb +147 -20
- data/lib/dill/list_item.rb +22 -0
- data/lib/dill/version.rb +1 -1
- data/lib/dill/widget.rb +183 -33
- data/lib/dill/widget_checkpoint.rb +30 -0
- data/lib/dill/widget_class.rb +11 -0
- data/lib/dill/widget_container.rb +2 -2
- metadata +21 -2
data/lib/dill.rb
CHANGED
@@ -3,12 +3,15 @@ require 'nokogiri'
|
|
3
3
|
require 'capybara'
|
4
4
|
|
5
5
|
require 'dill/checkpoint'
|
6
|
+
require 'dill/widget_class'
|
7
|
+
require 'dill/widget_checkpoint'
|
6
8
|
require 'dill/widget_container'
|
7
9
|
require 'dill/conversions'
|
8
10
|
require 'dill/instance_conversions'
|
9
11
|
require 'dill/node_text'
|
10
12
|
require 'dill/widget_name'
|
11
13
|
require 'dill/widget'
|
14
|
+
require 'dill/list_item'
|
12
15
|
require 'dill/list'
|
13
16
|
require 'dill/base_table'
|
14
17
|
require 'dill/auto_table'
|
data/lib/dill/auto_table.rb
CHANGED
@@ -50,11 +50,11 @@ module Dill
|
|
50
50
|
|
51
51
|
class Row < Widget
|
52
52
|
def initialize(settings)
|
53
|
-
|
53
|
+
root = settings.delete(:root)
|
54
54
|
|
55
|
-
self.cell_selector =
|
55
|
+
self.cell_selector = settings.delete(:cell_selector)
|
56
56
|
|
57
|
-
super
|
57
|
+
super root
|
58
58
|
end
|
59
59
|
|
60
60
|
def values
|
data/lib/dill/checkpoint.rb
CHANGED
@@ -5,25 +5,15 @@ module Dill
|
|
5
5
|
# @see http://rubydoc.info/github/jnicklas/capybara/master/Capybara/Node/Base#synchronize-instance_method,
|
6
6
|
# which inspired this class.
|
7
7
|
class Checkpoint
|
8
|
-
class ConditionNotMet <
|
8
|
+
class ConditionNotMet < StandardError; end
|
9
9
|
class TimeFrozen < StandardError; end
|
10
10
|
|
11
11
|
# @return the configured wait time, in seconds.
|
12
12
|
attr_reader :wait_time
|
13
13
|
|
14
|
-
#
|
15
|
-
def self.
|
16
|
-
|
17
|
-
end
|
18
|
-
|
19
|
-
# Executes +block+ repeatedly until it returns a "truthy" value or +timeout+
|
20
|
-
# expires.
|
21
|
-
#
|
22
|
-
# TODO: Expand documentation.
|
23
|
-
def self.wait_for(wait_time = Capybara.default_wait_time,
|
24
|
-
raise_errors = true,
|
25
|
-
&block)
|
26
|
-
new(wait_time).wait_for(raise_errors, &block)
|
14
|
+
# Shortcut for instance level wait_for.
|
15
|
+
def self.wait_for(wait_time = Capybara.default_wait_time, &block)
|
16
|
+
new(wait_time).wait_for(&block)
|
27
17
|
end
|
28
18
|
|
29
19
|
# Initializes a new Checkpoint.
|
@@ -34,47 +24,24 @@ module Dill
|
|
34
24
|
@wait_time = wait_time
|
35
25
|
end
|
36
26
|
|
37
|
-
#
|
38
|
-
#
|
39
|
-
# Automatically rescues some exceptions ({Capybara::ElementNotFound}, and
|
40
|
-
# driver specific exceptions) until {wait_time} is exceeded. At that point
|
41
|
-
# it raises whatever exception was raised in the condition block, or
|
42
|
-
# {ConditionNotMet}, if no exception was raised inside the block. However,
|
43
|
-
# if +raise_errors+ is set to +false+, returns +false+ instead of
|
44
|
-
# propagating any of the automatically rescued exceptions.
|
45
|
-
#
|
46
|
-
# If an "unknown" exception is raised, it is propagated immediately, without
|
47
|
-
# waiting for {wait_time} to expire.
|
48
|
-
#
|
49
|
-
# If a driver that doesn't support waiting is used, any exception raised is
|
50
|
-
# immediately propagated.
|
27
|
+
# Executes +block+ repeatedly until it returns a "truthy" value or
|
28
|
+
# +wait_time+ expires.
|
51
29
|
#
|
52
|
-
#
|
53
|
-
#
|
30
|
+
# Swallows any StandardError or StandardError descendent until +wait_time+
|
31
|
+
# expires. If an exception is raised and the time has expired, that
|
32
|
+
# exception will be raised again.
|
54
33
|
#
|
55
|
-
#
|
56
|
-
#
|
34
|
+
# If the block does not return a "truthy" value until +wait_time+ expires,
|
35
|
+
# raises a Dill::Checkpoint::ConditionNotMet error.
|
57
36
|
#
|
58
|
-
#
|
59
|
-
|
60
|
-
# +rescue_errors+ is false.
|
61
|
-
def wait_for(raise_errors = true, &condition)
|
37
|
+
# Returns whatever value is returned by the block.
|
38
|
+
def wait_for(&condition)
|
62
39
|
start
|
63
40
|
|
64
41
|
begin
|
65
42
|
yield or raise ConditionNotMet
|
66
|
-
rescue *rescuable_errors
|
67
|
-
if
|
68
|
-
raise e if raise_errors
|
69
|
-
|
70
|
-
return false
|
71
|
-
end
|
72
|
-
|
73
|
-
if expired?
|
74
|
-
raise e if raise_errors
|
75
|
-
|
76
|
-
return false
|
77
|
-
end
|
43
|
+
rescue *rescuable_errors
|
44
|
+
raise if expired?
|
78
45
|
|
79
46
|
wait
|
80
47
|
|
@@ -84,34 +51,22 @@ module Dill
|
|
84
51
|
end
|
85
52
|
end
|
86
53
|
|
54
|
+
def rescuable_errors
|
55
|
+
StandardError
|
56
|
+
end
|
57
|
+
|
87
58
|
private
|
88
59
|
|
89
60
|
attr_reader :start_time
|
90
61
|
|
91
|
-
def driver
|
92
|
-
self.class.driver
|
93
|
-
end
|
94
|
-
|
95
|
-
def driver_errors
|
96
|
-
driver.invalid_element_errors
|
97
|
-
end
|
98
|
-
|
99
62
|
def expired?
|
100
63
|
remaining_time > wait_time
|
101
64
|
end
|
102
65
|
|
103
|
-
def immediate?
|
104
|
-
! driver.wait?
|
105
|
-
end
|
106
|
-
|
107
66
|
def remaining_time
|
108
67
|
Time.now - start_time
|
109
68
|
end
|
110
69
|
|
111
|
-
def rescuable_errors
|
112
|
-
@rescuable_errors ||= [Capybara::ElementNotFound, *driver_errors]
|
113
|
-
end
|
114
|
-
|
115
70
|
def start
|
116
71
|
@start_time = Time.now
|
117
72
|
end
|
data/lib/dill/dsl.rb
CHANGED
data/lib/dill/field_group.rb
CHANGED
@@ -244,12 +244,12 @@ module Dill
|
|
244
244
|
|
245
245
|
# A form field.
|
246
246
|
class Field < Widget
|
247
|
-
def self.find_in(parent
|
248
|
-
new(
|
247
|
+
def self.find_in(parent)
|
248
|
+
new(parent.find_field(*selector))
|
249
249
|
end
|
250
250
|
|
251
251
|
def self.present_in?(parent)
|
252
|
-
parent.has_field?(selector)
|
252
|
+
parent.has_field?(*selector)
|
253
253
|
end
|
254
254
|
|
255
255
|
# @return This field's value.
|
data/lib/dill/list.rb
CHANGED
@@ -1,47 +1,174 @@
|
|
1
1
|
module Dill
|
2
|
+
# Use a List when you want to treat repeating elements as a unit.
|
3
|
+
#
|
4
|
+
# === Usage
|
5
|
+
#
|
6
|
+
# Consider the following HTML:
|
7
|
+
#
|
8
|
+
# <ul id="colors">
|
9
|
+
# <li>Red <span class="pt">Vermelho</span></li>
|
10
|
+
# <li>Green <span class="pt">Verde</span></li>
|
11
|
+
# <li>Blue <span class="pt">Azul</span></li>
|
12
|
+
# </ul>
|
13
|
+
#
|
14
|
+
# You can then define the following widget:
|
15
|
+
#
|
16
|
+
# class Colors < Dill::List
|
17
|
+
# root '#colors'
|
18
|
+
# item 'li'
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# Now you'll be able to iterate over each item:
|
22
|
+
#
|
23
|
+
# # prints:
|
24
|
+
# # Red Vermelho
|
25
|
+
# # Green Verde
|
26
|
+
# # Blue Azul
|
27
|
+
# widget(:colors).each do |e|
|
28
|
+
# puts e
|
29
|
+
# end
|
30
|
+
#
|
31
|
+
# This is the same as doing the following in Capybara:
|
32
|
+
#
|
33
|
+
# all('#colors li').each do |e|
|
34
|
+
# puts e.text.strip
|
35
|
+
# end
|
36
|
+
#
|
37
|
+
# Not that, by default, the root selector of a List is +ul+ and the list
|
38
|
+
# item selector is +li+. So you could wrap the +<ul>+ above simply by using
|
39
|
+
# the following:
|
40
|
+
#
|
41
|
+
# class Colors < Dill::List
|
42
|
+
# end
|
43
|
+
#
|
44
|
+
# ==== Narrowing items
|
45
|
+
#
|
46
|
+
# You can define the root selector for your list items using the ::item macro:
|
47
|
+
#
|
48
|
+
# class PortugueseColors < Dill::List
|
49
|
+
# root '#colors
|
50
|
+
# item '.pt'
|
51
|
+
# end
|
52
|
+
#
|
53
|
+
# If you iterate over this list you get the following:
|
54
|
+
#
|
55
|
+
# # prints:
|
56
|
+
# # Vermelho
|
57
|
+
# # Verde
|
58
|
+
# # Azul
|
59
|
+
# widget(:portuguese_colors).each do |e|
|
60
|
+
# puts e
|
61
|
+
# end
|
62
|
+
#
|
63
|
+
# You can make a list out of any repeating elements, as long as you can define
|
64
|
+
# parent and child selectors.
|
65
|
+
#
|
66
|
+
# <div id="not-a-list-colors">
|
67
|
+
# <div class=".child">Red</div>
|
68
|
+
# <div class=".child">Green</div>
|
69
|
+
# <div class=".child">Blue</div>
|
70
|
+
# </div>
|
71
|
+
#
|
72
|
+
# You can define the following widget:
|
73
|
+
#
|
74
|
+
# class NotAListColors < Dill::List
|
75
|
+
# root '#not-a-list-colors'
|
76
|
+
# item '.child'
|
77
|
+
# end
|
2
78
|
class List < Widget
|
3
|
-
DEFAULT_TYPE = Widget
|
4
|
-
|
5
79
|
include Enumerable
|
6
80
|
|
7
81
|
def_delegators :items, :size, :include?, :each, :empty?, :first, :last
|
8
82
|
|
9
|
-
|
10
|
-
|
11
|
-
|
83
|
+
class << self
|
84
|
+
# Configures the List item selector and class.
|
85
|
+
#
|
86
|
+
# === Usage
|
87
|
+
#
|
88
|
+
# Given the following HTML:
|
89
|
+
#
|
90
|
+
# <ul>
|
91
|
+
# <li>One</li>
|
92
|
+
# <li>Two</li>
|
93
|
+
# <li>Three</li>
|
94
|
+
# </ul>
|
95
|
+
#
|
96
|
+
# In its most basic form, allows you to configure the list item selector,
|
97
|
+
# using the default list item class (Dill::ListItem):
|
98
|
+
#
|
99
|
+
# class Numbers < Dill::List
|
100
|
+
# root 'ul'
|
101
|
+
# item 'li'
|
102
|
+
# end
|
103
|
+
#
|
104
|
+
# ==== Extending the list item class
|
105
|
+
#
|
106
|
+
# You can define the list item class for the current List:
|
107
|
+
#
|
108
|
+
# class Number < Dill::Widget
|
109
|
+
# # ...
|
110
|
+
# end
|
111
|
+
#
|
112
|
+
# class Numbers < Dill::List
|
113
|
+
# root 'ul'
|
114
|
+
# item 'li', Number
|
115
|
+
# end
|
116
|
+
#
|
117
|
+
# widget(:numbers).first.class < Number #=> true
|
118
|
+
#
|
119
|
+
# Alternatively, you can extend the list item type inline. This is useful
|
120
|
+
# when you want to add small extensions to the default list item class.
|
121
|
+
# The extensions will apply only to list items of the current List.
|
122
|
+
#
|
123
|
+
# class Numbers < Dill::List
|
124
|
+
# root 'ul'
|
125
|
+
#
|
126
|
+
# item 'li' do
|
127
|
+
# def upcase
|
128
|
+
# text.upcase
|
129
|
+
# end
|
130
|
+
# end
|
131
|
+
#
|
132
|
+
# widget(:numbers).first.upcase #=> "ONE"
|
133
|
+
# end
|
134
|
+
def item(selector, type = ListItem, &block)
|
135
|
+
self.item_factory = WidgetClass.new(selector, type, &block)
|
12
136
|
end
|
13
137
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
138
|
+
attr_writer :item_factory
|
139
|
+
|
140
|
+
def item_factory
|
141
|
+
@item_factory ||= WidgetClass.new('li', ListItem)
|
142
|
+
end
|
143
|
+
|
144
|
+
def selector
|
145
|
+
super ||
|
146
|
+
begin
|
147
|
+
root 'ul'
|
148
|
+
|
149
|
+
super
|
150
|
+
end
|
20
151
|
end
|
21
152
|
end
|
22
153
|
|
23
154
|
def to_table
|
24
|
-
items.map
|
155
|
+
items.map(&:to_row)
|
25
156
|
end
|
26
157
|
|
27
158
|
protected
|
28
159
|
|
29
|
-
|
30
|
-
|
31
|
-
def item_factory
|
32
|
-
DEFAULT_TYPE
|
33
|
-
end
|
160
|
+
def_delegator 'self.class', :item_factory
|
34
161
|
|
35
162
|
def item_for(node)
|
36
|
-
item_factory.new(
|
163
|
+
item_factory.new(node)
|
37
164
|
end
|
38
165
|
|
39
166
|
def item_selector
|
40
|
-
|
167
|
+
item_factory.selector
|
41
168
|
end
|
42
169
|
|
43
170
|
def items
|
44
|
-
root.all(item_selector).map { |node| item_for(node) }
|
171
|
+
root.all(*item_selector).map { |node| item_for(node) }
|
45
172
|
end
|
46
173
|
end
|
47
174
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Dill
|
2
|
+
class ListItem < Widget
|
3
|
+
# Returns this ListItem's contents formatted as a row, for comparison with a
|
4
|
+
# Cucumber::Ast::Table. By default, it simply returns an array with a single
|
5
|
+
# element--the widget's text.
|
6
|
+
#
|
7
|
+
# In general, this method will be called by List#to_table.
|
8
|
+
#
|
9
|
+
# === Overriding
|
10
|
+
#
|
11
|
+
# Feel free to override this method to return whatever you need it to.
|
12
|
+
# Usually, if the default return value isn't what you want, you'll probably
|
13
|
+
# want to return a Hash where both keys and values are strings, so that you
|
14
|
+
# don't need to worry about column order when you pass the table to
|
15
|
+
# Cucumber::Ast::Table#diff!.
|
16
|
+
#
|
17
|
+
# See List#to_table for more information.
|
18
|
+
def to_row
|
19
|
+
[to_cell]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/lib/dill/version.rb
CHANGED
data/lib/dill/widget.rb
CHANGED
@@ -86,7 +86,8 @@ module Dill
|
|
86
86
|
# root selector, if +type+ has one defined.
|
87
87
|
#
|
88
88
|
# @param name the child widget's name.
|
89
|
-
# @param selector the child widget's selector.
|
89
|
+
# @param selector the child widget's selector. You can pass either a
|
90
|
+
# String or, if you want to use a composite selector, an Array.
|
90
91
|
# @param type the child widget's parent class.
|
91
92
|
#
|
92
93
|
# @overload widget(name, type)
|
@@ -119,7 +120,7 @@ module Dill
|
|
119
120
|
unless type.selector
|
120
121
|
|
121
122
|
selector = type.selector
|
122
|
-
when String
|
123
|
+
when String, Array
|
123
124
|
arg_count = rest.size + 1
|
124
125
|
|
125
126
|
case arg_count
|
@@ -139,8 +140,7 @@ module Dill
|
|
139
140
|
raise ArgumentError, "unknown method signature: #{rest.inspect}"
|
140
141
|
end
|
141
142
|
|
142
|
-
child =
|
143
|
-
child.class_eval(&block) if block_given?
|
143
|
+
child = WidgetClass.new(selector, type, &block)
|
144
144
|
|
145
145
|
const_set(Dill::WidgetName.new(name).to_sym, child)
|
146
146
|
|
@@ -181,8 +181,8 @@ module Dill
|
|
181
181
|
# @return a new instance of the current widget class.
|
182
182
|
#
|
183
183
|
# @raise [Capybara::ElementNotFoundError] if the widget can't be found
|
184
|
-
def self.find_in(node
|
185
|
-
new(
|
184
|
+
def self.find_in(node)
|
185
|
+
new(node.find(*selector))
|
186
186
|
end
|
187
187
|
|
188
188
|
# Determines if an instance of this widget class exists in
|
@@ -192,50 +192,192 @@ module Dill
|
|
192
192
|
#
|
193
193
|
# @return +true+ if a widget instance is found, +false+ otherwise.
|
194
194
|
def self.present_in?(parent_node)
|
195
|
-
parent_node.has_selector?(selector)
|
195
|
+
parent_node.has_selector?(*selector)
|
196
196
|
end
|
197
197
|
|
198
198
|
# Sets this widget's default selector.
|
199
199
|
#
|
200
|
-
#
|
201
|
-
|
202
|
-
|
200
|
+
# You can pass more than one argument to it, or a single Array. Any valid
|
201
|
+
# Capybara selector accepted by Capybara::Node::Finders#find will work.
|
202
|
+
#
|
203
|
+
# === Examples
|
204
|
+
#
|
205
|
+
# Most of the time, your selectors will be Strings:
|
206
|
+
#
|
207
|
+
# class MyWidget < Dill::Widget
|
208
|
+
# root '.selector'
|
209
|
+
# end
|
210
|
+
#
|
211
|
+
# This will match any element with a class of "selector". For example:
|
212
|
+
#
|
213
|
+
# <span class="selector">Pick me!</span>
|
214
|
+
#
|
215
|
+
# ==== Composite selectors
|
216
|
+
#
|
217
|
+
# If you're using CSS as the query language, it's useful to be able to use
|
218
|
+
# +text: 'Some text'+ to zero in on a specific node:
|
219
|
+
#
|
220
|
+
# class MySpecificWidget < Dill::Widget
|
221
|
+
# root '.selector', text: 'Pick me!'
|
222
|
+
# end
|
223
|
+
#
|
224
|
+
# This is especially useful, e.g., when you want to create a widget
|
225
|
+
# to match a specific error or notification:
|
226
|
+
#
|
227
|
+
# class NoFreeSpace < Dill::Widget
|
228
|
+
# root '.error', text: 'No free space left!'
|
229
|
+
# end
|
230
|
+
#
|
231
|
+
# So, given the following HTML:
|
232
|
+
#
|
233
|
+
# <body>
|
234
|
+
# <div class="error">No free space left!</div>
|
235
|
+
#
|
236
|
+
# <!-- ... -->
|
237
|
+
# </body>
|
238
|
+
#
|
239
|
+
# You can test for the error's present using the following code:
|
240
|
+
#
|
241
|
+
# document.has_widget?(:no_free_space) #=> true
|
242
|
+
#
|
243
|
+
# Note: When you want to match text, consider using +I18n.t+ instead of
|
244
|
+
# hard-coding the text, so that your tests don't break when the text changes.
|
245
|
+
#
|
246
|
+
# Finally, you may want to override the query language:
|
247
|
+
#
|
248
|
+
# class MyWidgetUsesXPath < Dill::Widget
|
249
|
+
# root :xpath, '//some/node'
|
250
|
+
# end
|
251
|
+
def self.root(*selector)
|
252
|
+
@selector = selector.flatten
|
203
253
|
end
|
204
254
|
|
205
|
-
#
|
255
|
+
# Returns the selector specified with +root+.
|
206
256
|
def self.selector
|
207
257
|
@selector
|
208
258
|
end
|
209
259
|
|
210
|
-
#
|
260
|
+
# Returns the root node (a Capybara::Node::Element) of the current widget.
|
211
261
|
attr_reader :root
|
212
262
|
|
213
|
-
|
263
|
+
def initialize(root)
|
264
|
+
self.root = root
|
265
|
+
end
|
266
|
+
|
267
|
+
# Returns +true+ if this widget's representation is less than +value+.
|
268
|
+
#
|
269
|
+
# Waits for the result to be +true+ for the time defined in
|
270
|
+
# `Capybara.default_wait_time`.
|
271
|
+
def <(value)
|
272
|
+
test { cast_to_type_of(value) < value }
|
273
|
+
end
|
214
274
|
|
215
|
-
|
216
|
-
|
275
|
+
# Returns +true+ if this widget's representation is less than or equal to
|
276
|
+
# +value+.
|
277
|
+
#
|
278
|
+
# Waits for the result to be +true+ for the time defined in
|
279
|
+
# `Capybara.default_wait_time`.
|
280
|
+
def <=(value)
|
281
|
+
test { cast_to_type_of(value) <= value }
|
282
|
+
end
|
283
|
+
|
284
|
+
# Returns +true+ if this widget's representation is greater than +value+.
|
285
|
+
#
|
286
|
+
# Waits for the result to be +true+ for the time defined in
|
287
|
+
# `Capybara.default_wait_time`.
|
288
|
+
def >(value)
|
289
|
+
test { cast_to_type_of(value) > value }
|
290
|
+
end
|
291
|
+
|
292
|
+
# Returns +true+ if this widget's representation is greater than or equal to
|
293
|
+
# +value+.
|
294
|
+
#
|
295
|
+
# Waits for the result to be +true+ for the time defined in
|
296
|
+
# `Capybara.default_wait_time`.
|
297
|
+
def >=(value)
|
298
|
+
test { cast_to_type_of(value) >= value }
|
217
299
|
end
|
218
300
|
|
219
301
|
# Compares the current widget with +value+, waiting for the comparison
|
220
302
|
# to return +true+.
|
221
303
|
def ==(value)
|
222
|
-
|
304
|
+
test { cast_to_type_of(value) == value }
|
223
305
|
end
|
224
306
|
|
225
307
|
# Calls +=~+ on this widget's text content.
|
226
308
|
def =~(regexp)
|
227
|
-
|
309
|
+
test { to_s =~ regexp }
|
228
310
|
end
|
229
311
|
|
230
312
|
# Calls +!~+ on this widget's text content.
|
231
313
|
def !~(regexp)
|
232
|
-
|
314
|
+
test { to_s !~ regexp }
|
233
315
|
end
|
234
316
|
|
235
317
|
# Compares the current widget with +value+, waiting for the comparison
|
236
318
|
# to return +false+.
|
237
319
|
def !=(value)
|
238
|
-
|
320
|
+
test { cast_to_type_of(value) != value }
|
321
|
+
end
|
322
|
+
|
323
|
+
# Clicks the current widget, or the child widget given by +name+.
|
324
|
+
#
|
325
|
+
# === Usage
|
326
|
+
#
|
327
|
+
# Given the following widget definition:
|
328
|
+
#
|
329
|
+
# class Container < Dill::Widget
|
330
|
+
# root '#container'
|
331
|
+
#
|
332
|
+
# widget :link, 'a'
|
333
|
+
# end
|
334
|
+
#
|
335
|
+
# Send +click+ with no arguments to trigger a +click+ event on +#container+.
|
336
|
+
#
|
337
|
+
# widget(:container).click
|
338
|
+
#
|
339
|
+
# This is the equivalent of doing the following using Capybara:
|
340
|
+
#
|
341
|
+
# find('#container').click
|
342
|
+
#
|
343
|
+
# Send +click :link+ to trigger a +click+ event on +a+:
|
344
|
+
#
|
345
|
+
# widget(:container).click :link
|
346
|
+
#
|
347
|
+
# This is the equivalent of doing the following using Capybara:
|
348
|
+
#
|
349
|
+
# find('#container a').click
|
350
|
+
def click(name = nil)
|
351
|
+
if name
|
352
|
+
widget(name).click
|
353
|
+
else
|
354
|
+
root.click
|
355
|
+
end
|
356
|
+
end
|
357
|
+
|
358
|
+
# Compares this widget with the given +diffable+, as long as it responds to
|
359
|
+
# the same protocol as Cucumber::Ast::Table#diff!.
|
360
|
+
#
|
361
|
+
# Waits +wait_time+ seconds for the comparison to be successful, otherwise
|
362
|
+
# raises Cucumber::Ast::Table::Different on failure.
|
363
|
+
#
|
364
|
+
# This is especially useful when you're not sure if the widget is in the
|
365
|
+
# proper state to be compared with the table.
|
366
|
+
#
|
367
|
+
# === Example
|
368
|
+
#
|
369
|
+
# Then(/^some step that takes in a cucumber table$/) do |table|
|
370
|
+
# widget(:my_widget).diff table
|
371
|
+
# end
|
372
|
+
def diff(diffable, wait_time = Capybara.default_wait_time)
|
373
|
+
# #diff! raises an exception if the comparison fails, or returns nil if it
|
374
|
+
# doesn't. We don't need to worry about failure, because that will be
|
375
|
+
# propagated, but we need to return +true+ when it succeeds, to end the
|
376
|
+
# comparison.
|
377
|
+
#
|
378
|
+
# We use WidgetCheckpoint instead of #test because we want the
|
379
|
+
# succeed-or-raise behavior.
|
380
|
+
WidgetCheckpoint.wait_for(wait_time) { diffable.diff!(to_table) || true }
|
239
381
|
end
|
240
382
|
|
241
383
|
# Determines if the widget underlying an action exists.
|
@@ -265,8 +407,6 @@ module Dill
|
|
265
407
|
end
|
266
408
|
end
|
267
409
|
|
268
|
-
class Reload < Capybara::ElementNotFound; end
|
269
|
-
|
270
410
|
# Reloads the widget, waiting for its contents to change (by default),
|
271
411
|
# or until +wait_time+ expires.
|
272
412
|
#
|
@@ -289,13 +429,13 @@ module Dill
|
|
289
429
|
# @return the current widget
|
290
430
|
#
|
291
431
|
# @see Checkpoint
|
292
|
-
def reload(wait_time = Capybara.default_wait_time, &
|
432
|
+
def reload(wait_time = Capybara.default_wait_time, &condition)
|
293
433
|
unless test
|
294
434
|
old_root = root
|
295
435
|
test = ->{ old_root != root }
|
296
436
|
end
|
297
437
|
|
298
|
-
|
438
|
+
test wait_time, &condition
|
299
439
|
|
300
440
|
begin
|
301
441
|
root.inspect
|
@@ -317,7 +457,23 @@ module Dill
|
|
317
457
|
#
|
318
458
|
# @return [MatchData] the match data from running +match+ on the text.
|
319
459
|
def match(pattern, position = 0, &block)
|
320
|
-
|
460
|
+
test { to_s.match(pattern, position, &block) }
|
461
|
+
end
|
462
|
+
|
463
|
+
def text
|
464
|
+
NodeText.new(root)
|
465
|
+
end
|
466
|
+
|
467
|
+
# Converts this widget into a string representation suitable to be displayed
|
468
|
+
# in a Cucumber table cell. By default calls #text.
|
469
|
+
#
|
470
|
+
# This method will be called by methods that build tables or rows (usually
|
471
|
+
# #to_table or #to_row) so, in general, you won't call it directly, but feel
|
472
|
+
# free to override it when needed.
|
473
|
+
#
|
474
|
+
# Returns a String.
|
475
|
+
def to_cell
|
476
|
+
to_s
|
321
477
|
end
|
322
478
|
|
323
479
|
def to_i
|
@@ -328,9 +484,7 @@ module Dill
|
|
328
484
|
to_s.to_f
|
329
485
|
end
|
330
486
|
|
331
|
-
|
332
|
-
node_text(root)
|
333
|
-
end
|
487
|
+
alias_method :to_s, :text
|
334
488
|
|
335
489
|
protected
|
336
490
|
|
@@ -347,16 +501,12 @@ module Dill
|
|
347
501
|
end
|
348
502
|
end
|
349
503
|
|
350
|
-
def node_text(node)
|
351
|
-
NodeText.new(node)
|
352
|
-
end
|
353
|
-
|
354
504
|
private
|
355
505
|
|
356
506
|
attr_writer :root
|
357
507
|
|
358
|
-
def
|
359
|
-
|
508
|
+
def test(wait_time = Capybara.default_wait_time, &block)
|
509
|
+
WidgetCheckpoint.wait_for(wait_time, &block) rescue nil
|
360
510
|
end
|
361
511
|
|
362
512
|
def page
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Dill
|
2
|
+
# A Checkpoint that bypasses the standard wait time if the current
|
3
|
+
# Capybara driver doesn't support waiting.
|
4
|
+
class WidgetCheckpoint < Checkpoint
|
5
|
+
# Returns the Capybara driver in use.
|
6
|
+
def driver
|
7
|
+
Capybara.current_session.driver
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize(*)
|
11
|
+
if immediate?
|
12
|
+
super 0
|
13
|
+
else
|
14
|
+
super
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
protected
|
19
|
+
|
20
|
+
def rescuable_errors
|
21
|
+
@rescuable_errors ||= Array(super) + driver.invalid_element_errors
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def immediate?
|
27
|
+
! driver.wait?
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
metadata
CHANGED
@@ -2,14 +2,14 @@
|
|
2
2
|
name: dill
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.
|
5
|
+
version: 0.5.0
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- David Leal
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-09-02 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -107,6 +107,22 @@ dependencies:
|
|
107
107
|
- !ruby/object:Gem::Version
|
108
108
|
version: 1.3.0
|
109
109
|
none: false
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
version_requirements: !ruby/object:Gem::Requirement
|
112
|
+
requirements:
|
113
|
+
- - ~>
|
114
|
+
- !ruby/object:Gem::Version
|
115
|
+
version: 1.3.0
|
116
|
+
none: false
|
117
|
+
name: cucumber
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
requirement: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ~>
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: 1.3.0
|
125
|
+
none: false
|
110
126
|
description: See https://github.com/mojotech/dill/README.md
|
111
127
|
email:
|
112
128
|
- dleal@mojotech.com
|
@@ -126,6 +142,7 @@ files:
|
|
126
142
|
- lib/dill/form.rb
|
127
143
|
- lib/dill/instance_conversions.rb
|
128
144
|
- lib/dill/list.rb
|
145
|
+
- lib/dill/list_item.rb
|
129
146
|
- lib/dill/node_text.rb
|
130
147
|
- lib/dill/table.rb
|
131
148
|
- lib/dill/text_table.rb
|
@@ -135,6 +152,8 @@ files:
|
|
135
152
|
- lib/dill/text_table/void_mapping.rb
|
136
153
|
- lib/dill/version.rb
|
137
154
|
- lib/dill/widget.rb
|
155
|
+
- lib/dill/widget_checkpoint.rb
|
156
|
+
- lib/dill/widget_class.rb
|
138
157
|
- lib/dill/widget_container.rb
|
139
158
|
- lib/dill/widget_name.rb
|
140
159
|
homepage: https://github.com/mojotech/dill
|