diff --git a/src/pat/validation/tests.js b/src/pat/validation/tests.js index 8d76a39cb..55cc261d5 100644 --- a/src/pat/validation/tests.js +++ b/src/pat/validation/tests.js @@ -127,57 +127,107 @@ define(["pat-registry", "pat-validation"], function(registry, pattern) { expect($el.find('em.warning').text()).toBe("Slegs heelgetalle"); }); - // Unfortunately date validation cannot be tested using the Constraints - // API. ``ValidityState`` information is not updated after - // programmatically setting values. - // See whole discussion here: https://twitter.com/thetetet/status/1285239806205755393 - // and: https://stackoverflow.com/questions/53226031/html-input-validity-not-checked-when-value-is-changed-by-javascript - - //it("validates dates", function() { - // var $el = $( - // '
'); - // var input = $el[0].querySelector('input'); - // input.value = '2000-02-30'; - // var pat = pattern.init($el); - // pat.validateForm(); - // debugger; - // expect($el.find('em.warning').length).toBe(1); - // expect($el.find('em.warning').text()).toBe("This value must be a valid date"); - - // $el = $( - // ''); - // input = $el[0].querySelector('input'); - // input.value = '2000-02-28'; - // pat = pattern.init($el); - // pat.validateForm(); - // expect($el.find('em.warning').length).toBe(0); - //}); - - //it("doesn't validate empty optional dates", function() { - // var $el = $( - // ''); - // var pat = pattern.init($el); - // pat.validateForm(); - // expect($el.find('em.warning').length).toBe(0); - //}); - - //it("do require-validate non-empty required dates", function() { - // var $el = $( - // ''); - // var pat = pattern.init($el); - // pat.validateForm(); - // expect($el.find('em.warning').length).toBe(1); - // expect($el.find('em.warning').text()).toBe("This field is required"); - //}); + // Using ``type="text"`` for date validation as Constraints API + // ``ValidityState`` information is not updated after programmatically + // setting values. + // See: https://twitter.com/thetetet/status/1285239806205755393 + + it("validates dates", function() { + var $el = $( + ''); + + var $input = $el.find(':input'); + $input.val('2000-02-30'); + pattern.init($el); + $input.trigger('change'); + expect($el.find('em.warning').length).toBe(1); + expect($el.find('em.warning').text()).toBe("This value must be a valid date"); + + $input.val('2000-02-28'); + $input.trigger('change'); + expect($el.find('em.warning').length).toBe(0); + }); + + it("validates dates with before/after constraints", function() { + var $el = $( + ''); + $('#lab').append($el); + + pattern.init($el); + + var $start = $el.find('#start'); + var $end = $el.find('#end'); + + // Before/after constraints still allow for normal date validation + // (wasn't before this commit) + $start.val('2020-02-30'); + $start.trigger('change'); + expect($el.find('em.warning').length).toBe(1); + expect($el.find('em.warning').text()).toBe("The start date must on or before the end date."); + + // Before/after without required allows for empty dates of the + // relation. + $start.val('2020-10-10'); + $start.trigger('change'); + expect($el.find('em.warning').length).toBe(0); + + // Violate the before/after constraint + $end.val('2020-10-05'); + $end.trigger('change'); + expect($el.find('em.warning').length).toBe(2); + expect($el.find('em.warning').text().indexOf("The start date must on or before the end date.") !== -1).toBe(true); + expect($el.find('em.warning').text().indexOf("The end date must on or before the start date.") !== -1).toBe(true); + + // Fulfill the before/after constraint - same date + $end.val('2020-10-10'); + $end.trigger('change'); + expect($el.find('em.warning').length).toBe(0); + + // Fulfill the before/after constraint - start before end + $start.val('2020-10-01'); + $start.trigger('change'); + expect($el.find('em.warning').length).toBe(0); + + // Before/after without required allows for empty dates of the + // relation. + $start.val(''); + $start.trigger('change'); + expect($el.find('em.warning').length).toBe(0); + + }); + + it("doesn't validate empty optional dates", function() { + var $el = $( + ''); + + var $input = $el.find(':input'); + $input.val(''); + pattern.init($el); + $input.trigger('change'); + expect($el.find('em.warning').length).toBe(0); + }); + + it("do require-validate non-empty required dates", function() { + var $el = $( + ''); + + var $input = $el.find(':input'); + $input.val(''); + pattern.init($el); + $input.trigger('change'); + + expect($el.find('em.warning').length).toBe(1); + expect($el.find('em.warning').text()).toBe("This field is required"); + }); it("doesn't validate disabled elements", function() { var $el = $( diff --git a/src/pat/validation/validation.js b/src/pat/validation/validation.js index 0518dca43..57d69198d 100644 --- a/src/pat/validation/validation.js +++ b/src/pat/validation/validation.js @@ -93,40 +93,37 @@ define([ setLocalDateConstraints: function (input, opts, constraints) { /* Set the relative date constraints, i.e. not-after and not-before, as well as custom messages. */ - var name = input.getAttribute('name').replace(/\./g, '\\.'), - type = this.getFieldType(input), - c = constraints[name][type]; + var name = input.getAttribute('name').replace(/\./g, '\\.'); + var type = this.getFieldType(input); + var c = constraints[name][type]; if (!c || typeof opts == "undefined") { return constraints; } _.each(['before', 'after'], function (relation) { - var isDate = validate.moment.isDate, - relative = opts.not && opts.not[relation] || undefined, - arr, constraint, $ref; + var relative = opts.not ? opts.not[relation] : undefined; + var $ref; if (typeof relative === "undefined") { return; } - constraint = relation === "before" ? 'earliest' : 'latest'; - if (isDate(relative)) { - c[constraint] = relative; + var relative_constraint = relation === "before" ? 'earliest' : 'latest'; + if (validate.moment.isDate(relative)) { + c[relative_constraint] = relative; } else { try { $ref = $(relative); } catch (e) { console.log(e); } - arr = $ref.data('pat-validation-refs') || []; + var arr = $ref.data('pat-validation-refs') || []; if (!_.contains(arr, input)) { arr.unshift(input); $ref.data('pat-validation-refs', arr); } - c[constraint] = $ref.val(); - if (! $ref.val()) { - // do not validate empty reference dates - constraints[name][type] = false; - return; + if ($ref && $ref.val()) { + // relative constraint validation + c[relative_constraint] = $ref.val(); } } }); @@ -211,16 +208,18 @@ define([ // Returns true if a date check should be done. // Don't check if there is no input - this should be handled by // the ``required`` attribute. - // In case of HTML5 ``date``/``datetime-local`` support we - // implicitly check this if the input is ``badInput``, which is - // only set in case of an invalid date but not on empty values. - // Note that in case of HTML5 ``date``/``datetime-local`` support - // the value is empty on invalid input. + // In case of HTML5 date/datetime-local support we also have to + // check for ``badInput`` as invalid date input will result in an + // empty ``value``. var type = input.getAttribute('type'); // we need the raw type here - if (Modernizr.inputtypes.date && type.indexOf('date') === 0) { - // Do the date check if the input is invalid (actually - // double-checking here - HTML5 and validate.js). - return input.validity.badInput; + if ( + Modernizr.inputtypes.date && + type.indexOf('date') === 0 && + typeof input.validity.badInput !== "undefined" + ) { + // Do the date check if the input is invalid or not missing + // (actually double-checking here - HTML5 and validate.js). + return input.validity.badInput || !!input.value; } else { // Do the date check if input is not empty (Safari has yet no // date support at all)