simple_form 3.4.0 → 5.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +118 -8
  3. data/MIT-LICENSE +2 -1
  4. data/README.md +235 -67
  5. data/lib/generators/simple_form/install_generator.rb +1 -0
  6. data/lib/generators/simple_form/templates/README +2 -3
  7. data/lib/generators/simple_form/templates/_form.html.erb +2 -0
  8. data/lib/generators/simple_form/templates/_form.html.haml +2 -0
  9. data/lib/generators/simple_form/templates/_form.html.slim +1 -0
  10. data/lib/generators/simple_form/templates/config/initializers/simple_form.rb +14 -7
  11. data/lib/generators/simple_form/templates/config/initializers/simple_form_bootstrap.rb +360 -74
  12. data/lib/generators/simple_form/templates/config/initializers/simple_form_foundation.rb +20 -8
  13. data/lib/simple_form/action_view_extensions/builder.rb +1 -0
  14. data/lib/simple_form/action_view_extensions/form_helper.rb +1 -0
  15. data/lib/simple_form/components/errors.rb +15 -2
  16. data/lib/simple_form/components/hints.rb +1 -0
  17. data/lib/simple_form/components/html5.rb +1 -0
  18. data/lib/simple_form/components/label_input.rb +2 -1
  19. data/lib/simple_form/components/labels.rb +12 -7
  20. data/lib/simple_form/components/maxlength.rb +4 -17
  21. data/lib/simple_form/components/min_max.rb +1 -0
  22. data/lib/simple_form/components/minlength.rb +5 -18
  23. data/lib/simple_form/components/pattern.rb +1 -0
  24. data/lib/simple_form/components/placeholders.rb +2 -1
  25. data/lib/simple_form/components/readonly.rb +1 -0
  26. data/lib/simple_form/components.rb +1 -0
  27. data/lib/simple_form/error_notification.rb +1 -0
  28. data/lib/simple_form/form_builder.rb +104 -29
  29. data/lib/simple_form/helpers/autofocus.rb +1 -0
  30. data/lib/simple_form/helpers/disabled.rb +1 -0
  31. data/lib/simple_form/helpers/readonly.rb +1 -0
  32. data/lib/simple_form/helpers/required.rb +1 -0
  33. data/lib/simple_form/helpers/validators.rb +2 -1
  34. data/lib/simple_form/helpers.rb +1 -0
  35. data/lib/simple_form/inputs/base.rb +24 -5
  36. data/lib/simple_form/inputs/block_input.rb +1 -0
  37. data/lib/simple_form/inputs/boolean_input.rb +4 -2
  38. data/lib/simple_form/inputs/collection_check_boxes_input.rb +3 -2
  39. data/lib/simple_form/inputs/collection_input.rb +6 -7
  40. data/lib/simple_form/inputs/collection_radio_buttons_input.rb +2 -1
  41. data/lib/simple_form/inputs/collection_select_input.rb +1 -0
  42. data/lib/simple_form/inputs/color_input.rb +14 -0
  43. data/lib/simple_form/inputs/date_time_input.rb +1 -0
  44. data/lib/simple_form/inputs/file_input.rb +1 -0
  45. data/lib/simple_form/inputs/grouped_collection_select_input.rb +1 -0
  46. data/lib/simple_form/inputs/hidden_input.rb +1 -0
  47. data/lib/simple_form/inputs/numeric_input.rb +1 -0
  48. data/lib/simple_form/inputs/password_input.rb +1 -0
  49. data/lib/simple_form/inputs/priority_input.rb +1 -4
  50. data/lib/simple_form/inputs/range_input.rb +1 -0
  51. data/lib/simple_form/inputs/rich_text_area_input.rb +12 -0
  52. data/lib/simple_form/inputs/string_input.rb +2 -1
  53. data/lib/simple_form/inputs/text_input.rb +1 -0
  54. data/lib/simple_form/inputs.rb +3 -0
  55. data/lib/simple_form/map_type.rb +1 -0
  56. data/lib/simple_form/railtie.rb +1 -0
  57. data/lib/simple_form/tags.rb +7 -2
  58. data/lib/simple_form/version.rb +2 -1
  59. data/lib/simple_form/wrappers/builder.rb +1 -0
  60. data/lib/simple_form/wrappers/leaf.rb +2 -1
  61. data/lib/simple_form/wrappers/many.rb +1 -0
  62. data/lib/simple_form/wrappers/root.rb +9 -2
  63. data/lib/simple_form/wrappers/single.rb +2 -1
  64. data/lib/simple_form/wrappers.rb +1 -0
  65. data/lib/simple_form.rb +79 -11
  66. data/test/action_view_extensions/builder_test.rb +28 -9
  67. data/test/action_view_extensions/form_helper_test.rb +3 -2
  68. data/test/components/custom_components_test.rb +62 -0
  69. data/test/components/label_test.rb +33 -8
  70. data/test/form_builder/association_test.rb +33 -2
  71. data/test/form_builder/button_test.rb +1 -0
  72. data/test/form_builder/error_notification_test.rb +1 -0
  73. data/test/form_builder/error_test.rb +12 -0
  74. data/test/form_builder/general_test.rb +75 -13
  75. data/test/form_builder/hint_test.rb +6 -0
  76. data/test/form_builder/input_field_test.rb +30 -10
  77. data/test/form_builder/label_test.rb +10 -4
  78. data/test/form_builder/wrapper_test.rb +32 -5
  79. data/test/generators/simple_form_generator_test.rb +4 -3
  80. data/test/inputs/boolean_input_test.rb +17 -0
  81. data/test/inputs/collection_check_boxes_input_test.rb +38 -18
  82. data/test/inputs/collection_radio_buttons_input_test.rb +48 -28
  83. data/test/inputs/collection_select_input_test.rb +46 -43
  84. data/test/inputs/color_input_test.rb +10 -0
  85. data/test/inputs/datetime_input_test.rb +7 -16
  86. data/test/inputs/disabled_test.rb +14 -0
  87. data/test/inputs/discovery_test.rb +22 -0
  88. data/test/inputs/file_input_test.rb +1 -0
  89. data/test/inputs/general_test.rb +3 -2
  90. data/test/inputs/grouped_collection_select_input_test.rb +11 -10
  91. data/test/inputs/hidden_input_test.rb +1 -0
  92. data/test/inputs/numeric_input_test.rb +2 -1
  93. data/test/inputs/priority_input_test.rb +7 -14
  94. data/test/inputs/readonly_test.rb +1 -0
  95. data/test/inputs/required_test.rb +1 -0
  96. data/test/inputs/rich_text_area_input_test.rb +15 -0
  97. data/test/inputs/string_input_test.rb +10 -16
  98. data/test/inputs/text_input_test.rb +1 -0
  99. data/test/simple_form_test.rb +1 -0
  100. data/test/support/discovery_inputs.rb +8 -0
  101. data/test/support/misc_helpers.rb +22 -1
  102. data/test/support/mock_controller.rb +7 -1
  103. data/test/support/models.rb +80 -18
  104. data/test/test_helper.rb +9 -4
  105. metadata +49 -55
  106. data/lib/simple_form/i18n_cache.rb +0 -22
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+
5
+ # Module that represents a custom component.
6
+ module Numbers
7
+ def number(wrapper_options = nil)
8
+ @number ||= options[:number].to_s.html_safe
9
+ end
10
+ end
11
+
12
+ # Module that represents a custom component.
13
+ module InputGroup
14
+ def prepend(wrapper_options = nil)
15
+ span_tag = content_tag(:span, options[:prepend], class: 'input-group-text')
16
+ template.content_tag(:div, span_tag, class: 'input-group-prepend')
17
+ end
18
+
19
+ def append(wrapper_options = nil)
20
+ span_tag = content_tag(:span, options[:append], class: 'input-group-text')
21
+ template.content_tag(:div, span_tag, class: 'input-group-append')
22
+ end
23
+ end
24
+
25
+ class CustomComponentsTest < ActionView::TestCase
26
+ test 'includes the custom components' do
27
+ SimpleForm.include_component Numbers
28
+
29
+ custom_wrapper = SimpleForm.build tag: :div, class: "custom_wrapper" do |b|
30
+ b.use :number, wrap_with: { tag: 'div', class: 'number' }
31
+ end
32
+
33
+ with_form_for @user, :name, number: 1, wrapper: custom_wrapper
34
+
35
+ assert_select 'div.number', text: '1'
36
+ end
37
+
38
+ test 'includes custom components and use it as optional in the wrapper' do
39
+ SimpleForm.include_component InputGroup
40
+
41
+ custom_wrapper = SimpleForm.build tag: :div, class: 'custom_wrapper' do |b|
42
+ b.use :label
43
+ b.optional :prepend
44
+ b.use :input
45
+ b.use :append
46
+ end
47
+
48
+ with_form_for @user, :name, prepend: true, wrapper: custom_wrapper
49
+
50
+ assert_select 'div.input-group-prepend > span.input-group-text'
51
+ assert_select 'div.input-group-append > span.input-group-text'
52
+ end
53
+
54
+ test 'raises a TypeError when the component is not a Module' do
55
+ component = 'MyComponent'
56
+
57
+ exception = assert_raises TypeError do
58
+ SimpleForm.include_component(component)
59
+ end
60
+ assert_equal exception.message, "SimpleForm.include_component expects a module but got: String"
61
+ end
62
+ end
@@ -1,12 +1,9 @@
1
+ # frozen_string_literal: true
1
2
  # encoding: UTF-8
