dill 0.4.4 → 0.5.0

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/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'
@@ -50,11 +50,11 @@ module Dill
50
50
 
51
51
  class Row < Widget
52
52
  def initialize(settings)
53
- s = settings.dup
53
+ root = settings.delete(:root)
54
54
 
55
- self.cell_selector = s.delete(:cell_selector)
55
+ self.cell_selector = settings.delete(:cell_selector)
56
56
 
57
- super s
57
+ super root
58
58
  end
59
59
 
60
60
  def values
@@ -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 < Capybara::ElementNotFound; end
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
- # @return the Capybara driver in use.
15
- def self.driver
16
- Capybara.current_session.driver
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
- # Waits until the condition encapsulated by the block is met.
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
- # @param raise_errors [Boolean] whether to propagate exceptions that are
53
- # "rescuable" when {wait_time} expires.
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
- # @yield a block encapsulating the condition to be evaluated.
56
- # @yieldreturn a truthy value, if condition is met, a falsey value otherwise.
34
+ # If the block does not return a "truthy" value until +wait_time+ expires,
35
+ # raises a Dill::Checkpoint::ConditionNotMet error.
57
36
  #
58
- # @return whatever the condition block returns if the condition is
59
- # successful. If the condition is not met, returns +false+ if
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 => e
67
- if immediate?
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
@@ -17,8 +17,8 @@ module Dill
17
17
  # Returns a widget instance for the given name.
18
18
  #
19
19
  # @param name [String, Symbol]
20
- def widget(name, options = {})
21
- document.widget(name, options)
20
+ def widget(name)
21
+ document.widget(name)
22
22
  end
23
23
 
24
24
  def widget_lookup_scope
@@ -244,12 +244,12 @@ module Dill
244
244
 
245
245
  # A form field.
246
246
  class Field < Widget
247
- def self.find_in(parent, options)
248
- new({root: parent.find_field(selector)}.merge(options))
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
- def self.item(selector, type = DEFAULT_TYPE, &item_for)
10
- define_method :item_selector do
11
- @item_selector ||= selector
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
- if block_given?
15
- define_method :item_for, &item_for
16
- else
17
- define_method :item_factory do
18
- type
19
- end
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 { |e| Array(e) }
155
+ items.map(&:to_row)
25
156
  end
26
157
 
27
158
  protected
28
159
 
29
- attr_writer :item_selector
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(root: node)
163
+ item_factory.new(node)
37
164
  end
38
165
 
39
166
  def item_selector
40
- 'li'
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
@@ -1,3 +1,3 @@
1
1
  module Dill
2
- VERSION = "0.4.4"
2
+ VERSION = "0.5.0"
3
3
  end
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 = Class.new(type) { root selector }
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, options = {})
185
- new(options.merge(root: node.find(selector)))
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
- # @param selector [String] a CSS or XPath query
201
- def self.root(selector)
202
- @selector = selector
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
- # @return The selector specified with +root+.
255
+ # Returns the selector specified with +root+.
206
256
  def self.selector
207
257
  @selector
208
258
  end
209
259
 
210
- # @return The root node of the current widget
260
+ # Returns the root node (a Capybara::Node::Element) of the current widget.
211
261
  attr_reader :root
212
262
 
213
- def_delegators :root, :click
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
- def initialize(settings = {})
216
- self.root = settings.fetch(:root)
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
- wait_for { cast_to_type_of(value) == value }
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
- wait_for { to_s =~ regexp }
309
+ test { to_s =~ regexp }
228
310
  end
229
311
 
230
312
  # Calls +!~+ on this widget's text content.
231
313
  def !~(regexp)
232
- wait_for { to_s !~ regexp }
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
- wait_for { cast_to_type_of(value) != value }
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, &test)
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
- wait_for(wait_time, false, &test)
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
- wait_for { to_s.match(pattern, position, &block) }
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
- def to_s
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 wait_for(wait_time = Capybara.default_wait_time, raise_errors = false, &block)
359
- Checkpoint.wait_for(wait_time, raise_errors, &block)
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
@@ -0,0 +1,11 @@
1
+ module Dill
2
+ module WidgetClass
3
+ def self.new(selector, parent = Widget, &extensions)
4
+ klass = Class.new(parent) { root selector }
5
+
6
+ klass.class_eval(&extensions) if block_given?
7
+
8
+ klass
9
+ end
10
+ end
11
+ end
@@ -4,8 +4,8 @@ module Dill
4
4
  widget_class(name).present_in?(root)
5
5
  end
6
6
 
7
- def widget(name, options = {})
8
- widget_class(name).find_in(root, options)
7
+ def widget(name)
8
+ widget_class(name).find_in(root)
9
9
  end
10
10
 
11
11
  private
metadata CHANGED
@@ -2,14 +2,14 @@
2
2
  name: dill
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.4.4
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-08-08 00:00:00.000000000 Z
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