diff --git a/.travis.yml b/.travis.yml index 331435e7..1cdf0965 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,6 +2,12 @@ language: ruby rvm: - 1.9.3 - 2.0.0 +gemfile: + - gemfiles/Gemfile.rails-head + - Gemfile +matrix: + allow_failures: + - gemfile: gemfiles/Gemfile.rails-head notifications: email: false campfire: diff --git a/CHANGELOG.md b/CHANGELOG.md index 67a7fa10..083329ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,12 +2,21 @@ ### enhancements * For radio or checkbox collection always use `:item_wrapper_tag` to wrap the content and add `label` when using `boolean_style` with `:nested` [@kassio](https://github.com/kassio) and [@bernardoamc](https://github.com/bernardoamc) + * `input_field` uses the same wrapper as input but only with attribute components. [@nashby](https://github.com/nashby) * Add wrapper mapping per form basis [@rcillo](https://github.com/rcillo) and [@bernardoamc](https://github.com/bernardoamc) * Add `for` attribute to `label` when collections are rendered as radio or checkbox [@erichkist](https://github.com/erichkist), [@ulissesalmeida](https://github.com/ulissesalmeida) and [@fabioyamate](https://github.com/fabioyamate) + * Add `include_default_input_wrapper_class` config [@luizcosta](https://github.com/luizcosta) + * Map `datetime`, `date` and `time` input types to their respective HTML5 input tags + when the `:html5` is set to `true` [@volmer](https://github.com/volmer) ### bug fix * Collection input generates `required` attribute if it has `prompt` option. [@nashby](https://github.com/nashby) +## 3.0.1 + +### bug fix + * Fix XSS vulnerability on label, hint and error components. + ## 3.0.0 ### enhancements diff --git a/Gemfile b/Gemfile index eb04fccf..d2d6ea04 100644 --- a/Gemfile +++ b/Gemfile @@ -3,9 +3,9 @@ source 'https://rubygems.org' gemspec gem 'country_select', '~> 1.1.1' -gem 'railties', '>= 4.0.0', '< 4.1' -gem 'activemodel', '>= 4.0.0', '< 4.1' -gem 'actionpack', '>= 4.0.0', '< 4.1' +gem 'railties', github: 'rails/rails', branch: '4-0-stable' +gem 'activemodel', github: 'rails/rails', branch: '4-0-stable' +gem 'actionpack', github: 'rails/rails', branch: '4-0-stable' gem 'rake' gem 'rdoc' gem 'tzinfo' diff --git a/Gemfile.lock b/Gemfile.lock index 46d7040a..8cfe7adc 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,60 +1,66 @@ +GIT + remote: git://github.com/rails/rails.git + revision: b632a8237745c62747558551c74e32a3bb056e09 + branch: 4-0-stable + specs: + actionpack (4.0.2) + activesupport (= 4.0.2) + builder (~> 3.1.0) + erubis (~> 2.7.0) + rack (~> 1.5.2) + rack-test (~> 0.6.2) + activemodel (4.0.2) + activesupport (= 4.0.2) + builder (~> 3.1.0) + activesupport (4.0.2) + i18n (~> 0.6, >= 0.6.4) + minitest (~> 4.2) + multi_json (~> 1.3) + thread_safe (~> 0.1) + tzinfo (~> 0.3.37) + railties (4.0.2) + actionpack (= 4.0.2) + activesupport (= 4.0.2) + rake (>= 0.8.7) + thor (>= 0.18.1, < 2.0) + PATH remote: . specs: - simple_form (3.0.0) + simple_form (3.0.1) actionpack (>= 4.0.0, < 4.1) activemodel (>= 4.0.0, < 4.1) GEM remote: https://rubygems.org/ specs: - actionpack (4.0.0) - activesupport (= 4.0.0) - builder (~> 3.1.0) - erubis (~> 2.7.0) - rack (~> 1.5.2) - rack-test (~> 0.6.2) - activemodel (4.0.0) - activesupport (= 4.0.0) - builder (~> 3.1.0) - activesupport (4.0.0) - i18n (~> 0.6, >= 0.6.4) - minitest (~> 4.2) - multi_json (~> 1.3) - thread_safe (~> 0.1) - tzinfo (~> 0.3.37) - atomic (1.1.9) + atomic (1.1.14) builder (3.1.4) country_select (1.1.3) erubis (2.7.0) - i18n (0.6.4) - json (1.7.7) + i18n (0.6.9) + json (1.8.1) minitest (4.7.5) - multi_json (1.7.7) + multi_json (1.8.2) rack (1.5.2) rack-test (0.6.2) rack (>= 1.0) - railties (4.0.0) - actionpack (= 4.0.0) - activesupport (= 4.0.0) - rake (>= 0.8.7) - thor (>= 0.18.1, < 2.0) - rake (10.0.4) + rake (10.1.0) rdoc (4.0.1) json (~> 1.4) thor (0.18.1) - thread_safe (0.1.0) + thread_safe (0.1.3) atomic - tzinfo (0.3.37) + tzinfo (0.3.38) PLATFORMS ruby DEPENDENCIES - actionpack (>= 4.0.0, < 4.1) - activemodel (>= 4.0.0, < 4.1) + actionpack! + activemodel! country_select (~> 1.1.1) - railties (>= 4.0.0, < 4.1) + railties! rake rdoc simple_form! diff --git a/README.md b/README.md index 7b3dbcab..ad62cb4a 100644 --- a/README.md +++ b/README.md @@ -500,9 +500,9 @@ specifying the helper method in the column `Mapping` as the `as:` option. `float` | `input[type=number]` | `float` `decimal` | `input[type=number]` | `decimal` `range` | `input[type=range]` | - - `datetime` | `input[type=datetime]` | `datetime/timestamp` - `date` | `input[type=date]` | `date` - `time` | `input[type=time]` | `time` + `datetime` | `datetime select` | `datetime/timestamp` + `date` | `date select` | `date` + `time` | `time select` | `time` `select` | `select` | `belongs_to`/`has_many`/`has_and_belongs_to_many` associations `radio_buttons` | collection of `input[type=radio]` | `belongs_to` associations `check_boxes` | collection of `input[type=checkbox]` | `has_many`/`has_and_belongs_to_many` associations diff --git a/gemfiles/Gemfile.rails-head b/gemfiles/Gemfile.rails-head new file mode 100644 index 00000000..968c0042 --- /dev/null +++ b/gemfiles/Gemfile.rails-head @@ -0,0 +1,11 @@ +source 'https://rubygems.org' + +gemspec :path => '..' + +gem 'country_select', '~> 1.1.1' +gem 'railties', github: 'rails/rails' +gem 'activemodel', github: 'rails/rails' +gem 'actionpack', github: 'rails/rails' +gem 'rake' +gem 'rdoc' +gem 'tzinfo' diff --git a/lib/generators/simple_form/templates/config/initializers/simple_form.rb b/lib/generators/simple_form/templates/config/initializers/simple_form.rb index ecd67dc2..328bea0c 100644 --- a/lib/generators/simple_form/templates/config/initializers/simple_form.rb +++ b/lib/generators/simple_form/templates/config/initializers/simple_form.rb @@ -142,4 +142,8 @@ SimpleForm.setup do |config| # Default class for inputs # config.input_class = nil + + # Defines if the default input wrapper class should be included in radio + # collection wrappers. + # config.include_default_input_wrapper_class = true end diff --git a/lib/simple_form.rb b/lib/simple_form.rb index 89ca9d67..4f977dea 100644 --- a/lib/simple_form.rb +++ b/lib/simple_form.rb @@ -153,6 +153,10 @@ module SimpleForm mattr_accessor :input_class @@input_class = nil + # Defines if an input wrapper class should be included or not + mattr_accessor :include_default_input_wrapper_class + @@include_default_input_wrapper_class = true + ## WRAPPER CONFIGURATION # The default wrapper to be used by the FormBuilder. mattr_accessor :default_wrapper diff --git a/lib/simple_form/components/errors.rb b/lib/simple_form/components/errors.rb index 74d6f5ee..aad48335 100644 --- a/lib/simple_form/components/errors.rb +++ b/lib/simple_form/components/errors.rb @@ -12,7 +12,7 @@ module SimpleForm protected def error_text - "#{options[:error_prefix]} #{errors.send(error_method)}".lstrip.html_safe + "#{html_escape(options[:error_prefix])} #{errors.send(error_method)}".lstrip.html_safe end def error_method diff --git a/lib/simple_form/components/hints.rb b/lib/simple_form/components/hints.rb index a95f9c5e..f9890d5b 100644 --- a/lib/simple_form/components/hints.rb +++ b/lib/simple_form/components/hints.rb @@ -5,8 +5,13 @@ module SimpleForm def hint @hint ||= begin hint = options[:hint] - hint_content = hint.is_a?(String) ? hint : translate(:hints) - hint_content.html_safe if hint_content + + if hint.is_a?(String) + html_escape(hint) + else + content = translate(:hints) + content.html_safe if content + end end end diff --git a/lib/simple_form/components/labels.rb b/lib/simple_form/components/labels.rb index 2e78a3f4..ae33f5d2 100644 --- a/lib/simple_form/components/labels.rb +++ b/lib/simple_form/components/labels.rb @@ -30,7 +30,7 @@ module SimpleForm end def label_text - SimpleForm.label_text.call(raw_label_text, required_label_text).strip.html_safe + SimpleForm.label_text.call(html_escape(raw_label_text), required_label_text).strip.html_safe end def label_target diff --git a/lib/simple_form/form_builder.rb b/lib/simple_form/form_builder.rb index 44ae509e..94a0f5d5 100644 --- a/lib/simple_form/form_builder.rb +++ b/lib/simple_form/form_builder.rb @@ -108,16 +108,11 @@ module SimpleForm # def input(attribute_name, options={}, &block) options = @defaults.deep_dup.deep_merge(options) if @defaults - input = find_input(attribute_name, options, &block) - chosen = - if name = options[:wrapper] || find_wrapper_mapping(input.input_type) - name.respond_to?(:render) ? name : SimpleForm.wrapper(name) - else - wrapper - end + input = find_input(attribute_name, options, &block) + wrapper = find_wrapper(input.input_type, options) - chosen.render input + wrapper.render input end alias :attribute :input @@ -140,7 +135,11 @@ module SimpleForm options[:input_html] = options.except(:as, :collection, :label_method, :value_method, *ATTRIBUTE_COMPONENTS) options = @defaults.deep_dup.deep_merge(options) if @defaults - SimpleForm::Wrappers::Root.new(ATTRIBUTE_COMPONENTS + [:input], wrapper: false).render find_input(attribute_name, options) + input = find_input(attribute_name, options) + wrapper = find_wrapper(input.input_type, options) + components = (wrapper.components & ATTRIBUTE_COMPONENTS) + [:input] + + SimpleForm::Wrappers::Root.new(components, wrapper.options.merge(wrapper: false)).render input end # Helper for dealing with association selects/radios, generating the @@ -564,6 +563,14 @@ module SimpleForm end end + def find_wrapper(input_type, options) + if name = options[:wrapper] || find_wrapper_mapping(input_type) + name.respond_to?(:render) ? name : SimpleForm.wrapper(name) + else + wrapper + end + end + # If cache_discovery is enabled, use the class level cache that persists # between requests, otherwise use the instance one. def discovery_cache #:nodoc: diff --git a/lib/simple_form/inputs/base.rb b/lib/simple_form/inputs/base.rb index a98d4e6e..e9eb3def 100644 --- a/lib/simple_form/inputs/base.rb +++ b/lib/simple_form/inputs/base.rb @@ -1,8 +1,11 @@ require 'simple_form/i18n_cache' +require 'active_support/core_ext/string/output_safety' module SimpleForm module Inputs class Base + include ERB::Util + extend I18nCache include SimpleForm::Helpers::Autofocus diff --git a/lib/simple_form/inputs/collection_radio_buttons_input.rb b/lib/simple_form/inputs/collection_radio_buttons_input.rb index 0be885dc..52d47785 100644 --- a/lib/simple_form/inputs/collection_radio_buttons_input.rb +++ b/lib/simple_form/inputs/collection_radio_buttons_input.rb @@ -22,7 +22,7 @@ module SimpleForm options[:item_wrapper_tag] ||= options.fetch(:item_wrapper_tag, SimpleForm.item_wrapper_tag) options[:item_wrapper_class] = [ item_wrapper_class, options[:item_wrapper_class], SimpleForm.item_wrapper_class - ].compact.presence + ].compact.presence if SimpleForm.include_default_input_wrapper_class options[:collection_wrapper_tag] ||= options.fetch(:collection_wrapper_tag, SimpleForm.collection_wrapper_tag) options[:collection_wrapper_class] = [ diff --git a/lib/simple_form/inputs/date_time_input.rb b/lib/simple_form/inputs/date_time_input.rb index 64fd326f..0414ad2f 100644 --- a/lib/simple_form/inputs/date_time_input.rb +++ b/lib/simple_form/inputs/date_time_input.rb @@ -2,7 +2,11 @@ module SimpleForm module Inputs class DateTimeInput < Base def input - @builder.send(:"#{input_type}_select", attribute_name, input_options, input_html_options) + if use_html5_inputs? + @builder.send(:"#{input_type}_field", attribute_name, input_html_options) + else + @builder.send(:"#{input_type}_select", attribute_name, input_options, input_html_options) + end end private @@ -19,6 +23,10 @@ module SimpleForm position = ActionView::Helpers::DateTimeSelector::POSITION[position] "#{attribute_name}_#{position}i" end + + def use_html5_inputs? + input_options[:html5] + end end end end diff --git a/lib/simple_form/tags.rb b/lib/simple_form/tags.rb index 33a7db3b..aef3f995 100644 --- a/lib/simple_form/tags.rb +++ b/lib/simple_form/tags.rb @@ -17,7 +17,7 @@ module SimpleForm if @options.fetch(:boolean_style, SimpleForm.boolean_style) == :nested label_options = {} - add_default_name_and_id_for_value(text, label_options) + add_default_name_and_id_for_value(value, label_options) label_options['for'] = label_options['id'] rendered_item = content_tag(:label, rendered_item, label_options) end diff --git a/lib/simple_form/version.rb b/lib/simple_form/version.rb index 7b9bda35..d1115316 100644 --- a/lib/simple_form/version.rb +++ b/lib/simple_form/version.rb @@ -1,3 +1,3 @@ module SimpleForm - VERSION = "3.0.0".freeze + VERSION = "3.0.1".freeze end diff --git a/test/form_builder/error_test.rb b/test/form_builder/error_test.rb index ce42ccb4..06a024a7 100644 --- a/test/form_builder/error_test.rb +++ b/test/form_builder/error_test.rb @@ -80,8 +80,13 @@ class ErrorTest < ActionView::TestCase assert_no_select 'p.error[error_method]' end - test 'error should generate an error message with raw HTML tags' do + test 'error should escape error prefix text' do with_error_for @user, :name, error_prefix: 'Name' + assert_select 'span.error', "<b>Name</b> can't be blank" + end + + test 'error should generate an error message with raw HTML tags' do + with_error_for @user, :name, error_prefix: 'Name'.html_safe assert_select 'span.error', "Name can't be blank" assert_select 'span.error b', "Name" end diff --git a/test/form_builder/hint_test.rb b/test/form_builder/hint_test.rb index f8fd2cbe..38252720 100644 --- a/test/form_builder/hint_test.rb +++ b/test/form_builder/hint_test.rb @@ -43,8 +43,14 @@ class HintTest < ActionView::TestCase end test 'hint should be output as html_safe' do - with_hint_for @user, :name, hint: 'Bold and not...' + with_hint_for @user, :name, hint: 'Bold and not...'.html_safe assert_select 'span.hint', 'Bold and not...' + assert_select 'span.hint b', 'Bold' + end + + test 'builder should escape hint text' do + with_hint_for @user, :name, hint: '' + assert_select 'span.hint', "<script>alert(1337)</script>" end # Without attribute name @@ -132,7 +138,7 @@ class HintTest < ActionView::TestCase test 'hint with custom wrappers works' do swap_wrapper do with_hint_for @user, :name, hint: "can't be blank" - assert_select 'div.omg_hint', "can't be blank" + assert_select 'div.omg_hint', "can't be blank" end end end diff --git a/test/form_builder/input_field_test.rb b/test/form_builder/input_field_test.rb index 755ad85b..1f399c1e 100644 --- a/test/form_builder/input_field_test.rb +++ b/test/form_builder/input_field_test.rb @@ -88,14 +88,30 @@ class InputFieldTest < ActionView::TestCase assert_select 'input[min=18]' end - test 'builder input_field should use pattern component' do + test 'builder input_field should not use pattern component by default' do with_concat_form_for(@other_validating_user) do |f| f.input_field :country, as: :string end + assert_no_select 'input[pattern="\w+"]' + end + + test 'builder input_field should infer pattern from attributes' do + with_concat_form_for(@other_validating_user) do |f| + f.input_field :country, as: :string, pattern: true + end + assert_select 'input[pattern="\w+"]' end + test 'builder input_field should accept custom patter' do + with_concat_form_for(@other_validating_user) do |f| + f.input_field :country, as: :string, pattern: '\d+' + end + + assert_select 'input[pattern="\d+"]' + end + test 'builder input_field should use readonly component' do with_concat_form_for(@other_validating_user) do |f| f.input_field :age, as: :integer, readonly: true diff --git a/test/form_builder/label_test.rb b/test/form_builder/label_test.rb index 4d4155c6..4b7e0a78 100644 --- a/test/form_builder/label_test.rb +++ b/test/form_builder/label_test.rb @@ -29,6 +29,16 @@ class LabelTest < ActionView::TestCase assert_select 'label.string.required[for=validating_user_name]', /Name/ end + test 'builder should escape label text' do + with_label_for @user, :name, label: '', required: false + assert_select 'label.string', "<script>alert(1337)</script>" + end + + test 'builder should not escape label text if it is safe' do + with_label_for @user, :name, label: ''.html_safe, required: false + assert_select 'label.string script', "alert(1337)" + end + test 'builder should allow passing options to label tag' do with_label_for @user, :name, label: 'My label', id: 'name_label' assert_select 'label.string#name_label', /My label/ diff --git a/test/inputs/collection_check_boxes_input_test.rb b/test/inputs/collection_check_boxes_input_test.rb index 429ce680..5207b72f 100644 --- a/test/inputs/collection_check_boxes_input_test.rb +++ b/test/inputs/collection_check_boxes_input_test.rb @@ -230,4 +230,21 @@ class CollectionCheckBoxesInputTest < ActionView::TestCase assert_select 'span.checkbox.inline > label > input' end end + + test 'input check boxes wrapper class are not included when set to falsey' do + swap SimpleForm, include_default_input_wrapper_class: false, boolean_style: :nested do + with_input_for @user, :gender, :check_boxes, collection: [:male, :female] + + assert_no_select 'label.checkbox' + end + end + + test 'input check boxes custom wrapper class is included when include input wrapper class is falsey' do + swap SimpleForm, include_default_input_wrapper_class: false, boolean_style: :nested do + with_input_for @user, :gender, :check_boxes, collection: [:male, :female], item_wrapper_class: 'custom' + + assert_no_select 'label.checkbox' + assert_select 'label.custom' + end + end end diff --git a/test/inputs/collection_radio_buttons_input_test.rb b/test/inputs/collection_radio_buttons_input_test.rb index ff4b0e6e..3c78f56e 100644 --- a/test/inputs/collection_radio_buttons_input_test.rb +++ b/test/inputs/collection_radio_buttons_input_test.rb @@ -18,6 +18,14 @@ class CollectionRadioButtonsInputTest < ActionView::TestCase assert_select 'label[for=user_active_false]', 'No' end + test 'input as radio should generate internal labels with accurate `for` values with nested boolean style' do + swap SimpleForm, boolean_style: :nested do + with_input_for @user, :active, :radio_buttons + assert_select 'label[for=user_active_true]', 'Yes' + assert_select 'label[for=user_active_false]', 'No' + end + end + test 'input as radio should use i18n to translate internal labels' do store_translations(:en, simple_form: { yes: 'Sim', no: 'Não' }) do with_input_for @user, :active, :radio_buttons @@ -323,4 +331,21 @@ class CollectionRadioButtonsInputTest < ActionView::TestCase assert_select 'span.radio.inline > label > input' end end + + test 'input radio wrapper class are not included when set to falsey' do + swap SimpleForm, include_default_input_wrapper_class: false, boolean_style: :nested do + with_input_for @user, :gender, :radio_buttons, collection: [:male, :female] + + assert_no_select 'label.radio' + end + end + + test 'input check boxes custom wrapper class is included when include input wrapper class is falsey' do + swap SimpleForm, include_default_input_wrapper_class: false, boolean_style: :nested do + with_input_for @user, :gender, :radio_buttons, collection: [:male, :female], item_wrapper_class: 'custom' + + assert_no_select 'label.radio' + assert_select 'label.custom' + end + end end diff --git a/test/inputs/datetime_input_test.rb b/test/inputs/datetime_input_test.rb index b1a77354..d0528b1b 100644 --- a/test/inputs/datetime_input_test.rb +++ b/test/inputs/datetime_input_test.rb @@ -1,18 +1,69 @@ # encoding: UTF-8 require 'test_helper' -# Tests for all different kinds of inputs. -class DateTimeInputTest < ActionView::TestCase - # DateTime input - test 'input should generate a datetime select by default for datetime attributes' do +# Tests for datetime, date and time inputs when HTML5 compatibility is enabled in the wrapper. +class DateTimeInputWithHtml5Test < ActionView::TestCase + test 'input should generate a datetime input for datetime attributes if HTML5 compatibility is explicitly enbled' do + with_input_for @user, :created_at, :datetime, html5: true + + assert_select 'input[type="datetime"]' + end + + test 'input should generate a datetime select for datetime attributes' do with_input_for @user, :created_at, :datetime - 1.upto(5) do |i| - assert_select "form select.datetime#user_created_at_#{i}i" + + assert_select 'select.datetime' + end + + test 'input should generate a date input for date attributes if HTML5 compatibility is explicitly enbled' do + with_input_for @user, :born_at, :date, html5: true + + assert_select 'input[type="date"]' + end + + test 'input should generate a date select for date attributes' do + with_input_for @user, :born_at, :date + + assert_select 'select.date' + end + + test 'input should generate a time input for time attributes if HTML5 compatibility is explicitly enbled' do + with_input_for @user, :delivery_time, :time, html5: true + + assert_select 'input[type="time"]' + end + + test 'input should generate a time select for time attributes' do + with_input_for @user, :delivery_time, :time + + assert_select 'select.time' + end + + test 'input should generate required html attribute' do + with_input_for @user, :delivery_time, :time, required: true, html5: true + assert_select 'input.required' + assert_select 'input[required]' + end + + test 'input should have an aria-required html attribute' do + with_input_for @user, :delivery_time, :time, required: true, html5: true + assert_select 'input[aria-required=true]' + end +end + +# Tests for datetime, date and time inputs when HTML5 compatibility is enabled in the wrapper. +class DateTimeInputWithoutHtml5Test < ActionView::TestCase + test 'input should generate a datetime select by default for datetime attributes' do + swap_wrapper do + with_input_for @user, :created_at, :datetime + 1.upto(5) do |i| + assert_select "form select.datetime#user_created_at_#{i}i" + end end end test 'input should be able to pass options to datetime select' do - with_input_for @user, :created_at, :datetime, + with_input_for @user, :created_at, :datetime, html5: false, disabled: true, prompt: { year: 'ano', month: 'mês', day: 'dia' } assert_select 'select.datetime[disabled=disabled]' @@ -21,16 +72,26 @@ class DateTimeInputTest < ActionView::TestCase assert_select 'select.datetime option', 'dia' end + test 'input should generate a datetime input for datetime attributes if HTML5 compatibility is explicitly enabled' do + swap_wrapper do + with_input_for @user, :created_at, :datetime, html5: true + + assert_select 'input[type="datetime"]' + end + end + test 'input should generate a date select for date attributes' do - with_input_for @user, :born_at, :date - assert_select 'select.date#user_born_at_1i' - assert_select 'select.date#user_born_at_2i' - assert_select 'select.date#user_born_at_3i' - assert_no_select 'select.date#user_born_at_4i' + swap_wrapper do + with_input_for @user, :born_at, :date + assert_select 'select.date#user_born_at_1i' + assert_select 'select.date#user_born_at_2i' + assert_select 'select.date#user_born_at_3i' + assert_no_select 'select.date#user_born_at_4i' + end end test 'input should be able to pass options to date select' do - with_input_for @user, :born_at, :date, as: :date, + with_input_for @user, :born_at, :date, as: :date, html5: false, disabled: true, prompt: { year: 'ano', month: 'mês', day: 'dia' } assert_select 'select.date[disabled=disabled]' @@ -40,21 +101,31 @@ class DateTimeInputTest < ActionView::TestCase end test 'input should be able to pass :default to date select' do - with_input_for @user, :born_at, :date, default: Date.today + with_input_for @user, :born_at, :date, default: Date.today, html5: false assert_select "select.date option[value=#{Date.today.year}][selected=selected]" end + test 'input should generate a date input for date attributes if HTML5 compatibility is explicitly enabled' do + swap_wrapper do + with_input_for @user, :born_at, :date, html5: true + + assert_select 'input[type="date"]' + end + end + test 'input should generate a time select for time attributes' do - with_input_for @user, :delivery_time, :time - assert_select 'input[type=hidden]#user_delivery_time_1i' - assert_select 'input[type=hidden]#user_delivery_time_2i' - assert_select 'input[type=hidden]#user_delivery_time_3i' - assert_select 'select.time#user_delivery_time_4i' - assert_select 'select.time#user_delivery_time_5i' + swap_wrapper do + with_input_for @user, :delivery_time, :time + assert_select 'input[type=hidden]#user_delivery_time_1i' + assert_select 'input[type=hidden]#user_delivery_time_2i' + assert_select 'input[type=hidden]#user_delivery_time_3i' + assert_select 'select.time#user_delivery_time_4i' + assert_select 'select.time#user_delivery_time_5i' + end end test 'input should be able to pass options to time select' do - with_input_for @user, :delivery_time, :time, required: true, + with_input_for @user, :delivery_time, :time, required: true, html5: false, disabled: true, prompt: { hour: 'hora', minute: 'minuto' } assert_select 'select.time[disabled=disabled]' @@ -62,44 +133,40 @@ class DateTimeInputTest < ActionView::TestCase assert_select 'select.time option', 'minuto' end + test 'input should generate a time input for time attributes if HTML5 compatibility is explicitly enabled' do + swap_wrapper do + with_input_for @user, :delivery_time, :time, html5: true + + assert_select 'input[type="time"]' + end + end + test 'label should use i18n to get target for date input type' do store_translations(:en, date: { order: ['month', 'day', 'year'] }) do - with_input_for :project, :created_at, :date + with_input_for :project, :created_at, :date, html5: false assert_select 'label[for=project_created_at_2i]' end end test 'label should use i18n to get target for datetime input type' do store_translations(:en, date: { order: ['month', 'day', 'year'] }) do - with_input_for :project, :created_at, :datetime + with_input_for :project, :created_at, :datetime, html5: false assert_select 'label[for=project_created_at_2i]' end end test 'label should use order to get target when date input type' do - with_input_for :project, :created_at, :date, order: ['month', 'year', 'day'] + with_input_for :project, :created_at, :date, order: ['month', 'year', 'day'], html5: false assert_select 'label[for=project_created_at_2i]' end test 'label should use order to get target when datetime input type' do - with_input_for :project, :created_at, :datetime, order: ['month', 'year', 'day'] + with_input_for :project, :created_at, :datetime, order: ['month', 'year', 'day'], html5: false assert_select 'label[for=project_created_at_2i]' end test 'label should point to first option when time input type' do - with_input_for :project, :created_at, :time + with_input_for :project, :created_at, :time, html5: false assert_select 'label[for=project_created_at_4i]' end - - test 'date time input should generate required html attribute' do - with_input_for @user, :delivery_time, :time, required: true - assert_select 'select.required' - assert_select 'select[required]' - end - - test 'date time input has an aria-required html attribute' do - with_input_for @user, :delivery_time, :time, required: true - assert_select 'select.required' - assert_select 'select[aria-required=true]' - end end