capybara-ui 0.10.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (80) hide show
  1. checksums.yaml +4 -4
  2. data/lib/capybara/ui.rb +33 -0
  3. data/lib/capybara/ui/assertions.rb +21 -0
  4. data/lib/{capybara-ui → capybara/ui}/capybara.rb +0 -0
  5. data/lib/capybara/ui/checkpoint.rb +113 -0
  6. data/lib/capybara/ui/conversions.rb +33 -0
  7. data/lib/capybara/ui/cucumber.rb +5 -0
  8. data/lib/capybara/ui/dsl.rb +109 -0
  9. data/lib/capybara/ui/instance_conversions.rb +21 -0
  10. data/lib/{capybara-ui → capybara/ui}/matchers.rb +4 -4
  11. data/lib/capybara/ui/optional_dependencies.rb +7 -0
  12. data/lib/capybara/ui/rails.rb +5 -0
  13. data/lib/capybara/ui/rails/role.rb +11 -0
  14. data/lib/capybara/ui/role.rb +21 -0
  15. data/lib/capybara/ui/text_table.rb +109 -0
  16. data/lib/capybara/ui/text_table/cell_text.rb +9 -0
  17. data/lib/capybara/ui/text_table/mapping.rb +42 -0
  18. data/lib/capybara/ui/text_table/transformations.rb +15 -0
  19. data/lib/capybara/ui/text_table/void_mapping.rb +10 -0
  20. data/lib/capybara/ui/version.rb +5 -0
  21. data/lib/capybara/ui/widgets.rb +63 -0
  22. data/lib/capybara/ui/widgets/check_box.rb +28 -0
  23. data/lib/capybara/ui/widgets/cucumber_methods.rb +75 -0
  24. data/lib/capybara/ui/widgets/document.rb +21 -0
  25. data/lib/capybara/ui/widgets/dsl.rb +49 -0
  26. data/lib/capybara/ui/widgets/field.rb +24 -0
  27. data/lib/capybara/ui/widgets/field_group.rb +331 -0
  28. data/lib/capybara/ui/widgets/form.rb +28 -0
  29. data/lib/capybara/ui/widgets/list.rb +202 -0
  30. data/lib/capybara/ui/widgets/list_item.rb +24 -0
  31. data/lib/capybara/ui/widgets/parts/container.rb +48 -0
  32. data/lib/capybara/ui/widgets/parts/struct.rb +119 -0
  33. data/lib/capybara/ui/widgets/radio_button.rb +64 -0
  34. data/lib/capybara/ui/widgets/select.rb +59 -0
  35. data/lib/capybara/ui/widgets/string_value.rb +45 -0
  36. data/lib/capybara/ui/widgets/table.rb +78 -0
  37. data/lib/capybara/ui/widgets/text_field.rb +29 -0
  38. data/lib/capybara/ui/widgets/widget.rb +394 -0
  39. data/lib/capybara/ui/widgets/widget/node_filter.rb +50 -0
  40. data/lib/capybara/ui/widgets/widget_class.rb +13 -0
  41. data/lib/capybara/ui/widgets/widget_name.rb +58 -0
  42. metadata +47 -43
  43. data/lib/capybara-ui.rb +0 -31
  44. data/lib/capybara-ui/assertions.rb +0 -19
  45. data/lib/capybara-ui/checkpoint.rb +0 -111
  46. data/lib/capybara-ui/conversions.rb +0 -31
  47. data/lib/capybara-ui/cucumber.rb +0 -5
  48. data/lib/capybara-ui/dsl.rb +0 -107
  49. data/lib/capybara-ui/instance_conversions.rb +0 -19
  50. data/lib/capybara-ui/optional_dependencies.rb +0 -5
  51. data/lib/capybara-ui/rails.rb +0 -5
  52. data/lib/capybara-ui/rails/role.rb +0 -9
  53. data/lib/capybara-ui/role.rb +0 -19
  54. data/lib/capybara-ui/text_table.rb +0 -107
  55. data/lib/capybara-ui/text_table/cell_text.rb +0 -7
  56. data/lib/capybara-ui/text_table/mapping.rb +0 -40
  57. data/lib/capybara-ui/text_table/transformations.rb +0 -13
  58. data/lib/capybara-ui/text_table/void_mapping.rb +0 -8
  59. data/lib/capybara-ui/version.rb +0 -3
  60. data/lib/capybara-ui/widgets.rb +0 -61
  61. data/lib/capybara-ui/widgets/check_box.rb +0 -26
  62. data/lib/capybara-ui/widgets/cucumber_methods.rb +0 -73
  63. data/lib/capybara-ui/widgets/document.rb +0 -19
  64. data/lib/capybara-ui/widgets/dsl.rb +0 -47
  65. data/lib/capybara-ui/widgets/field.rb +0 -22
  66. data/lib/capybara-ui/widgets/field_group.rb +0 -329
  67. data/lib/capybara-ui/widgets/form.rb +0 -26
  68. data/lib/capybara-ui/widgets/list.rb +0 -200
  69. data/lib/capybara-ui/widgets/list_item.rb +0 -22
  70. data/lib/capybara-ui/widgets/parts/container.rb +0 -46
  71. data/lib/capybara-ui/widgets/parts/struct.rb +0 -117
  72. data/lib/capybara-ui/widgets/radio_button.rb +0 -62
  73. data/lib/capybara-ui/widgets/select.rb +0 -57
  74. data/lib/capybara-ui/widgets/string_value.rb +0 -43
  75. data/lib/capybara-ui/widgets/table.rb +0 -76
  76. data/lib/capybara-ui/widgets/text_field.rb +0 -27
  77. data/lib/capybara-ui/widgets/widget.rb +0 -392
  78. data/lib/capybara-ui/widgets/widget/node_filter.rb +0 -48
  79. data/lib/capybara-ui/widgets/widget_class.rb +0 -11
  80. data/lib/capybara-ui/widgets/widget_name.rb +0 -56
