simple_form 3.1.0 → 5.0.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 (104) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +148 -0
  3. data/MIT-LICENSE +1 -1
  4. data/README.md +278 -68
  5. data/lib/generators/simple_form/install_generator.rb +1 -0
  6. data/lib/generators/simple_form/templates/README +3 -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 +19 -9
  11. data/lib/generators/simple_form/templates/config/initializers/simple_form_bootstrap.rb +367 -63
  12. data/lib/generators/simple_form/templates/config/initializers/simple_form_foundation.rb +23 -8
  13. data/lib/simple_form/action_view_extensions/builder.rb +1 -0
  14. data/lib/simple_form/action_view_extensions/form_helper.rb +4 -1
  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 +15 -4
  18. data/lib/simple_form/components/label_input.rb +2 -1
  19. data/lib/simple_form/components/labels.rb +12 -5
  20. data/lib/simple_form/components/maxlength.rb +8 -4
  21. data/lib/simple_form/components/min_max.rb +1 -0
  22. data/lib/simple_form/components/minlength.rb +38 -0
  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 +2 -0
  27. data/lib/simple_form/error_notification.rb +1 -0
  28. data/lib/simple_form/form_builder.rb +117 -35
  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/i18n_cache.rb +1 -0
  36. data/lib/simple_form/inputs/base.rb +36 -12
  37. data/lib/simple_form/inputs/block_input.rb +1 -0
  38. data/lib/simple_form/inputs/boolean_input.rb +14 -3
  39. data/lib/simple_form/inputs/collection_check_boxes_input.rb +2 -1
  40. data/lib/simple_form/inputs/collection_input.rb +7 -5
  41. data/lib/simple_form/inputs/collection_radio_buttons_input.rb +3 -2
  42. data/lib/simple_form/inputs/collection_select_input.rb +1 -0
  43. data/lib/simple_form/inputs/color_input.rb +14 -0
  44. data/lib/simple_form/inputs/date_time_input.rb +13 -8
  45. data/lib/simple_form/inputs/file_input.rb +1 -0
  46. data/lib/simple_form/inputs/grouped_collection_select_input.rb +1 -0
  47. data/lib/simple_form/inputs/hidden_input.rb +1 -0
  48. data/lib/simple_form/inputs/numeric_input.rb +1 -0
  49. data/lib/simple_form/inputs/password_input.rb +2 -1
  50. data/lib/simple_form/inputs/priority_input.rb +1 -4
  51. data/lib/simple_form/inputs/range_input.rb +1 -0
  52. data/lib/simple_form/inputs/string_input.rb +3 -2
  53. data/lib/simple_form/inputs/text_input.rb +2 -1
  54. data/lib/simple_form/inputs.rb +2 -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 +2 -0
  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 -14
  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 -4
  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 +44 -9
  74. data/test/form_builder/general_test.rb +92 -20
  75. data/test/form_builder/hint_test.rb +6 -0
  76. data/test/form_builder/input_field_test.rb +76 -70
  77. data/test/form_builder/label_test.rb +27 -4
  78. data/test/form_builder/wrapper_test.rb +66 -14
  79. data/test/generators/simple_form_generator_test.rb +4 -3
  80. data/test/inputs/boolean_input_test.rb +35 -0
  81. data/test/inputs/collection_check_boxes_input_test.rb +38 -14
  82. data/test/inputs/collection_radio_buttons_input_test.rb +48 -24
  83. data/test/inputs/collection_select_input_test.rb +40 -39
  84. data/test/inputs/color_input_test.rb +10 -0
  85. data/test/inputs/datetime_input_test.rb +12 -8
  86. data/test/inputs/disabled_test.rb +14 -0
  87. data/test/inputs/discovery_test.rb +23 -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 +5 -1
  93. data/test/inputs/priority_input_test.rb +7 -6
  94. data/test/inputs/readonly_test.rb +1 -0
  95. data/test/inputs/required_test.rb +45 -0
  96. data/test/inputs/string_input_test.rb +18 -16
  97. data/test/inputs/text_input_test.rb +13 -0
  98. data/test/simple_form_test.rb +1 -0
  99. data/test/support/discovery_inputs.rb +8 -0
  100. data/test/support/misc_helpers.rb +44 -2
  101. data/test/support/mock_controller.rb +7 -1
  102. data/test/support/models.rb +105 -22
  103. data/test/test_helper.rb +14 -3
  104. metadata +42 -36
