dill 0.7.0 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7868bfe9fa6361d88848300bfecc8865641aa20d
4
- data.tar.gz: c9ef7dcdd55253ba2bd4c2c13b573e9ddce90fda
3
+ metadata.gz: 6c4f5e5b01a5a401e099e5bb0ed40f7112a9c2b9
4
+ data.tar.gz: a4c14342f65ff0442f4bbb0243386f5cf7e8854e
5
5
  SHA512:
6
- metadata.gz: a60a3f55322d125cb64522d7bfc8c8c8e2235f7a1c70bf7d64763286e22a1e6e296e057ad5886392fbf26b2f06e7cb892fdc03379c66f8a174578244cf211291
7
- data.tar.gz: bc94abaf53d82cc1d2b231c8fe4ad1fc47fff52724249e9b00f8a3f42c028047886722be6c7658779f5d059398fd74c823c346ff24d25a48fb30816a771d0df7
6
+ metadata.gz: 2ab277b0ac806f5feb0dea17401bbeb207bbf7c913c11efb2fa59d73585421197eedc1ef72ee55b750ffe46f0aefe47ffcf9b150cb1ce61360a6b93a98efc241
7
+ data.tar.gz: bf9dd094534adf9888becad2b13080be9f4dc3657991a01daab2acfd6977b23ded8edbed8c6f5fff40e5141239415745a9ae1ba4d7806b9e0b3738f5790e7bf8
@@ -13,7 +13,6 @@ require 'dill/text_table/transformations'
13
13
  require 'dill/text_table/cell_text'
14
14
  require 'dill/capybara'
15
15
  require 'dill/dsl'
16
- require 'dill/matchers'
17
16
 
18
17
  module Dill
19
18
  # An exception that signals that something is missing.
@@ -21,4 +20,10 @@ module Dill
21
20
  class MissingWidget < StandardError; end
22
21
  class AmbiguousWidget < StandardError; end
23
22
  class InvalidOption < StandardError; end
23
+
24
+ def deprecate(method, alternate_method, once=false)
25
+ @deprecation_notified ||= {}
26
+ warn "DEPRECATED: ##{method} is deprecated, please use ##{alternate_method} instead" unless once and @deprecation_notified[method]
27
+ @deprecation_notified[method] = true
28
+ end
24
29
  end
@@ -0,0 +1,19 @@
1
+ module Dill
2
+ module Assertions
3
+ def assert_visible(role, widget_name, *args)
4
+ eventually do
5
+ assert role.see?(widget_name, *args)
6
+
7
+ true
8
+ end
9
+ end
10
+
11
+ def assert_not_visible(role, widget_name, *args)
12
+ eventually do
13
+ refute role.see?(widget_name, *args)
14
+
15
+ true
16
+ end
17
+ end
18
+ end
19
+ end
@@ -7,6 +7,17 @@ module Dill
7
7
  class Checkpoint
8
8
  class ConditionNotMet < StandardError; end
9
9
 
10
+ class << self
11
+ attr_accessor :rescuable_errors
12
+ end
13
+
14
+ self.rescuable_errors = [StandardError]
15
+
16
+ if defined?(RSpec)
17
+ require 'rspec/expectations'
18
+ self.rescuable_errors << RSpec::Expectations::ExpectationNotMetError
19
+ end
20
+
10
21
  class Timer
11
22
  class Frozen < StandardError; end
12
23
 
@@ -52,7 +63,7 @@ module Dill
52
63
  end
53
64
 
54
65
  # Shortcut for instance level wait_for.
55
- def self.wait_for(wait_time = Capybara.default_wait_time, &block)
66
+ def self.wait_for(wait_time = Capybara.default_max_wait_time, &block)
56
67
  new(wait_time).call(&block)
57
68
  end
58
69
 
@@ -60,7 +71,7 @@ module Dill
60
71
  #
61
72
  # @param wait_time how long this checkpoint will wait for its conditions to
62
73
  # be met, in seconds.
63
- def initialize(wait_time = Capybara.default_wait_time)
74
+ def initialize(wait_time = Capybara.default_max_wait_time)
64
75
  @timer = Timer.new(wait_time)
65
76
  end
66
77
 
@@ -92,7 +103,7 @@ module Dill
92
103
  protected
93
104
 
94
105
  def rescuable_errors
95
- [StandardError]
106
+ self.class.rescuable_errors
96
107
  end
97
108
 
98
109
  attr_reader :timer
@@ -1,4 +1,5 @@
1
1
  require 'capybara/cucumber'
2
+ require 'dill/matchers'
2
3
  require 'dill/rails'
3
4
 
4
5
  World(Dill::DSL)
@@ -17,6 +17,11 @@ module Dill
17
17
  widget(name, *args).click
18
18
  end
19
19
 
20
+ # Hovers the widget defined by +name+ and optional +args+.
21
+ def hover(name, *args)
22
+ widget(name, *args).hover
23
+ end
24
+
20
25
  # @return [Document] the current document with the class of the
21
26
  # current object set as the widget lookup scope.
