Changed to constructor functions

This commit is contained in:
Sebastian Seedorf
2020-05-26 19:40:41 +02:00
parent 9fa06685ba
commit 836b1449bf
15 changed files with 281 additions and 168 deletions

View File

@@ -14,15 +14,42 @@ import {
Deno.test("validate schema (match)", async () => {
const values: [any, Validatable][] = [
["string", isString],
["string", [isString]],
[["arr", "ay"], { [ArraySymbol]: isString }],
[{ foo: 3.1415, lorem: "ipsum" }, { foo: isNumber, lorem: [isString] }],
[{}, { optional: [isString] }],
[{ foo: { bar: "" } }, { foo: { bar: [isRequired, isString] } }],
[{ foo: { bar: "" } }, { foo: { bar: [isString] } }],
[{ foo: {} }, { foo: { bar: [isString] } }],
[{}, { foo: { bar: [isString] } }],
[
"string",
isString(),
],
[
"string",
[isString()],
],
[
["arr", "ay"],
{ [ArraySymbol]: isString() },
],
[
{ foo: 3.1415, lorem: "ipsum" },
{ foo: isNumber(), lorem: [isString()] },
],
[
{},
{ optional: [isString()] },
],
[
{ foo: { bar: "" } },
{ foo: { bar: [isRequired(), isString()] } },
],
[
{ foo: { bar: "" } },
{ foo: { bar: [isString()] } },
],
[
{ foo: {} },
{ foo: { bar: [isString()] } },
],
[
{},
{ foo: { bar: [isString()] } },
],
];
for (const [value, constraints] of values) {
assertEquals([], await validate(value, constraints));
@@ -31,14 +58,38 @@ Deno.test("validate schema (match)", async () => {
Deno.test("validate schema (no match)", async () => {
const values: [any, Validatable][] = [
[6, isString],
[false, [isString]],
[["arr", ["ay"]], { [ArraySymbol]: isString }],
[{ foo: 3.1415, lorem: "ipsum" }, { foo: isInteger, lorem: [isString] }],
[{}, { required: [isRequired, isString] }],
[{ foo: { bar: 1 } }, { foo: { bar: [isRequired, isString] } }],
[{ foo: {} }, { foo: { bar: [isRequired, isString] } }],
[{}, { foo: { bar: [isRequired, isString] } }],
[
6,
isString(),
],
[
false,
[isString()],
],
[
["arr", ["ay"]],
{ [ArraySymbol]: isString() },
],
[
{ foo: 3.1415, lorem: "ipsum" },
{ foo: isInteger(), lorem: [isString()] },
],
[
{},
{ required: [isRequired(), isString()] },
],
[
{ foo: { bar: 1 } },
{ foo: { bar: [isRequired(), isString()] } },
],
[
{ foo: {} },
{ foo: { bar: [isRequired(), isString()] } },
],
[
{},
{ foo: { bar: [isRequired(), isString()] } },
],
];
for (const [value, constraints] of values) {
assertNotEquals([], await validate(value, constraints));

View File

@@ -2,6 +2,7 @@ export type Args = { [_: string]: any };
export interface Validator {
type: string;
extends?: Validator[];
check: (value: any) => Promise<Args | undefined> | Args | undefined;
message: (
value: any,
@@ -54,6 +55,13 @@ async function validateValue(
}
const result: ValidationError[] = [];
for (const validator of validators) {
if (validator.extends) {
const prerequisites = await validate(value, validator.extends);
if (prerequisites.length > 0) {
result.push(...prerequisites);
continue;
}
}
const args = await validator.check(value);
if (args !== undefined) {
const message = await validator.message(value, args);

View File

@@ -13,7 +13,7 @@ Deno.test("isArray (match)", async () => {
["foo"],
];
for (const value of values) {
assertEquals(await validate(value, isArray), []);
assertEquals(await validate(value, isArray()), []);
}
});
@@ -33,6 +33,6 @@ Deno.test("isArray (no match)", async () => {
Symbol(),
];
for (const value of values) {
assertNotEquals(await validate(value, isArray), []);
assertNotEquals(await validate(value, isArray()), []);
}
});

View File

@@ -1,14 +1,16 @@
import { Validator, Args } from "../mod.ts";
export const isArray: Validator = {
type: "isArray",
check: (value: any) => {
if (value === null || value === undefined) return;
if (!Array.isArray(value)) {
return {};
}
},
message: (value: any, args?: Args) => {
return `This value has to be an array.`;
},
};
export function isArray(): Validator {
return {
type: "isArray",
check: (value: any) => {
if (value === null || value === undefined) return;
if (!Array.isArray(value)) {
return {};
}
},
message: (value: any, args?: Args) => {
return `This value has to be an array.`;
},
};
}

View File

@@ -13,7 +13,7 @@ Deno.test("isBoolean (match)", async () => {
false,
];
for (const value of values) {
assertEquals(await validate(value, isBoolean), []);
assertEquals(await validate(value, isBoolean()), []);
}
});
@@ -31,6 +31,6 @@ Deno.test("isBoolean (no match)", async () => {
Symbol(),
];
for (const value of values) {
assertNotEquals(await validate(value, isBoolean), []);
assertNotEquals(await validate(value, isBoolean()), []);
}
});

View File

@@ -1,14 +1,16 @@
import { Validator, Args } from "../mod.ts";
export const isBoolean: Validator = {
type: "isBoolean",
check: (value: any) => {
if (value === null || value === undefined) return;
if (value !== true && value !== false) {
return {};
}
},
message: (value: any, args?: Args) => {
return `This value has to be a boolean.`;
},
};
export function isBoolean(): Validator {
return {
type: "isBoolean",
check: (value: any) => {
if (value === null || value === undefined) return;
if (value !== true && value !== false) {
return {};
}
},
message: (value: any, args?: Args) => {
return `This value has to be a boolean.`;
},
};
}

View File

@@ -22,7 +22,7 @@ Deno.test("isRequired (match)", async () => {
Symbol("foo"),
];
for (const value of values) {
assertEquals(await validate(value, isRequired), []);
assertEquals(await validate(value, isRequired()), []);
}
});
@@ -32,7 +32,7 @@ Deno.test("isRequired (no match)", async () => {
null,
];
for (const value of values) {
assertNotEquals(await validate(value, isRequired), []);
assertNotEquals(await validate(value, isRequired()), []);
}
});
@@ -54,7 +54,7 @@ Deno.test("isDefined (match)", async () => {
Symbol("foo"),
];
for (const value of values) {
assertEquals(await validate(value, isDefined), []);
assertEquals(await validate(value, isDefined()), []);
}
});
@@ -63,7 +63,7 @@ Deno.test("isDefined (no match)", async () => {
undefined,
];
for (const value of values) {
assertNotEquals(await validate(value, isDefined), []);
assertNotEquals(await validate(value, isDefined()), []);
}
});
@@ -80,7 +80,7 @@ Deno.test("isNotEmpty (match)", async () => {
Symbol("foo"),
];
for (const value of values) {
assertEquals(await validate(value, isNotEmpty), []);
assertEquals(await validate(value, isNotEmpty()), []);
}
});
@@ -94,6 +94,6 @@ Deno.test("isNotEmpty (no match)", async () => {
[],
];
for (const value of values) {
assertNotEquals(await validate(value, isNotEmpty), []);
assertNotEquals(await validate(value, isNotEmpty()), []);
}
});

View File

@@ -8,63 +8,71 @@ import {
isBoolean,
} from "../mod.ts";
export const isRequired: Validator = {
type: "isRequired",
check: (value: any) => {
if (value === null || value === undefined) {
return {};
}
},
message: (value: any, args?: Args) => {
return `This value is required.`;
},
};
export const isDefined: Validator = {
type: "isDefined",
check: (value: any) => {
if (value === undefined) {
return {};
}
},
message: (value: any, args?: Args) => {
return `This value has to be defined.`;
},
};
export const isNotEmpty: Validator = {
type: "isNotEmpty",
check: async (value: any) => {
if (value === null || value === undefined) {
return {};
}
if ((await isString.check(value)) === undefined) {
if (/^\s*$/.test(value)) {
export function isRequired(): Validator {
return {
type: "isRequired",
check: (value: any) => {
if (value === null || value === undefined) {
return {};
}
return;
}
if ((await isNumber.check(value)) === undefined || Number.isNaN(value)) {
return;
}
if (await isSymbol.check(value) === undefined) {
return;
}
if (await isBoolean.check(value) === undefined) {
return;
}
if ((await isArray.check(value)) === undefined) {
if (value.length > 0) {
},
message: (value: any, args?: Args) => {
return `This value is required.`;
},
};
}
export function isDefined(): Validator {
return {
type: "isDefined",
check: (value: any) => {
if (value === undefined) {
return {};
}
},
message: (value: any, args?: Args) => {
return `This value has to be defined.`;
},
};
}
export function isNotEmpty(): Validator {
return {
type: "isNotEmpty",
check: async (value: any) => {
if (value === null || value === undefined) {
return {};
}
if ((await isString().check(value)) === undefined) {
if (/^\s*$/.test(value)) {
return {};
}
return;
}
if (
(await isNumber().check(value)) === undefined || Number.isNaN(value)
) {
return;
}
if (await isSymbol().check(value) === undefined) {
return;
}
if (await isBoolean().check(value) === undefined) {
return;
}
if ((await isArray().check(value)) === undefined) {
if (value.length > 0) {
return;
}
return {};
}
for (const key in value) {
return;
}
return {};
}
for (const key in value) {
return;
}
return {};
},
message: (value: any, args?: Args) => {
return `This value has to be non-empty.`;
},
};
},
message: (value: any, args?: Args) => {
return `This value has to be non-empty.`;
},
};
}

View File

@@ -7,8 +7,8 @@ import {
Deno.test("or (match)", async () => {
const values: [any, Validator[]][] = [
["", [isString, isInteger]],
[1, [isString, isInteger]],
["", [isString(), isInteger()]],
[1, [isString(), isInteger()]],
];
for (const [value, validators] of values) {
assertEquals(await validate(value, or(...validators)), []);
@@ -17,9 +17,9 @@ Deno.test("or (match)", async () => {
Deno.test("or (no match)", async () => {
const values: [any, Validator[]][] = [
[true, [isString, isInteger]],
[3.1415, [isString, isInteger]],
[1, [isString]],
[true, [isString(), isInteger()]],
[3.1415, [isString(), isInteger()]],
[1, [isString()]],
];
for (const [value, validators] of values) {
assertNotEquals(await validate(value, or(...validators)), []);

View File

@@ -17,7 +17,7 @@ Deno.test("isNumber (match)", async () => {
Math.PI,
];
for (const value of values) {
assertEquals(await validate(value, isNumber), []);
assertEquals(await validate(value, isNumber()), []);
}
});
@@ -33,7 +33,7 @@ Deno.test("isNumber (no match)", async () => {
Symbol(),
];
for (const value of values) {
assertNotEquals(await validate(value, isNumber), []);
assertNotEquals(await validate(value, isNumber()), []);
}
});
@@ -47,7 +47,7 @@ Deno.test("isInteger (match)", async () => {
-1,
];
for (const value of values) {
assertEquals(await validate(value, isInteger), []);
assertEquals(await validate(value, isInteger()), []);
}
});
@@ -64,6 +64,6 @@ Deno.test("isInteger (no match)", async () => {
0.1,
];
for (const value of values) {
assertNotEquals(await validate(value, isInteger), []);
assertNotEquals(await validate(value, isInteger()), []);
}
});

View File

@@ -1,27 +1,31 @@
import { Validator, Args } from "../mod.ts";
export const isNumber: Validator = {
type: "isNumber",
check: (value: any) => {
if (value === null || value === undefined) return;
if (!Number.isFinite(value)) {
return {};
}
},
message: (value: any, args?: Args) => {
return `This value has to be a number.`;
},
};
export function isNumber(): Validator {
return {
type: "isNumber",
check: (value: any) => {
if (value === null || value === undefined) return;
if (!Number.isFinite(value)) {
return {};
}
},
message: (value: any, args?: Args) => {
return `This value has to be a number.`;
},
};
}
export const isInteger: Validator = {
type: "isInteger",
check: (value: any) => {
if (value === null || value === undefined) return;
if (!Number.isInteger(value)) {
return {};
}
},
message: (value: any, args?: Args) => {
return `This value has to be an integer.`;
},
};
export function isInteger(): Validator {
return {
type: "isInteger",
check: (value: any) => {
if (value === null || value === undefined) return;
if (!Number.isInteger(value)) {
return {};
}
},
message: (value: any, args?: Args) => {
return `This value has to be an integer.`;
},
};
}

View File

@@ -1,4 +1,4 @@
import { isString } from "./string.ts";
import { isString, isURL } from "./string.ts";
import { validate } from "../mod.ts";
import {
assertEquals,
@@ -15,7 +15,7 @@ Deno.test("isString (match)", async () => {
new String("bar"),
];
for (const value of values) {
assertEquals(await validate(value, isString), []);
assertEquals(await validate(value, isString()), []);
}
});
@@ -31,6 +31,24 @@ Deno.test("isString (no match)", async () => {
Symbol(),
];
for (const value of values) {
assertNotEquals(await validate(value, isString), []);
assertNotEquals(await validate(value, isString()), []);
}
});
Deno.test("isURL (match)", async () => {
const values = [
"http://google.com",
];
for (const value of values) {
assertEquals(await validate(value, isURL()), []);
}
});
Deno.test("isURL (no match)", async () => {
const values = [
"invalid",
];
for (const value of values) {
assertNotEquals(await validate(value, isURL()), []);
}
});

View File

@@ -1,14 +1,32 @@
import { Validator, Args } from "../mod.ts";
export const isString: Validator = {
type: "isString",
check: (value: any) => {
if (value === null || value === undefined) return;
if (typeof value !== "string" && !(value instanceof String)) {
return {};
}
},
message: (value: any, args?: Args) => {
return `This value has to be a string.`;
},
};
export function isString(): Validator {
return {
type: "isString",
check: (value: any) => {
if (value === null || value === undefined) return;
if (typeof value !== "string" && !(value instanceof String)) {
return {};
}
},
message: (value: any, args?: Args) => {
return `This value has to be a string.`;
},
};
}
export function isURL(): Validator {
return {
type: "isURL",
extends: [isString()],
check: (value: any) => {
if (value === null || value === undefined) return;
if (value !== "http://google.com") {
return {};
}
},
message: (value: any, args?: Args) => {
return `This value is not a valid URL.`;
},
};
}

View File

@@ -12,7 +12,7 @@ Deno.test("isSymbol (match)", async () => {
Symbol(),
];
for (const value of values) {
assertEquals(await validate(value, isSymbol), []);
assertEquals(await validate(value, isSymbol()), []);
}
});
@@ -31,6 +31,6 @@ Deno.test("isSymbol (no match)", async () => {
new Object(),
];
for (const value of values) {
assertNotEquals(await validate(value, isSymbol), []);
assertNotEquals(await validate(value, isSymbol()), []);
}
});

View File

@@ -1,14 +1,16 @@
import { Validator, Args } from "../mod.ts";
export const isSymbol: Validator = {
type: "isSymbol",
check: (value: any) => {
if (value === null || value === undefined) return;
if (typeof value !== "symbol") {
return {};
}
},
message: (value: any, args?: Args) => {
return `This value has to be a symbol.`;
},
};
export function isSymbol(): Validator {
return {
type: "isSymbol",
check: (value: any) => {
if (value === null || value === undefined) return;
if (typeof value !== "symbol") {
return {};
}
},
message: (value: any, args?: Args) => {
return `This value has to be a symbol.`;
},
};
}