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