22
27
  def document
@@ -31,6 +36,14 @@ module Dill
31
36
 
32
37
  alias_method :widget?, :has_widget?
33
38
 
39
+ def visible?(name, *args)
40
+ document.visible?(name, *args)
41
+ end
42
+
43
+ def not_visible?(name, *args)
44
+ document.not_visible?(name, *args)
45
+ end
46
+
34
47
  def set(name, fields)
35
48
  widget(name).set fields
36
49
  end
@@ -55,7 +68,7 @@ module Dill
55
68
  #
56
69
  # @param name [String, Symbol]
57
70
  def widget(name, *args)
58
- document.widget(name, *args)
71
+ eventually { document.widget(name, *args) }
59
72
  end
60
73
 
61
74
  # Returns a list of widget instances for the given name.
@@ -71,7 +84,7 @@ module Dill
71
84
 
72
85
  # re-run one or more assertions until either they all pass,
73
86
  # or Dill times out, which will result in a test failure.
74
- def eventually(wait_time = Capybara.default_wait_time, &block)
87
+ def eventually(wait_time = Capybara.default_max_wait_time, &block)
75
88
  Checkpoint.wait_for wait_time, &block
76
89
  end
77
90
 
@@ -1,7 +1,23 @@
1
- require 'rspec/matchers'
1
+ begin
2
+ require 'rspec/matchers'
2
3
 
3
- RSpec::Matchers.define :see do |widget_name, *args|
4
- match do |role|
5
- role.see?(widget_name, *args)
4
+ RSpec::Matchers.define :see do |widget_name, *args|
5
+ match do |role|
6
+ begin
7
+ eventually { role.see?(widget_name, *args) }
8
+ rescue Dill::Checkpoint::ConditionNotMet
9
+ false
10
+ end
11
+ end
12
+
13
+ match_when_negated do |role|
14
+ begin
15
+ eventually { !role.see?(widget_name, *args) }
16
+ rescue Dill::Checkpoint::ConditionNotMet
17
+ false
18
+ end
19
+ end
6
20
  end
21
+ rescue LoadError
22
+ # *crickets*
7
23
  end
@@ -8,7 +8,7 @@ module Dill
8
8
  if respond_to?("see_#{name}?")
9
9
  send("see_#{name}?", *args)
10
10
  else
11
- widget?(name, *args)
11
+ visible?(name, *args)
12
12
  end
13
13
  end
14
14
 
@@ -1,3 +1,3 @@
1
1
  module Dill
2
- VERSION = '0.7.0'
2
+ VERSION = '0.8.0'
3
3
  end
@@ -41,6 +41,7 @@ module Dill::WidgetParts; end
41
41
  require 'dill/widgets/parts/struct'
42
42
  require 'dill/widgets/parts/container'
43
43
 
44
+ require 'dill/widgets/cucumber_methods'
44
45
  require 'dill/widgets/dsl'
45
46
  require 'dill/widgets/widget_class'
46
47
  require 'dill/widgets/widget_name'
@@ -48,9 +49,6 @@ require 'dill/widgets/widget'
48
49
  require 'dill/widgets/widget/node_filter'
49
50
  require 'dill/widgets/list_item'
50
51
  require 'dill/widgets/list'
51
- require 'dill/widgets/base_table'
52
- require 'dill/widgets/auto_table'
53
- require 'dill/widgets/table'
54
52
  require 'dill/widgets/field'
55
53
  require 'dill/widgets/check_box'
56
54
  require 'dill/widgets/select'
@@ -58,3 +56,5 @@ require 'dill/widgets/text_field'
58
56
  require 'dill/widgets/field_group'
59
57
  require 'dill/widgets/form'
60
58
  require 'dill/widgets/document'
59
+ require 'dill/widgets/table'
60
+ require 'dill/widgets/string_value'
@@ -0,0 +1,73 @@
1
+ module Dill
2
+ module CucumberMethods
3
+ begin
4
+ require 'cucumber/ast/table'
5
+
6
+ # Compares this widget with the given Cucumber +table+.
7
+ #
8
+ # === Example
9
+ #
10
+ # Then(/^some step that takes in a cucumber table$/) do |table|
11
+ # widget(:my_widget).diff table
12
+ # end
13
+ #
14
+ # Pass +ignore_case: true+, for a case-insensitive table match
15
+ #
16
+ # === Example
17
+ #
18
+ # Then(/^some step that takes in a cucumber table$/) do |table|
19
+ # widget(:my_widget).diff table, ignore_case: true
20
+ # end
21
+ #
22
+ def diff(table, wait_time = Capybara.default_max_wait_time, ignore_case: false)
23
+ to_table = self.to_table
24
+
25
+ if ignore_case == true
26
+ table = downcase_table(table)
27
+ to_table = downcase_array(to_table)
28
+ end
29
+
30
+ table.diff!(to_table) || true
31
+ end
32
+
33
+ private
34
+
35
+ def downcase_table(table)
36
+ new_cucumber_table downcase_array(table.raw)
37
+ end
38
+
39
+ def downcase_array(array)
40
+ array.map do |item|
41
+ case item
42
+ when String
43
+ item.downcase
44
+ when Array
45
+ downcase_array(item)
46
+ when Hash
47
+ downcase_hash(item)
48
+ end
49
+ end
50
+ end
51
+
52
+ def downcase_hash(hash)
53
+ hash.each do |k, v|
54
+ case v
55
+ when String
56
+ hash[k] = v.downcase
57
+ when Array
58
+ downcase_array(v)
59
+ when Hash
60
+ downcase_hash(v)
61
+ end
62
+ end
63
+ end
64
+
65
+ def new_cucumber_table(table)
66
+ Cucumber::Ast::Table.new(table)
67
+ end
68
+
69
+ rescue LoadError
70
+ # *crickets*
71
+ end
72
+ end
73
+ end
@@ -1,8 +1,8 @@
1
1
  module Dill
