formtastic 0.2.5

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.
@@ -0,0 +1,11 @@
1
+ # coding: utf-8
2
+ module JustinFrench #:nodoc:
3
+ module Formtastic #:nodoc:
4
+ class SemanticFormBuilder < ::Formtastic::SemanticFormBuilder #:nodoc:
5
+ def initialize(*args) #:nodoc:
6
+ ::ActiveSupport::Deprecation.warn("JustinFrench::Formtastic::SemanticFormBuilder is deprecated. User Formtastic::SemanticFormBuilder instead", caller)
7
+ super
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,8 @@
1
+ en:
2
+ formtastic:
3
+ "yes": 'Yes'
4
+ "no": 'No'
5
+ create: 'Create'
6
+ save: 'Save'
7
+ submit: 'Submit'
8
+ required: 'Required'
@@ -0,0 +1,4 @@
1
+ # coding: utf-8
2
+ require File.join(File.dirname(__FILE__), *%w[.. lib formtastic])
3
+ require File.join(File.dirname(__FILE__), *%w[.. lib justin_french formtastic])
4
+ ActionView::Base.send :include, Formtastic::SemanticFormHelper
@@ -0,0 +1,3292 @@
1
+ # coding: utf-8
2
+ require File.dirname(__FILE__) + '/test_helper'
3
+ require 'formtastic'
4
+
5
+ module FormtasticSpecHelper
6
+ def default_input_type(column_type, column_name = :generic_column_name)
7
+ @new_post.stub!(column_name)
8
+ @new_post.stub!(:column_for_attribute).and_return(mock('column', :type => column_type)) unless column_type.nil?
9
+
10
+ semantic_form_for(@new_post) do |builder|
11
+ @default_type = builder.send(:default_input_type, column_name)
12
+ end
13
+
14
+ return @default_type
15
+ end
16
+ end
17
+
18
+ class Post;
19
+ def id; end
20
+ end
21
+
22
+ class Author; end
23
+
24
+ describe 'Formtastic' do
25
+
26
+ include ActionView::Helpers::FormHelper
27
+ include ActionView::Helpers::FormTagHelper
28
+ include ActionView::Helpers::FormOptionsHelper
29
+ include ActionView::Helpers::UrlHelper
30
+ include ActionView::Helpers::TagHelper
31
+ include ActionView::Helpers::TextHelper
32
+ include ActionView::Helpers::ActiveRecordHelper
33
+ include ActionView::Helpers::RecordIdentificationHelper
34
+ include ActionView::Helpers::DateHelper
35
+ include ActionView::Helpers::CaptureHelper
36
+ include ActiveSupport
37
+ include ActionController::PolymorphicRoutes
38
+
39
+ include Formtastic::SemanticFormHelper
40
+
41
+ attr_accessor :output_buffer
42
+
43
+ def protect_against_forgery?; false; end
44
+
45
+ before do
46
+ Formtastic::SemanticFormBuilder.label_str_method = :humanize
47
+
48
+ @output_buffer = ''
49
+
50
+ # Resource-oriented styles like form_for(@post) will expect a path method for the object,
51
+ # so we're defining some here.
52
+ def post_path(o); "/posts/1"; end
53
+ def posts_path; "/posts"; end
54
+ def new_post_path; "/posts/new"; end
55
+
56
+ def author_path(o); "/authors/1"; end
57
+ def authors_path; "/authors"; end
58
+ def new_author_path; "/authors/new"; end
59
+
60
+ @fred = mock('user')
61
+ @fred.stub!(:class).and_return(::Author)
62
+ @fred.stub!(:to_label).and_return('Fred Smith')
63
+ @fred.stub!(:login).and_return('fred_smith')
64
+ @fred.stub!(:id).and_return(37)
65
+ @fred.stub!(:new_record?).and_return(false)
66
+ @fred.stub!(:errors).and_return(mock('errors', :[] => nil))
67
+
68
+ @bob = mock('user')
69
+ @bob.stub!(:class).and_return(::Author)
70
+ @bob.stub!(:to_label).and_return('Bob Rock')
71
+ @bob.stub!(:login).and_return('bob')
72
+ @bob.stub!(:id).and_return(42)
73
+ @bob.stub!(:posts).and_return([])
74
+ @bob.stub!(:post_ids).and_return([])
75
+ @bob.stub!(:new_record?).and_return(false)
76
+ @bob.stub!(:errors).and_return(mock('errors', :[] => nil))
77
+
78
+ ::Author.stub!(:find).and_return([@fred, @bob])
79
+ ::Author.stub!(:human_attribute_name).and_return { |column_name| column_name.humanize }
80
+ ::Author.stub!(:human_name).and_return('::Author')
81
+ ::Author.stub!(:reflect_on_validations_for).and_return([])
82
+ ::Author.stub!(:reflect_on_association).and_return { |column_name| mock('reflection', :options => {}, :klass => Post, :macro => :has_many) if column_name == :posts }
83
+
84
+ # Sometimes we need a mock @post object and some Authors for belongs_to
85
+ @new_post = mock('post')
86
+ @new_post.stub!(:class).and_return(::Post)
87
+ @new_post.stub!(:id).and_return(nil)
88
+ @new_post.stub!(:new_record?).and_return(true)
89
+ @new_post.stub!(:errors).and_return(mock('errors', :[] => nil))
90
+ @new_post.stub!(:author).and_return(nil)
91
+
92
+ @freds_post = mock('post')
93
+ @freds_post.stub!(:class).and_return(::Post)
94
+ @freds_post.stub!(:to_label).and_return('Fred Smith')
95
+ @freds_post.stub!(:id).and_return(19)
96
+ @freds_post.stub!(:author).and_return(@fred)
97
+ @freds_post.stub!(:author_id).and_return(@fred.id)
98
+ @freds_post.stub!(:authors).and_return([@fred])
99
+ @freds_post.stub!(:author_ids).and_return([@fred.id])
100
+ @freds_post.stub!(:new_record?).and_return(false)
101
+ @freds_post.stub!(:errors).and_return(mock('errors', :[] => nil))
102
+ @fred.stub!(:posts).and_return([@freds_post])
103
+ @fred.stub!(:post_ids).and_return([@freds_post.id])
104
+
105
+ ::Post.stub!(:human_attribute_name).and_return { |column_name| column_name.humanize }
106
+ ::Post.stub!(:human_name).and_return('Post')
107
+ ::Post.stub!(:reflect_on_all_validations).and_return([])
108
+ ::Post.stub!(:reflect_on_validations_for).and_return([])
109
+ ::Post.stub!(:reflect_on_association).and_return do |column_name|
110
+ case column_name
111
+ when :author, :author_status
112
+ mock('reflection', :options => {}, :klass => ::Author, :macro => :belongs_to)
113
+ when :authors
114
+ mock('reflection', :options => {}, :klass => ::Author, :macro => :has_and_belongs_to_many)
115
+ end
116
+ end
117
+ ::Post.stub!(:find).and_return([@freds_post])
118
+ end
119
+
120
+ describe 'JustinFrench::Formtastic::SemanticFormBuilder' do
121
+ require 'justin_french/formtastic'
122
+ it 'should be deprecated' do
123
+ ::ActiveSupport::Deprecation.should_receive(:warn).with(/JustinFrench\:\:Formtastic\:\:SemanticFormBuilder/, anything())
124
+ form_for(@new_post, :builder => JustinFrench::Formtastic::SemanticFormBuilder) do |builder|
125
+ end
126
+ end
127
+ end
128
+
129
+ describe 'SemanticFormHelper' do
130
+
131
+ describe '#semantic_form_for' do
132
+
133
+ it 'yields an instance of SemanticFormBuilder' do
134
+ semantic_form_for(:post, ::Post.new, :url => '/hello') do |builder|
135
+ builder.class.should == Formtastic::SemanticFormBuilder
136
+ end
137
+ end
138
+
139
+ it 'adds a class of "formtastic" to the generated form' do
140
+ semantic_form_for(:post, ::Post.new, :url => '/hello') do |builder|
141
+ end
142
+ output_buffer.should have_tag("form.formtastic")
143
+ end
144
+
145
+ it 'adds class matching the object name to the generated form when a symbol is provided' do
146
+ semantic_form_for(:post, ::Post.new, :url => '/hello') do |builder|
147
+ end
148
+ output_buffer.should have_tag("form.post")
149
+
150
+ semantic_form_for(:project, :url => '/hello') do |builder|
151
+ end
152
+ output_buffer.should have_tag("form.project")
153
+ end
154
+
155
+ it 'adds class matching the object\'s class to the generated form when an object is provided' do
156
+ semantic_form_for(@new_post) do |builder|
157
+ end
158
+ output_buffer.should have_tag("form.post")
159
+ end
160
+
161
+ describe 'allows :html options' do
162
+ before(:each) do
163
+ semantic_form_for(:post, ::Post.new, :url => '/hello', :html => { :id => "something-special", :class => "something-extra", :multipart => true }) do |builder|
164
+ end
165
+ end
166
+
167
+ it 'to add a id of "something-special" to generated form' do
168
+ output_buffer.should have_tag("form#something-special")
169
+ end
170
+
171
+ it 'to add a class of "something-extra" to generated form' do
172
+ output_buffer.should have_tag("form.something-extra")
173
+ end
174
+
175
+ it 'to add enctype="multipart/form-data"' do
176
+ output_buffer.should have_tag('form[@enctype="multipart/form-data"]')
177
+ end
178
+ end
179
+
180
+ it 'can be called with a resource-oriented style' do
181
+ semantic_form_for(@new_post) do |builder|
182
+ builder.object.class.should == ::Post
183
+ builder.object_name.should == "post"
184
+ end
185
+ end
186
+
187
+ it 'can be called with a generic style and instance variable' do
188
+ semantic_form_for(:post, @new_post, :url => new_post_path) do |builder|
189
+ builder.object.class.should == ::Post
190
+ builder.object_name.to_s.should == "post" # TODO: is this forced .to_s a bad assumption somewhere?
191
+ end
192
+ end
193
+
194
+ it 'can be called with a generic style and inline object' do
195
+ semantic_form_for(:post, ::Post.new, :url => new_post_path) do |builder|
196
+ builder.object.class.should == ::Post
197
+ builder.object_name.to_s.should == "post" # TODO: is this forced .to_s a bad assumption somewhere?
198
+ end
199
+ end
200
+
201
+ end
202
+
203
+ describe '#semantic_fields_for' do
204
+ it 'yields an instance of SemanticFormBuilder' do
205
+ semantic_fields_for(:post, ::Post.new, :url => '/hello') do |builder|
206
+ builder.class.should == Formtastic::SemanticFormBuilder
207
+ end
208
+ end
209
+ end
210
+
211
+ describe '#semantic_form_remote_for' do
212
+ it 'yields an instance of SemanticFormBuilder' do
213
+ semantic_form_remote_for(:post, ::Post.new, :url => '/hello') do |builder|
214
+ builder.class.should == Formtastic::SemanticFormBuilder
215
+ end
216
+ end
217
+ end
218
+
219
+ describe '#semantic_form_for_remote' do
220
+ it 'yields an instance of SemanticFormBuilder' do
221
+ semantic_remote_form_for(:post, ::Post.new, :url => '/hello') do |builder|
222
+ builder.class.should == Formtastic::SemanticFormBuilder
223
+ end
224
+ end
225
+ end
226
+
227
+ end
228
+
229
+ describe 'SemanticFormBuilder' do
230
+
231
+ include FormtasticSpecHelper
232
+
233
+ describe "@@builder" do
234
+ before do
235
+ @new_post.stub!(:title)
236
+ @new_post.stub!(:body)
237
+ @new_post.stub!(:column_for_attribute).and_return(mock('column', :type => :string, :limit => 255))
238
+ end
239
+
240
+ after do
241
+ Formtastic::SemanticFormHelper.builder = Formtastic::SemanticFormBuilder
242
+ end
243
+
244
+ it "can be overridden" do
245
+
246
+ class CustomFormBuilder < Formtastic::SemanticFormBuilder
247
+ def custom(arg1, arg2, options = {})
248
+ [arg1, arg2, options]
249
+ end
250
+ end
251
+
252
+ Formtastic::SemanticFormHelper.builder = CustomFormBuilder
253
+
254
+ semantic_form_for(@new_post) do |builder|
255
+ builder.class.should == CustomFormBuilder
256
+ builder.custom("one", "two").should == ["one", "two", {}]
257
+ end
258
+ end
259
+
260
+ end
261
+
262
+ describe 'Formtastic::SemanticFormBuilder#semantic_fields_for' do
263
+ before do
264
+ @new_post.stub!(:author).and_return(::Author.new)
265
+ end
266
+
267
+ it 'yields an instance of SemanticFormHelper.builder' do
268
+ semantic_form_for(@new_post) do |builder|
269
+ builder.semantic_fields_for(:author) do |nested_builder|
270
+ nested_builder.class.should == Formtastic::SemanticFormHelper.builder
271
+ end
272
+ end
273
+ end
274
+
275
+ it 'nests the object name' do
276
+ semantic_form_for(@new_post) do |builder|
277
+ builder.semantic_fields_for(@bob) do |nested_builder|
278
+ nested_builder.object_name.should == 'post[author]'
279
+ end
280
+ end
281
+ end
282
+
283
+ it 'should sanitize html id for li tag' do
284
+ @bob.stub!(:column_for_attribute).and_return(mock('column', :type => :string, :limit => 255))
285
+ semantic_form_for(@new_post) do |builder|
286
+ builder.semantic_fields_for(@bob, :index => 1) do |nested_builder|
287
+ concat(nested_builder.inputs(:login))
288
+ end
289
+ end
290
+ output_buffer.should have_tag('form fieldset.inputs #post_author_1_login_input')
291
+ output_buffer.should_not have_tag('form fieldset.inputs #post[author]_1_login_input')
292
+ end
293
+ end
294
+
295
+ describe '#label' do
296
+ it 'should humanize the given attribute' do
297
+ semantic_form_for(@new_post) do |builder|
298
+ builder.label(:login).should have_tag('label', :with => /Login/)
299
+ end
300
+ end
301
+
302
+ it 'should be printed as span' do
303
+ semantic_form_for(@new_post) do |builder|
304
+ builder.label(:login, nil, { :required => true, :as_span => true }).should have_tag('span.label abbr')
305
+ end
306
+ end
307
+
308
+ describe 'when required is given' do
309
+ it 'should append a required note' do
310
+ semantic_form_for(@new_post) do |builder|
311
+ builder.label(:login, nil, :required => true).should have_tag('label abbr')
312
+ end
313
+ end
314
+
315
+ it 'should allow require option to be given as second argument' do
316
+ semantic_form_for(@new_post) do |builder|
317
+ builder.label(:login, :required => true).should have_tag('label abbr')
318
+ end
319
+ end
320
+ end
321
+
322
+ describe 'when label is given' do
323
+ it 'should allow the text to be given as label option' do
324
+ semantic_form_for(@new_post) do |builder|
325
+ builder.label(:login, :required => true, :label => 'My label').should have_tag('label', :with => /My label/)
326
+ end
327
+ end
328
+
329
+ it 'should return nil if label is false' do
330
+ semantic_form_for(@new_post) do |builder|
331
+ builder.label(:login, :label => false).should be_blank
332
+ end
333
+ end
334
+ end
335
+ end
336
+
337
+ describe '#errors_on' do
338
+ before(:each) do
339
+ @title_errors = ['must not be blank', 'must be longer than 10 characters', 'must be awesome']
340
+ @errors = mock('errors')
341
+ @new_post.stub!(:errors).and_return(@errors)
342
+ end
343
+
344
+ describe "field error proc" do
345
+ it "should not be overridden globally for all form builders" do
346
+ current_field_error_proc = ::ActionView::Base.field_error_proc
347
+
348
+ semantic_form_for(@new_post) do |builder|
349
+ ::ActionView::Base.field_error_proc.should_not == current_field_error_proc
350
+ end
351
+
352
+ ::ActionView::Base.field_error_proc.should == current_field_error_proc
353
+
354
+ form_for(@new_post) do |builder|
355
+ ::ActionView::Base.field_error_proc.should == current_field_error_proc
356
+ end
357
+ end
358
+ end
359
+
360
+ describe 'when there are errors' do
361
+ before do
362
+ @errors.stub!(:[]).with(:title).and_return(@title_errors)
363
+ end
364
+
365
+ it 'should render a paragraph with the errors joined into a sentence when inline_errors config is :sentence' do
366
+ Formtastic::SemanticFormBuilder.inline_errors = :sentence
367
+ semantic_form_for(@new_post) do |builder|
368
+ builder.errors_on(:title).should have_tag('p.inline-errors', @title_errors.to_sentence)
369
+ end
370
+ end
371
+
372
+ it 'should render an unordered list with the class errors when inline_errors config is :list' do
373
+ Formtastic::SemanticFormBuilder.inline_errors = :list
374
+ semantic_form_for(@new_post) do |builder|
375
+ builder.errors_on(:title).should have_tag('ul.errors')
376
+ @title_errors.each do |error|
377
+ builder.errors_on(:title).should have_tag('ul.errors li', error)
378
+ end
379
+ end
380
+ end
381
+
382
+ it 'should return nil when inline_errors config is :none' do
383
+ Formtastic::SemanticFormBuilder.inline_errors = :none
384
+ semantic_form_for(@new_post) do |builder|
385
+ builder.errors_on(:title).should be_nil
386
+ end
387
+ end
388
+
389
+ end
390
+
391
+ describe 'when there are no errors (nil)' do
392
+ before do
393
+ @errors.stub!(:[]).with(:title).and_return(nil)
394
+ end
395
+
396
+ it 'should return nil when inline_errors config is :sentence, :list or :none' do
397
+ [:sentence, :list, :none].each do |config|
398
+ Formtastic::SemanticFormBuilder.inline_errors = config
399
+ semantic_form_for(@new_post) do |builder|
400
+ builder.errors_on(:title).should be_nil
401
+ end
402
+ end
403
+ end
404
+ end
405
+
406
+ describe 'when there are no errors (empty array)' do
407
+ before do
408
+ @errors.stub!(:[]).with(:title).and_return([])
409
+ end
410
+
411
+ it 'should return nil when inline_errors config is :sentence, :list or :none' do
412
+ [:sentence, :list, :none].each do |config|
413
+ Formtastic::SemanticFormBuilder.inline_errors = config
414
+ semantic_form_for(@new_post) do |builder|
415
+ builder.errors_on(:title).should be_nil
416
+ end
417
+ end
418
+ end
419
+ end
420
+
421
+ end
422
+
423
+ describe '#input' do
424
+
425
+ before do
426
+ @new_post.stub!(:title)
427
+ @new_post.stub!(:body)
428
+ @new_post.stub!(:published)
429
+ @new_post.stub!(:column_for_attribute).with(:meta_description).and_return(mock('column', :type => :string, :limit => 255))
430
+ @new_post.stub!(:column_for_attribute).with(:title).and_return(mock('column', :type => :string, :limit => 255))
431
+ @new_post.stub!(:column_for_attribute).with(:body).and_return(mock('column', :type => :text))
432
+ @new_post.stub!(:column_for_attribute).with(:published).and_return(mock('column', :type => :boolean))
433
+ end
434
+
435
+ describe 'with inline order customization' do
436
+ it 'should allow input, hints, errors as order' do
437
+ Formtastic::SemanticFormBuilder.inline_order = [:input, :hints, :errors]
438
+
439
+ semantic_form_for(@new_post) do |builder|
440
+ builder.should_receive(:inline_input_for).once.ordered
441
+ builder.should_receive(:inline_hints_for).once.ordered
442
+ builder.should_receive(:inline_errors_for).once.ordered
443
+ concat(builder.input(:title))
444
+ end
445
+ end
446
+
447
+ it 'should allow hints, input, errors as order' do
448
+ Formtastic::SemanticFormBuilder.inline_order = [:hints, :input, :errors]
449
+
450
+ semantic_form_for(@new_post) do |builder|
451
+ builder.should_receive(:inline_hints_for).once.ordered
452
+ builder.should_receive(:inline_input_for).once.ordered
453
+ builder.should_receive(:inline_errors_for).once.ordered
454
+ concat(builder.input(:title))
455
+ end
456
+ end
457
+ end
458
+
459
+ describe 'arguments and options' do
460
+
461
+ it 'should require the first argument (the method on form\'s object)' do
462
+ lambda {
463
+ semantic_form_for(@new_post) do |builder|
464
+ concat(builder.input()) # no args passed in at all
465
+ end
466
+ }.should raise_error(ArgumentError)
467
+ end
468
+
469
+ describe ':required option' do
470
+
471
+ describe 'when true' do
472
+
473
+ before do
474
+ @string = Formtastic::SemanticFormBuilder.required_string = " required yo!" # ensure there's something in the string
475
+ @new_post.class.should_not_receive(:reflect_on_all_validations)
476
+ end
477
+
478
+ after do
479
+ Formtastic::SemanticFormBuilder.required_string = %{<abbr title="required">*</abbr>}
480
+ end
481
+
482
+ it 'should set a "required" class' do
483
+ semantic_form_for(@new_post) do |builder|
484
+ concat(builder.input(:title, :required => true))
485
+ end
486
+ output_buffer.should_not have_tag('form li.optional')
487
+ output_buffer.should have_tag('form li.required')
488
+ end
489
+
490
+ it 'should append the "required" string to the label' do
491
+ semantic_form_for(@new_post) do |builder|
492
+ concat(builder.input(:title, :required => true))
493
+ end
494
+ output_buffer.should have_tag('form li.required label', /#{@string}$/)
495
+ end
496
+
497
+ end
498
+
499
+ describe 'when false' do
500
+
501
+ before do
502
+ @string = Formtastic::SemanticFormBuilder.optional_string = " optional yo!" # ensure there's something in the string
503
+ @new_post.class.should_not_receive(:reflect_on_all_validations)
504
+ end
505
+
506
+ after do
507
+ Formtastic::SemanticFormBuilder.optional_string = ''
508
+ end
509
+
510
+ it 'should set an "optional" class' do
511
+ semantic_form_for(@new_post) do |builder|
512
+ concat(builder.input(:title, :required => false))
513
+ end
514
+ output_buffer.should_not have_tag('form li.required')
515
+ output_buffer.should have_tag('form li.optional')
516
+ end
517
+
518
+ it 'should append the "optional" string to the label' do
519
+ semantic_form_for(@new_post) do |builder|
520
+ concat(builder.input(:title, :required => false))
521
+ end
522
+ output_buffer.should have_tag('form li.optional label', /#{@string}$/)
523
+ end
524
+
525
+ end
526
+
527
+ describe 'when not provided' do
528
+
529
+ describe 'and an object was not given' do
530
+
531
+ it 'should use the default value' do
532
+ Formtastic::SemanticFormBuilder.all_fields_required_by_default.should == true
533
+ Formtastic::SemanticFormBuilder.all_fields_required_by_default = false
534
+
535
+ semantic_form_for(:project, :url => 'http://test.host/') do |builder|
536
+ concat(builder.input(:title))
537
+ end
538
+ output_buffer.should_not have_tag('form li.required')
539
+ output_buffer.should have_tag('form li.optional')
540
+
541
+ Formtastic::SemanticFormBuilder.all_fields_required_by_default = true
542
+ end
543
+
544
+ end
545
+
546
+ describe 'and an object was given' do
547
+
548
+ describe 'and the validation reflection plugin is available' do
549
+
550
+ before do
551
+ @new_post.class.stub!(:method_defined?).with(:reflect_on_validations_for).and_return(true)
552
+ end
553
+
554
+ describe 'and validates_presence_of was called for the method' do
555
+ it 'should be required' do
556
+ @new_post.class.should_receive(:reflect_on_validations_for).with(:title).and_return([
557
+ mock('MacroReflection', :macro => :validates_presence_of, :name => :title, :options => nil)
558
+ ])
559
+ @new_post.class.should_receive(:reflect_on_validations_for).with(:body).and_return([
560
+ mock('MacroReflection', :macro => :validates_presence_of, :name => :body, :options => {:if => true})
561
+ ])
562
+
563
+ semantic_form_for(@new_post) do |builder|
564
+ concat(builder.input(:title))
565
+ concat(builder.input(:body))
566
+ end
567
+ output_buffer.should have_tag('form li.required')
568
+ output_buffer.should_not have_tag('form li.optional')
569
+ end
570
+
571
+ it 'should be not be required if the optional :if condition is not satisifed' do
572
+ should_be_required(:required => false, :options => { :if => false })
573
+ end
574
+
575
+ it 'should not be required if the optional :if proc evaluates to false' do
576
+ should_be_required(:required => false, :options => { :if => proc { |record| false } })
577
+ end
578
+
579
+ it 'should be required if the optional :if proc evaluates to true' do
580
+ should_be_required(:required => true, :options => { :if => proc { |record| true } })
581
+ end
582
+
583
+ it 'should not be required if the optional :unless proc evaluates to true' do
584
+ should_be_required(:required => false, :options => { :unless => proc { |record| true } })
585
+ end
586
+
587
+ it 'should be required if the optional :unless proc evaluates to false' do
588
+ should_be_required(:required => true, :options => { :unless => proc { |record| false } })
589
+ end
590
+
591
+ it 'should be required if the optional :if with a method string evaluates to true' do
592
+ @new_post.should_receive(:required_condition).and_return(true)
593
+ should_be_required(:required => true, :options => { :if => :required_condition })
594
+ end
595
+
596
+ it 'should be required if the optional :if with a method string evaluates to false' do
597
+ @new_post.should_receive(:required_condition).and_return(false)
598
+ should_be_required(:required => false, :options => { :if => :required_condition })
599
+ end
600
+
601
+ it 'should not be required if the optional :unless with a method string evaluates to false' do
602
+ @new_post.should_receive(:required_condition).and_return(false)
603
+ should_be_required(:required => true, :options => { :unless => :required_condition })
604
+ end
605
+
606
+ it 'should be required if the optional :unless with a method string evaluates to true' do
607
+ @new_post.should_receive(:required_condition).and_return(true)
608
+ should_be_required(:required => false, :options => { :unless => :required_condition })
609
+ end
610
+ end
611
+
612
+ # TODO make a matcher for this?
613
+ def should_be_required(options)
614
+ @new_post.class.should_receive(:reflect_on_validations_for).with(:body).and_return([
615
+ mock('MacroReflection', :macro => :validates_presence_of, :name => :body, :options => options[:options])
616
+ ])
617
+
618
+ semantic_form_for(@new_post) do |builder|
619
+ concat(builder.input(:body))
620
+ end
621
+
622
+ if options[:required]
623
+ output_buffer.should_not have_tag('form li.optional')
624
+ output_buffer.should have_tag('form li.required')
625
+ else
626
+ output_buffer.should have_tag('form li.optional')
627
+ output_buffer.should_not have_tag('form li.required')
628
+ end
629
+ end
630
+
631
+ describe 'and validates_presence_of was not called for the method' do
632
+ before do
633
+ @new_post.class.should_receive(:reflect_on_validations_for).with(:title).and_return([])
634
+ end
635
+
636
+ it 'should not be required' do
637
+ semantic_form_for(@new_post) do |builder|
638
+ concat(builder.input(:title))
639
+ end
640
+ output_buffer.should_not have_tag('form li.required')
641
+ output_buffer.should have_tag('form li.optional')
642
+ end
643
+ end
644
+
645
+ end
646
+
647
+ describe 'and the validation reflection plugin is not available' do
648
+
649
+ it 'should use the default value' do
650
+ Formtastic::SemanticFormBuilder.all_fields_required_by_default.should == true
651
+ Formtastic::SemanticFormBuilder.all_fields_required_by_default = false
652
+
653
+ semantic_form_for(@new_post) do |builder|
654
+ concat(builder.input(:title))
655
+ end
656
+ output_buffer.should_not have_tag('form li.required')
657
+ output_buffer.should have_tag('form li.optional')
658
+
659
+ Formtastic::SemanticFormBuilder.all_fields_required_by_default = true
660
+ end
661
+
662
+ end
663
+
664
+ end
665
+
666
+ end
667
+
668
+ end
669
+
670
+ describe ':as option' do
671
+
672
+ describe 'when not provided' do
673
+
674
+ it 'should default to a string for forms without objects unless column is password' do
675
+ semantic_form_for(:project, :url => 'http://test.host') do |builder|
676
+ concat(builder.input(:anything))
677
+ end
678
+ output_buffer.should have_tag('form li.string')
679
+ end
680
+
681
+ it 'should default to password for forms without objects if column is password' do
682
+ semantic_form_for(:project, :url => 'http://test.host') do |builder|
683
+ concat(builder.input(:password))
684
+ concat(builder.input(:password_confirmation))
685
+ concat(builder.input(:confirm_password))
686
+ end
687
+ output_buffer.should have_tag('form li.password', :count => 3)
688
+ end
689
+
690
+ it 'should default to a string for methods on objects that don\'t respond to "column_for_attribute"' do
691
+ @new_post.stub!(:method_without_a_database_column)
692
+ @new_post.stub!(:column_for_attribute).and_return(nil)
693
+ default_input_type(nil, :method_without_a_database_column).should == :string
694
+ end
695
+
696
+ it 'should default to :password for methods that don\'t have a column in the database but "password" is in the method name' do
697
+ @new_post.stub!(:password_method_without_a_database_column)
698
+ @new_post.stub!(:column_for_attribute).and_return(nil)
699
+ default_input_type(nil, :password_method_without_a_database_column).should == :password
700
+ end
701
+
702
+ 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
703
+ @new_post.stub!(:password_method_without_a_database_column)
704
+ @new_post.stub!(:column_for_attribute).and_return(nil)
705
+ default_input_type(nil, :password_method_without_a_database_column).should == :password
706
+ end
707
+
708
+ it 'should default to :select for column names ending in "_id"' do
709
+ default_input_type(:integer, :user_id).should == :select
710
+ default_input_type(:integer, :section_id).should == :select
711
+ end
712
+
713
+ it 'should default to :password for :string column types with "password" in the method name' do
714
+ default_input_type(:string, :password).should == :password
715
+ default_input_type(:string, :hashed_password).should == :password
716
+ default_input_type(:string, :password_hash).should == :password
717
+ end
718
+
719
+ it 'should default to :text for :text column types' do
720
+ default_input_type(:text).should == :text
721
+ end
722
+
723
+ it 'should default to :date for :date column types' do
724
+ default_input_type(:date).should == :date
725
+ end
726
+
727
+ it 'should default to :datetime for :datetime and :timestamp column types' do
728
+ default_input_type(:datetime).should == :datetime
729
+ default_input_type(:timestamp).should == :datetime
730
+ end
731
+
732
+ it 'should default to :time for :time column types' do
733
+ default_input_type(:time).should == :time
734
+ end
735
+
736
+ it 'should default to :boolean for :boolean column types' do
737
+ default_input_type(:boolean).should == :boolean
738
+ end
739
+
740
+ it 'should default to :string for :string column types' do
741
+ default_input_type(:string).should == :string
742
+ end
743
+
744
+ it 'should default to :numeric for :integer, :float and :decimal column types' do
745
+ default_input_type(:integer).should == :numeric
746
+ default_input_type(:float).should == :numeric
747
+ default_input_type(:decimal).should == :numeric
748
+ end
749
+
750
+ it 'should default to :country for :string columns named country' do
751
+ default_input_type(:string, :country).should == :country
752
+ end
753
+
754
+ describe 'defaulting to file column' do
755
+ Formtastic::SemanticFormBuilder.file_methods.each do |method|
756
+ it "should default to :file for attributes that respond to ##{method}" do
757
+ @new_post.stub!(:column_for_attribute).and_return(nil)
758
+ column = mock('column')
759
+
760
+ Formtastic::SemanticFormBuilder.file_methods.each do |test|
761
+ column.stub!(:respond_to?).with(test).and_return(method == test)
762
+ end
763
+
764
+ @new_post.should_receive(method).and_return(column)
765
+
766
+ semantic_form_for(@new_post) do |builder|
767
+ builder.send(:default_input_type, method).should == :file
768
+ end
769
+ end
770
+ end
771
+
772
+ end
773
+ end
774
+
775
+ it 'should call the corresponding input method' do
776
+ [:select, :time_zone, :radio, :date, :datetime, :time, :boolean, :check_boxes, :hidden].each do |input_style|
777
+ @new_post.stub!(:generic_column_name)
778
+ @new_post.stub!(:column_for_attribute).and_return(mock('column', :type => :string, :limit => 255))
779
+ semantic_form_for(@new_post) do |builder|
780
+ builder.should_receive(:"#{input_style}_input").once.and_return("fake HTML output from #input")
781
+ concat(builder.input(:generic_column_name, :as => input_style))
782
+ end
783
+ end
784
+
785
+ Formtastic::SemanticFormBuilder::INPUT_MAPPINGS.keys.each do |input_style|
786
+ @new_post.stub!(:generic_column_name)
787
+ @new_post.stub!(:column_for_attribute).and_return(mock('column', :type => :string, :limit => 255))
788
+ semantic_form_for(@new_post) do |builder|
789
+ builder.should_receive(:input_simple).once.and_return("fake HTML output from #input")
790
+ concat(builder.input(:generic_column_name, :as => input_style))
791
+ end
792
+ end
793
+ end
794
+
795
+ end
796
+
797
+ describe ':label option' do
798
+
799
+ describe 'when provided' do
800
+ it 'should be passed down to the label tag' do
801
+ semantic_form_for(@new_post) do |builder|
802
+ concat(builder.input(:title, :label => "Kustom"))
803
+ end
804
+ output_buffer.should have_tag("form li label", /Kustom/)
805
+ end
806
+
807
+ it 'should not generate a label if false' do
808
+ semantic_form_for(@new_post) do |builder|
809
+ concat(builder.input(:title, :label => false))
810
+ end
811
+ output_buffer.should_not have_tag("form li label")
812
+ end
813
+
814
+ it 'should be dupped if frozen' do
815
+ semantic_form_for(@new_post) do |builder|
816
+ concat(builder.input(:title, :label => "Kustom".freeze))
817
+ end
818
+ output_buffer.should have_tag("form li label", /Kustom/)
819
+ end
820
+ end
821
+
822
+ describe 'when not provided' do
823
+ describe 'when localized label is NOT provided' do
824
+ describe 'and object is not given' do
825
+ it 'should default the humanized method name, passing it down to the label tag' do
826
+ Formtastic::SemanticFormBuilder.label_str_method = :humanize
827
+
828
+ semantic_form_for(:project, :url => 'http://test.host') do |builder|
829
+ concat(builder.input(:meta_description))
830
+ end
831
+
832
+ output_buffer.should have_tag("form li label", /#{'meta_description'.humanize}/)
833
+ end
834
+ end
835
+
836
+ describe 'and object is given' do
837
+ it 'should delegate the label logic to class human attribute name and pass it down to the label tag' do
838
+ @new_post.stub!(:meta_description) # a two word method name
839
+ @new_post.class.should_receive(:human_attribute_name).with('meta_description').and_return('meta_description'.humanize)
840
+
841
+ semantic_form_for(@new_post) do |builder|
842
+ concat(builder.input(:meta_description))
843
+ end
844
+
845
+ output_buffer.should have_tag("form li label", /#{'meta_description'.humanize}/)
846
+ end
847
+ end
848
+ end
849
+
850
+ describe 'when localized label is provided' do
851
+ before do
852
+ @localized_label_text = 'Localized title'
853
+ @default_localized_label_text = 'Default localized title'
854
+ ::I18n.backend.store_translations :en,
855
+ :formtastic => {
856
+ :labels => {
857
+ :title => @default_localized_label_text,
858
+ :published => @default_localized_label_text,
859
+ :post => {
860
+ :title => @localized_label_text,
861
+ :published => @default_localized_label_text
862
+ }
863
+ }
864
+ }
865
+ ::Formtastic::SemanticFormBuilder.i18n_lookups_by_default = false
866
+ end
867
+
868
+ it 'should render a label with localized label (I18n)' do
869
+ semantic_form_for(@new_post) do |builder|
870
+ concat(builder.input(:title, :label => true))
871
+ concat(builder.input(:published, :as => :boolean, :label => true))
872
+ end
873
+ output_buffer.should have_tag('form li label', @localized_label_text)
874
+ end
875
+
876
+ it 'should render a hint paragraph containing an optional localized label (I18n) if first is not set' do
877
+ ::I18n.backend.store_translations :en,
878
+ :formtastic => {
879
+ :labels => {
880
+ :post => {
881
+ :title => nil,
882
+ :published => nil
883
+ }
884
+ }
885
+ }
886
+ semantic_form_for(@new_post) do |builder|
887
+ concat(builder.input(:title, :label => true))
888
+ concat(builder.input(:published, :as => :boolean, :label => true))
889
+ end
890
+ output_buffer.should have_tag('form li label', @default_localized_label_text)
891
+ end
892
+ end
893
+ end
894
+
895
+ end
896
+
897
+ describe ':hint option' do
898
+
899
+ describe 'when provided' do
900
+ it 'should be passed down to the paragraph tag' do
901
+ hint_text = "this is the title of the post"
902
+ semantic_form_for(@new_post) do |builder|
903
+ concat(builder.input(:title, :hint => hint_text))
904
+ end
905
+ output_buffer.should have_tag("form li p.inline-hints", hint_text)
906
+ end
907
+ end
908
+
909
+ describe 'when not provided' do
910
+ describe 'when localized hint (I18n) is provided' do
911
+ before do
912
+ @localized_hint_text = "This is the localized hint."
913
+ @default_localized_hint_text = "This is the default localized hint."
914
+ ::I18n.backend.store_translations :en,
915
+ :formtastic => {
916
+ :hints => {
917
+ :title => @default_localized_hint_text,
918
+ :post => {
919
+ :title => @localized_hint_text
920
+ }
921
+ }
922
+ }
923
+ ::Formtastic::SemanticFormBuilder.i18n_lookups_by_default = false
924
+ end
925
+
926
+ describe 'when provided value (hint value) is set to TRUE' do
927
+ it 'should render a hint paragraph containing a localized hint (I18n)' do
928
+ semantic_form_for(@new_post) do |builder|
929
+ concat(builder.input(:title, :hint => true))
930
+ end
931
+ output_buffer.should have_tag('form li p.inline-hints', @localized_hint_text)
932
+ end
933
+
934
+ it 'should render a hint paragraph containing an optional localized hint (I18n) if first is not set' do
935
+ ::I18n.backend.store_translations :en,
936
+ :formtastic => {
937
+ :hints => {
938
+ :post => {
939
+ :title => nil
940
+ }
941
+ }
942
+ }
943
+ semantic_form_for(@new_post) do |builder|
944
+ concat(builder.input(:title, :hint => true))
945
+ end
946
+ output_buffer.should have_tag('form li p.inline-hints', @default_localized_hint_text)
947
+ end
948
+ end
949
+
950
+ describe 'when provided value (label value) is set to FALSE' do
951
+ it 'should not render a hint paragraph' do
952
+ semantic_form_for(@new_post) do |builder|
953
+ concat(builder.input(:title, :hint => false))
954
+ end
955
+ output_buffer.should_not have_tag('form li p.inline-hints', @localized_hint_text)
956
+ end
957
+ end
958
+ end
959
+
960
+ describe 'when localized hint (I18n) is not provided' do
961
+ it 'should not render a hint paragraph' do
962
+ semantic_form_for(@new_post) do |builder|
963
+ concat(builder.input(:title))
964
+ end
965
+ output_buffer.should_not have_tag('form li p.inline-hints')
966
+ end
967
+ end
968
+ end
969
+
970
+ end
971
+
972
+ describe ':wrapper_html option' do
973
+
974
+ describe 'when provided' do
975
+ it 'should be passed down to the li tag' do
976
+ semantic_form_for(@new_post) do |builder|
977
+ concat(builder.input(:title, :wrapper_html => {:id => :another_id}))
978
+ end
979
+ output_buffer.should have_tag("form li#another_id")
980
+ end
981
+
982
+ it 'should append given classes to li default classes' do
983
+ semantic_form_for(@new_post) do |builder|
984
+ concat(builder.input(:title, :wrapper_html => {:class => :another_class}, :required => true))
985
+ end
986
+ output_buffer.should have_tag("form li.string")
987
+ output_buffer.should have_tag("form li.required")
988
+ output_buffer.should have_tag("form li.another_class")
989
+ end
990
+
991
+ it 'should allow classes to be an array' do
992
+ semantic_form_for(@new_post) do |builder|
993
+ concat(builder.input(:title, :wrapper_html => {:class => [ :my_class, :another_class ]}))
994
+ end
995
+ output_buffer.should have_tag("form li.string")
996
+ output_buffer.should have_tag("form li.my_class")
997
+ output_buffer.should have_tag("form li.another_class")
998
+ end
999
+ end
1000
+
1001
+ describe 'when not provided' do
1002
+ it 'should use default id and class' do
1003
+ semantic_form_for(@new_post) do |builder|
1004
+ concat(builder.input(:title))
1005
+ end
1006
+ output_buffer.should have_tag("form li#post_title_input")
1007
+ output_buffer.should have_tag("form li.string")
1008
+ end
1009
+ end
1010
+
1011
+ end
1012
+ end
1013
+
1014
+ describe ':as any type of input' do
1015
+
1016
+ it 'should create a list item for each input' do
1017
+ semantic_form_for(@new_post) do |builder|
1018
+ concat(builder.input(:title))
1019
+ concat(builder.input(:body))
1020
+ end
1021
+ output_buffer.should have_tag('form li', :count => 2)
1022
+ end
1023
+
1024
+ describe 'when there are errors on the object for this method' do
1025
+ before do
1026
+ @title_errors = ['must not be blank', 'must be longer than 10 characters', 'must be awesome']
1027
+ @errors = mock('errors')
1028
+ @errors.stub!(:[]).with(:title).and_return(@title_errors)
1029
+ @new_post.stub!(:errors).and_return(@errors)
1030
+ end
1031
+
1032
+ it 'should apply an errors class to the list item' do
1033
+ semantic_form_for(@new_post) do |builder|
1034
+ concat(builder.input(:title))
1035
+ end
1036
+ output_buffer.should have_tag('form li.error')
1037
+ end
1038
+
1039
+ it 'should not wrap the input with the Rails default error wrapping' do
1040
+ semantic_form_for(@new_post) do |builder|
1041
+ concat(builder.input(:title))
1042
+ end
1043
+ output_buffer.should_not have_tag('div.fieldWithErrors')
1044
+ end
1045
+
1046
+ it 'should render a paragraph for the errors' do
1047
+ Formtastic::SemanticFormBuilder.inline_errors = :sentence
1048
+ semantic_form_for(@new_post) do |builder|
1049
+ concat(builder.input(:title))
1050
+ end
1051
+ output_buffer.should have_tag('form li.error p.inline-errors')
1052
+ end
1053
+
1054
+ it 'should not display an error list' do
1055
+ Formtastic::SemanticFormBuilder.inline_errors = :list
1056
+ semantic_form_for(@new_post) do |builder|
1057
+ concat(builder.input(:title))
1058
+ end
1059
+ output_buffer.should have_tag('form li.error ul.errors')
1060
+ end
1061
+ end
1062
+
1063
+ describe 'when there are no errors on the object for this method' do
1064
+ before do
1065
+ semantic_form_for(@new_post) do |builder|
1066
+ concat(builder.input(:title))
1067
+ end
1068
+ end
1069
+
1070
+ it 'should not apply an errors class to the list item' do
1071
+ output_buffer.should_not have_tag('form li.error')
1072
+ end
1073
+
1074
+ it 'should not render a paragraph for the errors' do
1075
+ output_buffer.should_not have_tag('form li.error p.inline-errors')
1076
+ end
1077
+
1078
+ it 'should not display an error list' do
1079
+ output_buffer.should_not have_tag('form li.error ul.errors')
1080
+ end
1081
+ end
1082
+
1083
+ describe 'when no object is provided' do
1084
+ before do
1085
+ semantic_form_for(:project, :url => 'http://test.host') do |builder|
1086
+ concat(builder.input(:title))
1087
+ end
1088
+ end
1089
+
1090
+ it 'should not apply an errors class to the list item' do
1091
+ output_buffer.should_not have_tag('form li.error')
1092
+ end
1093
+
1094
+ it 'should not render a paragraph for the errors' do
1095
+ output_buffer.should_not have_tag('form li.error p.inline-errors')
1096
+ end
1097
+
1098
+ it 'should not display an error list' do
1099
+ output_buffer.should_not have_tag('form li.error ul.errors')
1100
+ end
1101
+ end
1102
+ end
1103
+
1104
+ # Test string_mappings: :string, :password and :numeric
1105
+ string_mappings = Formtastic::SemanticFormBuilder::INPUT_MAPPINGS.slice(*Formtastic::SemanticFormBuilder::STRING_MAPPINGS)
1106
+ string_mappings.each do |type, template_method|
1107
+ describe ":as => #{type.inspect}" do
1108
+
1109
+ before do
1110
+ @new_post.stub!(:title)
1111
+ @new_post.stub!(:column_for_attribute).and_return(mock('column', :type => type, :limit => 50))
1112
+
1113
+ semantic_form_for(@new_post) do |builder|
1114
+ concat(builder.input(:title, :as => type))
1115
+ end
1116
+ end
1117
+
1118
+ it "should have a #{type} class on the wrapper" do
1119
+ output_buffer.should have_tag("form li.#{type}")
1120
+ end
1121
+
1122
+ it 'should have a post_title_input id on the wrapper' do
1123
+ output_buffer.should have_tag('form li#post_title_input')
1124
+ end
1125
+
1126
+ it 'should generate a label for the input' do
1127
+ output_buffer.should have_tag('form li label')
1128
+ output_buffer.should have_tag('form li label[@for="post_title"]')
1129
+ output_buffer.should have_tag('form li label', /Title/)
1130
+ end
1131
+
1132
+ input_type = template_method.to_s.split('_').first
1133
+
1134
+ it "should generate a #{input_type} input" do
1135
+ output_buffer.should have_tag("form li input")
1136
+ output_buffer.should have_tag("form li input#post_title")
1137
+ output_buffer.should have_tag("form li input[@type=\"#{input_type}\"]")
1138
+ output_buffer.should have_tag("form li input[@name=\"post[title]\"]")
1139
+ end
1140
+
1141
+ unless type == :numeric
1142
+ it 'should have a maxlength matching the column limit' do
1143
+ @new_post.column_for_attribute(:title).limit.should == 50
1144
+ output_buffer.should have_tag("form li input[@maxlength='50']")
1145
+ end
1146
+
1147
+ it 'should use default_text_field_size for columns longer than default_text_field_size' do
1148
+ default_size = Formtastic::SemanticFormBuilder.default_text_field_size
1149
+ @new_post.stub!(:column_for_attribute).and_return(mock('column', :type => type, :limit => default_size * 2))
1150
+
1151
+ semantic_form_for(@new_post) do |builder|
1152
+ concat(builder.input(:title, :as => type))
1153
+ end
1154
+
1155
+ output_buffer.should have_tag("form li input[@size='#{default_size}']")
1156
+ end
1157
+
1158
+ it 'should use the column size for columns shorter than default_text_field_size' do
1159
+ column_limit_shorted_than_default = 1
1160
+ @new_post.stub!(:column_for_attribute).and_return(mock('column', :type => type, :limit => column_limit_shorted_than_default))
1161
+
1162
+ semantic_form_for(@new_post) do |builder|
1163
+ concat(builder.input(:title, :as => type))
1164
+ end
1165
+
1166
+ output_buffer.should have_tag("form li input[@size='#{column_limit_shorted_than_default}']")
1167
+ end
1168
+ end
1169
+
1170
+ it 'should use default_text_field_size for methods without database columns' do
1171
+ default_size = Formtastic::SemanticFormBuilder.default_text_field_size
1172
+ @new_post.stub!(:column_for_attribute).and_return(nil) # Return a nil column
1173
+
1174
+ semantic_form_for(@new_post) do |builder|
1175
+ concat(builder.input(:title, :as => type))
1176
+ end
1177
+
1178
+ output_buffer.should have_tag("form li input[@size='#{default_size}']")
1179
+ end
1180
+
1181
+ it 'should use input_html to style inputs' do
1182
+ semantic_form_for(@new_post) do |builder|
1183
+ concat(builder.input(:title, :as => type, :input_html => { :class => 'myclass' }))
1184
+ end
1185
+ output_buffer.should have_tag("form li input.myclass")
1186
+ end
1187
+
1188
+ it 'should consider input_html :id in labels' do
1189
+ semantic_form_for(@new_post) do |builder|
1190
+ concat(builder.input(:title, :as => type, :input_html => { :id => 'myid' }))
1191
+ end
1192
+ output_buffer.should have_tag('form li label[@for="myid"]')
1193
+ end
1194
+
1195
+ it 'should generate input and labels even if no object is given' do
1196
+ semantic_form_for(:project, :url => 'http://test.host/') do |builder|
1197
+ concat(builder.input(:title, :as => type))
1198
+ end
1199
+
1200
+ output_buffer.should have_tag('form li label')
1201
+ output_buffer.should have_tag('form li label[@for="project_title"]')
1202
+ output_buffer.should have_tag('form li label', /Title/)
1203
+
1204
+ output_buffer.should have_tag("form li input")
1205
+ output_buffer.should have_tag("form li input#project_title")
1206
+ output_buffer.should have_tag("form li input[@type=\"#{input_type}\"]")
1207
+ output_buffer.should have_tag("form li input[@name=\"project[title]\"]")
1208
+ end
1209
+
1210
+ end
1211
+ end
1212
+
1213
+ # Test other mappings that are not strings: :text and :file.
1214
+ other_mappings = Formtastic::SemanticFormBuilder::INPUT_MAPPINGS.except(*Formtastic::SemanticFormBuilder::STRING_MAPPINGS)
1215
+ other_mappings.each do |type, template_method|
1216
+ describe ":as => #{type.inspect}" do
1217
+
1218
+ before do
1219
+ @new_post.stub!(:body)
1220
+ @new_post.stub!(:column_for_attribute).and_return(mock('column', :type => type))
1221
+
1222
+ semantic_form_for(@new_post) do |builder|
1223
+ concat(builder.input(:body, :as => type))
1224
+ end
1225
+ end
1226
+
1227
+ it "should have a #{type} class on the wrapper" do
1228
+ output_buffer.should have_tag("form li.#{type}")
1229
+ end
1230
+
1231
+ it 'should have a post_title_input id on the wrapper' do
1232
+ output_buffer.should have_tag('form li#post_body_input')
1233
+ end
1234
+
1235
+ it 'should generate a label for the input' do
1236
+ output_buffer.should have_tag('form li label')
1237
+ output_buffer.should have_tag('form li label[@for="post_body"]')
1238
+ output_buffer.should have_tag('form li label', /Body/)
1239
+ end
1240
+
1241
+ input_type = template_method.to_s.gsub(/_field|_/, '')
1242
+
1243
+ if template_method.to_s =~ /_field$/ # password_field
1244
+
1245
+ it "should generate a #{input_type} input" do
1246
+ output_buffer.should have_tag("form li input")
1247
+ output_buffer.should have_tag("form li input#post_body")
1248
+ output_buffer.should have_tag("form li input[@name=\"post[body]\"]")
1249
+ output_buffer.should have_tag("form li input[@type=\"#{input_type}\"]")
1250
+ end
1251
+
1252
+ it 'should use input_html to style inputs' do
1253
+ semantic_form_for(@new_post) do |builder|
1254
+ concat(builder.input(:title, :as => type, :input_html => { :class => 'myclass' }))
1255
+ end
1256
+ output_buffer.should have_tag("form li input.myclass")
1257
+ end
1258
+
1259
+ else # text_area
1260
+
1261
+ it "should generate a #{input_type} input" do
1262
+ output_buffer.should have_tag("form li #{input_type}")
1263
+ output_buffer.should have_tag("form li #{input_type}#post_body")
1264
+ output_buffer.should have_tag("form li #{input_type}[@name=\"post[body]\"]")
1265
+ end
1266
+
1267
+ it 'should use input_html to style inputs' do
1268
+ semantic_form_for(@new_post) do |builder|
1269
+ concat(builder.input(:title, :as => type, :input_html => { :class => 'myclass' }))
1270
+ end
1271
+ output_buffer.should have_tag("form li #{input_type}.myclass")
1272
+ end
1273
+
1274
+ end
1275
+
1276
+ describe 'when no object is given' do
1277
+ before(:each) do
1278
+ semantic_form_for(:project, :url => 'http://test.host/') do |builder|
1279
+ concat(builder.input(:title, :as => type))
1280
+ end
1281
+ end
1282
+
1283
+ it 'should generate input' do
1284
+ if template_method.to_s =~ /_field$/ # password_field
1285
+ output_buffer.should have_tag("form li input")
1286
+ output_buffer.should have_tag("form li input#project_title")
1287
+ output_buffer.should have_tag("form li input[@type=\"#{input_type}\"]")
1288
+ output_buffer.should have_tag("form li input[@name=\"project[title]\"]")
1289
+ else
1290
+ output_buffer.should have_tag("form li #{input_type}")
1291
+ output_buffer.should have_tag("form li #{input_type}#project_title")
1292
+ output_buffer.should have_tag("form li #{input_type}[@name=\"project[title]\"]")
1293
+ end
1294
+ end
1295
+
1296
+ it 'should generate labels' do
1297
+ output_buffer.should have_tag('form li label')
1298
+ output_buffer.should have_tag('form li label[@for="project_title"]')
1299
+ output_buffer.should have_tag('form li label', /Title/)
1300
+ end
1301
+ end
1302
+
1303
+ end
1304
+ end
1305
+
1306
+ describe ":as => :hidden" do
1307
+ before do
1308
+ @new_post.stub!(:secret)
1309
+ @new_post.stub!(:column_for_attribute).and_return(mock('column', :type => :string))
1310
+
1311
+ semantic_form_for(@new_post) do |builder|
1312
+ concat(builder.input(:secret, :as => :hidden))
1313
+ end
1314
+ end
1315
+
1316
+ it "should have a hidden class on the wrapper" do
1317
+ output_buffer.should have_tag('form li.hidden')
1318
+ end
1319
+
1320
+ it 'should have a post_hidden_input id on the wrapper' do
1321
+ output_buffer.should have_tag('form li#post_secret_input')
1322
+ end
1323
+
1324
+ it 'should not generate a label for the input' do
1325
+ output_buffer.should_not have_tag('form li label')
1326
+ end
1327
+
1328
+ it "should generate a input field" do
1329
+ output_buffer.should have_tag("form li input#post_secret")
1330
+ output_buffer.should have_tag("form li input[@type=\"hidden\"]")
1331
+ output_buffer.should have_tag("form li input[@name=\"post[secret]\"]")
1332
+ end
1333
+
1334
+ it "should not render inline errors" do
1335
+ @errors = mock('errors')
1336
+ @errors.stub!(:[]).with(:secret).and_return(["foo", "bah"])
1337
+ @new_post.stub!(:errors).and_return(@errors)
1338
+
1339
+ semantic_form_for(@new_post) do |builder|
1340
+ concat(builder.input(:secret, :as => :hidden))
1341
+ end
1342
+
1343
+ output_buffer.should_not have_tag("form li p.inline-errors")
1344
+ output_buffer.should_not have_tag("form li ul.errors")
1345
+ end
1346
+
1347
+ end
1348
+
1349
+ describe ":as => :time_zone" do
1350
+ before do
1351
+ @new_post.stub!(:time_zone)
1352
+ @new_post.stub!(:column_for_attribute).and_return(mock('column', :type => :string))
1353
+
1354
+ semantic_form_for(@new_post) do |builder|
1355
+ concat(builder.input(:time_zone))
1356
+ end
1357
+ end
1358
+
1359
+ it "should have a time_zone class on the wrapper" do
1360
+ output_buffer.should have_tag('form li.time_zone')
1361
+ end
1362
+
1363
+ it 'should have a post_title_input id on the wrapper' do
1364
+ output_buffer.should have_tag('form li#post_time_zone_input')
1365
+ end
1366
+
1367
+ it 'should generate a label for the input' do
1368
+ output_buffer.should have_tag('form li label')
1369
+ output_buffer.should have_tag('form li label[@for="post_time_zone"]')
1370
+ output_buffer.should have_tag('form li label', /Time zone/)
1371
+ end
1372
+
1373
+ it "should generate a select" do
1374
+ output_buffer.should have_tag("form li select")
1375
+ output_buffer.should have_tag("form li select#post_time_zone")
1376
+ output_buffer.should have_tag("form li select[@name=\"post[time_zone]\"]")
1377
+ end
1378
+
1379
+ it 'should use input_html to style inputs' do
1380
+ semantic_form_for(@new_post) do |builder|
1381
+ concat(builder.input(:time_zone, :input_html => { :class => 'myclass' }))
1382
+ end
1383
+ output_buffer.should have_tag("form li select.myclass")
1384
+ end
1385
+
1386
+ describe 'when no object is given' do
1387
+ before(:each) do
1388
+ semantic_form_for(:project, :url => 'http://test.host/') do |builder|
1389
+ concat(builder.input(:time_zone, :as => :time_zone))
1390
+ end
1391
+ end
1392
+
1393
+ it 'should generate labels' do
1394
+ output_buffer.should have_tag('form li label')
1395
+ output_buffer.should have_tag('form li label[@for="project_time_zone"]')
1396
+ output_buffer.should have_tag('form li label', /Time zone/)
1397
+ end
1398
+
1399
+ it 'should generate select inputs' do
1400
+ output_buffer.should have_tag("form li select")
1401
+ output_buffer.should have_tag("form li select#project_time_zone")
1402
+ output_buffer.should have_tag("form li select[@name=\"project[time_zone]\"]")
1403
+ end
1404
+ end
1405
+ end
1406
+
1407
+ describe ":as => :country" do
1408
+
1409
+ before do
1410
+ @new_post.stub!(:country)
1411
+ @new_post.stub!(:column_for_attribute).and_return(mock('column', :type => :string))
1412
+ end
1413
+
1414
+ describe "when country_select is not available as a helper from a plugin" do
1415
+
1416
+ it "should raise an error, sugesting the author installs a plugin" do
1417
+ lambda {
1418
+ semantic_form_for(@new_post) do |builder|
1419
+ concat(builder.input(:country, :as => :country))
1420
+ end
1421
+ }.should raise_error
1422
+ end
1423
+
1424
+ end
1425
+
1426
+ describe "when country_select is available as a helper (from a plugin)" do
1427
+
1428
+ before do
1429
+ semantic_form_for(@new_post) do |builder|
1430
+ builder.stub!(:country_select).and_return("<select><option>...</option></select>")
1431
+ concat(builder.input(:country, :as => :country))
1432
+ end
1433
+ end
1434
+
1435
+ it "should have a time_zone class on the wrapper" do
1436
+ output_buffer.should have_tag('form li.country')
1437
+ end
1438
+
1439
+ it 'should have a post_title_input id on the wrapper' do
1440
+ output_buffer.should have_tag('form li#post_country_input')
1441
+ end
1442
+
1443
+ it 'should generate a label for the input' do
1444
+ output_buffer.should have_tag('form li label')
1445
+ output_buffer.should have_tag('form li label[@for="post_country"]')
1446
+ output_buffer.should have_tag('form li label', /Country/)
1447
+ end
1448
+
1449
+ it "should generate a select" do
1450
+ output_buffer.should have_tag("form li select")
1451
+ end
1452
+
1453
+ end
1454
+
1455
+ describe ":priority_countries option" do
1456
+
1457
+ it "should be passed down to the country_select helper when provided" do
1458
+ priority_countries = ["Foo", "Bah"]
1459
+ semantic_form_for(@new_post) do |builder|
1460
+ builder.stub!(:country_select).and_return("<select><option>...</option></select>")
1461
+ builder.should_receive(:country_select).with(:country, priority_countries, {}, {}).and_return("<select><option>...</option></select>")
1462
+
1463
+ concat(builder.input(:country, :as => :country, :priority_countries => priority_countries))
1464
+ end
1465
+ end
1466
+
1467
+ it "should default to the @@priority_countries config when absent" do
1468
+ priority_countries = Formtastic::SemanticFormBuilder.priority_countries
1469
+ priority_countries.should_not be_empty
1470
+ priority_countries.should_not be_nil
1471
+
1472
+ semantic_form_for(@new_post) do |builder|
1473
+ builder.stub!(:country_select).and_return("<select><option>...</option></select>")
1474
+ builder.should_receive(:country_select).with(:country, priority_countries, {}, {}).and_return("<select><option>...</option></select>")
1475
+
1476
+ concat(builder.input(:country, :as => :country))
1477
+ end
1478
+ end
1479
+
1480
+ end
1481
+
1482
+ end
1483
+
1484
+ describe ':as => :radio' do
1485
+
1486
+ before do
1487
+ @new_post.stub!(:author).and_return(@bob)
1488
+ @new_post.stub!(:author_id).and_return(@bob.id)
1489
+ ::Post.stub!(:reflect_on_association).and_return { |column_name| mock('reflection', :options => {}, :klass => ::Author, :macro => :belongs_to) }
1490
+ end
1491
+
1492
+ describe 'for belongs_to association' do
1493
+ before do
1494
+ semantic_form_for(@new_post) do |builder|
1495
+ concat(builder.input(:author, :as => :radio, :value_as_class => true))
1496
+ end
1497
+ end
1498
+
1499
+ it 'should have a radio class on the wrapper' do
1500
+ output_buffer.should have_tag('form li.radio')
1501
+ end
1502
+
1503
+ it 'should have a post_author_input id on the wrapper' do
1504
+ output_buffer.should have_tag('form li#post_author_input')
1505
+ end
1506
+
1507
+ it 'should generate a fieldset and legend containing label text for the input' do
1508
+ output_buffer.should have_tag('form li fieldset')
1509
+ output_buffer.should have_tag('form li fieldset legend')
1510
+ output_buffer.should have_tag('form li fieldset legend', /Author/)
1511
+ end
1512
+
1513
+ it 'should generate an ordered list with a list item for each choice' do
1514
+ output_buffer.should have_tag('form li fieldset ol')
1515
+ output_buffer.should have_tag('form li fieldset ol li', :count => ::Author.find(:all).size)
1516
+ end
1517
+
1518
+ it 'should have one option with a "checked" attribute' do
1519
+ output_buffer.should have_tag('form li input[@checked]', :count => 1)
1520
+ end
1521
+
1522
+ describe "each choice" do
1523
+
1524
+ it 'should contain a label for the radio input with a nested input and label text' do
1525
+ ::Author.find(:all).each do |author|
1526
+ output_buffer.should have_tag('form li fieldset ol li label', /#{author.to_label}/)
1527
+ output_buffer.should have_tag("form li fieldset ol li label[@for='post_author_id_#{author.id}']")
1528
+ end
1529
+ end
1530
+
1531
+ it 'should use values as li.class when value_as_class is true' do
1532
+ ::Author.find(:all).each do |author|
1533
+ output_buffer.should have_tag("form li fieldset ol li.#{author.id} label")
1534
+ end
1535
+ end
1536
+
1537
+ it "should have a radio input" do
1538
+ ::Author.find(:all).each do |author|
1539
+ output_buffer.should have_tag("form li fieldset ol li label input#post_author_id_#{author.id}")
1540
+ output_buffer.should have_tag("form li fieldset ol li label input[@type='radio']")
1541
+ output_buffer.should have_tag("form li fieldset ol li label input[@value='#{author.id}']")
1542
+ output_buffer.should have_tag("form li fieldset ol li label input[@name='post[author_id]']")
1543
+ end
1544
+ end
1545
+
1546
+ it "should mark input as checked if it's the the existing choice" do
1547
+ @new_post.author_id.should == @bob.id
1548
+ @new_post.author.id.should == @bob.id
1549
+ @new_post.author.should == @bob
1550
+
1551
+ semantic_form_for(@new_post) do |builder|
1552
+ concat(builder.input(:author, :as => :radio))
1553
+ end
1554
+
1555
+ output_buffer.should have_tag("form li fieldset ol li label input[@checked='checked']")
1556
+ end
1557
+ end
1558
+
1559
+ describe 'and no object is given' do
1560
+ before(:each) do
1561
+ output_buffer.replace ''
1562
+ semantic_form_for(:project, :url => 'http://test.host') do |builder|
1563
+ concat(builder.input(:author_id, :as => :radio, :collection => ::Author.find(:all)))
1564
+ end
1565
+ end
1566
+
1567
+ it 'should generate a fieldset with legend' do
1568
+ output_buffer.should have_tag('form li fieldset legend', /Author/)
1569
+ end
1570
+
1571
+ it 'should generate an li tag for each item in the collection' do
1572
+ output_buffer.should have_tag('form li fieldset ol li', :count => ::Author.find(:all).size)
1573
+ end
1574
+
1575
+ it 'should generate labels for each item' do
1576
+ ::Author.find(:all).each do |author|
1577
+ output_buffer.should have_tag('form li fieldset ol li label', /#{author.to_label}/)
1578
+ output_buffer.should have_tag("form li fieldset ol li label[@for='project_author_id_#{author.id}']")
1579
+ end
1580
+ end
1581
+
1582
+ it 'should generate inputs for each item' do
1583
+ ::Author.find(:all).each do |author|
1584
+ output_buffer.should have_tag("form li fieldset ol li label input#project_author_id_#{author.id}")
1585
+ output_buffer.should have_tag("form li fieldset ol li label input[@type='radio']")
1586
+ output_buffer.should have_tag("form li fieldset ol li label input[@value='#{author.id}']")
1587
+ output_buffer.should have_tag("form li fieldset ol li label input[@name='project[author_id]']")
1588
+ end
1589
+ end
1590
+ end
1591
+ end
1592
+ end
1593
+
1594
+ describe ':as => :select' do
1595
+
1596
+ before do
1597
+ @new_post.stub!(:author).and_return(@bob)
1598
+ @new_post.stub!(:author_id).and_return(@bob.id)
1599
+ @new_post.stub!(:column_for_attribute).and_return(mock('column', :type => :integer, :limit => 255))
1600
+ end
1601
+
1602
+ describe 'for a belongs_to association' do
1603
+ before do
1604
+ semantic_form_for(@new_post) do |builder|
1605
+ concat(builder.input(:author, :as => :select))
1606
+ end
1607
+ end
1608
+
1609
+ it 'should have a select class on the wrapper' do
1610
+ output_buffer.should have_tag('form li.select')
1611
+ end
1612
+
1613
+ it 'should have a post_author_input id on the wrapper' do
1614
+ output_buffer.should have_tag('form li#post_author_input')
1615
+ end
1616
+
1617
+ it 'should have a label inside the wrapper' do
1618
+ output_buffer.should have_tag('form li label')
1619
+ output_buffer.should have_tag('form li label', /Author/)
1620
+ output_buffer.should have_tag("form li label[@for='post_author_id']")
1621
+ end
1622
+
1623
+ it 'should have a select inside the wrapper' do
1624
+ output_buffer.should have_tag('form li select')
1625
+ output_buffer.should have_tag('form li select#post_author_id')
1626
+ end
1627
+
1628
+ it 'should not create a multi-select' do
1629
+ output_buffer.should_not have_tag('form li select[@multiple]')
1630
+ end
1631
+
1632
+ it 'should create a select without size' do
1633
+ output_buffer.should_not have_tag('form li select[@size]')
1634
+ end
1635
+
1636
+ it 'should have a blank option' do
1637
+ output_buffer.should have_tag("form li select option[@value='']")
1638
+ end
1639
+
1640
+ it 'should have a select option for each Author' do
1641
+ output_buffer.should have_tag('form li select option', :count => ::Author.find(:all).size + 1)
1642
+ ::Author.find(:all).each do |author|
1643
+ output_buffer.should have_tag("form li select option[@value='#{author.id}']", /#{author.to_label}/)
1644
+ end
1645
+ end
1646
+
1647
+ it 'should have one option with a "selected" attribute' do
1648
+ output_buffer.should have_tag('form li select option[@selected]', :count => 1)
1649
+ end
1650
+
1651
+ it 'should not singularize the association name' do
1652
+ @new_post.stub!(:author_status).and_return(@bob)
1653
+ @new_post.stub!(:author_status_id).and_return(@bob.id)
1654
+ @new_post.stub!(:column_for_attribute).and_return(mock('column', :type => :integer, :limit => 255))
1655
+
1656
+ semantic_form_for(@new_post) do |builder|
1657
+ concat(builder.input(:author_status, :as => :select))
1658
+ end
1659
+
1660
+ output_buffer.should have_tag('form li select#post_author_status_id')
1661
+ end
1662
+ end
1663
+
1664
+ describe 'for a has_many association' do
1665
+ before do
1666
+ semantic_form_for(@fred) do |builder|
1667
+ concat(builder.input(:posts, :as => :select))
1668
+ end
1669
+ end
1670
+
1671
+ it 'should have a select class on the wrapper' do
1672
+ output_buffer.should have_tag('form li.select')
1673
+ end
1674
+
1675
+ it 'should have a post_author_input id on the wrapper' do
1676
+ output_buffer.should have_tag('form li#author_posts_input')
1677
+ end
1678
+
1679
+ it 'should have a label inside the wrapper' do
1680
+ output_buffer.should have_tag('form li label')
1681
+ output_buffer.should have_tag('form li label', /Post/)
1682
+ output_buffer.should have_tag("form li label[@for='author_post_ids']")
1683
+ end
1684
+
1685
+ it 'should have a select inside the wrapper' do
1686
+ output_buffer.should have_tag('form li select')
1687
+ output_buffer.should have_tag('form li select#author_post_ids')
1688
+ end
1689
+
1690
+ it 'should have a multi-select select' do
1691
+ output_buffer.should have_tag('form li select[@multiple="multiple"]')
1692
+ end
1693
+
1694
+ it 'should have a select option for each Post' do
1695
+ output_buffer.should have_tag('form li select option', :count => ::Post.find(:all).size)
1696
+ ::Post.find(:all).each do |post|
1697
+ output_buffer.should have_tag("form li select option[@value='#{post.id}']", /#{post.to_label}/)
1698
+ end
1699
+ end
1700
+
1701
+ it 'should not have a blank option' do
1702
+ output_buffer.should_not have_tag("form li select option[@value='']")
1703
+ end
1704
+
1705
+ it 'should have one option with a "selected" attribute' do
1706
+ output_buffer.should have_tag('form li select option[@selected]', :count => 1)
1707
+ end
1708
+ end
1709
+
1710
+ describe 'for a has_and_belongs_to_many association' do
1711
+ before do
1712
+ semantic_form_for(@freds_post) do |builder|
1713
+ concat(builder.input(:authors, :as => :select))
1714
+ end
1715
+ end
1716
+
1717
+ it 'should have a select class on the wrapper' do
1718
+ output_buffer.should have_tag('form li.select')
1719
+ end
1720
+
1721
+ it 'should have a post_author_input id on the wrapper' do
1722
+ output_buffer.should have_tag('form li#post_authors_input')
1723
+ end
1724
+
1725
+ it 'should have a label inside the wrapper' do
1726
+ output_buffer.should have_tag('form li label')
1727
+ output_buffer.should have_tag('form li label', /Author/)
1728
+ output_buffer.should have_tag("form li label[@for='post_author_ids']")
1729
+ end
1730
+
1731
+ it 'should have a select inside the wrapper' do
1732
+ output_buffer.should have_tag('form li select')
1733
+ output_buffer.should have_tag('form li select#post_author_ids')
1734
+ end
1735
+
1736
+ it 'should have a multi-select select' do
1737
+ output_buffer.should have_tag('form li select[@multiple="multiple"]')
1738
+ end
1739
+
1740
+ it 'should have a select option for each Author' do
1741
+ output_buffer.should have_tag('form li select option', :count => ::Author.find(:all).size)
1742
+ ::Author.find(:all).each do |author|
1743
+ output_buffer.should have_tag("form li select option[@value='#{author.id}']", /#{author.to_label}/)
1744
+ end
1745
+ end
1746
+
1747
+ it 'should not have a blank option' do
1748
+ output_buffer.should_not have_tag("form li select option[@value='']")
1749
+ end
1750
+
1751
+ it 'should have one option with a "selected" attribute' do
1752
+ output_buffer.should have_tag('form li select option[@selected]', :count => 1)
1753
+ end
1754
+ end
1755
+
1756
+ describe 'when :include_blank is not set' do
1757
+ before do
1758
+ @new_post.stub!(:author_id).and_return(nil)
1759
+ end
1760
+
1761
+ it 'blank value should be included if the default value specified in config is true' do
1762
+ Formtastic::SemanticFormBuilder.include_blank_for_select_by_default = true
1763
+ semantic_form_for(@new_post) do |builder|
1764
+ concat(builder.input(:author, :as => :select))
1765
+ end
1766
+ output_buffer.should have_tag("form li select option[@value='']", "")
1767
+ end
1768
+
1769
+ it 'blank value should not be included if the default value specified in config is false' do
1770
+ Formtastic::SemanticFormBuilder.include_blank_for_select_by_default = false
1771
+ semantic_form_for(@new_post) do |builder|
1772
+ concat(builder.input(:author, :as => :select))
1773
+ end
1774
+ output_buffer.should_not have_tag("form li select option[@value='']", "")
1775
+ end
1776
+
1777
+ after do
1778
+ Formtastic::SemanticFormBuilder.include_blank_for_select_by_default = true
1779
+ end
1780
+ end
1781
+
1782
+ describe 'when :include_blank is set to false' do
1783
+ before do
1784
+ @new_post.stub!(:author_id).and_return(nil)
1785
+ semantic_form_for(@new_post) do |builder|
1786
+ concat(builder.input(:author, :as => :select, :include_blank => false))
1787
+ end
1788
+ end
1789
+
1790
+ it 'should not have a blank option' do
1791
+ output_buffer.should_not have_tag("form li select option[@value='']", "")
1792
+ end
1793
+ end
1794
+
1795
+ describe 'when :prompt => "choose something" is set' do
1796
+ before do
1797
+ @new_post.stub!(:author_id).and_return(nil)
1798
+ semantic_form_for(@new_post) do |builder|
1799
+ concat(builder.input(:author, :as => :select, :prompt => "choose author"))
1800
+ end
1801
+ end
1802
+
1803
+ it 'should have a select with prompt' do
1804
+ output_buffer.should have_tag("form li select option[@value='']", /choose author/)
1805
+ end
1806
+
1807
+ it 'should not have a blank select option' do
1808
+ output_buffer.should_not have_tag("form li select option[@value='']", "")
1809
+ end
1810
+ end
1811
+
1812
+ describe 'when no object is given' do
1813
+ before(:each) do
1814
+ semantic_form_for(:project, :url => 'http://test.host') do |builder|
1815
+ concat(builder.input(:author, :as => :select, :collection => ::Author.find(:all)))
1816
+ end
1817
+ end
1818
+
1819
+ it 'should generate label' do
1820
+ output_buffer.should have_tag('form li label', /Author/)
1821
+ output_buffer.should have_tag("form li label[@for='project_author']")
1822
+ end
1823
+
1824
+ it 'should generate select inputs' do
1825
+ output_buffer.should have_tag('form li select#project_author')
1826
+ output_buffer.should have_tag('form li select option', :count => ::Author.find(:all).size + 1)
1827
+ end
1828
+
1829
+ it 'should generate an option to each item' do
1830
+ ::Author.find(:all).each do |author|
1831
+ output_buffer.should have_tag("form li select option[@value='#{author.id}']", /#{author.to_label}/)
1832
+ end
1833
+ end
1834
+ end
1835
+ end
1836
+
1837
+ describe ':as => :check_boxes' do
1838
+
1839
+ describe 'for a has_many association' do
1840
+ before do
1841
+ semantic_form_for(@fred) do |builder|
1842
+ concat(builder.input(:posts, :as => :check_boxes, :value_as_class => true))
1843
+ end
1844
+ end
1845
+
1846
+ it 'should have a check_boxes class on the wrapper' do
1847
+ output_buffer.should have_tag('form li.check_boxes')
1848
+ end
1849
+
1850
+ it 'should have a author_posts_input id on the wrapper' do
1851
+ output_buffer.should have_tag('form li#author_posts_input')
1852
+ end
1853
+
1854
+ it 'should generate a fieldset and legend containing label text for the input' do
1855
+ output_buffer.should have_tag('form li fieldset')
1856
+ output_buffer.should have_tag('form li fieldset legend')
1857
+ output_buffer.should have_tag('form li fieldset legend', /Posts/)
1858
+ end
1859
+
1860
+ it 'should generate an ordered list with a list item for each choice' do
1861
+ output_buffer.should have_tag('form li fieldset ol')
1862
+ output_buffer.should have_tag('form li fieldset ol li', :count => ::Post.find(:all).size)
1863
+ end
1864
+
1865
+ it 'should have one option with a "checked" attribute' do
1866
+ output_buffer.should have_tag('form li input[@checked]', :count => 1)
1867
+ end
1868
+
1869
+ it 'should generate hidden inputs with default value blank' do
1870
+ output_buffer.should have_tag("form li fieldset ol li label input[@type='hidden'][@value='']", :count => ::Post.find(:all).size)
1871
+ end
1872
+
1873
+ describe "each choice" do
1874
+
1875
+ it 'should contain a label for the radio input with a nested input and label text' do
1876
+ ::Post.find(:all).each do |post|
1877
+ output_buffer.should have_tag('form li fieldset ol li label', /#{post.to_label}/)
1878
+ output_buffer.should have_tag("form li fieldset ol li label[@for='author_post_ids_#{post.id}']")
1879
+ end
1880
+ end
1881
+
1882
+ it 'should use values as li.class when value_as_class is true' do
1883
+ ::Post.find(:all).each do |post|
1884
+ output_buffer.should have_tag("form li fieldset ol li.#{post.id} label")
1885
+ end
1886
+ end
1887
+
1888
+ it 'should have a checkbox input for each post' do
1889
+ ::Post.find(:all).each do |post|
1890
+ output_buffer.should have_tag("form li fieldset ol li label input#author_post_ids_#{post.id}")
1891
+ output_buffer.should have_tag("form li fieldset ol li label input[@name='author[post_ids][]']", :count => 2)
1892
+ end
1893
+ end
1894
+
1895
+ it "should mark input as checked if it's the the existing choice" do
1896
+ ::Post.find(:all).include?(@fred.posts.first).should be_true
1897
+ output_buffer.should have_tag("form li fieldset ol li label input[@checked='checked']")
1898
+ end
1899
+ end
1900
+
1901
+ describe 'and no object is given' do
1902
+ before(:each) do
1903
+ output_buffer.replace ''
1904
+ semantic_form_for(:project, :url => 'http://test.host') do |builder|
1905
+ concat(builder.input(:author_id, :as => :check_boxes, :collection => ::Author.find(:all)))
1906
+ end
1907
+ end
1908
+
1909
+ it 'should generate a fieldset with legend' do
1910
+ output_buffer.should have_tag('form li fieldset legend', /Author/)
1911
+ end
1912
+
1913
+ it 'shold generate an li tag for each item in the collection' do
1914
+ output_buffer.should have_tag('form li fieldset ol li', :count => ::Author.find(:all).size)
1915
+ end
1916
+
1917
+ it 'should generate labels for each item' do
1918
+ ::Author.find(:all).each do |author|
1919
+ output_buffer.should have_tag('form li fieldset ol li label', /#{author.to_label}/)
1920
+ output_buffer.should have_tag("form li fieldset ol li label[@for='project_author_id_#{author.id}']")
1921
+ end
1922
+ end
1923
+
1924
+ it 'should generate inputs for each item' do
1925
+ ::Author.find(:all).each do |author|
1926
+ output_buffer.should have_tag("form li fieldset ol li label input#project_author_id_#{author.id}")
1927
+ output_buffer.should have_tag("form li fieldset ol li label input[@type='checkbox']")
1928
+ output_buffer.should have_tag("form li fieldset ol li label input[@value='#{author.id}']")
1929
+ output_buffer.should have_tag("form li fieldset ol li label input[@name='project[author_id][]']")
1930
+ end
1931
+ end
1932
+ end
1933
+ end
1934
+ end
1935
+
1936
+ describe 'for collections' do
1937
+
1938
+ before do
1939
+ @new_post.stub!(:author).and_return(@bob)
1940
+ @new_post.stub!(:author_id).and_return(@bob.id)
1941
+ @new_post.stub!(:column_for_attribute).and_return(mock('column', :type => :integer, :limit => 255))
1942
+ end
1943
+
1944
+ { :select => :option, :radio => :input, :check_boxes => :'input[@type="checkbox"]' }.each do |type, countable|
1945
+
1946
+ describe ":as => #{type.inspect}" do
1947
+ describe 'when the :collection option is not provided' do
1948
+ it 'should perform a basic find on the association class' do
1949
+ ::Author.should_receive(:find)
1950
+
1951
+ semantic_form_for(@new_post) do |builder|
1952
+ concat(builder.input(:author, :as => type))
1953
+ end
1954
+ end
1955
+
1956
+ it 'should show a deprecation warning if user gives the association using _id' do
1957
+ # Check for deprecation message
1958
+ ::ActiveSupport::Deprecation.should_receive(:warn).with(/association/, anything())
1959
+
1960
+ ::Author.should_receive(:find)
1961
+ semantic_form_for(@new_post) do |builder|
1962
+ concat(builder.input(:author_id, :as => type))
1963
+ end
1964
+ end
1965
+ end
1966
+
1967
+ describe 'when the :collection option is provided' do
1968
+
1969
+ before do
1970
+ @authors = ::Author.find(:all) * 2
1971
+ output_buffer.replace '' # clears the output_buffer from the before block, hax!
1972
+ end
1973
+
1974
+ it 'should not call find() on the parent class' do
1975
+ ::Author.should_not_receive(:find)
1976
+ semantic_form_for(@new_post) do |builder|
1977
+ concat(builder.input(:author, :as => type, :collection => @authors))
1978
+ end
1979
+ end
1980
+
1981
+ it 'should use the provided collection' do
1982
+ semantic_form_for(@new_post) do |builder|
1983
+ concat(builder.input(:author, :as => type, :collection => @authors))
1984
+ end
1985
+ output_buffer.should have_tag("form li.#{type} #{countable}", :count => @authors.size + (type == :select ? 1 : 0))
1986
+ end
1987
+
1988
+ describe 'and the :collection is an array of strings' do
1989
+ before do
1990
+ @new_post.stub!(:category_name).and_return('')
1991
+ @categories = [ 'General', 'Design', 'Development', 'Quasi-Serious Inventions' ]
1992
+ end
1993
+
1994
+ it "should use the string as the label text and value for each #{countable}" do
1995
+ semantic_form_for(@new_post) do |builder|
1996
+ concat(builder.input(:category_name, :as => type, :collection => @categories))
1997
+ end
1998
+
1999
+ @categories.each do |value|
2000
+ output_buffer.should have_tag("form li.#{type}", /#{value}/)
2001
+ output_buffer.should have_tag("form li.#{type} #{countable}[@value='#{value}']")
2002
+ end
2003
+ end
2004
+
2005
+ if type == :radio
2006
+ it 'should generate a sanitized label for attribute' do
2007
+ @bob.stub!(:category_name).and_return(@categories)
2008
+ semantic_form_for(@new_post) do |builder|
2009
+ builder.semantic_fields_for(@bob) do |bob_builder|
2010
+ concat(bob_builder.input(:category_name, :as => type, :collection => @categories))
2011
+ end
2012
+ end
2013
+ output_buffer.should have_tag("form li fieldset ol li label[@for='post_author_category_name_general']")
2014
+ output_buffer.should have_tag("form li fieldset ol li label[@for='post_author_category_name_design']")
2015
+ output_buffer.should have_tag("form li fieldset ol li label[@for='post_author_category_name_development']")
2016
+ output_buffer.should have_tag("form li fieldset ol li label[@for='post_author_category_name_quasiserious_inventions']")
2017
+ end
2018
+ end
2019
+ end
2020
+
2021
+ describe 'and the :collection is a hash of strings' do
2022
+ before do
2023
+ @new_post.stub!(:category_name).and_return('')
2024
+ @categories = { 'General' => 'gen', 'Design' => 'des','Development' => 'dev' }
2025
+ end
2026
+
2027
+ it "should use the key as the label text and the hash value as the value attribute for each #{countable}" do
2028
+ semantic_form_for(@new_post) do |builder|
2029
+ concat(builder.input(:category_name, :as => type, :collection => @categories))
2030
+ end
2031
+
2032
+ @categories.each do |label, value|
2033
+ output_buffer.should have_tag("form li.#{type}", /#{label}/)
2034
+ output_buffer.should have_tag("form li.#{type} #{countable}[@value='#{value}']")
2035
+ end
2036
+ end
2037
+ end
2038
+
2039
+ describe 'and the :collection is an array of arrays' do
2040
+ before do
2041
+ @new_post.stub!(:category_name).and_return('')
2042
+ @categories = { 'General' => 'gen', 'Design' => 'des', 'Development' => 'dev' }.to_a
2043
+ end
2044
+
2045
+ it "should use the first value as the label text and the last value as the value attribute for #{countable}" do
2046
+ semantic_form_for(@new_post) do |builder|
2047
+ concat(builder.input(:category_name, :as => type, :collection => @categories))
2048
+ end
2049
+
2050
+ @categories.each do |text, value|
2051
+ label = type == :select ? :option : :label
2052
+ output_buffer.should have_tag("form li.#{type} #{label}", /#{text}/i)
2053
+ output_buffer.should have_tag("form li.#{type} #{countable}[@value='#{value.to_s}']")
2054
+ output_buffer.should have_tag("form li.#{type} #{countable}#post_category_name_#{value.to_s}") if type == :radio
2055
+ end
2056
+ end
2057
+ end
2058
+
2059
+ if type == :radio
2060
+ describe 'and the :collection is an array of arrays with boolean values' do
2061
+ before do
2062
+ @new_post.stub!(:category_name).and_return('')
2063
+ @choices = { 'Yeah' => true, 'Nah' => false }.to_a
2064
+ end
2065
+
2066
+ it "should use the first value as the label text and the last value as the value attribute for #{countable}" do
2067
+ semantic_form_for(@new_post) do |builder|
2068
+ concat(builder.input(:category_name, :as => type, :collection => @choices))
2069
+ end
2070
+
2071
+ output_buffer.should have_tag("form li.#{type} #{countable}#post_category_name_true")
2072
+ output_buffer.should have_tag("form li.#{type} #{countable}#post_category_name_false")
2073
+ end
2074
+ end
2075
+ end
2076
+
2077
+
2078
+ describe 'and the :collection is an array of symbols' do
2079
+ before do
2080
+ @new_post.stub!(:category_name).and_return('')
2081
+ @categories = [ :General, :Design, :Development ]
2082
+ end
2083
+
2084
+ it "should use the symbol as the label text and value for each #{countable}" do
2085
+ semantic_form_for(@new_post) do |builder|
2086
+ concat(builder.input(:category_name, :as => type, :collection => @categories))
2087
+ end
2088
+
2089
+ @categories.each do |value|
2090
+ label = type == :select ? :option : :label
2091
+ output_buffer.should have_tag("form li.#{type} #{label}", /#{value}/i)
2092
+ output_buffer.should have_tag("form li.#{type} #{countable}[@value='#{value.to_s}']")
2093
+ end
2094
+ end
2095
+ end
2096
+
2097
+ describe 'and the :collection is an OrderedHash of strings' do
2098
+ before do
2099
+ @new_post.stub!(:category_name).and_return('')
2100
+ @categories = ActiveSupport::OrderedHash.new('General' => 'gen', 'Design' => 'des','Development' => 'dev')
2101
+ end
2102
+
2103
+ it "should use the key as the label text and the hash value as the value attribute for each #{countable}" do
2104
+ semantic_form_for(@new_post) do |builder|
2105
+ concat(builder.input(:category_name, :as => type, :collection => @categories))
2106
+ end
2107
+
2108
+ @categories.each do |label, value|
2109
+ output_buffer.should have_tag("form li.#{type}", /#{label}/)
2110
+ output_buffer.should have_tag("form li.#{type} #{countable}[@value='#{value}']")
2111
+ end
2112
+ end
2113
+
2114
+ end
2115
+
2116
+ describe 'when the :label_method option is provided' do
2117
+
2118
+ describe 'as a symbol' do
2119
+ before do
2120
+ semantic_form_for(@new_post) do |builder|
2121
+ concat(builder.input(:author, :as => type, :label_method => :login))
2122
+ end
2123
+ end
2124
+
2125
+ it 'should have options with text content from the specified method' do
2126
+ ::Author.find(:all).each do |author|
2127
+ output_buffer.should have_tag("form li.#{type}", /#{author.login}/)
2128
+ end
2129
+ end
2130
+ end
2131
+
2132
+ describe 'as a proc' do
2133
+ before do
2134
+ semantic_form_for(@new_post) do |builder|
2135
+ concat(builder.input(:author, :as => type, :label_method => Proc.new {|a| a.login.reverse }))
2136
+ end
2137
+ end
2138
+
2139
+ it 'should have options with the proc applied to each' do
2140
+ ::Author.find(:all).each do |author|
2141
+ output_buffer.should have_tag("form li.#{type}", /#{author.login.reverse}/)
2142
+ end
2143
+ end
2144
+ end
2145
+
2146
+ end
2147
+
2148
+ describe 'when the :label_method option is not provided' do
2149
+ Formtastic::SemanticFormBuilder.collection_label_methods.each do |label_method|
2150
+
2151
+ describe "when the collection objects respond to #{label_method}" do
2152
+ before do
2153
+ @fred.stub!(:respond_to?).and_return { |m| m.to_s == label_method }
2154
+ ::Author.find(:all).each { |a| a.stub!(label_method).and_return('The Label Text') }
2155
+
2156
+ semantic_form_for(@new_post) do |builder|
2157
+ concat(builder.input(:author, :as => type))
2158
+ end
2159
+ end
2160
+
2161
+ it "should render the options with #{label_method} as the label" do
2162
+ ::Author.find(:all).each do |author|
2163
+ output_buffer.should have_tag("form li.#{type}", /The Label Text/)
2164
+ end
2165
+ end
2166
+ end
2167
+
2168
+ end
2169
+ end
2170
+
2171
+ describe 'when the :value_method option is provided' do
2172
+
2173
+ describe 'as a symbol' do
2174
+ before do
2175
+ semantic_form_for(@new_post) do |builder|
2176
+ concat(builder.input(:author, :as => type, :value_method => :login))
2177
+ end
2178
+ end
2179
+
2180
+ it 'should have options with values from specified method' do
2181
+ ::Author.find(:all).each do |author|
2182
+ output_buffer.should have_tag("form li.#{type} #{countable}[@value='#{author.login}']")
2183
+ end
2184
+ end
2185
+ end
2186
+
2187
+ describe 'as a proc' do
2188
+ before do
2189
+ semantic_form_for(@new_post) do |builder|
2190
+ concat(builder.input(:author, :as => type, :value_method => Proc.new {|a| a.login.reverse }))
2191
+ end
2192
+ end
2193
+
2194
+ it 'should have options with the proc applied to each value' do
2195
+ ::Author.find(:all).each do |author|
2196
+ output_buffer.should have_tag("form li.#{type} #{countable}[@value='#{author.login.reverse}']")
2197
+ end
2198
+ end
2199
+ end
2200
+ end
2201
+
2202
+ end
2203
+ end
2204
+ end
2205
+
2206
+ describe 'for boolean attributes' do
2207
+
2208
+ { :select => :option, :radio => :input }.each do |type, countable|
2209
+ checked_or_selected = { :select => :selected, :radio => :checked }[type]
2210
+
2211
+ describe ":as => #{type.inspect}" do
2212
+
2213
+ before do
2214
+ @new_post.stub!(:allow_comments)
2215
+ @new_post.stub!(:column_for_attribute).and_return(mock('column', :type => :boolean))
2216
+
2217
+ semantic_form_for(@new_post) do |builder|
2218
+ concat(builder.input(:allow_comments, :as => type))
2219
+ end
2220
+ end
2221
+
2222
+ it "should have a #{type} class on the wrapper" do
2223
+ output_buffer.should have_tag("form li.#{type}")
2224
+ end
2225
+
2226
+ it 'should have a post_allow_comments_input id on the wrapper' do
2227
+ output_buffer.should have_tag('form li#post_allow_comments_input')
2228
+ end
2229
+
2230
+ it 'should generate a fieldset containing a legend' do
2231
+ output_buffer.should have_tag("form li.#{type}", /Allow comments/)
2232
+ end
2233
+
2234
+ it "should generate two #{countable}" do
2235
+ output_buffer.should have_tag("form li.#{type} #{countable}", :count => (type == :select ? 3 : 2))
2236
+ output_buffer.should have_tag(%{form li.#{type} #{countable}[@value="true"]})
2237
+ output_buffer.should have_tag(%{form li.#{type} #{countable}[@value="false"]})
2238
+ end
2239
+
2240
+ describe 'when the locale sets the label text' do
2241
+ before do
2242
+ I18n.backend.store_translations 'en', :formtastic => {:yes => 'Absolutely!', :no => 'Never!'}
2243
+
2244
+ semantic_form_for(@new_post) do |builder|
2245
+ concat(builder.input(:allow_comments, :as => type))
2246
+ end
2247
+ end
2248
+
2249
+ after do
2250
+ I18n.backend.store_translations 'en', :formtastic => {:yes => nil, :no => nil}
2251
+ end
2252
+
2253
+ it 'should allow translation of the labels' do
2254
+ output_buffer.should have_tag("form li.#{type}", /Absolutely\!/)
2255
+ output_buffer.should have_tag("form li.#{type}", /Never\!/)
2256
+ end
2257
+ end
2258
+
2259
+ describe 'when the value is nil' do
2260
+ before do
2261
+ @new_post.stub!(:allow_comments).and_return(nil)
2262
+ @new_post.stub!(:column_for_attribute).and_return(mock('column', :type => :boolean))
2263
+
2264
+ semantic_form_for(@new_post) do |builder|
2265
+ concat(builder.input(:allow_comments, :as => type))
2266
+ end
2267
+ end
2268
+
2269
+ it "should not mark either #{countable} as #{checked_or_selected}" do
2270
+ output_buffer.should_not have_tag(%{form li.#{type} input[@#{checked_or_selected}="#{checked_or_selected}"]})
2271
+ end
2272
+ end
2273
+
2274
+ describe 'when the value is true' do
2275
+ before do
2276
+ @new_post.stub!(:allow_comments).and_return(true)
2277
+ @new_post.stub!(:column_for_attribute).and_return(mock('column', :type => :boolean))
2278
+ semantic_form_for(@new_post) do |builder|
2279
+ concat(builder.input(:allow_comments, :as => type))
2280
+ end
2281
+ end
2282
+
2283
+ it "should mark the true #{countable} as #{checked_or_selected}" do
2284
+ output_buffer.should have_tag(%{form li.#{type} #{countable}[@value="true"][@#{checked_or_selected}="#{checked_or_selected}"]}, :count => 1)
2285
+ end
2286
+
2287
+ it "should not mark the false #{countable} as #{checked_or_selected}" do
2288
+ output_buffer.should_not have_tag(%{form li.#{type} #{countable}[@value="false"][@#{checked_or_selected}="#{checked_or_selected}"]})
2289
+ end
2290
+ end
2291
+
2292
+ describe 'when the value is false' do
2293
+ before do
2294
+ @new_post.stub!(:allow_comments).and_return(false)
2295
+ @new_post.stub!(:column_for_attribute).and_return(mock('column', :type => :boolean))
2296
+ semantic_form_for(@new_post) do |builder|
2297
+ concat(builder.input(:allow_comments, :as => type))
2298
+ end
2299
+ end
2300
+
2301
+ it "should not mark the true #{countable} as #{checked_or_selected}" do
2302
+ output_buffer.should_not have_tag(%{form li.#{type} #{countable}[@value="true"][@#{checked_or_selected}="#{checked_or_selected}"]})
2303
+ end
2304
+
2305
+ it "should mark the false #{countable} as #{checked_or_selected}" do
2306
+ output_buffer.should have_tag(%{form li.#{type} #{countable}[@value="false"][@#{checked_or_selected}="#{checked_or_selected}"]}, :count => 1)
2307
+ end
2308
+ end
2309
+
2310
+ describe 'when :true and :false options are provided' do
2311
+ before do
2312
+ @new_post.stub!(:allow_comments)
2313
+ @new_post.stub!(:column_for_attribute).and_return(mock('column', :type => :boolean))
2314
+ semantic_form_for(@new_post) do |builder|
2315
+ concat(builder.input(:allow_comments, :as => type, :true => "Absolutely", :false => "No Way"))
2316
+ end
2317
+ end
2318
+
2319
+ it 'should use them as labels' do
2320
+ output_buffer.should have_tag("form li.#{type}", /Absolutely/)
2321
+ output_buffer.should have_tag("form li.#{type}", /No Way/)
2322
+ end
2323
+ end
2324
+ end
2325
+
2326
+ end
2327
+ end
2328
+ end
2329
+
2330
+ describe ':as => :date' do
2331
+
2332
+ before do
2333
+ @new_post.stub!(:publish_at)
2334
+ @new_post.stub!(:column_for_attribute).and_return(mock('column', :type => :date))
2335
+
2336
+ semantic_form_for(@new_post) do |builder|
2337
+ concat(builder.input(:publish_at, :as => :date))
2338
+ @builder = builder
2339
+ end
2340
+ end
2341
+
2342
+ it 'should have a date class on the wrapper li' do
2343
+ output_buffer.should have_tag('form li.date')
2344
+ end
2345
+
2346
+ it 'should have a fieldset inside the li wrapper' do
2347
+ output_buffer.should have_tag('form li.date fieldset')
2348
+ end
2349
+
2350
+ it 'should have a legend containing the label text inside the fieldset' do
2351
+ output_buffer.should have_tag('form li.date fieldset legend', /Publish at/)
2352
+ end
2353
+
2354
+ it 'should have an ordered list of three items inside the fieldset' do
2355
+ output_buffer.should have_tag('form li.date fieldset ol')
2356
+ output_buffer.should have_tag('form li.date fieldset ol li', :count => 3)
2357
+ end
2358
+
2359
+ it 'should have three labels for year, month and day' do
2360
+ output_buffer.should have_tag('form li.date fieldset ol li label', :count => 3)
2361
+ output_buffer.should have_tag('form li.date fieldset ol li label', /year/i)
2362
+ output_buffer.should have_tag('form li.date fieldset ol li label', /month/i)
2363
+ output_buffer.should have_tag('form li.date fieldset ol li label', /day/i)
2364
+ end
2365
+
2366
+ it 'should have three selects for year, month and day' do
2367
+ output_buffer.should have_tag('form li.date fieldset ol li select', :count => 3)
2368
+ end
2369
+ end
2370
+
2371
+ describe ':as => :datetime' do
2372
+
2373
+ before do
2374
+ @new_post.stub!(:publish_at)
2375
+ @new_post.stub!(:column_for_attribute).and_return(mock('column', :type => :datetime))
2376
+
2377
+ semantic_form_for(@new_post) do |builder|
2378
+ concat(builder.input(:publish_at, :as => :datetime))
2379
+ end
2380
+ end
2381
+
2382
+ it 'should have a datetime class on the wrapper li' do
2383
+ output_buffer.should have_tag('form li.datetime')
2384
+ end
2385
+
2386
+ it 'should have a fieldset inside the li wrapper' do
2387
+ output_buffer.should have_tag('form li.datetime fieldset')
2388
+ end
2389
+
2390
+ it 'should have a legend containing the label text inside the fieldset' do
2391
+ output_buffer.should have_tag('form li.datetime fieldset legend', /Publish at/)
2392
+ end
2393
+
2394
+ it 'should have an ordered list of five items inside the fieldset' do
2395
+ output_buffer.should have_tag('form li.datetime fieldset ol')
2396
+ output_buffer.should have_tag('form li.datetime fieldset ol li', :count => 5)
2397
+ end
2398
+
2399
+ it 'should have five labels for year, month, day, hour and minute' do
2400
+ output_buffer.should have_tag('form li.datetime fieldset ol li label', :count => 5)
2401
+ output_buffer.should have_tag('form li.datetime fieldset ol li label', /year/i)
2402
+ output_buffer.should have_tag('form li.datetime fieldset ol li label', /month/i)
2403
+ output_buffer.should have_tag('form li.datetime fieldset ol li label', /day/i)
2404
+ output_buffer.should have_tag('form li.datetime fieldset ol li label', /hour/i)
2405
+ output_buffer.should have_tag('form li.datetime fieldset ol li label', /minute/i)
2406
+ end
2407
+
2408
+ it 'should have five selects for year, month, day, hour and minute' do
2409
+ output_buffer.should have_tag('form li.datetime fieldset ol li select', :count => 5)
2410
+ end
2411
+
2412
+ it 'should generate a sanitized label and matching ids for attribute' do
2413
+ @bob.stub!(:publish_at)
2414
+ @bob.stub!(:column_for_attribute).and_return(mock('column', :type => :datetime))
2415
+
2416
+ semantic_form_for(@new_post) do |builder|
2417
+ builder.semantic_fields_for(@bob, :index => 10) do |bob_builder|
2418
+ concat(bob_builder.input(:publish_at, :as => :datetime))
2419
+ end
2420
+ end
2421
+
2422
+ 1.upto(5) do |i|
2423
+ output_buffer.should have_tag("form li fieldset ol li label[@for='post_author_10_publish_at_#{i}i']")
2424
+ output_buffer.should have_tag("form li fieldset ol li #post_author_10_publish_at_#{i}i")
2425
+ end
2426
+ end
2427
+
2428
+ describe 'when :discard_input => true is set' do
2429
+ it 'should use default hidden value equals to 1 when attribute returns nil' do
2430
+ semantic_form_for(@new_post) do |builder|
2431
+ concat(builder.input(:publish_at, :as => :datetime, :discard_day => true))
2432
+ end
2433
+
2434
+ output_buffer.should have_tag("form li input[@type='hidden'][@value='1']")
2435
+ end
2436
+
2437
+ it 'should use default attribute value when it is not nil' do
2438
+ @new_post.stub!(:publish_at).and_return(Date.new(2007,12,27))
2439
+ semantic_form_for(@new_post) do |builder|
2440
+ concat(builder.input(:publish_at, :as => :datetime, :discard_day => true))
2441
+ end
2442
+
2443
+ output_buffer.should have_tag("form li input[@type='hidden'][@value='27']")
2444
+ end
2445
+ end
2446
+
2447
+ describe 'when :include_blank => true is set' do
2448
+ before do
2449
+ semantic_form_for(@new_post) do |builder|
2450
+ concat(builder.input(:publish_at, :as => :datetime, :include_blank => true))
2451
+ end
2452
+ end
2453
+
2454
+ it 'should have a blank select option' do
2455
+ output_buffer.should have_tag("option[@value='']", "")
2456
+ end
2457
+ end
2458
+
2459
+ describe 'inputs order' do
2460
+ it 'should have a default' do
2461
+ semantic_form_for(@new_post) do |builder|
2462
+ self.should_receive(:select_year).once.ordered.and_return('')
2463
+ self.should_receive(:select_month).once.ordered.and_return('')
2464
+ self.should_receive(:select_day).once.ordered.and_return('')
2465
+ builder.input(:publish_at, :as => :datetime)
2466
+ end
2467
+ end
2468
+
2469
+ it 'should be specified with :order option' do
2470
+ I18n.backend.store_translations 'en', :date => { :order => [:month, :year, :day] }
2471
+ semantic_form_for(@new_post) do |builder|
2472
+ self.should_receive(:select_month).once.ordered.and_return('')
2473
+ self.should_receive(:select_year).once.ordered.and_return('')
2474
+ self.should_receive(:select_day).once.ordered.and_return('')
2475
+ builder.input(:publish_at, :as => :datetime)
2476
+ end
2477
+ end
2478
+
2479
+ it 'should be changed through I18n' do
2480
+ semantic_form_for(@new_post) do |builder|
2481
+ self.should_receive(:select_day).once.ordered.and_return('')
2482
+ self.should_receive(:select_month).once.ordered.and_return('')
2483
+ self.should_receive(:select_year).once.ordered.and_return('')
2484
+ builder.input(:publish_at, :as => :datetime, :order => [:day, :month, :year])
2485
+ end
2486
+ end
2487
+ end
2488
+
2489
+ describe 'when the locale changes the label text' do
2490
+ before do
2491
+ I18n.backend.store_translations 'en', :datetime => {:prompts => {
2492
+ :year => 'The Year', :month => 'The Month', :day => 'The Day',
2493
+ :hour => 'The Hour', :minute => 'The Minute'
2494
+ }}
2495
+ semantic_form_for(@new_post) do |builder|
2496
+ concat(builder.input(:publish_at, :as => :datetime))
2497
+ end
2498
+ end
2499
+
2500
+ after do
2501
+ I18n.backend.store_translations 'en', :formtastic => {
2502
+ :year => nil, :month => nil, :day => nil,
2503
+ :hour => nil, :minute => nil
2504
+ }
2505
+ end
2506
+
2507
+ it 'should have translated labels for year, month, day, hour and minute' do
2508
+ output_buffer.should have_tag('form li.datetime fieldset ol li label', /The Year/)
2509
+ output_buffer.should have_tag('form li.datetime fieldset ol li label', /The Month/)
2510
+ output_buffer.should have_tag('form li.datetime fieldset ol li label', /The Day/)
2511
+ output_buffer.should have_tag('form li.datetime fieldset ol li label', /The Hour/)
2512
+ output_buffer.should have_tag('form li.datetime fieldset ol li label', /The Minute/)
2513
+ end
2514
+ end
2515
+
2516
+ describe 'when no object is given' do
2517
+ before(:each) do
2518
+ output_buffer.replace ''
2519
+ semantic_form_for(:project, :url => 'http://test.host') do |builder|
2520
+ concat(builder.input(:publish_at, :as => :datetime))
2521
+ @builder = builder
2522
+ end
2523
+ end
2524
+
2525
+ it 'should have fieldset with legend' do
2526
+ output_buffer.should have_tag('form li.datetime fieldset legend', /Publish at/)
2527
+ end
2528
+
2529
+ it 'should have labels for each input' do
2530
+ output_buffer.should have_tag('form li.datetime fieldset ol li label', :count => 5)
2531
+ end
2532
+
2533
+ it 'should have selects for each inputs' do
2534
+ output_buffer.should have_tag('form li.datetime fieldset ol li select', :count => 5)
2535
+ end
2536
+ end
2537
+ end
2538
+
2539
+ describe ':as => :time' do
2540
+ before do
2541
+ @new_post.stub!(:publish_at)
2542
+ @new_post.stub!(:column_for_attribute).and_return(mock('column', :type => :time))
2543
+
2544
+ semantic_form_for(@new_post) do |builder|
2545
+ concat(builder.input(:publish_at, :as => :time))
2546
+ end
2547
+ end
2548
+
2549
+ it 'should have a time class on the wrapper li' do
2550
+ output_buffer.should have_tag('form li.time')
2551
+ end
2552
+
2553
+ it 'should have a fieldset inside the li wrapper' do
2554
+ output_buffer.should have_tag('form li.time fieldset')
2555
+ end
2556
+
2557
+ it 'should have a legend containing the label text inside the fieldset' do
2558
+ output_buffer.should have_tag('form li.time fieldset legend', /Publish at/)
2559
+ end
2560
+
2561
+ it 'should have an ordered list of two items inside the fieldset' do
2562
+ output_buffer.should have_tag('form li.time fieldset ol')
2563
+ output_buffer.should have_tag('form li.time fieldset ol li', :count => 2)
2564
+ end
2565
+
2566
+ it 'should have five labels for hour and minute' do
2567
+ output_buffer.should have_tag('form li.time fieldset ol li label', :count => 2)
2568
+ output_buffer.should have_tag('form li.time fieldset ol li label', /hour/i)
2569
+ output_buffer.should have_tag('form li.time fieldset ol li label', /minute/i)
2570
+ end
2571
+
2572
+ it 'should have two selects for hour and minute' do
2573
+ #output_buffer.should have_tag('form li.time fieldset ol li select', :count => 2)
2574
+ output_buffer.should have_tag('form li.time fieldset ol li', :count => 2)
2575
+ end
2576
+ end
2577
+
2578
+ [:boolean_select, :boolean_radio].each do |type|
2579
+ describe ":as => #{type.inspect}" do
2580
+ it 'should show a deprecation warning' do
2581
+ @new_post.stub!(:allow_comments)
2582
+ @new_post.stub!(:column_for_attribute).and_return(mock('column', :type => :boolean))
2583
+
2584
+ ::ActiveSupport::Deprecation.should_receive(:warn).with(/select|radio/, anything())
2585
+
2586
+ semantic_form_for(@new_post) do |builder|
2587
+ concat(builder.input(:allow_comments, :as => type))
2588
+ end
2589
+ end
2590
+ end
2591
+ end
2592
+
2593
+ describe ':as => :boolean' do
2594
+
2595
+ before do
2596
+ @new_post.stub!(:allow_comments)
2597
+ @new_post.stub!(:column_for_attribute).and_return(mock('column', :type => :boolean))
2598
+
2599
+ semantic_form_for(@new_post) do |builder|
2600
+ concat(builder.input(:allow_comments, :as => :boolean))
2601
+ end
2602
+ end
2603
+
2604
+ it 'should have a boolean class on the wrapper' do
2605
+ output_buffer.should have_tag('form li.boolean')
2606
+ end
2607
+
2608
+ it 'should have a post_allow_comments_input id on the wrapper' do
2609
+ output_buffer.should have_tag('form li#post_allow_comments_input')
2610
+ end
2611
+
2612
+ it 'should generate a label containing the input' do
2613
+ output_buffer.should have_tag('form li label')
2614
+ output_buffer.should have_tag('form li label[@for="post_allow_comments"]')
2615
+ output_buffer.should have_tag('form li label', /Allow comments/)
2616
+ output_buffer.should have_tag('form li label input[@type="checkbox"]')
2617
+ end
2618
+
2619
+ it 'should generate a checkbox input' do
2620
+ output_buffer.should have_tag('form li label input')
2621
+ output_buffer.should have_tag('form li label input#post_allow_comments')
2622
+ output_buffer.should have_tag('form li label input[@type="checkbox"]')
2623
+ output_buffer.should have_tag('form li label input[@name="post[allow_comments]"]')
2624
+ output_buffer.should have_tag('form li label input[@type="checkbox"][@value="1"]')
2625
+ end
2626
+
2627
+ it 'should allow checked and unchecked values to be sent' do
2628
+ semantic_form_for(@new_post) do |builder|
2629
+ concat(builder.input(:allow_comments, :as => :boolean, :checked_value => 'checked', :unchecked_value => 'unchecked'))
2630
+ end
2631
+
2632
+ output_buffer.should have_tag('form li label input[@type="checkbox"][@value="checked"]')
2633
+ output_buffer.should have_tag('form li label input[@type="hidden"][@value="unchecked"]')
2634
+ end
2635
+
2636
+ it 'should generate a label and a checkbox even if no object is given' do
2637
+ semantic_form_for(:project, :url => 'http://test.host') do |builder|
2638
+ concat(builder.input(:allow_comments, :as => :boolean))
2639
+ end
2640
+
2641
+ output_buffer.should have_tag('form li label[@for="project_allow_comments"]')
2642
+ output_buffer.should have_tag('form li label', /Allow comments/)
2643
+ output_buffer.should have_tag('form li label input[@type="checkbox"]')
2644
+
2645
+ output_buffer.should have_tag('form li label input#project_allow_comments')
2646
+ output_buffer.should have_tag('form li label input[@type="checkbox"]')
2647
+ output_buffer.should have_tag('form li label input[@name="project[allow_comments]"]')
2648
+ end
2649
+
2650
+ end
2651
+ end
2652
+
2653
+ describe '#inputs' do
2654
+
2655
+ describe 'with a block' do
2656
+
2657
+ describe 'when no options are provided' do
2658
+ before do
2659
+ output_buffer.replace 'before_builder' # clear the output buffer and sets before_builder
2660
+ semantic_form_for(@new_post) do |builder|
2661
+ @inputs_output = builder.inputs do
2662
+ concat('hello')
2663
+ end
2664
+ end
2665
+ end
2666
+
2667
+ it 'should output just the content wrapped in inputs, not the whole template' do
2668
+ output_buffer.should =~ /before_builder/
2669
+ @inputs_output.should_not =~ /before_builder/
2670
+ end
2671
+
2672
+ it 'should render a fieldset inside the form, with a class of "inputs"' do
2673
+ output_buffer.should have_tag("form fieldset.inputs")
2674
+ end
2675
+
2676
+ it 'should render an ol inside the fieldset' do
2677
+ output_buffer.should have_tag("form fieldset.inputs ol")
2678
+ end
2679
+
2680
+ it 'should render the contents of the block inside the ol' do
2681
+ output_buffer.should have_tag("form fieldset.inputs ol", /hello/)
2682
+ end
2683
+
2684
+ it 'should not render a legend inside the fieldset' do
2685
+ output_buffer.should_not have_tag("form fieldset.inputs legend")
2686
+ end
2687
+
2688
+ it 'should render a fieldset even if no object is given' do
2689
+ semantic_form_for(:project, :url => 'http://test.host/') do |builder|
2690
+ @inputs_output = builder.inputs do
2691
+ concat('bye')
2692
+ end
2693
+ end
2694
+
2695
+ output_buffer.should have_tag("form fieldset.inputs ol", /bye/)
2696
+ end
2697
+ end
2698
+
2699
+ describe 'when a :for option is provided' do
2700
+ it 'should render nested inputs' do
2701
+ @bob.stub!(:column_for_attribute).and_return(mock('column', :type => :string, :limit => 255))
2702
+
2703
+ semantic_form_for(@new_post) do |builder|
2704
+ builder.inputs :for => [:author, @bob] do |bob_builder|
2705
+ concat(bob_builder.input(:login))
2706
+ end
2707
+ end
2708
+
2709
+ output_buffer.should have_tag("form fieldset.inputs #post_author_login")
2710
+ output_buffer.should_not have_tag("form fieldset.inputs #author_login")
2711
+ end
2712
+
2713
+ it 'should raise an error if :for and block with no argument is given' do
2714
+ semantic_form_for(@new_post) do |builder|
2715
+ proc {
2716
+ builder.inputs(:for => [:author, @bob]) do
2717
+ #
2718
+ end
2719
+ }.should raise_error(ArgumentError, 'You gave :for option with a block to inputs method, ' <<
2720
+ 'but the block does not accept any argument.')
2721
+ end
2722
+ end
2723
+
2724
+ it 'should pass options down to semantic_fields_for' do
2725
+ @bob.stub!(:column_for_attribute).and_return(mock('column', :type => :string, :limit => 255))
2726
+
2727
+ semantic_form_for(@new_post) do |builder|
2728
+ builder.inputs :for => [:author, @bob], :for_options => { :index => 10 } do |bob_builder|
2729
+ concat(bob_builder.input(:login))
2730
+ end
2731
+ end
2732
+
2733
+ output_buffer.should have_tag('form fieldset ol li #post_author_10_login')
2734
+ end
2735
+
2736
+ it 'should not add builder as a fieldset attribute tag' do
2737
+ semantic_form_for(@new_post) do |builder|
2738
+ builder.inputs :for => [:author, @bob], :for_options => { :index => 10 } do |bob_builder|
2739
+ concat('input')
2740
+ end
2741
+ end
2742
+
2743
+ output_buffer.should_not have_tag('fieldset[@builder="Formtastic::SemanticFormHelper"]')
2744
+ end
2745
+
2746
+ it 'should send parent_builder as an option to allow child index interpolation' do
2747
+ semantic_form_for(@new_post) do |builder|
2748
+ builder.instance_variable_set('@nested_child_index', 0)
2749
+ builder.inputs :for => [:author, @bob], :name => 'Author #%i' do |bob_builder|
2750
+ concat('input')
2751
+ end
2752
+ end
2753
+
2754
+ output_buffer.should have_tag('fieldset legend', 'Author #1')
2755
+ end
2756
+
2757
+ it 'should also provide child index interpolation when nested child index is a hash' do
2758
+ semantic_form_for(@new_post) do |builder|
2759
+ builder.instance_variable_set('@nested_child_index', :author => 10)
2760
+ builder.inputs :for => [:author, @bob], :name => 'Author #%i' do |bob_builder|
2761
+ concat('input')
2762
+ end
2763
+ end
2764
+
2765
+ output_buffer.should have_tag('fieldset legend', 'Author #11')
2766
+ end
2767
+ end
2768
+
2769
+ describe 'when a :name or :title option is provided' do
2770
+ describe 'and is a string' do
2771
+ before do
2772
+ @legend_text = "Advanced options"
2773
+ @legend_text_using_title = "Advanced options 2"
2774
+ semantic_form_for(@new_post) do |builder|
2775
+ builder.inputs :name => @legend_text do
2776
+ end
2777
+ builder.inputs :title => @legend_text_using_title do
2778
+ end
2779
+ end
2780
+ end
2781
+
2782
+ it 'should render a fieldset with a legend inside the form' do
2783
+ output_buffer.should have_tag("form fieldset legend", /#{@legend_text}/)
2784
+ output_buffer.should have_tag("form fieldset legend", /#{@legend_text_using_title}/)
2785
+ end
2786
+ end
2787
+
2788
+ describe 'and is a symbol' do
2789
+ before do
2790
+ @localized_legend_text = "Localized advanced options"
2791
+ @localized_legend_text_using_title = "Localized advanced options 2"
2792
+ I18n.backend.store_translations :en, :formtastic => {
2793
+ :titles => {
2794
+ :post => {
2795
+ :advanced_options => @localized_legend_text,
2796
+ :advanced_options_2 => @localized_legend_text_using_title
2797
+ }
2798
+ }
2799
+ }
2800
+ semantic_form_for(@new_post) do |builder|
2801
+ builder.inputs :name => :advanced_options do
2802
+ end
2803
+ builder.inputs :title => :advanced_options_2 do
2804
+ end
2805
+ end
2806
+ end
2807
+
2808
+ it 'should render a fieldset with a localized legend inside the form' do
2809
+ output_buffer.should have_tag("form fieldset legend", /#{@localized_legend_text}/)
2810
+ output_buffer.should have_tag("form fieldset legend", /#{@localized_legend_text_using_title}/)
2811
+ end
2812
+ end
2813
+ end
2814
+
2815
+ describe 'when other options are provided' do
2816
+ before do
2817
+ @id_option = 'advanced'
2818
+ @class_option = 'wide'
2819
+
2820
+ semantic_form_for(@new_post) do |builder|
2821
+ builder.inputs :id => @id_option, :class => @class_option do
2822
+ end
2823
+ end
2824
+ end
2825
+
2826
+ it 'should pass the options into the fieldset tag as attributes' do
2827
+ output_buffer.should have_tag("form fieldset##{@id_option}")
2828
+ output_buffer.should have_tag("form fieldset.#{@class_option}")
2829
+ end
2830
+ end
2831
+
2832
+ end
2833
+
2834
+ describe 'without a block' do
2835
+
2836
+ before do
2837
+ ::Post.stub!(:reflections).and_return({:author => mock('reflection', :options => {}, :macro => :belongs_to),
2838
+ :comments => mock('reflection', :options => {}, :macro => :has_many) })
2839
+ ::Post.stub!(:content_columns).and_return([mock('column', :name => 'title'), mock('column', :name => 'body'), mock('column', :name => 'created_at')])
2840
+ ::Author.stub!(:find).and_return([@fred, @bob])
2841
+
2842
+ @new_post.stub!(:title)
2843
+ @new_post.stub!(:body)
2844
+ @new_post.stub!(:author_id)
2845
+
2846
+ @new_post.stub!(:column_for_attribute).with(:title).and_return(mock('column', :type => :string, :limit => 255))
2847
+ @new_post.stub!(:column_for_attribute).with(:body).and_return(mock('column', :type => :text))
2848
+ @new_post.stub!(:column_for_attribute).with(:created_at).and_return(mock('column', :type => :datetime))
2849
+ @new_post.stub!(:column_for_attribute).with(:author).and_return(nil)
2850
+ end
2851
+
2852
+ describe 'with no args' do
2853
+ before do
2854
+ semantic_form_for(@new_post) do |builder|
2855
+ concat(builder.inputs)
2856
+ end
2857
+ end
2858
+
2859
+ it 'should render a form' do
2860
+ output_buffer.should have_tag('form')
2861
+ end
2862
+
2863
+ it 'should render a fieldset inside the form' do
2864
+ output_buffer.should have_tag('form > fieldset.inputs')
2865
+ end
2866
+
2867
+ it 'should not render a legend in the fieldset' do
2868
+ output_buffer.should_not have_tag('form > fieldset.inputs > legend')
2869
+ end
2870
+
2871
+ it 'should render an ol in the fieldset' do
2872
+ output_buffer.should have_tag('form > fieldset.inputs > ol')
2873
+ end
2874
+
2875
+ it 'should render a list item in the ol for each column and reflection' do
2876
+ # Remove the :has_many macro and :created_at column
2877
+ count = ::Post.content_columns.size + ::Post.reflections.size - 2
2878
+ output_buffer.should have_tag('form > fieldset.inputs > ol > li', :count => count)
2879
+ end
2880
+
2881
+ it 'should render a string list item for title' do
2882
+ output_buffer.should have_tag('form > fieldset.inputs > ol > li.string')
2883
+ end
2884
+
2885
+ it 'should render a text list item for body' do
2886
+ output_buffer.should have_tag('form > fieldset.inputs > ol > li.text')
2887
+ end
2888
+
2889
+ it 'should render a select list item for author_id' do
2890
+ output_buffer.should have_tag('form > fieldset.inputs > ol > li.select', :count => 1)
2891
+ end
2892
+
2893
+ it 'should not render timestamps inputs by default' do
2894
+ output_buffer.should_not have_tag('form > fieldset.inputs > ol > li.datetime')
2895
+ end
2896
+ end
2897
+
2898
+ describe 'with column names as args' do
2899
+ describe 'and an object is given' do
2900
+ it 'should render a form with a fieldset containing two list items' do
2901
+ semantic_form_for(@new_post) do |builder|
2902
+ concat(builder.inputs(:title, :body))
2903
+ end
2904
+
2905
+ output_buffer.should have_tag('form > fieldset.inputs > ol > li', :count => 2)
2906
+ output_buffer.should have_tag('form > fieldset.inputs > ol > li.string')
2907
+ output_buffer.should have_tag('form > fieldset.inputs > ol > li.text')
2908
+ end
2909
+ end
2910
+
2911
+ describe 'and no object is given' do
2912
+ it 'should render a form with a fieldset containing two list items' do
2913
+ semantic_form_for(:project, :url => 'http://test.host') do |builder|
2914
+ concat(builder.inputs(:title, :body))
2915
+ end
2916
+
2917
+ output_buffer.should have_tag('form > fieldset.inputs > ol > li.string', :count => 2)
2918
+ end
2919
+ end
2920
+ end
2921
+
2922
+ describe 'when a :for option is provided' do
2923
+ describe 'and an object is given' do
2924
+ it 'should render nested inputs' do
2925
+ @bob.stub!(:column_for_attribute).and_return(mock('column', :type => :string, :limit => 255))
2926
+
2927
+ semantic_form_for(@new_post) do |builder|
2928
+ concat(builder.inputs(:login, :for => @bob))
2929
+ end
2930
+
2931
+ output_buffer.should have_tag("form fieldset.inputs #post_author_login")
2932
+ output_buffer.should_not have_tag("form fieldset.inputs #author_login")
2933
+ end
2934
+ end
2935
+
2936
+ describe 'and no object is given' do
2937
+ it 'should render nested inputs' do
2938
+ semantic_form_for(:project, :url => 'http://test.host/') do |builder|
2939
+ concat(builder.inputs(:login, :for => @bob))
2940
+ end
2941
+
2942
+ output_buffer.should have_tag("form fieldset.inputs #project_author_login")
2943
+ output_buffer.should_not have_tag("form fieldset.inputs #project_login")
2944
+ end
2945
+ end
2946
+ end
2947
+
2948
+ describe 'with column names and an options hash as args' do
2949
+ before do
2950
+ semantic_form_for(@new_post) do |builder|
2951
+ concat(builder.inputs(:title, :body, :name => "Legendary Legend Text", :id => "my-id"))
2952
+ end
2953
+ end
2954
+
2955
+ it 'should render a form with a fieldset containing two list items' do
2956
+ output_buffer.should have_tag('form > fieldset.inputs > ol > li', :count => 2)
2957
+ end
2958
+
2959
+ it 'should pass the options down to the fieldset' do
2960
+ output_buffer.should have_tag('form > fieldset#my-id.inputs')
2961
+ end
2962
+
2963
+ it 'should use the special :name option as a text for the legend tag' do
2964
+ output_buffer.should have_tag('form > fieldset#my-id.inputs > legend', /Legendary Legend Text/)
2965
+ end
2966
+ end
2967
+
2968
+ end
2969
+
2970
+ end
2971
+
2972
+ describe '#buttons' do
2973
+
2974
+ describe 'with a block' do
2975
+ describe 'when no options are provided' do
2976
+ before do
2977
+ semantic_form_for(@new_post) do |builder|
2978
+ builder.buttons do
2979
+ concat('hello')
2980
+ end
2981
+ end
2982
+ end
2983
+
2984
+ it 'should render a fieldset inside the form, with a class of "inputs"' do
2985
+ output_buffer.should have_tag("form fieldset.buttons")
2986
+ end
2987
+
2988
+ it 'should render an ol inside the fieldset' do
2989
+ output_buffer.should have_tag("form fieldset.buttons ol")
2990
+ end
2991
+
2992
+ it 'should render the contents of the block inside the ol' do
2993
+ output_buffer.should have_tag("form fieldset.buttons ol", /hello/)
2994
+ end
2995
+
2996
+ it 'should not render a legend inside the fieldset' do
2997
+ output_buffer.should_not have_tag("form fieldset.buttons legend")
2998
+ end
2999
+ end
3000
+
3001
+ describe 'when a :name option is provided' do
3002
+ before do
3003
+ @legend_text = "Advanced options"
3004
+
3005
+ semantic_form_for(@new_post) do |builder|
3006
+ builder.buttons :name => @legend_text do
3007
+ end
3008
+ end
3009
+ end
3010
+ it 'should render a fieldset inside the form' do
3011
+ output_buffer.should have_tag("form fieldset legend", /#{@legend_text}/)
3012
+ end
3013
+ end
3014
+
3015
+ describe 'when other options are provided' do
3016
+ before do
3017
+ @id_option = 'advanced'
3018
+ @class_option = 'wide'
3019
+
3020
+ semantic_form_for(@new_post) do |builder|
3021
+ builder.buttons :id => @id_option, :class => @class_option do
3022
+ end
3023
+ end
3024
+ end
3025
+ it 'should pass the options into the fieldset tag as attributes' do
3026
+ output_buffer.should have_tag("form fieldset##{@id_option}")
3027
+ output_buffer.should have_tag("form fieldset.#{@class_option}")
3028
+ end
3029
+ end
3030
+
3031
+ end
3032
+
3033
+ describe 'without a block' do
3034
+
3035
+ describe 'with no args (default buttons)' do
3036
+
3037
+ before do
3038
+ semantic_form_for(@new_post) do |builder|
3039
+ concat(builder.buttons)
3040
+ end
3041
+ end
3042
+
3043
+ it 'should render a form' do
3044
+ output_buffer.should have_tag('form')
3045
+ end
3046
+
3047
+ it 'should render a buttons fieldset inside the form' do
3048
+ output_buffer.should have_tag('form fieldset.buttons')
3049
+ end
3050
+
3051
+ it 'should not render a legend in the fieldset' do
3052
+ output_buffer.should_not have_tag('form fieldset.buttons legend')
3053
+ end
3054
+
3055
+ it 'should render an ol in the fieldset' do
3056
+ output_buffer.should have_tag('form fieldset.buttons ol')
3057
+ end
3058
+
3059
+ it 'should render a list item in the ol for each default button' do
3060
+ output_buffer.should have_tag('form fieldset.buttons ol li', :count => 1)
3061
+ end
3062
+
3063
+ it 'should render a commit list item for the commit button' do
3064
+ output_buffer.should have_tag('form fieldset.buttons ol li.commit')
3065
+ end
3066
+
3067
+ end
3068
+
3069
+ describe 'with button names as args' do
3070
+
3071
+ before do
3072
+ semantic_form_for(@new_post) do |builder|
3073
+ concat(builder.buttons(:commit))
3074
+ end
3075
+ end
3076
+
3077
+ it 'should render a form with a fieldset containing a list item for each button arg' do
3078
+ output_buffer.should have_tag('form > fieldset.buttons > ol > li', :count => 1)
3079
+ output_buffer.should have_tag('form > fieldset.buttons > ol > li.commit')
3080
+ end
3081
+
3082
+ end
3083
+
3084
+ describe 'with button names as args and an options hash' do
3085
+
3086
+ before do
3087
+ semantic_form_for(@new_post) do |builder|
3088
+ concat(builder.buttons(:commit, :name => "Now click a button", :id => "my-id"))
3089
+ end
3090
+ end
3091
+
3092
+ it 'should render a form with a fieldset containing a list item for each button arg' do
3093
+ output_buffer.should have_tag('form > fieldset.buttons > ol > li', :count => 1)
3094
+ output_buffer.should have_tag('form > fieldset.buttons > ol > li.commit', :count => 1)
3095
+ end
3096
+
3097
+ it 'should pass the options down to the fieldset' do
3098
+ output_buffer.should have_tag('form > fieldset#my-id.buttons')
3099
+ end
3100
+
3101
+ it 'should use the special :name option as a text for the legend tag' do
3102
+ output_buffer.should have_tag('form > fieldset#my-id.buttons > legend', /Now click a button/)
3103
+ end
3104
+
3105
+ end
3106
+
3107
+ end
3108
+
3109
+ end
3110
+
3111
+ describe '#commit_button' do
3112
+
3113
+ describe 'when used on any record' do
3114
+
3115
+ before do
3116
+ @new_post.stub!(:new_record?).and_return(false)
3117
+ semantic_form_for(@new_post) do |builder|
3118
+ concat(builder.commit_button)
3119
+ end
3120
+ end
3121
+
3122
+ it 'should render a commit li' do
3123
+ output_buffer.should have_tag('li.commit')
3124
+ end
3125
+
3126
+ it 'should render an input with a type attribute of "submit"' do
3127
+ output_buffer.should have_tag('li.commit input[@type="submit"]')
3128
+ end
3129
+
3130
+ it 'should render an input with a name attribute of "commit"' do
3131
+ output_buffer.should have_tag('li.commit input[@name="commit"]')
3132
+ end
3133
+
3134
+ it 'should pass options given in :button_html to the button' do
3135
+ @new_post.stub!(:new_record?).and_return(false)
3136
+ semantic_form_for(@new_post) do |builder|
3137
+ concat(builder.commit_button('text', :button_html => {:class => 'my_class', :id => 'my_id'}))
3138
+ end
3139
+
3140
+ output_buffer.should have_tag('li.commit input#my_id')
3141
+ output_buffer.should have_tag('li.commit input.my_class')
3142
+ end
3143
+
3144
+ end
3145
+
3146
+ describe 'when the first option is a string and the second is a hash' do
3147
+
3148
+ before do
3149
+ @new_post.stub!(:new_record?).and_return(false)
3150
+ semantic_form_for(@new_post) do |builder|
3151
+ concat(builder.commit_button("a string", :button_html => { :class => "pretty"}))
3152
+ end
3153
+ end
3154
+
3155
+ it "should render the string as the value of the button" do
3156
+ output_buffer.should have_tag('li input[@value="a string"]')
3157
+ end
3158
+
3159
+ it "should deal with the options hash" do
3160
+ output_buffer.should have_tag('li input.pretty')
3161
+ end
3162
+
3163
+ end
3164
+
3165
+ describe 'when the first option is a hash' do
3166
+
3167
+ before do
3168
+ @new_post.stub!(:new_record?).and_return(false)
3169
+ semantic_form_for(@new_post) do |builder|
3170
+ concat(builder.commit_button(:button_html => { :class => "pretty"}))
3171
+ end
3172
+ end
3173
+
3174
+ it "should deal with the options hash" do
3175
+ output_buffer.should have_tag('li input.pretty')
3176
+ end
3177
+
3178
+ end
3179
+
3180
+ describe 'when used on an existing record' do
3181
+
3182
+ it 'should render an input with a value attribute of "Save Post"' do
3183
+ @new_post.stub!(:new_record?).and_return(false)
3184
+ semantic_form_for(@new_post) do |builder|
3185
+ concat(builder.commit_button)
3186
+ end
3187
+ output_buffer.should have_tag('li.commit input[@value="Save Post"]')
3188
+ end
3189
+
3190
+ describe 'when the locale sets the label text' do
3191
+ before do
3192
+ I18n.backend.store_translations 'en', :formtastic => {:save => 'Save Changes To {{model}}' }
3193
+ @new_post.stub!(:new_record?).and_return(false)
3194
+ semantic_form_for(@new_post) do |builder|
3195
+ concat(builder.commit_button)
3196
+ end
3197
+ end
3198
+
3199
+ after do
3200
+ I18n.backend.store_translations 'en', :formtastic => {:save => nil}
3201
+ end
3202
+
3203
+ it 'should allow translation of the labels' do
3204
+ output_buffer.should have_tag('li.commit input[@value="Save Changes To Post"]')
3205
+ end
3206
+ end
3207
+
3208
+ describe 'when the label text is set for a locale with different word order from the default' do
3209
+ before do
3210
+ I18n.locale = 'ja'
3211
+ I18n.backend.store_translations 'ja', :formtastic => {:save => 'Save {{model}}'}
3212
+ @new_post.stub!(:new_record?).and_return(false)
3213
+ ::Post.stub!(:human_name).and_return('Post')
3214
+ semantic_form_for(@new_post) do |builder|
3215
+ concat(builder.commit_button)
3216
+ end
3217
+ end
3218
+
3219
+ after do
3220
+ I18n.backend.store_translations 'ja', :formtastic => {:save => nil}
3221
+ I18n.locale = 'en'
3222
+ end
3223
+
3224
+ it 'should allow the translated label to have a different word order' do
3225
+ output_buffer.should have_tag('li.commit input[@value="Save Post"]')
3226
+ end
3227
+ end
3228
+ end
3229
+
3230
+ describe 'when used on a new record' do
3231
+
3232
+ it 'should render an input with a value attribute of "Create Post"' do
3233
+ @new_post.stub!(:new_record?).and_return(true)
3234
+ semantic_form_for(@new_post) do |builder|
3235
+ concat(builder.commit_button)
3236
+ end
3237
+ output_buffer.should have_tag('li.commit input[@value="Create Post"]')
3238
+ end
3239
+
3240
+ describe 'when the locale sets the label text' do
3241
+ before do
3242
+ I18n.backend.store_translations 'en', :formtastic => {:create => 'Make {{model}}' }
3243
+ semantic_form_for(@new_post) do |builder|
3244
+ concat(builder.commit_button)
3245
+ end
3246
+ end
3247
+
3248
+ after do
3249
+ I18n.backend.store_translations 'en', :formtastic => {:create => nil}
3250
+ end
3251
+
3252
+ it 'should allow translation of the labels' do
3253
+ output_buffer.should have_tag('li.commit input[@value="Make Post"]')
3254
+ end
3255
+ end
3256
+
3257
+ end
3258
+
3259
+ describe 'when used without object' do
3260
+
3261
+ it 'should render an input with a value attribute of "Submit"' do
3262
+ semantic_form_for(:project, :url => 'http://test.host') do |builder|
3263
+ concat(builder.commit_button)
3264
+ end
3265
+
3266
+ output_buffer.should have_tag('li.commit input[@value="Submit Project"]')
3267
+ end
3268
+
3269
+ describe 'when the locale sets the label text' do
3270
+ before do
3271
+ I18n.backend.store_translations 'en', :formtastic => { :submit => 'Send {{model}}' }
3272
+ semantic_form_for(:project, :url => 'http://test.host') do |builder|
3273
+ concat(builder.commit_button)
3274
+ end
3275
+ end
3276
+
3277
+ after do
3278
+ I18n.backend.store_translations 'en', :formtastic => {:submit => nil}
3279
+ end
3280
+
3281
+ it 'should allow translation of the labels' do
3282
+ output_buffer.should have_tag('li.commit input[@value="Send Project"]')
3283
+ end
3284
+ end
3285
+
3286
+ end
3287
+
3288
+ end
3289
+
3290
+ end
3291
+
3292
+ end