dill 0.5.2 → 0.6.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.
metadata CHANGED
@@ -2,14 +2,14 @@
2
2
  name: dill
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.5.2
5
+ version: 0.6.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-09-02 00:00:00.000000000 Z
12
+ date: 2013-12-29 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  version_requirements: !ruby/object:Gem::Requirement
@@ -123,6 +123,38 @@ dependencies:
123
123
  - !ruby/object:Gem::Version
124
124
  version: 1.3.0
125
125
  none: false
126
+ - !ruby/object:Gem::Dependency
127
+ version_requirements: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ! '>='
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ none: false
133
+ name: pry
134
+ type: :development
135
+ prerelease: false
136
+ requirement: !ruby/object:Gem::Requirement
137
+ requirements:
138
+ - - ! '>='
139
+ - !ruby/object:Gem::Version
140
+ version: '0'
141
+ none: false
142
+ - !ruby/object:Gem::Dependency
143
+ version_requirements: !ruby/object:Gem::Requirement
144
+ requirements:
145
+ - - ! '>='
146
+ - !ruby/object:Gem::Version
147
+ version: '0'
148
+ none: false
149
+ name: pry-nav
150
+ type: :development
151
+ prerelease: false
152
+ requirement: !ruby/object:Gem::Requirement
153
+ requirements:
154
+ - - ! '>='
155
+ - !ruby/object:Gem::Version
156
+ version: '0'
157
+ none: false
126
158
  description: See https://github.com/mojotech/dill/README.md
127
159
  email:
128
160
  - dleal@mojotech.com
@@ -131,31 +163,36 @@ extensions: []
131
163
  extra_rdoc_files: []
132
164
  files:
133
165
  - lib/dill.rb
134
- - lib/dill/auto_table.rb
135
- - lib/dill/base_table.rb
166
+ - lib/dill/capybara.rb
136
167
  - lib/dill/checkpoint.rb
137
168
  - lib/dill/conversions.rb
138
169
  - lib/dill/cucumber.rb
139
- - lib/dill/document.rb
140
170
  - lib/dill/dsl.rb
141
- - lib/dill/field_group.rb
142
- - lib/dill/form.rb
143
171
  - lib/dill/instance_conversions.rb
144
- - lib/dill/list.rb
145
- - lib/dill/list_item.rb
146
- - lib/dill/node_text.rb
147
- - lib/dill/table.rb
148
172
  - lib/dill/text_table.rb
149
173
  - lib/dill/text_table/cell_text.rb
150
174
  - lib/dill/text_table/mapping.rb
151
175
  - lib/dill/text_table/transformations.rb
152
176
  - lib/dill/text_table/void_mapping.rb
153
177
  - lib/dill/version.rb
154
- - lib/dill/widget.rb
155
- - lib/dill/widget_checkpoint.rb
156
- - lib/dill/widget_class.rb
157
- - lib/dill/widget_container.rb
158
- - lib/dill/widget_name.rb
178
+ - lib/dill/widgets.rb
179
+ - lib/dill/widgets/auto_table.rb
180
+ - lib/dill/widgets/base_table.rb
181
+ - lib/dill/widgets/check_box.rb
182
+ - lib/dill/widgets/document.rb
183
+ - lib/dill/widgets/field.rb
184
+ - lib/dill/widgets/field_group.rb
185
+ - lib/dill/widgets/form.rb
186
+ - lib/dill/widgets/list.rb
187
+ - lib/dill/widgets/list_item.rb
188
+ - lib/dill/widgets/parts/container.rb
189
+ - lib/dill/widgets/parts/struct.rb
190
+ - lib/dill/widgets/select.rb
191
+ - lib/dill/widgets/table.rb
192
+ - lib/dill/widgets/text_field.rb
193
+ - lib/dill/widgets/widget.rb
194
+ - lib/dill/widgets/widget_class.rb
195
+ - lib/dill/widgets/widget_name.rb
159
196
  homepage: https://github.com/mojotech/dill
160
197
  licenses:
161
198
  - MIT
