Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
5578dad
refactor: allow for splitting tests to different files
WikiRik Oct 18, 2022
afda0bd
feat(isAfter): allow usage of options object
WikiRik Oct 18, 2022
cad192e
style: make options italic
WikiRik Oct 18, 2022
5ba1a43
refactor: rename test file extension to .test.js
WikiRik Oct 19, 2022
71102d4
refactor: rename test-functions to testFunctions
WikiRik Oct 19, 2022
f32d228
refactor: implement suggestion from #2019 review
WikiRik Oct 23, 2022
7799d9b
refactor: remove custom repeat to use native function
WikiRik Oct 23, 2022
5375f17
refactor: implement suggestion new Date
WikiRik Oct 23, 2022
17370bd
Refactor isAlpha with options API
pixelbucket-dev Oct 25, 2022
2c33aa7
Refactor isAlpha tests
pixelbucket-dev Oct 25, 2022
4bf7277
Improve readability of test helper
pixelbucket-dev Oct 25, 2022
8559785
Fix name bug
pixelbucket-dev Oct 25, 2022
1f65dac
Format code
pixelbucket-dev Oct 25, 2022
74a024e
Fix typo
pixelbucket-dev Oct 25, 2022
3fe6d24
Improve comment
pixelbucket-dev Oct 25, 2022
a596cdb
Improve Error message
pixelbucket-dev Oct 25, 2022
bb77b71
Update Readme
pixelbucket-dev Oct 25, 2022
d3ad86c
Rename function
pixelbucket-dev Oct 25, 2022
e0fb4b2
Improve resilience with missing "locale" option
pixelbucket-dev Oct 25, 2022
47877de
Fix test description
pixelbucket-dev Oct 25, 2022
1ee8afc
Improve README
pixelbucket-dev Oct 25, 2022
b55cab6
Reintroduce legacy args for backwards-compat
pixelbucket-dev Oct 26, 2022
8949343
Remove commented code
pixelbucket-dev Oct 26, 2022
dab3ee1
Make helper reusable
pixelbucket-dev Oct 25, 2022
1ce0ccb
Refactor isAlphanumeric with options API
pixelbucket-dev Oct 25, 2022
e5fc2b6
Refactor isAlphanumeric tests
pixelbucket-dev Oct 25, 2022
04f1eae
Refactor to use UPPER_SNAKE_CASE for constants
pixelbucket-dev Oct 25, 2022
9c2166e
Remove unnecessary export statements
pixelbucket-dev Oct 25, 2022
00ec566
Refactor for better code reuse
pixelbucket-dev Oct 25, 2022
15830a7
Harden tests against with missing "locale" option
pixelbucket-dev Oct 25, 2022
90d1465
Fix README
pixelbucket-dev Oct 25, 2022
7eefdeb
Reintroduce legacy args for backwards-compat
pixelbucket-dev Oct 26, 2022
cfddf79
Improve comment
pixelbucket-dev Oct 26, 2022
fd02a94
Revert "Refactor to use UPPER_SNAKE_CASE for constants"
pixelbucket-dev Oct 26, 2022
ed8ce77
Simplify
pixelbucket-dev Oct 26, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,9 @@ Validator | Description
--------------------------------------- | --------------------------------------
**contains(str, seed [, options ])** | check if the string contains the seed.<br/><br/>`options` is an object that defaults to `{ ignoreCase: false, minOccurrences: 1 }`.<br />Options: <br/> `ignoreCase`: Ignore case when doing comparison, default false<br/>`minOccurences`: Minimum number of occurrences for the seed in the string. Defaults to 1.
**equals(str, comparison)** | check if the string matches the comparison.
**isAfter(str [, date])** | check if the string is a date that's after the specified date (defaults to now).
**isAlpha(str [, locale, options])** | check if the string contains only letters (a-zA-Z).<br/><br/>Locale is one of `['ar', 'ar-AE', 'ar-BH', 'ar-DZ', 'ar-EG', 'ar-IQ', 'ar-JO', 'ar-KW', 'ar-LB', 'ar-LY', 'ar-MA', 'ar-QA', 'ar-QM', 'ar-SA', 'ar-SD', 'ar-SY', 'ar-TN', 'ar-YE', 'bg-BG', 'bn', 'cs-CZ', 'da-DK', 'de-DE', 'el-GR', 'en-AU', 'en-GB', 'en-HK', 'en-IN', 'en-NZ', 'en-US', 'en-ZA', 'en-ZM', 'es-ES', 'fa-IR', 'fi-FI', 'fr-CA', 'fr-FR', 'he', 'hi-IN', 'hu-HU', 'it-IT', 'ko-KR', 'ja-JP', 'ku-IQ', 'nb-NO', 'nl-NL', 'nn-NO', 'pl-PL', 'pt-BR', 'pt-PT', 'ru-RU', 'si-LK', 'sl-SI', 'sk-SK', 'sr-RS', 'sr-RS@latin', 'sv-SE', 'tr-TR', 'uk-UA']`) and defaults to `en-US`. Locale list is `validator.isAlphaLocales`. options is an optional object that can be supplied with the following key(s): ignore which can either be a String or RegExp of characters to be ignored e.g. " -" will ignore spaces and -'s.
**isAlphanumeric(str [, locale, options])** | check if the string contains only letters and numbers (a-zA-Z0-9).<br/><br/>Locale is one of `['ar', 'ar-AE', 'ar-BH', 'ar-DZ', 'ar-EG', 'ar-IQ', 'ar-JO', 'ar-KW', 'ar-LB', 'ar-LY', 'ar-MA', 'ar-QA', 'ar-QM', 'ar-SA', 'ar-SD', 'ar-SY', 'ar-TN', 'ar-YE', 'bn', 'bg-BG', 'cs-CZ', 'da-DK', 'de-DE', 'el-GR', 'en-AU', 'en-GB', 'en-HK', 'en-IN', 'en-NZ', 'en-US', 'en-ZA', 'en-ZM', 'es-ES', 'fa-IR', 'fi-FI', 'fr-CA', 'fr-FR', 'he', 'hi-IN', 'hu-HU', 'it-IT', 'ko-KR', 'ja-JP','ku-IQ', 'nb-NO', 'nl-NL', 'nn-NO', 'pl-PL', 'pt-BR', 'pt-PT', 'ru-RU', 'si-LK', 'sl-SI', 'sk-SK', 'sr-RS', 'sr-RS@latin', 'sv-SE', 'tr-TR', 'uk-UA']`) and defaults to `en-US`. Locale list is `validator.isAlphanumericLocales`. options is an optional object that can be supplied with the following key(s): ignore which can either be a String or RegExp of characters to be ignored e.g. " -" will ignore spaces and -'s.
**isAfter(str [, options])** | check if the string is a date that's after the specified date.<br/><br/>`options` is an object that defaults to `{ date: new Date() }`.<br/>_Options:_<br/>`date`: Date to compare to. Defaults to `new Date()` (now).
**isAlpha(str [, options])** | check if the string contains only letters (a-zA-Z).<br/><br/>`options` is an object that defaults to `{ locale: 'en-US' }`.<br/><br/>**Options:**<br/>`locale`: The supported locale is one of `['ar', 'ar-AE', 'ar-BH', 'ar-DZ', 'ar-EG', 'ar-IQ', 'ar-JO', 'ar-KW', 'ar-LB', 'ar-LY', 'ar-MA', 'ar-QA', 'ar-QM', 'ar-SA', 'ar-SD', 'ar-SY', 'ar-TN', 'ar-YE', 'bg-BG', 'bn', 'cs-CZ', 'da-DK', 'de-DE', 'el-GR', 'en-AU', 'en-GB', 'en-HK', 'en-IN', 'en-NZ', 'en-US', 'en-ZA', 'en-ZM', 'es-ES', 'fa-IR', 'fi-FI', 'fr-CA', 'fr-FR', 'he', 'hi-IN', 'hu-HU', 'it-IT', 'ko-KR', 'ja-JP', 'ku-IQ', 'nb-NO', 'nl-NL', 'nn-NO', 'pl-PL', 'pt-BR', 'pt-PT', 'ru-RU', 'si-LK', 'sl-SI', 'sk-SK', 'sr-RS', 'sr-RS@latin', 'sv-SE', 'tr-TR', 'uk-UA']`) and defaults to `en-US`. The locale list is `validator.isAlphaLocales`.<br/><br/>`ignore`: (optional) A string or RegExp containing characters to ignore, e.g. `'- /' ` or `/[\s/-]/g` to ignore spaces and "-".
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we make a separate table for later in this page where we show the different locales? And just refer to that here. Same holds for isAlphanumeric ofc

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, good idea. It's pretty hard to read and redundant as well. Should be a single source of truth, so to speak :D. So, on a second thought, we could just remove the listings here and keep only the reference to validator.isAlphaLocales and validator.isAlphanumericLocales. I am not sure, however, if this reduces transparency. One has to navigated manually though the code base to find the original definition.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would make a new table for isAlphaLocales, isAlphanumericLocales etc in the README and link to there. So in this table you only reference to that list in the README and mention the default.
People don't have to go through the code that way and that makes this easier to read

