simple_form 3.4.0 → 4.1.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of simple_form might be problematic. Click here for more details.

Files changed (103) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +62 -0
  3. data/README.md +208 -36
  4. data/lib/generators/simple_form/install_generator.rb +1 -0
  5. data/lib/generators/simple_form/templates/README +3 -3
  6. data/lib/generators/simple_form/templates/_form.html.erb +2 -0
  7. data/lib/generators/simple_form/templates/_form.html.haml +2 -0
  8. data/lib/generators/simple_form/templates/_form.html.slim +1 -0
  9. data/lib/generators/simple_form/templates/config/initializers/simple_form.rb +15 -5
  10. data/lib/generators/simple_form/templates/config/initializers/simple_form_bootstrap.rb +358 -73
  11. data/lib/generators/simple_form/templates/config/initializers/simple_form_foundation.rb +20 -8
  12. data/lib/simple_form/action_view_extensions/builder.rb +1 -0
  13. data/lib/simple_form/action_view_extensions/form_helper.rb +1 -0
  14. data/lib/simple_form/components/errors.rb +15 -2
  15. data/lib/simple_form/components/hints.rb +1 -0
  16. data/lib/simple_form/components/html5.rb +1 -0
  17. data/lib/simple_form/components/label_input.rb +2 -1
  18. data/lib/simple_form/components/labels.rb +11 -4
  19. data/lib/simple_form/components/maxlength.rb +4 -13
  20. data/lib/simple_form/components/min_max.rb +1 -0
  21. data/lib/simple_form/components/minlength.rb +5 -14
  22. data/lib/simple_form/components/pattern.rb +1 -0
  23. data/lib/simple_form/components/placeholders.rb +2 -1
  24. data/lib/simple_form/components/readonly.rb +1 -0
  25. data/lib/simple_form/components.rb +1 -0
  26. data/lib/simple_form/error_notification.rb +1 -0
  27. data/lib/simple_form/form_builder.rb +81 -26
  28. data/lib/simple_form/helpers/autofocus.rb +1 -0
  29. data/lib/simple_form/helpers/disabled.rb +1 -0
  30. data/lib/simple_form/helpers/readonly.rb +1 -0
  31. data/lib/simple_form/helpers/required.rb +1 -0
  32. data/lib/simple_form/helpers/validators.rb +2 -1
  33. data/lib/simple_form/helpers.rb +1 -0
  34. data/lib/simple_form/i18n_cache.rb +1 -0
  35. data/lib/simple_form/inputs/base.rb +24 -2
  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 +2 -1
  39. data/lib/simple_form/inputs/collection_input.rb +3 -2
  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 -0
  50. data/lib/simple_form/inputs/range_input.rb +1 -0
  51. data/lib/simple_form/inputs/string_input.rb +2 -1
  52. data/lib/simple_form/inputs/text_input.rb +1 -0
  53. data/lib/simple_form/inputs.rb +2 -0
  54. data/lib/simple_form/map_type.rb +1 -0
  55. data/lib/simple_form/railtie.rb +1 -0
  56. data/lib/simple_form/tags.rb +1 -0
  57. data/lib/simple_form/version.rb +2 -1
  58. data/lib/simple_form/wrappers/builder.rb +1 -0
  59. data/lib/simple_form/wrappers/leaf.rb +2 -1
  60. data/lib/simple_form/wrappers/many.rb +1 -0
  61. data/lib/simple_form/wrappers/root.rb +2 -0
  62. data/lib/simple_form/wrappers/single.rb +2 -1
  63. data/lib/simple_form/wrappers.rb +1 -0
  64. data/lib/simple_form.rb +58 -7
  65. data/test/action_view_extensions/builder_test.rb +6 -5
  66. data/test/action_view_extensions/form_helper_test.rb +3 -2
  67. data/test/components/custom_components_test.rb +62 -0
  68. data/test/components/label_test.rb +33 -4
  69. data/test/form_builder/association_test.rb +27 -2
  70. data/test/form_builder/button_test.rb +1 -0
  71. data/test/form_builder/error_notification_test.rb +1 -0
  72. data/test/form_builder/error_test.rb +12 -0
  73. data/test/form_builder/general_test.rb +67 -3
  74. data/test/form_builder/hint_test.rb +6 -0
  75. data/test/form_builder/input_field_test.rb +30 -10
  76. data/test/form_builder/label_test.rb +9 -3
  77. data/test/form_builder/wrapper_test.rb +24 -4
  78. data/test/generators/simple_form_generator_test.rb +4 -3
  79. data/test/inputs/boolean_input_test.rb +17 -0
  80. data/test/inputs/collection_check_boxes_input_test.rb +30 -14
  81. data/test/inputs/collection_radio_buttons_input_test.rb +40 -24
  82. data/test/inputs/collection_select_input_test.rb +40 -39
  83. data/test/inputs/color_input_test.rb +10 -0
  84. data/test/inputs/datetime_input_test.rb +7 -16
  85. data/test/inputs/disabled_test.rb +14 -0
  86. data/test/inputs/discovery_test.rb +22 -0
  87. data/test/inputs/file_input_test.rb +1 -0
  88. data/test/inputs/general_test.rb +3 -2
  89. data/test/inputs/grouped_collection_select_input_test.rb +11 -10
  90. data/test/inputs/hidden_input_test.rb +1 -0
  91. data/test/inputs/numeric_input_test.rb +2 -1
  92. data/test/inputs/priority_input_test.rb +3 -10
  93. data/test/inputs/readonly_test.rb +1 -0
  94. data/test/inputs/required_test.rb +1 -0
  95. data/test/inputs/string_input_test.rb +10 -16
  96. data/test/inputs/text_input_test.rb +1 -0
  97. data/test/simple_form_test.rb +1 -0
  98. data/test/support/discovery_inputs.rb +8 -0
  99. data/test/support/misc_helpers.rb +22 -1
  100. data/test/support/mock_controller.rb +7 -1
  101. data/test/support/models.rb +52 -16
  102. data/test/test_helper.rb +2 -0
  103. metadata +40 -47
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module SimpleForm
2
3
  module Wrappers