2
2
  # A form field.
3
3
  class Field < Widget
4
- def self.root(*selector)
5
- @filter = NodeFilter.new([:field] + selector)
4
+ def self.root(selector)
5
+ super String === selector ? [:field, selector] : selector
6
6
  end
7
7
 
8
8
  # @return This field's value.
@@ -168,6 +168,8 @@ module Dill
168
168
  # <name>:: Returns the current text field value, or +nil+ if no value
169
169
  # has been set.
170
170
  # <name>=:: Sets the current text field value.
171
+ # <name>? Returns +true+ if the current text field has content or
172
+ # +false+ otherwise
171
173
  #
172
174
  # @example
173
175
  # # Given the following HTML:
@@ -194,6 +196,9 @@ module Dill
194
196
  # form.filled_field #=> "Content"
195
197
  # form.empty_field #=> nil
196
198
  #
199
+ # form.filled_field? #=> true
200
+ # form.empty_field? #=> false
201
+ #
197
202
  # form.empty_field = "Not anymore"
198
203
  # form.empty_field #=> "Not anymore"
199
204
  #
@@ -204,6 +209,10 @@ module Dill
204
209
  # @todo Handle text field access when the field is disabled (raise an
205
210
  # exception?)
206
211
  def self.text_field(name, locator = nil)
212
+ define_method "#{name}?" do
213
+ widget(name).content?
214
+ end
215
+
207
216
  field name, locator, TextField
208
217
  end
209
218
 
@@ -34,7 +34,7 @@ module Dill
34
34
  # puts e.text.strip
35
35
  # end
36
36
  #
37
- # Not that, by default, the root selector of a List is +ul+ and the list
37
+ # Note that, by default, the root selector of a List is +ul+ and the list
38
38
  # item selector is +li+. So you could wrap the +<ul>+ above simply by using
39
39
  # the following:
40
40
  #
@@ -1,22 +1,33 @@
1
1
  module Dill
2
2
  module WidgetParts
3
3
  module Container
4
- def has_no_widget?(name)
5
- widget(name).absent?
6
- end
4
+ include Dill
7
5
 
8
6
  def has_widget?(name, *args)
9
- widget(name, *args).present?
7
+ deprecate('has_widget? and its alias widget?', 'visible?')
8
+ widget_class(name).present_in?(self, *args)
10
9
  end
11
10
 
12
11
  alias_method :widget?, :has_widget?
13
12
 
13
+ def visible?(name, *args)
14
+ widget_class(name).present_in?(self, *args)
15
+ end
16
+
17
+ def not_visible?(name, *args)
18
+ widget_class(name).not_present_in?(self, *args)
19
+ end
20
+
14
21
  def widget(name, *args)
15
- widget_class(name).find_in(self, *args)
22
+ first, rest = [*name, *args]
23
+
24
+ widget_class(first).find_in(self, *rest)
16
25
  end
17
26
 
18
27
  def widgets(name, *args)
19
- widget_class(name).find_all_in(self, *args)
28
+ first, rest = [*name, *args]
29
+
30
+ widget_class(first).find_all_in(self, *rest)
20
31
  end
21
32
 
22
33
  private
@@ -1,23 +1,40 @@
1
1
  module Dill
2
2
  # A select.
3
3
  class Select < Field
4
+ widget :selected, '[selected]'
5
+
6
+ module Selectable
7
+ def select
8
+ root.select_option
9
+ end
10
+ end
11
+
12
+ widget :option, -> (opt) {
13
+ opt.is_a?(Regexp) ? ["option", text: opt] : [:option, opt]
14
+ } do
15
+ include Selectable
16
+ end
17
+
18
+ widget :option_by_value, -> (opt) { "option[value = #{opt.inspect}]" } do
19
+ include Selectable
20
+ end
21
+
4
22
  # @return [String] The text of the selected option.
5
23
  def get
6
- option = root.find('[selected]') rescue nil
7
-
8
- option && option.text
24
+ visible?(:selected) ? widget(:selected).text : nil
9
25
  end
10
26
 
11
27
  # Selects the given +option+.
12
28
  #
13
29
  # You may pass in the option text or value.
