formtastic 3.0.0 → 3.1.0.rc1

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