capybara-ui 0.10.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.
Files changed (42) hide show
  1. checksums.yaml +7 -0
  2. data/lib/capybara-ui.rb +31 -0
  3. data/lib/capybara-ui/assertions.rb +19 -0
  4. data/lib/capybara-ui/capybara.rb +7 -0
  5. data/lib/capybara-ui/checkpoint.rb +111 -0
  6. data/lib/capybara-ui/conversions.rb +31 -0
  7. data/lib/capybara-ui/cucumber.rb +5 -0
  8. data/lib/capybara-ui/dsl.rb +107 -0
  9. data/lib/capybara-ui/instance_conversions.rb +19 -0
  10. data/lib/capybara-ui/matchers.rb +28 -0
  11. data/lib/capybara-ui/optional_dependencies.rb +5 -0
  12. data/lib/capybara-ui/rails.rb +5 -0
  13. data/lib/capybara-ui/rails/role.rb +9 -0
  14. data/lib/capybara-ui/role.rb +19 -0
  15. data/lib/capybara-ui/text_table.rb +107 -0
  16. data/lib/capybara-ui/text_table/cell_text.rb +7 -0
  17. data/lib/capybara-ui/text_table/mapping.rb +40 -0
  18. data/lib/capybara-ui/text_table/transformations.rb +13 -0
  19. data/lib/capybara-ui/text_table/void_mapping.rb +8 -0
  20. data/lib/capybara-ui/version.rb +3 -0
  21. data/lib/capybara-ui/widgets.rb +61 -0
  22. data/lib/capybara-ui/widgets/check_box.rb +26 -0
  23. data/lib/capybara-ui/widgets/cucumber_methods.rb +73 -0
  24. data/lib/capybara-ui/widgets/document.rb +19 -0
  25. data/lib/capybara-ui/widgets/dsl.rb +47 -0
  26. data/lib/capybara-ui/widgets/field.rb +22 -0
  27. data/lib/capybara-ui/widgets/field_group.rb +329 -0
  28. data/lib/capybara-ui/widgets/form.rb +26 -0
  29. data/lib/capybara-ui/widgets/list.rb +200 -0
  30. data/lib/capybara-ui/widgets/list_item.rb +22 -0
  31. data/lib/capybara-ui/widgets/parts/container.rb +46 -0
  32. data/lib/capybara-ui/widgets/parts/struct.rb +117 -0
  33. data/lib/capybara-ui/widgets/radio_button.rb +62 -0
  34. data/lib/capybara-ui/widgets/select.rb +57 -0
  35. data/lib/capybara-ui/widgets/string_value.rb +43 -0
  36. data/lib/capybara-ui/widgets/table.rb +76 -0
  37. data/lib/capybara-ui/widgets/text_field.rb +27 -0
  38. data/lib/capybara-ui/widgets/widget.rb +392 -0
  39. data/lib/capybara-ui/widgets/widget/node_filter.rb +48 -0
  40. data/lib/capybara-ui/widgets/widget_class.rb +11 -0
  41. data/lib/capybara-ui/widgets/widget_name.rb +56 -0
  42. 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