14
30
  def set(option)
15
- root.find(:option, option).select_option
31
+ widget(:option, option).select
16
32
  rescue
17
33
  begin
18
- root.find("option[value = #{option.inspect}]").select_option
19
- rescue Capybara::ElementNotFound => e
20
- raise wrap_exception(e, InvalidOption)
34
+ widget(:option_by_value, option).select
35
+ rescue Dill::MissingWidget => e
36
+ raise InvalidOption.new(e.message).
37
+ tap { |x| x.set_backtrace e.backtrace }
21
38
  end
22
39
  end
23
40
 
@@ -0,0 +1,43 @@
1
+ module Dill
2
+ class StringValue < String
3
+ def to_date(format = nil)
4
+ format ? Date.strptime(self, format) : super()
5
+ end
6
+
7
+ def to_key
8
+ fst, rest = first, self[1..-1]
9
+ decamelized = fst + rest.gsub(/([A-Z])/, '_\1')
10
+ underscored = decamelized.gsub(/[\W_]+/, '_')
11
+ stripped = underscored.gsub(/^_|_$/, '')
12
+ downcased = stripped.downcase
13
+ key = downcased.to_sym
14
+
15
+ key
16
+ end
17
+
18
+ class Money
19
+ extend Forwardable
20
+
21
+ delegate %w(to_i to_f) => :str
22
+
23
+ def initialize(str)
24
+ fail ArgumentError, "can't convert `#{str}` to money" \
25
+ unless str =~ /^-?\$\d+(?:,\d{3})*(?:\.\d+)?/
26
+
27
+ @str = (str =~ /^-/ ? '-' : '') + str.gsub(/^-?\$|,/, '')
28
+ end
29
+
30
+ private
31
+
32
+ attr_reader :str
33
+ end
34
+
35
+ def to_usd
36
+ Money.new(self)
37
+ end
38
+
39
+ def to_split
40
+ split(',').map(&:strip).map { |e| self.class.new(e) }
41
+ end
42
+ end
43
+ end
@@ -1,66 +1,76 @@
1
1
  module Dill
2
- class Table < BaseTable
3
- class ColumnDefinition
4
- attr_reader :header
5
-
6
- def initialize(selector, header, transform)
7
- self.selector = selector
8
- self.header = header
9
- self.transform = transform
10
- end
2
+ class Table < Dill::Widget
3
+ root 'table'
11
4
 
12
- def ensure_loaded(container)
13
- container.find(selector)
14
- rescue Capybara::Ambiguous
5
+ class Row < Dill::List
6
+ def self.column(*args, &block)
7
+ item(*args, &block)
15
8
  end
9
+ end
16
10
 
17
- def values(container)
18
- container.all(selector).map { |n| transform.(node_text(n)).to_s }
19
- end
11
+ def self.header_row(selector, &block)
12
+ widget :header_row, selector, Row, &block
13
+ end
20
14
 
21
- private
15
+ header_row 'thead tr' do
16
+ column 'th'
17
+ end
22
18
 
23
- attr_accessor :selector
24
- attr_writer :header, :transform
19
+ def self.data_row(selector, &block)
20
+ widget :data_row, selector, Row, &block
21
+ end
25
22
 
26
- def node_text(node)
27
- node.text.strip
28
- end
23
+ data_row 'tbody tr' do
24
+ column 'td'
25
+ end
29
26
 
30
- def transform
31
- @transform ||= ->(v) { v }
27
+ class Columns
28
+ include Enumerable
29
+
30
+ def initialize(parent)
31
+ @parent = parent
32
32
  end
33
- end
34
33
 
35
- class << self
36
- attr_accessor :column_selector, :header_selector
37
- end
34
+ def [](header_or_index)
35
+ case header_or_index
36
+ when Integer
37
+ values_by_index(header_or_index)
38
+ when String
39
+ values_by_header(header_or_index)
40
+ else
41
+ raise TypeError,
42
+ "can't convert #{header_or_index.inspect} to Integer or String"
43
+ end
44
+ end
38
45
 
39
- def self.column(selector, header = nil, &transform)
40
- column_definitions << ColumnDefinition.new(selector, header, transform)
41
- end
46
+ def each(&block)
47
+ parent.each(&block)
48
+ end
42
49
 
43
- def self.column_definitions
44
- @column_definitions ||= []
45
- end
50
+ private
46
51
 
47
- protected
52
+ attr_reader :parent
48
53
 
49
- def ensure_table_loaded
50
- column_definitions.first.ensure_loaded(self)
51
- end
54
+ def values_by_index(index)
55
+ parent.rows.transpose[index]
56
+ end
52
57
 
53
- private
58
+ def values_by_header(header)
59
+ values_by_index(find_header_index(header))
60
+ end
54
61
 
55
- def_delegators 'self.class', :column_selector, :column_definitions,
56
- :header_selector
62
+ def find_header_index(header)
63
+ parent.widget(:header_row).value.find_index(header) or
64
+ raise ArgumentError, "header not found: #{header.inspect}"
65
+ end
66
+ end
57
67
 
