Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
php: [ 7.3, 7.4, 8.0 ]
php: [ 7.4, 8.0 ]
steps:
- name: Checkout
uses: actions/checkout@v3
Expand Down
100 changes: 62 additions & 38 deletions index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,32 +15,69 @@ import {DecimalValidator} from './js/validators/DecimalValidator';
import {ConfirmationValidator} from './js/validators/ConfirmationValidator.js';
import {RegexValidator} from './js/validators/RegexValidator.js';

function testSuccess(response)
{
function testSuccess(response) {
expect(response).toHaveProperty('errors', []);
}

function testFailure(response, errors, potentiallyValid = false)
{
function testFailure(response, errors, potentiallyValid = false) {
expect(response).toHaveProperty('errors', errors);
expect(response).toHaveProperty('potentiallyValid', potentiallyValid);
}

test(
'deserialize',
() =>
{
() => {
let v = Validator.fromJsonObject({t: 'String', c: {'minLength': 2, 'maxLength': 5}});
expect(v).toBeInstanceOf(StringValidator);
expect(v._minLength).toStrictEqual(2);
expect(v._maxLength).toStrictEqual(5);
}
);

test(
'TranslatedStringValidator',
() => {
let v = new StringValidator(2, 5);
v.dictionary = {
invalid: 'kein gültiger Wert',
min: 'muss mindestens sein %s characters',
max: 'darf nicht mehr als sein %s characters'
};

testFailure(v.validate(), ['kein gültiger Wert']);
testFailure(v.validate(''), ['muss mindestens sein 2 characters']);
testFailure(v.validate('testtest'), ['darf nicht mehr als sein 5 characters']);
}
);

test(
'MissinKeyTranslatedStringValidator',
() => {
let v = new StringValidator(2, 5);
v.dictionary = {
invalid: 'kein gültiger Wert',
min: 'muss mindestens sein %s characters'
// max is missing
};

testFailure(v.validate(), ['kein gültiger Wert']);
testFailure(v.validate(''), ['muss mindestens sein 2 characters']);
testFailure(v.validate('testesttest'), ['must be no more than 5 characters']); // default error message
}
)

test(
'TranslatedRequiredValidator',
() => {
let v = new RequiredValidator();
v.dictionary = {invalid: 'kein gültiger Wert'};
testFailure(v.validate(), ['kein gültiger Wert']);
}
);

test(
'StringValidator',
() =>
{
() => {
let v = new StringValidator();
testSuccess(v.validate('test'));
testSuccess(v.validate(''));
Expand Down Expand Up @@ -70,8 +107,7 @@ test(

test(
'BoolValidator',
() =>
{
() => {
const v = new BoolValidator();
testFailure(v.validate('test'), ['Invalid boolean value']);
testFailure(v.validate(''), ['Invalid boolean value']);
Expand All @@ -90,8 +126,7 @@ test(

test(
'EnumValidator',
() =>
{
() => {
let v = new EnumValidator();
testSuccess(v.validate(''));
testFailure(v.validate('test'), ['not a valid value']);
Expand All @@ -117,8 +152,7 @@ test(

test(
'ConstEnumValidator',
() =>
{
() => {
let v = new ConstEnumValidator();
testSuccess(v.validate(''));
testFailure(v.validate('test'), ['not a valid value']);
Expand All @@ -144,8 +178,7 @@ test(

test(
'EqualValidator',
() =>
{
() => {
let v = new EqualValidator('test');
testFailure(v.validate(''), ['value does not match']);
testSuccess(v.validate('test'));
Expand All @@ -157,8 +190,7 @@ test(

test(
'NotEqualValidator',
() =>
{
() => {
let v = new NotEqualValidator('test');
testFailure(v.validate('test'), ['value must not match']);
testSuccess(v.validate(''));
Expand All @@ -170,8 +202,7 @@ test(

test(
'RequiredValidator',
() =>
{
() => {
let v = new RequiredValidator();
testSuccess(v.validate(true));
testSuccess(v.validate(false));
Expand All @@ -189,8 +220,7 @@ test(

test(
'EmailValidator',
() =>
{
() => {
let v = new EmailValidator();
testSuccess(v.validate('test@test.com'));
testSuccess(v.validate('a@b.com'));
Expand All @@ -202,8 +232,7 @@ test(

test(
'IPv4Validator',
() =>
{
() => {
let v = new IPv4Validator();
testSuccess(v.validate('0.0.0.0'));
testSuccess(v.validate('255.255.255.255'));
Expand All @@ -222,8 +251,7 @@ test(

test(
'NumberValidator',
() =>
{
() => {
let v = new NumberValidator();
testFailure(v.validate('test'), ['must be a number']);
testSuccess(v.validate(1));
Expand All @@ -246,28 +274,26 @@ test(

test(
'RegexValidator',
() =>
{
() => {
let v = new RegexValidator('not valid regex');
testFailure(v.validate('test'), ['not a valid regular expression']);

v = new RegexValidator({});
testFailure(v.validate('test'), ['not a valid regular expression']);

v = new RegexValidator('/test/', 'not test');
testFailure(v.validate('abc'), ['not test']);
v = new RegexValidator('/test/');
testFailure(v.validate('abc'), ['not a valid regular expression']);

v = new RegexValidator('/test/');
testFailure(v.validate('abc'), ['does not match regular expression']);
testFailure(v.validate('1'), ['does not match regular expression']);
testFailure(v.validate('abc'), ['not a valid regular expression']);
testFailure(v.validate('1'), ['not a valid regular expression']);
testSuccess(v.validate('test'));
}
);

test(
'IntegerValidator',
() =>
{
() => {
let v = new IntegerValidator();
testFailure(v.validate('test'), ['must be a number']);
testSuccess(v.validate(1));
Expand Down Expand Up @@ -296,8 +322,7 @@ test(

test(
'DecimalValidator',
() =>
{
() => {
let v = new DecimalValidator();
testFailure(v.validate('test'), ['must be a number']);
testSuccess(v.validate(1));
Expand Down Expand Up @@ -328,8 +353,7 @@ test(

test(
'ConfirmationValidator',
() =>
{
() => {
let v = new ConfirmationValidator('field2');
v.setData({'field1': 10});
testFailure(v.validate(v.getData()['field1']), ['value does not match']);
Expand Down
73 changes: 32 additions & 41 deletions js/validator.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/**
* @typedef {function(new: Validator), deserialize} ValidatorType
* @typedef {function(new: Validator)} ValidatorType
* @property deserialize
*/

/**
Expand All @@ -8,24 +9,22 @@
*/
const _validatorMap = new Map();

export class Validator
{
export class Validator {
/**
* @param {Object} obj
* @return {Validator}
*/
static fromJsonObject(obj)
{
if(!_validatorMap.has(obj.t))
{
static fromJsonObject(obj) {
if(!_validatorMap.has(obj.t)) {
throw 'unrecognised type ' + obj.t;
}
const c = _validatorMap.get(obj.t);
return c.deserialize(obj.c);
const validator = _validatorMap.get(obj.t);
const initializedValidator = validator.deserialize(obj.c);
initializedValidator.dictionary = obj.d;
return initializedValidator;
}

static deserialize(config)
{
static deserialize(config) {
return new this();
}

Expand All @@ -34,70 +33,63 @@ export class Validator
* @return {ValidationResponse}
* @throws
*/
validate(value)
{
validate(value) {
throw 'validate not implemented on ' + this.constructor.name;
}

/**
* @param {Object} dictionary
*/
set dictionary(dictionary) {
this._dictionary = dictionary || {};
}
}

export class DataSetValidator extends Validator
{
export class DataSetValidator extends Validator {
_data = {};

setData(data)
{
setData(data) {
this._data = data;
}

getData()
{
getData() {
return this._data;
}
}

export class ValidationResponse
{
constructor(errors, potentiallyValid)
{
export class ValidationResponse {
constructor(errors, potentiallyValid) {
this._errors = errors || [];
this._potentiallyValid = potentiallyValid || false;
}

get errors()
{
get errors() {
return this._errors;
}

get potentiallyValid()
{
get potentiallyValid() {
return this._potentiallyValid;
}

static success()
{
static success() {
return new ValidationResponse([], true);
}

static potentiallyValid(errors = [])
{
static potentiallyValid(errors = []) {
return new ValidationResponse(errors, true);
}

static error(errors = [])
{
static error(errors = []) {
return new ValidationResponse(errors, false);
}

/**
* @param {...ValidationResponse} responses
*/
combine(...responses)
{
combine(...responses) {
responses.forEach(
r =>
{
if(r instanceof ValidationResponse)
{
r => {
if(r instanceof ValidationResponse) {
this._errors.push(...r._errors);
this._potentiallyValid = this._potentiallyValid && r._potentiallyValid;
}
Expand All @@ -110,7 +102,6 @@ export class ValidationResponse
* @param {string} name
* @param {ValidatorType} validator
*/
export function addValidator(name, validator)
{
export function addValidator(name, validator) {
_validatorMap.set(name, validator);
}
Loading