diff --git a/README.md b/README.md index 41159bf9e..03a5fbf5f 100644 --- a/README.md +++ b/README.md @@ -139,7 +139,7 @@ Validator | Description **isJSON(str [, options])** | check if the string is valid JSON (note: uses JSON.parse).

`options` is an object which defaults to `{ allow_primitives: false }`. If `allow_primitives` is true, the primitives 'true', 'false' and 'null' are accepted as valid JSON values. **isJWT(str)** | check if the string is valid JWT token. **isLatLong(str [, options])** | check if the string is a valid latitude-longitude coordinate in the format `lat,long` or `lat, long`.

`options` is an object that defaults to `{ checkDMS: false }`. Pass `checkDMS` as `true` to validate DMS(degrees, minutes, and seconds) latitude-longitude format. -**isLength(str [, options])** | check if the string's length falls in a range.

`options` is an object which defaults to `{ min: 0, max: undefined }`. Note: this function takes into account surrogate pairs. +**isLength(str [, options])** | check if the string's length falls in a range and equal to any of the integers of the `discreteLengths` array if provided.

`options` is an object which defaults to `{ min: 0, max: undefined, discreteLengths: undefined }`. Note: this function takes into account surrogate pairs. **isLicensePlate(str, locale)** | check if the string matches the format of a country's license plate.

