diff --git a/.travis.yml b/.travis.yml index 6fd811c22..e8802398f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,7 +11,7 @@ gemfile: - gemfiles/rails_5.0.gemfile - gemfiles/rails_5.1.gemfile - gemfiles/rails_5.2.gemfile - - gemfiles/rails_6.0.0.beta1.gemfile + - gemfiles/rails_6.0.0.beta2.gemfile - gemfiles/rails_edge.gemfile matrix: allow_failures: @@ -19,15 +19,15 @@ matrix: - gemfile: gemfiles/rails_edge.gemfile exclude: - rvm: 2.2.10 - gemfile: gemfiles/rails_6.0.0.beta1.gemfile + gemfile: gemfiles/rails_6.0.0.beta2.gemfile - rvm: 2.2.10 gemfile: gemfiles/rails_edge.gemfile - rvm: 2.3.8 - gemfile: gemfiles/rails_6.0.0.beta1.gemfile + gemfile: gemfiles/rails_6.0.0.beta2.gemfile - rvm: 2.3.8 gemfile: gemfiles/rails_edge.gemfile - rvm: 2.4.5 - gemfile: gemfiles/rails_6.0.0.beta1.gemfile + gemfile: gemfiles/rails_6.0.0.beta2.gemfile - rvm: 2.4.5 gemfile: gemfiles/rails_edge.gemfile fast_finish: true diff --git a/Appraisals b/Appraisals index b75196bde..0d2deb3f9 100644 --- a/Appraisals +++ b/Appraisals @@ -12,8 +12,8 @@ appraise 'rails-5.2' do gem 'rails', '~> 5.2.0' end -appraise 'rails-6.0.0.beta1' do - gem 'rails', '~> 6.0.0.beta1' +appraise 'rails-6.0.0.beta2' do + gem 'rails', '~> 6.0.0.beta2' end appraise 'rails-edge' do diff --git a/client_side_validations.gemspec b/client_side_validations.gemspec index 40373884c..95d3e7f38 100644 --- a/client_side_validations.gemspec +++ b/client_side_validations.gemspec @@ -20,7 +20,7 @@ Gem::Specification.new do |spec| spec.files = `git ls-files -z -- {CHANGELOG.md,LICENSE.md,README.md,lib,vendor}`.split("\x0") spec.require_paths = ['lib'] - spec.add_dependency 'rails', '>= 5.0.0.1', '<= 6.0.0.beta1' + spec.add_dependency 'rails', '>= 5.0.0.1', '<= 6.0.0.beta2' spec.add_dependency 'jquery-rails', '~> 4.3' spec.add_dependency 'js_regex', '~> 3.1' diff --git a/coffeescript/rails.validations.coffee b/coffeescript/rails.validations.coffee index 644a33d36..a5d495297 100644 --- a/coffeescript/rails.validations.coffee +++ b/coffeescript/rails.validations.coffee @@ -413,8 +413,14 @@ ClientSideValidations = valid = true form.find(":input[name^=\"#{name_prefix}\"][name$=\"#{name_suffix}\"]").each -> + other_value = $(@).val() + + unless options.case_sensitive + value = value.toLowerCase() + other_value = other_value.toLowerCase() + if $(@).attr('name') != name - if $(@).val() == value + if other_value == value valid = false $(@).data('notLocallyUnique', true) else diff --git a/gemfiles/rails_6.0.0.beta1.gemfile b/gemfiles/rails_6.0.0.beta2.gemfile similarity index 80% rename from gemfiles/rails_6.0.0.beta1.gemfile rename to gemfiles/rails_6.0.0.beta2.gemfile index 8e589dd73..aed864127 100644 --- a/gemfiles/rails_6.0.0.beta1.gemfile +++ b/gemfiles/rails_6.0.0.beta2.gemfile @@ -4,6 +4,6 @@ source 'https://rubygems.org' -gem 'rails', '~> 6.0.0.beta1' +gem 'rails', '~> 6.0.0.beta2' gemspec path: '../' diff --git a/lib/client_side_validations/active_record/uniqueness.rb b/lib/client_side_validations/active_record/uniqueness.rb index 8d4115506..aeb359485 100644 --- a/lib/client_side_validations/active_record/uniqueness.rb +++ b/lib/client_side_validations/active_record/uniqueness.rb @@ -6,7 +6,7 @@ module Uniqueness def client_side_hash(model, attribute, _force = nil) hash = {} hash[:message] = model.errors.generate_message(attribute, message_type, options.except(:scope)) - hash[:case_sensitive] = options[:case_sensitive] + hash[:case_sensitive] = true if options[:case_sensitive] hash[:id] = model.id unless model.new_record? hash[:allow_blank] = true if options[:allow_nil] || options[:allow_blank] diff --git a/test/active_record/cases/test_uniqueness_validator.rb b/test/active_record/cases/test_uniqueness_validator.rb index 29b66e88d..cd44226a7 100644 --- a/test/active_record/cases/test_uniqueness_validator.rb +++ b/test/active_record/cases/test_uniqueness_validator.rb @@ -4,37 +4,55 @@ module ActiveRecord class UniquenessValidatorTest < ClientSideValidations::ActiveRecordTestBase + def uniqueness_validator_options(hash) + if defined?(Rails.version) && Gem::Version.new(Rails.version) < Gem::Version.new('6.0.0.beta2') + { case_sensitive: true }.merge(hash) + else + hash + end + end + def test_uniqueness_client_side_hash - expected_hash = { message: 'has already been taken', case_sensitive: true } + expected_hash = uniqueness_validator_options(message: 'has already been taken') assert_equal expected_hash, UniquenessValidator.new(attributes: [:name]).client_side_hash(@user, :name) end def test_uniqueness_client_side_hash_allowing_blank - expected_hash = { message: 'has already been taken', case_sensitive: true, allow_blank: true } + expected_hash = uniqueness_validator_options(message: 'has already been taken', allow_blank: true) assert_equal expected_hash, UniquenessValidator.new(attributes: [:name], allow_blank: true).client_side_hash(@user, :name) end def test_uniqueness_client_side_hash_allowing_nil - expected_hash = { message: 'has already been taken', case_sensitive: true, allow_blank: true } + expected_hash = uniqueness_validator_options(message: 'has already been taken', allow_blank: true) assert_equal expected_hash, UniquenessValidator.new(attributes: [:name], allow_nil: true).client_side_hash(@user, :name) end + def test_uniqueness_client_side_hash_case_insensitive + expected_hash = { message: 'has already been taken' } + assert_equal expected_hash, UniquenessValidator.new(attributes: [:name], case_sensitive: false).client_side_hash(@user, :name) + end + + def test_uniqueness_client_side_hash_case_sensitive + expected_hash = uniqueness_validator_options(message: 'has already been taken', case_sensitive: true) + assert_equal expected_hash, UniquenessValidator.new(attributes: [:name], case_sensitive: true).client_side_hash(@user, :name) + end + def test_uniqueness_client_side_hash_with_custom_message - expected_hash = { message: 'is not available', case_sensitive: true } + expected_hash = uniqueness_validator_options(message: 'is not available') assert_equal expected_hash, UniquenessValidator.new(attributes: [:name], message: 'is not available').client_side_hash(@user, :name) end def test_uniqueness_client_side_hash_with_existing_record @user.stubs(:new_record?).returns(false) @user.stubs(:id).returns(1) - expected_hash = { message: 'has already been taken', case_sensitive: true, id: 1 } + expected_hash = uniqueness_validator_options(message: 'has already been taken', id: 1) assert_equal expected_hash, UniquenessValidator.new(attributes: [:name]).client_side_hash(@user, :name) end def test_uniqueness_client_side_hash_with_single_scope_item @user.stubs(:age).returns(30) @user.stubs(:title).returns('test title') - expected_hash = { message: 'has already been taken', case_sensitive: true, scope: { title: 'test title' } } + expected_hash = uniqueness_validator_options(message: 'has already been taken', scope: { title: 'test title' }) result_hash = UniquenessValidator.new(attributes: [:name], scope: :title).client_side_hash(@user, :name) assert_equal expected_hash, result_hash end @@ -42,26 +60,26 @@ def test_uniqueness_client_side_hash_with_single_scope_item def test_uniqueness_client_side_hash_with_multiple_scope_items @user.stubs(:age).returns(30) @user.stubs(:title).returns('test title') - expected_hash = { message: 'has already been taken', case_sensitive: true, scope: { age: 30, title: 'test title' } } + expected_hash = uniqueness_validator_options(message: 'has already been taken', scope: { age: 30, title: 'test title' }) result_hash = UniquenessValidator.new(attributes: [:name], scope: %i[age title]).client_side_hash(@user, :name) assert_equal expected_hash, result_hash end def test_uniqueness_client_side_hash_with_empty_scope_array - expected_hash = { message: 'has already been taken', case_sensitive: true } + expected_hash = uniqueness_validator_options(message: 'has already been taken') result_hash = UniquenessValidator.new(attributes: [:name], scope: []).client_side_hash(@user, :name) assert_equal expected_hash, result_hash end def test_uniqueness_client_side_hash_when_nested_module @user = ActiveRecordTestModule::User2.new - expected_hash = { message: 'has already been taken', case_sensitive: true, class: 'active_record_test_module/user2' } + expected_hash = uniqueness_validator_options(message: 'has already been taken', class: 'active_record_test_module/user2') assert_equal expected_hash, UniquenessValidator.new(attributes: [:name]).client_side_hash(@user, :name) end def test_uniqueness_client_side_hash_with_class_from_options @user = UserForm.new - expected_hash = { message: 'has already been taken', case_sensitive: true, class: 'user' } + expected_hash = uniqueness_validator_options(message: 'has already been taken', class: 'user') assert_equal expected_hash, UniquenessValidator.new(attributes: [:name], client_validations: { class: 'User' }).client_side_hash(@user, :name) end end diff --git a/test/javascript/public/test/validators/uniqueness.js b/test/javascript/public/test/validators/uniqueness.js index 20ecd1d45..7a93c70bc 100644 --- a/test/javascript/public/test/validators/uniqueness.js +++ b/test/javascript/public/test/validators/uniqueness.js @@ -33,7 +33,7 @@ QUnit.module('Uniqueness options', { } }); -QUnit.test('when matching local uniqueness for nested has-many resources', function(assert) { +QUnit.test('when matching local case-insensitive uniqueness for nested has-many resources', function(assert) { dataCsv = { html_settings: { type: 'ActionView::Helpers::FormBuilder', @@ -67,7 +67,46 @@ QUnit.test('when matching local uniqueness for nested has-many resources', funct options = { 'message': "must be unique" }; user_0_email.val('not-locally-unique'); - user_1_email.val('not-locally-unique'); + user_1_email.val('Not-Locally-Unique'); assert.equal(ClientSideValidations.validators.local.uniqueness(user_1_email, options), "must be unique"); }); + +QUnit.test('when matching case-sensitive local uniqueness for nested has-many resources', function(assert) { + dataCsv = { + html_settings: { + type: 'ActionView::Helpers::FormBuilder', + input_tag: '
', + label_tag: '
' + }, + validators: { 'user[email]':{"uniqueness":[{"message": "must be unique"}]}} + } + + $('#qunit-fixture') + .append($('
', { + action: '/users', + 'data-client-side-validations': JSON.stringify(dataCsv), + method: 'post', + id: 'new_user_3' + })) + .find('form') + .append($('', { + name: 'profile[user_attributes][0][email]', + id: 'user_0_email', + })) + .append($('', { + name: 'profile[user_attributes][1][email]', + id: 'user_1_email', + })); + + $('form#new_user_3').validate(); + + var user_0_email = $('#user_0_email'), + user_1_email = $('#user_1_email'), + options = { 'message': "must be unique", "case_sensitive": true }; + + user_0_email.val('locally-unique'); + user_1_email.val('Locally-Unique'); + + assert.equal(ClientSideValidations.validators.local.uniqueness(user_1_email, options), undefined); +}); diff --git a/vendor/assets/javascripts/rails.validations.js b/vendor/assets/javascripts/rails.validations.js index 37351e3e4..b84a5c643 100644 --- a/vendor/assets/javascripts/rails.validations.js +++ b/vendor/assets/javascripts/rails.validations.js @@ -535,8 +535,14 @@ form = element.closest('form'); valid = true; form.find(":input[name^=\"" + name_prefix + "\"][name$=\"" + name_suffix + "\"]").each(function() { + var other_value; + other_value = $(this).val(); + if (!options.case_sensitive) { + value = value.toLowerCase(); + other_value = other_value.toLowerCase(); + } if ($(this).attr('name') !== name) { - if ($(this).val() === value) { + if (other_value === value) { valid = false; return $(this).data('notLocallyUnique', true); } else {