formtastic-bootstrap 1.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 (72) hide show
  1. data/.document +5 -0
  2. data/.rspec +1 -0
  3. data/Gemfile +12 -0
  4. data/Gemfile.lock +123 -0
  5. data/LICENSE.txt +20 -0
  6. data/README.md +159 -0
  7. data/Rakefile +49 -0
  8. data/VERSION +1 -0
  9. data/formtastic-bootstrap.gemspec +128 -0
  10. data/lib/action_view/helpers/text_field_date_helper.rb +166 -0
  11. data/lib/formtastic-bootstrap.rb +5 -0
  12. data/lib/formtastic-bootstrap/form_builder.rb +38 -0
  13. data/lib/formtastic-bootstrap/helpers.rb +19 -0
  14. data/lib/formtastic-bootstrap/helpers/buttons_helper.rb +47 -0
  15. data/lib/formtastic-bootstrap/helpers/fieldset_wrapper.rb +37 -0
  16. data/lib/formtastic-bootstrap/helpers/input_helper.rb +12 -0
  17. data/lib/formtastic-bootstrap/helpers/inputs_helper.rb +36 -0
  18. data/lib/formtastic-bootstrap/inputs.rb +28 -0
  19. data/lib/formtastic-bootstrap/inputs/base.rb +22 -0
  20. data/lib/formtastic-bootstrap/inputs/base/choices.rb +49 -0
  21. data/lib/formtastic-bootstrap/inputs/base/errors.rb +48 -0
  22. data/lib/formtastic-bootstrap/inputs/base/hints.rb +27 -0
  23. data/lib/formtastic-bootstrap/inputs/base/html.rb +21 -0
  24. data/lib/formtastic-bootstrap/inputs/base/labelling.rb +18 -0
  25. data/lib/formtastic-bootstrap/inputs/base/stringish.rb +18 -0
  26. data/lib/formtastic-bootstrap/inputs/base/timeish.rb +35 -0
  27. data/lib/formtastic-bootstrap/inputs/base/wrapping.rb +56 -0
  28. data/lib/formtastic-bootstrap/inputs/boolean_input.rb +33 -0
  29. data/lib/formtastic-bootstrap/inputs/check_boxes_input.rb +35 -0
  30. data/lib/formtastic-bootstrap/inputs/date_input.rb +16 -0
  31. data/lib/formtastic-bootstrap/inputs/datetime_input.rb +19 -0
  32. data/lib/formtastic-bootstrap/inputs/email_input.rb +15 -0
  33. data/lib/formtastic-bootstrap/inputs/file_input.rb +14 -0
  34. data/lib/formtastic-bootstrap/inputs/hidden_input.rb +12 -0
  35. data/lib/formtastic-bootstrap/inputs/number_input.rb +15 -0
  36. data/lib/formtastic-bootstrap/inputs/password_input.rb +15 -0
  37. data/lib/formtastic-bootstrap/inputs/phone_input.rb +15 -0
  38. data/lib/formtastic-bootstrap/inputs/radio_input.rb +32 -0
  39. data/lib/formtastic-bootstrap/inputs/range_input.rb +15 -0
  40. data/lib/formtastic-bootstrap/inputs/search_input.rb +15 -0
  41. data/lib/formtastic-bootstrap/inputs/select_input.rb +14 -0
  42. data/lib/formtastic-bootstrap/inputs/string_input.rb +15 -0
  43. data/lib/formtastic-bootstrap/inputs/text_input.rb +14 -0
  44. data/lib/formtastic-bootstrap/inputs/time_input.rb +16 -0
  45. data/lib/formtastic-bootstrap/inputs/url_input.rb +14 -0
  46. data/spec/builder/errors_spec.rb +214 -0
  47. data/spec/builder/semantic_fields_for_spec.rb +130 -0
  48. data/spec/helpers/input_helper_spec.rb +956 -0
  49. data/spec/helpers/inputs_helper_spec.rb +577 -0
  50. data/spec/inputs/boolean_input_spec.rb +193 -0
  51. data/spec/inputs/check_boxes_input_spec.rb +439 -0
  52. data/spec/inputs/date_input_spec.rb +147 -0
  53. data/spec/inputs/datetime_input_spec.rb +101 -0
  54. data/spec/inputs/email_input_spec.rb +59 -0
  55. data/spec/inputs/file_input_spec.rb +63 -0
  56. data/spec/inputs/hidden_input_spec.rb +122 -0
  57. data/spec/inputs/number_input_spec.rb +787 -0
  58. data/spec/inputs/password_input_spec.rb +73 -0
  59. data/spec/inputs/phone_input_spec.rb +59 -0
  60. data/spec/inputs/radio_input_spec.rb +240 -0
  61. data/spec/inputs/range_input_spec.rb +479 -0
  62. data/spec/inputs/search_input_spec.rb +59 -0
  63. data/spec/inputs/select_input_spec.rb +567 -0
  64. data/spec/inputs/string_input_spec.rb +182 -0
  65. data/spec/inputs/text_input_spec.rb +163 -0
  66. data/spec/inputs/time_input_spec.rb +206 -0
  67. data/spec/inputs/url_input_spec.rb +59 -0
  68. data/spec/spec_helper.rb +24 -0
  69. data/spec/support/custom_macros.rb +704 -0
  70. data/spec/support/depracation.rb +6 -0
  71. data/spec/support/formtastic_spec_helper.rb +382 -0
  72. metadata +204 -0