2
3
  require 'test_helper'
3
4
 
4
5
  # Isolated tests for label without triggering f.label.
5
6
  class IsolatedLabelTest < ActionView::TestCase
6
- setup do
7
- SimpleForm::Inputs::Base.reset_i18n_cache :translate_required_html
8
- end
9
-
10
7
  def with_label_for(object, attribute_name, type, options = {})
11
8
  with_concat_form_for(object) do |f|
12
9
  options[:reflection] = Association.new(Company, :company, {}) if options.delete(:setup_association)
@@ -177,7 +174,7 @@ class IsolatedLabelTest < ActionView::TestCase
177
174
  end
178
175
 
179
176
  test 'label does not have css class from type when generate_additional_classes_for does not include :label' do
180
- swap SimpleForm, generate_additional_classes_for: [:wrapper, :input] do
177
+ swap SimpleForm, generate_additional_classes_for: %i[wrapper input] do
181
178
  with_label_for @user, :name, :string
182
179
  assert_no_select 'label.string'
183
180
  with_label_for @user, :description, :text
@@ -192,7 +189,7 @@ class IsolatedLabelTest < ActionView::TestCase
192
189
  end
193
190
 
194
191
  test 'label does not generate empty css class' do
195
- swap SimpleForm, generate_additional_classes_for: [:wrapper, :input] do
192
+ swap SimpleForm, generate_additional_classes_for: %i[wrapper input] do
196
193
  with_label_for @user, :name, :string
