tynder
Version:
TypeScript friendly Data validator for JavaScript.
447 lines (421 loc) • 16.8 kB
text/typescript
// tslint:disable-next-line: no-implicit-dependencies no-var-requires
const Ajv = require('ajv');
import { ValidationContext } from '../types';
import { validate,
getType } from '../validator';
import { pick,
patch } from '../picker';
import { compile } from '../compiler';
import { generateJsonSchemaObject } from '../codegen';
import { serialize, deserialize } from '../serializer';
import * as op from '../operators';
describe("foo", function() {
it("foo-1", function() {
try {
/*
const ast1 = parse(`
export type A = string;
type AA = (string);
type AAA = string|number;
type AAAA = string | number;
type AAAAA = (string|number);
type AAAAAA = {a: string, b: number};
type AAAAAAA = (string|(number|'aaa')|{a: string, b: number}|[string, number]);
type AAAAAAA2 = (string|(number|'aaa')|{a: string, b: number}|[string, @range(10, 20) number]);
interface B {
a: string;
b?: number;
'b2'?: number;
@range(2, 5) @max(10) @min(3)
b3: number;
b4: @range(2, 5) @max(10) @min(3) number;
@msg({a: "1234", b: "3456"})
b5: number;
c: [string];
d: [string, number];
e: [1, 2, 'aaa'];
e2: [1, ...<string, 3..5>, 'aaa'];
e3: [...<string|number>];
f: Array<number>;
g: Array<Array<number>>;
g2: Array<{a: string, b: number}>;
h: Array<number[]>;
i: number[];
i2: number[3];
i3: number[3..];
i4: number[..3];
i5: number[1..3];
i6: (number)[];
i7: (number|string)[];
j: number[][];
j2: (number|string)[][];
k: number | string;
k2: number[] | string [];
k3: number[][] | string [][];
k4: number[][] | string [][] | string[][][];
k4b: @range(2, 5) @max(10) @min(3) number[][] |
@range(2, 5) @max(10) @min(3) string [][] |
@range(2, 5) @max(10) @min(3) string[][][];
k5: {} | string [][] | string[][][];
k6: {a: number} | string [][] | string[][][];
k7: {a: number, b:string} | string [][] | string[][][];
k8: {a: number, b:string} | {c?: boolean} | string[][][];
k9: (number[]) | (string []);
k10: (number[][]) | (string [][]);
k11: (number[3..4][5..6]) | (string [3..4][5..6]);
k12: (number[3..4][5..6]) | (string [3..4][5..6]) | (string[][3..4][5..6]);
k13: ({}) | (string [3..4][5..6]) | (string[][3..4][5..6]);
k14: ({a: number}) | (string [3..4][5..6]) | (string[][3..4][5..6]);
k15: ({a: number, b:string}) | (string [3..4][5..6]) | (string[][3..4][5..6]);
k16: ({a: number, b:string}) | ({c?: boolean}) | (string[][3..4][5..6]);
}
export interface C extends A {}
export interface D extends A, B {
x: string;
}
`);
*/
/*
const ast2 = parse(`
type A = @min(3) @range(5, 9) @msgId({a: '12345', b: [1, {c: 3}]}) string;
`);
*/
const schema2 = compile(`
import * as fs from 'fs';
external U, W;
/** doc comment X - line 1
* doc comment X - line 2
*/
export type X = string | number | 'foo';
type XX = number;
/** doc comment S */
enum S {
/** doc comment S.q */
q,
w = 3,
e,
r,
t = 10,
u = 'aaa',
}
export enum R {}
interface B1111 {}
/** doc comment B - line 1 */
interface B {
@range(2, 5)
a: number;
/** doc comment B.b - line 1
* doc comment B.b - line 2
*/
b?: string;
}
export interface C {
c?: string[10..20][];
d: string|number|boolean[1][2..3]|B|B[]|B|{/** doc comment C.d.p */p: string};
d2?: X;
d3?: U;
d4?: S;
[propName: /^[B][0-9]$/]: U;
}
interface A extends B,C, D,DD {
e: (string[])|(@minValue(3) number[])|(boolean[]);
f: X | boolean;
@match(/^[A-F]+$/)
g: string;
@msg('custom msg of A.h')
h?: C;
i: [string, number?, string?];
j: [...<number, 1..2>, string];
j2: [string, ...<number, 2..3>];
j3: string[..2];
k: Y;
l: B;
[propName: /^[a-z][0-9]$/ | number]?: string;
/** doc comment A.propName (C) */
[propName: /^[A][0-9]$/]: C;
}
interface AA extends A {}
type Y = boolean;
interface D {
d90: string;
}
interface DD {
d91: string;
}
interface H {
// interface H extends HH { // <- recursive extend
a?: HH;
b: H | number;
}
interface HH extends H {c?:number;}
/*
interface HH extends H {c?:number;}
interface H {
a?: HH;
// a?: H;
b: H | number;
}
*/
`);
// console.log(JSON.stringify(getType(schema2, 'X'), null, 2));
// console.log(JSON.stringify(getType(schema2, 'C'), null, 2));
// console.log(JSON.stringify(getType(schema2, 'A'), null, 2));
// console.log(JSON.stringify(getType(schema2, 'H'), null, 2));
/*
const ctx: Partial<ValidationContext> = {
checkAll: true,
// noAdditionalProps: true,
schema: schema2,
};
expect(validate({
a: 5,
c: [['', '', '', '', '', '', '', '', '', '']],
d: 10,
// d2: true,
d3: 11,
// d4: 11,
d90: '',
d91: '',
e: [true, false],
// e: [0, 1, 2],
f: true,
g: 'DEBCB',
// h: 1,
i: ['q', 12, 'a'],
j: [1, 2, 'aaa'],
// j: ['', 1, 2, 'aaa'],
// j: [1, 2, 3, 'aaa'],
// j: '',
j2: ['aaa', 1, 2, 3],
// j2: ['aaa', '', 1, 2, 3],
j3: ['', ''],
// j3: ['', 0],
// j3: '',
k: false,
l: {a: 5},
z1: 'a',
// A0: 1,
// B0: 2,
}, getType(schema2, 'A'), ctx)).toEqual({} as any);
console.log(generateTypeScriptCode(schema2));
console.log(generateProto3Code(schema2));
// console.log(JSON.stringify(ctx));
console.log(ctx.errors);
delete ctx.errors;
expect(validate({
a: {a: {b: 2}, b: 1},
b: {b: {b: 3}},
}, getType(schema2, 'HH'), ctx)).toEqual({} as any);
// console.log(JSON.stringify(ctx));
console.log(ctx.errors);
*/
} catch (e) {
throw e;
}
expect(1).toEqual(1);
});
it("foo-2", function() {
/*
console.log(JSON.stringify(getType(z, 'A'), null, 2));
*/
const z = compile(`
type Foo = string;
interface A {
a: Foo;
@range(3, 5)
b: number;
@maxLength(3)
c: Foo;
}
`);
const ctx: Partial<ValidationContext> = {checkAll: true};
const r = validate({b: 6, c: '1234'}, getType(z, 'A'), ctx);
// console.log(JSON.stringify(r));
// console.log(ctx.errors);
expect(1).toEqual(1);
});
it("foo-2b", function() {
/*
console.log(JSON.stringify(getType(z, 'A'), null, 2));
*/
const z = compile(`
type Foo = string;
interface A {
a: Foo;
@range(3, 5)
b: number;
@maxLength(3)
c: Foo;
}
`);
const ctx: Partial<ValidationContext> = {checkAll: true};
const subtype = op.picked(getType(z, 'A'), 'a', 'c');
const original = {a: '', b: 3, c: '123', d: 0};
const needle = pick(original, subtype, ctx);
needle.a = '1';
needle.b = 6;
(needle as any).e = 5;
try {
const r = patch(original, needle, subtype, ctx);
// console.log(r);
expect(1).toEqual(1);
} catch (e) {
// console.log(e.message);
// console.log(e.ctx?.errors);
expect(1).toEqual(0);
}
});
it("foo-4", function() {
const z = compile(`
type Foo = string;
interface A {
a: Foo;
@range(3, 5)
b: number;
@maxLength(3)
c: Foo;
}
`);
const zz = deserialize(serialize(z));
// console.log(zz.keys());
expect(1).toEqual(1);
});
it("readme-examples-1", function() {
{
const mySchema = compile(`
type Foo = string;
interface A {
@maxLength(4)
a: Foo;
z?: boolean;
}
`);
type Foo = string;
interface A {
a: Foo;
z?: boolean;
}
{
const validated1 = validate({
a: 'x',
b: 3,
}, getType(mySchema, 'A')); // {value: {a: 'x', b: 3}}
expect(validated1).toEqual({value: {a: 'x', b: 3}});
const validated2 = validate({
aa: 'x',
b: 3,
}, getType(mySchema, 'A')); // null
if (validated2 === null) {
expect(1).toEqual(1);
} else {
expect(1).toEqual(0);
}
const ctx3: Partial<ValidationContext> =
{ // To receive the error messages, define the context as a variable.
checkAll: true, // (optional) Set to true to continue validation after the first error.
noAdditionalProps: true, // (optional) Do not allow implicit additional properties.
schema: mySchema, // (optional) Pass "schema" to check for recursive types.
};
const validated3 = validate({
aa: 'x',
b: 3,
}, getType(mySchema, 'A'), ctx3);
if (validated3 === null) {
expect(1).toEqual(1);
// console.log(JSON.stringify(
// ctx3.errors, // error messages
// null, 2));
} else {
expect(1).toEqual(0);
}
}
{
const original = {
a: 'x',
b: 3,
};
const needleType = op.picked(getType(mySchema, 'A'), 'a');
try {
const needle1 = pick(original, needleType); // {a: 'x'}
const unknownInput1: unknown = { // Edit the needle data
...needle1,
a: 'y',
q: 1234,
};
const changed1 = patch(original, unknownInput1, needleType); // {a: 'y', b: 3}
expect(changed1).toEqual({a: 'y', b: 3});
} catch (e) {
expect(1).toEqual(0);
// console.log(e.message);
// console.log(e.ctx?.errors);
}
try {
const needle2 = pick(original, needleType); // {a: 'x'}
const unknownInput2: unknown = { // Edit the needle data
...needle2,
a: 'yyyyy',
q: 1234,
};
const changed1 = patch(original, unknownInput2, needleType); // Throws an error
expect(1).toEqual(0);
} catch (e) {
expect(1).toEqual(1);
// console.log(e.message);
// console.log(e.ctx?.errors);
}
try {
const ctx3: Partial<ValidationContext> =
{ // To receive the error messages, define the context as a variable.
checkAll: true, // (optional) Set to true to continue validation after the first error.
schema: mySchema, // (optional) Pass "schema" to check for recursive types.
};
const needle3 = pick({
aa: 'x',
b: 3,
}, needleType, ctx3); // Throws an error
expect(1).toEqual(0);
} catch (e) {
expect(1).toEqual(1);
// console.log(e.message);
// console.log(e.ctx?.errors);
}
}
{
const unknownInput: unknown = {a: 'x'};
const validated = validate<A>(unknownInput, getType(mySchema, 'A'));
if (validated) {
const validatedInput = validated.value;
expect(validatedInput).toEqual({a: 'x'});
} else {
expect(1).toEqual(0);
}
}
{
interface Store {
baz: A;
}
const store: Store = {
baz: {
a: 'x',
z: false,
}
};
const needleType = op.picked(getType(mySchema, 'A'), 'a');
try {
const needle = pick(store.baz, needleType); // {a: 'x'}
const unknownInput: unknown = { // Edit the needle data
...needle,
a: 'y',
q: 1234,
};
store.baz = patch(store.baz, unknownInput, needleType); // {a: 'y', z: false}
expect(store.baz).toEqual({a: 'y', z: false});
} catch (e) {
expect(1).toEqual(0);
// console.log(e.message);
// console.log(e.ctx?.errors);
}
}
}
});
});