dill 0.7.0 → 0.8.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.
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