simple_form 4.0.1 → 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.
- checksums.yaml +5 -5
- data/CHANGELOG.md +68 -8
- data/MIT-LICENSE +2 -1
- data/README.md +47 -51
- data/lib/generators/simple_form/templates/README +2 -3
- data/lib/generators/simple_form/templates/config/initializers/simple_form.rb +1 -7
- data/lib/generators/simple_form/templates/config/initializers/simple_form_bootstrap.rb +26 -25
- data/lib/generators/simple_form/templates/config/initializers/simple_form_foundation.rb +1 -1
- data/lib/simple_form.rb +22 -5
- data/lib/simple_form/components/errors.rb +5 -1
- data/lib/simple_form/components/labels.rb +3 -5
- data/lib/simple_form/components/maxlength.rb +0 -4
- data/lib/simple_form/components/minlength.rb +0 -4
- data/lib/simple_form/form_builder.rb +30 -10
- data/lib/simple_form/inputs.rb +2 -0
- data/lib/simple_form/inputs/base.rb +4 -4
- data/lib/simple_form/inputs/boolean_input.rb +1 -0
- data/lib/simple_form/inputs/collection_check_boxes_input.rb +1 -1
- data/lib/simple_form/inputs/collection_input.rb +3 -5
- data/lib/simple_form/inputs/color_input.rb +14 -0
- data/lib/simple_form/inputs/priority_input.rb +0 -4
- data/lib/simple_form/inputs/rich_text_area_input.rb +12 -0
- data/lib/simple_form/inputs/string_input.rb +1 -1
- data/lib/simple_form/tags.rb +6 -2
- data/lib/simple_form/version.rb +1 -1
- data/lib/simple_form/wrappers/root.rb +8 -3
- data/test/action_view_extensions/builder_test.rb +22 -4
- data/test/components/label_test.rb +0 -4
- data/test/form_builder/association_test.rb +6 -0
- data/test/form_builder/error_test.rb +6 -0
- data/test/form_builder/general_test.rb +23 -19
- data/test/form_builder/input_field_test.rb +3 -9
- data/test/form_builder/label_test.rb +1 -1
- data/test/form_builder/wrapper_test.rb +8 -1
- data/test/inputs/boolean_input_test.rb +8 -0
- data/test/inputs/collection_check_boxes_input_test.rb +8 -4
- data/test/inputs/collection_radio_buttons_input_test.rb +8 -4
- data/test/inputs/collection_select_input_test.rb +6 -4
- data/test/inputs/color_input_test.rb +10 -0
- data/test/inputs/datetime_input_test.rb +2 -12
- data/test/inputs/disabled_test.rb +13 -0
- data/test/inputs/discovery_test.rb +21 -0
- data/test/inputs/priority_input_test.rb +6 -14
- data/test/inputs/rich_text_area_input_test.rb +15 -0
- data/test/inputs/string_input_test.rb +8 -15
- data/test/support/discovery_inputs.rb +7 -0
- data/test/support/misc_helpers.rb +8 -2
- data/test/support/models.rb +29 -6
- data/test/test_helper.rb +7 -4
- metadata +44 -40
- data/lib/simple_form/i18n_cache.rb +0 -23
@@ -2,7 +2,7 @@
|
|
2
2
|
#
|
3
3
|
# Uncomment this and change the path if necessary to include your own
|
4
4
|
# components.
|
5
|
-
# See https://github.com/
|
5
|
+
# See https://github.com/heartcombo/simple_form#custom-components to know
|
6
6
|
# more about custom components.
|
7
7
|
# Dir[Rails.root.join('lib/components/**/*.rb')].each { |f| require f }
|
8
8
|
#
|
data/lib/simple_form.rb
CHANGED
@@ -35,7 +35,18 @@ to
|
|
35
35
|
|
36
36
|
def %{name}(wrapper_options)
|
37
37
|
|
38
|
-
See https://github.com/
|
38
|
+
See https://github.com/heartcombo/simple_form/pull/997 for more information.
|
39
|
+
WARN
|
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.
|
39
50
|
WARN
|
40
51
|
|
41
52
|
@@configured = false
|
@@ -120,10 +131,6 @@ See https://github.com/plataformatec/simple_form/pull/997 for more information.
|
|
120
131
|
mattr_accessor :browser_validations
|
121
132
|
@@browser_validations = true
|
122
133
|
|
123
|
-
# Collection of methods to detect if a file type was given.
|
124
|
-
mattr_accessor :file_methods
|
125
|
-
@@file_methods = %i[mounted_as file? public_filename attached?]
|
126
|
-
|
127
134
|
# Custom mappings for input types. This should be a hash containing a regexp
|
128
135
|
# to match as key, and the input type that will be used when the field name
|
129
136
|
# matches the regexp as value, such as { /count/ => :integer }.
|
@@ -265,6 +272,16 @@ See https://github.com/plataformatec/simple_form/pull/997 for more information.
|
|
265
272
|
@@form_class = value
|
266
273
|
end
|
267
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
|
+
|
268
285
|
# Default way to setup Simple Form. Run rails generate simple_form:install
|
269
286
|
# to create a fresh initializer with all configuration values.
|
270
287
|
def self.setup
|
@@ -11,7 +11,7 @@ module SimpleForm
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def has_errors?
|
14
|
-
|
14
|
+
object_with_errors? || object.nil? && has_custom_error?
|
15
15
|
end
|
16
16
|
|
17
17
|
def has_value?
|
@@ -34,6 +34,10 @@ module SimpleForm
|
|
34
34
|
has_custom_error? ? options[:error] : full_errors.send(error_method)
|
35
35
|
end
|
36
36
|
|
37
|
+
def object_with_errors?
|
38
|
+
object && object.respond_to?(:errors) && errors.present?
|
39
|
+
end
|
40
|
+
|
37
41
|
def error_method
|
38
42
|
options[:error_method] || SimpleForm.error_method
|
39
43
|
end
|
@@ -6,11 +6,9 @@ module SimpleForm
|
|
6
6
|
|
7
7
|
module ClassMethods #:nodoc:
|
8
8
|
def translate_required_html
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
)
|
13
|
-
end
|
9
|
+
I18n.t(:"required.html", scope: i18n_scope, default:
|
10
|
+
%(<abbr title="#{translate_required_text}">#{translate_required_mark}</abbr>)
|
11
|
+
)
|
14
12
|
end
|
15
13
|
|
16
14
|
def translate_required_text
|
@@ -24,10 +24,6 @@ module SimpleForm
|
|
24
24
|
find_validator(:length)
|
25
25
|
end
|
26
26
|
|
27
|
-
def has_tokenizer?(length_validator)
|
28
|
-
length_validator.options[:tokenizer]
|
29
|
-
end
|
30
|
-
|
31
27
|
def maximum_length_value_from(length_validator)
|
32
28
|
if length_validator
|
33
29
|
length_validator.options[:is] || length_validator.options[:maximum]
|
@@ -24,10 +24,6 @@ module SimpleForm
|
|
24
24
|
find_validator(:length)
|
25
25
|
end
|
26
26
|
|
27
|
-
def has_tokenizer?(length_validator)
|
28
|
-
length_validator.options[:tokenizer]
|
29
|
-
end
|
30
|
-
|
31
27
|
def minimum_length_value_from(length_validator)
|
32
28
|
if length_validator
|
33
29
|
length_validator.options[:is] || length_validator.options[:minimum]
|
@@ -26,6 +26,7 @@ module SimpleForm
|
|
26
26
|
map_type :range, to: SimpleForm::Inputs::RangeInput
|
27
27
|
map_type :check_boxes, to: SimpleForm::Inputs::CollectionCheckBoxesInput
|
28
28
|
map_type :radio_buttons, to: SimpleForm::Inputs::CollectionRadioButtonsInput
|
29
|
+
map_type :rich_text_area, to: SimpleForm::Inputs::RichTextAreaInput
|
29
30
|
map_type :select, to: SimpleForm::Inputs::CollectionSelectInput
|
30
31
|
map_type :grouped_select, to: SimpleForm::Inputs::GroupedCollectionSelectInput
|
31
32
|
map_type :date, :time, :datetime, to: SimpleForm::Inputs::DateTimeInput
|
@@ -165,7 +166,7 @@ module SimpleForm
|
|
165
166
|
components = (wrapper.components.map(&:namespace) & ATTRIBUTE_COMPONENTS)
|
166
167
|
|
167
168
|
options = options.dup
|
168
|
-
options[:input_html] = options.except(:as, :boolean_style, :collection, :label_method, :value_method, :prompt, *components)
|
169
|
+
options[:input_html] = options.except(:as, :boolean_style, :collection, :disabled, :label_method, :value_method, :prompt, *components)
|
169
170
|
options = @defaults.deep_dup.deep_merge(options) if @defaults
|
170
171
|
|
171
172
|
input = find_input(attribute_name, options)
|
@@ -511,7 +512,7 @@ module SimpleForm
|
|
511
512
|
when :has_one
|
512
513
|
raise ArgumentError, ":has_one associations are not supported by f.association"
|
513
514
|
else
|
514
|
-
if options[:as] == :select
|
515
|
+
if options[:as] == :select || options[:as] == :grouped_select
|
515
516
|
html_options = options[:input_html] ||= {}
|
516
517
|
html_options[:multiple] = true unless html_options.key?(:multiple)
|
517
518
|
end
|
@@ -552,12 +553,12 @@ module SimpleForm
|
|
552
553
|
:datetime
|
553
554
|
when :string, :citext, nil
|
554
555
|
case attribute_name.to_s
|
555
|
-
when /password/ then :password
|
556
|
-
when /time_zone/ then :time_zone
|
557
|
-
when /country/ then :country
|
558
|
-
when /email/ then :email
|
559
|
-
when /phone/ then :tel
|
560
|
-
when /url/ then :url
|
556
|
+
when /(?:\b|\W|_)password(?:\b|\W|_)/ then :password
|
557
|
+
when /(?:\b|\W|_)time_zone(?:\b|\W|_)/ then :time_zone
|
558
|
+
when /(?:\b|\W|_)country(?:\b|\W|_)/ then :country
|
559
|
+
when /(?:\b|\W|_)email(?:\b|\W|_)/ then :email
|
560
|
+
when /(?:\b|\W|_)phone(?:\b|\W|_)/ then :tel
|
561
|
+
when /(?:\b|\W|_)url(?:\b|\W|_)/ then :url
|
561
562
|
else
|
562
563
|
file_method?(attribute_name) ? :file : (input_type || :string)
|
563
564
|
end
|
@@ -572,9 +573,28 @@ module SimpleForm
|
|
572
573
|
}.try(:last) if SimpleForm.input_mappings
|
573
574
|
end
|
574
575
|
|
576
|
+
# Internal: Try to discover whether an attribute corresponds to a file or not.
|
577
|
+
#
|
578
|
+
# Most upload Gems add some kind of attributes to the ActiveRecord's model they are included in.
|
579
|
+
# This method tries to guess if an attribute belongs to some of these Gems by checking the presence
|
580
|
+
# of their methods using `#respond_to?`.
|
581
|
+
#
|
582
|
+
# Note: This does not support multiple file upload inputs, as this is very application-specific.
|
583
|
+
#
|
584
|
+
# The order here was chosen based on the popularity of Gems:
|
585
|
+
#
|
586
|
+
# - `#{attribute_name}_attachment` - ActiveStorage >= `5.2` and Refile >= `0.2.0` <= `0.4.0`
|
587
|
+
# - `remote_#{attribute_name}_url` - Refile >= `0.3.0` and CarrierWave >= `0.2.2`
|
588
|
+
# - `#{attribute_name}_attacher` - Refile >= `0.4.0` and Shrine >= `0.9.0`
|
589
|
+
# - `#{attribute_name}_file_name` - Paperclip ~> `2.0` (added for backwards compatibility)
|
590
|
+
#
|
591
|
+
# Returns a Boolean.
|
575
592
|
def file_method?(attribute_name)
|
576
|
-
|
577
|
-
|
593
|
+
@object.respond_to?("#{attribute_name}_attachment") ||
|
594
|
+
@object.respond_to?("#{attribute_name}_attachments") ||
|
595
|
+
@object.respond_to?("remote_#{attribute_name}_url") ||
|
596
|
+
@object.respond_to?("#{attribute_name}_attacher") ||
|
597
|
+
@object.respond_to?("#{attribute_name}_file_name")
|
578
598
|
end
|
579
599
|
|
580
600
|
def find_attribute_column(attribute_name)
|
data/lib/simple_form/inputs.rb
CHANGED
@@ -10,6 +10,7 @@ module SimpleForm
|
|
10
10
|
autoload :CollectionInput
|
11
11
|
autoload :CollectionRadioButtonsInput
|
12
12
|
autoload :CollectionSelectInput
|
13
|
+
autoload :ColorInput
|
13
14
|
autoload :DateTimeInput
|
14
15
|
autoload :FileInput
|
15
16
|
autoload :GroupedCollectionSelectInput
|
@@ -18,6 +19,7 @@ module SimpleForm
|
|
18
19
|
autoload :PasswordInput
|
19
20
|
autoload :PriorityInput
|
20
21
|
autoload :RangeInput
|
22
|
+
autoload :RichTextAreaInput
|
21
23
|
autoload :StringInput
|
22
24
|
autoload :TextInput
|
23
25
|
end
|
@@ -1,5 +1,4 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
require 'simple_form/i18n_cache'
|
3
2
|
require 'active_support/core_ext/string/output_safety'
|
4
3
|
require 'action_view/helpers'
|
5
4
|
|
@@ -9,8 +8,6 @@ module SimpleForm
|
|
9
8
|
include ERB::Util
|
10
9
|
include ActionView::Helpers::TranslationHelper
|
11
10
|
|
12
|
-
extend I18nCache
|
13
|
-
|
14
11
|
include SimpleForm::Helpers::Autofocus
|
15
12
|
include SimpleForm::Helpers::Disabled
|
16
13
|
include SimpleForm::Helpers::Readonly
|
@@ -72,7 +69,10 @@ module SimpleForm
|
|
72
69
|
@html_classes = SimpleForm.additional_classes_for(:input) { additional_classes }
|
73
70
|
|
74
71
|
@input_html_classes = @html_classes.dup
|
75
|
-
|
72
|
+
|
73
|
+
input_html_classes = self.input_html_classes
|
74
|
+
|
75
|
+
if SimpleForm.input_class && input_html_classes.any?
|
76
76
|
input_html_classes << SimpleForm.input_class
|
77
77
|
end
|
78
78
|
|
@@ -63,6 +63,7 @@ module SimpleForm
|
|
63
63
|
return "" if !include_hidden? || !unchecked_value
|
64
64
|
options = { value: unchecked_value, id: nil, disabled: input_html_options[:disabled] }
|
65
65
|
options[:name] = input_html_options[:name] if input_html_options.key?(:name)
|
66
|
+
options[:form] = input_html_options[:form] if input_html_options.key?(:form)
|
66
67
|
|
67
68
|
@builder.hidden_field(attribute_name, options)
|
68
69
|
end
|
@@ -5,7 +5,7 @@ module SimpleForm
|
|
5
5
|
protected
|
6
6
|
|
7
7
|
# Checkbox components do not use the required html tag.
|
8
|
-
# More info: https://github.com/
|
8
|
+
# More info: https://github.com/heartcombo/simple_form/issues/340#issuecomment-2871956
|
9
9
|
def has_required?
|
10
10
|
false
|
11
11
|
end
|
@@ -10,10 +10,8 @@ module SimpleForm
|
|
10
10
|
# Texts can be translated using i18n in "simple_form.yes" and
|
11
11
|
# "simple_form.no" keys. See the example locale file.
|
12
12
|
def self.boolean_collection
|
13
|
-
|
14
|
-
[
|
15
|
-
[I18n.t(:"simple_form.no", default: 'No'), false] ]
|
16
|
-
end
|
13
|
+
[ [I18n.t(:"simple_form.yes", default: 'Yes'), true],
|
14
|
+
[I18n.t(:"simple_form.no", default: 'No'), false] ]
|
17
15
|
end
|
18
16
|
|
19
17
|
def input(wrapper_options = nil)
|
@@ -41,7 +39,7 @@ module SimpleForm
|
|
41
39
|
end
|
42
40
|
|
43
41
|
def has_required?
|
44
|
-
super && (input_options[:include_blank] || input_options[:prompt] || multiple?)
|
42
|
+
super && (input_options[:include_blank] || input_options[:prompt].present? || multiple?)
|
45
43
|
end
|
46
44
|
|
47
45
|
# Check if :include_blank must be included by default.
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module SimpleForm
|
3
|
+
module Inputs
|
4
|
+
class ColorInput < Base
|
5
|
+
def input(wrapper_options = nil)
|
6
|
+
input_html_options[:type] ||= "color" if html5?
|
7
|
+
|
8
|
+
merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
|
9
|
+
|
10
|
+
@builder.text_field(attribute_name, merged_input_options)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module SimpleForm
|
3
|
+
module Inputs
|
4
|
+
class RichTextAreaInput < Base
|
5
|
+
def input(wrapper_options = nil)
|
6
|
+
merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
|
7
|
+
|
8
|
+
@builder.rich_text_area(attribute_name, merged_input_options)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
data/lib/simple_form/tags.rb
CHANGED
@@ -48,7 +48,9 @@ module SimpleForm
|
|
48
48
|
private
|
49
49
|
|
50
50
|
def render_component(builder)
|
51
|
-
|
51
|
+
label_class = "#{@options[:item_label_class]} collection_radio_buttons".strip
|
52
|
+
|
53
|
+
builder.radio_button + builder.label(class: label_class)
|
52
54
|
end
|
53
55
|
end
|
54
56
|
|
@@ -62,7 +64,9 @@ module SimpleForm
|
|
62
64
|
private
|
63
65
|
|
64
66
|
def render_component(builder)
|
65
|
-
|
67
|
+
label_class = "#{@options[:item_label_class]} collection_check_boxes".strip
|
68
|
+
|
69
|
+
builder.check_box + builder.label(class: label_class)
|
66
70
|
end
|
67
71
|
end
|
68
72
|
end
|
data/lib/simple_form/version.rb
CHANGED
@@ -28,11 +28,16 @@ module SimpleForm
|
|
28
28
|
css += SimpleForm.additional_classes_for(:wrapper) do
|
29
29
|
input.additional_classes + [input.input_class]
|
30
30
|
end
|
31
|
-
css << (
|
32
|
-
css << (
|
33
|
-
css << (
|
31
|
+
css << html_class(:error_class, options) { input.has_errors? }
|
32
|
+
css << html_class(:hint_class, options) { input.has_hint? }
|
33
|
+
css << html_class(:valid_class, options) { input.valid? }
|
34
34
|
css.compact
|
35
35
|
end
|
36
|
+
|
37
|
+
def html_class(key, options)
|
38
|
+
css = (options[:"wrapper_#{key}"] || @defaults[key])
|
39
|
+
css if css && yield
|
40
|
+
end
|
36
41
|
end
|
37
42
|
end
|
38
43
|
end
|
@@ -45,8 +45,17 @@ class BuilderTest < ActionView::TestCase
|
|
45
45
|
|
46
46
|
test "collection radio sanitizes collection values for labels correctly" do
|
47
47
|
with_collection_radio_buttons @user, :name, ['$0.99', '$1.99'], :to_s, :to_s
|
48
|
-
|
49
|
-
|
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
|
50
59
|
end
|
51
60
|
|
52
61
|
test "collection radio checks the correct value to local variables" do
|
@@ -292,8 +301,17 @@ class BuilderTest < ActionView::TestCase
|
|
292
301
|
|
293
302
|
test "collection check box sanitizes collection values for labels correctly" do
|
294
303
|
with_collection_check_boxes @user, :name, ['$0.99', '$1.99'], :to_s, :to_s
|
295
|
-
|
296
|
-
|
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
|
297
315
|
end
|
298
316
|
|
299
317
|
test "collection check box checks the correct value to local variables" do
|
@@ -4,10 +4,6 @@ require 'test_helper'
|
|
4
4
|
|
5
5
|
# Isolated tests for label without triggering f.label.
|
6
6
|
class IsolatedLabelTest < ActionView::TestCase
|
7
|
-
setup do
|
8
|
-
SimpleForm::Inputs::Base.reset_i18n_cache :translate_required_html
|
9
|
-
end
|
10
|
-
|
11
7
|
def with_label_for(object, attribute_name, type, options = {})
|
12
8
|
with_concat_form_for(object) do |f|
|
13
9
|
options[:reflection] = Association.new(Company, :company, {}) if options.delete(:setup_association)
|
@@ -243,4 +243,10 @@ class AssociationTest < ActionView::TestCase
|
|
243
243
|
assert_equal({ as: :check_boxes, collection_wrapper_tag: :ul, item_wrapper_tag: :li },
|
244
244
|
options)
|
245
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
|
246
252
|
end
|