197
194
  assert_no_select 'label[class]'
198
195
  end
@@ -206,7 +203,7 @@ class IsolatedLabelTest < ActionView::TestCase
206
203
  end
207
204
 
208
205
  test 'label does not obtain required from ActiveModel::Validations when generate_additional_classes_for does not include :label' do
209
- swap SimpleForm, generate_additional_classes_for: [:wrapper, :input] do
206
+ swap SimpleForm, generate_additional_classes_for: %i[wrapper input] do
210
207
  with_label_for @validating_user, :name, :string
211
208
  assert_no_select 'label.required'
212
209
  with_label_for @validating_user, :status, :string
@@ -248,6 +245,15 @@ class IsolatedLabelTest < ActionView::TestCase
248
245
  end
249
246
  end
250
247
 
248
+ test 'label uses custom i18n scope to find required text' do
249
+ store_translations(:en, my_scope: { required: { text: 'Pflichtfeld' } }) do
250
+ swap SimpleForm, i18n_scope: :my_scope do
251
+ with_label_for @user, :name, :string
252
+ assert_select 'form label abbr[title="Pflichtfeld"]', '*'
253
+ end
254
+ end
255
+ end
256
+
251
257
  test 'label uses i18n to find required mark' do
252
258
  store_translations(:en, simple_form: { required: { mark: '*-*' } }) do
253
259
  with_label_for @user, :name, :string
@@ -255,6 +261,15 @@ class IsolatedLabelTest < ActionView::TestCase
255
261
  end
256
262
  end
257
263
 
264
+ test 'label uses custom i18n scope to find required mark' do
265
+ store_translations(:en, my_scope: { required: { mark: '!!' } }) do
266
+ swap SimpleForm, i18n_scope: :my_scope do
267
+ with_label_for @user, :name, :string
268
+ assert_select 'form label abbr', '!!'
269
+ end
270
+ end
271
+ end
272
+
258
273
  test 'label uses i18n to find required string tag' do
259
274
  store_translations(:en, simple_form: { required: { html: '<span class="required" title="requerido">*</span>' } }) do
260
275
  with_label_for @user, :name, :string
@@ -263,6 +278,16 @@ class IsolatedLabelTest < ActionView::TestCase
263
278
  end
264
279
  end
265
280
 
281
+ test 'label uses custom i18n scope to find required string tag' do
282
+ store_translations(:en, my_scope: { required: { html: '<span class="mandatory" title="Pflichtfeld">!!</span>' } }) do
283
+ swap SimpleForm, i18n_scope: :my_scope do
284
+ with_label_for @user, :name, :string
285
+ assert_no_select 'form label abbr'
286
+ assert_select 'form label span.mandatory[title=Pflichtfeld]', '!!'
287
+ end
288
+ end
289
+ end
290
+
266
291
  test 'label allows overwriting input id' do