@@ -1,14 +0,0 @@
1
- module Dill
2
- class Document
3
- include WidgetContainer
4
-
5
- def initialize(options)
6
- self.widget_lookup_scope =
7
- options.delete(:widget_lookup_scope) or raise "No scope given"
8
- end
9
-
10
- def root
11
- Capybara.current_session
12
- end
13
- end
14
- end
@@ -1,9 +0,0 @@
1
- module Dill
2
- class NodeText < String
3
- include InstanceConversions
4
-
5
- def initialize(node)
6
- super node.text.strip
7
- end
8
- end
9
- end
@@ -1,516 +0,0 @@
1
- module Dill
2
- class Widget
3
- extend Forwardable
4
-
5
- include WidgetContainer
6
-
7
- class Removed < StandardError; end
8
-
9
- # @!group Widget macros
10
-
11
- # Defines a new action.
12
- #
13
- # This is a shortcut to help defining a widget and a method that clicks
14
- # on that widget. You can then send a widget instance the message given
15
- # by +name+.
16
- #
17
- # You can access the underlying widget by appending "_widget" to the
18
- # action name.
19
- #
20
- # @example
21
- # # Consider the widget will encapsulate the following HTML
22
- # #
23
- # # <div id="profile">
24
- # # <a href="/profiles/1/edit" rel="edit">Edit</a>
25
- # # </div>
26
- # class PirateProfile < Dill::Widget
27
- # root "#profile"
28
- #
29
- # # Declare the action
30
- # action :edit, '[rel = edit]'
31
- # end
32
- #
33
- # pirate_profile = widget(:pirate_profile)
34
- #
35
- # # Access the action widget
36
- # action_widget = pirate_profile.widget(:edit_widget)
37
- # action_widget = pirate_profile.edit_widget
38
- #
39
- # # Click the link
40
- # pirate_profile.edit
41
- #
42
- # @param name the name of the action
43
- # @param selector the selector for the widget that will be clicked
44
- def self.action(name, selector)
45
- wname = :"#{name}_widget"
46
-
47
- widget wname, selector
48
-
49
- define_method name do
50
- widget(wname).click
51
-
52
- self
53
- end
54
- end
55
-
56
- # Declares a new child widget.
57
- #
58
- # Child widgets are accessible inside the container widget using the
59
- # {#widget} message, or by sending a message +name+. They
60
- # are automatically scoped to the parent widget's root node.
61
- #
62
- # @example Defining a widget
63
- # # Given the following HTML:
64
- # #
65
- # # <div id="root">
66
- # # <span id="child">Child</span>
67
- # # </div>
68
- # class Container < Dill::Widget
69
- # root '#root'
70
- #
71
- # widget :my_widget, '#child'
72
- # end
73
- #
74
- # container = widget(:container)
75
- #
76
- # # accessing using #widget
77
- # my_widget = container.widget(:my_widget)
78
- #
79
- # # accessing using #my_widget
80
- # my_widget = container.my_widget
81
- #
82
- # @overload widget(name, selector, type = Widget)
83
- #
84
- # The most common form, it allows you to pass in a selector as well as a
85
- # type for the child widget. The selector will override +type+'s
86
- # root selector, if +type+ has one defined.
87
- #
88
- # @param name the child widget's name.
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.
91
- # @param type the child widget's parent class.
92
- #
93
- # @overload widget(name, type)
94
- #
95
- # This form allows you to omit +selector+ from the arguments. It will
96
- # reuse +type+'s root selector.
97
- #
98
- # @param name the child widget's name.
99
- # @param type the child widget's parent class.
100
- #
101
- # @raise ArgumentError if +type+ has no root selector defined.
102
- #
103
- # @yield A block allowing you to further customize the widget behavior.
104
- #
105
- # @see #widget
106
- def self.widget(name, *rest, &block)
107
- raise ArgumentError, "`#{name}' is a reserved name" \
108
- if WidgetContainer.instance_methods.include?(name.to_sym)
109
-
110
- case rest.first
111
- when Class
112
- arg_count = rest.size + 1
113
- raise ArgumentError, "wrong number of arguments (#{arg_count} for 2)" \
114
- unless arg_count == 2
115
-
116
- type = rest.first
117
- raise TypeError, "can't convert `#{type}' to Widget" \
118
- unless type.methods.include?(:selector)
119
- raise ArgumentError, "missing root selector for `#{type}'" \
120
- unless type.selector
121
-
122
- selector = type.selector
123
- when String, Array
124
- arg_count = rest.size + 1
125
-
126
- case arg_count
127
- when 0, 1
128
- raise ArgumentError, "wrong number of arguments (#{arg_count} for 2)"
129
- when 2
130
- selector, type = [*rest, Widget]
131
- when 3
132
- selector, type = rest
133
-
134
- raise TypeError, "can't convert `#{type}' to Widget" \
135
- unless Class === type
136
- else
137
- raise ArgumentError, "wrong number of arguments (#{arg_count} for 3)"
138
- end
139
- else
140
- raise ArgumentError, "unknown method signature: #{rest.inspect}"
141
- end
142
-
143
- child = WidgetClass.new(selector, type, &block)
144
-
145
- const_set(Dill::WidgetName.new(name).to_sym, child)
146
-
147
- define_method name do
148
- widget(name)
149
- end
150
- end
151
-
152
- # Creates a delegator for one child widget message.
153
- #
154
- # Since widgets are accessed through {WidgetContainer#widget}, we can't
155
- # use {Forwardable} to delegate messages to widgets.
156
- #
157
- # @param name the name of the receiver child widget
158
- # @param widget_message the name of the message to be sent to the child widget
159
- # @param method_name the name of the delegator. If +nil+ the method will
160
- # have the same name as the message it will send.
161
- def self.widget_delegator(name, widget_message, method_name = nil)
162
- method_name = method_name || widget_message
163
-
164
- class_eval <<-RUBY
165
- def #{method_name}(*args)
166
- if args.size == 1
167
- widget(:#{name}).#{widget_message} args.first
168
- else
169
- widget(:#{name}).#{widget_message} *args
170
- end
171
- end
172
- RUBY
173
- end
174
-
175
- # @!endgroup
176
-
177
- # Finds a single instance of the current widget in +node+.
178
- #
179
- # @param node the node we want to search in
180
- #
181
- # @return a new instance of the current widget class.
182
- #
183
- # @raise [Capybara::ElementNotFoundError] if the widget can't be found
184
- def self.find_in(node)
185
- new(node.find(*selector))
186
- end
187
-
188
- # Determines if an instance of this widget class exists in
189
- # +parent_node+.
190
- #
191
- # @param parent_node [Capybara::Node] the node we want to search in
192
- #
193
- # @return +true+ if a widget instance is found, +false+ otherwise.
194
- def self.present_in?(parent_node)
195
- parent_node.has_selector?(*selector)
196
- end
197
-
198
- # Sets this widget's default selector.
199
- #
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
253
- end
254
-
255
- # Returns the selector specified with +root+.
256
- def self.selector
257
- @selector
258
- end
259
-
260
- # Returns the root node (a Capybara::Node::Element) of the current widget.
261
- attr_reader :root
262
-
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
274
-
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 }
299
- end
300
-
301
- # Compares the current widget with +value+, waiting for the comparison
302
- # to return +true+.
303
- def ==(value)
304
- test { cast_to_type_of(value) == value }
305
- end
306
-
307
- # Calls +=~+ on this widget's text content.
308
- def =~(regexp)
309
- test { to_s =~ regexp }
310
- end
311
-
312
- # Calls +!~+ on this widget's text content.
313
- def !~(regexp)
314
- test { to_s !~ regexp }
315
- end
316
-
317
- # Compares the current widget with +value+, waiting for the comparison
318
- # to return +false+.
319
- def !=(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 }
381
- end
382
-
383
- # Determines if the widget underlying an action exists.
384
- #
385
- # @param name the name of the action
386
- #
387
- # @raise Missing if an action with +name+ can't be found.
388
- #
389
- # @return [Boolean] +true+ if the action widget is found, +false+
390
- # otherwise.
391
- def has_action?(name)
392
- raise Missing, "couldn't find `#{name}' action" unless respond_to?(name)
393
-
394
- has_widget?(:"#{name}_widget")
395
- end
396
-
397
- def inspect
398
- inspection = "<!-- #{self.class.name}: -->\n"
399
- root = self.root
400
-
401
- begin
402
- xml = Nokogiri::HTML(page.body).at(root.path).to_xml
403
-
404
- inspection << Nokogiri::XML(xml, &:noblanks).to_xhtml
405
- rescue Capybara::NotSupportedByDriverError
406
- inspection << "<#{root.tag_name}>\n#{to_s}"
407
- end
408
- end
409
-
410
- # Reloads the widget, waiting for its contents to change (by default),
411
- # or until +wait_time+ expires.
412
- #
413
- # Call this method to make sure a widget has enough time to update
414
- # itself.
415
- #
416
- # You can pass a block to this method to control what it means for the
417
- # widget to be reloaded.
418
- #
419
- # *Note: does not account for multiple changes to the widget yet.*
420
- #
421
- # @param wait_time [Numeric] how long we should wait for changes, in
422
- # seconds.
423
- #
424
- # @yield A block that determines what it means for a widget to be
425
- # reloaded.
426
- # @yieldreturn [Boolean] +true+ if the widget is considered to be
427
- # reloaded, +false+ otherwise.
428
- #
429
- # @return the current widget
430
- #
431
- # @see Checkpoint
432
- def reload(wait_time = Capybara.default_wait_time, &condition)
433
- unless test
434
- old_root = root
435
- test = ->{ old_root != root }
436
- end
437
-
438
- test wait_time, &condition
439
-
440
- begin
441
- root.inspect
442
- rescue
443
- raise Removed, "widget was removed"
444
- end
445
-
446
- self
447
- end
448
-
449
- # Calls +match+ on this widget's text content.
450
- #
451
- # If a block is given, passes the resulting match data to the block.
452
- #
453
- # @param pattern the pattern to match
454
- # @param position where to begin the search
455
- #
456
- # @yieldparam [MatchData] the match data from running +match+ on the text.
457
- #
458
- # @return [MatchData] the match data from running +match+ on the text.
459
- def match(pattern, position = 0, &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
477
- end
478
-
479
- def to_i
480
- to_s.to_i
481
- end
482
-
483
- def to_f
484
- to_s.to_f
485
- end
486
-
487
- alias_method :to_s, :text
488
-
489
- protected
490
-
491
- def cast_to_type_of(value)
492
- case value
493
- when Float
494
- to_f
495
- when Integer
496
- to_i
497
- when String
498
- to_s
499
- else
500
- raise TypeError, "can't convert this widget to `#{klass}'"
501
- end
502
- end
503
-
504
- private
505
-
506
- attr_writer :root
507
-
508
- def test(wait_time = Capybara.default_wait_time, &block)
509
- WidgetCheckpoint.wait_for(wait_time, &block) rescue nil
510
- end
511
-
512
- def page
513
- Capybara.current_session
514
- end
515
- end
516
- end