@@ -0,0 +1,59 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+ describe 'search input' do
5
+
6
+ include FormtasticSpecHelper
7
+
8
+ before do
9
+ @output_buffer = ''
10
+ mock_everything
11
+ Formtastic::Helpers::FormHelper.builder = FormtasticBootstrap::FormBuilder
12
+ end
13
+
14
+ describe "when object is provided" do
15
+ before do
16
+ concat(semantic_form_for(@new_post) do |builder|
17
+ concat(builder.input(:search))
18
+ end)
19
+ end
20
+
21
+ it_should_have_input_wrapper_with_class(:search)
22
+ it_should_have_input_wrapper_with_class(:clearfix)
23
+ it_should_have_input_wrapper_with_class(:stringish)
24
+ it_should_have_input_class_in_the_right_place
25
+ it_should_have_input_wrapper_with_id("post_search_input")
26
+ it_should_have_label_with_text(/Search/)
27
+ it_should_have_label_for("post_search")
28
+ it_should_have_input_with_id("post_search")
29
+ it_should_have_input_with_type(:search)
30
+ it_should_have_input_with_name("post[search]")
31
+
32
+ end
33
+
34
+ describe "when namespace is provided" do
35
+
36
+ before do
37
+ concat(semantic_form_for(@new_post, :namespace => "context2") do |builder|
38
+ concat(builder.input(:search))
39
+ end)
40
+ end
41
+
42
+ it_should_have_input_wrapper_with_id("context2_post_search_input")
43
+ it_should_have_label_and_input_with_id("context2_post_search")
44
+
45
+ end
46
+
47
+ describe "when required" do
48
+ it "should add the required attribute to the input's html options" do
49
+ with_config :use_required_attribute, true do
50
+ concat(semantic_form_for(@new_post) do |builder|
51
+ concat(builder.input(:title, :as => :search, :required => true))
52
+ end)
53
+ output_buffer.should have_tag("input[@required]")
54
+ end
55
+ end
56
+ end
57
+
58
+ end
59
+
@@ -0,0 +1,567 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+ describe 'select input' do
5
+
6
+ include FormtasticSpecHelper
7
+
8
+ before do
9
+ @output_buffer = ''
10
+ mock_everything
11
+ Formtastic::Helpers::FormHelper.builder = FormtasticBootstrap::FormBuilder
12
+ end
13
+
14
+ describe 'explicit values' do
15
+ describe 'using an array of values' do
16
+ before do
17
+ @array_with_values = ["Title A", "Title B", "Title C"]
18
+ @array_with_keys_and_values = [["Title D", 1], ["Title E", 2], ["Title F", 3]]
19
+ concat(semantic_form_for(@new_post) do |builder|
20
+ concat(builder.input(:title, :as => :select, :collection => @array_with_values))
21
+ concat(builder.input(:title, :as => :select, :collection => @array_with_keys_and_values))
22
+ end)
23
+ end
24
+
25
+ it 'should have a option for each key and/or value' do
26
+ @array_with_values.each do |v|
27
+ output_buffer.should have_tag("form div.clearfix div.input select option[@value='#{v}']", /^#{v}$/)
28
+ end
29
+ @array_with_keys_and_values.each do |v|
30
+ output_buffer.should have_tag("form div.clearfix div.input select option[@value='#{v.second}']", /^#{v.first}$/)
31
+ end
32
+ end
33
+ end
34
+
35
+ describe "using a related model without reflection's options (Mongoid Document)" do
36
+ before do
37
+ @new_post.stub!(:mongoid_reviewer)
38
+ concat(semantic_form_for(@new_post) do |builder|
39
+ concat(builder.input(:mongoid_reviewer, :as => :select))
40
+ end)
41
+ end
42
+
43
+ it 'should draw select options' do
44
+ output_buffer.should have_tag('form div.clearfix div.input select')
45
+ output_buffer.should have_tag('form div.clearfix div.input select#post_reviewer_id')
46
+ output_buffer.should_not have_tag('form div.clearfix div.input select#post_mongoid_reviewer_id')
47
+ end
48
+ end
49
+
50
+ describe 'using a range' do
51
+ before do
52
+ @range_with_values = 1..5
53
+ concat(semantic_form_for(@new_post) do |builder|
54
+ concat(builder.input(:title, :as => :select, :collection => @range_with_values))
55
+ end)
56
+ end
57
+
58
+ it 'should have an option for each value' do
59
+ @range_with_values.each do |v|
60
+ output_buffer.should have_tag("form div.clearfix div.input select option[@value='#{v}']", /^#{v}$/)
61
+ end
62
+ end
63
+ end
64
+
65
+ describe 'using a string' do
66
+ before do
67
+ @string ="<option value='0'>0</option><option value='1'>1</option>"
68
+ concat(semantic_form_for(@new_post) do |builder|
69
+ concat(builder.input(:title, :as => :select, :collection => @string))
70
+ end)
71
+ end
72
+
73
+ it 'should render select options using provided HTML string' do
74
+ 2.times do |v|
75
+ output_buffer.should have_tag("form div.clearfix div.input select option[@value='#{v}']", /^#{v}$/)
76
+ end
77
+ end
78
+ end
79
+ end
80
+
81
+ describe 'for boolean columns' do
82
+ # describe 'default formtastic locale' do
83
+ # before do
84
+ # # Note: Works, but something like Formtastic.root.join(...) would probably be "safer".
85
+ # ::I18n.load_path = [File.join(File.dirname(__FILE__), *%w[.. .. lib locale en.yml])]
86
+ # ::I18n.backend.send(:init_translations)
87
+ #
88
+ # concat(semantic_form_for(@new_post) do |builder|
89
+ # concat(builder.input(:published, :as => :select))
90
+ # end)
91
+ # end
92
+ #
93
+ # after do
94
+ # ::I18n.backend.store_translations :en, {}
95
+ # end
96
+ #
97
+ # it 'should render a select with at least options: true/false' do
98
+ # output_buffer.should have_tag("form div.clearfix div.input select option[@value='true']", /^Yes$/)
99
+ # output_buffer.should have_tag("form div.clearfix div.input select option[@value='false']", /^No$/)
100
+ # end
101
+ # end
102
+
103
+ describe 'custom locale' do
104
+ before do
105
+ @boolean_select_labels = {:yes => 'Yep', :no => 'Nope'}
106
+ ::I18n.backend.store_translations :en, :formtastic => @boolean_select_labels
107
+
108
+ concat(semantic_form_for(@new_post) do |builder|
109
+ concat(builder.input(:published, :as => :select))
110
+ end)
111
+ end
112
+
113
+ after do
114
+ ::I18n.backend.store_translations :en, {}
115
+ end
116
+
117
+ it 'should render a select with at least options: true/false' do
118
+ output_buffer.should have_tag("form div.clearfix div.input select option[@value='true']", /#{@boolean_select_labels[:yes]}/)
119
+ output_buffer.should have_tag("form div.clearfix div.input select option[@value='false']", /#{@boolean_select_labels[:no]}/)
120
+ end
121
+ end
122
+ end
123
+
124
+ describe 'for a belongs_to association' do
125
+ before do
126
+ concat(semantic_form_for(@new_post) do |builder|
127
+ concat(builder.input(:author, :as => :select))
128
+ concat(builder.input(:reviewer, :as => :select))
129
+ end)
130
+ end
131
+
132
+ it_should_have_input_wrapper_with_class("select")
133
+ it_should_have_input_wrapper_with_class(:clearfix)
134
+ it_should_have_input_class_in_the_right_place
135
+ it_should_have_input_wrapper_with_id("post_author_input")
136
+ it_should_have_label_with_text(/Author/)
137
+ it_should_have_label_for('post_author_id')
138
+ it_should_apply_error_logic_for_input_type(:select)
139
+ it_should_call_find_on_association_class_when_no_collection_is_provided(:select)
140
+ it_should_use_the_collection_when_provided(:select, 'option')
141
+
142
+ it 'should have a select inside the wrapper' do
143
+ output_buffer.should have_tag('form div.clearfix div.input select')
144
+ output_buffer.should have_tag('form div.clearfix div.input select#post_author_id')
145
+ output_buffer.should have_tag('form div.clearfix div.input select#post_reviewer_id')
146
+ end
147
+
148
+ it 'should have a valid name' do
149
+ output_buffer.should have_tag("form div.clearfix div.input select[@name='post[author_id]']")
150
+ output_buffer.should_not have_tag("form div.clearfix div.input select[@name='post[author_id][]']")
151
+ output_buffer.should_not have_tag("form div.clearfix div.input select[@name='post[reviewer_id][]']")
152
+ end
153
+
154
+ it 'should not create a multi-select' do
155
+ output_buffer.should_not have_tag('form div.clearfix div.input select[@multiple]')
156
+ end
157
+
158
+ it 'should create a select without size' do
159
+ output_buffer.should_not have_tag('form div.clearfix div.input select[@size]')
160
+ end
161
+
162
+ it 'should have a blank option' do
163
+ output_buffer.should have_tag("form div.clearfix div.input select option[@value='']")
164
+ end
165
+
166
+ it 'should have a select option for each Author' do
167
+ output_buffer.should have_tag("form div.clearfix div.input select[@name='post[author_id]'] option", :count => ::Author.all.size + 1)
168
+ ::Author.all.each do |author|
169
+ output_buffer.should have_tag("form div.clearfix div.input select option[@value='#{author.id}']", /#{author.to_label}/)
170
+ end
171
+ end
172
+
173
+ it 'should have one option with a "selected" attribute (bob)' do
174
+ output_buffer.should have_tag("form div.clearfix div.input select[@name='post[author_id]'] option[@selected]", :count => 1)
175
+ end
176
+
177
+ it 'should not singularize the association name' do
178
+ @new_post.stub!(:author_status).and_return(@bob)
179
+ @new_post.stub!(:author_status_id).and_return(@bob.id)
180
+ @new_post.stub!(:column_for_attribute).and_return(mock('column', :type => :integer, :limit => 255))
181
+
182
+ concat(semantic_form_for(@new_post) do |builder|
183
+ concat(builder.input(:author_status, :as => :select))
184
+ end)
185
+
186
+ output_buffer.should have_tag('form div.clearfix div.input select#post_author_status_id')
187
+ end
188
+ end
189
+
190
+ describe "for a belongs_to association with :group_by => :author" do
191
+ it "should call author.posts" do
192
+ ::Author.stub!(:reflect_on_all_associations).and_return { |macro| macro == :has_many ? [mock('reflection', :klass => Post, :name => :posts)] : []}
193
+
194
+ [@freds_post].each { |post| post.stub!(:to_label).and_return("Post - #{post.id}") }
195
+ @fred.should_receive(:posts)
196
+
197
+ concat(semantic_form_for(@new_post) do |builder|
198
+ concat(builder.input(:main_post, :as => :select, :group_by => :author ) )
199
+ end)
200
+ end
201
+ end
202
+
203
+ describe "for a belongs_to association with :conditions" do
204
+ before do
205
+ ::Post.stub!(:reflect_on_association).with(:author).and_return do
206
+ mock = mock('reflection', :options => {:conditions => {:active => true}}, :klass => ::Author, :macro => :belongs_to)
207
+ mock.stub!(:[]).with(:class_name).and_return("Author")
208
+ mock
209
+ end
210
+ end
211
+
212
+ it "should call author.find with association conditions" do
213
+ ::Author.should_receive(:scoped).with(:conditions => {:active => true})
214
+
215
+ semantic_form_for(@new_post) do |builder|
216
+ concat(builder.input(:author, :as => :select))
217
+ end
218
+ end
219
+
220
+ it "should call author.find with association conditions and find_options conditions" do
221
+ ::Author.should_receive(:scoped).with(:conditions => {:active => true})
222
+ ::Author.should_receive(:where).with({:publisher => true})
223
+
224
+ semantic_form_for(@new_post) do |builder|
225
+ concat(builder.input(:author, :as => :select, :find_options => {:conditions => {:publisher => true}}))
226
+ end
227
+ end
228
+ end
229
+
230
+ describe 'for a belongs_to association with :group_by => :continent' do
231
+ before do
232
+ @authors = [@bob, @fred, @fred, @fred]
233
+ ::Author.stub!(:find).and_return(@authors)
234
+ @continent_names = %w(Europe Africa)
235
+ @continents = (0..1).map { |i| c = ::Continent.new; c.stub!(:id).and_return(100 - i);c }
236
+ @authors[0..1].each_with_index { |author, i| author.stub!(:continent).and_return(@continents[i]) }
237
+
238
+ ::Continent.stub!(:reflect_on_all_associations).and_return { |macro| macro == :has_many ? [mock('reflection', :klass => Author, :name => :authors)] : [] }
239
+ ::Continent.stub!(:reflect_on_association).and_return {|column_name| mock('reflection', :klass => Author) if column_name == :authors}
240
+ ::Author.stub!(:reflect_on_association).and_return { |column_name| mock('reflection', :options => {}, :klass => Continent, :macro => :belongs_to) if column_name == :continent }
241
+
242
+
243
+ @continents.each_with_index do |continent, i|
244
+ continent.stub!(:to_label).and_return(@continent_names[i])
245
+ continent.stub!(:authors).and_return([@authors[i]])
246
+ end
247
+
248
+ concat(semantic_form_for(@new_post) do |builder|
249
+ concat(builder.input(:author, :as => :select, :group_by => :continent ) )
250
+ concat(builder.input(:author, :as => :select, :group_by => :continent, :group_label => :id ) )
251
+ concat(builder.input(:author, :as => :select, :group_by => :continent, :member_label => :login ) )
252
+ concat(builder.input(:author, :as => :select, :group_by => :continent, :member_label => :login, :group_label => :id ) )
253
+ end)
254
+ end
255
+
256
+ it_should_have_input_wrapper_with_class("select")
257
+ it_should_have_input_wrapper_with_id("post_author_input")
258
+ it_should_have_label_with_text(/Author/)
259
+ it_should_have_label_for('post_author_id')
260
+
261
+ # TODO, need to find a way to repeat some of the specs and logic from the belongs_to specs without grouping
262
+
263
+ 0.upto(1) do |i|
264
+ it 'should have all option groups and the right values' do
265
+ output_buffer.should have_tag("form div.clearfix div.input select optgroup[@label='#{@continent_names[i]}']", @authors[i].to_label)
266
+ end
267
+
268
+ it 'should have custom group labels' do
269
+ output_buffer.should have_tag("form div.clearfix div.input select optgroup[@label='#{@continents[i].id}']", @authors[i].to_label)
270
+ end
271
+
272
+ it 'should have custom author labels' do
273
+ output_buffer.should have_tag("form div.clearfix div.input select optgroup[@label='#{@continent_names[i]}']", @authors[i].login)
274
+ end
275
+
276
+ it 'should have custom author and group labels' do
277
+ output_buffer.should have_tag("form div.clearfix div.input select optgroup[@label='#{@continents[i].id}']", @authors[i].login)
278
+ end
279
+ end
280
+
281
+ it 'should have no duplicate groups' do
282
+ output_buffer.should have_tag('form div.clearfix div.input select optgroup', :count => 8)
283
+ end
284
+
285
+ it 'should sort the groups on the label method' do
286
+ output_buffer.should have_tag("form div.clearfix div.input select optgroup[@label='Africa']")
287
+ output_buffer.should have_tag("form div.clearfix div.input select optgroup[@label='99']")
288
+ end
289
+
290
+ it 'should call find with :include for more optimized queries' do
291
+ Author.should_receive(:where).with(:include => :continent)
292
+
293
+ semantic_form_for(@new_post) do |builder|
294
+ concat(builder.input(:author, :as => :select, :group_by => :continent ) )
295
+ end
296
+ end
297
+ end
298
+
299
+ describe 'for a has_many association' do
300
+ before do
301
+ concat(semantic_form_for(@fred) do |builder|
302
+ concat(builder.input(:posts, :as => :select))
303
+ end)
304
+ end
305
+
306
+ it_should_have_input_wrapper_with_class("select")
307
+ it_should_have_input_wrapper_with_id("author_posts_input")
308
+ it_should_have_label_with_text(/Post/)
309
+ it_should_have_label_for('author_post_ids')
310
+ it_should_apply_error_logic_for_input_type(:select)
311
+ it_should_call_find_on_association_class_when_no_collection_is_provided(:select)
312
+ it_should_use_the_collection_when_provided(:select, 'option')
313
+
314
+ it 'should have a select inside the wrapper' do
315
+ output_buffer.should have_tag('form div.clearfix div.input select')
316
+ output_buffer.should have_tag('form div.clearfix div.input select#author_post_ids')
317
+ end
318
+
319
+ it 'should have a multi-select select' do
320
+ output_buffer.should have_tag('form div.clearfix div.input select[@multiple="multiple"]')
321
+ end
322
+
323
+ it 'should append [] to the name attribute for multiple select' do
324
+ output_buffer.should have_tag('form div.clearfix div.input select[@multiple="multiple"][@name="author[post_ids][]"]')
325
+ end
326
+
327
+ it 'should have a select option for each Post' do
328
+ output_buffer.should have_tag('form div.clearfix div.input select option', :count => ::Post.all.size)
329
+ ::Post.all.each do |post|
330
+ output_buffer.should have_tag("form div.clearfix div.input select option[@value='#{post.id}']", /#{post.to_label}/)
331
+ end
332
+ end
333
+
334
+ it 'should not have a blank option by default' do
335
+ output_buffer.should_not have_tag("form div.clearfix div.input select option[@value='']")
336
+ end
337
+
338
+ it 'should respect the :include_blank option for single selects' do
339
+ concat(semantic_form_for(@fred) do |builder|
340
+ concat(builder.input(:posts, :as => :select, :multiple => false, :include_blank => true))
341
+ end)
342
+
343
+ output_buffer.should have_tag("form div.clearfix div.input select option[@value='']")
344
+ end
345
+
346
+ it 'should respect the :include_blank option for multiple selects' do
347
+ concat(semantic_form_for(@fred) do |builder|
348
+ concat(builder.input(:posts, :as => :select, :multiple => true, :include_blank => true))
349
+ end)
350
+
351
+ output_buffer.should have_tag("form div.clearfix div.input select option[@value='']")
352
+ end
353
+
354
+ it 'should have one option with a "selected" attribute' do
355
+ output_buffer.should have_tag('form div.clearfix div.input select option[@selected]', :count => 1)
356
+ end
357
+ end
358
+
359
+ describe 'for a has_and_belongs_to_many association' do
360
+ before do
361
+ concat(semantic_form_for(@freds_post) do |builder|
362
+ concat(builder.input(:authors, :as => :select))
363
+ end)
364
+ end
365
+
366
+ it_should_have_input_wrapper_with_class("select")
367
+ it_should_have_input_wrapper_with_id("post_authors_input")
368
+ it_should_have_label_with_text(/Author/)
369
+ it_should_have_label_for('post_author_ids')
370
+ it_should_apply_error_logic_for_input_type(:select)
371
+ it_should_call_find_on_association_class_when_no_collection_is_provided(:select)
372
+ it_should_use_the_collection_when_provided(:select, 'option')
373
+
374
+ it 'should have a select inside the wrapper' do
375
+ output_buffer.should have_tag('form div.clearfix div.input select')
376
+ output_buffer.should have_tag('form div.clearfix div.input select#post_author_ids')
377
+ end
378
+
379
+ it 'should have a multi-select select' do
380
+ output_buffer.should have_tag('form div.clearfix div.input select[@multiple="multiple"]')
381
+ end
382
+
383
+ it 'should have a select option for each Author' do
384
+ output_buffer.should have_tag('form div.clearfix div.input select option', :count => ::Author.all.size)
385
+ ::Author.all.each do |author|
386
+ output_buffer.should have_tag("form div.clearfix div.input select option[@value='#{author.id}']", /#{author.to_label}/)
387
+ end
388
+ end
389
+
390
+ it 'should not have a blank option by default' do
391
+ output_buffer.should_not have_tag("form div.clearfix div.input select option[@value='']")
392
+ end
393
+
394
+ it 'should respect the :include_blank option for single selects' do
395
+ concat(semantic_form_for(@freds_post) do |builder|
396
+ concat(builder.input(:authors, :as => :select, :multiple => false, :include_blank => true))
397
+ end)
398
+
399
+ output_buffer.should have_tag("form div.clearfix div.input select option[@value='']")
400
+ end
401
+
402
+ it 'should respect the :include_blank option for multiple selects' do
403
+ concat(semantic_form_for(@freds_post) do |builder|
404
+ concat(builder.input(:authors, :as => :select, :multiple => true, :include_blank => true))
405
+ end)
406
+
407
+ output_buffer.should have_tag("form div.clearfix div.input select option[@value='']")
408
+ end
409
+
410
+ it 'should have one option with a "selected" attribute' do
411
+ output_buffer.should have_tag('form div.clearfix div.input select option[@selected]', :count => 1)
412
+ end
413
+ end
414
+
415
+ describe 'when :prompt => "choose something" is set' do
416
+ before do
417
+ @new_post.stub!(:author_id).and_return(nil)
418
+ concat(semantic_form_for(@new_post) do |builder|
419
+ concat(builder.input(:author, :as => :select, :prompt => "choose author"))
420
+ end)
421
+ end
422
+
423
+ it 'should have a select with prompt' do
424
+ output_buffer.should have_tag("form div.clearfix div.input select option[@value='']", /choose author/, :count => 1)
425
+ end
426
+
427
+ it 'should not have a blank select option' do
428
+ output_buffer.should_not have_tag("form div.clearfix div.input select option[@value='']", "")
429
+ end
430
+ end
431
+
432
+ describe 'when no object is given' do
433
+ before(:each) do
434
+ concat(semantic_form_for(:project, :url => 'http://test.host') do |builder|
435
+ concat(builder.input(:author, :as => :select, :collection => ::Author.all))
436
+ end)
437
+ end
438
+
439
+ it 'should generate label' do
440
+ output_buffer.should have_tag('form div.clearfix label', /Author/)
441
+ output_buffer.should have_tag("form div.clearfix label[@for='project_author']")
442
+ end
443
+
444
+ it 'should generate select inputs' do
445
+ output_buffer.should have_tag('form div.clearfix div.input select#project_author')
446
+ output_buffer.should have_tag('form div.clearfix div.input select option', :count => ::Author.all.size + 1)
447
+ end
448
+
449
+ it 'should generate an option to each item' do
450
+ ::Author.all.each do |author|
451
+ output_buffer.should have_tag("form div.clearfix div.input select option[@value='#{author.id}']", /#{author.to_label}/)
452
+ end
453
+ end
454
+ end
455
+
456
+ describe 'when no association exists' do
457
+
458
+ it 'should still generate a valid name attribute' do
459
+ concat(semantic_form_for(:project, :url => 'http://test.host') do |builder|
460
+ concat(builder.input(:author_name, :as => :select, :collection => ::Author.all))
461
+ end)
462
+ output_buffer.should have_tag("form div.clearfix div.input select[@name='project[author_name]']")
463
+ end
464
+
465
+ describe 'and :multiple is set to true through :input_html' do
466
+ it "should make the select a multi-select" do
467
+ concat(semantic_form_for(:project, :url => 'http://test.host') do |builder|
468
+ concat(builder.input(:author_name, :as => :select, :input_html => {:multiple => true} ))
469
+ end)
470
+ output_buffer.should have_tag("form div.clearfix div.input select[@multiple]")
471
+ end
472
+ end
473
+
474
+ describe 'and :multiple is set to true' do
475
+ it "should make the select a multi-select" do
476
+ concat(semantic_form_for(:project, :url => 'http://test.host') do |builder|
477
+ concat(builder.input(:author_name, :as => :select, :multiple => true, :collection => ["Fred", "Bob"]))
478
+ end)
479
+ output_buffer.should have_tag("form div.clearfix div.input select[@multiple]")
480
+ end
481
+ end
482
+
483
+ end
484
+
485
+ describe 'when a grouped collection collection is given' do
486
+ before(:each) do
487
+ concat(semantic_form_for(:project, :url => 'http://test.host') do |builder|
488
+ @grouped_opts = [['one', ['pencil', 'crayon', 'pen']],
489
+ ['two', ['eyes', 'hands', 'feet']],
490
+ ['three', ['wickets', 'witches', 'blind mice']]]
491
+ concat(builder.input(:author, :as => :select, :collection => grouped_options_for_select(@grouped_opts, "hands")))
492
+ end)
493
+ end
494
+
495
+ it 'should generate an option to each item' do
496
+ @grouped_opts.each do |opt_pair|
497
+ output_buffer.should have_tag("form div.clearfix div.input select optgroup[@label='#{opt_pair[0]}']")
498
+ opt_pair[1].each do |v|
499
+ output_buffer.should have_tag("form div.clearfix div.input select optgroup[@label='#{opt_pair[0]}'] option[@value='#{v}']")
500
+ end
501
+ end
502
+ output_buffer.should have_tag("form div.clearfix div.input select optgroup option[@selected]","hands")
503
+ end
504
+ end
505
+
506
+ describe "enum" do
507
+ before do
508
+ @output_buffer = ''
509
+ @some_meta_descriptions = ["One", "Two", "Three"]
510
+ @new_post.stub!(:meta_description).any_number_of_times
511
+ end
512
+
513
+ describe ":as is not set" do
514
+ before do
515
+ concat(semantic_form_for(@new_post) do |builder|
516
+ concat(builder.input(:meta_description, :collection => @some_meta_descriptions))
517
+ end)
518
+ concat(semantic_form_for(:project, :url => 'http://test.host') do |builder|
519
+ concat(builder.input(:meta_description, :collection => @some_meta_descriptions))
520
+ end)
521
+ end
522
+
523
+ it "should render a select field" do
524
+ output_buffer.should have_tag("form div.clearfix div.input select", :count => 2)
525
+ end
526
+ end
527
+
528
+ describe ":as is set" do
529
+ before do
530
+ # Should not be a case, but just checking :as got highest priority in setting input type.
531
+ concat(semantic_form_for(@new_post) do |builder|
532
+ concat(builder.input(:meta_description, :as => :string, :collection => @some_meta_descriptions))
533
+ end)
534
+ concat(semantic_form_for(:project, :url => 'http://test.host') do |builder|
535
+ concat(builder.input(:meta_description, :as => :string, :collection => @some_meta_descriptions))
536
+ end)
537
+ end
538
+
539
+ it "should render a text field" do
540
+ output_buffer.should have_tag("form div.clearfix div.input input[@type='text']", :count => 2)
541
+ end
542
+ end
543
+ end
544
+
545
+ describe 'when a namespace is provided' do
546
+ before do
547
+ concat(semantic_form_for(@freds_post, :namespace => 'context2') do |builder|
548
+ concat(builder.input(:authors, :as => :select))
549
+ end)
550
+ end
551
+ it_should_have_input_wrapper_with_id("context2_post_authors_input")
552
+ it_should_have_select_with_id("context2_post_author_ids")
553
+ it_should_have_label_for("context2_post_author_ids")
554
+ end
555
+
556
+ context "when required" do
557
+ it "should add the required attribute to the select's html options" do
558
+ with_config :use_required_attribute, true do
559
+ concat(semantic_form_for(@new_post) do |builder|
560
+ concat(builder.input(:author, :as => :select, :required => true))
561
+ end)
562
+ output_buffer.should have_tag("select[@required]")
563
+ end
564
+ end
565
+ end
566
+
567
+ end