267
292
  with_label_for @user, :name, :string, input_html: { id: 'my_new_id' }
268
293
  assert_select 'label[for=my_new_id]'
@@ -289,7 +314,7 @@ class IsolatedLabelTest < ActionView::TestCase
289
314
  end
290
315
 
291
316
  test 'label includes for attribute for select collection' do
292
- with_label_for @user, :sex, :select, collection: [:male, :female]
317
+ with_label_for @user, :sex, :select, collection: %i[male female]
293
318
  assert_select 'label[for=user_sex]'
294
319
  end
295
320
 
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  # encoding: UTF-8
2
3
  require 'test_helper'
3
4
 
@@ -14,6 +15,12 @@ class AssociationTest < ActionView::TestCase
14
15
  end
15
16
  end
16
17
 
18
+ test 'builder association works with decorated object responsive to #to_model' do
19
+ assert_nothing_raised do
20
+ with_association_for @decorated_user, :company
21
+ end
22
+ end
23
+
17
24
  test 'builder association with a block calls simple_fields_for' do
18
25
  simple_form_for @user do |f|
19
26
  f.association :posts do |posts_form|
@@ -121,6 +128,15 @@ class AssociationTest < ActionView::TestCase
121
128
  assert_no_select 'form select option[value="2"]'
122
129
  end
123
130
 
131
+ test 'builder allows collection to have a scope with parameter' do
132
+ with_association_for @user, :special_tags
133
+ assert_select 'form select.select#user_special_tag_ids'
134
+ assert_select 'form select[multiple=multiple]'
135
+ assert_select 'form select option[value="1"]', 'Tag 1'
136
+ assert_no_select 'form select option[value="2"]'
137
+ assert_no_select 'form select option[value="3"]'
138
+ end
139
+
124
140
  test 'builder marks the record which already belongs to the user' do
125
141
  @user.company_id = 2
126
142
  with_association_for @user, :company, as: :radio_buttons
@@ -154,6 +170,15 @@ class AssociationTest < ActionView::TestCase
154
170
  end
155
171
  end
156
172
 
173
+ test 'builder does not call where if the given association does not respond to it' do
174
+ with_association_for @user, :friends
175
+ assert_select 'form select.select#user_friend_ids'
176
+ assert_select 'form select[multiple=multiple]'
177
+ assert_select 'form select option[value="1"]', 'Friend 1'
178
+ assert_select 'form select option[value="2"]', 'Friend 2'
179
+ assert_select 'form select option[value="3"]', 'Friend 3'
180
+ end
181
+
157
182
  test 'builder does not call order if the given association does not respond to it' do
158
183
  with_association_for @user, :pictures
159
184
  assert_select 'form select.select#user_picture_ids'
@@ -210,12 +235,18 @@ class AssociationTest < ActionView::TestCase
210
235
  end
211
236
 
212
237
  test 'builder with collection support does not change the options hash' do
213
- options = { as: :check_boxes, collection_wrapper_tag: :ul, item_wrapper_tag: :li}
238
+ options = { as: :check_boxes, collection_wrapper_tag: :ul, item_wrapper_tag: :li }
214
239
  with_association_for @user, :tags, options
215
240
 
216
241
  assert_select 'form ul', count: 1
217
242
  assert_select 'form ul li', count: 3