58
- def headers
59
- @headers ||= column_definitions.map(&:header)
68
+ def columns
69
+ Columns.new(self)
60
70
  end
61
71
 
62
- def values
63
- @values ||= column_definitions.map { |d| d.values(root) }.transpose
72
+ def rows
73
+ widgets(:data_row).map(&:value)
64
74
  end
65
75
  end
66
76
  end
@@ -19,5 +19,9 @@ module Dill
19
19
  def to_cell
20
20
  get
21
21
  end
22
+
23
+ def content?
24
+ get.respond_to?(:empty?) ? ! get.empty? : !! get
25
+ end
22
26
  end
23
27
  end
@@ -5,9 +5,12 @@ module Dill
5
5
 
6
6
  include WidgetParts::Struct
7
7
  include WidgetParts::Container
8
+ include CucumberMethods
8
9
 
9
10
  class Removed < StandardError; end
10
11
 
12
+ attr_reader :root
13
+
11
14
  # @!group Widget macros
12
15
 
13
16
  # Defines a new action.
@@ -90,7 +93,13 @@ module Dill
90
93
  #
91
94
  # @raise [Capybara::ElementNotFoundError] if the widget can't be found
92
95
  def self.find_in(parent, *args)
93
- new(filter.query(parent, *args))
96
+ new(filter.node(parent, *args))
97
+ rescue Capybara::Ambiguous => e
98
+ raise AmbiguousWidget.new(e.message).
99
+ tap { |x| x.set_backtrace e.backtrace }
100
+ rescue Capybara::ElementNotFound => e
101
+ raise MissingWidget.new(e.message).
102
+ tap { |x| x.set_backtrace e.backtrace }
94
103
  end
95
104
 
96
105
  def self.find_all_in(parent, *args)
@@ -103,8 +112,12 @@ module Dill
103
112
  # @param parent_node [Capybara::Node] the node we want to search in
104
113
  #
105
114
  # @return +true+ if a widget instance is found, +false+ otherwise.
106
- def self.present_in?(parent)
107
- find_in(parent).present?
115
+ def self.present_in?(parent, *args)
116
+ filter.node?(parent, *args)
117
+ end
118
+
119
+ def self.not_present_in?(parent, *args)
120
+ filter.nodeless?(parent, *args)
108
121
  end
109
122
 
110
123
  # Sets this widget's default selector.
@@ -150,7 +163,7 @@ module Dill
150
163
  #
151
164
  # You can test for the error's present using the following code:
152
165
  #
153
- # document.has_widget?(:no_free_space) #=> true
166
+ # document.visible?(:no_free_space) #=> true
154
167
  #
155
168
  # Note: When you want to match text, consider using +I18n.t+ instead of
156
169
  # hard-coding the text, so that your tests don't break when the text changes.
@@ -181,13 +194,8 @@ module Dill
181
194
  filter.selector
182
195
  end
183
196
 
184
- def initialize(node)
185
- self.query = node.respond_to?(:call) ? node : -> { node }
186
- end
187
-
188
- # Alias for #gone?
189
- def absent?
190
- gone?
197
+ def initialize(root)
198
+ @root = root
191
199
  end
192
200
 
193
201
  # Clicks the current widget, or the child widget given by +name+.
@@ -225,21 +233,39 @@ module Dill
225
233
  end
226
234
  end
227
235
 
228
- # Compares this widget with the given Cucumber +table+.
236
+ # Hovers over the current widget, or the child widget given by +name+.
237
+ #
238
+ # === Usage
239
+ #
240
+ # Given the following widget definition:
229
241
  #
230
- # === Example
242
+ # class Container < Dill::Widget
243
+ # root '#container'
231
244
  #
232
- # Then(/^some step that takes in a cucumber table$/) do |table|
233
- # widget(:my_widget).diff table
245
+ # widget :link, 'a'
234
246
  # end
235
- def diff(table, wait_time = Capybara.default_wait_time)
236
- table.diff!(to_table) || true
237
- end
238
-
239
- # Returns +true+ if the widget is not visible, or has been removed from the
240
- # DOM.
241
- def gone?
242
- ! root rescue true
247
+ #
248
+ # Send +hover+ with no arguments to trigger a +hover+ event on +#container+.
249
+ #
250
+ # widget(:container).hover
251
+ #
252
+ # This is the equivalent of doing the following using Capybara:
253
+ #
254
+ # find('#container').hover
255
+ #
256
+ # Send +hover :link+ to trigger a +hover+ event on +a+:
257
+ #
258
+ # widget(:container).hover :link
259
+ #
260
+ # This is the equivalent of doing the following using Capybara:
261
+ #
262
+ # find('#container a').hover
263
+ def hover(*args)
264
+ if args.empty?
265
+ root.hover
266
+ else
267
+ widget(*args).hover
268
+ end
243
269
  end
244
270
 
245
271
  # Determines if the widget underlying an action exists.
@@ -253,7 +279,7 @@ module Dill
253
279
  def has_action?(name)
