Просмотр исходного кода

chore: adds the "regexp" property validator

e22m4u 1 год назад
Родитель
Сommit
a960b32bb2

+ 1 - 0
README.md

@@ -283,6 +283,7 @@ schema.defineModel({
 
 - `minLength: number` минимальная длинна строки или массива
 - `maxLength: number` максимальная длинна строки или массива
+- `regexp: string | RegExp` проверка по регулярному выражению
 
 **Пример**
 

+ 1 - 0
docs/index.html

@@ -136,6 +136,7 @@
 <ul>
 <li><code>minLength: number</code> минимальная длинна строки или массива</li>
 <li><code>maxLength: number</code> максимальная длинна строки или массива</li>
+<li><code>regexp: string | RegExp</code> проверка по регулярному выражению</li>
 </ul>
 <p><strong>Пример</strong></p>
 <p>Валидаторы указываются в объявлении свойства модели параметром

+ 1 - 0
src/definition/model/properties/property-validator/builtin/index.d.ts

@@ -1,2 +1,3 @@
+export * from './regexp-validator.js';
 export * from './max-length-validator.js';
 export * from './min-length-validator.js';

+ 1 - 0
src/definition/model/properties/property-validator/builtin/index.js

@@ -1,2 +1,3 @@
+export * from './regexp-validator.js';
 export * from './max-length-validator.js';
 export * from './min-length-validator.js';

+ 29 - 1
src/definition/model/properties/property-validator/builtin/max-length-validator.spec.js

@@ -3,11 +3,36 @@ import {format} from '@e22m4u/js-format';
 import {maxLengthValidator} from './max-length-validator.js';
 
 describe('maxLengthValidator', function () {
-  it('returns true by the false value in options', function () {
+  it('returns true if the "options" argument is false', function () {
     const res = maxLengthValidator(undefined, false, {});
     expect(res).to.be.true;
   });
 
+  it('requires the "value" argument as a String or an Array', function () {
+    const throwable = v => () =>
+      maxLengthValidator(v, 10, {
+        validatorName: 'myValidator',
+      });
+    const error = v =>
+      format(
+        'The property validator "myValidator" requires a String ' +
+          'or an Array value, but %s given.',
+        v,
+      );
+    expect(throwable(10)).to.throw(error('10'));
+    expect(throwable(0)).to.throw(error('0'));
+    expect(throwable(true)).to.throw(error('true'));
+    expect(throwable(false)).to.throw(error('false'));
+    expect(throwable(undefined)).to.throw(error('undefined'));
+    expect(throwable(null)).to.throw(error('null'));
+    expect(throwable({})).to.throw(error('Object'));
+    expect(throwable(() => undefined)).to.throw(error('Function'));
+    throwable('str')();
+    throwable('')();
+    throwable([1, 2, 3])();
+    throwable([])();
+  });
+
   it('requires the "options" argument to be a number', function () {
     const throwable = v => () =>
       maxLengthValidator('test', v, {
@@ -27,6 +52,9 @@ describe('maxLengthValidator', function () {
     expect(throwable({})).to.throw(error('Object'));
     expect(throwable([])).to.throw(error('Array'));
     expect(throwable(() => undefined)).to.throw(error('Function'));
+    throwable(10)();
+    throwable(0)();
+    throwable(false)();
   });
 
   describe('a string value', function () {

+ 29 - 1
src/definition/model/properties/property-validator/builtin/min-length-validator.spec.js

@@ -3,11 +3,36 @@ import {format} from '@e22m4u/js-format';
 import {minLengthValidator} from './min-length-validator.js';
 
 describe('minLengthValidator', function () {
-  it('returns true by the false value in options', function () {
+  it('returns true if the "options" argument is false', function () {
     const res = minLengthValidator(undefined, false, {});
     expect(res).to.be.true;
   });
 
+  it('requires the "value" argument as a String or an Array', function () {
+    const throwable = v => () =>
+      minLengthValidator(v, 0, {
+        validatorName: 'myValidator',
+      });
+    const error = v =>
+      format(
+        'The property validator "myValidator" requires a String ' +
+          'or an Array value, but %s given.',
+        v,
+      );
+    expect(throwable(10)).to.throw(error('10'));
+    expect(throwable(0)).to.throw(error('0'));
+    expect(throwable(true)).to.throw(error('true'));
+    expect(throwable(false)).to.throw(error('false'));
+    expect(throwable(undefined)).to.throw(error('undefined'));
+    expect(throwable(null)).to.throw(error('null'));
+    expect(throwable({})).to.throw(error('Object'));
+    expect(throwable(() => undefined)).to.throw(error('Function'));
+    throwable('str')();
+    throwable('')();
+    throwable([1, 2, 3])();
+    throwable([])();
+  });
+
   it('requires the "options" argument to be a number', function () {
     const throwable = v => () =>
       minLengthValidator('test', v, {
@@ -27,6 +52,9 @@ describe('minLengthValidator', function () {
     expect(throwable({})).to.throw(error('Object'));
     expect(throwable([])).to.throw(error('Array'));
     expect(throwable(() => undefined)).to.throw(error('Function'));
+    throwable(10)();
+    throwable(0)();
+    throwable(false)();
   });
 
   describe('a string value', function () {

+ 6 - 0
src/definition/model/properties/property-validator/builtin/regexp-validator.d.ts

@@ -0,0 +1,6 @@
+import {PropertyValidator} from '../property-validator.js';
+
+/**
+ * Regexp validator.
+ */
+export declare type regexpValidator = PropertyValidator;

+ 30 - 0
src/definition/model/properties/property-validator/builtin/regexp-validator.js

@@ -0,0 +1,30 @@
+import {stringToRegexp} from '../../../../../utils/index.js';
+import {InvalidArgumentError} from '../../../../../errors/index.js';
+
+/**
+ * Regexp validator.
+ *
+ * @param {*} value
+ * @param {string|RegExp|boolean} options
+ * @param {object} context
+ * @returns {boolean}
+ */
+export function regexpValidator(value, options, context) {
+  if (options === false) return true;
+  if (typeof options !== 'string' && !(options instanceof RegExp))
+    throw new InvalidArgumentError(
+      'The validator %v requires the "options" argument ' +
+        'as a String or RegExp, but %v given.',
+      context.validatorName,
+      options,
+    );
+  if (typeof value === 'string') {
+    const regexp = stringToRegexp(options);
+    return regexp.test(value);
+  }
+  throw new InvalidArgumentError(
+    'The property validator %v requires ' + 'a String value, but %v given.',
+    context.validatorName,
+    value,
+  );
+}

+ 95 - 0
src/definition/model/properties/property-validator/builtin/regexp-validator.spec.js

@@ -0,0 +1,95 @@
+import {expect} from 'chai';
+import {format} from '@e22m4u/js-format';
+import {regexpValidator} from './regexp-validator.js';
+
+describe('regexpValidator', function () {
+  it('returns true if the "options" argument is false', function () {
+    const res = regexpValidator(undefined, false, {});
+    expect(res).to.be.true;
+  });
+
+  it('requires the "value" argument to be a string', function () {
+    const throwable = v => () =>
+      regexpValidator(v, '.*', {
+        validatorName: 'myValidator',
+      });
+    const error = v =>
+      format(
+        'The property validator "myValidator" requires ' +
+          'a String value, but %s given.',
+        v,
+      );
+    expect(throwable(true)).to.throw(error('true'));
+    expect(throwable(false)).to.throw(error('false'));
+    expect(throwable(undefined)).to.throw(error('undefined'));
+    expect(throwable(null)).to.throw(error('null'));
+    expect(throwable({})).to.throw(error('Object'));
+    expect(throwable([])).to.throw(error('Array'));
+    expect(throwable(() => undefined)).to.throw(error('Function'));
+    throwable('str')();
+    throwable('')();
+  });
+
+  it('requires the "options" argument to be a string or RegExp', function () {
+    const throwable = v => () =>
+      regexpValidator('test', v, {
+        validatorName: 'myValidator',
+      });
+    const error = v =>
+      format(
+        'The validator "myValidator" requires the "options" argument ' +
+          'as a String or RegExp, but %s given.',
+        v,
+      );
+    expect(throwable(true)).to.throw(error('true'));
+    expect(throwable(undefined)).to.throw(error('undefined'));
+    expect(throwable(null)).to.throw(error('null'));
+    expect(throwable({})).to.throw(error('Object'));
+    expect(throwable([])).to.throw(error('Array'));
+    expect(throwable(() => undefined)).to.throw(error('Function'));
+    throwable('str')();
+    throwable('')();
+    throwable(false)();
+    throwable(new RegExp(''))();
+  });
+
+  describe('the given regexp as a String', function () {
+    it('returns false if the given regexp is not matched', function () {
+      const res1 = regexpValidator('foo', 'bar', {});
+      const res2 = regexpValidator('foo', '^\\d+', {});
+      const res3 = regexpValidator('foo', '^baz$', {});
+      expect(res1).to.be.false;
+      expect(res2).to.be.false;
+      expect(res3).to.be.false;
+    });
+
+    it('returns true if the given regexp is matched', function () {
+      const res1 = regexpValidator('foo', '^fo', {});
+      const res2 = regexpValidator('foo', '^foo$', {});
+      const res3 = regexpValidator('foo', '^\\w+$', {});
+      expect(res1).to.be.true;
+      expect(res2).to.be.true;
+      expect(res3).to.be.true;
+    });
+  });
+
+  describe('the given regexp as an instance of RegExp', function () {
+    it('returns false if the given regexp is not matched', function () {
+      const res1 = regexpValidator('foo', /bar/, {});
+      const res2 = regexpValidator('foo', /^\d+/, {});
+      const res3 = regexpValidator('foo', /^bar$/, {});
+      expect(res1).to.be.false;
+      expect(res2).to.be.false;
+      expect(res3).to.be.false;
+    });
+
+    it('returns true if the given regexp is matched', function () {
+      const res1 = regexpValidator('foo', /^fo/, {});
+      const res2 = regexpValidator('foo', /^foo$/, {});
+      const res3 = regexpValidator('foo', /^\w+$/, {});
+      expect(res1).to.be.true;
+      expect(res2).to.be.true;
+      expect(res3).to.be.true;
+    });
+  });
+});

+ 2 - 0
src/definition/model/properties/property-validator/property-validator-registry.js

@@ -1,4 +1,5 @@
 import {Service} from '@e22m4u/js-service';
+import {regexpValidator} from './builtin/index.js';
 import {maxLengthValidator} from './builtin/index.js';
 import {minLengthValidator} from './builtin/index.js';
 import {InvalidArgumentError} from '../../../../errors/index.js';
@@ -15,6 +16,7 @@ export class PropertyValidatorRegistry extends Service {
   _validators = {
     maxLength: maxLengthValidator,
     minLength: minLengthValidator,
+    regexp: regexpValidator,
   };
 
   /**