dill 0.5.2 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
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