data/lib/simple_form.rb CHANGED
@@ -1,4 +1,6 @@
1
+ # frozen_string_literal: true
1
2
  require 'action_view'
3
+ require 'action_pack'
2
4
  require 'simple_form/action_view_extensions/form_helper'
3
5
  require 'simple_form/action_view_extensions/builder'
4
6
  require 'active_support/core_ext/hash/slice'
@@ -36,6 +38,17 @@ to
36
38
  See https://github.com/plataformatec/simple_form/pull/997 for more information.
37
39
  WARN
38
40
 
41
+ FILE_METHODS_DEPRECATION_WARN = <<-WARN
42
+ [SIMPLE_FORM] SimpleForm.file_methods is deprecated and has no effect.
43
+
44
+ Since version 5, Simple Form now supports automatically discover of file inputs for the following Gems: activestorage, carrierwave, paperclip, refile and shrine.
45
+ If you are using a custom method that is not from one of the supported Gems, please change your forms to pass the input type explicitly:
46
+
47
+ <%= form.input :avatar, as: :file %>
48
+
49
+ See http://blog.plataformatec.com.br/2019/09/incorrect-access-control-in-simple-form-cve-2019-16676 for more information.
50
+ WARN
51
+
39
52
  @@configured = false
40
53
 
41
54
  def self.configured? #:nodoc:
@@ -58,11 +71,11 @@ See https://github.com/plataformatec/simple_form/pull/997 for more information.
58
71
 
59
72
  # Series of attemps to detect a default label method for collection.
60
73
  mattr_accessor :collection_label_methods
61
- @@collection_label_methods = [:to_label, :name, :title, :to_s]
74
+ @@collection_label_methods = %i[to_label name title to_s]
62
75
 
63
76
  # Series of attemps to detect a default value method for collection.
64
77
  mattr_accessor :collection_value_methods
65
- @@collection_value_methods = [:id, :to_s]
78
+ @@collection_value_methods = %i[id to_s]
66
79
 
67
80
  # You can wrap a collection of radio/check boxes in a pre-defined tag, defaulting to none.
68
81
  mattr_accessor :collection_wrapper_tag
@@ -84,7 +97,7 @@ See https://github.com/plataformatec/simple_form/pull/997 for more information.
84
97
 
85
98
  # How the label text should be generated altogether with the required text.
86
99
  mattr_accessor :label_text
87
- @@label_text = lambda { |label, required, explicit_label| "#{required} #{label}" }
100
+ @@label_text = ->(label, required, explicit_label) { "#{required} #{label}" }
88
101
 
89
102
  # You can define the class to be used on all labels. Defaults to none.
90
103
  mattr_accessor :label_class
@@ -108,7 +121,7 @@ See https://github.com/plataformatec/simple_form/pull/997 for more information.
108
121
 
109
122
  # You can define which elements should obtain additional classes.
110
123
  mattr_accessor :generate_additional_classes_for
111
- @@generate_additional_classes_for = [:wrapper, :label, :input]
124
+ @@generate_additional_classes_for = %i[wrapper label input]
112
125
 
113
126
  # Whether attributes are required by default or not.
114
127
  mattr_accessor :required_by_default
@@ -118,10 +131,6 @@ See https://github.com/plataformatec/simple_form/pull/997 for more information.
118
131
  mattr_accessor :browser_validations
119
132
  @@browser_validations = true
120
133
 
121
- # Collection of methods to detect if a file type was given.
122
- mattr_accessor :file_methods
123
- @@file_methods = [:mounted_as, :file?, :public_filename]
124
-
125
134
  # Custom mappings for input types. This should be a hash containing a regexp
126
135
  # to match as key, and the input type that will be used when the field name
127
136
  # matches the regexp as value, such as { /count/ => :integer }.
@@ -152,10 +161,6 @@ See https://github.com/plataformatec/simple_form/pull/997 for more information.
152
161
  mattr_accessor :country_priority
153
162
  @@country_priority = nil
154
163
 
