formtastic 3.1.5 → 4.0.0.rc1

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