3
4
  class Leaf
@@ -11,7 +12,7 @@ module SimpleForm
11
12
  def render(input)
12
13
  method = input.method(@namespace)
13
14
 
14
- if method.arity == 0
15
+ if method.arity.zero?
15
16
  ActiveSupport::Deprecation.warn(SimpleForm::CUSTOM_INPUT_DEPRECATION_WARN % { name: @namespace })
16
17
 
17
18
  method.call
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module SimpleForm
2
3
  module Wrappers
3
4
  # A wrapper is an object that holds several components and render them.
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module SimpleForm
2
3
  module Wrappers
3
4
  # `Root` is the root wrapper for all components. It is special cased to
@@ -29,6 +30,7 @@ module SimpleForm
29
30
  end
30
31
  css << (options[:wrapper_error_class] || @defaults[:error_class]) if input.has_errors?
31
32
  css << (options[:wrapper_hint_class] || @defaults[:hint_class]) if input.has_hint?
33
+ css << (options[:wrapper_valid_class] || @defaults[:valid_class]) if input.valid?
32
34
  css.compact
33
35
  end
34
36
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module SimpleForm
2
3
  module Wrappers
3
4
  # `Single` is an optimization for a wrapper that has only one component.
@@ -19,7 +20,7 @@ module SimpleForm
19
20
  private
20
21
 
21
22
  def html_options(options)
22
- [:label, :input].include?(namespace) ? {} : super
23
+ %i[label input].include?(namespace) ? {} : super
23
24
  end
24
25
  end
25
26
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module SimpleForm
2
3
  module Wrappers
3
4
  autoload :Builder, 'simple_form/wrappers/builder'
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'
@@ -58,11 +60,11 @@ See https://github.com/plataformatec/simple_form/pull/997 for more information.
58
60
 
59
61
  # Series of attemps to detect a default label method for collection.
60
62
  mattr_accessor :collection_label_methods
61
- @@collection_label_methods = [:to_label, :name, :title, :to_s]
63
+ @@collection_label_methods = %i[to_label name title to_s]
62
64
 
63
65
  # Series of attemps to detect a default value method for collection.
64
66
  mattr_accessor :collection_value_methods
65
- @@collection_value_methods = [:id, :to_s]
67
+ @@collection_value_methods = %i[id to_s]
66
68
 
67
69
  # You can wrap a collection of radio/check boxes in a pre-defined tag, defaulting to none.
68
70
  mattr_accessor :collection_wrapper_tag
@@ -84,7 +86,7 @@ See https://github.com/plataformatec/simple_form/pull/997 for more information.
84
86
 
85
87
  # How the label text should be generated altogether with the required text.
86
88
  mattr_accessor :label_text
87
- @@label_text = lambda { |label, required, explicit_label| "#{required} #{label}" }
89
+ @@label_text = ->(label, required, explicit_label) { "#{required} #{label}" }
88
90
 
89
91
  # You can define the class to be used on all labels. Defaults to none.
90
92
  mattr_accessor :label_class
@@ -108,7 +110,7 @@ See https://github.com/plataformatec/simple_form/pull/997 for more information.
108
110
 
109
111
  # You can define which elements should obtain additional classes.
110
112
  mattr_accessor :generate_additional_classes_for