155
- # DEPRECATED: Maximum size allowed for inputs.
156
- mattr_accessor :default_input_size
157
- @@default_input_size = nil
158
-
159
164
  # When off, do not use translations in labels. Disabling translation in
160
165
  # hints and placeholders can be done manually in the wrapper API.
161
166
  mattr_accessor :translate_labels
@@ -167,7 +172,7 @@ See https://github.com/plataformatec/simple_form/pull/997 for more information.
167
172
 
168
173
  # Cache SimpleForm inputs discovery.
169
174
  mattr_accessor :cache_discovery
170
- @@cache_discovery = defined?(Rails) && !Rails.env.development?
175
+ @@cache_discovery = defined?(Rails.env) && !Rails.env.development?
171
176
 
172
177
  # Adds a class to each generated button, mostly for compatiblity.
173
178
  mattr_accessor :button_class
@@ -202,6 +207,12 @@ See https://github.com/plataformatec/simple_form/pull/997 for more information.
202
207
  mattr_accessor :i18n_scope
203
208
  @@i18n_scope = 'simple_form'
204
209
 
210
+ mattr_accessor :input_field_error_class
211
+ @@input_field_error_class = nil
212
+
213
+ mattr_accessor :input_field_valid_class
214
+ @@input_field_valid_class = nil
215
+
205
216
  # Retrieves a given wrapper
206
217
  def self.wrapper(name)
207
218
  @@wrappers[name.to_s] or raise WrapperNotFound, "Couldn't find wrapper with name #{name}"
@@ -231,11 +242,12 @@ See https://github.com/plataformatec/simple_form/pull/997 for more information.
231
242
  SimpleForm::Wrappers::Root.new(builder.to_a, options)
232
243
  end
233
244
 
234
- wrappers class: :input, hint_class: :field_with_hint, error_class: :field_with_errors do |b|
245
+ wrappers class: :input, hint_class: :field_with_hint, error_class: :field_with_errors, valid_class: :field_without_errors do |b|
235
246
  b.use :html5
236
247
 
237
248
  b.use :min_max
238
249
  b.use :maxlength
250
+ b.use :minlength
239
251
  b.use :placeholder
240
252
  b.optional :pattern
241
253
  b.optional :readonly
@@ -260,12 +272,65 @@ See https://github.com/plataformatec/simple_form/pull/997 for more information.
260
272
  @@form_class = value
261
273
  end
262
274
 
275
+ def self.file_methods=(file_methods)
276
+ ActiveSupport::Deprecation.warn(FILE_METHODS_DEPRECATION_WARN, caller)
277
+ @@file_methods = file_methods
278
+ end
279
+
280
+ def self.file_methods
281
+ ActiveSupport::Deprecation.warn(FILE_METHODS_DEPRECATION_WARN, caller)
282
+ @@file_methods
283
+ end
284
+
263
285
  # Default way to setup Simple Form. Run rails generate simple_form:install
264
286
  # to create a fresh initializer with all configuration values.
265
287
  def self.setup
266
288
  @@configured = true
267
289
  yield self
268
290
  end
291
+
292
+ # Includes a component to be used by Simple Form. Methods defined in a
293
+ # component will be exposed to be used in the wrapper as Simple::Components
294
+ #
295
+ # Examples
296
+ #
297
+ # # The application needs to tell where the components will be.
298
+ # Dir[Rails.root.join('lib/components/**/*.rb')].each { |f| require f }
299
+ #
300
+ # # Create a custom component in the path specified above.
301
+ # # lib/components/input_group_component.rb
302
+ # module InputGroupComponent
303
+ # def prepend
304
+ # ...
305
+ # end
306
+ #
307
+ # def append
308
+ # ...
309
+ # end
310
+ # end
311
+ #
312
+ # SimpleForm.setup do |config|
313
+ # # Create a wrapper using the custom component.
314
+ # config.wrappers :input_group, tag: :div, error_class: :error do |b|
315
+ # b.use :label
316
+ # b.optional :prepend
317
+ # b.use :input
318
+ # b.use :append
319
+ # end
320
+ # end
321
+ #
322
+ # # Using the custom component in the form.
323
+ # <%= simple_form_for @blog, wrapper: input_group do |f| %>
324
+ # <%= f.input :title, prepend: true %>
325
+ # <% end %>
326
+ #
327
+ def self.include_component(component)
328
+ if Module === component
329
+ SimpleForm::Inputs::Base.include(component)
330
+ else
331
+ raise TypeError, "SimpleForm.include_component expects a module but got: #{component.class}"
332
+ end
333
+ end
269
334
  end
