-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(validation): validateAge, validateDate, validateFileSize, valida…
…teFileType, validateMaxStringLength, validateMinStringLength, validateMaxNumber, validateMinNumber, validateRegExp, validateValueRequired, validateEmail, validatePostcode
- Loading branch information
Showing
4 changed files
with
387 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,240 @@ | ||
goog.module('clulib.validation'); | ||
|
||
const {objectToMap} = goog.require('clulib.collections'); | ||
|
||
/** | ||
* Map of postcodes by country. | ||
* | ||
* @type {Map<string, RegExp>} | ||
*/ | ||
const postcodes = objectToMap({ | ||
// Argentina | ||
'ar': /^[B-T]{1}\\d{4}[A-Z]{3}$/i, | ||
// Austria | ||
'at': /^[0-9]{4}$/i, | ||
// Australia | ||
'au': /^[2-9][0-9]{2,3}$/i, | ||
// Belgium | ||
'be': /^[1-9][0-9]{3}$/i, | ||
// Canada | ||
'ca': /^[a-z][0-9][a-z][ \t-]*[0-9][a-z][0-9]$/i, | ||
// Switzerland | ||
'ch': /^[0-9]{4}$/i, | ||
// China | ||
'cn': /^[0-9]{6}$/i, | ||
// Germany | ||
'de': /^[0-9]{5}$/i, | ||
// Denmark | ||
'dk': /^(DK-)?[0-9]{4}$/i, | ||
// Estonia | ||
'ee': /^[0-9]{5}$/i, | ||
// Spain | ||
'es': /^[0-4][0-9]{4}$/i, | ||
// Finland | ||
'fi': /^(FI-)?[0-9]{5}$/i, | ||
// France | ||
'fr': /^(0[1-9]|[1-9][0-9])[0-9][0-9][0-9]$/i, | ||
// Great Britain | ||
// eslint-disable-next-line max-len | ||
'gb': /^(([gG][iI][rR] {0,}0[aA]{2})|((([a-pr-uwyzA-PR-UWYZ][a-hk-yA-HK-Y]?[0-9][0-9]?)|(([a-pr-uwyzA-PR-UWYZ][0-9][a-hjkstuwA-HJKSTUW])|([a-pr-uwyzA-PR-UWYZ][a-hk-yA-HK-Y][0-9][abehmnprv-yABEHMNPRV-Y]))) {0,}[0-9][abd-hjlnp-uw-zABD-HJLNP-UW-Z]{2}))$/, | ||
// India | ||
'in': /^[1-9]{1}[0-9]{2}(\\s|-)?[0-9]{3}$/i, | ||
// Italy | ||
'it': /^[0-9]{5}$/i, | ||
// Iceland | ||
'is': /^[0-9]{3}$/i, | ||
// Latvia | ||
'lv': /^(LV-)?[1-9][0-9]{3}$/i, | ||
// Mexico | ||
'mx': /^[0-9]{5}$/i, | ||
// Netherlands | ||
'nl': /^[0-9]{4}.?[a-z]{2}$/i, | ||
// Norway | ||
'no': /^[0-9]{4}$/i, | ||
// New Zealand | ||
'nz': /^[0-9]{4}$/i, | ||
// Poland | ||
'pl': /^[0-9]{2}-[0-9]{3}$/i, | ||
// Portugal | ||
'pt': /^[0-9]{4}-[0-9]{3}$/i, | ||
// Romania | ||
'ro': /^[0-9]{6}$/i, | ||
// Russian Federation | ||
'ru': /^[0-9]{6}$/i, | ||
// Sweden | ||
'se': /^[0-9]{3}\s?[0-9]{2}$/i, | ||
// Turkey | ||
'tr': /^[0-9]{5}$/i, | ||
// United States | ||
'us': /^[0-9]{5}((-| )[0-9]{4})?$/i | ||
}); | ||
|
||
/** | ||
* Validates for a min age. | ||
* | ||
* @param {Date} birthDate The birthdate | ||
* @param {number} minAge Min. age in years | ||
* @returns {boolean} True if the date is older than minAge | ||
*/ | ||
function validateAge (birthDate, minAge) { | ||
const now = new Date(); | ||
const deadlineDate = new Date(now.getFullYear() - minAge, now.getMonth(), now.getDate()); | ||
|
||
return deadlineDate.getTime() >= birthDate.getTime(); | ||
} | ||
|
||
/** | ||
* Checks if a date is valid and exists. | ||
* | ||
* @param {number} year Full year | ||
* @param {number} month Month, 1 = january | ||
* @param {number} day Day | ||
* @returns {boolean} True if the date is valid | ||
*/ | ||
function validateDate (year, month, day) { | ||
month -= 1; | ||
|
||
const date = new Date(year, month, day); | ||
|
||
return date.getFullYear() === year | ||
&& date.getMonth() === month | ||
&& date.getDate() === day; | ||
} | ||
|
||
/** | ||
* Checks if a File does not exceed a maximum size. | ||
* | ||
* @param {Blob} blob File object | ||
* @param {number} maxSize Max size in Megabytes | ||
* @returns {boolean} True if the file is not too large | ||
*/ | ||
function validateFileSize (blob, maxSize) { | ||
const bytes = maxSize * 1000000; | ||
|
||
return blob.size <= bytes; | ||
} | ||
|
||
/** | ||
* Checks if a File is of certain mime types. | ||
* | ||
* @param {Blob} blob File object | ||
* @param {Array<string>} mimeTypes allowed mime types | ||
* @returns {boolean} True if the File has one of the mime types | ||
*/ | ||
function validateFileType (blob, mimeTypes) { | ||
return mimeTypes.reduce((old, currentMimeType) => { | ||
return blob.type === currentMimeType || old; | ||
}, false); | ||
} | ||
|
||
/** | ||
* Validates a string for max length, does not count whitespace. | ||
* | ||
* @param {string} string | ||
* @param {number} maxLength | ||
* @returns {boolean} | ||
*/ | ||
function validateMaxStringLength (string, maxLength) { | ||
string = string.trim(); | ||
return validateMaxNumber(string.length, maxLength); | ||
} | ||
|
||
/** | ||
* Validates a string for min length, does not count whitespace. | ||
* | ||
* @param {string} string | ||
* @param {number} minLength | ||
* @returns {boolean} | ||
*/ | ||
function validateMinStringLength (string, minLength) { | ||
string = string.trim(); | ||
return validateMinNumber(string.length, minLength); | ||
} | ||
|
||
/** | ||
* Validates a number for max value. | ||
* | ||
* @param {number} number | ||
* @param {number} maxValue | ||
* @returns {boolean} | ||
*/ | ||
function validateMaxNumber (number, maxValue) { | ||
return number <= maxValue; | ||
} | ||
|
||
/** | ||
* Validates a number for min value. | ||
* | ||
* @param {number} number | ||
* @param {number} minValue | ||
* @returns {boolean} | ||
*/ | ||
function validateMinNumber (number, minValue) { | ||
return number >= minValue; | ||
} | ||
|
||
/** | ||
* Validates a string with a regular expression. | ||
* | ||
* @param {string} string | ||
* @param {RegExp} regExp | ||
* @returns {boolean} | ||
*/ | ||
function validateRegExp (string, regExp) { | ||
return regExp.test(string); | ||
} | ||
|
||
/** | ||
* Validates a value that is required (neither null or undefined, or not empty if it's a string). | ||
* | ||
* @param {?} value | ||
* @returns {boolean} | ||
*/ | ||
function validateValueRequired (value) { | ||
if (goog.isString(value)) | ||
return validateMinStringLength(value, 1); | ||
else | ||
return value != null; | ||
} | ||
|
||
/** | ||
* Validates if an email adress is valid. | ||
* | ||
* @param {string} string | ||
* @returns {boolean} | ||
*/ | ||
function validateEmail (string) { | ||
// eslint-disable-next-line no-useless-escape,max-len | ||
return validateRegExp(string, /^[a-z0-9!#$%&\'*+\/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&\'*+\/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i); | ||
} | ||
|
||
/** | ||
* Validates a postcode by country array (see {@link postcodes} for values). | ||
* | ||
* @param {string} string | ||
* @param {Array<string>} countries An array of country codes. | ||
* @returns {boolean} | ||
*/ | ||
function validatePostcode (string, countries) { | ||
return countries.reduce((old, country) => { | ||
if (!postcodes.has(country)) | ||
throw new Error(`Postcode for country '${country}' is not defined.`); | ||
const postcodeRegExp = postcodes.get(country); | ||
return old || validateRegExp(string, postcodeRegExp); | ||
}, false); | ||
} | ||
|
||
exports = { | ||
validateAge, | ||
validateDate, | ||
validateFileSize, | ||
validateFileType, | ||
validateMaxStringLength, | ||
validateMinStringLength, | ||
validateMaxNumber, | ||
validateMinNumber, | ||
validateRegExp, | ||
validateValueRequired, | ||
validateEmail, | ||
validatePostcode | ||
}; |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
goog.module('test.clulib.validation'); | ||
|
||
const ResponseType = goog.require('goog.net.XhrIo.ResponseType'); | ||
|
||
const validation = goog.require('clulib.validation'); | ||
const {basePath} = goog.require('testing.environment'); | ||
const http = goog.require('clulib.net.http_request'); | ||
|
||
/** | ||
* @returns {Promise<Blob>} | ||
*/ | ||
async function getBlob () { | ||
const request = http.httpRequest(dummyJsonUrl, 'GET', null, ResponseType.BLOB); | ||
const result = await request.promise; | ||
|
||
return result.response; | ||
} | ||
|
||
const dummyJsonUrl = `${basePath}/test-assets/json/dummy.json`; | ||
|
||
exports = function () { | ||
describe('clulib.validation', () => { | ||
describe('validateAge', () => { | ||
it('should validate a min birthdate', () => { | ||
jasmine.clock().install(); | ||
|
||
// mock 2020-01-02 | ||
jasmine.clock().mockDate(new Date(2020, 0, 2)); | ||
expect(validation.validateAge(new Date(2002, 0, 3), 18)).toBe(false); | ||
expect(validation.validateAge(new Date(2002, 0, 2), 18)).toBe(true); | ||
expect(validation.validateAge(new Date(2002, 0, 1), 18)).toBe(true); | ||
|
||
// check for people born on leap year (29. February) | ||
// mock 2034-02-28 | ||
jasmine.clock().mockDate(new Date(2034, 1, 28)); | ||
expect(validation.validateAge(new Date(2016, 1, 29), 18)).toBe(false); | ||
|
||
jasmine.clock().mockDate(new Date(2034, 2, 1)); | ||
expect(validation.validateAge(new Date(2016, 1, 29), 18)).toBe(true); | ||
|
||
jasmine.clock().uninstall(); | ||
}); | ||
}); | ||
|
||
describe('validateDate', () => { | ||
it('should validate a date', () => { | ||
expect(validation.validateDate(2000, 1, 1)).toBe(true); | ||
// leap year | ||
expect(validation.validateDate(2016, 2, 29)).toBe(true); | ||
|
||
expect(validation.validateDate(2015, 2, 29)).toBe(false); | ||
expect(validation.validateDate(2015, 0, 10)).toBe(false); | ||
expect(validation.validateDate(2015, 20, 20)).toBe(false); | ||
}); | ||
}); | ||
|
||
describe('validateFileSize', () => { | ||
it('should validate for file size', async () => { | ||
const blob = await getBlob(); | ||
|
||
expect(validation.validateFileSize(blob, 100 / 1000000)).toBe(true); | ||
expect(validation.validateFileSize(blob, 20 / 1000000)).toBe(false); | ||
}); | ||
}); | ||
|
||
describe('validateFileType', () => { | ||
it('should validate for file type', async () => { | ||
const blob = await getBlob(); | ||
|
||
expect(validation.validateFileType(blob, ['application/json'])).toBe(true); | ||
expect(validation.validateFileType(blob, ['application/javascript'])).toBe(false); | ||
}); | ||
}); | ||
|
||
describe('validateMaxStringLength', () => { | ||
it('should validate a string for max length', () => { | ||
expect(validation.validateMaxStringLength('ab', 3)).toBe(true); | ||
expect(validation.validateMaxStringLength('abc', 3)).toBe(true); | ||
expect(validation.validateMaxStringLength('abcd', 3)).toBe(false); | ||
}); | ||
}); | ||
|
||
describe('validateMinStringLength', () => { | ||
it('should validate a string for min length', () => { | ||
expect(validation.validateMinStringLength('ab', 3)).toBe(false); | ||
expect(validation.validateMinStringLength('abc', 3)).toBe(true); | ||
expect(validation.validateMinStringLength('abcd', 3)).toBe(true); | ||
}); | ||
}); | ||
|
||
describe('validateMaxNumber', () => { | ||
it('should validate a number for max value', () => { | ||
expect(validation.validateMaxNumber(1, 2)).toBe(true); | ||
expect(validation.validateMaxNumber(2, 2)).toBe(true); | ||
expect(validation.validateMaxNumber(3, 2)).toBe(false); | ||
}); | ||
}); | ||
|
||
describe('validateMinNumber', () => { | ||
it('should validate a number for min value', () => { | ||
expect(validation.validateMinNumber(1, 2)).toBe(false); | ||
expect(validation.validateMinNumber(2, 2)).toBe(true); | ||
expect(validation.validateMinNumber(3, 2)).toBe(true); | ||
}); | ||
}); | ||
|
||
describe('validateRegExp', () => { | ||
it('should validate a regular expression', () => { | ||
expect(validation.validateRegExp('aa', new RegExp('[a-z]'))).toBe(true); | ||
expect(validation.validateRegExp('AA', new RegExp('[a-z]'))).toBe(false); | ||
|
||
expect(validation.validateRegExp('AA', new RegExp('[a-z]', 'i'))).toBe(true); | ||
|
||
expect(validation.validateRegExp('AA', /[a-z]/i)).toBe(true); | ||
}); | ||
}); | ||
|
||
describe('validateValueRequired', () => { | ||
it('should validate for a required value', () => { | ||
expect(validation.validateValueRequired(null)).toBe(false); | ||
expect(validation.validateValueRequired(undefined)).toBe(false); | ||
expect(validation.validateValueRequired('')).toBe(false); | ||
expect(validation.validateValueRequired('abc')).toBe(true); | ||
expect(validation.validateValueRequired(10)).toBe(true); | ||
}); | ||
}); | ||
|
||
describe('validateEmail', () => { | ||
it('should validate for a valid email adress', () => { | ||
expect(validation.validateEmail('')).toBe(false); | ||
expect(validation.validateEmail('afda@@adfad.com')).toBe(false); | ||
expect(validation.validateEmail('email@example.com')).toBe(true); | ||
}); | ||
}); | ||
|
||
describe('validatePostcode', () => { | ||
it('should validate for a valid postcode', () => { | ||
expect(validation.validatePostcode('000-00', ['de'])).toBe(false); | ||
expect(validation.validatePostcode('12345', ['de'])).toBe(true); | ||
expect(validation.validatePostcode('12345', ['de', 'gb'])).toBe(true); | ||
expect(validation.validatePostcode('AA00 0AA', ['de', 'gb'])).toBe(true); | ||
}); | ||
}); | ||
}); | ||
}; |