From 33a43fd4f99241fbf6ba7ad1cb8af2fa17af1c11 Mon Sep 17 00:00:00 2001 From: Lior Chen Date: Mon, 26 Sep 2016 10:01:50 +0300 Subject: [PATCH] Allow empty values to pass regex validation In regex validators (such as number) when the value is undefined (or null) the validator should return success, to comply with the html5 number input. Since this is a breaking change, it will be enabled only using an opt-in flag called "allowEmptyValues". --- API.md | 11 ++++ dist/angular-validation.js | 20 ++++++- dist/angular-validation.min.js | 2 +- src/provider.js | 13 +++++ src/validator.directive.js | 7 ++- test/unit/actualValue.js | 48 +++++++++++++++- test/unit/actualValueWithEmpty.js | 93 +++++++++++++++++++++++++++++++ test/unit/validationGroupSpec.js | 16 +++--- 8 files changed, 194 insertions(+), 16 deletions(-) create mode 100644 test/unit/actualValueWithEmpty.js diff --git a/API.md b/API.md index 0e60eea..735feb8 100644 --- a/API.md +++ b/API.md @@ -306,6 +306,17 @@ Easily disable success/error message }]); ``` +### **Allow empty values to pass regex validation**
+`default: false`
+To allow empty values to pass regex validation (such as: number) set allowEmptyValues to true. +This way (unlike the default behaviour), an empty input would pass a "number" validation. (same as HTML5 number input) + +```javascript +.config(['$validationProvider', function ($validationProvider) { + $validationProvider.allowEmptyValues = true; // or false(default) +}]); +``` + ### **Multiple validators**
Use commas to separate multiple validators. diff --git a/dist/angular-validation.js b/dist/angular-validation.js index 34f394e..1a9650d 100644 --- a/dist/angular-validation.js +++ b/dist/angular-validation.js @@ -160,6 +160,18 @@ angular.module('validation.directive', ['validation.provider']); */ this.showErrorMessage = true; + /** + * Whether to allow for empty values to pass validation. + * When true, empty values will pass regex validations such as 'number' (vacuous truth). + * This will comply with the w3 specs for number validation. + * Otherwise, empty values will fail the regex validation (default). + * You can easily change this to true in your config + * example: $validationProvider.allowEmptyValues = true; + * + * @type {boolean} + */ + this.allowEmptyValues = false; + /** * Check form valid, return true * checkValid(Form): Check the specific form(Form) valid from angular `$valid` @@ -308,6 +320,7 @@ angular.module('validation.directive', ['validation.provider']); getDefaultMsg: this.getDefaultMsg, showSuccessMessage: this.showSuccessMessage, showErrorMessage: this.showErrorMessage, + allowEmptyValues: this.allowEmptyValues, checkValid: this.checkValid, validate: this.validate, validCallback: this.validCallback, @@ -603,8 +616,8 @@ angular.module('validation.directive', ['validation.provider']); // Check with RegExp else if (expression.constructor === RegExp) { - // Only apply the test if the value is neither undefined or null - if (value !== undefined && value !== null) { + // Only apply the test if the value is defined + if (value) { if ($validationProvider.getExpression(validator).test(value)) { if (validationGroup) { groups[validationGroup][ctrl.$name] = true; @@ -623,7 +636,8 @@ angular.module('validation.directive', ['validation.provider']); return valid.error(); } } else return valid.error(); - } + // if the value is empty or undefined, regex pass as vacuous truth + } else return $validationProvider.allowEmptyValues ? valid.success() : valid.error(); } else return valid.error(); }; diff --git a/dist/angular-validation.min.js b/dist/angular-validation.min.js index 4d75ee0..ff163be 100644 --- a/dist/angular-validation.min.js +++ b/dist/angular-validation.min.js @@ -1 +1 @@ -angular.module("validation",["validation.provider","validation.directive"]),angular.module("validation.provider",[]),angular.module("validation.directive",["validation.provider"]),function(){function a(){var a,b,c,d,e,f=this,g=function(f){a=f,b=a.get("$rootScope"),c=a.get("$http"),d=a.get("$q"),e=a.get("$timeout")},h={},i=null,j={};this.setExpression=function(a){return angular.extend(h,a),f},this.getExpression=function(a){return h[a]},this.setDefaultMsg=function(a){return angular.extend(j,a),f},this.getDefaultMsg=function(a){return j[a]},this.setValidMethod=function(a){i=a},this.getValidMethod=function(){return i},this.setErrorHTML=function(a){if(a.constructor===Function)return f.getErrorHTML=a,f},this.getErrorHTML=function(a){return'

'+a+"

"},this.setSuccessHTML=function(a){if(a.constructor===Function)return f.getSuccessHTML=a,f},this.getSuccessHTML=function(a){return'

'+a+"

"},this.showSuccessMessage=!0,this.showErrorMessage=!0,this.checkValid=function(a){return!(!a||!a.$valid)},this.validate=function(a){var c=d.defer(),g=0;if(void 0===a)return console.error("This is not a regular Form name scope"),c.reject("This is not a regular Form name scope"),c.promise;if(a.validationId)b.$broadcast(a.$name+"submit-"+a.validationId,g++);else if(a.constructor===Array)for(var h in a)b.$broadcast(a[h].$name+"submit-"+a[h].validationId,g++);else for(var i in a)"$"!==i[0]&&a[i].hasOwnProperty("$dirty")&&b.$broadcast(i+"submit-"+a[i].validationId,g++);return c.promise.success=function(a){return c.promise.then(function(b){a(b)}),c.promise},c.promise.error=function(a){return c.promise.then(null,function(b){a(b)}),c.promise},e(function(){f.checkValid(a)?c.resolve("success"):c.reject("error")}),c.promise},this.validCallback=null,this.invalidCallback=null,this.resetCallback=null,this.reset=function(a){if(void 0===a)return void console.error("This is not a regular Form name scope");if(a.validationId)b.$broadcast(a.$name+"reset-"+a.validationId);else if(a.constructor===Array)for(var c in a)b.$broadcast(a[c].$name+"reset-"+a[c].validationId);else for(var d in a)"$"!==d[0]&&a[d].hasOwnProperty("$dirty")&&b.$broadcast(d+"reset-"+a[d].validationId)},this.addMsgElement=function(a){return a.after("")},this.getMsgElement=function(a){return a.next()},this.$get=["$injector",function(a){return g(a),{setValidMethod:this.setValidMethod,getValidMethod:this.getValidMethod,setErrorHTML:this.setErrorHTML,getErrorHTML:this.getErrorHTML,setSuccessHTML:this.setSuccessHTML,getSuccessHTML:this.getSuccessHTML,setExpression:this.setExpression,getExpression:this.getExpression,setDefaultMsg:this.setDefaultMsg,getDefaultMsg:this.getDefaultMsg,showSuccessMessage:this.showSuccessMessage,showErrorMessage:this.showErrorMessage,checkValid:this.checkValid,validate:this.validate,validCallback:this.validCallback,invalidCallback:this.invalidCallback,resetCallback:this.resetCallback,reset:this.reset,addMsgElement:this.addMsgElement,getMsgElement:this.getMsgElement}}]}angular.module("validation.provider").provider("$validation",a)}.call(this),function(){function a(a){var b=a.get("$validation"),c=a.get("$timeout"),d=a.get("$parse");return{link:function(a,e,f){var g=d(f.validationReset)(a);c(function(){e.on("click",function(a){a.preventDefault(),b.reset(g)})})}}}angular.module("validation.directive").directive("validationReset",a),a.$inject=["$injector"]}.call(this),function(){function a(a){var b=a.get("$validation"),c=a.get("$timeout"),d=a.get("$parse");return{priority:1,require:"?ngClick",link:function(a,e,f){var g=d(f.validationSubmit)(a);c(function(){e.off("click"),e.on("click",function(c){c.preventDefault(),b.validate(g).success(function(){d(f.ngClick)(a)})})})}}}angular.module("validation.directive").directive("validationSubmit",a),a.$inject=["$injector"]}.call(this),function(){function a(a){function b(a,b,c){for(var d=document.querySelectorAll("*[validation-group="+b+"]"),e=0,f=d.length;e0?(b=a[0],angular.isObject(b)||(b={result:b,message:""})):b={result:!1,message:""},b}var d=a.get("$validation"),e=a.get("$q"),f=a.get("$timeout"),g=a.get("$compile"),h=a.get("$parse"),i={},j=function(a,b,c,e,f,i){var j,k=b||d.getDefaultMsg(c).success,l=h(i.validCallback),m=i.messageId,n=i.validationGroup;return j=m||n?angular.element(document.querySelector("#"+(m||n))):d.getMsgElement(a),a.attr("no-validation-message")?j.css("display","none"):d.showSuccessMessage&&k?(j.html("").append(g(d.getSuccessHTML(k,a,i))(e)),j.css("display","")):j.css("display","none"),f.$setValidity(f.$name,!0),l(e,{message:k}),d.validCallback&&d.validCallback(a),!0},k=function(a,b,c,e,f,i){var j,k=b||d.getDefaultMsg(c).error,l=h(i.invalidCallback),m=i.messageId,n=i.validationGroup;return j=m||n?angular.element(document.querySelector("#"+(m||n))):d.getMsgElement(a),a.attr("no-validation-message")?j.css("display","none"):d.showErrorMessage&&k?(j.html("").append(g(d.getErrorHTML(k,a,i))(e)),j.css("display","")):j.css("display","none"),f.$setValidity(f.$name,!1),l(e,{message:k}),d.invalidCallback&&d.invalidCallback(a),!1},l=function(a){var b=i[a];return Object.keys(b).some(function(a){return b[a]})},m={},n=function(a,f,g,h,m,o){var p=m.slice(0),q=p[0].trim(),r=q.indexOf("="),s=r===-1?q:q.substr(0,r),t=r===-1?null:q.substr(r+1),u=p.slice(1),v=s+"SuccessMessage",w=s+"ErrorMessage",x=d.getExpression(s),y=g.validationGroup,z={success:function(b){return j(f,b||g[v],s,a,h,g),!u.length||n(a,f,g,h,u,o)},error:function(b){return k(f,b||g[w],s,a,h,g)}};if(void 0===x)return console.error('You are using undefined validator "%s"',s),u.length?n(a,f,g,h,u,o):void 0;if(x.constructor===Function)return e.all([d.getExpression(s)(o,a,f,g,t)]).then(function(d){var e=c(d),f=e.message;return e.result?(y&&(i[y][h.$name]=!0,b(a,y,!0)),z.success(f)):y?(i[y][h.$name]=!1,l(y)?void b(a,y,!0):(b(a,y,!1),z.error(f))):z.error(f)},function(){return z.error()});if(x.constructor!==RegExp)return z.error();if(void 0!==o&&null!==o){if(d.getExpression(s).test(o))return y&&(i[y][h.$name]=!0,b(a,y,!0)),z.success();if(!y)return z.error();if(i[y][h.$name]=!1,!l(y))return b(a,y,!1),z.error();b(a,y,!0)}},o=function(){return(65536*(1+Math.random())|0).toString(16).substring(1)},p=function(){return o()+o()+o()+o()};return{restrict:"A",require:"ngModel",link:function(a,b,c,e){var g,h=c.validator,j=c.messageId,k=c.validationGroup,l=c.validMethod,o=c.ngModel,q=function(){},r=h.split(","),s=e.validationId=p(),t=null;return"boolean"==typeof a.initialValidity&&(g=a.initialValidity),k&&(i[k]||(i[k]={}),i[k][e.$name]=!1),j||k||d.addMsgElement(b),e.$setValidity(e.$name,g),a.$on(e.$name+"reset-"+s,function(){q(),f(function(){e.$setViewValue(t),e.$setPristine(),e.$setValidity(e.$name,void 0),e.$render(),j||k?angular.element(document.querySelector("#"+(j||k))).html(""):d.getMsgElement(b).html(""),d.resetCallback&&d.resetCallback(b)})}),l=angular.isUndefined(l)?d.getValidMethod():l,a.$on(e.$name+"submit-"+s,function(d,g){var h=e.$viewValue,i=!1;i=n(a,b,c,e,r,h),"submit"===l&&(q(),q=a.$watch(function(){return a.$eval(o)},function(d,f){d!==f&&(void 0!==d&&null!==d||(d=""),i=n(a,b,c,e,r,d))}));var j=function(a){a?delete m[g]:(m[g]=b[0],f(function(){m[Math.min.apply(null,Object.keys(m))].focus()},0))};i.constructor===Object?i.then(j):j(i)}),"blur"===l?void b.bind("blur",function(){var d=a.$eval(o);a.$apply(function(){n(a,b,c,e,r,d)})}):void("submit"!==l&&"submit-only"!==l&&(a.$watch(function(){return a.$eval(o)},function(f){if(e.$pristine&&e.$viewValue)t=e.$viewValue||"",e.$setViewValue(e.$viewValue);else if(e.$pristine)return void(j||k?angular.element(document.querySelector("#"+(j||k))).html(""):d.getMsgElement(b).html(""));n(a,b,c,e,r,f)}),f(function(){c.$observe("noValidationMessage",function(a){var c;c=j||k?angular.element(document.querySelector("#"+(j||k))):d.getMsgElement(b),"true"===a||a===!0?c.css("display","none"):"false"!==a&&a!==!1||c.css("display","block")})})))}}}angular.module("validation.directive").directive("validator",a),a.$inject=["$injector"]}.call(this); \ No newline at end of file +angular.module("validation",["validation.provider","validation.directive"]),angular.module("validation.provider",[]),angular.module("validation.directive",["validation.provider"]),function(){function a(){var a,b,c,d,e,f=this,g=function(f){a=f,b=a.get("$rootScope"),c=a.get("$http"),d=a.get("$q"),e=a.get("$timeout")},h={},i=null,j={};this.setExpression=function(a){return angular.extend(h,a),f},this.getExpression=function(a){return h[a]},this.setDefaultMsg=function(a){return angular.extend(j,a),f},this.getDefaultMsg=function(a){return j[a]},this.setValidMethod=function(a){i=a},this.getValidMethod=function(){return i},this.setErrorHTML=function(a){if(a.constructor===Function)return f.getErrorHTML=a,f},this.getErrorHTML=function(a){return'

'+a+"

"},this.setSuccessHTML=function(a){if(a.constructor===Function)return f.getSuccessHTML=a,f},this.getSuccessHTML=function(a){return'

'+a+"

"},this.showSuccessMessage=!0,this.showErrorMessage=!0,this.allowEmptyValues=!1,this.checkValid=function(a){return!(!a||!a.$valid)},this.validate=function(a){var c=d.defer(),g=0;if(void 0===a)return console.error("This is not a regular Form name scope"),c.reject("This is not a regular Form name scope"),c.promise;if(a.validationId)b.$broadcast(a.$name+"submit-"+a.validationId,g++);else if(a.constructor===Array)for(var h in a)b.$broadcast(a[h].$name+"submit-"+a[h].validationId,g++);else for(var i in a)"$"!==i[0]&&a[i].hasOwnProperty("$dirty")&&b.$broadcast(i+"submit-"+a[i].validationId,g++);return c.promise.success=function(a){return c.promise.then(function(b){a(b)}),c.promise},c.promise.error=function(a){return c.promise.then(null,function(b){a(b)}),c.promise},e(function(){f.checkValid(a)?c.resolve("success"):c.reject("error")}),c.promise},this.validCallback=null,this.invalidCallback=null,this.resetCallback=null,this.reset=function(a){if(void 0===a)return void console.error("This is not a regular Form name scope");if(a.validationId)b.$broadcast(a.$name+"reset-"+a.validationId);else if(a.constructor===Array)for(var c in a)b.$broadcast(a[c].$name+"reset-"+a[c].validationId);else for(var d in a)"$"!==d[0]&&a[d].hasOwnProperty("$dirty")&&b.$broadcast(d+"reset-"+a[d].validationId)},this.addMsgElement=function(a){return a.after("")},this.getMsgElement=function(a){return a.next()},this.$get=["$injector",function(a){return g(a),{setValidMethod:this.setValidMethod,getValidMethod:this.getValidMethod,setErrorHTML:this.setErrorHTML,getErrorHTML:this.getErrorHTML,setSuccessHTML:this.setSuccessHTML,getSuccessHTML:this.getSuccessHTML,setExpression:this.setExpression,getExpression:this.getExpression,setDefaultMsg:this.setDefaultMsg,getDefaultMsg:this.getDefaultMsg,showSuccessMessage:this.showSuccessMessage,showErrorMessage:this.showErrorMessage,allowEmptyValues:this.allowEmptyValues,checkValid:this.checkValid,validate:this.validate,validCallback:this.validCallback,invalidCallback:this.invalidCallback,resetCallback:this.resetCallback,reset:this.reset,addMsgElement:this.addMsgElement,getMsgElement:this.getMsgElement}}]}angular.module("validation.provider").provider("$validation",a)}.call(this),function(){function a(a){var b=a.get("$validation"),c=a.get("$timeout"),d=a.get("$parse");return{link:function(a,e,f){var g=d(f.validationReset)(a);c(function(){e.on("click",function(a){a.preventDefault(),b.reset(g)})})}}}angular.module("validation.directive").directive("validationReset",a),a.$inject=["$injector"]}.call(this),function(){function a(a){var b=a.get("$validation"),c=a.get("$timeout"),d=a.get("$parse");return{priority:1,require:"?ngClick",link:function(a,e,f){var g=d(f.validationSubmit)(a);c(function(){e.off("click"),e.on("click",function(c){c.preventDefault(),b.validate(g).success(function(){d(f.ngClick)(a)})})})}}}angular.module("validation.directive").directive("validationSubmit",a),a.$inject=["$injector"]}.call(this),function(){function a(a){function b(a,b,c){for(var d=document.querySelectorAll("*[validation-group="+b+"]"),e=0,f=d.length;e0?(b=a[0],angular.isObject(b)||(b={result:b,message:""})):b={result:!1,message:""},b}var d=a.get("$validation"),e=a.get("$q"),f=a.get("$timeout"),g=a.get("$compile"),h=a.get("$parse"),i={},j=function(a,b,c,e,f,i){var j,k=b||d.getDefaultMsg(c).success,l=h(i.validCallback),m=i.messageId,n=i.validationGroup;return j=m||n?angular.element(document.querySelector("#"+(m||n))):d.getMsgElement(a),a.attr("no-validation-message")?j.css("display","none"):d.showSuccessMessage&&k?(j.html("").append(g(d.getSuccessHTML(k,a,i))(e)),j.css("display","")):j.css("display","none"),f.$setValidity(f.$name,!0),l(e,{message:k}),d.validCallback&&d.validCallback(a),!0},k=function(a,b,c,e,f,i){var j,k=b||d.getDefaultMsg(c).error,l=h(i.invalidCallback),m=i.messageId,n=i.validationGroup;return j=m||n?angular.element(document.querySelector("#"+(m||n))):d.getMsgElement(a),a.attr("no-validation-message")?j.css("display","none"):d.showErrorMessage&&k?(j.html("").append(g(d.getErrorHTML(k,a,i))(e)),j.css("display","")):j.css("display","none"),f.$setValidity(f.$name,!1),l(e,{message:k}),d.invalidCallback&&d.invalidCallback(a),!1},l=function(a){var b=i[a];return Object.keys(b).some(function(a){return b[a]})},m={},n=function(a,f,g,h,m,o){var p=m.slice(0),q=p[0].trim(),r=q.indexOf("="),s=r===-1?q:q.substr(0,r),t=r===-1?null:q.substr(r+1),u=p.slice(1),v=s+"SuccessMessage",w=s+"ErrorMessage",x=d.getExpression(s),y=g.validationGroup,z={success:function(b){return j(f,b||g[v],s,a,h,g),!u.length||n(a,f,g,h,u,o)},error:function(b){return k(f,b||g[w],s,a,h,g)}};return void 0===x?(console.error('You are using undefined validator "%s"',s),u.length?n(a,f,g,h,u,o):void 0):x.constructor===Function?e.all([d.getExpression(s)(o,a,f,g,t)]).then(function(d){var e=c(d),f=e.message;return e.result?(y&&(i[y][h.$name]=!0,b(a,y,!0)),z.success(f)):y?(i[y][h.$name]=!1,l(y)?void b(a,y,!0):(b(a,y,!1),z.error(f))):z.error(f)},function(){return z.error()}):x.constructor!==RegExp?z.error():o?d.getExpression(s).test(o)?(y&&(i[y][h.$name]=!0,b(a,y,!0)),z.success()):y?(i[y][h.$name]=!1,l(y)?void b(a,y,!0):(b(a,y,!1),z.error())):z.error():d.allowEmptyValues?z.success():z.error()},o=function(){return(65536*(1+Math.random())|0).toString(16).substring(1)},p=function(){return o()+o()+o()+o()};return{restrict:"A",require:"ngModel",link:function(a,b,c,e){var g,h=c.validator,j=c.messageId,k=c.validationGroup,l=c.validMethod,o=c.ngModel,q=function(){},r=h.split(","),s=e.validationId=p(),t=null;return"boolean"==typeof a.initialValidity&&(g=a.initialValidity),k&&(i[k]||(i[k]={}),i[k][e.$name]=!1),j||k||d.addMsgElement(b),e.$setValidity(e.$name,g),a.$on(e.$name+"reset-"+s,function(){q(),f(function(){e.$setViewValue(t),e.$setPristine(),e.$setValidity(e.$name,void 0),e.$render(),j||k?angular.element(document.querySelector("#"+(j||k))).html(""):d.getMsgElement(b).html(""),d.resetCallback&&d.resetCallback(b)})}),l=angular.isUndefined(l)?d.getValidMethod():l,a.$on(e.$name+"submit-"+s,function(d,g){var h=e.$viewValue,i=!1;i=n(a,b,c,e,r,h),"submit"===l&&(q(),q=a.$watch(function(){return a.$eval(o)},function(d,f){d!==f&&(void 0!==d&&null!==d||(d=""),i=n(a,b,c,e,r,d))}));var j=function(a){a?delete m[g]:(m[g]=b[0],f(function(){m[Math.min.apply(null,Object.keys(m))].focus()},0))};i.constructor===Object?i.then(j):j(i)}),"blur"===l?void b.bind("blur",function(){var d=a.$eval(o);a.$apply(function(){n(a,b,c,e,r,d)})}):void("submit"!==l&&"submit-only"!==l&&(a.$watch(function(){return a.$eval(o)},function(f){if(e.$pristine&&e.$viewValue)t=e.$viewValue||"",e.$setViewValue(e.$viewValue);else if(e.$pristine)return void(j||k?angular.element(document.querySelector("#"+(j||k))).html(""):d.getMsgElement(b).html(""));n(a,b,c,e,r,f)}),f(function(){c.$observe("noValidationMessage",function(a){var c;c=j||k?angular.element(document.querySelector("#"+(j||k))):d.getMsgElement(b),"true"===a||a===!0?c.css("display","none"):"false"!==a&&a!==!1||c.css("display","block")})})))}}}angular.module("validation.directive").directive("validator",a),a.$inject=["$injector"]}.call(this); \ No newline at end of file diff --git a/src/provider.js b/src/provider.js index 6dc7cda..eb8054e 100644 --- a/src/provider.js +++ b/src/provider.js @@ -156,6 +156,18 @@ */ this.showErrorMessage = true; + /** + * Whether to allow for empty values to pass validation. + * When true, empty values will pass regex validations such as 'number' (vacuous truth). + * This will comply with the w3 specs for number validation. + * Otherwise, empty values will fail the regex validation (default). + * You can easily change this to true in your config + * example: $validationProvider.allowEmptyValues = true; + * + * @type {boolean} + */ + this.allowEmptyValues = false; + /** * Check form valid, return true * checkValid(Form): Check the specific form(Form) valid from angular `$valid` @@ -304,6 +316,7 @@ getDefaultMsg: this.getDefaultMsg, showSuccessMessage: this.showSuccessMessage, showErrorMessage: this.showErrorMessage, + allowEmptyValues: this.allowEmptyValues, checkValid: this.checkValid, validate: this.validate, validCallback: this.validCallback, diff --git a/src/validator.directive.js b/src/validator.directive.js index 5b39e50..f120cec 100644 --- a/src/validator.directive.js +++ b/src/validator.directive.js @@ -225,8 +225,8 @@ // Check with RegExp else if (expression.constructor === RegExp) { - // Only apply the test if the value is neither undefined or null - if (value !== undefined && value !== null) { + // Only apply the test if the value is defined + if (value) { if ($validationProvider.getExpression(validator).test(value)) { if (validationGroup) { groups[validationGroup][ctrl.$name] = true; @@ -245,7 +245,8 @@ return valid.error(); } } else return valid.error(); - } + // if the value is empty or undefined, regex pass as vacuous truth + } else return $validationProvider.allowEmptyValues ? valid.success() : valid.error(); } else return valid.error(); }; diff --git a/test/unit/actualValue.js b/test/unit/actualValue.js index 6967131..6b8c7ac 100644 --- a/test/unit/actualValue.js +++ b/test/unit/actualValue.js @@ -2,7 +2,7 @@ /* jasmine specs for provider go here */ -describe('provider', function() { +describe('provider with empty values disallowed', function() { var $rootScope; var $compile; var $scope; @@ -109,4 +109,50 @@ describe('provider', function() { expect(successSpy).not.toHaveBeenCalled(); expect(errorSpy).toHaveBeenCalled(); })); + + it('set value to undefined', inject(function() { + var submitSpy = jasmine.createSpy('submitSpy'); + var successSpy = jasmine.createSpy('successSpy'); + var errorSpy = jasmine.createSpy('errorSpy'); + + $scope.$apply(function() { + $scope.number = undefined; + }); + + // expect fail as a empty values are disallowed + validationProvider.validate($scope.Form) + .success(function() { + successSpy(); + }) + .error(function() { + errorSpy(); + }); + + $timeout.flush(); + expect(successSpy).not.toHaveBeenCalled(); + expect(errorSpy).toHaveBeenCalled(); + })); + + it('set value to empty', inject(function() { + var submitSpy = jasmine.createSpy('submitSpy'); + var successSpy = jasmine.createSpy('successSpy'); + var errorSpy = jasmine.createSpy('errorSpy'); + + $scope.$apply(function() { + $scope.number = ''; + }); + + // expect fail as a empty values are disallowed + validationProvider.validate($scope.Form) + .success(function() { + successSpy(); + }) + .error(function() { + errorSpy(); + }); + + $timeout.flush(); + expect(successSpy).not.toHaveBeenCalled(); + expect(errorSpy).toHaveBeenCalled(); + })); }); diff --git a/test/unit/actualValueWithEmpty.js b/test/unit/actualValueWithEmpty.js new file mode 100644 index 0000000..058f2cb --- /dev/null +++ b/test/unit/actualValueWithEmpty.js @@ -0,0 +1,93 @@ +'use strict'; + +/* jasmine specs for provider go here */ + +describe('provider with empty values allowed', function() { + var $rootScope; + var $compile; + var $scope; + var $timeout; + var element; + var validationProvider; + var myApp; + + beforeEach(function() { + myApp = angular.module('myApp', ['validation', 'validation.rule']) + .config(function($validationProvider) { + validationProvider = $validationProvider; + // Allow empty values + $validationProvider.allowEmptyValues = true; + }); + + return myApp; + }); + + beforeEach(module('myApp')); + + beforeEach(inject(function($injector) { + $rootScope = $injector.get('$rootScope'); + $compile = $injector.get('$compile'); + $scope = $rootScope.$new(); + $timeout = $injector.get('$timeout'); + + element = $compile('
')($scope); + angular.element(document.body).append(element); + $scope.$digest(); + })); + + it('set value to undefined', inject(function() { + var submitSpy = jasmine.createSpy('submitSpy'); + var successSpy = jasmine.createSpy('successSpy'); + var errorSpy = jasmine.createSpy('errorSpy'); + + $scope.$apply(function() { + $scope.number = undefined; + }); + + $scope.$on('numberWatchsubmit-' + $scope.Form.numberWatch.validationId, function() { + submitSpy(); + }); + + // expect success as a vacuous truth (value is undefined and should therefore pass both number and maxlength tests) + validationProvider.validate($scope.Form) + .success(function() { + successSpy(); + }) + .error(function() { + errorSpy(); + }); + + $timeout.flush(); + expect(submitSpy).toHaveBeenCalled(); + expect(successSpy).toHaveBeenCalled(); + expect(errorSpy).not.toHaveBeenCalled(); + })); + + it('set value to empty', inject(function() { + var submitSpy = jasmine.createSpy('submitSpy'); + var successSpy = jasmine.createSpy('successSpy'); + var errorSpy = jasmine.createSpy('errorSpy'); + + $scope.$apply(function() { + $scope.number = ''; + }); + + $scope.$on('numberWatchsubmit-' + $scope.Form.numberWatch.validationId, function() { + submitSpy(); + }); + + // expect success as a vacuous truth (value is empty and should therefore pass both number and maxlength tests) + validationProvider.validate($scope.Form) + .success(function() { + successSpy(); + }) + .error(function() { + errorSpy(); + }); + + $timeout.flush(); + expect(submitSpy).toHaveBeenCalled(); + expect(successSpy).toHaveBeenCalled(); + expect(errorSpy).not.toHaveBeenCalled(); + })); +}); diff --git a/test/unit/validationGroupSpec.js b/test/unit/validationGroupSpec.js index 9a77b8a..4df78c3 100644 --- a/test/unit/validationGroupSpec.js +++ b/test/unit/validationGroupSpec.js @@ -154,8 +154,8 @@ describe('validation-group directive', function() { it('should be dirty and invalid', function() { $scope.Form.email.$setViewValue('foo@bar.com'); $scope.Form.telephone.$setViewValue('065839481'); - $scope.Form.email.$setViewValue(''); - $scope.Form.telephone.$setViewValue(''); + $scope.Form.email.$setViewValue('aa'); + $scope.Form.telephone.$setViewValue('aa'); expect($scope.Form.$dirty).toBe(true); expect(element.hasClass('ng-dirty')).toBe(true); @@ -169,11 +169,11 @@ describe('validation-group directive', function() { expect(element.hasClass('ng-valid')).toBe(true); $scope.Form.telephone.$setViewValue('065839481'); - $scope.Form.email.$setViewValue(''); + $scope.Form.email.$setViewValue('a'); expect($scope.Form.$valid).toBe(true); expect(element.hasClass('ng-valid')).toBe(true); - $scope.Form.telephone.$setViewValue(''); + $scope.Form.telephone.$setViewValue('aa'); expect($scope.Form.$valid).toBe(false); expect(element.hasClass('ng-invalid')).toBe(true); }); @@ -188,7 +188,7 @@ describe('validation-group directive', function() { it('should have an error message inside the #contact element when no element is valid', function() { $scope.Form.email.$setViewValue('foo@bar.com'); - $scope.Form.email.$setViewValue(''); + $scope.Form.email.$setViewValue('a'); messageElem = angular.element(element[0].querySelector('#contact > p')); expect(messageElem.hasClass('validation-invalid')).toBe(true); @@ -205,7 +205,7 @@ describe('validation-group directive', function() { it('should have a success message inside the #contact element when one of element is valid', function() { $scope.Form.email.$setViewValue('foo@bar.com'); $scope.Form.telephone.$setViewValue('065839481'); - $scope.Form.email.$setViewValue(''); + $scope.Form.email.$setViewValue('a'); messageElem = angular.element(element[0].querySelector('#contact > p')); expect(messageElem.hasClass('validation-valid')).toBe(true); @@ -214,8 +214,8 @@ describe('validation-group directive', function() { it('should have an error message inside the #contact element when both of elements are invalid', function() { $scope.Form.email.$setViewValue('foo@bar.com'); $scope.Form.telephone.$setViewValue('065839481'); - $scope.Form.email.$setViewValue(''); - $scope.Form.telephone.$setViewValue(''); + $scope.Form.email.$setViewValue('a'); + $scope.Form.telephone.$setViewValue('a'); messageElem = angular.element(element[0].querySelector('#contact > p')); expect(messageElem.hasClass('validation-invalid')).toBe(true);