218
- assert_equal({ as: :check_boxes, collection_wrapper_tag: :ul, item_wrapper_tag: :li},
243
+ assert_equal({ as: :check_boxes, collection_wrapper_tag: :ul, item_wrapper_tag: :li },
219
244
  options)
220
245
  end
246
+
247
+ test 'builder with group select considers multiple select by default' do
248
+ with_association_for @user, :tags, as: :grouped_select, group_method: :group_method
249
+
250
+ assert_select 'select[multiple="multiple"].grouped_select'
251
+ end
221
252
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  # encoding: UTF-8
2
3
  require 'test_helper'
3
4
 
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  # encoding: UTF-8
2
3
  require 'test_helper'
3
4
 
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'test_helper'
2
3
 
3
4
  # Tests for f.error and f.full_error
@@ -35,6 +36,11 @@ class ErrorTest < ActionView::TestCase
35
36
  assert_select 'span.error', "cannot be blank"
36
37
  end
37
38
 
39
+ test 'error generates messages with decorated object responsive to #to_model' do
40
+ with_error_for @decorated_user, :name
41
+ assert_select 'span.error', "cannot be blank"
42
+ end
43
+
38
44
  test 'error generates messages for attribute with one error when using first' do
39
45
  swap SimpleForm, error_method: :first do
40
46
  with_error_for @user, :age
@@ -218,6 +224,12 @@ class ErrorTest < ActionView::TestCase
218
224
  assert_no_select 'span.error'
219
225
  end
220
226
 
227
+ test 'input with custom error works when form does not use a model' do
228
+ with_form_for :user, :active, error: "Super User Active! cannot be blank"
229
+
230
+ assert_select 'span.error'
231
+ end
232
+
221
233
  test 'input with custom error works when using full_error component' do
222
234
  swap_wrapper :default, custom_wrapper_with_full_error do
223
235
  error_text = "Super User Name! cannot be blank"
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  # encoding: UTF-8
2
3
  require 'test_helper'
3
4
 
@@ -30,6 +31,12 @@ class FormBuilderTest < ActionView::TestCase
30
31
  end
31
32
  end
32
33
 
34
+ test 'builder works with decorated object responsive to #to_model' do
35
+ assert_nothing_raised do
36
+ with_form_for @decorated_user, :name
37
+ end
38
+ end
39
+
33
40
  test 'builder input allows a block to configure input' do
34
41
  with_form_for @user, :name do
35
42
  text_field_tag :foo, :bar, id: :cool
@@ -49,7 +56,7 @@ class FormBuilderTest < ActionView::TestCase
49
56
  test 'builder does not override custom input mappings for custom collection' do
50
57
  swap SimpleForm, input_mappings: { /gender$/ => :check_boxes } do
51
58
  with_concat_form_for @user do |f|
52
- f.input :gender, collection: [:male, :female]
59
+ f.input :gender, collection: %i[male female]
53
60
  end
54
61
 
55
62
  assert_no_select 'select option', 'Male'
@@ -58,7 +65,7 @@ class FormBuilderTest < ActionView::TestCase
58
65
  end
59
66
 
60
67
  test 'builder allows to skip input_type class' do
61
- swap SimpleForm, generate_additional_classes_for: [:label, :wrapper] do
68
+ swap SimpleForm, generate_additional_classes_for: %i[label wrapper] do
62
69
  with_form_for @user, :post_count
63
70
  assert_no_select "form input#user_post_count.integer"
64
71
  assert_select "form input#user_post_count"
@@ -119,9 +126,40 @@ class FormBuilderTest < ActionView::TestCase
119
126
 
120
127
  test 'builder generates text areas for text columns' do
121
128
  with_form_for @user, :description
129
+ assert_no_select 'form input#user_description.string'
130
+ assert_select 'form textarea#user_description.text'
131
+ end
132
+
133
+ test 'builder generates text areas for text columns when hinted' do
134
+ with_form_for @user, :description, as: :text
135
+ assert_no_select 'form input#user_description.string'
122
136
  assert_select 'form textarea#user_description.text'
123
137
  end
124
138
 
139
+ test 'builder generates text field for text columns when hinted' do
140
+ with_form_for @user, :description, as: :string
141
+ assert_no_select 'form textarea#user_description.text'
142
+ assert_select 'form input#user_description.string'
143
+ end
144
+
145
+ test 'builder generates text areas for hstore columns' do
146
+ with_form_for @user, :hstore
147
+ assert_no_select 'form input#user_hstore.string'
148
+ assert_select 'form textarea#user_hstore.text'
149
+ end
150
+
151
+ test 'builder generates text areas for json columns' do
152
+ with_form_for @user, :json
153
+ assert_no_select 'form input#user_json.string'
154
+ assert_select 'form textarea#user_json.text'
155
+ end
156
+
157
+ test 'builder generates text areas for jsonb columns' do
158
+ with_form_for @user, :jsonb
159
+ assert_no_select 'form input#user_jsonb.string'
160
+ assert_select 'form textarea#user_jsonb.text'
161
+ end
162
+
125
163
  test 'builder generates a checkbox for boolean columns' do
126
164
  with_form_for @user, :active
127
165
  assert_select 'form input[type=checkbox]#user_active.boolean'
@@ -146,6 +184,11 @@ class FormBuilderTest < ActionView::TestCase
146
184
  end
147
185
  end
148
186
 
187
+ test 'builder generates string fields for citext columns' do
188
+ with_form_for @user, :citext
189
+ assert_select 'form input#user_citext.string'
190
+ end
191
+
149
192
  test 'builder generates password fields for columns that matches password' do
150
193
  with_form_for @user, :password
151
194
  assert_select 'form input#user_password.password'
@@ -196,20 +239,29 @@ class FormBuilderTest < ActionView::TestCase
196
239
  assert_select 'form select#user_updated_at_1i.datetime'
197
240
  end
198
241
 
199
- test 'builder generates file for file columns' do
200
- @user.avatar = MiniTest::Mock.new
201
- @user.avatar.expect(:public_filename, true)
242
+ test 'builder generates file input for ActiveStorage >= 5.2 and Refile >= 0.2.0 <= 0.4.0' do
243
+ with_form_for UserWithAttachment.build, :avatar
244
+ assert_select 'form input#user_with_attachment_avatar.file'
245
+ end
246
+
247
+ test 'builder generates file input for ActiveStorage::Attached::Many' do
248
+ with_form_for UserWithAttachment.build, :avatars
249
+ assert_select 'form input#user_with_attachment_avatars.file'
250
+ end
202
251
 
203
- with_form_for @user, :avatar
204
- assert_select 'form input#user_avatar.file'
252
+ test 'builder generates file input for Refile >= 0.3.0 and CarrierWave >= 0.2.2' do
253
+ with_form_for UserWithAttachment.build, :cover
254
+ assert_select 'form input#user_with_attachment_cover.file'
205
255
  end
206
256
 
207
- test 'builder generates file for attributes that are real db columns but have file methods' do
208
- @user.home_picture = MiniTest::Mock.new
209
- @user.home_picture.expect(:mounted_as, true)
257
+ test 'builder generates file input for Refile >= 0.4.0 and Shrine >= 0.9.0' do
258
+ with_form_for UserWithAttachment.build, :profile_image
259
+ assert_select 'form input#user_with_attachment_profile_image.file'
260
+ end
210
261
 
211
- with_form_for @user, :home_picture
212
- assert_select 'form input#user_home_picture.file'
262
+ test 'builder generates file input for Paperclip ~> 2.0' do
263
+ with_form_for UserWithAttachment.build, :portrait
264
+ assert_select 'form input#user_with_attachment_portrait.file'
213
265
  end
214
266
 
215
267
  test 'build generates select if a collection is given' do
@@ -217,15 +269,25 @@ class FormBuilderTest < ActionView::TestCase
217
269
  assert_select 'form select#user_age.select'
218
270
  end
219
271
 
272
+ test 'builder does not generate url fields for columns that contain only the letters url' do
273
+ with_form_for @user, :hourly
274
+ assert_no_select 'form input#user_url.string.url'
275
+ assert_select 'form input#user_hourly.string'
276
+ end
277
+
220
278
  test 'builder allows overriding default input type for text' do
221
279
  with_form_for @user, :name, as: :text
222
280
  assert_no_select 'form input#user_name'
223
281
  assert_select 'form textarea#user_name.text'
282
+ end
224
283
 
284
+ test 'builder allows overriding default input type for radio_buttons' do
225
285
  with_form_for @user, :active, as: :radio_buttons
226
286
  assert_no_select 'form input[type=checkbox]'
227
287
  assert_select 'form input.radio_buttons[type=radio]', count: 2
288
+ end
228
289
 
290
+ test 'builder allows overriding default input type for string' do
229
291
  with_form_for @user, :born_at, as: :string
230
292
  assert_no_select 'form select'
231
293
  assert_select 'form input#user_born_at.string'
@@ -365,7 +427,7 @@ class FormBuilderTest < ActionView::TestCase
365
427
  end
366
428
 
367
429
  # DEFAULT OPTIONS
368
- [:input, :input_field].each do |method|
430
+ %i[input input_field].each do |method|
369
431
  test "builder receives a default argument and pass it to the inputs when calling '#{method}'" do
370
432
  with_concat_form_for @user, defaults: { input_html: { class: 'default_class' } } do |f|
371
433
  f.public_send(method, :name)
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'test_helper'
2
3
 
3
4
  # Tests for f.hint
@@ -18,6 +19,11 @@ class HintTest < ActionView::TestCase
18
19
  assert_select 'span.hint', 'Use with care...'
19
20
  end
20
21
 
22
+ test 'hint is generated with decorated object responsive to #to_model' do
23
+ with_hint_for @decorated_user, :name, hint: 'Use with care...'
24
+ assert_select 'span.hint', 'Use with care...'
25
+ end
26
+
21
27
  test 'hint does not modify the options hash' do
22
28
  options = { hint: 'Use with care...' }
23
29
  with_hint_for @user, :name, options
@@ -1,13 +1,8 @@
1
+ # frozen_string_literal: true
1
2
  require 'test_helper'
2
3
 
3
4
  # Tests for f.input_field
4
5
  class InputFieldTest < ActionView::TestCase
5
- def with_input_field_for(object, *args)
6
- with_concat_form_for(object) do |f|
7
- f.input_field(*args)
8
- end
9
- end
10
-
11
6
  test "builder input_field only renders the input tag, nothing else" do
12
7
  with_input_field_for @user, :name
13
8
 
@@ -84,13 +79,13 @@ class InputFieldTest < ActionView::TestCase
84
79
  test 'builder input_field infers pattern from attributes' do
85
80
  with_input_field_for @other_validating_user, :country, as: :string, pattern: true
86
81
 
87
- assert_select 'input[pattern="\w+"]'
82
+ assert_select "input:match('pattern', ?)", /\w+/
88
83
  end
89
84
 
90
85
  test 'builder input_field accepts custom pattern' do
91
86
  with_input_field_for @other_validating_user, :country, as: :string, pattern: '\d+'
92
87
 
93
- assert_select 'input[pattern="\d+"]'
88
+ assert_select "input:match('pattern', ?)", /\\d+/
94
89
  end
95
90
 
96
91
  test 'builder input_field uses readonly component' do
@@ -112,7 +107,7 @@ class InputFieldTest < ActionView::TestCase
112
107
  end
113
108
 
114
109
  test 'builder collection input_field generates input tag with a clean HTML' do
115
- with_input_field_for @user, :status, collection: ['Open', 'Closed'],
110
+ with_input_field_for @user, :status, collection: %w[Open Closed],
116
111
  class: 'status', label_method: :to_s, value_method: :to_s
117
112
 
118
113
  assert_no_select 'select.status[input_html]'
@@ -137,7 +132,7 @@ class InputFieldTest < ActionView::TestCase
137
132
  swap_wrapper :default, custom_wrapper_with_html5_components do
138
133
  with_input_field_for @user, :name, pattern: '\w+'
139
134
 
140
- assert_select 'input[pattern="\w+"]'
135
+ assert_select "input:match('pattern', ?)", /\w+/
141
136
  end
142
137
  end
143
138
 
@@ -172,4 +167,29 @@ class InputFieldTest < ActionView::TestCase
172
167
  assert_select 'input[readonly="readonly"]'
173
168
  end
174
169
  end
170
+
171
+ test 'adds valid class to input_field when it is configured' do
172
+ swap SimpleForm, input_field_valid_class: 'is-valid' do
173
+ @user.instance_eval { undef errors }
174
+ with_input_field_for @user, :name
175
+
176
+ assert_select 'input.string.required.is-valid'
177
+ end
178
+ end
179
+
180
+ test 'adds error class to input_field when it is configured' do
181
+ swap SimpleForm, input_field_error_class: 'is-invalid' do
182
+ with_input_field_for @user, :name
183
+
184
+ assert_select 'input.string.required.is-invalid'
185
+ end
186
+ end
187
+
188
+ test 'does not add validation classes to input_field when it is not configured' do
189
+ swap SimpleForm, input_field_error_class: nil, input_field_valid_class: nil do
190
+ with_input_field_for @user, :name
191
+
192
+ assert_select 'input.string.required'
193
+ end
194
+ end
175
195
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  # encoding: UTF-8
2
3
  require 'test_helper'
3
4
 
@@ -13,7 +14,12 @@ class LabelTest < ActionView::TestCase
13
14
  assert_select 'label.string[for=user_name]', /Name/
14
15
  end
15
16
 
16
- test 'builder generates a label for the boolean attrbiute' do
17
+ test 'builder generates a label for the attribute with decorated object responsive to #to_model' do
18
+ with_label_for @decorated_user, :name
19
+ assert_select 'label.string[for=user_name]', /Name/
20
+ end
21
+
22
+ test 'builder generates a label for the boolean attribute' do
17
23
  with_label_for @user, :name, as: :boolean
18
24
  assert_select 'label.boolean[for=user_name]', /Name/
19
25
  assert_no_select 'label[as=boolean]'
@@ -108,21 +114,21 @@ class LabelTest < ActionView::TestCase
108
114
  end
109
115
 
110
116
  test 'builder allows custom formatting when label is explicitly specified' do
111
- swap SimpleForm, label_text: lambda { |l, r, explicit_label| explicit_label ? l : "#{l.titleize}:" } do
117
+ swap SimpleForm, label_text: ->(l, r, explicit_label) { explicit_label ? l : "#{l.titleize}:" } do
112
118
  with_label_for @user, :time_zone, 'What is your home time zone?'
113
119
  assert_select 'label[for=user_time_zone]', 'What is your home time zone?'
114
120
  end
115
121
  end
116
122
 
117
123
  test 'builder allows custom formatting when label is generated' do
118
- swap SimpleForm, label_text: lambda { |l, r, explicit_label| explicit_label ? l : "#{l.titleize}:" } do
124
+ swap SimpleForm, label_text: ->(l, r, explicit_label) { explicit_label ? l : "#{l.titleize}:" } do
119
125
  with_label_for @user, :time_zone
120
126
  assert_select 'label[for=user_time_zone]', 'Time Zone:'
121
127
  end
122
128
  end
123
129
 
124
130
  test 'builder allows label specific `label_text` option' do
125
- with_label_for @user, :time_zone, label_text: lambda { |l, _, _| "#{l.titleize}:" }
131
+ with_label_for @user, :time_zone, label_text: ->(l, _, _) { "#{l.titleize}:" }
126
132
 
127
133
  assert_no_select 'label[label_text]'
128
134
  assert_select 'label[for=user_time_zone]', 'Time Zone:'
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'test_helper'
2
3
 
3
4
  class WrapperTest < ActionView::TestCase
@@ -37,6 +38,34 @@ class WrapperTest < ActionView::TestCase
37
38
  assert_select 'div.field_with_errors'
38
39
  end
39
40
 
41
+ test 'wrapper adds error class to input for attribute with errors' do
42
+ with_form_for @user, :name, wrapper: custom_wrapper_with_input_error_class
43
+ assert_select 'div.field_with_errors'
44
+ assert_select 'input.is-invalid'
45
+ end
46
+
47
+ test 'wrapper does not add error class to input when the attribute is valid' do
48
+ with_form_for @user, :phone_number, wrapper: custom_wrapper_with_input_error_class
49
+ assert_no_select 'div.field_with_errors'
50
+ assert_no_select 'input.is-invalid'
51
+ end
52
+
53
+ test 'wrapper adds valid class for present attribute without errors' do
54
+ @user.instance_eval { undef errors }
55
+ with_form_for @user, :name, wrapper: custom_wrapper_with_input_valid_class
56
+ assert_select 'div.field_without_errors'
57
+ assert_select 'input.is-valid'
58
+ assert_no_select 'div.field_with_errors'
59
+ assert_no_select 'input.is-invalid'
60
+ end
61
+
62
+ test 'wrapper does not determine if valid class is needed when it is set to nil' do
63
+ @user.instance_eval { undef errors }
64
+ with_form_for @user, :name, wrapper: custom_wrapper_with_input_valid_class(valid_class: nil)
65
+
66
+ assert_no_select 'div.field_without_errors'
67
+ end
68
+
40
69
  test 'wrapper adds hint class for attribute with a hint' do
41
70
  with_form_for @user, :name, hint: 'hint'
42
71
  assert_select 'div.field_with_hint'
@@ -88,7 +117,7 @@ class WrapperTest < ActionView::TestCase
88
117
  end
89
118
 
90
119
  test 'wrapper skips additional classes when configured' do
91
- swap SimpleForm, generate_additional_classes_for: [:input, :label] do
120
+ swap SimpleForm, generate_additional_classes_for: %i[input label] do
92
121
  with_form_for @user, :name, wrapper_class: :wrapper
93
122
  assert_select 'form div.wrapper'
94
123
  assert_no_select 'div.required'
@@ -98,7 +127,7 @@ class WrapperTest < ActionView::TestCase
98
127
  end
99
128
 
100
129
  test 'wrapper does not generate empty css class' do
101
- swap SimpleForm, generate_additional_classes_for: [:input, :label] do
130
+ swap SimpleForm, generate_additional_classes_for: %i[input label] do
102
131
  swap_wrapper :default, custom_wrapper_without_class do
103
132
  with_form_for @user, :name
104
133
  assert_no_select 'div#custom_wrapper_without_class[class]'
@@ -139,7 +168,7 @@ class WrapperTest < ActionView::TestCase
139
168
  test 'custom wrappers can have full error message on attributes' do
140
169
  swap_wrapper :default, custom_wrapper_with_full_error do
141
170
  with_form_for @user, :name
142
- assert_select 'span.error', "Name cannot be blank"
171
+ assert_select 'span.error', "Super User Name! cannot be blank"
143
172
  end
144
173
  end
145
174
 
@@ -263,8 +292,6 @@ class WrapperTest < ActionView::TestCase
263
292
  end
264
293
 
265
294
  test "input with aria attributes will merge with wrapper_options' aria" do
266
- skip unless ActionPack::VERSION::MAJOR == '4' && ActionPack::VERSION::MINOR >= '2'
267
-
268
295
  swap_wrapper :default, custom_wrapper_with_input_aria_modal do
269
296
  with_concat_form_for @user do |f|
270
297
  concat f.input :name, input_html: { aria: { modal: 'another-aria', target: 'merge-aria' } }