formtastic 3.1.3 → 5.0.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 (179) hide show
  1. checksums.yaml +7 -0
  2. data/.gitattributes +1 -0
  3. data/.github/workflows/test.yml +61 -0
  4. data/.gitignore +3 -2
  5. data/CHANGELOG.md +61 -0
  6. data/Gemfile.lock +140 -0
  7. data/MIT-LICENSE +1 -1
  8. data/{README.textile → README.md} +183 -167
  9. data/RELEASE_PROCESS +3 -1
  10. data/Rakefile +20 -1
  11. data/app/assets/stylesheets/formtastic.css +1 -1
  12. data/bin/appraisal +8 -0
  13. data/formtastic.gemspec +12 -16
  14. data/gemfiles/rails_6.0/Gemfile +5 -0
  15. data/gemfiles/rails_6.1/Gemfile +5 -0
  16. data/gemfiles/rails_7.0/Gemfile +5 -0
  17. data/gemfiles/rails_7.1/Gemfile +5 -0
  18. data/gemfiles/rails_edge/Gemfile +13 -0
  19. data/lib/formtastic/action_class_finder.rb +1 -0
  20. data/lib/formtastic/actions/base.rb +1 -0
  21. data/lib/formtastic/actions/button_action.rb +1 -0
  22. data/lib/formtastic/actions/buttonish.rb +1 -0
  23. data/lib/formtastic/actions/input_action.rb +1 -0
  24. data/lib/formtastic/actions/link_action.rb +1 -0
  25. data/lib/formtastic/actions.rb +7 -3
  26. data/lib/formtastic/deprecation.rb +2 -38
  27. data/lib/formtastic/engine.rb +4 -1
  28. data/lib/formtastic/form_builder.rb +12 -24
  29. data/lib/formtastic/helpers/action_helper.rb +2 -48
  30. data/lib/formtastic/helpers/actions_helper.rb +1 -0
  31. data/lib/formtastic/helpers/enum.rb +14 -0
  32. data/lib/formtastic/helpers/errors_helper.rb +3 -2
  33. data/lib/formtastic/helpers/fieldset_wrapper.rb +14 -9
  34. data/lib/formtastic/helpers/file_column_detection.rb +1 -0
  35. data/lib/formtastic/helpers/form_helper.rb +2 -1
  36. data/lib/formtastic/helpers/input_helper.rb +20 -76
  37. data/lib/formtastic/helpers/inputs_helper.rb +29 -23
  38. data/lib/formtastic/helpers/reflection.rb +1 -0
  39. data/lib/formtastic/helpers.rb +2 -2
  40. data/lib/formtastic/html_attributes.rb +1 -0
  41. data/lib/formtastic/i18n.rb +2 -1
  42. data/lib/formtastic/input_class_finder.rb +1 -0
  43. data/lib/formtastic/inputs/base/associations.rb +1 -0
  44. data/lib/formtastic/inputs/base/choices.rb +3 -2
  45. data/lib/formtastic/inputs/base/collections.rb +46 -10
  46. data/lib/formtastic/inputs/base/database.rb +5 -7
  47. data/lib/formtastic/inputs/base/datetime_pickerish.rb +1 -0
  48. data/lib/formtastic/inputs/base/errors.rb +7 -6
  49. data/lib/formtastic/inputs/base/fileish.rb +1 -0
  50. data/lib/formtastic/inputs/base/hints.rb +2 -1
  51. data/lib/formtastic/inputs/base/html.rb +9 -7
  52. data/lib/formtastic/inputs/base/labelling.rb +3 -2
  53. data/lib/formtastic/inputs/base/naming.rb +5 -4
  54. data/lib/formtastic/inputs/base/numeric.rb +1 -0
  55. data/lib/formtastic/inputs/base/options.rb +3 -3
  56. data/lib/formtastic/inputs/base/placeholder.rb +1 -0
  57. data/lib/formtastic/inputs/base/stringish.rb +1 -0
  58. data/lib/formtastic/inputs/base/timeish.rb +9 -4
  59. data/lib/formtastic/inputs/base/validations.rb +39 -12
  60. data/lib/formtastic/inputs/base/wrapping.rb +1 -0
  61. data/lib/formtastic/inputs/base.rb +3 -2
  62. data/lib/formtastic/inputs/boolean_input.rb +2 -1
  63. data/lib/formtastic/inputs/check_boxes_input.rb +15 -6
  64. data/lib/formtastic/inputs/color_input.rb +1 -1
  65. data/lib/formtastic/inputs/country_input.rb +4 -1
  66. data/lib/formtastic/inputs/datalist_input.rb +1 -0
  67. data/lib/formtastic/inputs/date_picker_input.rb +1 -0
  68. data/lib/formtastic/inputs/date_select_input.rb +1 -0
  69. data/lib/formtastic/inputs/datetime_picker_input.rb +1 -0
  70. data/lib/formtastic/inputs/datetime_select_input.rb +1 -0
  71. data/lib/formtastic/inputs/email_input.rb +1 -0
  72. data/lib/formtastic/inputs/file_input.rb +1 -0
  73. data/lib/formtastic/inputs/hidden_input.rb +3 -2
  74. data/lib/formtastic/inputs/number_input.rb +1 -0
  75. data/lib/formtastic/inputs/password_input.rb +1 -0
  76. data/lib/formtastic/inputs/phone_input.rb +1 -0
  77. data/lib/formtastic/inputs/radio_input.rb +21 -0
  78. data/lib/formtastic/inputs/range_input.rb +1 -0
  79. data/lib/formtastic/inputs/search_input.rb +1 -0
  80. data/lib/formtastic/inputs/select_input.rb +30 -1
  81. data/lib/formtastic/inputs/string_input.rb +1 -0
  82. data/lib/formtastic/inputs/text_input.rb +1 -0
  83. data/lib/formtastic/inputs/time_picker_input.rb +1 -0
  84. data/lib/formtastic/inputs/time_select_input.rb +1 -0
  85. data/lib/formtastic/inputs/time_zone_input.rb +17 -6
  86. data/lib/formtastic/inputs/url_input.rb +1 -0
  87. data/lib/formtastic/inputs.rb +33 -29
  88. data/lib/formtastic/localized_string.rb +1 -0
  89. data/lib/formtastic/localizer.rb +21 -22
  90. data/lib/formtastic/namespaced_class_finder.rb +8 -9
  91. data/lib/formtastic/version.rb +2 -1
  92. data/lib/formtastic.rb +10 -11
  93. data/lib/generators/formtastic/form/form_generator.rb +2 -1
  94. data/lib/generators/formtastic/input/input_generator.rb +47 -0
  95. data/lib/generators/formtastic/install/install_generator.rb +1 -0
  96. data/lib/generators/templates/formtastic.rb +15 -13
  97. data/lib/generators/templates/input.rb +19 -0
  98. data/sample/basic_inputs.html +1 -1
  99. data/script/integration-template.rb +73 -0
  100. data/script/integration.sh +19 -0
  101. data/spec/action_class_finder_spec.rb +2 -1
  102. data/spec/actions/button_action_spec.rb +21 -20
  103. data/spec/actions/generic_action_spec.rb +134 -133
  104. data/spec/actions/input_action_spec.rb +20 -19
  105. data/spec/actions/link_action_spec.rb +30 -29
  106. data/spec/builder/custom_builder_spec.rb +39 -22
  107. data/spec/builder/error_proc_spec.rb +6 -5
  108. data/spec/builder/semantic_fields_for_spec.rb +46 -45
  109. data/spec/fast_spec_helper.rb +13 -0
  110. data/spec/generators/formtastic/form/form_generator_spec.rb +33 -32
  111. data/spec/generators/formtastic/input/input_generator_spec.rb +125 -0
  112. data/spec/generators/formtastic/install/install_generator_spec.rb +10 -9
  113. data/spec/helpers/action_helper_spec.rb +329 -10
  114. data/spec/helpers/actions_helper_spec.rb +43 -42
  115. data/spec/helpers/form_helper_spec.rb +45 -38
  116. data/spec/helpers/input_helper_spec.rb +976 -2
  117. data/spec/helpers/inputs_helper_spec.rb +217 -202
  118. data/spec/helpers/reflection_helper_spec.rb +7 -6
  119. data/spec/helpers/semantic_errors_helper_spec.rb +26 -25
  120. data/spec/i18n_spec.rb +30 -29
  121. data/spec/input_class_finder_spec.rb +2 -1
  122. data/spec/inputs/base/collections_spec.rb +78 -0
  123. data/spec/inputs/base/validations_spec.rb +481 -0
  124. data/spec/inputs/boolean_input_spec.rb +73 -72
  125. data/spec/inputs/check_boxes_input_spec.rb +169 -121
  126. data/spec/inputs/color_input_spec.rb +53 -64
  127. data/spec/inputs/country_input_spec.rb +23 -22
  128. data/spec/inputs/custom_input_spec.rb +3 -6
  129. data/spec/inputs/datalist_input_spec.rb +3 -2
  130. data/spec/inputs/date_picker_input_spec.rb +114 -113
  131. data/spec/inputs/date_select_input_spec.rb +76 -61
  132. data/spec/inputs/datetime_picker_input_spec.rb +123 -122
  133. data/spec/inputs/datetime_select_input_spec.rb +85 -68
  134. data/spec/inputs/email_input_spec.rb +17 -16
  135. data/spec/inputs/file_input_spec.rb +18 -17
  136. data/spec/inputs/hidden_input_spec.rb +32 -31
  137. data/spec/inputs/include_blank_spec.rb +10 -9
  138. data/spec/inputs/label_spec.rb +26 -25
  139. data/spec/inputs/number_input_spec.rb +212 -211
  140. data/spec/inputs/password_input_spec.rb +17 -16
  141. data/spec/inputs/phone_input_spec.rb +17 -16
  142. data/spec/inputs/placeholder_spec.rb +18 -17
  143. data/spec/inputs/radio_input_spec.rb +92 -65
  144. data/spec/inputs/range_input_spec.rb +136 -135
  145. data/spec/inputs/readonly_spec.rb +51 -0
  146. data/spec/inputs/search_input_spec.rb +16 -15
  147. data/spec/inputs/select_input_spec.rb +209 -102
  148. data/spec/inputs/string_input_spec.rb +51 -50
  149. data/spec/inputs/text_input_spec.rb +34 -33
  150. data/spec/inputs/time_picker_input_spec.rb +115 -114
  151. data/spec/inputs/time_select_input_spec.rb +84 -70
  152. data/spec/inputs/time_zone_input_spec.rb +58 -31
  153. data/spec/inputs/url_input_spec.rb +17 -16
  154. data/spec/inputs/with_options_spec.rb +9 -8
  155. data/spec/localizer_spec.rb +18 -17
  156. data/spec/namespaced_class_finder_spec.rb +18 -6
  157. data/spec/schema.rb +22 -0
  158. data/spec/spec_helper.rb +172 -260
  159. data/spec/support/custom_macros.rb +74 -76
  160. data/spec/support/deprecation.rb +2 -1
  161. data/spec/support/shared_examples.rb +2 -1233
  162. data/spec/support/specialized_class_finder_shared_example.rb +1 -0
  163. data/spec/support/test_environment.rb +24 -9
  164. metadata +78 -170
  165. data/.travis.yml +0 -29
  166. data/Appraisals +0 -29
  167. data/CHANGELOG +0 -39
  168. data/DEPRECATIONS +0 -49
  169. data/gemfiles/rails_3.2.gemfile +0 -7
  170. data/gemfiles/rails_4.0.4.gemfile +0 -7
  171. data/gemfiles/rails_4.1.gemfile +0 -7
  172. data/gemfiles/rails_4.2.gemfile +0 -7
  173. data/gemfiles/rails_4.gemfile +0 -7
  174. data/gemfiles/rails_edge.gemfile +0 -10
  175. data/lib/formtastic/util.rb +0 -57
  176. data/spec/helpers/namespaced_action_helper_spec.rb +0 -43
  177. data/spec/helpers/namespaced_input_helper_spec.rb +0 -36
  178. data/spec/support/deferred_garbage_collection.rb +0 -21
  179. data/spec/util_spec.rb +0 -66