**isAlphanumeric(str [, options])** | check if the string contains only letters and numbers (a-zA-Z0-9).<br/><br/>`options` is an object that defaults to `{ locale: 'en-US' }`.<br/><br/>**Options:**<br/>`locale`: The supported locale is one of `['ar', 'ar-AE', 'ar-BH', 'ar-DZ', 'ar-EG', 'ar-IQ', 'ar-JO', 'ar-KW', 'ar-LB', 'ar-LY', 'ar-MA', 'ar-QA', 'ar-QM', 'ar-SA', 'ar-SD', 'ar-SY', 'ar-TN', 'ar-YE', 'bg-BG', 'bn', 'cs-CZ', 'da-DK', 'de-DE', 'el-GR', 'en-AU', 'en-GB', 'en-HK', 'en-IN', 'en-NZ', 'en-US', 'en-ZA', 'en-ZM', 'es-ES', 'fa-IR', 'fi-FI', 'fr-CA', 'fr-FR', 'he', 'hi-IN', 'hu-HU', 'it-IT', 'ko-KR', 'ja-JP', 'ku-IQ', 'nb-NO', 'nl-NL', 'nn-NO', 'pl-PL', 'pt-BR', 'pt-PT', 'ru-RU', 'si-LK', 'sl-SI', 'sk-SK', 'sr-RS', 'sr-RS@latin', 'sv-SE', 'tr-TR', 'uk-UA']`) and defaults to `en-US`. The locale list is `validator.isAlphanumericLocales`.<br/><br/>`ignore`: (optional) A string or RegExp containing characters to ignore, e.g. `'- /' ` or `/[\s/-]/g` to ignore spaces and "-".
**isAscii(str)** | check if the string contains ASCII chars only.
**isBase32(str [, options])** | check if a string is base32 encoded. `options` is optional and defaults to `{crockford: false}`.<br/> When `crockford` is true it tests the given base32 encoded string using [Crockford's base32 alternative](http://www.crockford.com/base32.html).
**isBase58(str)** | check if a string is base58 encoded.
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@
"build:node": "babel src -d .",
"build": "run-p build:*",
"pretest": "npm run build && npm run lint",
"test": "nyc --reporter=cobertura --reporter=text-summary mocha --require @babel/register --reporter dot"
"test": "nyc --reporter=cobertura --reporter=text-summary mocha --require @babel/register --reporter dot --recursive"
},
"engines": {
"node": ">= 0.10"
Expand Down
60 changes: 54 additions & 6 deletions src/lib/alpha.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import assertString from './util/assertString';

export const alpha = {
'en-US': /^[A-Z]+$/i,
'az-AZ': /^[A-VXYZÇƏĞİıÖŞÜ]+$/i,
Expand Down Expand Up @@ -83,7 +85,7 @@ export const decimal = {
};


export const englishLocales = ['AU', 'GB', 'HK', 'IN', 'NZ', 'ZA', 'ZM'];
const englishLocales = ['AU', 'GB', 'HK', 'IN', 'NZ', 'ZA', 'ZM'];

for (let locale, i = 0; i < englishLocales.length; i++) {
locale = `en-${englishLocales[i]}`;
Expand All @@ -93,7 +95,7 @@ for (let locale, i = 0; i < englishLocales.length; i++) {
}

// Source: http://www.localeplanet.com/java/
export const arabicLocales = ['AE', 'BH', 'DZ', 'EG', 'IQ', 'JO', 'KW', 'LB', 'LY',
const arabicLocales = ['AE', 'BH', 'DZ', 'EG', 'IQ', 'JO', 'KW', 'LB', 'LY',
'MA', 'QM', 'QA', 'SA', 'SD', 'SY', 'TN', 'YE'];

for (let locale, i = 0; i < arabicLocales.length; i++) {
Expand All @@ -103,15 +105,15 @@ for (let locale, i = 0; i < arabicLocales.length; i++) {
decimal[locale] = decimal.ar;
}

export const farsiLocales = ['IR', 'AF'];
const farsiLocales = ['IR', 'AF'];

for (let locale, i = 0; i < farsiLocales.length; i++) {
locale = `fa-${farsiLocales[i]}`;
alphanumeric[locale] = alphanumeric.fa;
decimal[locale] = decimal.ar;
}

export const bengaliLocales = ['BD', 'IN'];
const bengaliLocales = ['BD', 'IN'];

for (let locale, i = 0; i < bengaliLocales.length; i++) {
locale = `bn-${bengaliLocales[i]}`;
Expand All @@ -121,8 +123,8 @@ for (let locale, i = 0; i < bengaliLocales.length; i++) {
}

// Source: https://en.wikipedia.org/wiki/Decimal_mark
export const dotDecimal = ['ar-EG', 'ar-LB', 'ar-LY'];
export const commaDecimal = [
const dotDecimal = ['ar-EG', 'ar-LB', 'ar-LY'];
const commaDecimal = [
'bg-BG', 'cs-CZ', 'da-DK', 'de-DE', 'el-GR', 'en-ZM', 'es-ES', 'fr-CA', 'fr-FR',
'id-ID', 'it-IT', 'ku-IQ', 'hi-IN', 'hu-HU', 'nb-NO', 'nn-NO', 'nl-NL', 'pl-PL', 'pt-PT',
'ru-RU', 'si-LK', 'sl-SI', 'sr-RS@latin', 'sr-RS', 'sv-SE', 'tr-TR', 'uk-UA', 'vi-VN',
Expand Down Expand Up @@ -150,3 +152,49 @@ decimal['pl-Pl'] = decimal['pl-PL'];

// see #1455
alpha['fa-AF'] = alpha.fa;


function removeIgnoredCharacters(str, ignoredCharacters) {
if (!ignoredCharacters) {
return str;
}

if (ignoredCharacters instanceof RegExp) {
return str.replace(ignoredCharacters, '');
}

if (typeof ignoredCharacters === 'string') {
return str.replace(new RegExp(`[${ignoredCharacters.replace(/[-[\]{}()*+?.,\\^$|#\\s]/g, '\\$&')}]`, 'g'), ''); // escape regex for 'ignoredCharacters'
}

throw new Error('"ignore" should be instance of a String or RegExp');
}

const ALPHA_TYPE_MAP = {
alpha,
alphanumeric,
};

function validate(typeKey) {
return (_str, ...args) => {
assertString(_str);

// For backwards compatibility:
// isAlpha | isAlphaNumeric(str [, locale, options])
// i.e. `options` could be used as argument for the legacy `locale`
const locale = (typeof args[0] === 'object' ? args[0].locale : args[0]) || 'en-US';
const ignore = (typeof args[0] === 'object' ? args[0].ignore : args[1]?.ignore);

const str = removeIgnoredCharacters(_str, ignore);
const alphaType = ALPHA_TYPE_MAP[typeKey];

if (alphaType[locale]) {
return alphaType[locale].test(str);
}

throw new Error(`Invalid "locale" '${locale}'`);
};
}

export const isAlpha = validate('alpha');
export const isAlphanumeric = validate('alphanumeric');
7 changes: 6 additions & 1 deletion src/lib/isAfter.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import assertString from './util/assertString';
import toDate from './toDate';

export default function isAfter(str, date = String(new Date())) {
export default function isAfter(str, options) {
assertString(str);

// accessing 'arguments' for backwards compatibility: isAfter(str [, date])
// eslint-disable-next-line prefer-rest-params
const date = (typeof options === 'object' ? options.date : arguments[1]) || Date().toString();

const comparison = toDate(date);
const original = toDate(str);
return !!(original && comparison && original > comparison);
Expand Down
25 changes: 2 additions & 23 deletions src/lib/isAlpha.js
Original file line number Diff line number Diff line change
@@ -1,26 +1,5 @@
import assertString from './util/assertString';
import { alpha } from './alpha';
import { alpha, isAlpha } from './alpha';

export default function isAlpha(_str, locale = 'en-US', options = {}) {
assertString(_str);

let str = _str;
const { ignore } = options;

if (ignore) {
if (ignore instanceof RegExp) {
str = str.replace(ignore, '');
} else if (typeof ignore === 'string') {
str = str.replace(new RegExp(`[${ignore.replace(/[-[\]{}()*+?.,\\^$|#\\s]/g, '\\$&')}]`, 'g'), ''); // escape regex for ignore
} else {
throw new Error('ignore should be instance of a String or RegExp');
}
}

if (locale in alpha) {
return alpha[locale].test(str);
}
throw new Error(`Invalid locale '${locale}'`);
}
export default isAlpha;

export const locales = Object.keys(alpha);
25 changes: 2 additions & 23 deletions src/lib/isAlphanumeric.js
Original file line number Diff line number Diff line change
@@ -1,26 +1,5 @@
import assertString from './util/assertString';
import { alphanumeric } from './alpha';
import { alphanumeric, isAlphanumeric } from './alpha';

export default function isAlphanumeric(_str, locale = 'en-US', options = {}) {
assertString(_str);

let str = _str;
const { ignore } = options;

if (ignore) {
if (ignore instanceof RegExp) {
str = str.replace(ignore, '');
} else if (typeof ignore === 'string') {
str = str.replace(new RegExp(`[${ignore.replace(/[-[\]{}()*+?.,\\^$|#\\s]/g, '\\$&')}]`, 'g'), ''); // escape regex for ignore
} else {
throw new Error('ignore should be instance of a String or RegExp');
}
}

if (locale in alphanumeric) {
return alphanumeric[locale].test(str);
}
throw new Error(`Invalid locale '${locale}'`);
}
export default isAlphanumeric;

export const locales = Object.keys(alphanumeric);
File renamed without changes.
File renamed without changes.
File renamed without changes.
60 changes: 60 additions & 0 deletions test/testFunctions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import assert from 'assert';
import { format } from 'util';
import validator from '../src/index';

function test(options) {
const args = options.args || [];

args.unshift(null);

if (options.error) {
options.error.forEach((error) => {
args[0] = error;

try {
assert.throws(() => validator[options.validator](...args));
} catch (err) {
const warning = format(
'validator.%s(%s) passed but should error',
options.validator, args.join(', ')
);

throw new Error(warning);
}
});
}

if (options.valid) {
options.valid.forEach((valid) => {
args[0] = valid;

if (validator[options.validator](...args) !== true) {
const warning = format(
'validator.%s(%s) failed but should have passed',
options.validator, args.join(', ')
);

throw new Error(warning);
}
});
}

if (options.invalid) {
options.invalid.forEach((invalid) => {
args[0] = invalid;

if (validator[options.validator](...args) !== false) {
const warning = format(
'validator.%s(%s) passed but should have failed',
options.validator, args.join(', ')
);

throw new Error(warning);
}
});
}
}

export default {
test,
};
File renamed without changes.
Loading