270
335
 
271
336
  require 'simple_form/railtie' if defined?(Rails)
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'test_helper'
2
3
 
3
4
  class BuilderTest < ActionView::TestCase
@@ -36,7 +37,7 @@ class BuilderTest < ActionView::TestCase
36
37
  end
37
38
 
38
39
  test "collection radio handles camelized collection values for labels correctly" do
39
- with_collection_radio_buttons @user, :active, ['Yes', 'No'], :to_s, :to_s
40
+ with_collection_radio_buttons @user, :active, %w[Yes No], :to_s, :to_s
40
41
 
41
42
  assert_select 'form label.collection_radio_buttons[for=user_active_yes]', 'Yes'
42
43
  assert_select 'form label.collection_radio_buttons[for=user_active_no]', 'No'
@@ -44,8 +45,17 @@ class BuilderTest < ActionView::TestCase
44
45
 
45
46
  test "collection radio sanitizes collection values for labels correctly" do
46
47
  with_collection_radio_buttons @user, :name, ['$0.99', '$1.99'], :to_s, :to_s
47
- assert_select 'label.collection_radio_buttons[for=user_name_099]', '$0.99'
48
- assert_select 'label.collection_radio_buttons[for=user_name_199]', '$1.99'
48
+
49
+ # Rails 6 changed the way it sanitizes the values
50
+ # https://github.com/rails/rails/blob/6-0-stable/actionview/lib/action_view/helpers/tags/base.rb#L141
51
+ # https://github.com/rails/rails/blob/5-2-stable/actionview/lib/action_view/helpers/tags/base.rb#L141
52
+ if ActionView::VERSION::MAJOR == 5
53
+ assert_select 'label.collection_radio_buttons[for=user_name_099]', '$0.99'
54
+ assert_select 'label.collection_radio_buttons[for=user_name_199]', '$1.99'
55
+ else
56
+ assert_select 'label.collection_radio_buttons[for=user_name_0_99]', '$0.99'
57
+ assert_select 'label.collection_radio_buttons[for=user_name_1_99]', '$1.99'
58
+ end
49
59
  end
50
60
 
51
61
  test "collection radio checks the correct value to local variables" do
@@ -243,7 +253,7 @@ class BuilderTest < ActionView::TestCase
243
253
 
244
254
  test "collection radio with block helpers does not leak the template" do
245
255
  with_concat_form_for(@user) do |f|
246
- collection_input = f.collection_radio_buttons :active, [true, false], :to_s, :to_s do |b|
256
+ collection_input = f.collection_radio_buttons :active, [true, false], :to_s, :to_s do |b|
247
257
  b.label(class: b.object) { b.radio_button + b.text }
248
258
  end
249
259
  concat collection_input
@@ -283,7 +293,7 @@ class BuilderTest < ActionView::TestCase
283
293
  end
284
294
 
285
295
  test "collection check box handles camelized collection values for labels correctly" do
286
- with_collection_check_boxes @user, :active, ['Yes', 'No'], :to_s, :to_s
296
+ with_collection_check_boxes @user, :active, %w[Yes No], :to_s, :to_s
287
297
 
288
298
  assert_select 'form label.collection_check_boxes[for=user_active_yes]', 'Yes'
289
299
  assert_select 'form label.collection_check_boxes[for=user_active_no]', 'No'
@@ -291,8 +301,17 @@ class BuilderTest < ActionView::TestCase
291
301
 
292
302
  test "collection check box sanitizes collection values for labels correctly" do
293
303
  with_collection_check_boxes @user, :name, ['$0.99', '$1.99'], :to_s, :to_s