111
- @@generate_additional_classes_for = [:wrapper, :label, :input]
113
+ @@generate_additional_classes_for = %i[wrapper label input]
112
114
 
113
115
  # Whether attributes are required by default or not.
114
116
  mattr_accessor :required_by_default
@@ -120,7 +122,7 @@ See https://github.com/plataformatec/simple_form/pull/997 for more information.
120
122
 
121
123
  # Collection of methods to detect if a file type was given.
122
124
  mattr_accessor :file_methods
123
- @@file_methods = [:mounted_as, :file?, :public_filename]
125
+ @@file_methods = %i[mounted_as file? public_filename attached?]
124
126
 
125
127
  # Custom mappings for input types. This should be a hash containing a regexp
126
128
  # to match as key, and the input type that will be used when the field name
@@ -163,7 +165,7 @@ See https://github.com/plataformatec/simple_form/pull/997 for more information.
163
165
 
164
166
  # Cache SimpleForm inputs discovery.
165
167
  mattr_accessor :cache_discovery
166
- @@cache_discovery = defined?(Rails) && !Rails.env.development?
168
+ @@cache_discovery = defined?(Rails.env) && !Rails.env.development?
167
169
 
168
170
  # Adds a class to each generated button, mostly for compatiblity.
169
171
  mattr_accessor :button_class
@@ -198,6 +200,12 @@ See https://github.com/plataformatec/simple_form/pull/997 for more information.
198
200
  mattr_accessor :i18n_scope
199
201
  @@i18n_scope = 'simple_form'
200
202
 
203
+ mattr_accessor :input_field_error_class
204
+ @@input_field_error_class = nil
205
+
206
+ mattr_accessor :input_field_valid_class
207
+ @@input_field_valid_class = nil
208
+
201
209
  # Retrieves a given wrapper
202
210
  def self.wrapper(name)
203
211
  @@wrappers[name.to_s] or raise WrapperNotFound, "Couldn't find wrapper with name #{name}"
@@ -227,7 +235,7 @@ See https://github.com/plataformatec/simple_form/pull/997 for more information.
227
235
  SimpleForm::Wrappers::Root.new(builder.to_a, options)
228
236
  end
229
237
 
230
- wrappers class: :input, hint_class: :field_with_hint, error_class: :field_with_errors do |b|
238
+ wrappers class: :input, hint_class: :field_with_hint, error_class: :field_with_errors, valid_class: :field_without_errors do |b|
231
239
  b.use :html5
232
240
 
233
241
  b.use :min_max
@@ -263,6 +271,49 @@ See https://github.com/plataformatec/simple_form/pull/997 for more information.
263
271
  @@configured = true
264
272
  yield self
265
273
  end
274
+
275
+ # Includes a component to be used by Simple Form. Methods defined in a
276
+ # component will be exposed to be used in the wrapper as Simple::Components
277
+ #
278
+ # Examples
279
+ #
280
+ # # The application needs to tell where the components will be.
281
+ # Dir[Rails.root.join('lib/components/**/*.rb')].each { |f| require f }
282
+ #
283
+ # # Create a custom component in the path specified above.
284
+ # # lib/components/input_group_component.rb
285
+ # module InputGroupComponent
286
+ # def prepend
287
+ # ...
288
+ # end
289
+ #
290
+ # def append
291
+ # ...
292
+ # end
293
+ # end
294
+ #
295
+ # SimpleForm.setup do |config|
296
+ # # Create a wrapper using the custom component.
297
+ # config.wrappers :input_group, tag: :div, error_class: :error do |b|
298
+ # b.use :label
299
+ # b.optional :prepend
300
+ # b.use :input
301
+ # b.use :append
302
+ # end
303
+ # end
304
+ #
305
+ # # Using the custom component in the form.
306
+ # <%= simple_form_for @blog, wrapper: input_group do |f| %>
307
+ # <%= f.input :title, prepend: true %>
308
+ # <% end %>
309
+ #
310
+ def self.include_component(component)
311
+ if Module === component
312
+ SimpleForm::Inputs::Base.include(component)
313
+ else
314
+ raise TypeError, "SimpleForm.include_component expects a module but got: #{component.class}"
315
+ end
316
+ end
266
317
  end
267
318
 
268
319
  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'
@@ -243,7 +244,7 @@ class BuilderTest < ActionView::TestCase
243
244
 
244
245
  test "collection radio with block helpers does not leak the template" do
245
246
  with_concat_form_for(@user) do |f|
246
- collection_input = f.collection_radio_buttons :active, [true, false], :to_s, :to_s do |b|
247
+ collection_input = f.collection_radio_buttons :active, [true, false], :to_s, :to_s do |b|
247
248
  b.label(class: b.object) { b.radio_button + b.text }
248
249
  end
249
250
  concat collection_input
