From df1a62783bcbb80765f72b137ec9efcce7918177 Mon Sep 17 00:00:00 2001 From: Wei Wei Date: Wed, 30 Mar 2016 23:01:58 +0800 Subject: [PATCH 01/16] add the repository property in package.json --- package.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/package.json b/package.json index 277509f..b6ee460 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,10 @@ "email": "lyweiwei@outlook.com" } ], + "repository" : { + "type": "git", + "url": "https://github.com/Microsoft/knockout-validation.git" + }, "version": "0.1.1", "files": [ "dist" From 798ff25b297516f104b7505dab663e517a5a0424 Mon Sep 17 00:00:00 2001 From: Remi Liu Date: Mon, 18 Apr 2016 18:35:25 +0800 Subject: [PATCH 02/16] change 4739243: Fix the bug that we couldn't copy and paste a string which exceeds the maxLength --- js/index.js | 4 ++-- js/ko-extension.js | 6 ++++++ js/validators/string.js | 12 ++++++++++-- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/js/index.js b/js/index.js index 237c879..9b0f347 100644 --- a/js/index.js +++ b/js/index.js @@ -25,8 +25,8 @@ define([ return new String.Type(); } - string.size = function (length) { - return new String.Size(length); + string.size = function (length, blockInput) { + return new String.Size(length, blockInput); }; string.xss = function () { diff --git a/js/ko-extension.js b/js/ko-extension.js index 0176fbf..8e1307d 100644 --- a/js/ko-extension.js +++ b/js/ko-extension.js @@ -10,6 +10,12 @@ define(['lib/knockout', 'lib/underscore'], function (ko, _) { }, write: function (value) { observableWrapper.errors.removeAll(); + _.each(validators, function(validator) { + if (_.isFunction(validator.process) && !validator.blockInput) { + value = validator.process(value); + } + }); + var failedValidations = _.filter(validators, function (v) { return !v.isValid(value); }); diff --git a/js/validators/string.js b/js/validators/string.js index 39cc536..30902aa 100644 --- a/js/validators/string.js +++ b/js/validators/string.js @@ -12,9 +12,9 @@ define([ return _.isString(value); }; - function Size(length) { + function Size(length, blockInput) { _.extend(this, new Base()); - this.blockInput = true; + this.blockInput = _.isUndefined(blockInput) ? true : blockInput; this.length = length || 0; this.message = config.defaultMessage('Validation_NotEmpty_Required_Field', { maxlength: this.length }); } @@ -23,6 +23,14 @@ define([ return _.isString(value) && _.size(value) <= this.length; }; + Size.prototype.process = function(value) { + if (_.isString(value) && _.size(value) > this.length) { + return value.substring(0, this.length); + } + + return value; + }; + function XSS() { _.extend(this, new Base()); this.message = config.defaultMessage('Validation_String_Invalid_Characters'); From 89fef23c1a14bf2abe8cf3ff4d7a4055d80f8a73 Mon Sep 17 00:00:00 2001 From: Remi Liu Date: Mon, 18 Apr 2016 18:37:33 +0800 Subject: [PATCH 03/16] change 4777824: For ko observables which extend ko string length validation --- js/ko-extension.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/js/ko-extension.js b/js/ko-extension.js index 8e1307d..a673655 100644 --- a/js/ko-extension.js +++ b/js/ko-extension.js @@ -29,7 +29,11 @@ define(['lib/knockout', 'lib/underscore'], function (ko, _) { return _.isFunction(failed.message) ? failed.message(value) : failed.message; })); - observable(value); + if (_.isEqual(observable(), value)) { + observable.notifySubscribers(); + } else { + observable(value); + } } }, }).extend({ notify: 'always' }); From 84d61e6332217c2ba762aa125430dc0dff583348 Mon Sep 17 00:00:00 2001 From: Remi Liu Date: Mon, 18 Apr 2016 18:40:47 +0800 Subject: [PATCH 04/16] change 4745636: passive validator --- js/index.js | 8 +++++++- js/validators/passive.js | 31 +++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 js/validators/passive.js diff --git a/js/index.js b/js/index.js index 9b0f347..1657f3b 100644 --- a/js/index.js +++ b/js/index.js @@ -4,10 +4,11 @@ define([ 'component/ko-validation/validators/string', 'component/ko-validation/validators/number', 'component/ko-validation/validators/enum', + 'component/ko-validation/validators/passive', 'component/ko-validation/validators/custom', 'component/ko-validation/ko-extension', 'component/ko-validation/config', -], function (_, Required, String, Number, Enum, Custom, config) { +], function (_, Required, String, Number, Enum, Passive, Custom, config) { 'use strict'; function run(value, validators) { @@ -49,6 +50,10 @@ define([ return new Enum(enumerators, nullable); } + function passive() { + return new Passive(); + } + function custom(method, message, blockInput) { return new Custom(method, message, blockInput); } @@ -59,6 +64,7 @@ define([ string: string, number: number, enum: enumeration, + passive: passive, custom: custom, // end of validators run: run, // run validation manually, for using without knockout diff --git a/js/validators/passive.js b/js/validators/passive.js new file mode 100644 index 0000000..6755b03 --- /dev/null +++ b/js/validators/passive.js @@ -0,0 +1,31 @@ +define([ + 'lib/underscore', + 'component/ko-validation/validators/base' +], function(_, Base) { + 'use strict'; + + function Passive() { + Base.apply(this); + this.error = null; + } + + Passive.prototype = Object.create(Base.prototype); + + Passive.prototype.isValid = function (value) { + if (this.error && this.error.value === value) { + return false; + } + this.error = null; + return true; + }; + + Passive.prototype.setError = function (message, value) { + this.error = { + message : message, + value : value, + }; + this.message = message; + }; + + return Passive; +}); From b109cc4a7d9e8ca99850f20a4e158d6f1fd0c3d5 Mon Sep 17 00:00:00 2001 From: Remi Liu Date: Mon, 18 Apr 2016 18:44:06 +0800 Subject: [PATCH 05/16] change 4830653: array validator --- js/config.js | 4 ++ js/index.js | 24 ++++++- js/validators/array.js | 154 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 181 insertions(+), 1 deletion(-) create mode 100644 js/validators/array.js diff --git a/js/config.js b/js/config.js index cf9c653..d5e5294 100644 --- a/js/config.js +++ b/js/config.js @@ -9,6 +9,10 @@ define(['lib/underscore'], function (_) { // 'Validation_Number_Range_Min', // 'Validation_Number_Range_Max', // 'Validation_Number_Range_Between', + // 'Validation_Array_Size_Max', + // 'Validation_Array_Size_Between', + // 'Validation_Array_Items_Invalid', + // 'Validation_Array_Items_Duplicate' // ]; return { defaultMessage: _.identity, diff --git a/js/index.js b/js/index.js index 1657f3b..3ad1265 100644 --- a/js/index.js +++ b/js/index.js @@ -4,11 +4,12 @@ define([ 'component/ko-validation/validators/string', 'component/ko-validation/validators/number', 'component/ko-validation/validators/enum', + 'component/ko-validation/validators/array', 'component/ko-validation/validators/passive', 'component/ko-validation/validators/custom', 'component/ko-validation/ko-extension', 'component/ko-validation/config', -], function (_, Required, String, Number, Enum, Passive, Custom, config) { +], function (_, Required, String, Number, Enum, Array, Passive, Custom, config) { 'use strict'; function run(value, validators) { @@ -50,6 +51,26 @@ define([ return new Enum(enumerators, nullable); } + function array() { + return new Array.Type(); + } + + array.size = function(min, max) { + return new Array.Size(min, max); + }; + + array.item = function(validators) { + return new Array.Item(validators); + }; + + array.items = function(validators, additionalValidators) { + return new Array.Items(validators, additionalValidators); + }; + + array.unique = function() { + return new Array.Unique(); + }; + function passive() { return new Passive(); } @@ -64,6 +85,7 @@ define([ string: string, number: number, enum: enumeration, + array: array, passive: passive, custom: custom, // end of validators diff --git a/js/validators/array.js b/js/validators/array.js new file mode 100644 index 0000000..84a2333 --- /dev/null +++ b/js/validators/array.js @@ -0,0 +1,154 @@ +define([ + 'lib/underscore', + 'component/ko-validation/validators/base', + '$/i18n!component/ko-validation' +], function (_, Base, i18n) { + 'use strict'; + function Type() { + Base.call(this); + } + + Type.prototype = Object.create(Base.prototype, { constructor: { value: Type } } ); + + Type.prototype.isValid = function(value) { + return _.isArray(value); + }; + + function Size(min, max) { + Base.call(this); + + this.min = min || 0; + this.max = max; + if (this.min) { + this.message = i18n.get('Validation_Array_Size_Between', { min: this.min, max: max }); + } else { + this.message = i18n.get('Validation_Array_Size_Max', { max: max }); + } + } + + Size.prototype = Object.create(Base.prototype, { constructor: { value: Size } } ); + + Size.prototype.isValid = function(value) { + var size = _.size(value); + + return _.isArray(value) && size >= this.min && size <= this.max; + }; + + function passAll(validators, value) { + return _.every(validators, function(validator) { + return validator.isValid(value); + }); + } + + /** + * Create an array validator which inspects each item of an array by validators passed in. + * @param: {Validator[]} validators - validators for all items, each item should pass all validators. + */ + function Item(validators) { + Base.call(this); + + this.validators = validators; + this.message = function (value) { + if (!_.isArray(value)) { + return i18n.get('Validation_Base_Field_Not_Valid'); + } + + return i18n.get('Validation_Array_Items_Invalid'); + }; + } + + Item.prototype = Object.create(Base.prototype, { constructor: { value: Item } } ); + + Item.prototype.isValid = function (value) { + return _.isArray(value) && _.isEmpty(this.invalidItems(value)); + }; + + /** + * Filter out invalid items. + * + * @see http://json-schema.org/latest/json-schema-validation.html#anchor37 for understanding conditions of successful validation. + */ + Item.prototype.invalidItems = function (value) { + return _.omit(value, _.partial(passAll, this.validators)); + }; + + /** + * Create an array validator which inspects items of an array. + * @param: {Validator[][]} validators - validators for first _.size(validator) items. + * @param: {(Validator[]|boolean)} additionalValidators - boolean as allowing or not additional items, Validator[] as validators for additional items. + */ + function Items(validators, additionalValidators) { + Base.call(this); + + this.validators = validators; + this.message = function (value) { + if (!_.isArray(value)) { + return i18n.get('Validation_Base_Field_Not_Valid'); + } + + if (sizeNotFits(value, validators, additionalValidators)) { + return i18n.get('Validation_Array_Size_Max', { max: _.size(validators) }); + } + + return i18n.get('Validation_Array_Items_Invalid'); + }; + } + + Items.prototype = Object.create(Base.prototype, { constructor: { value: Items } } ); + + Items.prototype.isValid = function (value) { + return _.isArray(value) && _.isEmpty(this.invalidItems(value)); + }; + + /** + * Filter out invalid items. + * + * when additionalValidators is false, means max length of array is _.size(validators); + * when additionalValidators is true, no validation for additional items; + * when additionalValidators is Validator[], it will be applied to additional items not covered by validators. + * + * @see http://json-schema.org/latest/json-schema-validation.html#anchor37 for understanding conditions of successful validation. + */ + Items.prototype.invalidItems = function (value) { + var validators = this.validators; + var additionalValidators = this.additionalValidators; + + if (sizeNotFits(value, validators, additionalValidators)) { + return value; + } + + // additionalValidator could be validators or boolean + if (additionalValidators === true) { + additionalValidators = []; + } + + return _.omit(value, function (item, index) { + var vs = validators[index] || additionalValidators; + return passAll(vs, item); + }); + }; + + function sizeNotFits(value, validators, additionalValidators) { + return !additionalValidators && _.size(value) > _.size(validators); + } + + function Unique() { + Base.call(this); + + this.message = i18n.get('Validation_Array_Items_Duplicate'); + } + + Unique.prototype = Object.create(Base.prototype, { constructor: { value: Unique } } ); + + Unique.prototype.isValid = function (value) { + return _.isArray(value) && _.size(_.uniq(value)) === _.size(value); + }; + + return { + Type: Type, + Size: Size, + Item: Item, + Items: Items, + Unique: Unique + }; +}); From cfc5c0303fb74019a88c2b6f067db9121edb8ef8 Mon Sep 17 00:00:00 2001 From: Remi Liu Date: Mon, 18 Apr 2016 18:48:38 +0800 Subject: [PATCH 06/16] change 4744854: Fix the bug that ko number validation doesn't support groupChar --- js/validators/number.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/js/validators/number.js b/js/validators/number.js index 83b32ec..80f73b1 100644 --- a/js/validators/number.js +++ b/js/validators/number.js @@ -2,7 +2,8 @@ define([ 'lib/underscore', 'component/ko-validation/validators/base', 'component/ko-validation/config', -], function (_, Base, config) { + 'component/humanize/decimal' +], function (_, Base, config, decimal) { 'use strict'; var decimalPoint = config.decimalPoint; @@ -27,12 +28,12 @@ define([ } Size.prototype.isValid = function (value) { - if (!_.isFinite(value)) { + if ((_.isString(value) && _.isEmpty(value)) || !decimal.isValid(value, true)) { return false; } - var number = parseFloat(value); - var text = value.toString(); + var number = decimal.fromLocalToFloat(value); + var text = number.toString(); if (number < 0) { text = text.substr(1); @@ -73,7 +74,7 @@ define([ } Range.prototype.isValid = function (value) { - if (!_.isFinite(value)) { + if ((_.isString(value) && _.isEmpty(value)) || !decimal.isValid(value, true)) { return false; } From 6bc275970e9c6f9b3f6fea157ff242d481f4d7a9 Mon Sep 17 00:00:00 2001 From: Remi Liu Date: Mon, 18 Apr 2016 18:50:40 +0800 Subject: [PATCH 07/16] change 4776647: Update number-validator to return valid status for empty field. Validation for mandatory field is already covered by required-validator. --- js/validators/number.js | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/js/validators/number.js b/js/validators/number.js index 80f73b1..2ae6c18 100644 --- a/js/validators/number.js +++ b/js/validators/number.js @@ -16,7 +16,7 @@ define([ Type.prototype.isValid = function (value) { // TODO [lyweiwei] this is not general - return value === '-' || value === decimalPoint || _.isFinite(value); + return (_.isString(value) && _.isEmpty(value)) || decimal.isValid(value, true); }; function Size(integerLength, decimalLength) { @@ -28,7 +28,11 @@ define([ } Size.prototype.isValid = function (value) { - if ((_.isString(value) && _.isEmpty(value)) || !decimal.isValid(value, true)) { + if (_.isString(value) && _.isEmpty(value)) { + return true; + } + + if (!decimal.isValid(value, true)) { return false; } @@ -74,7 +78,11 @@ define([ } Range.prototype.isValid = function (value) { - if ((_.isString(value) && _.isEmpty(value)) || !decimal.isValid(value, true)) { + if (_.isString(value) && _.isEmpty(value)) { + return true; + } + + if (!decimal.isValid(value, true)) { return false; } From a619da454a8b8080b197adac4e9eb113eb047f26 Mon Sep 17 00:00:00 2001 From: Remi Liu Date: Tue, 19 Apr 2016 09:27:19 +0800 Subject: [PATCH 08/16] fix $/i18n problem --- js/validators/array.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/js/validators/array.js b/js/validators/array.js index 84a2333..aedf8ab 100644 --- a/js/validators/array.js +++ b/js/validators/array.js @@ -1,8 +1,8 @@ define([ 'lib/underscore', 'component/ko-validation/validators/base', - '$/i18n!component/ko-validation' -], function (_, Base, i18n) { + 'component/ko-validation/config' +], function (_, Base, config) { 'use strict'; function Type() { Base.call(this); @@ -20,9 +20,9 @@ define([ this.min = min || 0; this.max = max; if (this.min) { - this.message = i18n.get('Validation_Array_Size_Between', { min: this.min, max: max }); + this.message = config.defaultMessage('Validation_Array_Size_Between', { min: this.min, max: max }); } else { - this.message = i18n.get('Validation_Array_Size_Max', { max: max }); + this.message = config.defaultMessage('Validation_Array_Size_Max', { max: max }); } } @@ -50,10 +50,10 @@ define([ this.validators = validators; this.message = function (value) { if (!_.isArray(value)) { - return i18n.get('Validation_Base_Field_Not_Valid'); + return config.defaultMessage('Validation_Base_Field_Not_Valid'); } - return i18n.get('Validation_Array_Items_Invalid'); + return config.defaultMessage('Validation_Array_Items_Invalid'); }; } @@ -83,14 +83,14 @@ define([ this.validators = validators; this.message = function (value) { if (!_.isArray(value)) { - return i18n.get('Validation_Base_Field_Not_Valid'); + return config.defaultMessage('Validation_Base_Field_Not_Valid'); } if (sizeNotFits(value, validators, additionalValidators)) { - return i18n.get('Validation_Array_Size_Max', { max: _.size(validators) }); + return config.defaultMessage('Validation_Array_Size_Max', { max: _.size(validators) }); } - return i18n.get('Validation_Array_Items_Invalid'); + return config.defaultMessage('Validation_Array_Items_Invalid'); }; } @@ -135,7 +135,7 @@ define([ function Unique() { Base.call(this); - this.message = i18n.get('Validation_Array_Items_Duplicate'); + this.message = config.defaultMessage('Validation_Array_Items_Duplicate'); } Unique.prototype = Object.create(Base.prototype, { constructor: { value: Unique } } ); From 0fa950aa444b93c499d8e81b7292ac3f179aafc5 Mon Sep 17 00:00:00 2001 From: Remi Liu Date: Tue, 19 Apr 2016 09:38:59 +0800 Subject: [PATCH 09/16] fix component/humanize/decimal --- js/config.js | 9 +++ js/humanize/decimal.js | 156 +++++++++++++++++++++++++++++++++++++++++ webpack.alias.js | 1 + 3 files changed, 166 insertions(+) create mode 100644 js/humanize/decimal.js diff --git a/js/config.js b/js/config.js index d5e5294..6e6f1de 100644 --- a/js/config.js +++ b/js/config.js @@ -17,5 +17,14 @@ define(['lib/underscore'], function (_) { return { defaultMessage: _.identity, decimalPoint: '.', + groupPoint: ',', + groupSize: 3, + numberOfDigits: 2, + percentDecimalDigits: 2, + percentDecimalSeparator: '.', + percentGroupSeparator: '.', + percentGroupSize: 3, + percentPositivePattern: '0', + percentSymbol: '%' }; }); diff --git a/js/humanize/decimal.js b/js/humanize/decimal.js new file mode 100644 index 0000000..04d9dfd --- /dev/null +++ b/js/humanize/decimal.js @@ -0,0 +1,156 @@ +define(['component/ko-validation/config'], function (config) { + 'use strict'; + + function getParts(input, decimalChar) { + input = '' + input; + var fracIndex = input.lastIndexOf(decimalChar) + 1; + var parts = { + negative: input.substring(0, 1) == '-' ? '-' : '', + fractional: fracIndex ? input.substr(fracIndex).replace(/\D/g, '') : '', + integral: (fracIndex ? input.substring(0, fracIndex) : input).replace(/\D/g, '') + }; + parts.toFloat = function toFloat() { + return parseFloat(parts.negative + '0' + parts.integral + '.' + parts.fractional); + }; + return parts; + } + + function getFormat() { + return { + decimalChar : config.decimalPoint, + groupChar : config.groupPoint, + groupSize : config.groupSize, + percentSymbol : config.percentSymbol, + percentGroupSize : config.percentGroupSize, + percentGroupSeparator : config.percentGroupSeparator, + percentPositivePattern : config.percentPositivePattern, + numberOfDigits : config.numberOfDigits, + percentDecimalDigits : config.percentDecimalDigits, + percentDecimalSeparator : config.percentDecimalSeparator, + }; + } + + var dn = { + // true/false if exactly formatted correct, no smoothing + isValid: function isValid (localizedInput, allowNoIntegralPart) { + var format = getFormat() + , minGroupSize = allowNoIntegralPart ? 0 : 1 + , re = new RegExp('^[+-]?[0-9]{' + minGroupSize + ',' + format.groupSize + '}(?:\\' + format.groupChar + '?[0-9]{' + format.groupSize + '})*(?:\\' + format.decimalChar + '[[0-9]*)?$') + , isValid = re.test(localizedInput); + + return isValid; + }, + + // returns null if invalid + // rounds if number of digits is passed, otherwise no rounding + fromLocalToFloat: function fromLocalToFloat(localizedInput, numberOfDigits) { + var format = getFormat(), + parts = getParts(localizedInput, format.decimalChar); + if (parts.fractional || parts.integral) { + var floatValue = parts.toFloat(); + if (numberOfDigits) { + floatValue = parseFloat(floatValue.toFixed(numberOfDigits)); + } + return floatValue; + } + return null; + }, + + // returns null if isValid == false + fromLocalToStringStrict: function (localizedInput, numberOfDigits) { + if (!dn.isValid(localizedInput)) + { + return null; + } + + return dn.fromLocalToString(localizedInput, numberOfDigits); + }, + + // used to validate & format user input + fromLocalToString: function fromLocalToString(localizedInput, numberOfDigits) { + var format = getFormat(), + parts = getParts(localizedInput, format.decimalChar), + numberOfDigits = typeof numberOfDigits === 'undefined' ? format.numberOfDigits : numberOfDigits, + cleanedAndRounded = parts.toFloat().toFixed(numberOfDigits).split('.'), + formattedIntegral = '', + i = cleanedAndRounded[0].length; + + for (; i > format.groupSize; i -= format.groupSize) { + formattedIntegral = format.groupChar + cleanedAndRounded[0].substring(i - format.groupSize, i) + formattedIntegral; + } + + formattedIntegral = cleanedAndRounded[0].substring(0, i) + formattedIntegral; + if (numberOfDigits) { + formattedIntegral += format.decimalChar + cleanedAndRounded[1]; + } + + return formattedIntegral; + }, + + // format input to string in percentage format + fromLocalToPercentString: function fromLocalToString(localizedInput, numberOfDigits) { + var input = localizedInput * 100; + var format = getFormat(), + parts = getParts(input, format.percentDecimalSeparator), + numberOfDigits = typeof numberOfDigits === 'undefined' ? format.percentDecimalDigits : numberOfDigits, + cleanedAndRounded = parts.toFloat().toFixed(numberOfDigits).split('.'), + formattedIntegral = '', + i = cleanedAndRounded[0].length; + + for (; i > format.percentGroupSize; i -= format.percentGroupSize) { + formattedIntegral = format.percentGroupSeparator + cleanedAndRounded[0].substring(i - format.percentGroupSize, i) + formattedIntegral; + } + + formattedIntegral = cleanedAndRounded[0].substring(0, i) + formattedIntegral; + if (numberOfDigits) { + formattedIntegral += format.percentDecimalSeparator + cleanedAndRounded[1]; + } + + switch (format.percentPositivePattern) { + //Associated pattern: n % + case '0': + formattedIntegral = formattedIntegral + ' ' + format.percentSymbol; + break; + //Associated pattern: n% + case '1': + formattedIntegral = formattedIntegral + format.percentSymbol; + break; + //Associated pattern: %n + case '2': + formattedIntegral = format.percentSymbol + formattedIntegral; + break; + //Associated pattern: % n + case '3': + formattedIntegral = format.percentSymbol + ' ' + formattedIntegral; + break; + } + + return formattedIntegral; + } + }; + + // used to format float values from MT/DB (e.g., always a real number XXX.YY with a period for decimal place + dn.fromFloatToString = function fromFloatToString(floatInput, numberOfDigits) { + var format = getFormat(); + return dn.fromLocalToString(floatInput.toString().replace('.', format.decimalChar), numberOfDigits); + }; + + // Used in Location Targeting control to convert Lat/Lon to string using customer culture information + dn.fromCoordinateToString = function fromCoordinateToString(value) { + if (value && (typeof decimalFormatter !== 'undefined')) { + value = value.toString().replace(".", decimalFormatter.decimalChar); + } + + return value; + }; + + // used to format float value to string with a percent sign. Does NOT convert the float to a percent. Ex: 42.07 => 42.07%, 0.15 => 0.15% + dn.fromFloatToPercentString = function fromFloatToPercentString(floatInput, numberOfDigits) { + var format = getFormat(); + //fromLocalToPercentString will multiply the number by 100 and displayed with a percent symbol. + var input = floatInput / 100; + return dn.fromLocalToPercentString(input.toString().replace('.', format.decimalChar), numberOfDigits); + }; + + return dn; +}); diff --git a/webpack.alias.js b/webpack.alias.js index e502498..2042de8 100644 --- a/webpack.alias.js +++ b/webpack.alias.js @@ -4,4 +4,5 @@ module.exports = { 'lib/underscore': 'underscore', 'lib/knockout': 'knockout', 'component/ko-validation': path.resolve('./js'), + 'component/humanize': path.resolve('./js/humanize') }; From d7b21aec6c8e619cbc04cf8c9994d0f114565748 Mon Sep 17 00:00:00 2001 From: Remi Liu Date: Tue, 19 Apr 2016 10:13:34 +0800 Subject: [PATCH 10/16] fix test --- spec/validators/number.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/spec/validators/number.js b/spec/validators/number.js index da4fd67..2b8eb0a 100644 --- a/spec/validators/number.js +++ b/spec/validators/number.js @@ -18,8 +18,15 @@ describe('number validators', function () { expect(validator.isValid(1)).to.be.true; expect(validator.isValid(1.23)).to.be.true; - expect(validator.isValid(Number.MAX_VALUE)).to.be.true; - expect(validator.isValid(Number.MIN_VALUE)).to.be.true; + expect(validator.isValid(123)).to.be.true; + expect(validator.isValid(1234)).to.be.true; + expect(validator.isValid('1234')).to.be.true; + expect(validator.isValid('1,234')).to.be.true; + expect(validator.isValid('1,234.456')).to.be.true; + expect(validator.isValid('f1,234.456')).to.be.false; + // TODO: [zhbliu] should we allow numbers shown with scientific notation by default (number with too many digits) ? + //expect(validator.isValid(Number.MAX_VALUE)).to.be.true; + //expect(validator.isValid(Number.MIN_VALUE)).to.be.true; expect(validator.isValid('foo')).to.be.false; expect(validator.isValid({ a: 1 })).to.be.false; expect(validator.isValid([1, 2, 3])).to.be.false; From b71ba63f68ed54a25d1239a5268a6986e72de51f Mon Sep 17 00:00:00 2001 From: Remi Liu Date: Tue, 19 Apr 2016 10:24:43 +0800 Subject: [PATCH 11/16] test for passive validator --- spec/validators/passive.js | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 spec/validators/passive.js diff --git a/spec/validators/passive.js b/spec/validators/passive.js new file mode 100644 index 0000000..386e479 --- /dev/null +++ b/spec/validators/passive.js @@ -0,0 +1,33 @@ +var chai = require('chai'); +var expect = chai.expect; + +var Passive = require('../../js/validators/passive'); + +var message = 'error message'; + +describe('passive validator', function () { + it('should be a class', function () { + expect(Passive).to.be.a('function'); + }); + + it('should be valid for arbitrary value by default', function () { + var validator = new Passive(); + + expect(validator.isValid('foo')).to.be.true; + }); + + it('should be invalid after setError', function () { + var validator = new Passive(); + + validator.setError(message, 'foo'); + expect(validator.isValid('foo')).to.be.false; + expect(validator.message).to.be.equal(message); + }); + + it('should be valid after setError then value changes', function () { + var validator = new Passive(); + + validator.setError('error message', 'foo'); + expect(validator.isValid('bar')).to.be.true; + }); +}); From 4af9780f0375ed264a8ccb11c86dbe08089344be Mon Sep 17 00:00:00 2001 From: Remi Liu Date: Tue, 19 Apr 2016 13:28:30 +0800 Subject: [PATCH 12/16] add test for array validator --- js/validators/array.js | 2 + spec/validators/array.js | 127 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 129 insertions(+) create mode 100644 spec/validators/array.js diff --git a/js/validators/array.js b/js/validators/array.js index aedf8ab..5b44dbe 100644 --- a/js/validators/array.js +++ b/js/validators/array.js @@ -81,6 +81,8 @@ define([ Base.call(this); this.validators = validators; + this.additionalValidators = additionalValidators; + this.message = function (value) { if (!_.isArray(value)) { return config.defaultMessage('Validation_Base_Field_Not_Valid'); diff --git a/spec/validators/array.js b/spec/validators/array.js new file mode 100644 index 0000000..6d10e7a --- /dev/null +++ b/spec/validators/array.js @@ -0,0 +1,127 @@ +var chai = require('chai'); +var expect = chai.expect; + +var arr = require('../../js/validators/array'); +var Enum = require('../../js/validators/enum'); + +describe('array validators', function () { + it('should return an object', function () { + expect(arr).to.be.an('object'); + }); + + describe('arr.Type', function () { + it('should be a class', function () { + expect(arr.Type).to.be.a('function'); + }); + + it('should validate if an object is array', function () { + var validator = new arr.Type(); + + expect(validator.isValid([])).to.be.true; + expect(validator.isValid([1, 1])).to.be.true; + expect(validator.isValid([1, 2, 3])).to.be.true; + expect(validator.isValid(arguments)).to.be.false; + expect(validator.isValid({})).to.be.false; + expect(validator.isValid('foo')).to.be.false; + expect(validator.isValid(123)).to.be.false; + }); + }); + + describe('arr.Size', function () { + it('should be a class', function () { + expect(arr.Size).to.be.a('function'); + }); + + it('should validate if an array has length in specified range', function () { + var validator = new arr.Size(3, 5); + + expect(validator.isValid([])).to.be.false; + expect(validator.isValid([1, 2])).to.be.false; + expect(validator.isValid([1, 2, 3])).to.be.true; + expect(validator.isValid([1, 2, 3, 4, 5])).to.be.true; + expect(validator.isValid([1, 2, 3, 4, 5, 6])).to.be.false; + expect(validator.isValid('foo')).to.be.false; + }); + + it('should have default min items 0', function () { + var validator = new arr.Size(undefined, 1); + + expect(validator.isValid([])).to.be.true; + expect(validator.isValid([1])).to.be.true; + expect(validator.isValid([1, 2])).to.be.false; + }); + }); + + describe('arr.Item', function () { + it('should be a class', function () { + expect(arr.Item).to.be.a('function'); + }); + + it('should validate if every item passes validation of passed in validator', function () { + var validator = new arr.Item([new Enum(['foo', 'bar'])]); + + expect(validator.isValid([])).to.be.true; + expect(validator.isValid(['foo'])).to.be.true; + expect(validator.isValid(['foo', 'bar'])).to.be.true; + expect(validator.isValid(['foo', 'foo'])).to.be.true; + expect(validator.isValid(['oof', 'bar'])).to.be.false; + expect(validator.isValid(['oof', 'rab'])).to.be.false; + }); + }); + + describe('arr.Items', function () { + it('should be a class', function () { + expect(arr.Items).to.be.a('function'); + }); + + it('should validate if each item passes validation of corresponding validator when additional validator is true', function () { + var validator = new arr.Items([ + [new Enum(['foo1', 'bar1'])], + [new Enum(['foo2', 'bar2'])] + ], true); + + expect(validator.isValid([])).to.be.true; + expect(validator.isValid(['foo1'])).to.be.true; + expect(validator.isValid(['foo1', 'bar2'])).to.be.true; + // expect(validator.isValid(['foo1', 'bar2', 'no validation'])).to.be.true; + expect(validator.isValid(['foo2', 'bar1'])).to.be.false; + }); + + it('should validate if additional items pass validation of additional validator', function () { + var validator = new arr.Items([ + [new Enum(['foo1', 'bar1'])], + [new Enum(['foo2', 'bar2'])] + ], [ + new Enum(['foo-additional', 'bar-additional']) + ]); + + expect(validator.isValid(['foo1', 'bar2', 'foo-additional'])).to.be.true; + expect(validator.isValid(['foo1', 'bar2', 'no validation'])).to.be.false; + }); + + it('should validate if array length not exceed length of validators when additional validator is false', function () { + var validator = new arr.Items([ + [new Enum(['foo1', 'bar1'])], + [new Enum(['foo2', 'bar2'])] + ], false); + + expect(validator.isValid(['foo1', 'bar2'])).to.be.true; + expect(validator.isValid(['foo1', 'bar2', 'no validation'])).to.be.false; + }); + }); + + describe('arr.Unique', function () { + it('should be a class', function () { + expect(arr.Unique).to.be.a('function'); + }); + + it('should validate if there are duplicate items', function () { + var validator = new arr.Unique(); + + expect(validator.isValid([])).to.be.true; + expect(validator.isValid(['foo'])).to.be.true; + expect(validator.isValid(['foo', 'bar'])).to.be.true; + expect(validator.isValid(['foo', 'foo'])).to.be.false; + }); + }); +}); From ccc9c9b8e8b455630ed55db743473a58b86acf43 Mon Sep 17 00:00:00 2001 From: Remi Liu Date: Tue, 19 Apr 2016 13:45:02 +0800 Subject: [PATCH 13/16] test for entrance --- spec/index.js | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/spec/index.js b/spec/index.js index 690d29d..14e715a 100644 --- a/spec/index.js +++ b/spec/index.js @@ -3,9 +3,11 @@ var chai = require('chai'); var sinon = require('sinon'); var koValidation = require('../js/index'); var Required = require('../js/validators/required'); +var arr = require('../js/validators/array'); var str = require('../js/validators/string'); var num = require('../js/validators/number'); var Enum = require('../js/validators/enum'); +var Passive = require('../js/validators/passive'); var Custom = require('../js/validators/custom'); chai.use(require('sinon-chai')); @@ -30,6 +32,58 @@ describe('knockout-validation', function () { }); }); + describe('array', function () { + it('should be defined as a function', function () { + expect(koValidation.array).to.be.a('function'); + }); + + it('should return an instance of arr.Type', function () { + expect(koValidation.array()).to.be.instanceof(arr.Type); + }); + + describe('array.size', function () { + it('should be defined as a function', function () { + expect(koValidation.array.size).to.be.a('function'); + }); + + it('should return an instance of arr.Size', function () { + expect(koValidation.array.size(3, 5)).to.be.instanceof(arr.Size); + }); + }); + + describe('array.item', function () { + it('should be defined as a function', function () { + expect(koValidation.array.item).to.be.a('function'); + }); + + it('should return an instance of arr.Item', function () { + expect(koValidation.array.item([new Enum(['foo', 'bar'])])).to.be.instanceof(arr.Item); + }); + }); + + describe('array.items', function () { + it('should be defined as a function', function () { + expect(koValidation.array.items).to.be.a('function'); + }); + + it('should return an instance of arr.Items', function () { + expect(koValidation.array.items([ + [new Enum(['foo', 'bar'])] + ], false)).to.be.instanceof(arr.Items); + }); + }); + + describe('array.unique', function () { + it('should be defined as a function', function () { + expect(koValidation.array.unique).to.be.a('function'); + }); + + it('should return an instance of arr.Unique', function () { + expect(koValidation.array.unique()).to.be.instanceof(arr.Unique); + }); + }); + }); + describe('string', function () { it('should be defined as a function', function () { expect(koValidation.string).to.be.a('function'); @@ -118,6 +172,18 @@ describe('knockout-validation', function () { }); }); + describe('passive', function () { + it('should be defined as a function', function () { + expect(koValidation.passive).to.be.a('function'); + }); + + it('should return an instance of Passive', function () { + var validator = koValidation.passive(); + + expect(validator).to.be.instanceof(Passive); + }); + }); + describe('custom', function () { it('should be defined as a function', function () { expect(koValidation.custom).to.be.a('function'); From 5d7757a5fbedab0dd56520ac3697af8da16727d9 Mon Sep 17 00:00:00 2001 From: Remi Liu Date: Tue, 19 Apr 2016 14:39:08 +0800 Subject: [PATCH 14/16] comment out unused code in humanize/decimal --- js/humanize/decimal.js | 186 ++++++++++++++++++++--------------------- 1 file changed, 93 insertions(+), 93 deletions(-) diff --git a/js/humanize/decimal.js b/js/humanize/decimal.js index 04d9dfd..bd5e963 100644 --- a/js/humanize/decimal.js +++ b/js/humanize/decimal.js @@ -56,101 +56,101 @@ define(['component/ko-validation/config'], function (config) { return null; }, - // returns null if isValid == false - fromLocalToStringStrict: function (localizedInput, numberOfDigits) { - if (!dn.isValid(localizedInput)) - { - return null; - } - - return dn.fromLocalToString(localizedInput, numberOfDigits); - }, - - // used to validate & format user input - fromLocalToString: function fromLocalToString(localizedInput, numberOfDigits) { - var format = getFormat(), - parts = getParts(localizedInput, format.decimalChar), - numberOfDigits = typeof numberOfDigits === 'undefined' ? format.numberOfDigits : numberOfDigits, - cleanedAndRounded = parts.toFloat().toFixed(numberOfDigits).split('.'), - formattedIntegral = '', - i = cleanedAndRounded[0].length; - - for (; i > format.groupSize; i -= format.groupSize) { - formattedIntegral = format.groupChar + cleanedAndRounded[0].substring(i - format.groupSize, i) + formattedIntegral; - } - - formattedIntegral = cleanedAndRounded[0].substring(0, i) + formattedIntegral; - if (numberOfDigits) { - formattedIntegral += format.decimalChar + cleanedAndRounded[1]; - } - - return formattedIntegral; - }, - - // format input to string in percentage format - fromLocalToPercentString: function fromLocalToString(localizedInput, numberOfDigits) { - var input = localizedInput * 100; - var format = getFormat(), - parts = getParts(input, format.percentDecimalSeparator), - numberOfDigits = typeof numberOfDigits === 'undefined' ? format.percentDecimalDigits : numberOfDigits, - cleanedAndRounded = parts.toFloat().toFixed(numberOfDigits).split('.'), - formattedIntegral = '', - i = cleanedAndRounded[0].length; - - for (; i > format.percentGroupSize; i -= format.percentGroupSize) { - formattedIntegral = format.percentGroupSeparator + cleanedAndRounded[0].substring(i - format.percentGroupSize, i) + formattedIntegral; - } - - formattedIntegral = cleanedAndRounded[0].substring(0, i) + formattedIntegral; - if (numberOfDigits) { - formattedIntegral += format.percentDecimalSeparator + cleanedAndRounded[1]; - } - - switch (format.percentPositivePattern) { - //Associated pattern: n % - case '0': - formattedIntegral = formattedIntegral + ' ' + format.percentSymbol; - break; - //Associated pattern: n% - case '1': - formattedIntegral = formattedIntegral + format.percentSymbol; - break; - //Associated pattern: %n - case '2': - formattedIntegral = format.percentSymbol + formattedIntegral; - break; - //Associated pattern: % n - case '3': - formattedIntegral = format.percentSymbol + ' ' + formattedIntegral; - break; - } - - return formattedIntegral; - } + // // returns null if isValid == false + // fromLocalToStringStrict: function (localizedInput, numberOfDigits) { + // if (!dn.isValid(localizedInput)) + // { + // return null; + // } + + // return dn.fromLocalToString(localizedInput, numberOfDigits); + // }, + + // // used to validate & format user input + // fromLocalToString: function fromLocalToString(localizedInput, numberOfDigits) { + // var format = getFormat(), + // parts = getParts(localizedInput, format.decimalChar), + // numberOfDigits = typeof numberOfDigits === 'undefined' ? format.numberOfDigits : numberOfDigits, + // cleanedAndRounded = parts.toFloat().toFixed(numberOfDigits).split('.'), + // formattedIntegral = '', + // i = cleanedAndRounded[0].length; + + // for (; i > format.groupSize; i -= format.groupSize) { + // formattedIntegral = format.groupChar + cleanedAndRounded[0].substring(i - format.groupSize, i) + formattedIntegral; + // } + + // formattedIntegral = cleanedAndRounded[0].substring(0, i) + formattedIntegral; + // if (numberOfDigits) { + // formattedIntegral += format.decimalChar + cleanedAndRounded[1]; + // } + + // return formattedIntegral; + // }, + + // // format input to string in percentage format + // fromLocalToPercentString: function fromLocalToString(localizedInput, numberOfDigits) { + // var input = localizedInput * 100; + // var format = getFormat(), + // parts = getParts(input, format.percentDecimalSeparator), + // numberOfDigits = typeof numberOfDigits === 'undefined' ? format.percentDecimalDigits : numberOfDigits, + // cleanedAndRounded = parts.toFloat().toFixed(numberOfDigits).split('.'), + // formattedIntegral = '', + // i = cleanedAndRounded[0].length; + + // for (; i > format.percentGroupSize; i -= format.percentGroupSize) { + // formattedIntegral = format.percentGroupSeparator + cleanedAndRounded[0].substring(i - format.percentGroupSize, i) + formattedIntegral; + // } + + // formattedIntegral = cleanedAndRounded[0].substring(0, i) + formattedIntegral; + // if (numberOfDigits) { + // formattedIntegral += format.percentDecimalSeparator + cleanedAndRounded[1]; + // } + + // switch (format.percentPositivePattern) { + // //Associated pattern: n % + // case '0': + // formattedIntegral = formattedIntegral + ' ' + format.percentSymbol; + // break; + // //Associated pattern: n% + // case '1': + // formattedIntegral = formattedIntegral + format.percentSymbol; + // break; + // //Associated pattern: %n + // case '2': + // formattedIntegral = format.percentSymbol + formattedIntegral; + // break; + // //Associated pattern: % n + // case '3': + // formattedIntegral = format.percentSymbol + ' ' + formattedIntegral; + // break; + // } + + // return formattedIntegral; + // } }; - // used to format float values from MT/DB (e.g., always a real number XXX.YY with a period for decimal place - dn.fromFloatToString = function fromFloatToString(floatInput, numberOfDigits) { - var format = getFormat(); - return dn.fromLocalToString(floatInput.toString().replace('.', format.decimalChar), numberOfDigits); - }; - - // Used in Location Targeting control to convert Lat/Lon to string using customer culture information - dn.fromCoordinateToString = function fromCoordinateToString(value) { - if (value && (typeof decimalFormatter !== 'undefined')) { - value = value.toString().replace(".", decimalFormatter.decimalChar); - } - - return value; - }; - - // used to format float value to string with a percent sign. Does NOT convert the float to a percent. Ex: 42.07 => 42.07%, 0.15 => 0.15% - dn.fromFloatToPercentString = function fromFloatToPercentString(floatInput, numberOfDigits) { - var format = getFormat(); - //fromLocalToPercentString will multiply the number by 100 and displayed with a percent symbol. - var input = floatInput / 100; - return dn.fromLocalToPercentString(input.toString().replace('.', format.decimalChar), numberOfDigits); - }; + // // used to format float values from MT/DB (e.g., always a real number XXX.YY with a period for decimal place + // dn.fromFloatToString = function fromFloatToString(floatInput, numberOfDigits) { + // var format = getFormat(); + // return dn.fromLocalToString(floatInput.toString().replace('.', format.decimalChar), numberOfDigits); + // }; + + // // Used in Location Targeting control to convert Lat/Lon to string using customer culture information + // dn.fromCoordinateToString = function fromCoordinateToString(value) { + // if (value && (typeof decimalFormatter !== 'undefined')) { + // value = value.toString().replace(".", decimalFormatter.decimalChar); + // } + + // return value; + // }; + + // // used to format float value to string with a percent sign. Does NOT convert the float to a percent. Ex: 42.07 => 42.07%, 0.15 => 0.15% + // dn.fromFloatToPercentString = function fromFloatToPercentString(floatInput, numberOfDigits) { + // var format = getFormat(); + // //fromLocalToPercentString will multiply the number by 100 and displayed with a percent symbol. + // var input = floatInput / 100; + // return dn.fromLocalToPercentString(input.toString().replace('.', format.decimalChar), numberOfDigits); + // }; return dn; }); From 00d0fa25f4f71454cb252fbd06c671ec98dde188 Mon Sep 17 00:00:00 2001 From: Remi Liu Date: Tue, 19 Apr 2016 15:15:46 +0800 Subject: [PATCH 15/16] add tests for new code --- js/humanize/decimal.js | 16 ++++++++-------- js/validators/string.js | 2 +- spec/ko-extension.js | 35 +++++++++++++++++++++++++++++++++++ spec/validators/array.js | 10 +++++++++- spec/validators/number.js | 10 ++++++++++ spec/validators/string.js | 14 ++++++++++++++ 6 files changed, 77 insertions(+), 10 deletions(-) diff --git a/js/humanize/decimal.js b/js/humanize/decimal.js index bd5e963..adbd6e1 100644 --- a/js/humanize/decimal.js +++ b/js/humanize/decimal.js @@ -34,7 +34,7 @@ define(['component/ko-validation/config'], function (config) { // true/false if exactly formatted correct, no smoothing isValid: function isValid (localizedInput, allowNoIntegralPart) { var format = getFormat() - , minGroupSize = allowNoIntegralPart ? 0 : 1 + , minGroupSize = 0// allowNoIntegralPart ? 0 : 1 , re = new RegExp('^[+-]?[0-9]{' + minGroupSize + ',' + format.groupSize + '}(?:\\' + format.groupChar + '?[0-9]{' + format.groupSize + '})*(?:\\' + format.decimalChar + '[[0-9]*)?$') , isValid = re.test(localizedInput); @@ -43,17 +43,17 @@ define(['component/ko-validation/config'], function (config) { // returns null if invalid // rounds if number of digits is passed, otherwise no rounding - fromLocalToFloat: function fromLocalToFloat(localizedInput, numberOfDigits) { + fromLocalToFloat: function fromLocalToFloat(localizedInput /*, numberOfDigits */) { var format = getFormat(), parts = getParts(localizedInput, format.decimalChar); - if (parts.fractional || parts.integral) { + // if (parts.fractional || parts.integral) { var floatValue = parts.toFloat(); - if (numberOfDigits) { - floatValue = parseFloat(floatValue.toFixed(numberOfDigits)); - } + // if (numberOfDigits) { + // floatValue = parseFloat(floatValue.toFixed(numberOfDigits)); + // } return floatValue; - } - return null; + // } + // return null; }, // // returns null if isValid == false diff --git a/js/validators/string.js b/js/validators/string.js index 30902aa..80d3d53 100644 --- a/js/validators/string.js +++ b/js/validators/string.js @@ -15,7 +15,7 @@ define([ function Size(length, blockInput) { _.extend(this, new Base()); this.blockInput = _.isUndefined(blockInput) ? true : blockInput; - this.length = length || 0; + this.length = length; this.message = config.defaultMessage('Validation_NotEmpty_Required_Field', { maxlength: this.length }); } diff --git a/spec/ko-extension.js b/spec/ko-extension.js index afb7e7c..ba32cf1 100644 --- a/spec/ko-extension.js +++ b/spec/ko-extension.js @@ -63,6 +63,41 @@ describe('knockout extension', function () { expect(obNonblock.errors()).to.deep.equal([message]); }); + it('should cause no error when no validator passed in', function () { + expect(function () { + ko.observable().extend({ validate: null }); + }).to.not.throw(); + }); + + it('should validate value after process when not block input', function () { + var validator = new Custom(isValid, message, false); + validator.process = sinon.stub().returns('processed'); + + var ob = ko.observable().extend({ + validate: [validator] + }); + + ob('foo'); + + expect(isValid).to.be.calledOnce.and.calledWith('processed'); + expect(ob()).be.equal('processed'); + expect(ob.errors()[0]).to.be.equal(message); + }); + + it('should set error as return value of message function', function () { + var validator = new Custom(isValid, function (value) { + return value + ': ' + message; + }, false); + + var ob = ko.observable().extend({ + validate: [validator] + }); + + ob('foo'); + + expect(ob.errors()[0]).to.be.equal('foo: ' + message); + }); + describe('observable.validate', function () { it('should validate the value immediately', function () { var result = obNonblock.validate(); diff --git a/spec/validators/array.js b/spec/validators/array.js index 6d10e7a..56ccbb3 100644 --- a/spec/validators/array.js +++ b/spec/validators/array.js @@ -66,6 +66,9 @@ describe('array validators', function () { expect(validator.isValid(['foo', 'foo'])).to.be.true; expect(validator.isValid(['oof', 'bar'])).to.be.false; expect(validator.isValid(['oof', 'rab'])).to.be.false; + + expect(validator.message({})).to.be.equal('Validation_Base_Field_Not_Valid'); + expect(validator.message(['oof', 'bar'])).to.be.equal('Validation_Array_Items_Invalid'); }); }); @@ -83,8 +86,11 @@ describe('array validators', function () { expect(validator.isValid([])).to.be.true; expect(validator.isValid(['foo1'])).to.be.true; expect(validator.isValid(['foo1', 'bar2'])).to.be.true; - // expect(validator.isValid(['foo1', 'bar2', 'no validation'])).to.be.true; + expect(validator.isValid(['foo1', 'bar2', 'no validation'])).to.be.true; expect(validator.isValid(['foo2', 'bar1'])).to.be.false; + + expect(validator.message({})).to.be.equal('Validation_Base_Field_Not_Valid'); + expect(validator.message(['foo2', 'bar1'])).to.be.equal('Validation_Array_Items_Invalid'); }); it('should validate if additional items pass validation of additional validator', function () { @@ -107,6 +113,8 @@ describe('array validators', function () { expect(validator.isValid(['foo1', 'bar2'])).to.be.true; expect(validator.isValid(['foo1', 'bar2', 'no validation'])).to.be.false; + + expect(validator.message(['foo1', 'bar2', 'no validation'])).to.be.equal('Validation_Array_Size_Max'); }); }); diff --git a/spec/validators/number.js b/spec/validators/number.js index 2b8eb0a..38fdf3d 100644 --- a/spec/validators/number.js +++ b/spec/validators/number.js @@ -38,6 +38,11 @@ describe('number validators', function () { expect(num.Size).to.be.a('function'); }); + it('should leave empty string validation to required validator', function () { + var validator = new num.Size(3, 2); + expect(validator.isValid('')).to.be.true; + }); + it('should validate if a number has limited integer and decimal length', function () { var validator = new num.Size(3, 2); @@ -68,6 +73,11 @@ describe('number validators', function () { expect(num.Range).to.be.a('function'); }); + it('should leave empty string validation to required validator', function () { + var validator = new num.Range(1); + expect(validator.isValid('')).to.be.true; + }); + it('should validate if a number is no smaller than min', function () { var validator = new num.Range(1); diff --git a/spec/validators/string.js b/spec/validators/string.js index 1671877..f1f70b5 100644 --- a/spec/validators/string.js +++ b/spec/validators/string.js @@ -41,6 +41,20 @@ describe('str validators', function () { expect(validator.isValid('abcd')).to.be.false; expect(validator.isValid(123)).to.be.false; }); + + it('should provide second argument for block input or not', function () { + var validator = new str.Size(3, false); + + expect(validator.blockInput).to.be.false; + }); + + it('should provide a process function to cut the string', function () { + var validator = new str.Size(3); + + expect(validator.process).to.be.a('function'); + expect(validator.process('abcdefg')).to.be.equal('abc'); + expect(validator.process('ef')).to.be.equal('ef'); + }); }); describe('str.XSS', function () { From 774e488083334b9c94052c478a54c096cb900778 Mon Sep 17 00:00:00 2001 From: Remi Liu Date: Tue, 19 Apr 2016 15:24:05 +0800 Subject: [PATCH 16/16] force rerun --- js/humanize/decimal.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/humanize/decimal.js b/js/humanize/decimal.js index adbd6e1..f619faf 100644 --- a/js/humanize/decimal.js +++ b/js/humanize/decimal.js @@ -34,7 +34,7 @@ define(['component/ko-validation/config'], function (config) { // true/false if exactly formatted correct, no smoothing isValid: function isValid (localizedInput, allowNoIntegralPart) { var format = getFormat() - , minGroupSize = 0// allowNoIntegralPart ? 0 : 1 + , minGroupSize = 0 // allowNoIntegralPart ? 0 : 1 , re = new RegExp('^[+-]?[0-9]{' + minGroupSize + ',' + format.groupSize + '}(?:\\' + format.groupChar + '?[0-9]{' + format.groupSize + '})*(?:\\' + format.decimalChar + '[[0-9]*)?$') , isValid = re.test(localizedInput);