294
- assert_select 'label.collection_check_boxes[for=user_name_099]', '$0.99'
295
- assert_select 'label.collection_check_boxes[for=user_name_199]', '$1.99'
304
+
305
+ # Rails 6 changed the way it sanitizes the values
306
+ # https://github.com/rails/rails/blob/6-0-stable/actionview/lib/action_view/helpers/tags/base.rb#L141
307
+ # https://github.com/rails/rails/blob/5-2-stable/actionview/lib/action_view/helpers/tags/base.rb#L141
308
+ if ActionView::VERSION::MAJOR == 5
309
+ assert_select 'label.collection_check_boxes[for=user_name_099]', '$0.99'
310
+ assert_select 'label.collection_check_boxes[for=user_name_199]', '$1.99'
311
+ else
312
+ assert_select 'label.collection_check_boxes[for=user_name_0_99]', '$0.99'
313
+ assert_select 'label.collection_check_boxes[for=user_name_1_99]', '$1.99'
314
+ end
296
315
  end
297
316
 
298
317
  test "collection check box checks the correct value to local variables" do
@@ -317,7 +336,7 @@ class BuilderTest < ActionView::TestCase
317
336
 
318
337
  test "collection check boxes accepts selected string values as :checked option" do
319
338
  collection = (1..3).map { |i| [i, "Category #{i}"] }
320
- with_collection_check_boxes :user, :category_ids, collection, :first, :last, checked: ['1', '3']
339
+ with_collection_check_boxes :user, :category_ids, collection, :first, :last, checked: %w[1 3]
321
340
 
322
341
  assert_select 'input[type=checkbox][value="1"][checked=checked]'
323
342
  assert_select 'input[type=checkbox][value="3"][checked=checked]'
@@ -541,7 +560,7 @@ class BuilderTest < ActionView::TestCase
541
560
 
542
561
  test "collection check boxes with block helpers does not leak the template" do
543
562
  with_concat_form_for(@user) do |f|
544
- collection_input = f.collection_check_boxes :active, [true, false], :to_s, :to_s do |b|
563
+ collection_input = f.collection_check_boxes :active, [true, false], :to_s, :to_s do |b|
545
564
  b.label(class: b.object) { b.check_box + b.text }
546
565
  end
547
566
  concat collection_input
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'test_helper'
2
3
 
3
4
  class FormHelperTest < ActionView::TestCase
@@ -151,7 +152,7 @@ class FormHelperTest < ActionView::TestCase
151
152
  end
152
153
 
153
154
  test 'SimpleForm for swaps default action view field_error_proc' do
154
- expected_error_proc = lambda {}
155
+ expected_error_proc = -> {}
155
156
  swap SimpleForm, field_error_proc: expected_error_proc do
156
157
  simple_form_for :user do |f|
157
158
  assert_equal expected_error_proc, ::ActionView::Base.field_error_proc
@@ -161,7 +162,7 @@ class FormHelperTest < ActionView::TestCase
161
162
 
162
163
  private
163
164
 
164
- def swap_field_error_proc(expected_error_proc = lambda {})
165
+ def swap_field_error_proc(expected_error_proc = -> {})
165
166
  swap ActionView::Base, field_error_proc: expected_error_proc do
166
167
  yield
167
168
 
@@ -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,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  # encoding: UTF-8
2
3
  require 'test_helper'
3
4
 
@@ -177,7 +178,7 @@ class IsolatedLabelTest < ActionView::TestCase
177
178
  end
178
179
 
179
180
  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
181
+ swap SimpleForm, generate_additional_classes_for: %i[wrapper input] do
181
182
  with_label_for @user, :name, :string
182
183
  assert_no_select 'label.string'
183
184
  with_label_for @user, :description, :text
@@ -192,7 +193,7 @@ class IsolatedLabelTest < ActionView::TestCase
192
193
  end
193
194
 
194
195
  test 'label does not generate empty css class' do
195
- swap SimpleForm, generate_additional_classes_for: [:wrapper, :input] do
196
+ swap SimpleForm, generate_additional_classes_for: %i[wrapper input] do
196
197
  with_label_for @user, :name, :string
197
198
  assert_no_select 'label[class]'
198
199
  end
@@ -206,7 +207,7 @@ class IsolatedLabelTest < ActionView::TestCase
206
207
  end
207
208
 
208
209
  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
210
+ swap SimpleForm, generate_additional_classes_for: %i[wrapper input] do
210
211
  with_label_for @validating_user, :name, :string
211
212
  assert_no_select 'label.required'