@@ -283,7 +284,7 @@ class BuilderTest < ActionView::TestCase
283
284
  end
284
285
 
285
286
  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
287
+ with_collection_check_boxes @user, :active, %w[Yes No], :to_s, :to_s
287
288
 
288
289
  assert_select 'form label.collection_check_boxes[for=user_active_yes]', 'Yes'
289
290
  assert_select 'form label.collection_check_boxes[for=user_active_no]', 'No'
@@ -317,7 +318,7 @@ class BuilderTest < ActionView::TestCase
317
318
 
318
319
  test "collection check boxes accepts selected string values as :checked option" do
319
320
  collection = (1..3).map { |i| [i, "Category #{i}"] }
320
- with_collection_check_boxes :user, :category_ids, collection, :first, :last, checked: ['1', '3']
321
+ with_collection_check_boxes :user, :category_ids, collection, :first, :last, checked: %w[1 3]
321
322
 
322
323
  assert_select 'input[type=checkbox][value="1"][checked=checked]'
323
324
  assert_select 'input[type=checkbox][value="3"][checked=checked]'
@@ -541,7 +542,7 @@ class BuilderTest < ActionView::TestCase
541
542
 
542
543
  test "collection check boxes with block helpers does not leak the template" do
543
544
  with_concat_form_for(@user) do |f|
544
- collection_input = f.collection_check_boxes :active, [true, false], :to_s, :to_s do |b|
545
+ collection_input = f.collection_check_boxes :active, [true, false], :to_s, :to_s do |b|
545
546
  b.label(class: b.object) { b.check_box + b.text }
546
547
  end
547
548
  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,12 @@ 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
221
246
  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'
122
130
  assert_select 'form textarea#user_description.text'
123
131
  end
124
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'
136
+ assert_select 'form textarea#user_description.text'
137
+ end
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'
@@ -199,6 +242,16 @@ class FormBuilderTest < ActionView::TestCase
199
242
  test 'builder generates file for file columns' do
200
243
  @user.avatar = MiniTest::Mock.new
201
244
  @user.avatar.expect(:public_filename, true)
245
+ @user.avatar.expect(:!, false)
246
+
247
+ with_form_for @user, :avatar
248
+ assert_select 'form input#user_avatar.file'
249
+ end
250
+
251
+ test 'builder generates file for activestorage entries' do
252
+ @user.avatar = MiniTest::Mock.new
253
+ @user.avatar.expect(:attached?, false)
254
+ @user.avatar.expect(:!, false)
202
255
 
203
256
  with_form_for @user, :avatar
204
257
  assert_select 'form input#user_avatar.file'
@@ -207,6 +260,7 @@ class FormBuilderTest < ActionView::TestCase
207
260
  test 'builder generates file for attributes that are real db columns but have file methods' do
208
261
  @user.home_picture = MiniTest::Mock.new
209
262
  @user.home_picture.expect(:mounted_as, true)
263
+ @user.home_picture.expect(:!, false)
210
264
 
211
265
  with_form_for @user, :home_picture
212
266
  assert_select 'form input#user_home_picture.file'
@@ -217,15 +271,25 @@ class FormBuilderTest < ActionView::TestCase
217
271
  assert_select 'form select#user_age.select'
218
272
  end
219
273
 
274
+ test 'builder does not generate url fields for columns that contain only the letters url' do
275
+ with_form_for @user, :hourly
276
+ assert_no_select 'form input#user_url.string.url'
277
+ assert_select 'form input#user_hourly.string'
278
+ end
279
+
220
280
  test 'builder allows overriding default input type for text' do
221
281
  with_form_for @user, :name, as: :text
222
282
  assert_no_select 'form input#user_name'
223
283
  assert_select 'form textarea#user_name.text'
284
+ end
224
285
 
286
+ test 'builder allows overriding default input type for radio_buttons' do
225
287
  with_form_for @user, :active, as: :radio_buttons
226
288
  assert_no_select 'form input[type=checkbox]'
227
289
  assert_select 'form input.radio_buttons[type=radio]', count: 2
290
+ end
228
291
 
292
+ test 'builder allows overriding default input type for string' do
229
293
  with_form_for @user, :born_at, as: :string
230
294
  assert_no_select 'form select'
231
295
  assert_select 'form input#user_born_at.string'
@@ -365,7 +429,7 @@ class FormBuilderTest < ActionView::TestCase
365
429
  end
366
430
 
367
431
  # DEFAULT OPTIONS
368
- [:input, :input_field].each do |method|
432
+ %i[input input_field].each do |method|
369
433
  test "builder receives a default argument and pass it to the inputs when calling '#{method}'" do
370
434
  with_concat_form_for @user, defaults: { input_html: { class: 'default_class' } } do |f|
371
435
  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