`locale` is one of `['cs-CZ', 'de-DE', 'de-LI', 'en-IN', 'en-SG', 'en-PK', 'es-AR', 'hu-HU', 'pt-BR', 'pt-PT', 'sq-AL', 'sv-SE']` or `'any'`. **isLocale(str)** | check if the string is a locale. **isLowercase(str)** | check if the string is lowercase. diff --git a/src/lib/isDate.js b/src/lib/isDate.js index ede3e33e6..3a1e4afd2 100644 --- a/src/lib/isDate.js +++ b/src/lib/isDate.js @@ -28,6 +28,7 @@ export default function isDate(input, options) { options = merge(options, default_date_options); } if (typeof input === 'string' && isValidFormat(options.format)) { + if (options.strictMode && input.length !== options.format.length) return false; const formatDelimiter = options.delimiters .find(delimiter => options.format.indexOf(delimiter) !== -1); const dateDelimiter = options.strictMode diff --git a/src/lib/isISO6346.js b/src/lib/isISO6346.js index 0cb657e7c..2c28c1123 100644 --- a/src/lib/isISO6346.js +++ b/src/lib/isISO6346.js @@ -27,7 +27,8 @@ export function isISO6346(str) { } else sum += str[i] * (2 ** i); } - const checkSumDigit = sum % 11; + let checkSumDigit = sum % 11; + if (checkSumDigit === 10) checkSumDigit = 0; return Number(str[str.length - 1]) === checkSumDigit; } diff --git a/src/lib/isLength.js b/src/lib/isLength.js index 4ef8b83eb..4d5d52546 100644 --- a/src/lib/isLength.js +++ b/src/lib/isLength.js @@ -5,6 +5,7 @@ export default function isLength(str, options) { assertString(str); let min; let max; + if (typeof (options) === 'object') { min = options.min || 0; max = options.max; @@ -12,8 +13,15 @@ export default function isLength(str, options) { min = arguments[1] || 0; max = arguments[2]; } + const presentationSequences = str.match(/(\uFE0F|\uFE0E)/g) || []; const surrogatePairs = str.match(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g) || []; const len = str.length - presentationSequences.length - surrogatePairs.length; - return len >= min && (typeof max === 'undefined' || len <= max); + const isInsideRange = len >= min && (typeof max === 'undefined' || len <= max); + + if (isInsideRange && Array.isArray(options?.discreteLengths)) { + return options.discreteLengths.some(discreteLen => discreteLen === len); + } + + return isInsideRange; } diff --git a/src/lib/isMobilePhone.js b/src/lib/isMobilePhone.js index 21d840fb5..ad5ff1c93 100644 --- a/src/lib/isMobilePhone.js +++ b/src/lib/isMobilePhone.js @@ -41,7 +41,7 @@ const phones = { 'en-BS': /^(\+?1[-\s]?|0)?\(?242\)?[-\s]?\d{3}[-\s]?\d{4}$/, 'en-GB': /^(\+?44|0)7[1-9]\d{8}$/, 'en-GG': /^(\+?44|0)1481\d{6}$/, - 'en-GH': /^(\+233|0)(20|50|24|54|27|57|26|56|23|28|55|59)\d{7}$/, + 'en-GH': /^(\+233|0)(20|50|24|54|27|57|26|56|23|53|28|55|59)\d{7}$/, 'en-GY': /^(\+592|0)6\d{6}$/, 'en-HK': /^(\+?852[-\s]?)?[456789]\d{3}[-\s]?\d{4}$/, 'en-MO': /^(\+?853[-\s]?)?[6]\d{3}[-\s]?\d{4}$/, @@ -70,7 +70,7 @@ const phones = { 'en-UG': /^(\+?256|0)?[7]\d{8}$/, 'en-US': /^((\+1|1)?( |-)?)?(\([2-9][0-9]{2}\)|[2-9][0-9]{2})( |-)?([2-9][0-9]{2}( |-)?[0-9]{4})$/, 'en-ZA': /^(\+?27|0)\d{9}$/, - 'en-ZM': /^(\+?26)?09[567]\d{7}$/, + 'en-ZM': /^(\+?26)?0[79][567]\d{7}$/, 'en-ZW': /^(\+263)[0-9]{9}$/, 'en-BW': /^(\+?267)?(7[1-8]{1})\d{6}$/, 'es-AR': /^\+?549(11|[2368]\d)\d{8}$/, diff --git a/src/lib/isPostalCode.js b/src/lib/isPostalCode.js index 103656205..cadf39346 100644 --- a/src/lib/isPostalCode.js +++ b/src/lib/isPostalCode.js @@ -14,7 +14,7 @@ const patterns = { BA: /^([7-8]\d{4}$)/, BE: fourDigit, BG: fourDigit, - BR: /^\d{5}-\d{3}$/, + BR: /^\d{5}-?\d{3}$/, BY: /^2[1-4]\d{4}$/, CA: /^[ABCEGHJKLMNPRSTVXY]\d[ABCEGHJ-NPRSTV-Z][\s\-]?\d[ABCEGHJ-NPRSTV-Z]\d$/i, CH: fourDigit, diff --git a/test/validators.test.js b/test/validators.test.js index a8611a094..490adfafe 100644 --- a/test/validators.test.js +++ b/test/validators.test.js @@ -5394,12 +5394,42 @@ describe('Validators', () => { valid: ['abc', 'de', 'a', ''], invalid: ['abcd'], }); + test({ + validator: 'isLength', + args: [{ max: 6, discreteLengths: 5 }], + valid: ['abcd', 'vfd', 'ff', '', 'k'], + invalid: ['abcdefgh', 'hfjdksks'], + }); + test({ + validator: 'isLength', + args: [{ min: 2, max: 6, discreteLengths: 5 }], + valid: ['bsa', 'vfvd', 'ff'], + invalid: ['', ' ', 'hfskdunvc'], + }); + test({ + validator: 'isLength', + args: [{ min: 1, discreteLengths: 2 }], + valid: [' ', 'hello', 'bsa'], + invalid: [''], + }); test({ validator: 'isLength', args: [{ max: 0 }], valid: [''], invalid: ['a', 'ab'], }); + test({ + validator: 'isLength', + args: [{ min: 5, max: 10, discreteLengths: [2, 6, 8, 9] }], + valid: ['helloguy', 'shopping', 'validator', 'length'], + invalid: ['abcde', 'abcdefg'], + }); + test({ + validator: 'isLength', + args: [{ discreteLengths: '9' }], + valid: ['a', 'abcd', 'abcdefghijkl'], + invalid: [], + }); test({ validator: 'isLength', valid: ['a', '', 'asds'], @@ -8023,6 +8053,7 @@ describe('Validators', () => { '0502345671', '0242345671', '0542345671', + '0532345671', '0272345671', '0572345671', '0262345671', @@ -8033,6 +8064,7 @@ describe('Validators', () => { '+233502345671', '+233242345671', '+233542345671', + '+233532345671', '+233272345671', '+233572345671', '+233262345671', @@ -8764,6 +8796,8 @@ describe('Validators', () => { '+260966684590', '+260976684590', '260976684590', + '+260779493521', + '+260760010936', ], invalid: [ '12345', @@ -8771,6 +8805,7 @@ describe('Validators', () => { 'Vml2YW11cyBmZXJtZtesting123', '010-38238383', '966684590', + '760010936', ], }, { @@ -12724,6 +12759,9 @@ describe('Validators', () => { '39100-000', '22040-020', '39400-152', + '39100000', + '22040020', + '39400152', ], invalid: [ '79800A12', @@ -13050,6 +13088,55 @@ describe('Validators', () => { }); }); + it('should validate ISO6346 shipping container IDs with checksum digit 10 represented as 0', () => { + test({ + validator: 'isISO6346', + valid: [ + 'APZU3789870', + 'TEMU1002030', + 'DFSU1704420', + 'CMAU2221480', + 'SEGU5060260', + 'FCIU8939320', + 'TRHU3495670', + 'MEDU3871410', + 'CMAU2184010', + 'TCLU2265970', + ], + invalid: [ + 'APZU3789871', // Incorrect check digit + 'TEMU1002031', + 'DFSU1704421', + 'CMAU2221481', + 'SEGU5060261', + ], + }); + }); + it('should validate ISO6346 shipping container IDs with checksum digit 10 represented as 0', () => { + test({ + validator: 'isFreightContainerID', + valid: [ + 'APZU3789870', + 'TEMU1002030', + 'DFSU1704420', + 'CMAU2221480', + 'SEGU5060260', + 'FCIU8939320', + 'TRHU3495670', + 'MEDU3871410', + 'CMAU2184010', + 'TCLU2265970', + ], + invalid: [ + 'APZU3789871', // Incorrect check digit + 'TEMU1002031', + 'DFSU1704421', + 'CMAU2221481', + 'SEGU5060261', + ], + }); + }); + // EU-UK valid numbers sourced from https://ec.europa.eu/taxation_customs/tin/specs/FS-TIN%20Algorithms-Public.docx or constructed by @tplessas. it('should validate taxID', () => { test({ @@ -13985,6 +14072,7 @@ describe('Validators', () => { new Date([2014, 2, 15]), new Date('2014-03-15'), '29.02.2020', + '02.29.2020.20', '2024-', '2024-05', '2024-05-',