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 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