254
280
  raise Missing, "couldn't find `#{name}' action" unless respond_to?(name)
255
281
 
256
- has_widget?(:"#{name}_widget")
282
+ visible?(:"#{name}_widget")
257
283
  end
258
284
 
259
285
  def id
@@ -264,36 +290,23 @@ module Dill
264
290
  root['class'].split
265
291
  end
266
292
 
267
- def inspect
268
- inspection = "<!-- #{self.class.name}: -->\n"
269
-
270
- begin
271
- root = self.root
272
- xml = Nokogiri::HTML(page.body).at(root.path).to_xml
273
-
274
- inspection << Nokogiri::XML(xml, &:noblanks).to_xhtml
275
- rescue Capybara::NotSupportedByDriverError
276
- inspection << "<#{root.tag_name}>\n#{to_s}"
277
- rescue Dill::MissingWidget, *page.driver.invalid_element_errors
278
- "#<DETACHED>"
279
- end
293
+ # Determines if the widget has a specific class
294
+ #
295
+ # @param name the name of the class
296
+ #
297
+ # @return [Boolean] +true+ if the class is found, +false+ otherwise
298
+ def class?(name)
299
+ classes.include?(name)
280
300
  end
281
301
 
282
- # Returns +true+ if widget is visible.
283
- def present?
284
- !! root rescue false
285
- end
302
+ def html
303
+ xml = Nokogiri::HTML(page.body).at(root.path).to_xml
286
304
 
287
- def root
288
- query.()
289
- rescue Capybara::Ambiguous => e
290
- raise wrap_exception(e, AmbiguousWidget)
291
- rescue Capybara::ElementNotFound => e
292
- raise wrap_exception(e, MissingWidget)
305
+ Nokogiri::XML(xml, &:noblanks).to_xhtml.gsub("\n", "")
293
306
  end
294
307
 
295
308
  def text
296
- root.text.strip
309
+ StringValue.new(root.text.strip)
297
310
  end
298
311
 
299
312
  # Converts this widget into a string representation suitable to be displayed
@@ -318,14 +331,8 @@ module Dill
318
331
 
319
332
  private
320
333
 
321
- attr_accessor :node, :query
322
-
323
334
  def page
324
335
  Capybara.current_session
325
336
  end
326
-
327
- def wrap_exception(e, wrapper_class)
328
- wrapper_class.new(e.message).tap { |x| x.set_backtrace e.backtrace }
329
- end
330
337
  end
331
338
  end
@@ -11,12 +11,16 @@ module Dill
11
11
  parent_widget.root.find(*capybara_selector(*args))
12
12
  end
13
13
 
14
- def nodes(parent_widget, *args)
15
- parent_widget.root.all(*capybara_selector(*args))
14
+ def node?(parent_widget, *args)
15
+ parent_widget.root.has_selector?(*capybara_selector(*args))
16
+ end
17
+
18
+ def nodeless?(parent_widget, *args)
19
+ parent_widget.root.has_no_selector?(*capybara_selector(*args))
16
20
  end
17
21
 
18
- def query(parent, *args)
19
- -> { node(parent, *args) }
22
+ def nodes(parent_widget, *args)
23
+ parent_widget.root.all(*capybara_selector(*args))
20
24
  end
21
25
 
22
26
  private
@@ -24,10 +28,19 @@ module Dill
24
28
  def capybara_selector(*args)
25
29
  # TODO detect signature errors (e.g., passing a different arity than the
26
30
  # one required by the selector proc, etc)
27
- if @selector.first.respond_to?(:call)
28
- @selector.first.call(*args)
31
+ selector = if @selector.first.respond_to?(:call)
32
+ @selector.first.call(*args)
33
+ else
34
+ @selector
35
+ end
36
+ selector = Array(selector).flatten
37
+
38
+ defaults = {:wait => 0}
39
+
40
+ if Hash === selector.last
41
+ selector + [defaults.merge(selector.pop)]
29
42
  else
30
- @selector
43
+ selector + [defaults]
31
44
  end
32
45
  end
33
46
  end
@@ -39,7 +39,7 @@ module Dill
39
39
  when CAMEL_CASE_FORMAT
40
40
  str
41
41
  else
42
- raise ArgumentError, "can't convert `#{str.inspect}' to canonical form"
42
+ raise ArgumentError, "`#{str.inspect}' is an unrecognized format. Try snake case or camel case."
43
43
  end
44
44
  end
45
45
 
metadata CHANGED
@@ -1,181 +1,167 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dill
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Leal
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-09-02 00:00:00.000000000 Z
11
+ date: 2015-09-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: chronic
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - '>='
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: '0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - '>='
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: capybara
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - '>='
31
+ - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '2.0'
33
+ version: '2.5'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - '>='
38
+ - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '2.0'
41
- - !ruby/object:Gem::Dependency
42
- name: rspec
43
- requirement: !ruby/object:Gem::Requirement
44
- requirements:
45
- - - <
46
- - !ruby/object:Gem::Version
47
- version: 3.0.0
48
- type: :runtime
49
- prerelease: false
50
- version_requirements: !ruby/object:Gem::Requirement
51
- requirements:
52
- - - <
53
- - !ruby/object:Gem::Version
54
- version: 3.0.0
40
+ version: '2.5'
55
41
  - !ruby/object:Gem::Dependency