@@ -0,0 +1,9 @@
1
+ module Capybara
2
+ module UI
3
+ class TextTable
4
+ class CellText < String
5
+ include InstanceConversions
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,42 @@
1
+ module Capybara
2
+ module UI
3
+ class TextTable
4
+ class Mapping
5
+ def initialize(settings = {})
6
+ self.key = settings[:key]
7
+ self.value_transformer = transformer(settings[:value_transformer], :pass)
8
+ self.key_transformer = transformer(settings[:key_transformer], :keyword)
9
+ end
10
+
11
+ def set(instance, row, key, value)
12
+ row[transform_key(instance, key)] = transform_value(instance, value)
13
+ end
14
+
15
+ private
16
+
17
+ attr_accessor :key, :value_transformer, :key_transformer
18
+
19
+ def transform_key(_, k)
20
+ key || key_transformer.(k)
21
+ end
22
+
23
+ def transform_value(instance, value)
24
+ instance.instance_exec(value, &value_transformer)
25
+ end
26
+
27
+ def transformer(set, fallback)
28
+ case set
29
+ when Symbol
30
+ Transformations.send(set)
31
+ when Proc
32
+ set
33
+ when nil
34
+ Transformations.send(fallback)
35
+ else
36
+ raise ArgumentError, "can't convert #{set.inspect} to transformer"
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,15 @@
1
+ module Capybara
2
+ module UI
3
+ class TextTable
4
+ module Transformations
5
+ def self.keyword
6
+ ->(val) { val.squeeze(' ').strip.gsub(' ', '_').sub(/\?$/, '').to_sym }
7
+ end
8
+
9
+ def self.pass
10
+ ->(val) { val }
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,10 @@
1
+ module Capybara
2
+ module UI
3
+ class TextTable
4
+ class VoidMapping
5
+ def set(*)
6
+ end
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,5 @@
1
+ module Capybara
2
+ module UI
3
+ VERSION = '1.0.0'
4
+ end
5
+ end
@@ -0,0 +1,63 @@
1
+ # This file describes the organization of the major Widget API components.
2
+ #
3
+ # === Parts
4
+ #
5
+ # Widget parts encapsulate the set of behaviours that constitute a widget.
6
+ module Capybara
7
+ module UI
8
+ module Constructors
9
+ def Widget(*selector, &block)
10
+ if block_given?
11
+ WidgetClass.new(selector.flatten) do
12
+ define_method :value do
13
+ block.call(text)
14
+ end
15
+ end
16
+ else
17
+ WidgetClass.new(selector.flatten)
18
+ end
19
+ end
20
+
21
+ alias_method :String, :Widget
22
+
23
+ def Integer(*selector)
24
+ Widget(selector) { |text| Kernel::Integer(text) }
25
+ end
26
+
27
+ require 'bigdecimal'
28
+
29
+ def Decimal(*selector)
30
+ Widget(selector) { |text|
31
+ # ensure we can convert to float first
32
+ Float(text) && BigDecimal.new(text)
33
+ }
34
+ end
35
+ end
36
+
37
+ extend Constructors
38
+ end
39
+ end
40
+
41
+ module Capybara::UI::WidgetParts; end
42
+
43
+ require 'capybara/ui/widgets/parts/struct'
44
+ require 'capybara/ui/widgets/parts/container'
45
+
46
+ require 'capybara/ui/widgets/cucumber_methods'
47
+ require 'capybara/ui/widgets/dsl'
48
+ require 'capybara/ui/widgets/widget_class'
49
+ require 'capybara/ui/widgets/widget_name'
50
+ require 'capybara/ui/widgets/widget'
51
+ require 'capybara/ui/widgets/widget/node_filter'
52
+ require 'capybara/ui/widgets/list_item'
53
+ require 'capybara/ui/widgets/list'
54
+ require 'capybara/ui/widgets/field'
55
+ require 'capybara/ui/widgets/check_box'
56
+ require 'capybara/ui/widgets/radio_button'
57
+ require 'capybara/ui/widgets/select'
58
+ require 'capybara/ui/widgets/text_field'
59
+ require 'capybara/ui/widgets/field_group'
60
+ require 'capybara/ui/widgets/form'
61
+ require 'capybara/ui/widgets/document'
62
+ require 'capybara/ui/widgets/table'
63
+ require 'capybara/ui/widgets/string_value'
@@ -0,0 +1,28 @@
1
+ module Capybara
2
+ module UI
3
+ # A check box.
4
+ class CheckBox < Field
5
+ # @!method set(value)
6
+ # Checks or unchecks the current checkbox.
7
+ #
8
+ # @param value [Boolean] +true+ to check the checkbox, +false+
9
+ # otherwise.
10
+ def_delegator :root, :set
11
+
12
+ # @return [Boolean] +true+ if the checkbox is checked, +false+
13
+ # otherwise.
14
+ def get
15
+ !! root.checked?
16
+ end
17
+
18
+ def to_cell
19
+ to_s
20
+ end
21
+
22
+ # @return +"yes"+ if the checkbox is checked, +"no"+ otherwise.
23
+ def to_s
24
+ get ? 'yes' : 'no'
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,75 @@
1
+ module Capybara
2
+ module UI
3
+ module CucumberMethods
4
+ begin
5
+ require 'cucumber/ast/table'
6
+
7
+ # Compares this widget with the given Cucumber +table+.
8
+ #
9
+ # === Example
10
+ #
11
+ # Then(/^some step that takes in a cucumber table$/) do |table|
12
+ # widget(:my_widget).diff table
13
+ # end
14
+ #
15
+ # Pass +ignore_case: true+, for a case-insensitive table match
16
+ #
17
+ # === Example
18
+ #
19
+ # Then(/^some step that takes in a cucumber table$/) do |table|
20
+ # widget(:my_widget).diff table, ignore_case: true
21
+ # end
22
+ #
23
+ def diff(table, wait_time = Capybara.default_max_wait_time, ignore_case: false)
24
+ to_table = self.to_table
25
+
26
+ if ignore_case == true
27
+ table = downcase_table(table)
28
+ to_table = downcase_array(to_table)
29
+ end
30
+
31
+ table.diff!(to_table) || true
32
+ end
33
+
34
+ private
35
+
36
+ def downcase_table(table)
37
+ new_cucumber_table downcase_array(table.raw)
38
+ end
39
+
40
+ def downcase_array(array)
41
+ array.map do |item|
42
+ case item
43
+ when String
44
+ item.downcase
45
+ when Array
46
+ downcase_array(item)
47
+ when Hash
48
+ downcase_hash(item)
49
+ end
50
+ end
51
+ end
52
+
53
+ def downcase_hash(hash)
54
+ hash.each do |k, v|
55
+ case v
56
+ when String
57
+ hash[k] = v.downcase
58
+ when Array
59
+ downcase_array(v)
60
+ when Hash
61
+ downcase_hash(v)
62
+ end
63
+ end
64
+ end
65
+
66
+ def new_cucumber_table(table)
67
+ Cucumber::Ast::Table.new(table)
68
+ end
69
+
70
+ rescue LoadError
71
+ # *crickets*
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,21 @@
1
+ module Capybara
2
+ module UI
3
+ class Document
4
+ include WidgetParts::Container
5
+
6
+ def initialize(widget_lookup_scope)
7
+ self.widget_lookup_scope = widget_lookup_scope or raise 'No scope given'
8
+ end
9
+
10
+ def root
11
+ Capybara.current_session
12
+ end
13
+
14
+ def body
15
+ xml = Nokogiri::HTML(Capybara.page.body).to_xml
16
+
17
+ Nokogiri::XML(xml, &:noblanks).to_xhtml
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,49 @@
1
+ module Capybara
2
+ module UI
3
+ module Widgets
4
+ module DSL
5
+ # Declares a new form widget.
6
+ #
7
+ # See features/role.feature.
8
+ def form(name, *rest, &block)
9
+ widget name, *rest, Capybara::UI::Form, &block
10
+ end
11
+
12
+ # Declares a new list widget.
13
+ #
14
+ # See features/roles/list.feature.
15
+ def list(name, *rest, &block)
16
+ widget name, *rest, Capybara::UI::List, &block
17
+ end
18
+
19
+ # Declares a new child widget.
20
+ #
21
+ # See https://github.com/mojotech/capybara-ui/blob/master/features/roles/widget.feature
22
+ def widget(name, selector_or_parent, parent = Widget, &block)
23
+ raise ArgumentError, "`#{name}' is a reserved name" \
24
+ if WidgetParts::Container.instance_methods.include?(name.to_sym)
25
+
26
+ case selector_or_parent
27
+ when String, Array, Proc
28
+ selector, type = selector_or_parent, parent
29
+ when Class
30
+ selector, type = selector_or_parent.selector, selector_or_parent
31
+ else
32
+ raise ArgumentError, "wrong number of arguments (#{rest.size} for 1)"
33
+ end
34
+
35
+ raise TypeError, "can't convert `#{type}' to Widget" \
36
+ unless type.methods.include?(:selector)
37
+
38
+ raise ArgumentError, "missing selector" unless selector || type.selector
39
+
40
+ child = WidgetClass.new(selector, type, &block)
41
+
42
+ const_set(Capybara::UI::WidgetName.new(name).to_sym, child)
43
+
44
+ child
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,24 @@
1
+ module Capybara
2
+ module UI
3
+ # A form field.
4
+ class Field < Widget
5
+ def self.root(selector)
6
+ super String === selector ? [:field, selector] : selector
7
+ end
8
+
9
+ # @return This field's value.
10
+ #
11
+ # Override this to get the actual value.
12
+ def get
13
+ raise NotImplementedError
14
+ end
15
+
16
+ # Sets the field value.
17
+ #
18
+ # Override this to set the value.
19
+ def set(value)
20
+ raise NotImplementedError
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,331 @@
1
+ module Capybara
2
+ module UI
3
+ # A group of form fields.
4
+ #
5
+ # @todo Explain how to use locators when defining fields, including what
6
+ # happens when locators are omitted.
7
+ class FieldGroup < Widget
8
+ root 'fieldset'
9
+
10
+ def self.default_locator(type = nil, &block)
11
+ alias_method :name_to_locator, type if type
12
+
13
+ define_method :name_to_locator, &block if block
14
+ end
15
+
16
+ # The names of all the fields that belong to this field group.
17
+ #
18
+ # Field names are automatically added to this group as long as you use
19
+ # the field definition macros.
20
+ #
21
+ # @return [Set] the field names.
22
+ #
23
+ # @see field
24
+ def self.field_names
25
+ @field_names ||= Set.new
26
+ end
27
+
28
+ # @!group Field definition macros
29
+
30
+ # Creates a new checkbox accessor.
31
+ #
32
+ # Adds the following methods to the widget:
33
+ #
34
+ # <name>:: Gets the current checkbox state, as a boolean. Returns +true+
35
+ # if the corresponding check box is checked, +false+ otherwise.
36
+ # <name>=:: Sets the current checkbox state. Pass +true+ to check the
37
+ # checkbox, +false+ otherwise.
38
+ #
39
+ # @example
40
+ # # Given the following HTML:
41
+ # #
42
+ # # <form>
43
+ # # <p>
44
+ # # <label for="checked-box">
45
+ # # <input type="checkbox" value="1" id="checked-box" checked>
46
+ # # </p>
47
+ # # <p>
48
+ # # <label for="unchecked-box">
49
+ # # <input type="checkbox" value="1" id="unchecked-box">
50
+ # # </p>
51
+ # # </form>
52
+ # class MyFieldGroup < Capybara::UI::FieldGroup
53
+ # root 'form'
54
+ #
55
+ # check_box :checked_box, 'checked-box'
56
+ # check_box :unchecked_box, 'unchecked-box'
57
+ # end
58
+ #
59
+ # form = widget(:my_field_group)
60
+ #
61
+ # form.checked_box #=> true
62
+ # form.unchecked_box #=> false
63
+ #
64
+ # form.unchecked_box = true
65
+ # form.unchecked_box #=> true
66
+ #
67
+ # @param name the name of the checkbox accessor.
68
+ # @param locator the locator for the checkbox. If +nil+ the locator will
69
+ # be derived from +name+.
70
+ #
71
+ # @todo Handle checkbox access when the field is disabled (raise an
72
+ # exception?)
73
+ def self.check_box(name, locator = nil)
74
+ field name, locator, CheckBox
75
+ end
76
+
77
+ # Defines a new field.
78
+ #
79
+ # @param name the name of the field accessor.
80
+ # @param locator the field locator.
81
+ # @param type the field class name.
82
+ #
83
+ # @api private
84
+ def self.field(name, locator, type)
85
+ raise TypeError, "can't convert `#{name}' to Symbol" \
86
+ unless name.respond_to?(:to_sym)
87
+
88
+ field_names << name.to_sym
89
+
90
+ label = name.to_s.gsub(/_/, ' ').capitalize
91
+ locator ||= label
92
+
93
+ widget name, locator, type do
94
+ define_method :label do
95
+ label
96
+ end
97
+ end
98
+
99
+ define_method "#{name}=" do |val|
100
+ widget(name).set val
101
+ end
102
+
103
+ define_method name do
104
+ widget(name).get
105
+ end
106
+ end
107
+
108
+ # Creates a new select accessor.
109
+ #
110
+ # Adds the following methods to the widget:
111
+ #
112
+ # <name>:: Gets the text of the current selected option, or +nil+,
113
+ # if no option is selected.
114
+ # <name>_value:: Gets the value of the current selected option, or
115
+ # +nil+, if no option is selected.
116
+ # <name>=:: Selects an option on the current select. Pass the text or
117
+ # value of the option you want to select.
118
+ #
119
+ # @example
120
+ # # Given the following HTML:
121
+ # #
122
+ # # <form>
123
+ # # <p>
124
+ # # <label for="selected">
125
+ # # <select id="selected">
126
+ # # <option value ="1s" selected>Selected option</option>
127
+ # # <option value ="2s">Selected option two</option>
128
+ # # </select>
129
+ # # </p>
130
+ # # <p>
131
+ # # <label for="unselected">
132
+ # # <select id="unselected">
133
+ # # <option value="1u">Unselected option</option>
134
+ # # <option value="2u">Unselected option two</option>
135
+ # # </select>
136
+ # # </p>
137
+ # # </form>
138
+ # class MyFieldGroup < Capybara::UI::FieldGroup
139
+ # root 'form'
140
+ #
141
+ # select :selected, 'selected'
142
+ # select :unselected, 'unselected'
143
+ # end
144
+ #
145
+ # form = widget(:my_field_group)
146
+ #
147
+ # form.selected #=> "Selected option"
148
+ # form.selected_value #=> "1s"
149
+ #
150
+ # # Select by text
151
+ # form.unselected #=> nil
152
+ # form.unselected = "Unselected option"
153
+ # form.unselected #=> "Unselected option"
154
+ #
155
+ # # Select by value
156
+ # form.unselected = "2u"
157
+ # form.unselected #=> "Unselected option two"
158
+ # form.unselected_value #=> "2u"
159
+ #
160
+ # @param name the name of the select accessor.
161
+ # @param locator the locator for the select. If +nil+ the locator will
162
+ # be derived from +name+.
163
+ #
164
+ # @todo Handle select access when the field is disabled (raise an
165
+ # exception?)
166
+ # @todo Raise an exception when an option doesn't exist.
167
+ # @todo Allow passing the option value to set an option.
168
+ # @todo Ensure an option with no text returns the empty string.
169
+ # @todo What to do when +nil+ is passed to the writer?
170
+ def self.select(name, locator = nil)
171
+ field name, locator, Select
172
+
173
+ define_method "#{name}_value" do
174
+ widget(name).value
175
+ end
176
+ end
177
+
178
+ # Creates a new radio button group accessor.
179
+ #
180
+ # Adds the following methods to the widget:
181
+ #
182
+ # <name>:: Gets the text of the current checked button, or +nil+,
183
+ # if no button is checked.
184
+ # <name>_value:: Gets the value of the current checked button, or +nil+,
185
+ # if no button is checked.
186
+ # <name>=:: Checks a button in the current container. Pass the text of
187
+ # the label or the id or value of the button you want to choose.
188
+ #
189
+ # @example
190
+ # # Given the following HTML:
191
+ # #
192
+ # # <form>
193
+ # # <p class='checked'>
194
+ # # <label for="checked">Checked button</label>
195
+ # # <input type="radio" id="checked" name="c" value="checked_value" checked>
196
+ # # <label for="checked_two">Checked button two</label>
197
+ # # <input type="radio" id="checked_two" name="c" value="checked_two_value">
198
+ # # </p>
199
+ # # <p class='unchecked'>
200
+ # # <label for="unchecked">Unchecked button</label>
201
+ # # <input type="radio" id="unchecked" name="u" value="unchecked_value_one">
202
+ # # <label for="unchecked_two">Unchecked button two</label>
203
+ # # <input type="radio" id="unchecked_two" name="u" value="unchecked_value_two">
204
+ # # </p>
205
+ # # </form>
206
+ # class MyFieldGroup < Capybara::UI::FieldGroup
207
+ # root 'form'
208
+ #
209
+ # radio_button :checked, '.checked'
210
+ # radio_button :unchecked, '.unchecked'
211
+ # end
212
+ #
213
+ # form = widget(:my_field_group)
214
+ #
215
+ # form.checked #=> "Checked button"
216
+ # form.unchecked #=> nil
217
+ #
218
+ # form.unchecked = "Unchecked button" # Choose by label text
219
+ # form.unchecked #=> "Unchecked button"
220
+ # form.unchecked_value #=> "unchecked_value_one"
221
+ #
222
+ # form.unchecked = "unchecked_two" # Choose by id
223
+ # form.unchecked #=> "Unchecked button two"
224
+ # form.unchecked_value #=> "unchecked_value_two"
225
+ #
226
+ # form.unchecked = "unchecked_value_one" # Choose by value
227
+ # form.unchecked #=> "Unchecked button"
228
+ #
229
+ # @param name the name of the radio_button group accessor.
230
+ # @param locator the locator for the radio_button group.
231
+ #
232
+ def self.radio_button(name, locator = nil)
233
+ field name, locator, RadioButton
234
+
235
+ define_method "#{name}_value" do
236
+ widget(name).value
237
+ end
238
+ end
239
+
240
+ # Creates a new text field accessor.
241
+ #
242
+ # Adds the following methods to the widget:
243
+ #
244
+ # <name>:: Returns the current text field value, or +nil+ if no value
245
+ # has been set.
246
+ # <name>=:: Sets the current text field value.
247
+ # <name>? Returns +true+ if the current text field has content or
248
+ # +false+ otherwise
249
+ #
250
+ # @example
251
+ # # Given the following HTML:
252
+ # #
253
+ # # <form>
254
+ # # <p>
255
+ # # <label for="text-field">
256
+ # # <input type="text" value="Content" id="text-field">
257
+ # # </p>
258
+ # # <p>
259
+ # # <label for="empty-field">
260
+ # # <input type="text" id="empty-field">
261
+ # # </p>
262
+ # # </form>
263
+ # class MyFieldGroup < Capybara::UI::FieldGroup
264
+ # root 'form'
265
+ #
266
+ # text_field :filled_field, 'text-field'
267
+ # text_field :empty_field, 'empty-field'
268
+ # end
269
+ #
270
+ # form = widget(:my_field_group)
271
+ #
272
+ # form.filled_field #=> "Content"
273
+ # form.empty_field #=> nil
274
+ #
275
+ # form.filled_field? #=> true
276
+ # form.empty_field? #=> false
277
+ #
278
+ # form.empty_field = "Not anymore"
279
+ # form.empty_field #=> "Not anymore"
280
+ #
281
+ # @param name the name of the text field accessor.
282
+ # @param locator the locator for the text field. If +nil+ the locator
283
+ # will be derived from +name+.
284
+ #
285
+ # @todo Handle text field access when the field is disabled (raise an
286
+ # exception?)
287
+ def self.text_field(name, locator = nil)
288
+ define_method "#{name}?" do
289
+ widget(name).content?
290
+ end
291
+
292
+ field name, locator, TextField
293
+ end
294
+
295
+ # @!endgroup
296
+
297
+ # @return This field group's field widgets.
298
+ def fields
299
+ self.class.field_names.map { |name| widget(name) }
300
+ end
301
+
302
+ # Sets the given form attributes.
303
+ #
304
+ # @param attributes [Hash] the attributes and values we want to set.
305
+ #
306
+ # @return the current widget.
307
+ def set(attributes)
308
+ attributes.each do |k, v|
309
+ send "#{k}=", v
310
+ end
311
+
312
+ self
313
+ end
314
+
315
+ # Converts the current field group into a table suitable for diff'ing
316
+ # with Cucumber::Ast::Table.
317
+ #
318
+ # Field labels are determined by the widget name.
319
+ #
320
+ # Field values correspond to the return value of each field's +to_s+.
321
+ #
322
+ # @return [Array<Array>] the table.
323
+ def to_table
324
+ headers = fields.map { |field| field.label.downcase }
325
+ body = fields.map { |field| field.to_s.downcase }
326
+
327
+ [headers, body]
328
+ end
329
+ end
330
+ end
331
+ end