jferris-formtastic 0.2.1.0.1246297983

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