212
213
  with_label_for @validating_user, :status, :string
@@ -248,6 +249,15 @@ class IsolatedLabelTest < ActionView::TestCase
248
249
  end
249
250
  end
250
251
 
252
+ test 'label uses custom i18n scope to find required text' do
253
+ store_translations(:en, my_scope: { required: { text: 'Pflichtfeld' } }) do
254
+ swap SimpleForm, i18n_scope: :my_scope do
255
+ with_label_for @user, :name, :string
256
+ assert_select 'form label abbr[title="Pflichtfeld"]', '*'
257
+ end
258
+ end
259
+ end
260
+
251
261
  test 'label uses i18n to find required mark' do
252
262
  store_translations(:en, simple_form: { required: { mark: '*-*' } }) do
253
263
  with_label_for @user, :name, :string
@@ -255,6 +265,15 @@ class IsolatedLabelTest < ActionView::TestCase
255
265
  end
256
266
  end
257
267
 
268
+ test 'label uses custom i18n scope to find required mark' do
269
+ store_translations(:en, my_scope: { required: { mark: '!!' } }) do
270
+ swap SimpleForm, i18n_scope: :my_scope do
271
+ with_label_for @user, :name, :string
272
+ assert_select 'form label abbr', '!!'
273
+ end
274
+ end
275
+ end
276
+
258
277
  test 'label uses i18n to find required string tag' do
259
278
  store_translations(:en, simple_form: { required: { html: '<span class="required" title="requerido">*</span>' } }) do
260
279
  with_label_for @user, :name, :string
@@ -263,6 +282,16 @@ class IsolatedLabelTest < ActionView::TestCase
263
282
  end
264
283
  end
265
284
 
285
+ test 'label uses custom i18n scope to find required string tag' do
286
+ store_translations(:en, my_scope: { required: { html: '<span class="mandatory" title="Pflichtfeld">!!</span>' } }) do
287
+ swap SimpleForm, i18n_scope: :my_scope do
288
+ with_label_for @user, :name, :string
289
+ assert_no_select 'form label abbr'
290
+ assert_select 'form label span.mandatory[title=Pflichtfeld]', '!!'
291
+ end
292
+ end
293
+ end
294
+
266
295
  test 'label allows overwriting input id' do
267
296
  with_label_for @user, :name, :string, input_html: { id: 'my_new_id' }
268
297
  assert_select 'label[for=my_new_id]'
@@ -289,7 +318,7 @@ class IsolatedLabelTest < ActionView::TestCase
289
318
  end
290
319
 
291
320
  test 'label includes for attribute for select collection' do
292
- with_label_for @user, :sex, :select, collection: [:male, :female]
321
+ with_label_for @user, :sex, :select, collection: %i[male female]
293
322
  assert_select 'label[for=user_sex]'
294
323
  end
295
324
 
@@ -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
@@ -94,13 +100,36 @@ class ErrorTest < ActionView::TestCase
94
100
  assert_no_select 'span.error b', 'markup'
95
101
  end
96
102
 
97
-
98
103
  test 'error generates an error message with raw HTML tags' do
99
104
  with_error_for @user, :name, error_prefix: '<b>Name</b>'.html_safe
100
105
  assert_select 'span.error', "Name cannot be blank"
101
106
  assert_select 'span.error b', "Name"
102
107
  end
103
108
 
109
+ test 'error adds aria-invalid attribute to inputs' do
110
+ with_form_for @user, :name, error: true
111
+ assert_select "input#user_name[name='user[name]'][aria-invalid='true']"
112
+
113
+ with_form_for @user, :name, as: :text, error: true
114
+ assert_select "textarea#user_name[name='user[name]'][aria-invalid='true']"
115
+
116
+ @user.errors.add(:active, 'must select one')
117
+ with_form_for @user, :active, as: :radio_buttons
118
+ assert_select "input#user_active_true[type=radio][name='user[active]'][aria-invalid='true']"
119
+ assert_select "input#user_active_false[type=radio][name='user[active]'][aria-invalid='true']"
120
+
121
+ with_form_for @user, :active, as: :check_boxes
122
+ assert_select "input#user_active_true[type=checkbox][aria-invalid='true']"
123
+ assert_select "input#user_active_false[type=checkbox][aria-invalid='true']"
124
+
125
+ with_form_for @user, :company_id, as: :select, error: true
126
+ assert_select "select#user_company_id[aria-invalid='true']"
127
+
128
+ @user.errors.add(:password, 'must not be blank')
129
+ with_form_for @user, :password
130
+ assert_select "input#user_password[type=password][aria-invalid='true']"
131
+ end
132
+
104
133
  # FULL ERRORS
