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 +4 -4
- data/lib/dill.rb +6 -1
- data/lib/dill/assertions.rb +19 -0
- data/lib/dill/checkpoint.rb +14 -3
- data/lib/dill/cucumber.rb +1 -0
- data/lib/dill/dsl.rb +15 -2
- data/lib/dill/matchers.rb +20 -4
- data/lib/dill/role.rb +1 -1
- data/lib/dill/version.rb +1 -1
- data/lib/dill/widgets.rb +3 -3
- data/lib/dill/widgets/cucumber_methods.rb +73 -0
- data/lib/dill/widgets/field.rb +2 -2
- data/lib/dill/widgets/field_group.rb +9 -0
- data/lib/dill/widgets/list.rb +1 -1
- data/lib/dill/widgets/parts/container.rb +17 -6
- data/lib/dill/widgets/select.rb +24 -7
- data/lib/dill/widgets/string_value.rb +43 -0
- data/lib/dill/widgets/table.rb +54 -44
- data/lib/dill/widgets/text_field.rb +4 -0
- data/lib/dill/widgets/widget.rb +61 -54
- data/lib/dill/widgets/widget/node_filter.rb +20 -7
- data/lib/dill/widgets/widget_name.rb +1 -1
- metadata +39 -53
- data/lib/dill/widgets/auto_table.rb +0 -75
- data/lib/dill/widgets/base_table.rb +0 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6c4f5e5b01a5a401e099e5bb0ed40f7112a9c2b9
|
4
|
+
data.tar.gz: a4c14342f65ff0442f4bbb0243386f5cf7e8854e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2ab277b0ac806f5feb0dea17401bbeb207bbf7c913c11efb2fa59d73585421197eedc1ef72ee55b750ffe46f0aefe47ffcf9b150cb1ce61360a6b93a98efc241
|
7
|
+
data.tar.gz: bf9dd094534adf9888becad2b13080be9f4dc3657991a01daab2acfd6977b23ded8edbed8c6f5fff40e5141239415745a9ae1ba4d7806b9e0b3738f5790e7bf8
|
data/lib/dill.rb
CHANGED
@@ -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
|
data/lib/dill/checkpoint.rb
CHANGED
@@ -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.
|
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.
|
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
|
-
|
106
|
+
self.class.rescuable_errors
|
96
107
|
end
|
97
108
|
|
98
109
|
attr_reader :timer
|
data/lib/dill/cucumber.rb
CHANGED
data/lib/dill/dsl.rb
CHANGED
@@ -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.
|
87
|
+
def eventually(wait_time = Capybara.default_max_wait_time, &block)
|
75
88
|
Checkpoint.wait_for wait_time, &block
|
76
89
|
end
|
77
90
|
|
data/lib/dill/matchers.rb
CHANGED
@@ -1,7 +1,23 @@
|
|
1
|
-
|
1
|
+
begin
|
2
|
+
require 'rspec/matchers'
|
2
3
|
|
3
|
-
RSpec::Matchers.define :see do |widget_name, *args|
|
4
|
-
|
5
|
-
|
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
|
data/lib/dill/role.rb
CHANGED
data/lib/dill/version.rb
CHANGED
data/lib/dill/widgets.rb
CHANGED
@@ -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
|
data/lib/dill/widgets/field.rb
CHANGED
@@ -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
|
|
data/lib/dill/widgets/list.rb
CHANGED
@@ -34,7 +34,7 @@ module Dill
|
|
34
34
|
# puts e.text.strip
|
35
35
|
# end
|
36
36
|
#
|
37
|
-
#
|
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
|
-
|
5
|
-
widget(name).absent?
|
6
|
-
end
|
4
|
+
include Dill
|
7
5
|
|
8
6
|
def has_widget?(name, *args)
|
9
|
-
widget
|
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
|
-
|
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
|
-
|
28
|
+
first, rest = [*name, *args]
|
29
|
+
|
30
|
+
widget_class(first).find_all_in(self, *rest)
|
20
31
|
end
|
21
32
|
|
22
33
|
private
|
data/lib/dill/widgets/select.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
31
|
+
widget(:option, option).select
|
16
32
|
rescue
|
17
33
|
begin
|
18
|
-
|
19
|
-
rescue
|
20
|
-
raise
|
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
|
data/lib/dill/widgets/table.rb
CHANGED
@@ -1,66 +1,76 @@
|
|
1
1
|
module Dill
|
2
|
-
class Table <
|
3
|
-
|
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
|
-
|
13
|
-
|
14
|
-
|
5
|
+
class Row < Dill::List
|
6
|
+
def self.column(*args, &block)
|
7
|
+
item(*args, &block)
|
15
8
|
end
|
9
|
+
end
|
16
10
|
|
17
|
-
|
18
|
-
|
19
|
-
|
11
|
+
def self.header_row(selector, &block)
|
12
|
+
widget :header_row, selector, Row, &block
|
13
|
+
end
|
20
14
|
|
21
|
-
|
15
|
+
header_row 'thead tr' do
|
16
|
+
column 'th'
|
17
|
+
end
|
22
18
|
|
23
|
-
|
24
|
-
|
19
|
+
def self.data_row(selector, &block)
|
20
|
+
widget :data_row, selector, Row, &block
|
21
|
+
end
|
25
22
|
|
26
|
-
|
27
|
-
|
28
|
-
|
23
|
+
data_row 'tbody tr' do
|
24
|
+
column 'td'
|
25
|
+
end
|
29
26
|
|
30
|
-
|
31
|
-
|
27
|
+
class Columns
|
28
|
+
include Enumerable
|
29
|
+
|
30
|
+
def initialize(parent)
|
31
|
+
@parent = parent
|
32
32
|
end
|
33
|
-
end
|
34
33
|
|
35
|
-
|
36
|
-
|
37
|
-
|
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
|
-
|
40
|
-
|
41
|
-
|
46
|
+
def each(&block)
|
47
|
+
parent.each(&block)
|
48
|
+
end
|
42
49
|
|
43
|
-
|
44
|
-
@column_definitions ||= []
|
45
|
-
end
|
50
|
+
private
|
46
51
|
|
47
|
-
|
52
|
+
attr_reader :parent
|
48
53
|
|
49
|
-
|
50
|
-
|
51
|
-
|
54
|
+
def values_by_index(index)
|
55
|
+
parent.rows.transpose[index]
|
56
|
+
end
|
52
57
|
|
53
|
-
|
58
|
+
def values_by_header(header)
|
59
|
+
values_by_index(find_header_index(header))
|
60
|
+
end
|
54
61
|
|
55
|
-
|
56
|
-
|
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
|
59
|
-
|
68
|
+
def columns
|
69
|
+
Columns.new(self)
|
60
70
|
end
|
61
71
|
|
62
|
-
def
|
63
|
-
|
72
|
+
def rows
|
73
|
+
widgets(:data_row).map(&:value)
|
64
74
|
end
|
65
75
|
end
|
66
76
|
end
|
data/lib/dill/widgets/widget.rb
CHANGED
@@ -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.
|
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
|
-
|
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.
|
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(
|
185
|
-
|
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
|
-
#
|
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
|
-
#
|
242
|
+
# class Container < Dill::Widget
|
243
|
+
# root '#container'
|
231
244
|
#
|
232
|
-
#
|
233
|
-
# widget(:my_widget).diff table
|
245
|
+
# widget :link, 'a'
|
234
246
|
# end
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
#
|
240
|
-
#
|
241
|
-
|
242
|
-
|
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
|
-
|
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
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
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
|
-
|
283
|
-
|
284
|
-
!! root rescue false
|
285
|
-
end
|
302
|
+
def html
|
303
|
+
xml = Nokogiri::HTML(page.body).at(root.path).to_xml
|
286
304
|
|
287
|
-
|
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
|
15
|
-
parent_widget.root.
|
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
|
19
|
-
|
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
|
-
|
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
|
-
|
43
|
+
selector + [defaults]
|
31
44
|
end
|
32
45
|
end
|
33
46
|
end
|
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.
|
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:
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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:
|
245
|
-
rubygems_version: 2.
|
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
|