@@ -1,6 +1,980 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
  require 'spec_helper'
3
4
 
4
- describe 'Formtastic::FormBuilder#input' do
5
- it_behaves_like 'Input Helper' # from spec/support/shared_examples.rb
5
+ RSpec.describe 'with input class finder' do
6
+ include_context 'form builder'
7
+
8
+ before do
9
+ @errors = double('errors')
10
+ allow(@errors).to receive(:[]).and_return([])
11
+ allow(@new_post).to receive(:errors).and_return(@errors)
12
+ end
13
+
14
+ describe 'arguments and options' do
15
+
16
+ it 'should require the first argument (the method on form\'s object)' do
17
+ expect {
18
+ concat(semantic_form_for(@new_post) do |builder|
19
+ concat(builder.input()) # no args passed in at all
20
+ end)
21
+ }.to raise_error(ArgumentError)
22
+ end
23
+
24
+ describe ':required option' do
25
+
26
+ describe 'when true' do
27
+
28
+ it 'should set a "required" class' do
29
+ with_config :required_string, " required yo!" do
30
+ concat(semantic_form_for(@new_post) do |builder|
31
+ concat(builder.input(:title, :required => true))
32
+ end)
33
+ expect(output_buffer.to_str).not_to have_tag('form li.optional')
34
+ expect(output_buffer.to_str).to have_tag('form li.required')
35
+ end
36
+ end
37
+
38
+ it 'should append the "required" string to the label' do
39
+ with_config :required_string, " required yo!" do
40
+ concat(semantic_form_for(@new_post) do |builder|
41
+ concat(builder.input(:title, :required => true))
42
+ end)
43
+ expect(output_buffer.to_str).to have_tag('form li.required label', :text => /required yo/)
44
+ end
45
+ end
46
+ end
47
+
48
+ describe 'when false' do
49
+
50
+ before do
51
+ @string = Formtastic::FormBuilder.optional_string = " optional yo!" # ensure there's something in the string
52
+ expect(@new_post.class).not_to receive(:reflect_on_all_validations)
53
+ end
54
+
55
+ after do
56
+ Formtastic::FormBuilder.optional_string = ''
57
+ end
58
+
59
+ it 'should set an "optional" class' do
60
+ concat(semantic_form_for(@new_post) do |builder|
61
+ concat(builder.input(:title, :required => false))
62
+ end)
63
+ expect(output_buffer.to_str).not_to have_tag('form li.required')
64
+ expect(output_buffer.to_str).to have_tag('form li.optional')
65
+ end
66
+
67
+ it 'should set and "optional" class also when there is presence validator' do
68
+ expect(@new_post.class).to receive(:validators_on).with(:title).at_least(:once).and_return([
69
+ active_model_presence_validator([:title])
70
+ ])
71
+ concat(semantic_form_for(@new_post) do |builder|
72
+ concat(builder.input(:title, :required => false))
73
+ end)
74
+ expect(output_buffer.to_str).not_to have_tag('form li.required')
75
+ expect(output_buffer.to_str).to have_tag('form li.optional')
76
+ end
77
+
78
+ it 'should append the "optional" string to the label' do
79
+ concat(semantic_form_for(@new_post) do |builder|
80
+ concat(builder.input(:title, :required => false))
81
+ end)
82
+ expect(output_buffer.to_str).to have_tag('form li.optional label', :text => /#{@string}$/)
83
+ end
84
+
85
+ end
86
+
87
+ describe 'when not provided' do
88
+
89
+ describe 'and an object was not given' do
90
+
91
+ it 'should use the default value' do
92
+ expect(Formtastic::FormBuilder.all_fields_required_by_default).to eq(true)
93
+ Formtastic::FormBuilder.all_fields_required_by_default = false
94
+
95
+ concat(semantic_form_for(:project, :url => 'http://test.host/') do |builder|
96
+ concat(builder.input(:title))
97
+ end)
98
+ expect(output_buffer.to_str).not_to have_tag('form li.required')
99
+ expect(output_buffer.to_str).to have_tag('form li.optional')
100
+
101
+ Formtastic::FormBuilder.all_fields_required_by_default = true
102
+ end
103
+
104
+ end
105
+
106
+ describe 'and an object with :validators_on was given (ActiveModel, Active Resource)' do
107
+ before do
108
+ allow(@new_post).to receive(:class).and_return(::PostModel)
109
+ end
110
+
111
+ after do
112
+ allow(@new_post).to receive(:class).and_return(::Post)
113
+ end
114
+ describe 'and validates_presence_of was called for the method' do
115
+ it 'should be required' do
116
+
117
+ expect(@new_post.class).to receive(:validators_on).with(:title).at_least(:once).and_return([
118
+ active_model_presence_validator([:title])
119
+ ])
120
+
121
+ expect(@new_post.class).to receive(:validators_on).with(:body).at_least(:once).and_return([
122
+ active_model_presence_validator([:body], {:if => true})
123
+ ])
124
+
125
+ concat(semantic_form_for(@new_post) do |builder|
126
+ concat(builder.input(:title))
127
+ concat(builder.input(:body))
128
+ end)
129
+ expect(output_buffer.to_str).to have_tag('form li.required')
130
+ expect(output_buffer.to_str).not_to have_tag('form li.optional')
131
+ end
132
+
133
+ it 'should be required when there is :on => :create option on create' do
134
+ with_config :required_string, " required yo!" do
135
+ expect(@new_post.class).to receive(:validators_on).with(:title).at_least(:once).and_return([
136
+ active_model_presence_validator([:title], {:on => :create})
137
+ ])
138
+ concat(semantic_form_for(@new_post) do |builder|
139
+ concat(builder.input(:title))
140
+ end)
141
+ expect(output_buffer.to_str).to have_tag('form li.required')
142
+ expect(output_buffer.to_str).not_to have_tag('form li.optional')
143
+ end
144
+ end
145
+
146
+ it 'should be required when there is :create option in validation contexts array on create' do
147
+ with_config :required_string, " required yo!" do
148
+ expect(@new_post.class).to receive(:validators_on).with(:title).at_least(:once).and_return([
149
+ active_model_presence_validator([:title], {:on => [:create]})
150
+ ])
151
+ concat(semantic_form_for(@new_post) do |builder|
152
+ concat(builder.input(:title))
153
+ end)
154
+ expect(output_buffer.to_str).to have_tag('form li.required')
155
+ expect(output_buffer.to_str).not_to have_tag('form li.optional')
156
+ end
157
+ end
158
+
159
+ it 'should be required when there is :on => :save option on create' do
160
+ with_config :required_string, " required yo!" do
161
+ expect(@new_post.class).to receive(:validators_on).with(:title).at_least(:once).and_return([
162
+ active_model_presence_validator([:title], {:on => :save})
163
+ ])
164
+ concat(semantic_form_for(@new_post) do |builder|
165
+ concat(builder.input(:title))
166
+ end)
167
+ expect(output_buffer.to_str).to have_tag('form li.required')
168
+ expect(output_buffer.to_str).not_to have_tag('form li.optional')
169
+ end
170
+ end
171
+
172
+ it 'should be required when there is :save option in validation contexts array on create' do
173
+ with_config :required_string, " required yo!" do
174
+ expect(@new_post.class).to receive(:validators_on).with(:title).at_least(:once).and_return([
175
+ active_model_presence_validator([:title], {:on => [:save]})
176
+ ])
177
+ concat(semantic_form_for(@new_post) do |builder|
178
+ concat(builder.input(:title))
179
+ end)
180
+ expect(output_buffer.to_str).to have_tag('form li.required')
181
+ expect(output_buffer.to_str).not_to have_tag('form li.optional')
182
+ end
183
+ end
184
+
185
+ it 'should be required when there is :on => :save option on update' do
186
+ with_config :required_string, " required yo!" do
187
+ expect(@fred.class).to receive(:validators_on).with(:login).at_least(:once).and_return([
188
+ active_model_presence_validator([:login], {:on => :save})
189
+ ])
190
+ concat(semantic_form_for(@fred) do |builder|
191
+ concat(builder.input(:login))
192
+ end)
193
+ expect(output_buffer.to_str).to have_tag('form li.required')
194
+ expect(output_buffer.to_str).not_to have_tag('form li.optional')
195
+ end
196
+ end
197
+
198
+ it 'should be required when there is :save option in validation contexts array on update' do
199
+ with_config :required_string, " required yo!" do
200
+ expect(@fred.class).to receive(:validators_on).with(:login).at_least(:once).and_return([
201
+ active_model_presence_validator([:login], {:on => [:save]})
202
+ ])
203
+ concat(semantic_form_for(@fred) do |builder|
204
+ concat(builder.input(:login))
205
+ end)
206
+ expect(output_buffer.to_str).to have_tag('form li.required')
207
+ expect(output_buffer.to_str).not_to have_tag('form li.optional')
208
+ end
209
+ end
210
+
211
+ it 'should not be required when there is :on => :create option on update' do
212
+ expect(@fred.class).to receive(:validators_on).with(:login).at_least(:once).and_return([
213
+ active_model_presence_validator([:login], {:on => :create})
214
+ ])
215
+ concat(semantic_form_for(@fred) do |builder|
216
+ concat(builder.input(:login))
217
+ end)
218
+ expect(output_buffer.to_str).not_to have_tag('form li.required')
219
+ expect(output_buffer.to_str).to have_tag('form li.optional')
220
+ end
221
+
222
+ it 'should not be required when there is :create option in validation contexts array on update' do
223
+ expect(@fred.class).to receive(:validators_on).with(:login).at_least(:once).and_return([
224
+ active_model_presence_validator([:login], {:on => [:create]})
225
+ ])
226
+ concat(semantic_form_for(@fred) do |builder|
227
+ concat(builder.input(:login))
228
+ end)
229
+ expect(output_buffer.to_str).not_to have_tag('form li.required')
230
+ expect(output_buffer.to_str).to have_tag('form li.optional')
231
+ end
232
+
233
+ it 'should not be required when there is :on => :update option on create' do
234
+ expect(@new_post.class).to receive(:validators_on).with(:title).at_least(:once).and_return([
235
+ active_model_presence_validator([:title], {:on => :update})
236
+ ])
237
+ concat(semantic_form_for(@new_post) do |builder|
238
+ concat(builder.input(:title))
239
+ end)
240
+ expect(output_buffer.to_str).not_to have_tag('form li.required')
241
+ expect(output_buffer.to_str).to have_tag('form li.optional')
242
+ end
243
+
244
+ it 'should not be required when there is :update option in validation contexts array on create' do
245
+ expect(@new_post.class).to receive(:validators_on).with(:title).at_least(:once).and_return([
246
+ active_model_presence_validator([:title], {:on => [:update]})
247
+ ])
248
+ concat(semantic_form_for(@new_post) do |builder|
249
+ concat(builder.input(:title))
250
+ end)
251
+ expect(output_buffer.to_str).not_to have_tag('form li.required')
252
+ expect(output_buffer.to_str).to have_tag('form li.optional')
253
+ end
254
+
255
+ it 'should be not be required if the optional :if condition is not satisifed' do
256
+ presence_should_be_required(:required => false, :tag => :body, :options => { :if => false })
257
+ end
258
+
259
+ it 'should not be required if the optional :if proc evaluates to false' do
260
+ presence_should_be_required(:required => false, :tag => :body, :options => { :if => proc { |record| false } })
261
+ end
262
+
263
+ it 'should be required if the optional :if proc evaluates to true' do
264
+ presence_should_be_required(:required => true, :tag => :body, :options => { :if => proc { |record| true } })
265
+ end
266
+
267
+ it 'should not be required if the optional :unless proc evaluates to true' do
268
+ presence_should_be_required(:required => false, :tag => :body, :options => { :unless => proc { |record| true } })
269
+ end
270
+
271
+ it 'should be required if the optional :unless proc evaluates to false' do
272
+ presence_should_be_required(:required => true, :tag => :body, :options => { :unless => proc { |record| false } })
273
+ end
274
+
275
+ it 'should be required if the optional :if with a method string evaluates to true' do
276
+ expect(@new_post).to receive(:required_condition).and_return(true)
277
+ presence_should_be_required(:required => true, :tag => :body, :options => { :if => :required_condition })
278
+ end
279
+
280
+ it 'should be required if the optional :if with a method string evaluates to false' do
281
+ expect(@new_post).to receive(:required_condition).and_return(false)
282
+ presence_should_be_required(:required => false, :tag => :body, :options => { :if => :required_condition })
283
+ end
284
+
285
+ it 'should be required if the optional :unless with a method string evaluates to false' do
286
+ expect(@new_post).to receive(:required_condition).and_return(false)
287
+ presence_should_be_required(:required => true, :tag => :body, :options => { :unless => :required_condition })
288
+ end
289
+
290
+ it 'should not be required if the optional :unless with a method string evaluates to true' do
291
+ expect(@new_post).to receive(:required_condition).and_return(true)
292
+ presence_should_be_required(:required => false, :tag => :body, :options => { :unless => :required_condition })
293
+ end
294
+ end
295
+
296
+ describe 'and validates_inclusion_of was called for the method' do
297
+ it 'should be required' do
298
+ expect(@new_post.class).to receive(:validators_on).with(:published).at_least(:once).and_return([
299
+ active_model_inclusion_validator([:published], {:in => [false, true]})
300
+ ])
301
+ should_be_required(:tag => :published, :required => true)
302
+ end
303
+
304
+ it 'should not be required if allow_blank is true' do
305
+ expect(@new_post.class).to receive(:validators_on).with(:published).at_least(:once).and_return([
306
+ active_model_inclusion_validator([:published], {:in => [false, true], :allow_blank => true})
307
+ ])
308
+ should_be_required(:tag => :published, :required => false)
309
+ end
310
+ end
311
+
312
+ describe 'and validates_length_of was called for the method' do
313
+ it 'should be required if minimum is set' do
314
+ length_should_be_required(:tag => :title, :required => true, :options => {:minimum => 1})
315
+ end
316
+
317
+ it 'should be required if :within is set' do
318
+ length_should_be_required(:tag => :title, :required => true, :options => {:within => 1..5})
319
+ end
320
+
321
+ it 'should not be required if :within allows zero length' do
322
+ length_should_be_required(:tag => :title, :required => false, :options => {:within => 0..5})
323
+ end
324
+
325
+ it 'should not be required if only :minimum is zero' do
326
+ length_should_be_required(:tag => :title, :required => false, :options => {:minimum => 0})
327
+ end
328
+
329
+ it 'should not be required if only :minimum is not set' do
330
+ length_should_be_required(:tag => :title, :required => false, :options => {:maximum => 5})
331
+ end
332
+
333
+ it 'should not be required if allow_blank is true' do
334
+ length_should_be_required(:tag => :published, :required => false, :options => {:allow_blank => true})
335
+ end
336
+ end
337
+
338
+ def add_presence_validator(options)
339
+ allow(@new_post.class).to receive(:validators_on).with(options[:tag]).and_return([
340
+ active_model_presence_validator([options[:tag]], options[:options])
341
+ ])
342
+ end
343
+
344
+ def add_length_validator(options)
345
+ expect(@new_post.class).to receive(:validators_on).with(options[:tag]).at_least(:once) {[
346
+ active_model_length_validator([options[:tag]], options[:options])
347
+ ]}
348
+ end
349
+
350
+ # TODO make a matcher for this?
351
+ def should_be_required(options)
352
+ concat(semantic_form_for(@new_post) do |builder|
353
+ concat(builder.input(options[:tag]))
354
+ end)
355
+
356
+ if options[:required]
357
+ expect(output_buffer.to_str).not_to have_tag('form li.optional')
358
+ expect(output_buffer.to_str).to have_tag('form li.required')
359
+ else
360
+ expect(output_buffer.to_str).to have_tag('form li.optional')
361
+ expect(output_buffer.to_str).not_to have_tag('form li.required')
362
+ end
363
+ end
364
+
365
+ def presence_should_be_required(options)
366
+ add_presence_validator(options)
367
+ should_be_required(options)
368
+ end
369
+
370
+ def length_should_be_required(options)
371
+ add_length_validator(options)
372
+ should_be_required(options)
373
+ end
374
+
375
+ # TODO JF reversed this during refactor, need to make sure
376
+ describe 'and there are no requirement validations on the method' do
377
+ before do
378
+ expect(@new_post.class).to receive(:validators_on).with(:title).and_return([])
379
+ end
380
+
381
+ it 'should not be required' do
382
+ concat(semantic_form_for(@new_post) do |builder|
383
+ concat(builder.input(:title))
384
+ end)
385
+ expect(output_buffer.to_str).not_to have_tag('form li.required')
386
+ expect(output_buffer.to_str).to have_tag('form li.optional')
387
+ end
388
+ end
389
+
390
+ end
391
+
392
+ describe 'and an object without :validators_on' do
393
+
394
+ it 'should use the default value' do
395
+ expect(Formtastic::FormBuilder.all_fields_required_by_default).to eq(true)
396
+ Formtastic::FormBuilder.all_fields_required_by_default = false
397
+
398
+ concat(semantic_form_for(@new_post) do |builder|
399
+ concat(builder.input(:title))
400
+ end)
401
+ expect(output_buffer.to_str).not_to have_tag('form li.required')
402
+ expect(output_buffer.to_str).to have_tag('form li.optional')
403
+
404
+ Formtastic::FormBuilder.all_fields_required_by_default = true
405
+ end
406
+
407
+ end
408
+
409
+ end
410
+
411
+ end
412
+
413
+ describe ':as option' do
414
+
415
+ describe 'when not provided' do
416
+
417
+ it 'should default to a string for forms without objects unless column is password' do
418
+ concat(semantic_form_for(:project, :url => 'http://test.host') do |builder|
419
+ concat(builder.input(:anything))
420
+ end)
421
+ expect(output_buffer.to_str).to have_tag('form li.string')
422
+ end
423
+
424
+ it 'should default to password for forms without objects if column is password' do
425
+ concat(semantic_form_for(:project, :url => 'http://test.host') do |builder|
426
+ concat(builder.input(:password))
427
+ concat(builder.input(:password_confirmation))
428
+ concat(builder.input(:confirm_password))
429
+ end)
430
+ expect(output_buffer.to_str).to have_tag('form li.password', :count => 3)
431
+ end
432
+
433
+ it 'should default to a string for methods on objects that don\'t respond to "column_for_attribute"' do
434
+ allow(@new_post).to receive(:method_without_a_database_column)
435
+ allow(@new_post).to receive(:column_for_attribute).and_return(nil)
436
+ expect(default_input_type(nil, :method_without_a_database_column)).to eq(:string)
437
+ end
438
+
439
+ it 'should default to :password for methods that don\'t have a column in the database but "password" is in the method name' do
440
+ allow(@new_post).to receive(:password_method_without_a_database_column)
441
+ allow(@new_post).to receive(:column_for_attribute).and_return(nil)
442
+ expect(default_input_type(nil, :password_method_without_a_database_column)).to eq(:password)
443
+ end
444
+
445
+ it 'should default to :password for methods on objects that don\'t respond to "column_for_attribute" but "password" is in the method name' do
446
+ allow(@new_post).to receive(:password_method_without_a_database_column)
447
+ allow(@new_post).to receive(:column_for_attribute).and_return(nil)
448
+ expect(default_input_type(nil, :password_method_without_a_database_column)).to eq(:password)
449
+ end
450
+
451
+ it 'should default to :number for "integer" column with name ending in "_id"' do
452
+ allow(@new_post).to receive(:aws_instance_id)
453
+ allow(@new_post).to receive(:column_for_attribute).with(:aws_instance_id).and_return(double('column', :type => :integer))
454
+ expect(default_input_type(:integer, :aws_instance_id)).to eq(:number)
455
+ end
456
+
457
+ it 'should default to :select for associations' do
458
+ allow(@new_post.class).to receive(:reflect_on_association).with(:user_id).and_return(double('ActiveRecord::Reflection::AssociationReflection'))
459
+ allow(@new_post.class).to receive(:reflect_on_association).with(:section_id).and_return(double('ActiveRecord::Reflection::AssociationReflection'))
460
+ expect(default_input_type(:integer, :user_id)).to eq(:select)
461
+ expect(default_input_type(:integer, :section_id)).to eq(:select)
462
+ end
463
+
464
+ it 'should default to :select for enum' do
465
+ statuses = ActiveSupport::HashWithIndifferentAccess.new("active"=>0, "inactive"=>1)
466
+ allow(@new_post.class).to receive(:statuses) { statuses }
467
+ allow(@new_post).to receive(:defined_enums) { {"status" => statuses } }
468
+
469
+ expect(default_input_type(:integer, :status)).to eq(:select)
470
+ end
471
+
472
+ it 'should default to :password for :string column types with "password" in the method name' do
473
+ expect(default_input_type(:string, :password)).to eq(:password)
474
+ expect(default_input_type(:string, :hashed_password)).to eq(:password)
475
+ expect(default_input_type(:string, :password_hash)).to eq(:password)
476
+ end
477
+
478
+ it 'should default to :text for :text column types' do
479
+ expect(default_input_type(:text)).to eq(:text)
480
+ end
481
+
482
+ it 'should default to :date_select for :date column types' do
483
+ expect(default_input_type(:date)).to eq(:date_select)
484
+ end
485
+
486
+ it 'should default to :text for :hstore, :json and :jsonb column types' do
487
+ expect(default_input_type(:hstore)).to eq(:text)
488
+ expect(default_input_type(:json)).to eq(:text)
489
+ expect(default_input_type(:jsonb)).to eq(:text)
490
+ end
491
+
492
+ it 'should default to :datetime_select for :datetime and :timestamp column types' do
493
+ expect(default_input_type(:datetime)).to eq(:datetime_select)
494
+ expect(default_input_type(:timestamp)).to eq(:datetime_select)
495
+ end
496
+
497
+ it 'should default to :time_select for :time column types' do
498
+ expect(default_input_type(:time)).to eq(:time_select)
499
+ end
500
+
501
+ it 'should default to :boolean for :boolean column types' do
502
+ expect(default_input_type(:boolean)).to eq(:boolean)
503
+ end
504
+
505
+ it 'should default to :string for :string column types' do
506
+ expect(default_input_type(:string)).to eq(:string)
507
+ end
508
+
509
+ it 'should default to :string for :citext column types' do
510
+ expect(default_input_type(:citext)).to eq(:string)
511
+ end
512
+
513
+ it 'should default to :string for :inet column types' do
514
+ expect(default_input_type(:inet)).to eq(:string)
515
+ end
516
+
517
+ it 'should default to :number for :integer, :float and :decimal column types' do
518
+ expect(default_input_type(:integer)).to eq(:number)
519
+ expect(default_input_type(:float)).to eq(:number)
520
+ expect(default_input_type(:decimal)).to eq(:number)
521
+ end
522
+
523
+ it 'should default to :country for :string columns named country' do
524
+ expect(default_input_type(:string, :country)).to eq(:country)
525
+ end
526
+
527
+ it 'should default to :email for :string columns matching email' do
528
+ expect(default_input_type(:string, :email)).to eq(:email)
529
+ expect(default_input_type(:string, :customer_email)).to eq(:email)
530
+ expect(default_input_type(:string, :email_work)).to eq(:email)
531
+ end
532
+
533
+ it 'should default to :url for :string columns named url or website' do
534
+ expect(default_input_type(:string, :url)).to eq(:url)
535
+ expect(default_input_type(:string, :website)).to eq(:url)
536
+ expect(default_input_type(:string, :my_url)).to eq(:url)
537
+ expect(default_input_type(:string, :hurl)).not_to eq(:url)
538
+ end
539
+
540
+ it 'should default to :phone for :string columns named phone or fax' do
541
+ expect(default_input_type(:string, :phone)).to eq(:phone)
542
+ expect(default_input_type(:string, :fax)).to eq(:phone)
543
+ end
544
+
545
+ it 'should default to :search for :string columns named search' do
546
+ expect(default_input_type(:string, :search)).to eq(:search)
547
+ end
548
+
549
+ it 'should default to :color for :string columns matching color' do
550
+ expect(default_input_type(:string, :color)).to eq(:color)
551
+ expect(default_input_type(:string, :user_color)).to eq(:color)
552
+ expect(default_input_type(:string, :color_for_image)).to eq(:color)
553
+ end
554
+
555
+ describe 'defaulting to file column' do
556
+ Formtastic::FormBuilder.file_methods.each do |method|
557
+ it "should default to :file for attributes that respond to ##{method}" do
558
+ column = double('column')
559
+
560
+ Formtastic::FormBuilder.file_methods.each do |test|
561
+ ### TODO: Check if this is ok
562
+ allow(column).to receive(method).with(test).and_return(method == test)
563
+ end
564
+
565
+ expect(@new_post).to receive(method).and_return(column)
566
+
567
+ semantic_form_for(@new_post) do |builder|
568
+ expect(builder.send(:default_input_type, method)).to eq(:file)
569
+ end
570
+ end
571
+ end
572
+
573
+ end
574
+ end
575
+
576
+ it 'should call the corresponding input class with .to_html' do
577
+ [:select, :time_zone, :radio, :date_select, :datetime_select, :time_select, :boolean, :check_boxes, :hidden, :string, :password, :number, :text, :file].each do |input_style|
578
+ allow(@new_post).to receive(:generic_column_name)
579
+ allow(@new_post).to receive(:column_for_attribute).and_return(double('column', :type => :string, :limit => 255))
580
+ semantic_form_for(@new_post) do |builder|
581
+ input_instance = double('Input instance')
582
+ input_class = "#{input_style.to_s}_input".classify
583
+ input_constant = "Formtastic::Inputs::#{input_class}".constantize
584
+
585
+ expect(input_constant).to receive(:new).and_return(input_instance)
586
+ expect(input_instance).to receive(:to_html).and_return("some HTML")
587
+
588
+ concat(builder.input(:generic_column_name, :as => input_style))
589
+ end
590
+ end
591
+ end
592
+
593
+ end
594
+
595
+ describe ':label option' do
596
+
597
+ describe 'when provided' do
598
+ it 'should be passed down to the label tag' do
599
+ concat(semantic_form_for(@new_post) do |builder|
600
+ concat(builder.input(:title, :label => "Kustom"))
601
+ end)
602
+ expect(output_buffer.to_str).to have_tag("form li label", :text => /Kustom/)
603
+ end
604
+
605
+ it 'should not generate a label if false' do
606
+ concat(semantic_form_for(@new_post) do |builder|
607
+ concat(builder.input(:title, :label => false))
608
+ end)
609
+ expect(output_buffer.to_str).not_to have_tag("form li label")
610
+ end
611
+
612
+ it 'should be dupped if frozen' do
613
+ concat(semantic_form_for(@new_post) do |builder|
614
+ concat(builder.input(:title, :label => "Kustom".freeze))
615
+ end)
616
+ expect(output_buffer.to_str).to have_tag("form li label", :text => /Kustom/)
617
+ end
618
+ end
619
+
620
+ describe 'when not provided' do
621
+ describe 'when localized label is provided' do
622
+ describe 'and object is given' do
623
+ describe 'and label_str_method not :humanize' do
624
+ it 'should render a label with localized text and not apply the label_str_method' do
625
+ with_config :label_str_method, :reverse do
626
+ @localized_label_text = 'Localized title'
627
+ allow(@new_post).to receive(:meta_description)
628
+ ::I18n.backend.store_translations :en,
629
+ :formtastic => {
630
+ :labels => {
631
+ :meta_description => @localized_label_text
632
+ }
633
+ }
634
+
635
+ concat(semantic_form_for(@new_post) do |builder|
636
+ concat(builder.input(:meta_description))
637
+ end)
638
+ expect(output_buffer.to_str).to have_tag('form li label', :text => /Localized title/)
639
+ end
640
+ end
641
+ end
642
+ end
643
+ end
644
+
645
+ describe 'when localized label is NOT provided' do
646
+ describe 'and object is not given' do
647
+ it 'should default the humanized method name, passing it down to the label tag' do
648
+ ::I18n.backend.store_translations :en, :formtastic => {}
649
+ with_config :label_str_method, :humanize do
650
+ concat(semantic_form_for(:project, :url => 'http://test.host') do |builder|
651
+ concat(builder.input(:meta_description))
652
+ end)
653
+ expect(output_buffer.to_str).to have_tag("form li label", :text => /#{'meta_description'.humanize}/)
654
+ end
655
+ end
656
+ end
657
+
658
+ describe 'and object is given' do
659
+ it 'should delegate the label logic to class human attribute name and pass it down to the label tag' do
660
+ allow(@new_post).to receive(:meta_description) # a two word method name
661
+ expect(@new_post.class).to receive(:human_attribute_name).with('meta_description').and_return('meta_description'.humanize)
662
+
663
+ concat(semantic_form_for(@new_post) do |builder|
664
+ concat(builder.input(:meta_description))
665
+ end)
666
+ expect(output_buffer.to_str).to have_tag("form li label", :text => /#{'meta_description'.humanize}/)
667
+ end
668
+ end
669
+
670
+ describe 'and object is given with label_str_method set to :capitalize' do
671
+ it 'should capitalize method name, passing it down to the label tag' do
672
+ with_config :label_str_method, :capitalize do
673
+ allow(@new_post).to receive(:meta_description)
674
+
675
+ concat(semantic_form_for(@new_post) do |builder|
676
+ concat(builder.input(:meta_description))
677
+ end)
678
+ expect(output_buffer.to_str).to have_tag("form li label", :text => /#{'meta_description'.capitalize}/)
679
+ end
680
+ end
681
+ end
682
+ end
683
+
684
+ describe 'when localized label is provided' do
685
+ before do
686
+ @localized_label_text = 'Localized title'
687
+ @default_localized_label_text = 'Default localized title'
688
+ ::I18n.backend.store_translations :en,
689
+ :formtastic => {
690
+ :labels => {
691
+ :title => @default_localized_label_text,
692
+ :published => @default_localized_label_text,
693
+ :post => {
694
+ :title => @localized_label_text,
695
+ :published => @default_localized_label_text
696
+ }
697
+ }
698
+ }
699
+ end
700
+
701
+ it 'should render a label with localized label (I18n)' do
702
+ with_config :i18n_lookups_by_default, false do
703
+ concat(semantic_form_for(@new_post) do |builder|
704
+ concat(builder.input(:title, :label => true))
705
+ concat(builder.input(:published, :as => :boolean, :label => true))
706
+ end)
707
+ expect(output_buffer.to_str).to have_tag('form li label', :text => Regexp.new('^' + @localized_label_text))
708
+ end
709
+ end
710
+
711
+ it 'should render a hint paragraph containing an optional localized label (I18n) if first is not set' do
712
+ with_config :i18n_lookups_by_default, false do
713
+ ::I18n.backend.store_translations :en,
714
+ :formtastic => {
715
+ :labels => {
716
+ :post => {
717
+ :title => nil,
718
+ :published => nil
719
+ }
720
+ }
721
+ }
722
+ concat(semantic_form_for(@new_post) do |builder|
723
+ concat(builder.input(:title, :label => true))
724
+ concat(builder.input(:published, :as => :boolean, :label => true))
725
+ end)
726
+ expect(output_buffer.to_str).to have_tag('form li label', :text => Regexp.new('^' + @default_localized_label_text))
727
+ end
728
+ end
729
+ end
730
+ end
731
+
732
+ end
733
+
734
+ describe ':hint option' do
735
+
736
+ describe 'when provided' do
737
+
738
+ after do
739
+ Formtastic::FormBuilder.default_hint_class = "inline-hints"
740
+ end
741
+
742
+ it 'should be passed down to the paragraph tag' do
743
+ hint_text = "this is the title of the post"
744
+ concat(semantic_form_for(@new_post) do |builder|
745
+ concat(builder.input(:title, :hint => hint_text))
746
+ end)
747
+ expect(output_buffer.to_str).to have_tag("form li p.inline-hints", :text => hint_text)
748
+ end
749
+
750
+ it 'should have a custom hint class defaulted for all forms' do
751
+ hint_text = "this is the title of the post"
752
+ Formtastic::FormBuilder.default_hint_class = "custom-hint-class"
753
+ concat(semantic_form_for(@new_post) do |builder|
754
+ concat(builder.input(:title, :hint => hint_text))
755
+ end)
756
+ expect(output_buffer.to_str).to have_tag("form li p.custom-hint-class", :text => hint_text)
757
+ end
758
+ end
759
+
760
+ describe 'when not provided' do
761
+ describe 'when localized hint (I18n) is provided' do
762
+ before do
763
+ @localized_hint_text = "This is the localized hint."
764
+ @default_localized_hint_text = "This is the default localized hint."
765
+ ::I18n.backend.store_translations :en,
766
+ :formtastic => {
767
+ :hints => {
768
+ :title => @default_localized_hint_text,
769
+ }
770
+ }
771
+ end
772
+
773
+ after do
774
+ ::I18n.backend.reload!
775
+ end
776
+
777
+ describe 'when provided value (hint value) is set to TRUE' do
778
+ it 'should render a hint paragraph containing a localized hint (I18n)' do
779
+ with_config :i18n_lookups_by_default, false do
780
+ ::I18n.backend.store_translations :en,
781
+ :formtastic => {
782
+ :hints => {
783
+ :post => {
784
+ :title => @localized_hint_text
785
+ }
786
+ }
787
+ }
788
+ concat(semantic_form_for(@new_post) do |builder|
789
+ concat(builder.input(:title, :hint => true))
790
+ end)
791
+ expect(output_buffer.to_str).to have_tag('form li p.inline-hints', :text => @localized_hint_text)
792
+ end
793
+ end
794
+
795
+ it 'should render a hint paragraph containing an optional localized hint (I18n) if first is not set' do
796
+ with_config :i18n_lookups_by_default, false do
797
+ concat(semantic_form_for(@new_post) do |builder|
798
+ concat(builder.input(:title, :hint => true))
799
+ end)
800
+ expect(output_buffer.to_str).to have_tag('form li p.inline-hints', :text => @default_localized_hint_text)
801
+ end
802
+ end
803
+ end
804
+
805
+ describe 'when provided value (label value) is set to FALSE' do
806
+ it 'should not render a hint paragraph' do
807
+ with_config :i18n_lookups_by_default, false do
808
+ concat(semantic_form_for(@new_post) do |builder|
809
+ concat(builder.input(:title, :hint => false))
810
+ end)
811
+ expect(output_buffer.to_str).not_to have_tag('form li p.inline-hints', :text => @localized_hint_text)
812
+ end
813
+ end
814
+ end
815
+ end
816
+
817
+ describe 'when localized hint (I18n) is a model with attribute hints' do
818
+ it "should see the provided hash as a blank entry" do
819
+ with_config :i18n_lookups_by_default, false do
820
+ ::I18n.backend.store_translations :en,
821
+ :formtastic => {
822
+ :hints => {
823
+ :title => { # movie title
824
+ :summary => @localized_hint_text # summary of movie
825
+ }
826
+ }
827
+ }
828
+ semantic_form_for(@new_post) do |builder|
829
+ concat(builder.input(:title, :hint => true))
830
+ end
831
+ expect(output_buffer.to_str).not_to have_tag('form li p.inline-hints', :text => @localized_hint_text)
832
+ end
833
+ end
834
+ end
835
+
836
+ describe 'when localized hint (I18n) is not provided' do
837
+ it 'should not render a hint paragraph' do
838
+ with_config :i18n_lookups_by_default, false do
839
+ concat(semantic_form_for(@new_post) do |builder|
840
+ concat(builder.input(:title))
841
+ end)
842
+ expect(output_buffer.to_str).not_to have_tag('form li p.inline-hints')
843
+ end
844
+ end
845
+ end
846
+ end
847
+
848
+ end
849
+
850
+ describe ':wrapper_html option' do
851
+
852
+ describe 'when provided' do
853
+ it 'should be passed down to the li tag' do
854
+ concat(semantic_form_for(@new_post) do |builder|
855
+ concat(builder.input(:title, :wrapper_html => {:id => :another_id}))
856
+ end)
857
+ expect(output_buffer.to_str).to have_tag("form li#another_id")
858
+ end
859
+
860
+ it 'should append given classes to li default classes' do
861
+ concat(semantic_form_for(@new_post) do |builder|
862
+ concat(builder.input(:title, :wrapper_html => {:class => :another_class}, :required => true))
863
+ end)
864
+ expect(output_buffer.to_str).to have_tag("form li.string")
865
+ expect(output_buffer.to_str).to have_tag("form li.required")
866
+ expect(output_buffer.to_str).to have_tag("form li.another_class")
867
+ end
868
+
869
+ it 'should allow classes to be an array' do
870
+ concat(semantic_form_for(@new_post) do |builder|
871
+ concat(builder.input(:title, :wrapper_html => {:class => [ :my_class, :another_class ]}))
872
+ end)
873
+ expect(output_buffer.to_str).to have_tag("form li.string")
874
+ expect(output_buffer.to_str).to have_tag("form li.my_class")
875
+ expect(output_buffer.to_str).to have_tag("form li.another_class")
876
+ end
877
+
878
+ describe 'when nil' do
879
+ it 'should not put an id attribute on the div tag' do
880
+ concat(semantic_form_for(@new_post) do |builder|
881
+ concat(builder.input(:title, :wrapper_html => {:id => nil}))
882
+ end)
883
+ expect(output_buffer.to_str).to have_tag('form li:not([id])')
884
+ end
885
+ end
886
+ end
887
+
888
+ describe 'when not provided' do
889
+ it 'should use default id and class' do
890
+ concat(semantic_form_for(@new_post) do |builder|
891
+ concat(builder.input(:title))
892
+ end)
893
+ expect(output_buffer.to_str).to have_tag("form li#post_title_input")
894
+ expect(output_buffer.to_str).to have_tag("form li.string")
895
+ end
896
+ end
897
+
898
+ end
899
+
900
+ describe ':collection option' do
901
+
902
+ it "should be required on polymorphic associations" do
903
+ allow(@new_post).to receive(:commentable)
904
+ allow(@new_post.class).to receive(:reflections).and_return({
905
+ :commentable => double('macro_reflection', :options => { :polymorphic => true }, :macro => :belongs_to)
906
+ })
907
+ allow(@new_post).to receive(:column_for_attribute).with(:commentable).and_return(
908
+ double('column', :type => :integer)
909
+ )
910
+ allow(@new_post.class).to receive(:reflect_on_association).with(:commentable).and_return(
911
+ double('reflection', :macro => :belongs_to, :options => { :polymorphic => true })
912
+ )
913
+ expect {
914
+ concat(semantic_form_for(@new_post) do |builder|
915
+ concat(builder.inputs do
916
+ concat(builder.input :commentable)
917
+ end)
918
+ end)
919
+ }.to raise_error(Formtastic::PolymorphicInputWithoutCollectionError)
920
+ end
921
+
922
+ end
923
+
924
+ end
925
+
926
+ describe 'options re-use' do
927
+
928
+ it 'should retain :as option when re-using the same options hash' do
929
+ my_options = { :as => :string }
930
+ output = ''
931
+
932
+ concat(semantic_form_for(@new_post) do |builder|
933
+ concat(builder.input(:title, my_options))
934
+ concat(builder.input(:publish_at, my_options))
935
+ end)
936
+ expect(output_buffer.to_str).to have_tag 'li.string', :count => 2
937
+ end
938
+ end
939
+
940
+ describe 'instantiating an input class' do
941
+ context 'when a class does not exist' do
942
+ it "should raise an error" do
943
+ expect {
944
+ concat(semantic_form_for(@new_post) do |builder|
945
+ builder.input(:title, :as => :non_existant)
946
+ end)
947
+ }.to raise_error(Formtastic::UnknownInputError)
948
+ end
949
+ end
950
+
951
+ context 'when a customized top-level class does not exist' do
952
+
953
+ it 'should instantiate the Formtastic input' do
954
+ input = double('input', :to_html => 'some HTML')
955
+ expect(Formtastic::Inputs::StringInput).to receive(:new).and_return(input)
956
+ concat(semantic_form_for(@new_post) do |builder|
957
+ builder.input(:title, :as => :string)
958
+ end)
959
+ end
960
+
961
+ end
962
+
963
+ describe 'when a top-level input class exists' do
964
+ it "should instantiate the top-level input instead of the Formtastic one" do
965
+ class ::StringInput < Formtastic::Inputs::StringInput
966
+ end
967
+
968
+ input = double('input', :to_html => 'some HTML')
969
+ expect(Formtastic::Inputs::StringInput).not_to receive(:new)
970
+ expect(::StringInput).to receive(:new).and_return(input)
971
+
972
+ concat(semantic_form_for(@new_post) do |builder|
973
+ builder.input(:title, :as => :string)
974
+ end)
975
+ end
976
+ end
977
+
978
+
979
+ end
6
980
  end