56
42
  name: rspec-given
57
43
  requirement: !ruby/object:Gem::Requirement
58
44
  requirements:
59
- - - ~>
45
+ - - "~>"
60
46
  - !ruby/object:Gem::Version
61
- version: 3.0.0
47
+ version: 3.5.0
62
48
  type: :development
63
49
  prerelease: false
64
50
  version_requirements: !ruby/object:Gem::Requirement
65
51
  requirements:
66
- - - ~>
52
+ - - "~>"
67
53
  - !ruby/object:Gem::Version
68
- version: 3.0.0
54
+ version: 3.5.0
69
55
  - !ruby/object:Gem::Dependency
70
56
  name: capybara-webkit
71
57
  requirement: !ruby/object:Gem::Requirement
72
58
  requirements:
73
- - - ~>
59
+ - - "~>"
74
60
  - !ruby/object:Gem::Version
75
- version: 1.0.0
61
+ version: 1.7.0
76
62
  type: :development
77
63
  prerelease: false
78
64
  version_requirements: !ruby/object:Gem::Requirement
79
65
  requirements:
80
- - - ~>
66
+ - - "~>"
81
67
  - !ruby/object:Gem::Version
82
- version: 1.0.0
68
+ version: 1.7.0
83
69
  - !ruby/object:Gem::Dependency
84
70
  name: poltergeist
85
71
  requirement: !ruby/object:Gem::Requirement
86
72
  requirements:
87
- - - ~>
73
+ - - "~>"
88
74
  - !ruby/object:Gem::Version
89
- version: 1.3.0
75
+ version: 1.5.0
90
76
  type: :development
91
77
  prerelease: false
92
78
  version_requirements: !ruby/object:Gem::Requirement
93
79
  requirements:
94
- - - ~>
80
+ - - "~>"
95
81
  - !ruby/object:Gem::Version
96
- version: 1.3.0
82
+ version: 1.5.0
97
83
  - !ruby/object:Gem::Dependency
98
84
  name: cucumber
99
85
  requirement: !ruby/object:Gem::Requirement
100
86
  requirements:
101
- - - ~>
87
+ - - "~>"
102
88
  - !ruby/object:Gem::Version
103
89
  version: 1.3.0
104
90
  type: :development
105
91
  prerelease: false
106
92
  version_requirements: !ruby/object:Gem::Requirement
107
93
  requirements:
108
- - - ~>
94
+ - - "~>"
109
95
  - !ruby/object:Gem::Version
110
96
  version: 1.3.0
111
97
  - !ruby/object:Gem::Dependency
112
98
  name: pry
113
99
  requirement: !ruby/object:Gem::Requirement
114
100
  requirements:
115
- - - '>='
101
+ - - ">="
116
102
  - !ruby/object:Gem::Version
117
103
  version: '0'
118
104
  type: :development
119
105
  prerelease: false
120
106
  version_requirements: !ruby/object:Gem::Requirement
121
107
  requirements:
122
- - - '>='
108
+ - - ">="
123
109
  - !ruby/object:Gem::Version
124
110
  version: '0'
125
111
  - !ruby/object:Gem::Dependency
126
112
  name: pry-nav
127
113
  requirement: !ruby/object:Gem::Requirement
128
114
  requirements:
129
- - - '>='
115
+ - - ">="
130
116
  - !ruby/object:Gem::Version
131
117
  version: '0'
132
118
  type: :development
133
119
  prerelease: false
134
120
  version_requirements: !ruby/object:Gem::Requirement
135
121
  requirements:
136
- - - '>='
122
+ - - ">="
137
123
  - !ruby/object:Gem::Version
138
124
  version: '0'
139
125
  - !ruby/object:Gem::Dependency
140
126
  name: sinatra
141
127
  requirement: !ruby/object:Gem::Requirement
142
128
  requirements:
143
- - - '>='
129
+ - - ">="
144
130
  - !ruby/object:Gem::Version
145
131
  version: '0'
146
132
  type: :development
147
133
  prerelease: false
148
134
  version_requirements: !ruby/object:Gem::Requirement
149
135
  requirements:
150
- - - '>='
136
+ - - ">="
151
137
  - !ruby/object:Gem::Version
152
138
  version: '0'
153
139
  - !ruby/object:Gem::Dependency
154
140
  name: rails
155
141
  requirement: !ruby/object:Gem::Requirement
156
142
  requirements:
157
- - - ~>
143
+ - - "~>"
158
144
  - !ruby/object:Gem::Version
159
145
  version: 4.0.0
160
146
  type: :development
161
147
  prerelease: false
162
148
  version_requirements: !ruby/object:Gem::Requirement
163
149
  requirements:
164
- - - ~>
150
+ - - "~>"
165
151
  - !ruby/object:Gem::Version
