├── .github
└── workflows
│ └── main.yml
├── .gitignore
├── CHANGELOG.md
├── Gemfile
├── MIT-LICENSE
├── README.md
├── Rakefile
├── html5_validators.gemspec
├── lib
├── html5_validators.rb
└── html5_validators
│ ├── action_view
│ ├── form_helpers.rb
│ └── form_helpers_rails3.rb
│ ├── active_model
│ ├── helper_methods.rb
│ └── validations.rb
│ ├── active_record
│ └── base.rb
│ └── version.rb
└── test
├── fake_app.rb
├── features
├── active_model_validation_test.rb
└── active_record_validation_test.rb
├── models
└── active_record
│ └── inherited_test.rb
└── test_helper.rb
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | name: build
2 |
3 | on: [push, pull_request]
4 |
5 | jobs:
6 | build:
7 | strategy:
8 | matrix:
9 | ruby_version: [ruby-head, '3.4', '3.3', '3.2', '3.1']
10 | rails_version: [edge, '8.0', '7.2', '7.1', '7.0', '6.1']
11 |
12 | include:
13 | - ruby_version: '3.1'
14 | activemodel_version: '7.0'
15 |
16 | - ruby_version: '3.0'
17 | rails_version: '7.1'
18 | - ruby_version: '3.0'
19 | activemodel_version: '7.1'
20 | - ruby_version: '3.0'
21 | rails_version: '7.0'
22 | - ruby_version: '3.0'
23 | activemodel_version: '7.0'
24 | - ruby_version: '3.0'
25 | rails_version: '6.1'
26 | - ruby_version: '3.0'
27 | activemodel_version: '6.1'
28 | - ruby_version: '3.0'
29 | rails_version: '6.0'
30 | - ruby_version: '3.0'
31 | activemodel_version: '6.0'
32 |
33 | - ruby_version: '2.7'
34 | rails_version: '7.1'
35 | - ruby_version: '2.7'
36 | activemodel_version: '7.1'
37 | - ruby_version: '2.7'
38 | rails_version: '7.0'
39 | - ruby_version: '2.7'
40 | activemodel_version: '7.0'
41 | - ruby_version: '2.7'
42 | rails_version: '6.1'
43 | - ruby_version: '2.7'
44 | activemodel_version: '6.1'
45 | - ruby_version: '2.7'
46 | rails_version: '6.0'
47 | - ruby_version: '2.7'
48 | activemodel_version: '6.0'
49 |
50 | - ruby_version: '2.6'
51 | rails_version: '5.2'
52 | - ruby_version: '2.6'
53 | activemodel_version: '5.2'
54 | - ruby_version: '2.6'
55 | rails_version: '5.1'
56 | - ruby_version: '2.6'
57 | rails_version: '5.0'
58 | - ruby_version: '2.6'
59 | rails_version: '4.2'
60 |
61 | - ruby_version: '2.5'
62 | rails_version: '5.2'
63 |
64 | - ruby_version: '2.4'
65 | rails_version: '4.2'
66 |
67 | - ruby_version: '2.3'
68 | rails_version: '4.2'
69 | - ruby_version: '2.3'
70 | rails_version: '4.1'
71 | - ruby_version: '2.3'
72 | rails_version: '4.0'
73 |
74 | - ruby_version: '2.2'
75 | rails_version: '4.2'
76 |
77 | - ruby_version: '2.1'
78 | rails_version: '3.2'
79 |
80 | exclude:
81 | - ruby_version: '3.1'
82 | rails_version: '8.0'
83 |
84 | runs-on: ubuntu-24.04
85 |
86 | env:
87 | BUNDLE_GEMFILE: ${{ matrix.gemfile }}
88 | RAILS_VERSION: ${{ matrix.rails_version }}
89 | ACTIVEMODEL_VERSION: ${{ matrix.activemodel_version }}
90 |
91 | steps:
92 | - uses: actions/checkout@v4
93 |
94 | - uses: ruby/setup-ruby@v1
95 | with:
96 | ruby-version: ${{ matrix.ruby_version }}
97 | rubygems: ${{ matrix.ruby_version < '2.6' && 'default' || 'latest' }}
98 | bundler: ${{ (contains(matrix.gemfile, 'rails_4') || contains(matrix.gemfile, 'rails_3')) && '1' || '2' }}
99 | bundler-cache: true
100 | continue-on-error: ${{ (matrix.ruby_version == 'ruby-head') || (matrix.rails_version == 'edge') || (matrix.allow_failures == 'true') }}
101 |
102 | - run: bundle exec rake
103 | continue-on-error: ${{ (matrix.ruby_version == 'ruby-head') || (matrix.rails_version == 'edge') || (matrix.allow_failures == 'true') }}
104 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.gem
2 | .bundle
3 | Gemfile.lock
4 | pkg/*
5 | tmp
6 | log
7 | gemfiles/*.lock
8 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## 1.9.0
2 |
3 | * Added support for `form_with` [@DialBird]
4 |
5 | * Enhanced support for non-AR applications to load all necessary monkey-patches on application initialization
6 |
7 |
8 | ## 1.8.0
9 |
10 | * Added support for AR `validation_context` such as `on: :create` or `on: :update`
11 |
12 | ## 1.7.0
13 |
14 | * Fixed a bug that `required: false` helper option does not override `required: true` value from PresenceValidator [@sandipransing]
15 |
16 |
17 | ## 1.5.0
18 |
19 | * Rails 5 support
20 |
21 | * Changed `auto_html5_validation` config behavior not to propagate to children classes
22 |
23 |
24 | ## 1.4.0
25 |
26 | * Dropped Ruby 1.8 support
27 |
28 | * Dropped Ruby 1.9 support
29 |
30 | * Dropped Rails 3.2 support
31 |
32 | * Fixed a bug that `maxlength` validation for inputs which can be blank was ignored [@kv109]
33 |
34 | * Fixed a bug that `auto_html5_validation` was not properly enabled for models that are loaded after loading this gem
35 |
36 |
37 | ## 1.3.0
38 |
39 | * Switched from `alias_method_chain` to `Module#prepend` on Ruby 2 and Rails 4
40 |
41 | * Added `minlength` support
42 |
43 |
44 | ## 1.2.2
45 |
46 | * Fixed undefined method `auto_html5_validation` error for non-AR Active Model models
47 |
48 |
49 | ## 1.2.1
50 |
51 | * Fixed a bug that `minlength / maxlength` values were assigned to `min / max`
52 |
53 |
54 | ## 1.2.0
55 |
56 | * Added global `Html5Validators.enabled` flag [@sinsoku]
57 |
58 | * Fixed a bug that some Symbol keyed helper options were ignored [@lucas-nelson]
59 |
60 |
61 | ## 1.1.3
62 |
63 | * Fixed a bug that AR::Base.inherited conflicts with other gems [@tricknotes]
64 |
65 |
66 | ## 1.1.2
67 |
68 | * Fixed a bug that `cattr_accessor` + `:instance_accessor => false` still creates an `instance_accessor` on AR 3.0 and 3.1
69 |
70 |
71 | ## 1.1.1
72 |
73 | * Rails 4 support
74 |
75 |
76 | ## 1.1.0
77 |
78 | * Added `maxlength` validator support for `text_area` [@ursm]
79 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | source 'https://rubygems.org'
3 |
4 | # Specify your gem's dependencies in html5_validators.gemspec
5 | gemspec
6 |
7 | if ENV['RAILS_VERSION'] == 'edge'
8 | gem 'railties', git: 'https://github.com/rails/rails.git'
9 | gem 'activerecord', git: 'https://github.com/rails/rails.git'
10 | gem 'sqlite3'
11 | elsif ENV['RAILS_VERSION'] && !ENV['RAILS_VERSION'].empty?
12 | gem 'railties', "~> #{ENV['RAILS_VERSION']}.0"
13 | gem 'activerecord', "~> #{ENV['RAILS_VERSION']}.0"
14 | if ENV['RAILS_VERSION'] <= '5.0'
15 | gem 'sqlite3', '< 1.4'
16 | elsif (ENV['RAILS_VERSION'] <= '8') || (RUBY_VERSION < '3')
17 | gem 'sqlite3', '< 2'
18 | end
19 |
20 | if ENV['RAILS_VERSION'] < '4'
21 | gem 'capybara', '~> 2.0.0'
22 | gem 'test-unit-rails', '1.0.2'
23 | end
24 |
25 | gem 'webdrivers' if (ENV['RAILS_VERSION'] && ENV['RAILS_VERSION'] >= '6') && (RUBY_VERSION < '3')
26 |
27 | elsif ENV['ACTIVEMODEL_VERSION']
28 | gem 'railties', "~> #{ENV['ACTIVEMODEL_VERSION']}.0"
29 | gem 'activemodel', "~> #{ENV['ACTIVEMODEL_VERSION']}.0"
30 | else
31 | gem 'railties'
32 | gem 'activerecord'
33 | gem 'sqlite3'
34 | end
35 |
36 | if RUBY_VERSION < '2.7'
37 | gem 'puma', '< 6'
38 | else
39 | gem 'puma'
40 | end
41 |
42 | gem 'nokogiri', RUBY_VERSION < '2.1' ? '~> 1.6.0' : '>= 1.7'
43 | gem 'loofah', RUBY_VERSION < '2.5' ? '< 2.21.0' : '>= 0'
44 | gem 'selenium-webdriver'
45 | gem 'net-smtp' if RUBY_VERSION >= '3.1'
46 | gem 'bigdecimal' if RUBY_VERSION >= '3.4'
47 | gem 'mutex_m' if RUBY_VERSION >= '3.4'
48 | gem 'drb' if RUBY_VERSION >= '3.4'
49 | gem 'benchmark' if RUBY_VERSION >= '3.5'
50 |
--------------------------------------------------------------------------------
/MIT-LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2011 Akira Matsuda
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining
4 | a copy of this software and associated documentation files (the
5 | "Software"), to deal in the Software without restriction, including
6 | without limitation the rights to use, copy, modify, merge, publish,
7 | distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to
9 | the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be
12 | included in all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # HTML5Validators
2 |
3 | Automatic client-side validation using HTML5 Form Validation
4 |
5 | ## What is this?
6 |
7 | html5_validators is a gem/plugin for Rails 3+ that enables automatic client-side
8 | validation using ActiveModel + HTML5. Once you bundle this gem on your app,
9 | the gem will automatically translate your model validation code into HTML5
10 | validation attributes on every `form_for` invocation unless you explicitly
11 | cancel it.
12 |
13 | ## Features
14 |
15 | ### PresenceValidator => required
16 |
17 | * Model
18 | ```ruby
19 | class User
20 | include ActiveModel::Validations
21 | validates_presence_of :name
22 | end
23 | ```
24 |
25 | * View
26 | ```erb
27 | <%= f.text_field :name %>
28 | ```
29 | other `text_field`ish helpers, `text_area`, `radio_button`, and `check_box` are also available
30 |
31 | * HTML
32 | ```html
33 |
34 | ```
35 |
36 | * SPEC
37 |
38 | http://dev.w3.org/html5/spec/Overview.html#attr-input-required
39 |
40 | 
41 |
42 | ### LengthValidator => maxlength
43 |
44 | * Model
45 | ```ruby
46 | class User
47 | include ActiveModel::Validations
48 | validates_length_of :name, maximum: 10
49 | end
50 | ```
51 |
52 | * View
53 | ```erb
54 | <%= f.text_field :name %>
55 | ```
56 | `text_area` is also available
57 |
58 | * HTML
59 | ```html
60 |
61 | ```
62 |
63 | * SPEC
64 |
65 | http://dev.w3.org/html5/spec/Overview.html#attr-input-maxlength
66 |
67 | ### NumericalityValidator => max, min
68 |
69 | * Model
70 | ```ruby
71 | class User
72 | include ActiveModel::Validations
73 | validates_numericality_of :age, greater_than_or_equal_to: 20
74 | end
75 | ```
76 |
77 | * View (be sure to use number_field)
78 | ```erb
79 | <%= f.number_field :age %>
80 | ```
81 |
82 | * HTML
83 | ```html
84 |
85 | ```
86 |
87 | * SPEC
88 |
89 | http://dev.w3.org/html5/spec/Overview.html#attr-input-max
90 | http://dev.w3.org/html5/spec/Overview.html#attr-input-min
91 |
92 | 
93 |
94 | ### And more (coming soon...?)
95 | :construction:
96 |
97 | ## Disabling automatic client-side validation
98 |
99 | There are four ways to cancel the automatic HTML5 validation.
100 |
101 | ### 1. Per form (via form_for option)
102 |
103 | Set `auto_html5_validation: false` to `form_for` parameter.
104 |
105 | * View
106 | ```erb
107 | <%= form_for @user, auto_html5_validation: false do |f| %>
108 | ...
109 | <% end %>
110 | ```
111 |
112 | ### 2. Per model instance (via model attribute)
113 |
114 | Set `auto_html5_validation = false` attribute to ActiveModelish object.
115 |
116 | * Controller
117 | ```ruby
118 | @user = User.new auto_html5_validation: false
119 | ```
120 |
121 | * View
122 | ```erb
123 | <%= form_for @user do |f| %>
124 | ...
125 | <% end %>
126 | ```
127 |
128 | ### 3. Per model class (via model class attribute)
129 |
130 | Set `auto_html5_validation = false` to ActiveModelish class' class variable.
131 | This configuration will never be propagated to inherited children classes.
132 |
133 | * Model
134 | ```ruby
135 | class User < ActiveRecord::Base
136 | self.auto_html5_validation = false
137 | end
138 | ```
139 |
140 | * Controller
141 | ```ruby
142 | @user = User.new
143 | ```
144 |
145 | * View
146 | ```erb
147 | <%= form_for @user do |f| %>
148 | ...
149 | <% end %>
150 | ```
151 |
152 | ### 4. Globally (via HTML5Validators module configuration)
153 |
154 | Set `config.enabled = false` to Html5Validators module.
155 | Maybe you want to put this in your test_helper, or add a controller filter as
156 | follows for development mode.
157 |
158 | * Controller
159 | ```ruby
160 | # an example filter that disables the validator if the request has {h5v: 'disable'} params
161 | around_action do |controller, block|
162 | h5v_enabled_was = Html5Validators.enabled
163 | Html5Validators.enabled = false if params[:h5v] == 'disable'
164 | block.call
165 | Html5Validators.enabled = h5v_enabled_was
166 | end
167 | ```
168 |
169 | ## Supported versions
170 |
171 | * Ruby 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, 3.0, 3.1, 3.2, 3.3 (trunk)
172 |
173 | * Rails 3.2.x, 4.0.x, 4.1, 4.2, 5.0, 5.1, 5.2, 6.0, 6.1, 7.0, 7.1 (edge)
174 |
175 | * HTML5 compatible browsers
176 |
177 |
178 | ## Installation
179 |
180 | Put this line into your Gemfile:
181 | ```ruby
182 | gem 'html5_validators'
183 | ```
184 |
185 | Then bundle:
186 | ```
187 | % bundle
188 | ```
189 |
190 | ## Notes
191 |
192 | When accessed by an HTML5 incompatible legacy browser, these extra attributes
193 | will just be ignored.
194 |
195 | ## Todo
196 |
197 | * more validations
198 |
199 |
200 | ## Copyright
201 |
202 | Copyright (c) 2011 Akira Matsuda. See MIT-LICENSE for further details.
203 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | require 'bundler/gem_tasks'
3 | require 'rake/testtask'
4 |
5 | Rake::TestTask.new do |t|
6 | t.libs << 'test'
7 | if Gem.loaded_specs['activerecord']
8 | t.test_files = Dir['test/**/*_test.rb']
9 | else
10 | t.test_files = Dir['test/**/*_test.rb'] - Dir['test/**/*active_record*'] - Dir['test/**/*active_record*/*.rb']
11 | end
12 | t.warning = true
13 | t.verbose = true
14 | end
15 |
16 | task default: :test
17 |
--------------------------------------------------------------------------------
/html5_validators.gemspec:
--------------------------------------------------------------------------------
1 | # -*- encoding: utf-8 -*-
2 | # frozen_string_literal: true
3 | $:.push File.expand_path('../lib', __FILE__)
4 | require 'html5_validators/version'
5 |
6 | Gem::Specification.new do |s|
7 | s.name = 'html5_validators'
8 | s.version = Html5Validators::VERSION
9 | s.platform = Gem::Platform::RUBY
10 | s.authors = ['Akira Matsuda']
11 | s.email = ['ronnie@dio.jp']
12 | s.homepage = 'https://github.com/amatsuda/html5_validators'
13 | s.summary = 'Automatic client side validation using HTML5 Form Validation'
14 | s.description = 'A gem/plugin for Rails 3+ that enables client-side validation using ActiveModel + HTML5'
15 | s.license = 'MIT'
16 |
17 | s.files = `git ls-files`.split("\n")
18 | s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19 | s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20 | s.require_paths = ['lib']
21 | s.add_development_dependency 'test-unit-activesupport'
22 | s.add_development_dependency 'test-unit-capybara'
23 | s.add_development_dependency 'selenium-webdriver'
24 | s.add_development_dependency 'puma'
25 | s.add_development_dependency 'capybara', '>= 2'
26 | s.add_development_dependency 'sqlite3'
27 | s.add_development_dependency 'rake'
28 | s.add_dependency 'activemodel'
29 | end
30 |
--------------------------------------------------------------------------------
/lib/html5_validators.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'rails'
4 |
5 | module Html5Validators
6 | @enabled = true
7 |
8 | class << self
9 | attr_accessor :enabled
10 | end
11 |
12 | class Railtie < ::Rails::Railtie #:nodoc:
13 | initializer 'html5_validators' do
14 | config.after_initialize do
15 | require 'html5_validators/active_model/helper_methods'
16 | require 'html5_validators/active_model/validations'
17 | end
18 |
19 | ActiveSupport.on_load(:active_record) do
20 | require 'html5_validators/active_record/base'
21 | end
22 |
23 | ActiveSupport.on_load(:action_view) do
24 | if ActionPack::VERSION::STRING >= '4'
25 | require 'html5_validators/action_view/form_helpers'
26 | else
27 | require 'html5_validators/action_view/form_helpers_rails3'
28 | end
29 | end
30 | end
31 | end
32 | end
33 |
--------------------------------------------------------------------------------
/lib/html5_validators/action_view/form_helpers.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Html5Validators
4 | module ActionViewExtension
5 | module FormHelper
6 | def form_for(record, options = {}, &block)
7 | if record.respond_to?(:auto_html5_validation=)
8 | if !Html5Validators.enabled || (options[:auto_html5_validation] == false)
9 | record.auto_html5_validation = false
10 | end
11 | end
12 | super
13 | end
14 |
15 | if Rails::VERSION::STRING >= '5.1'
16 | def form_with(model: nil, scope: nil, url: nil, format: nil, **options)
17 | if model && model.respond_to?(:auto_html5_validation=)
18 | if !Html5Validators.enabled || (options[:auto_html5_validation] == false)
19 | model.auto_html5_validation = false
20 | end
21 | end
22 | super
23 | end
24 | end
25 | end
26 |
27 | module PresenceValidator
28 | def render
29 | if object.is_a?(ActiveModel::Validations) && (object.auto_html5_validation != false) && (object.class.auto_html5_validation != false)
30 | if defined? @html_options
31 | @html_options["required"] ||= @html_options.fetch(:required) { object.attribute_required?(@method_name) }
32 | else
33 | @options["required"] ||= @options.fetch(:required) { object.attribute_required?(@method_name) }
34 | end
35 | end
36 | super
37 | end
38 | end
39 |
40 | module LengthValidator
41 | def render
42 | if object.is_a?(ActiveModel::Validations) && (object.auto_html5_validation != false) && (object.class.auto_html5_validation != false)
43 | @options["maxlength"] ||= @options[:maxlength] || object.attribute_maxlength(@method_name)
44 | @options["minlength"] ||= @options[:minlength] || object.attribute_minlength(@method_name)
45 | end
46 | super
47 | end
48 | end
49 |
50 | module NumericalityValidator
51 | def render
52 | if object.is_a?(ActiveModel::Validations) && (object.auto_html5_validation != false) && (object.class.auto_html5_validation != false)
53 | @options["max"] ||= @options["max"] || @options[:max] || object.attribute_max(@method_name)
54 | @options["min"] ||= @options["min"] || @options[:min] || object.attribute_min(@method_name)
55 | end
56 | super
57 | end
58 | end
59 | end
60 | end
61 |
62 |
63 | module ActionView
64 | Base.send :prepend, Html5Validators::ActionViewExtension::FormHelper
65 |
66 | module Helpers
67 | module Tags
68 | class TextField
69 | prepend Html5Validators::ActionViewExtension::NumericalityValidator
70 | prepend Html5Validators::ActionViewExtension::LengthValidator
71 | prepend Html5Validators::ActionViewExtension::PresenceValidator
72 | end
73 |
74 | class TextArea
75 | prepend Html5Validators::ActionViewExtension::LengthValidator
76 | prepend Html5Validators::ActionViewExtension::PresenceValidator
77 | end
78 |
79 | #TODO probably I have to add some more classes here
80 | [RadioButton, CheckBox, Select, DateSelect, TimeZoneSelect].each do |kls|
81 | kls.send :prepend, Html5Validators::ActionViewExtension::PresenceValidator
82 | end
83 | end
84 | end
85 | end
86 |
--------------------------------------------------------------------------------
/lib/html5_validators/action_view/form_helpers_rails3.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # Legacy Rails 3.x support
4 | module ActionView
5 | module Helpers
6 | module FormHelper
7 | def form_for_with_auto_html5_validation_option(record, options = {}, &proc)
8 | if record.respond_to?(:auto_html5_validation=)
9 | if !Html5Validators.enabled || (options[:auto_html5_validation] == false)
10 | record.auto_html5_validation = false
11 | end
12 | end
13 | form_for_without_auto_html5_validation_option record, options, &proc
14 | end
15 | alias_method_chain :form_for, :auto_html5_validation_option
16 | end
17 |
18 | class InstanceTag
19 | def to_input_field_tag_with_html5_attributes(field_type, options = {})
20 | if object.is_a?(ActiveModel::Validations) && (object.auto_html5_validation != false) && (object.class.auto_html5_validation != false)
21 | options["required"] ||= object.attribute_required?(method_name)
22 | options["maxlength"] ||= object.attribute_maxlength(method_name)
23 | options["minlength"] ||= object.attribute_minlength(method_name)
24 | options["max"] ||= object.attribute_max(method_name)
25 | options["min"] ||= object.attribute_min(method_name)
26 | end
27 | to_input_field_tag_without_html5_attributes field_type, options
28 | end
29 | alias_method_chain :to_input_field_tag, :html5_attributes
30 |
31 | def to_text_area_tag_with_html5_attributes(options = {})
32 | if object.is_a?(ActiveModel::Validations) && (object.auto_html5_validation != false) && (object.class.auto_html5_validation != false)
33 | options["required"] ||= object.attribute_required?(method_name)
34 | options["maxlength"] ||= object.attribute_maxlength(method_name)
35 | options["minlength"] ||= object.attribute_minlength(method_name)
36 | end
37 | to_text_area_tag_without_html5_attributes options
38 | end
39 | alias_method_chain :to_text_area_tag, :html5_attributes
40 |
41 | def to_radio_button_tag_with_html5_attributes(tag_value, options = {})
42 | if object.is_a?(ActiveModel::Validations) && (object.auto_html5_validation != false) && (object.class.auto_html5_validation != false)
43 | options["required"] ||= object.attribute_required?(method_name)
44 | end
45 | to_radio_button_tag_without_html5_attributes tag_value, options
46 | end
47 | alias_method_chain :to_radio_button_tag, :html5_attributes
48 |
49 | def to_check_box_tag_with_html5_attributes(options = {}, checked_value = "1", unchecked_value = "0")
50 | if object.is_a?(ActiveModel::Validations) && (object.auto_html5_validation != false) && (object.class.auto_html5_validation != false)
51 | options["required"] ||= object.attribute_required?(method_name)
52 | end
53 | to_check_box_tag_without_html5_attributes options, checked_value, unchecked_value
54 | end
55 | alias_method_chain :to_check_box_tag, :html5_attributes
56 |
57 | private
58 |
59 | def select_content_tag_with_html5_attributes(option_tags, options, html_options)
60 | if object.is_a?(ActiveModel::Validations) && (object.auto_html5_validation != false) && (object.class.auto_html5_validation != false)
61 | html_options["required"] ||= object.attribute_required?(method_name)
62 | end
63 | select_content_tag_without_html5_attributes option_tags, options, html_options
64 | end
65 | alias_method_chain :select_content_tag, :html5_attributes
66 | end
67 | end
68 | end
69 |
--------------------------------------------------------------------------------
/lib/html5_validators/active_model/helper_methods.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Html5Validators
4 | module DafaultValidationContext
5 | if defined?(::ActiveRecord::Base) && (::ActiveRecord::VERSION::MAJOR < 5)
6 | refine ::ActiveRecord::Base do
7 | def default_validation_context
8 | new_record? ? :create : :update
9 | end
10 | end
11 | end
12 | refine Object do
13 | def default_validation_context
14 | nil
15 | end
16 | end
17 | end
18 | end
19 |
20 | using Html5Validators::DafaultValidationContext
21 |
22 | module ActiveModel
23 | module Validations
24 | module HelperMethods
25 | def attribute_required?(attribute)
26 | self.class.validators.grep(PresenceValidator).any? do |v|
27 | if v.attributes.include?(attribute.to_sym) && (v.options.keys & [:if, :unless]).empty?
28 | !(on = v.options[:on]) || Array(on).include?(default_validation_context)
29 | end
30 | end
31 | end
32 |
33 | def attribute_maxlength(attribute)
34 | self.class.validators.grep(LengthValidator).select {|v|
35 | if v.attributes.include?(attribute.to_sym) && (v.options.keys & [:maximum, :is]).any? && (v.options.keys & [:if, :unless, :tokenizer]).empty?
36 | !(on = v.options[:on]) || Array(on).include?(default_validation_context)
37 | end
38 | }.map {|v| v.options.slice(:maximum, :is)}.map(&:values).flatten.max
39 | end
40 |
41 | def attribute_minlength(attribute)
42 | self.class.validators.grep(LengthValidator).select {|v|
43 | if v.attributes.include?(attribute.to_sym) && (v.options.keys & [:minimum, :is]).any? && (v.options.keys & [:if, :unless, :allow_nil, :allow_blank, :tokenizer]).empty?
44 | !(on = v.options[:on]) || Array(on).include?(default_validation_context)
45 | end
46 | }.map {|v| v.options.slice(:minimum, :is)}.map(&:values).flatten.min
47 | end
48 |
49 | def attribute_max(attribute)
50 | self.class.validators.grep(NumericalityValidator).select {|v|
51 | if v.attributes.include?(attribute.to_sym) && (v.options.keys & [:less_than, :less_than_or_equal_to]).any? && (v.options.keys & [:if, :unless, :allow_nil, :allow_blank]).empty?
52 | !(on = v.options[:on]) || Array(on).include?(default_validation_context)
53 | end
54 | }.map {|v| v.options.slice(:less_than, :less_than_or_equal_to)}.map(&:values).flatten.max
55 | end
56 |
57 | def attribute_min(attribute)
58 | self.class.validators.grep(NumericalityValidator).select {|v|
59 | if v.attributes.include?(attribute.to_sym) && (v.options.keys & [:greater_than, :greater_than_or_equal_to]).any? && (v.options.keys & [:if, :unless, :allow_nil, :allow_blank]).empty?
60 | !(on = v.options[:on]) || Array(on).include?(default_validation_context)
61 | end
62 | }.map {|v| v.options.slice(:greater_than, :greater_than_or_equal_to)}.map(&:values).flatten.min
63 | end
64 | end
65 | end
66 | end
67 |
--------------------------------------------------------------------------------
/lib/html5_validators/active_model/validations.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Html5Validators
4 | module ActiveModelExtension
5 | extend ActiveSupport::Concern
6 |
7 | included do
8 | cattr_accessor :auto_html5_validation, instance_accessor: false, instance_reader: false, instance_writer: false
9 | end
10 | end
11 | end
12 |
13 | module ActiveModel
14 | module Validations
15 | attr_accessor :auto_html5_validation
16 | end
17 | end
18 |
19 | ActiveModel::Validations.send(:include, Html5Validators::ActiveModelExtension)
20 |
--------------------------------------------------------------------------------
/lib/html5_validators/active_record/base.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Html5Validators
4 | module ActiveRecordExtension
5 | extend ActiveSupport::Concern
6 |
7 | # Future subclasses will pick up the model extension
8 | module ClassMethods
9 | def inherited(kls)
10 | super
11 | class << kls
12 | attr_accessor :auto_html5_validation
13 | end
14 | kls.auto_html5_validation = true
15 | end
16 | end
17 |
18 | included do
19 | # Existing subclasses pick up the model extension as well
20 | descendants.each do |kls|
21 | class << kls
22 | attr_accessor :auto_html5_validation
23 | end
24 | kls.auto_html5_validation = true
25 | end
26 | end
27 | end
28 | end
29 |
30 | ActiveRecord::Base.send(:include, Html5Validators::ActiveRecordExtension)
31 |
--------------------------------------------------------------------------------
/lib/html5_validators/version.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Html5Validators
4 | VERSION = '1.9.0'
5 | end
6 |
--------------------------------------------------------------------------------
/test/fake_app.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'action_controller/railtie'
4 | require 'active_model'
5 | begin
6 | require 'active_record'
7 | rescue LoadError
8 | end
9 |
10 | # config
11 | class Html5ValidatorsTestApp < Rails::Application
12 | config.secret_token = "You know I'm born to lose, and gambling's for fools, But that's the way I like it baby, I don't wanna live for ever, And don't forget the joker!"
13 | config.session_store :cookie_store, key: '_myapp_session'
14 | config.active_support.deprecation = :log
15 | config.eager_load = false
16 | end
17 | Rails.application.initialize!
18 |
19 | # routes
20 | Rails.application.routes.draw do
21 | resources :people, only: [:new, :create] do
22 | collection do
23 | get :new_without_html5_validation
24 | get :new_with_required_true
25 | get :new_with_required_false
26 | end
27 | end
28 | resources :items, only: [:new, :create] do
29 | collection do
30 | get :new_without_html5_validation
31 | get :new_with_required_true
32 | get :new_with_required_false
33 | end
34 | end
35 | end
36 |
37 | # models
38 | if defined? ActiveRecord
39 | ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')
40 |
41 | # migrations
42 | ActiveRecord::Base.connection.instance_eval do
43 | create_table :people do |t|
44 | t.string :name
45 | t.string :password
46 | t.string :email
47 | t.integer :age
48 | t.text :bio
49 | t.string :blood_type
50 | t.boolean :terms_of_service
51 | t.string :user_type
52 | end
53 | end
54 |
55 | class ApplicationRecord < ActiveRecord::Base
56 | self.abstract_class = true
57 | end
58 | class Person < ApplicationRecord
59 | end
60 | end
61 |
62 | class Item
63 | if ActiveModel::VERSION::STRING >= '4'
64 | include ActiveModel::Model
65 | else
66 | include ActiveModel::Validations
67 | include ActiveModel::Conversion
68 | def persisted?; false; end
69 | end
70 |
71 | attr_accessor :name, :description
72 | end
73 |
74 | # controllers
75 | class ApplicationController < ActionController::Base; end
76 | class PeopleController < ApplicationController
77 | def new
78 | @person = Person.new
79 | render inline: <<-ERB
80 | <%= form_for @person, html: { id: 'form_for' } do |f| %>
81 | <%= f.text_field :name %>
82 | <%= f.password_field :password %>
83 | <%= f.text_area :bio %>
84 | <%= f.select :blood_type, %w(A B O AB), include_blank: true %>
85 | <%= f.check_box :terms_of_service %>
86 | <%= f.radio_button :user_type, 'normal' %>
87 | <%= f.radio_button :user_type, 'admin' %>
88 | <% end %>
89 |
90 | <% if Rails::VERSION::STRING >= '5.1' %>
91 | <%= form_with model: @person, id: 'form_with' do |f| %>
92 | <%= f.text_field :name, id: 'person_name' %>
93 | <%= f.password_field :password, id: 'person_password' %>
94 | <%= f.text_area :bio, id: 'person_bio' %>
95 | <%= f.select :blood_type, %w(A B O AB), include_blank: true %>
96 | <%= f.check_box :terms_of_service %>
97 | <%= f.radio_button :user_type, 'normal' %>
98 | <%= f.radio_button :user_type, 'admin' %>
99 | <% end %>
100 | <% end %>
101 | ERB
102 | end
103 |
104 | def new_without_html5_validation
105 | @person = Person.new
106 | render inline: <<-ERB
107 | <%= form_for @person, html: { id: 'form_for' }, auto_html5_validation: false do |f| %>
108 | <%= f.text_field :name %>
109 | <%= f.text_field :email %>
110 | <% end %>
111 |
112 | <% if Rails::VERSION::STRING >= '5.1' %>
113 | <%= form_with model: @person, id: 'form_with', auto_html5_validation: false do |f| %>
114 | <%= f.text_field :name, id: 'person_name' %>
115 | <%= f.text_field :email, id: 'person_email' %>
116 | <% end %>
117 | <% end %>
118 | ERB
119 | end
120 |
121 | def new_with_required_true
122 | @person = Person.new
123 | render inline: <<-ERB
124 | <%= form_for @person, html: { id: 'form_for' } do |f| %>
125 | <%= f.text_field :email, required: true %>
126 | <% end %>
127 |
128 | <% if Rails::VERSION::STRING >= '5.1' %>
129 | <%= form_with model: @person, id: 'form_with' do |f| %>
130 | <%= f.text_field :email, required: true, id: 'person_email' %>
131 | <% end %>
132 | <% end %>
133 | ERB
134 | end
135 |
136 | def new_with_required_false
137 | @person = Person.new
138 | render inline: <<-ERB
139 | <%= form_for @person, html: { id: 'form_for' } do |f| %>
140 | <%= f.text_field :email, required: false %>
141 | <% end %>
142 |
143 | <% if Rails::VERSION::STRING >= '5.1' %>
144 | <%= form_with model: @person, id: 'form_with' do |f| %>
145 | <%= f.text_field :email, required: false, id: 'person_email' %>
146 | <% end %>
147 | <% end %>
148 | ERB
149 | end
150 | end
151 | class ItemsController < ApplicationController
152 | def new
153 | @item = Item.new
154 | render inline: <<-ERB
155 | <%= form_for @item, html: { id: 'form_for' } do |f| %>
156 | <%= f.text_field :name %>
157 | <%= f.text_area :description %>
158 | <% end %>
159 |
160 | <% if Rails::VERSION::STRING >= '5.1' %>
161 | <%= form_with model: @item, id: 'form_with' do |f| %>
162 | <%= f.text_field :name, id: 'item_name' %>
163 | <%= f.text_area :description, id: 'item_description' %>
164 | <% end %>
165 | <% end %>
166 | ERB
167 | end
168 |
169 | def new_without_html5_validation
170 | @item = Item.new
171 | render inline: <<-ERB
172 | <%= form_for @item, html: { id: 'form_for' }, auto_html5_validation: false do |f| %>
173 | <%= f.text_field :name %>
174 | <%= f.text_area :description %>
175 | <% end %>
176 |
177 | <% if Rails::VERSION::STRING >= '5.1' %>
178 | <%= form_with model: @item, id: 'form_with', auto_html5_validation: false do |f| %>
179 | <%= f.text_field :name, id: 'item_name' %>
180 | <%= f.text_area :description, id: 'item_description' %>
181 | <% end %>
182 | <% end %>
183 | ERB
184 | end
185 |
186 | def new_with_required_true
187 | @item = Item.new
188 | render inline: <<-ERB
189 | <%= form_for @item, html: { id: 'form_for' } do |f| %>
190 | <%= f.text_field :name, required: true %>
191 | <% end %>
192 |
193 | <% if Rails::VERSION::STRING >= '5.1' %>
194 | <%= form_with model: @item, id: 'form_with' do |f| %>
195 | <%= f.text_field :name, required: true, id: 'item_name' %>
196 | <% end %>
197 | <% end %>
198 | ERB
199 | end
200 |
201 | def new_with_required_false
202 | @item = Item.new
203 | render inline: <<-ERB
204 | <%= form_for @item, html: { id: 'form_for' } do |f| %>
205 | <%= f.text_field :name, required: false %>
206 | <% end %>
207 |
208 | <% if Rails::VERSION::STRING >= '5.1' %>
209 | <%= form_with model: @item, id: 'form_with' do |f| %>
210 | <%= f.text_field :name, required: false, id: 'item_name' %>
211 | <% end %>
212 | <% end %>
213 | ERB
214 | end
215 | end
216 |
217 | # helpers
218 | module ApplicationHelper; end
219 |
--------------------------------------------------------------------------------
/test/features/active_model_validation_test.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'test_helper'
4 |
5 | class ActiveModelValidationTest < ActionDispatch::IntegrationTest
6 | FORM_ID_LIST = if Rails::VERSION::STRING >= '5.1'
7 | %w[#form_for #form_with].freeze
8 | else
9 | %w[#form_for].freeze
10 | end
11 |
12 | teardown do
13 | Item._validators.clear
14 | end
15 |
16 | sub_test_case 'without validation' do
17 | test 'new form' do
18 | visit '/items/new'
19 | FORM_ID_LIST.each do |form|
20 | assert page.has_css? "#{form} input#item_name"
21 | assert page.has_no_css? "#{form} input#item_name[required=required]"
22 | end
23 | end
24 |
25 | test 'new_without_html5_validation form' do
26 | visit '/items/new_without_html5_validation'
27 | FORM_ID_LIST.each do |form|
28 | assert page.has_css? "#{form} textarea#item_description"
29 | assert page.has_no_css? "#{form} textarea#item_description[required=required]"
30 | end
31 | end
32 | end
33 |
34 | sub_test_case 'with required validation' do
35 | setup do
36 | Item.validates_presence_of :name, :description
37 | end
38 | test 'new form' do
39 | visit '/items/new'
40 | FORM_ID_LIST.each do |form|
41 | assert_equal 'required', find("#{form} input#item_name")[:required]
42 | assert_equal 'required', find("#{form} textarea#item_description")[:required]
43 | end
44 | end
45 | test 'new_without_html5_validation form' do
46 | visit '/items/new_without_html5_validation'
47 | FORM_ID_LIST.each do |form|
48 | assert_nil find("#{form} input#item_name")[:required]
49 | end
50 | end
51 | test 'new_with_required_true form' do
52 | visit '/items/new_with_required_true'
53 | FORM_ID_LIST.each do |form|
54 | assert_equal 'required', find("#{form} input#item_name")[:required]
55 | end
56 | end
57 | test 'new_with_required_false form' do
58 | visit '/items/new_with_required_false'
59 | FORM_ID_LIST.each do |form|
60 | assert_nil find("#{form} input#item_name")[:required]
61 | end
62 | end
63 | sub_test_case 'disabling html5_validation in class level' do
64 | setup do
65 | Item.class_eval do |kls|
66 | kls.auto_html5_validation = false
67 | end
68 | end
69 | teardown do
70 | Item.class_eval do |kls|
71 | kls.auto_html5_validation = nil
72 | end
73 | end
74 | test 'new form' do
75 | visit '/items/new'
76 | FORM_ID_LIST.each do |form|
77 | assert_nil find("#{form} input#item_name")[:required]
78 | end
79 | end
80 | end
81 |
82 | sub_test_case 'disabling html5_validations in gem' do
83 | setup do
84 | Html5Validators.enabled = false
85 | end
86 | teardown do
87 | Html5Validators.enabled = true
88 | end
89 | test 'new form' do
90 | visit '/items/new'
91 | FORM_ID_LIST.each do |form|
92 | assert_nil find("#{form} input#item_name")[:required]
93 | assert_nil find("#{form} textarea#item_description")[:required]
94 | end
95 | end
96 | end
97 | end
98 |
99 | sub_test_case 'with maxlength validation' do
100 | setup do
101 | Item.validates_length_of :name, maximum: 20
102 | Item.validates_length_of :description, maximum: 100
103 | end
104 |
105 | test 'new form' do
106 | visit '/items/new'
107 | FORM_ID_LIST.each do |form|
108 | assert_equal '20', find("#{form} input#item_name")[:maxlength]
109 | assert_equal '100', find("#{form} textarea#item_description")[:maxlength]
110 | end
111 | end
112 | end
113 |
114 | sub_test_case 'with minlength validation' do
115 | setup do
116 | Item.validates_length_of :name, minimum: 3
117 | Item.validates_length_of :description, minimum: 10
118 | end
119 |
120 | test 'new form' do
121 | visit '/items/new'
122 | FORM_ID_LIST.each do |form|
123 | assert_equal '3', find("#{form} input#item_name")[:minlength]
124 | assert_equal '10', find("#{form} textarea#item_description")[:minlength]
125 | end
126 | end
127 | end
128 | end
129 |
--------------------------------------------------------------------------------
/test/features/active_record_validation_test.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'test_helper'
4 |
5 | class ActiveRecordValidationTest < ActionDispatch::IntegrationTest
6 | FORM_ID_LIST = if Rails::VERSION::STRING >= '5.1'
7 | %w[#form_for #form_with].freeze
8 | else
9 | %w[#form_for].freeze
10 | end
11 |
12 | teardown do
13 | Person._validators.clear
14 | end
15 |
16 | sub_test_case 'without validation' do
17 | test 'new form' do
18 | visit '/people/new'
19 | FORM_ID_LIST.each do |form|
20 | assert page.has_css? "#{form} input#person_name"
21 | assert page.has_no_css? "#{form} input#person_name[required=required]"
22 | end
23 | end
24 |
25 | test 'new_without_html5_validation form' do
26 | visit '/people/new_without_html5_validation'
27 | FORM_ID_LIST.each do |form|
28 | assert page.has_css? "#{form} input#person_email"
29 | assert page.has_no_css? "#{form} input#person_email[required=required]"
30 | end
31 | end
32 | end
33 |
34 | sub_test_case 'with required validation' do
35 | setup do
36 | Person.validates_presence_of :name, :password, :bio, :blood_type, :terms_of_service, :user_type
37 | end
38 | test 'new form' do
39 | visit '/people/new'
40 | FORM_ID_LIST.each do |form|
41 | assert_equal 'required', find("#{form} input#person_name")[:required]
42 | assert_equal 'required', find("#{form} input#person_password")[:required]
43 | assert_equal 'required', find("#{form} textarea#person_bio")[:required]
44 | assert_equal 'required', find("#{form} select[name='person[blood_type]']")[:required]
45 | assert_equal 'required', find("#{form} input[type=checkbox][name='person[terms_of_service]']")[:required]
46 | assert_equal 2, all("#{form} input[name='person[user_type]'][required=required]").size
47 | end
48 | end
49 | test 'new_without_html5_validation form' do
50 | visit '/people/new_without_html5_validation'
51 | FORM_ID_LIST.each do |form|
52 | assert_nil find("#{form} input#person_name")[:required]
53 | end
54 | end
55 | test 'new_with_required_true form' do
56 | visit '/people/new_with_required_true'
57 | FORM_ID_LIST.each do |form|
58 | assert_equal 'required', find("#{form} input#person_email")[:required]
59 | end
60 | end
61 | test 'new_with_required_false form' do
62 | visit '/people/new_with_required_false'
63 | FORM_ID_LIST.each do |form|
64 | assert_nil find("#{form} input#person_email")[:required]
65 | end
66 | end
67 | sub_test_case 'disabling html5_validation in class level' do
68 | setup do
69 | Person.class_eval do |kls|
70 | kls.auto_html5_validation = false
71 | end
72 | end
73 | teardown do
74 | Person.class_eval do |kls|
75 | kls.auto_html5_validation = nil
76 | end
77 | end
78 | test 'new form' do
79 | visit '/people/new'
80 | FORM_ID_LIST.each do |form|
81 | assert_nil find("#{form} input#person_name")[:required]
82 | end
83 | end
84 | end
85 |
86 | sub_test_case 'disabling html5_validations in gem' do
87 | setup do
88 | Html5Validators.enabled = false
89 | end
90 | teardown do
91 | Html5Validators.enabled = true
92 | end
93 | test 'new form' do
94 | visit '/people/new'
95 | FORM_ID_LIST.each do |form|
96 | assert_nil find("#{form} input#person_name")[:required]
97 | assert_nil find("#{form} input#person_password")[:required]
98 | assert_nil find("#{form} textarea#person_bio")[:required]
99 | assert_nil find("#{form} select[name='person[blood_type]']")[:required]
100 | assert_nil find("#{form} input[type=checkbox][name='person[terms_of_service]']")[:required]
101 | all("#{form} input[name='person[user_type]']").each do |radio|
102 | assert_nil radio[:required]
103 | end
104 | end
105 | end
106 | end
107 | end
108 |
109 | sub_test_case 'with maxlength validation' do
110 | setup do
111 | Person.validates_length_of :name, maximum: 20
112 | Person.validates_length_of :bio, maximum: 100
113 | end
114 |
115 | test 'new form' do
116 | visit '/people/new'
117 | FORM_ID_LIST.each do |form|
118 | assert_equal '20', find("#{form} input#person_name")[:maxlength]
119 | assert_equal '100', find("#{form} textarea#person_bio")[:maxlength]
120 | end
121 | end
122 | end
123 |
124 | sub_test_case 'with minlength validation' do
125 | setup do
126 | Person.validates_length_of :name, minimum: 3
127 | Person.validates_length_of :bio, minimum: 10
128 | end
129 |
130 | test 'new form' do
131 | visit '/people/new'
132 | FORM_ID_LIST.each do |form|
133 | assert_equal '3', find("#{form} input#person_name")[:minlength]
134 | assert_equal '10', find("#{form} textarea#person_bio")[:minlength]
135 | end
136 | end
137 | end
138 |
139 | sub_test_case 'validation with context' do
140 | sub_test_case 'with an active context' do
141 | setup do
142 | Person.validates_presence_of :name, on: :create
143 | Person.validates_length_of :bio, maximum: 100, on: :create
144 | end
145 | test 'new form' do
146 | visit '/people/new'
147 | FORM_ID_LIST.each do |form|
148 | assert_equal 'required', find("#{form} input#person_name")[:required]
149 | assert_equal '100', find("#{form} textarea#person_bio")[:maxlength]
150 | end
151 | end
152 | end
153 |
154 | sub_test_case 'without an active context' do
155 | setup do
156 | Person.validates_presence_of :name, on: :update
157 | end
158 | test 'new form' do
159 | visit '/people/new'
160 | FORM_ID_LIST.each do |form|
161 | assert_nil find("#{form} input#person_name")[:required]
162 | assert_nil find("#{form} textarea#person_bio")[:maxlength]
163 | end
164 | end
165 | end
166 | end
167 | end
168 |
--------------------------------------------------------------------------------
/test/models/active_record/inherited_test.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'test_helper'
4 |
5 | if defined? ActiveRecord
6 | class Html5Validators::ActiveRecordExtensionTest < ActiveSupport::TestCase
7 | test 'An AR model' do
8 | assert_respond_to Class.new(ActiveRecord::Base), :auto_html5_validation
9 | end
10 |
11 | test "Changing a model's auto_html5_validation value doesn't affect other model's auto_html5_validation value" do
12 | cow = Class.new ApplicationRecord
13 | horse = Class.new ApplicationRecord
14 | cow.auto_html5_validation = false
15 |
16 | assert_equal true, horse.auto_html5_validation
17 | end
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/test/test_helper.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | ENV['RAILS_ENV'] ||= 'test'
4 |
5 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6 | $LOAD_PATH.unshift(File.dirname(__FILE__))
7 | # require logger before rails, or Rails 6 fails to boot
8 | require 'logger'
9 | # load Rails first
10 | require 'rails'
11 | require 'bundler/setup'
12 | Bundler.require
13 |
14 | begin
15 | require 'rackup/handler'
16 | # Work around "uninitialized constant Rack::Handler" on Capybara here: https://github.com/teamcapybara/capybara/blob/0480f90168a40780d1398c75031a255c1819dce8/lib/capybara/registrations/servers.rb#L31
17 | ::Rack::Handler = ::Rackup::Handler unless defined?(::Rack::Handler)
18 | rescue LoadError # Rails < 7.1
19 | require 'rack/handler'
20 | end
21 |
22 | require 'capybara'
23 | require 'selenium/webdriver'
24 |
25 | # needs to load the app before loading rspec/rails => capybara
26 | require 'fake_app'
27 |
28 | require 'active_support/test_case'
29 | require 'test/unit/active_support'
30 | require 'test/unit/capybara'
31 | require 'capybara/rails'
32 | require 'capybara/dsl'
33 | ActionDispatch::IntegrationTest.send :include, Capybara::DSL
34 |
35 | # Requires supporting files with custom matchers and macros, etc,
36 | # in ./support/ and its subdirectories.
37 | Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
38 |
39 | begin
40 | require "action_dispatch/system_test_case"
41 | rescue LoadError
42 | Capybara.register_driver :chrome do |app|
43 | options = Selenium::WebDriver::Chrome::Options.new(args: %w[no-sandbox headless disable-gpu])
44 | Capybara::Selenium::Driver.new(app, browser: :chrome, options: options)
45 | end
46 | Capybara.javascript_driver = :chrome
47 | else
48 | if ActionPack::VERSION::STRING > '5.2'
49 | ActionDispatch::SystemTestCase.driven_by :selenium, using: :headless_chrome
50 | else
51 | ActionDispatch::SystemTestCase.driven_by :selenium_chrome_headless
52 | end
53 | end
54 |
--------------------------------------------------------------------------------