105
134
 
106
135
  test 'full error generates a full error tag for the attribute' do
@@ -146,14 +175,14 @@ class ErrorTest < ActionView::TestCase
146
175
  # FULL_ERROR_WRAPPER
147
176
 
148
177
  test 'full error finds errors on association' do
149
- swap_wrapper :default, self.custom_wrapper_with_full_error do
178
+ swap_wrapper :default, custom_wrapper_with_full_error do
150
179
  with_form_for @user, :company_id, as: :select
151
180
  assert_select 'span.error', 'Company must be valid'
152
181
  end
153
182
  end
154
183
 
155
184
  test 'full error finds errors on association with reflection' do
156
- swap_wrapper :default, self.custom_wrapper_with_full_error do
185
+ swap_wrapper :default, custom_wrapper_with_full_error do
157
186
  with_form_for @user, :company_id, as: :select,
158
187
  reflection: Association.new(Company, :company, {})
159
188
  assert_select 'span.error', 'Company must be valid'
@@ -161,14 +190,14 @@ class ErrorTest < ActionView::TestCase
161
190
  end
162
191
 
163
192
  test 'full error can be disabled' do
164
- swap_wrapper :default, self.custom_wrapper_with_full_error do
193
+ swap_wrapper :default, custom_wrapper_with_full_error do
165
194
  with_form_for @user, :company_id, as: :select, full_error: false
166
195
  assert_no_select 'span.error'
167
196
  end
168
197
  end
169
198
 
170
199
  test 'full error can be disabled setting error to false' do
171
- swap_wrapper :default, self.custom_wrapper_with_full_error do
200
+ swap_wrapper :default, custom_wrapper_with_full_error do
172
201
  with_form_for @user, :company_id, as: :select, error: false
173
202
  assert_no_select 'span.error'
174
203
  end
@@ -195,8 +224,14 @@ class ErrorTest < ActionView::TestCase
195
224
  assert_no_select 'span.error'
196
225
  end
197
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
+
198
233
  test 'input with custom error works when using full_error component' do
199
- swap_wrapper :default, self.custom_wrapper_with_full_error do
234
+ swap_wrapper :default, custom_wrapper_with_full_error do
200
235
  error_text = "Super User Name! cannot be blank"
201
236
  with_form_for @user, :name, error: error_text
202
237
 
@@ -219,7 +254,7 @@ class ErrorTest < ActionView::TestCase
219
254
  end
220
255
 
221
256
  test 'input with custom error escapes the error text using full_error component' do
222
- swap_wrapper :default, self.custom_wrapper_with_full_error do
257
+ swap_wrapper :default, custom_wrapper_with_full_error do
223
258
  with_form_for @user, :name, error: 'error must not contain <b>markup</b>'
224
259
 
225
260
  assert_select 'span.error'
@@ -228,7 +263,7 @@ class ErrorTest < ActionView::TestCase
228
263
  end
229
264
 
230
265
  test 'input with custom error does not escape the error text if it is safe using full_error component' do
231
- swap_wrapper :default, self.custom_wrapper_with_full_error do
266
+ swap_wrapper :default, custom_wrapper_with_full_error do
232
267
  with_form_for @user, :name, error: 'error must contain <b>markup</b>'.html_safe
233
268
 
234
269
  assert_select 'span.error'
@@ -237,7 +272,7 @@ class ErrorTest < ActionView::TestCase
237
272
  end
238
273
 
239
274
  test 'input with custom error when using full_error component does not generate the error if there is no error on the attribute' do
240
- swap_wrapper :default, self.custom_wrapper_with_full_error do
275
+ swap_wrapper :default, custom_wrapper_with_full_error do
241
276
  with_form_for @user, :active, error: "Super User Active! can't be blank"
242
277
 
243
278
  assert_no_select 'span.error'