166
152
  version: 4.0.0
167
153
  - !ruby/object:Gem::Dependency
168
154
  name: codeclimate-test-reporter
169
155
  requirement: !ruby/object:Gem::Requirement
170
156
  requirements:
171
- - - '>='
157
+ - - ">="
172
158
  - !ruby/object:Gem::Version
173
159
  version: '0'
174
160
  type: :development
175
161
  prerelease: false
176
162
  version_requirements: !ruby/object:Gem::Requirement
177
163
  requirements:
178
- - - '>='
164
+ - - ">="
179
165
  - !ruby/object:Gem::Version
180
166
  version: '0'
181
167
  description: See https://github.com/mojotech/dill/README.md
@@ -186,6 +172,7 @@ extensions: []
186
172
  extra_rdoc_files: []
187
173
  files:
188
174
  - lib/dill.rb
175
+ - lib/dill/assertions.rb
189
176
  - lib/dill/capybara.rb
190
177
  - lib/dill/checkpoint.rb
191
178
  - lib/dill/conversions.rb
@@ -203,9 +190,8 @@ files:
203
190
  - lib/dill/text_table/void_mapping.rb
204
191
  - lib/dill/version.rb
205
192
  - lib/dill/widgets.rb
206
- - lib/dill/widgets/auto_table.rb
207
- - lib/dill/widgets/base_table.rb
208
193
  - lib/dill/widgets/check_box.rb
194
+ - lib/dill/widgets/cucumber_methods.rb
209
195
  - lib/dill/widgets/document.rb
210
196
  - lib/dill/widgets/dsl.rb
211
197
  - lib/dill/widgets/field.rb
@@ -216,6 +202,7 @@ files:
216
202
  - lib/dill/widgets/parts/container.rb
217
203
  - lib/dill/widgets/parts/struct.rb
218
204
  - lib/dill/widgets/select.rb
205
+ - lib/dill/widgets/string_value.rb
219
206
  - lib/dill/widgets/table.rb
220
207
  - lib/dill/widgets/text_field.rb
221
208
  - lib/dill/widgets/widget.rb
@@ -232,19 +219,18 @@ require_paths:
232
219
  - lib
233
220
  required_ruby_version: !ruby/object:Gem::Requirement
234
221
  requirements:
235
- - - '>='
222
+ - - ">="
236
223
  - !ruby/object:Gem::Version
237
224
  version: '0'
238
225
  required_rubygems_version: !ruby/object:Gem::Requirement
239
226
  requirements:
240
- - - '>='
227
+ - - ">="
241
228
  - !ruby/object:Gem::Version
242
229
  version: '0'
243
230
  requirements: []
244
- rubyforge_project: '[none]'
245
- rubygems_version: 2.2.1
231
+ rubyforge_project: "[none]"
232
+ rubygems_version: 2.4.6
246
233
  signing_key:
247
234
  specification_version: 4
248
235
  summary: A set of helpers to ease integration testing
249
236
  test_files: []
250
- has_rdoc:
@@ -1,75 +0,0 @@
1
- module Dill
2
- class AutoTable < BaseTable
3
-
4
- # don't include footer in to_table, because footer column configuration is very
5
- # often different from the headers & values.
6
- def footers
7
- @footers ||= root.all(footer_selector).map { |n| Widget.new(n).text }
8
- end
9
-
10
- protected
11
-
12
- def ensure_table_loaded
13
- root.find(data_row_selector)
14
- rescue Capybara::Ambiguous
15
- end
16
-
17
- private
18
-
19
- def data_cell_selector
20
- 'td'
21
- end
22
-
23
- def data_row(node)
24
- Row.new(root: node, cell_selector: data_cell_selector)
25
- end
26
-
27
- def data_row_selector
28
- 'tbody tr'
29
- end
30
-
31
- def data_rows
32
- @data_rows ||= root.all(data_row_selector).map { |n| data_row(n) }
33
- end
34
-
35
- def header_selector
36
- 'thead th'
37
- end
38
-
39
- def headers
40
- @headers ||= root.
41
- all(header_selector).
42
- map { |n| Widget.new(n).text.downcase }
43
- end
44
-
45
- def footer_selector
46
- 'tfoot td'
47
- end
48
-
49
- def values
50
- @values ||= data_rows.map(&:values)
51
- end
52
-
53
- class Row < Widget
54
- def initialize(settings)
55
- root = settings.delete(:root)
56
-
57
- self.cell_selector = settings.delete(:cell_selector)
58
-
59
- super root
60
- end
61
-
62
- def values
63
- root.all(cell_selector).map { |n| node_text(n) }
64
- end
65
-
66
- private
67
-
68
- attr_accessor :cell_selector
69
-
70
- def node_text(node)
71
- node.text.strip
72
- end
73
- end
74
- end
75
- end
@@ -1,9 +0,0 @@
1
- module Dill
2
- class BaseTable < Widget
3
- def to_table
4
- ensure_table_loaded
5
-
6
- headers.any? ? [headers, *values] : values
7
- end
8
- end
9
- end