capybara-ui 0.10.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/capybara-ui.rb +31 -0
- data/lib/capybara-ui/assertions.rb +19 -0
- data/lib/capybara-ui/capybara.rb +7 -0
- data/lib/capybara-ui/checkpoint.rb +111 -0
- data/lib/capybara-ui/conversions.rb +31 -0
- data/lib/capybara-ui/cucumber.rb +5 -0
- data/lib/capybara-ui/dsl.rb +107 -0
- data/lib/capybara-ui/instance_conversions.rb +19 -0
- data/lib/capybara-ui/matchers.rb +28 -0
- data/lib/capybara-ui/optional_dependencies.rb +5 -0
- data/lib/capybara-ui/rails.rb +5 -0
- data/lib/capybara-ui/rails/role.rb +9 -0
- data/lib/capybara-ui/role.rb +19 -0
- data/lib/capybara-ui/text_table.rb +107 -0
- data/lib/capybara-ui/text_table/cell_text.rb +7 -0
- data/lib/capybara-ui/text_table/mapping.rb +40 -0
- data/lib/capybara-ui/text_table/transformations.rb +13 -0
- data/lib/capybara-ui/text_table/void_mapping.rb +8 -0
- data/lib/capybara-ui/version.rb +3 -0
- data/lib/capybara-ui/widgets.rb +61 -0
- data/lib/capybara-ui/widgets/check_box.rb +26 -0
- data/lib/capybara-ui/widgets/cucumber_methods.rb +73 -0
- data/lib/capybara-ui/widgets/document.rb +19 -0
- data/lib/capybara-ui/widgets/dsl.rb +47 -0
- data/lib/capybara-ui/widgets/field.rb +22 -0
- data/lib/capybara-ui/widgets/field_group.rb +329 -0
- data/lib/capybara-ui/widgets/form.rb +26 -0
- data/lib/capybara-ui/widgets/list.rb +200 -0
- data/lib/capybara-ui/widgets/list_item.rb +22 -0
- data/lib/capybara-ui/widgets/parts/container.rb +46 -0
- data/lib/capybara-ui/widgets/parts/struct.rb +117 -0
- data/lib/capybara-ui/widgets/radio_button.rb +62 -0
- data/lib/capybara-ui/widgets/select.rb +57 -0
- data/lib/capybara-ui/widgets/string_value.rb +43 -0
- data/lib/capybara-ui/widgets/table.rb +76 -0
- data/lib/capybara-ui/widgets/text_field.rb +27 -0
- data/lib/capybara-ui/widgets/widget.rb +392 -0
- data/lib/capybara-ui/widgets/widget/node_filter.rb +48 -0
- data/lib/capybara-ui/widgets/widget_class.rb +11 -0
- data/lib/capybara-ui/widgets/widget_name.rb +56 -0
- metadata +240 -0
@@ -0,0 +1,26 @@
|
|
1
|
+
module CapybaraUI
|
2
|
+
class Form < FieldGroup
|
3
|
+
root 'form'
|
4
|
+
|
5
|
+
action :submit, '[type = submit]'
|
6
|
+
|
7
|
+
# Submit form with +attributes+.
|
8
|
+
#
|
9
|
+
# @param attributes [Hash] the form fields and their values
|
10
|
+
#
|
11
|
+
# @return the current widget
|
12
|
+
def submit_with(attributes)
|
13
|
+
set attributes
|
14
|
+
submit
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_table
|
18
|
+
info = self.
|
19
|
+
class.
|
20
|
+
field_names.
|
21
|
+
each_with_object({}) { |e, a| a[e.to_s] = widget(e).to_cell }
|
22
|
+
|
23
|
+
[info]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,200 @@
|
|
1
|
+
module CapybaraUI
|
2
|
+
# Use a List when you want to treat repeating elements as a unit.
|
3
|
+
#
|
4
|
+
# === Usage
|
5
|
+
#
|
6
|
+
# Consider the following HTML:
|
7
|
+
#
|
8
|
+
# <ul id="colors">
|
9
|
+
# <li>Red <span class="pt">Vermelho</span></li>
|
10
|
+
# <li>Green <span class="pt">Verde</span></li>
|
11
|
+
# <li>Blue <span class="pt">Azul</span></li>
|
12
|
+
# </ul>
|
13
|
+
#
|
14
|
+
# You can then define the following widget:
|
15
|
+
#
|
16
|
+
# class Colors < CapybaraUI::List
|
17
|
+
# root '#colors'
|
18
|
+
# item 'li'
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# Now you'll be able to iterate over each item:
|
22
|
+
#
|
23
|
+
# # prints:
|
24
|
+
# # Red Vermelho
|
25
|
+
# # Green Verde
|
26
|
+
# # Blue Azul
|
27
|
+
# widget(:colors).each do |e|
|
28
|
+
# puts e
|
29
|
+
# end
|
30
|
+
#
|
31
|
+
# This is the same as doing the following in Capybara:
|
32
|
+
#
|
33
|
+
# all('#colors li').each do |e|
|
34
|
+
# puts e.text.strip
|
35
|
+
# end
|
36
|
+
#
|
37
|
+
# Note that, by default, the root selector of a List is +ul+ and the list
|
38
|
+
# item selector is +li+. So you could wrap the +<ul>+ above simply by using
|
39
|
+
# the following:
|
40
|
+
#
|
41
|
+
# class Colors < CapybaraUI::List
|
42
|
+
# end
|
43
|
+
#
|
44
|
+
# ==== Narrowing items
|
45
|
+
#
|
46
|
+
# You can define the root selector for your list items using the ::item macro:
|
47
|
+
#
|
48
|
+
# class PortugueseColors < CapybaraUI::List
|
49
|
+
# root '#colors
|
50
|
+
# item '.pt'
|
51
|
+
# end
|
52
|
+
#
|
53
|
+
# If you iterate over this list you get the following:
|
54
|
+
#
|
55
|
+
# # prints:
|
56
|
+
# # Vermelho
|
57
|
+
# # Verde
|
58
|
+
# # Azul
|
59
|
+
# widget(:portuguese_colors).each do |e|
|
60
|
+
# puts e
|
61
|
+
# end
|
62
|
+
#
|
63
|
+
# You can make a list out of any repeating elements, as long as you can define
|
64
|
+
# parent and child selectors.
|
65
|
+
#
|
66
|
+
# <div id="not-a-list-colors">
|
67
|
+
# <div class="child">Red</div>
|
68
|
+
# <div class="child">Green</div>
|
69
|
+
# <div class="child">Blue</div>
|
70
|
+
# </div>
|
71
|
+
#
|
72
|
+
# You can define the following widget:
|
73
|
+
#
|
74
|
+
# class NotAListColors < CapybaraUI::List
|
75
|
+
# root '#not-a-list-colors'
|
76
|
+
# item '.child'
|
77
|
+
# end
|
78
|
+
class List < Widget
|
79
|
+
include Enumerable
|
80
|
+
|
81
|
+
def_delegators :items, :each, :first, :last
|
82
|
+
|
83
|
+
root 'ul' unless filter?
|
84
|
+
|
85
|
+
class << self
|
86
|
+
# Configures the List item selector and class.
|
87
|
+
#
|
88
|
+
# === Usage
|
89
|
+
#
|
90
|
+
# Given the following HTML:
|
91
|
+
#
|
92
|
+
# <ul>
|
93
|
+
# <li>One</li>
|
94
|
+
# <li>Two</li>
|
95
|
+
# <li>Three</li>
|
96
|
+
# </ul>
|
97
|
+
#
|
98
|
+
# In its most basic form, allows you to configure the list item selector,
|
99
|
+
# using the default list item class (CapybaraUI::ListItem):
|
100
|
+
#
|
101
|
+
# class Numbers < CapybaraUI::List
|
102
|
+
# root 'ul'
|
103
|
+
# item 'li'
|
104
|
+
# end
|
105
|
+
#
|
106
|
+
# ==== Extending the list item class
|
107
|
+
#
|
108
|
+
# You can define the list item class for the current List:
|
109
|
+
#
|
110
|
+
# class Number < CapybaraUI::Widget
|
111
|
+
# # ...
|
112
|
+
# end
|
113
|
+
#
|
114
|
+
# class Numbers < CapybaraUI::List
|
115
|
+
# root 'ul'
|
116
|
+
# item 'li', Number
|
117
|
+
# end
|
118
|
+
#
|
119
|
+
# widget(:numbers).first.class < Number #=> true
|
120
|
+
#
|
121
|
+
# Alternatively, you can extend the list item type inline. This is useful
|
122
|
+
# when you want to add small extensions to the default list item class.
|
123
|
+
# The extensions will apply only to list items of the current List.
|
124
|
+
#
|
125
|
+
# class Numbers < CapybaraUI::List
|
126
|
+
# root 'ul'
|
127
|
+
#
|
128
|
+
# item 'li' do
|
129
|
+
# def upcase
|
130
|
+
# text.upcase
|
131
|
+
# end
|
132
|
+
# end
|
133
|
+
#
|
134
|
+
# widget(:numbers).first.upcase #=> "ONE"
|
135
|
+
# end
|
136
|
+
def item(selector, type = ListItem, &block)
|
137
|
+
self.item_factory = WidgetClass.new(selector, type, &block)
|
138
|
+
end
|
139
|
+
|
140
|
+
attr_writer :item_factory
|
141
|
+
|
142
|
+
def item_factory
|
143
|
+
@item_factory ||= WidgetClass.new('li', ListItem)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def count
|
148
|
+
items.count
|
149
|
+
end
|
150
|
+
|
151
|
+
# TODO: Convert value to primitive data structures.
|
152
|
+
def empty?
|
153
|
+
items.empty?
|
154
|
+
end
|
155
|
+
|
156
|
+
def exclude?(element)
|
157
|
+
! include?(element)
|
158
|
+
end
|
159
|
+
|
160
|
+
def include?(element)
|
161
|
+
value.include?(element)
|
162
|
+
end
|
163
|
+
|
164
|
+
def length
|
165
|
+
items.length
|
166
|
+
end
|
167
|
+
|
168
|
+
def size
|
169
|
+
items.size
|
170
|
+
end
|
171
|
+
|
172
|
+
def to_row
|
173
|
+
items.map(&:to_cell)
|
174
|
+
end
|
175
|
+
|
176
|
+
def to_table
|
177
|
+
items.map(&:to_row)
|
178
|
+
end
|
179
|
+
|
180
|
+
def value
|
181
|
+
items.map(&:value)
|
182
|
+
end
|
183
|
+
|
184
|
+
protected
|
185
|
+
|
186
|
+
def_delegator 'self.class', :item_factory
|
187
|
+
|
188
|
+
def item_for(node)
|
189
|
+
item_factory.new(node)
|
190
|
+
end
|
191
|
+
|
192
|
+
def item_filter
|
193
|
+
item_factory.filter
|
194
|
+
end
|
195
|
+
|
196
|
+
def items
|
197
|
+
item_filter.nodes(self).map { |node| item_for(node) }
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module CapybaraUI
|
2
|
+
class ListItem < Widget
|
3
|
+
# Returns this ListItem's contents formatted as a row, for comparison with a
|
4
|
+
# Cucumber::Ast::Table. By default, it simply returns an array with a single
|
5
|
+
# element--the widget's text.
|
6
|
+
#
|
7
|
+
# In general, this method will be called by List#to_table.
|
8
|
+
#
|
9
|
+
# === Overriding
|
10
|
+
#
|
11
|
+
# Feel free to override this method to return whatever you need it to.
|
12
|
+
# Usually, if the default return value isn't what you want, you'll probably
|
13
|
+
# want to return a Hash where both keys and values are strings, so that you
|
14
|
+
# don't need to worry about column order when you pass the table to
|
15
|
+
# Cucumber::Ast::Table#diff!.
|
16
|
+
#
|
17
|
+
# See List#to_table for more information.
|
18
|
+
def to_row
|
19
|
+
[to_cell]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module CapybaraUI
|
2
|
+
module WidgetParts
|
3
|
+
module Container
|
4
|
+
include CapybaraUI
|
5
|
+
|
6
|
+
def has_widget?(name, *args)
|
7
|
+
deprecate('has_widget? and its alias widget?', 'visible?')
|
8
|
+
widget_class(name).present_in?(self, *args)
|
9
|
+
end
|
10
|
+
|
11
|
+
alias_method :widget?, :has_widget?
|
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
|
+
|
21
|
+
def widget(name, *args)
|
22
|
+
first, rest = [*name, *args]
|
23
|
+
|
24
|
+
widget_class(first).find_in(self, *rest)
|
25
|
+
end
|
26
|
+
|
27
|
+
def widgets(name, *args)
|
28
|
+
first, rest = [*name, *args]
|
29
|
+
|
30
|
+
widget_class(first).find_all_in(self, *rest)
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
attr_writer :widget_lookup_scope
|
36
|
+
|
37
|
+
def widget_class(name)
|
38
|
+
WidgetName.new(name).to_class(widget_lookup_scope)
|
39
|
+
end
|
40
|
+
|
41
|
+
def widget_lookup_scope
|
42
|
+
@widget_lookup_scope || self.class
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
module CapybaraUI
|
2
|
+
module WidgetParts
|
3
|
+
module Struct
|
4
|
+
def self.included(target)
|
5
|
+
target.extend ClassMethods
|
6
|
+
end
|
7
|
+
|
8
|
+
module ClassMethods
|
9
|
+
def attribute(name, selector, &block)
|
10
|
+
child = widget(name, selector, &block)
|
11
|
+
|
12
|
+
class_eval <<-WIDGET
|
13
|
+
def #{name}
|
14
|
+
widget(:#{name}).value
|
15
|
+
end
|
16
|
+
WIDGET
|
17
|
+
|
18
|
+
child
|
19
|
+
end
|
20
|
+
|
21
|
+
def boolean(name, selector, &block)
|
22
|
+
child = widget(name, selector, &block)
|
23
|
+
|
24
|
+
class_eval <<-WIDGET
|
25
|
+
def #{name}?
|
26
|
+
widget(:#{name}).value
|
27
|
+
end
|
28
|
+
WIDGET
|
29
|
+
|
30
|
+
child.class_eval <<-VALUE
|
31
|
+
def value
|
32
|
+
CapybaraUI::Conversions::Boolean(text)
|
33
|
+
end
|
34
|
+
VALUE
|
35
|
+
|
36
|
+
child
|
37
|
+
end
|
38
|
+
|
39
|
+
def date(name, selector, &block)
|
40
|
+
child = attribute(name, selector, &block)
|
41
|
+
|
42
|
+
child.class_eval <<-VALUE
|
43
|
+
def value
|
44
|
+
Date.parse(text)
|
45
|
+
end
|
46
|
+
VALUE
|
47
|
+
|
48
|
+
child
|
49
|
+
end
|
50
|
+
|
51
|
+
def float(name, selector, &block)
|
52
|
+
child = attribute(name, selector, &block)
|
53
|
+
|
54
|
+
child.class_eval <<-VALUE
|
55
|
+
def value
|
56
|
+
Float(text)
|
57
|
+
end
|
58
|
+
VALUE
|
59
|
+
|
60
|
+
child
|
61
|
+
end
|
62
|
+
|
63
|
+
def integer(name, selector, &block)
|
64
|
+
child = attribute(name, selector, &block)
|
65
|
+
|
66
|
+
child.class_eval <<-VALUE
|
67
|
+
def value
|
68
|
+
Integer(text)
|
69
|
+
end
|
70
|
+
VALUE
|
71
|
+
|
72
|
+
child
|
73
|
+
end
|
74
|
+
|
75
|
+
def list(name, selector, options = {}, &block)
|
76
|
+
child = widget(name, selector, CapybaraUI::List) do
|
77
|
+
item options[:item_selector], options[:item_class] || ListItem
|
78
|
+
end
|
79
|
+
|
80
|
+
class_eval <<-WIDGET
|
81
|
+
def #{name}
|
82
|
+
widget(:#{name}).value
|
83
|
+
end
|
84
|
+
WIDGET
|
85
|
+
|
86
|
+
child.class_eval(&block) if block_given?
|
87
|
+
|
88
|
+
child
|
89
|
+
end
|
90
|
+
|
91
|
+
def string(name, *args, &block)
|
92
|
+
child = attribute(name, *args, &block)
|
93
|
+
|
94
|
+
child.class_eval <<-VALUE
|
95
|
+
def value
|
96
|
+
text
|
97
|
+
end
|
98
|
+
VALUE
|
99
|
+
|
100
|
+
child
|
101
|
+
end
|
102
|
+
|
103
|
+
def time(name, *args, &block)
|
104
|
+
child = attribute(name, *args, &block)
|
105
|
+
|
106
|
+
child.class_eval <<-VALUE
|
107
|
+
def value
|
108
|
+
Time.parse(text)
|
109
|
+
end
|
110
|
+
VALUE
|
111
|
+
|
112
|
+
child
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module CapybaraUI
|
2
|
+
# A radio button.
|
3
|
+
class RadioButton < Field
|
4
|
+
widget :checked, -> {
|
5
|
+
case Capybara.current_driver
|
6
|
+
when :rack_test
|
7
|
+
['[checked]']
|
8
|
+
else
|
9
|
+
['input:checked']
|
10
|
+
end
|
11
|
+
}
|
12
|
+
widget :checked_label_by_value, -> (val) { [:xpath, ".//label[input[@type='radio' and @value='#{val}']]"] }
|
13
|
+
widget :checked_label_by_id, -> (id) { [:xpath, ".//label[@for='#{id}']"] }
|
14
|
+
widget :button_by_value, -> (val) { "[value='#{val}']" }
|
15
|
+
|
16
|
+
def self.root(selector)
|
17
|
+
super(["#{selector}"])
|
18
|
+
end
|
19
|
+
|
20
|
+
# @return [String] The text of the checked button's label.
|
21
|
+
def get
|
22
|
+
if visible?(:checked_label_by_value, value)
|
23
|
+
widget(:checked_label_by_value, value).text
|
24
|
+
elsif visible?(:checked_label_by_id, id)
|
25
|
+
widget(:checked_label_by_id, id).text
|
26
|
+
else
|
27
|
+
nil
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# @return [String] The value of the checked button.
|
32
|
+
def value
|
33
|
+
visible?(:checked) ? widget(:checked).root.value : nil
|
34
|
+
end
|
35
|
+
|
36
|
+
# @return [String] The id of the checked button.
|
37
|
+
def id
|
38
|
+
visible?(:checked) ? widget(:checked).id : nil
|
39
|
+
end
|
40
|
+
|
41
|
+
# First attempts to choose the button by id or label text
|
42
|
+
# Then attempts to choose the button by value
|
43
|
+
def set(str)
|
44
|
+
root.choose(str)
|
45
|
+
rescue
|
46
|
+
begin
|
47
|
+
widget(:button_by_value, str).root.set(true)
|
48
|
+
rescue CapybaraUI::MissingWidget => e
|
49
|
+
raise InvalidRadioButton.new(e.message).
|
50
|
+
tap { |x| x.set_backtrace e.backtrace }
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# @return the text of the checked button, or an empty string if
|
55
|
+
# no button is checked.
|
56
|
+
def_delegator :get, :to_s
|
57
|
+
|
58
|
+
def to_cell
|
59
|
+
get
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|