tynder
Version:
TypeScript friendly Data validator for JavaScript.
695 lines (664 loc) • 23.2 kB
text/typescript
import { TypeAssertion,
ValidationContext } from '../types';
import { validate,
getType } from '../validator';
import { pick,
patch } from '../picker';
import { compile } from '../compiler';
import { generateTypeScriptCode } from '../codegen';
import { serialize,
deserialize } from '../serializer';
describe("compiler-9", function() {
it("compiler-import-statement-1", function() {
const schemas = [compile(`
import from 'foo';
import * as foo from 'foo';
import {\na, b as bb} from 'foo';
`)];
{
expect(Array.from(schemas[0].keys())).toEqual([
'__$$$gensym_0$$$__',
'__$$$gensym_1$$$__',
'__$$$gensym_2$$$__',
]);
}
for (const schema of schemas) {
{
const rhs: TypeAssertion = {
kind: 'never',
passThruCodeBlock: `import from 'foo';`,
};
const ty = getType(schema, '__$$$gensym_0$$$__');
expect(ty).toEqual(rhs);
}
{
const rhs: TypeAssertion = {
kind: 'never',
passThruCodeBlock: `import * as foo from 'foo';`,
};
const ty = getType(schema, '__$$$gensym_1$$$__');
expect(ty).toEqual(rhs);
}
{
const rhs: TypeAssertion = {
kind: 'never',
passThruCodeBlock: `import {\na, b as bb} from 'foo';`,
};
const ty = getType(schema, '__$$$gensym_2$$$__');
expect(ty).toEqual(rhs);
}
{
expect(generateTypeScriptCode(schema).trim()).toEqual(
`import from 'foo';\n\n` +
`import * as foo from 'foo';\n\n` +
`import {\na, b as bb} from 'foo';`
);
}
}
});
it("compiler-declare-statement-1", function() {
const schemas = [compile(`
declare var x: number;
declare let y: number[];
declare const z: \nnumber[][];
`)];
{
expect(Array.from(schemas[0].keys())).toEqual([
'__$$$gensym_0$$$__',
'__$$$gensym_1$$$__',
'__$$$gensym_2$$$__',
]);
}
for (const schema of schemas) {
{
const rhs: TypeAssertion = {
kind: 'never',
passThruCodeBlock: `declare var x: number;`,
};
const ty = getType(schema, '__$$$gensym_0$$$__');
expect(ty).toEqual(rhs);
}
{
const rhs: TypeAssertion = {
kind: 'never',
passThruCodeBlock: `declare let y: number[];`,
};
const ty = getType(schema, '__$$$gensym_1$$$__');
expect(ty).toEqual(rhs);
}
{
const rhs: TypeAssertion = {
kind: 'never',
passThruCodeBlock: `declare const z: \nnumber[][];`,
};
const ty = getType(schema, '__$$$gensym_2$$$__');
expect(ty).toEqual(rhs);
}
{
expect(generateTypeScriptCode(schema).trim()).toEqual(
`declare var x: number;\n\n` +
`declare let y: number[];\n\n` +
`declare const z: \nnumber[][];`
);
}
}
});
it("compiler-declare-statement-1b", function() {
const schemas = [compile(`
/** comment1 */
declare var x: number;
/** comment2 */
declare let y: number[];
/** comment3 */
declare const z: \nnumber[][];
`)];
{
expect(Array.from(schemas[0].keys())).toEqual([
'__$$$gensym_0$$$__',
'__$$$gensym_1$$$__',
'__$$$gensym_2$$$__',
]);
}
for (const schema of schemas) {
{
const rhs: TypeAssertion = {
kind: 'never',
passThruCodeBlock: `declare var x: number;`,
docComment: 'comment1',
};
const ty = getType(schema, '__$$$gensym_0$$$__');
expect(ty).toEqual(rhs);
}
{
const rhs: TypeAssertion = {
kind: 'never',
passThruCodeBlock: `declare let y: number[];`,
docComment: 'comment2',
};
const ty = getType(schema, '__$$$gensym_1$$$__');
expect(ty).toEqual(rhs);
}
{
const rhs: TypeAssertion = {
kind: 'never',
passThruCodeBlock: `declare const z: \nnumber[][];`,
docComment: 'comment3',
};
const ty = getType(schema, '__$$$gensym_2$$$__');
expect(ty).toEqual(rhs);
}
{
expect(generateTypeScriptCode(schema).trim()).toEqual(
`/** comment1 */\ndeclare var x: number;\n\n` +
`/** comment2 */\ndeclare let y: number[];\n\n` +
`/** comment3 */\ndeclare const z: \nnumber[][];`
);
}
}
});
it("compiler-declare-statement-2", function() {
const schemas = [compile(`
export declare var x: number;
export declare let y: number[];
export declare const z: \nnumber[][];
`)];
{
expect(Array.from(schemas[0].keys())).toEqual([
'__$$$gensym_0$$$__',
'__$$$gensym_1$$$__',
'__$$$gensym_2$$$__',
]);
}
for (const schema of schemas) {
{
const rhs: TypeAssertion = {
kind: 'never',
passThruCodeBlock: `export declare var x: number;`,
};
const ty = getType(schema, '__$$$gensym_0$$$__');
expect(ty).toEqual(rhs);
}
{
const rhs: TypeAssertion = {
kind: 'never',
passThruCodeBlock: `export declare let y: number[];`,
};
const ty = getType(schema, '__$$$gensym_1$$$__');
expect(ty).toEqual(rhs);
}
{
const rhs: TypeAssertion = {
kind: 'never',
passThruCodeBlock: `export declare const z: \nnumber[][];`,
};
const ty = getType(schema, '__$$$gensym_2$$$__');
expect(ty).toEqual(rhs);
}
{
expect(generateTypeScriptCode(schema).trim()).toEqual(
`export declare var x: number;\n\n` +
`export declare let y: number[];\n\n` +
`export declare const z: \nnumber[][];`
);
}
}
});
it("compiler-declare-statement-2b", function() {
const schemas = [compile(`
/** comment1 */
export declare var x: number;
/** comment2 */
export declare let y: number[];
/** comment3 */
export declare const z: \nnumber[][];
`)];
{
expect(Array.from(schemas[0].keys())).toEqual([
'__$$$gensym_0$$$__',
'__$$$gensym_1$$$__',
'__$$$gensym_2$$$__',
]);
}
for (const schema of schemas) {
{
const rhs: TypeAssertion = {
kind: 'never',
passThruCodeBlock: `export declare var x: number;`,
docComment: 'comment1',
};
const ty = getType(schema, '__$$$gensym_0$$$__');
expect(ty).toEqual(rhs);
}
{
const rhs: TypeAssertion = {
kind: 'never',
passThruCodeBlock: `export declare let y: number[];`,
docComment: 'comment2',
};
const ty = getType(schema, '__$$$gensym_1$$$__');
expect(ty).toEqual(rhs);
}
{
const rhs: TypeAssertion = {
kind: 'never',
passThruCodeBlock: `export declare const z: \nnumber[][];`,
docComment: 'comment3',
};
const ty = getType(schema, '__$$$gensym_2$$$__');
expect(ty).toEqual(rhs);
}
{
expect(generateTypeScriptCode(schema).trim()).toEqual(
`/** comment1 */\nexport declare var x: number;\n\n` +
`/** comment2 */\nexport declare let y: number[];\n\n` +
`/** comment3 */\nexport declare const z: \nnumber[][];`
);
}
}
});
it("compiler-directives-1", function() {
const schemas = [compile(`
/// @tynder-external P, Q, R
// @tynder-external S
type X = Q;
interface Y { a: R; }
`), compile(`
/* @tynder-external P, Q, R */
/* @tynder-external
S
*/
type X = Q;
interface Y { a: R; }
`), compile(`
external P, Q, R;
external S;
type X = Q;
interface Y { a: R; }
`)];
{
expect(Array.from(schemas[0].keys())).toEqual([
'P', 'Q', 'R', 'S', 'X', 'Y',
]);
expect(Array.from(schemas[1].keys())).toEqual([
'P', 'Q', 'R', 'S', 'X', 'Y',
]);
}
for (const schema of schemas) {
{
const rhs: TypeAssertion = {
name: 'P',
typeName: 'P',
kind: 'any',
noOutput: true,
};
const ty = getType(schema, 'P');
expect(ty).toEqual(rhs);
expect(validate<any>(3, ty)).toEqual({value: 3});
}
{
const rhs: TypeAssertion = {
name: 'X',
typeName: 'X',
originalTypeName: 'Q',
kind: 'any',
};
const ty = getType(schema, 'X');
expect(ty).toEqual(rhs);
expect(validate<any>(3, ty)).toEqual({value: 3});
}
{
const rhs: TypeAssertion = {
name: 'Y',
typeName: 'Y',
kind: 'object',
members: [['a', {
name: 'a',
typeName: 'R',
kind: 'any',
}]],
};
const ty = getType(schema, 'Y');
expect(ty).toEqual(rhs);
expect(validate<any>({a: 3}, ty)).toEqual({value: {a: 3}});
expect(validate<any>({b: 3}, ty)).toEqual(null);
}
}
});
it("compiler-primitive", function() {
const schema = compile(`
type FooA = any;
type FooB = unknown;
type FooC = never;
`);
{
expect(Array.from(schema.keys())).toEqual([
'FooA', 'FooB', 'FooC',
]);
}
{
const rhs: TypeAssertion = {
name: 'FooA',
typeName: 'FooA',
kind: 'any',
};
const ty = getType(schema, 'FooA');
expect(ty).toEqual(rhs);
expect(validate<any>(0, ty)).toEqual({value: 0});
expect(validate<any>(1, ty)).toEqual({value: 1});
expect(validate<any>(BigInt(0), ty)).toEqual({value: BigInt(0)});
expect(validate<any>(BigInt(1), ty)).toEqual({value: BigInt(1)});
expect(validate<any>('', ty)).toEqual({value: ''});
expect(validate<any>('1', ty)).toEqual({value: '1'});
expect(validate<any>(false, ty)).toEqual({value: false});
expect(validate<any>(true, ty)).toEqual({value: true});
expect(validate<any>(null, ty)).toEqual({value: null});
expect(validate<any>(void 0, ty)).toEqual({value: void 0});
expect(validate<any>({}, ty)).toEqual({value: {}});
expect(validate<any>([], ty)).toEqual({value: []});
expect(validate<any>(3, ty)).toEqual({value: 3});
expect(validate<any>(BigInt(7), ty)).toEqual({value: BigInt(7)});
expect(validate<any>('XB', ty)).toEqual({value: 'XB'});
expect(validate<any>(true, ty)).toEqual({value: true});
}
{
const rhs: TypeAssertion = {
name: 'FooB',
typeName: 'FooB',
kind: 'unknown',
};
const ty = getType(schema, 'FooB');
expect(ty).toEqual(rhs);
expect(validate<any>(0, ty)).toEqual({value: 0});
expect(validate<any>(1, ty)).toEqual({value: 1});
expect(validate<any>(BigInt(0), ty)).toEqual({value: BigInt(0)});
expect(validate<any>(BigInt(1), ty)).toEqual({value: BigInt(1)});
expect(validate<any>('', ty)).toEqual({value: ''});
expect(validate<any>('1', ty)).toEqual({value: '1'});
expect(validate<any>(false, ty)).toEqual({value: false});
expect(validate<any>(true, ty)).toEqual({value: true});
expect(validate<any>(null, ty)).toEqual({value: null});
expect(validate<any>(void 0, ty)).toEqual({value: void 0});
expect(validate<any>({}, ty)).toEqual({value: {}});
expect(validate<any>([], ty)).toEqual({value: []});
expect(validate<any>(3, ty)).toEqual({value: 3});
expect(validate<any>(BigInt(7), ty)).toEqual({value: BigInt(7)});
expect(validate<any>('XB', ty)).toEqual({value: 'XB'});
expect(validate<any>(true, ty)).toEqual({value: true});
}
{
const rhs: TypeAssertion = {
name: 'FooC',
typeName: 'FooC',
kind: 'never',
};
const ty = getType(schema, 'FooC');
expect(ty).toEqual(rhs);
expect(validate<string>(0, ty)).toEqual(null);
expect(validate<string>(1, ty)).toEqual(null);
expect(validate<string>(BigInt(0), ty)).toEqual(null);
expect(validate<string>(BigInt(1), ty)).toEqual(null);
expect(validate<string>('', ty)).toEqual(null);
expect(validate<string>('1', ty)).toEqual(null);
expect(validate<string>(false, ty)).toEqual(null);
expect(validate<string>(true, ty)).toEqual(null);
expect(validate<string>(null, ty)).toEqual(null);
expect(validate<string>(void 0, ty)).toEqual(null);
expect(validate<string>({}, ty)).toEqual(null);
expect(validate<string>([], ty)).toEqual(null);
expect(validate<string>(3, ty)).toEqual(null);
expect(validate<string>(BigInt(7), ty)).toEqual(null);
expect(validate<string>('XB', ty)).toEqual(null);
expect(validate<string>(true, ty)).toEqual(null);
}
});
it("compiler-deep-cherrypick-patch-1", function() {
const schema = compile(`
interface A {
a1: string;
a2?: number;
a3: string[];
}
interface B {
b1: boolean;
b2: A;
}
interface C {
c1: string;
c2: B;
c3?: A;
}
`);
const ty = getType(schema, 'C');
const original = {
c1: 'ccccc',
c2: {
b1: true,
b2: {
a1: 'bbbbb',
a2: 22,
a3: ['1', '2'],
a4: true,
},
b3: '33333',
},
c3: {
a1: 'aaaaa',
a2: 2,
a3: ['11', '12'],
a4: false,
},
c4: '44444',
};
const needle = pick(original, ty);
expect(needle).toEqual({
c1: 'ccccc',
c2: {
b1: true,
b2: {
a1: 'bbbbb',
a2: 22,
a3: ['1', '2'],
},
},
c3: {
a1: 'aaaaa',
a2: 2,
a3: ['11', '12'],
},
} as any);
// no changes
const patched = patch(original, needle, ty);
expect(patched).toEqual({
c1: 'ccccc',
c2: {
b1: true,
b2: {
a1: 'bbbbb',
a2: 22,
a3: ['1', '2'],
a4: true,
},
b3: '33333',
},
c3: {
a1: 'aaaaa',
a2: 2,
a3: ['11', '12'],
a4: false,
},
c4: '44444',
} as any);
});
it("compiler-deep-cherrypick-patch-2", function() {
const schema = compile(`
interface A {
a1: string;
a2?: number;
a3: string[];
}
interface B {
b1: boolean;
b2: A;
}
interface C {
c1: string;
c2: B;
c3?: A;
}
`);
const ty = getType(schema, 'C');
const original = {
c1: 'ccccc',
c2: {
b1: true,
b2: {
a1: 'bbbbb',
a2: 22,
a3: ['1', '2'],
a4: true,
},
b3: '33333',
},
c3: {
a1: 'aaaaa',
a2: 2,
a3: ['11', '12'],
a4: false,
},
c4: '44444',
};
const needle = pick(original, ty);
expect(needle).toEqual({
c1: 'ccccc',
c2: {
b1: true,
b2: {
a1: 'bbbbb',
a2: 22,
a3: ['1', '2'],
},
},
c3: {
a1: 'aaaaa',
a2: 2,
a3: ['11', '12'],
},
} as any);
needle.c1 = '!ccccc!';
needle.c4 = '!44444!';
(needle as any).c5 = true;
(needle as any).c2.b2.a3 = ['!1!'];
(needle as any).c2.b2.a4 = false;
(needle as any).c2.b2.a5 = 999;
(needle as any).c3.a4 = true;
const patched = patch(original, needle, ty);
expect(patched).toEqual({
c1: '!ccccc!',
c2: {
b1: true,
b2: {
a1: 'bbbbb',
a2: 22,
a3: ['!1!'],
a4: true,
},
b3: '33333',
},
c3: {
a1: 'aaaaa',
a2: 2,
a3: ['11', '12'],
a4: false,
},
c4: '44444',
} as any);
});
it("compiler-deep-cherrypick-patch-3", function() {
const schema = compile(`
interface A {
a1: string;
a2?: number;
a3: string[];
[propNames: string]: any;
}
interface B {
b1: boolean;
b2: A;
}
interface C {
c1: string;
c2: B;
c3?: A;
}
`);
const ty = getType(schema, 'C');
const original = {
c1: 'ccccc',
c2: {
b1: true,
b2: {
a1: 'bbbbb',
a2: 22,
a3: ['1', '2'],
a4: true,
},
b3: '33333',
},
c3: {
a1: 'aaaaa',
a2: 2,
a3: ['11', '12'],
a4: false,
},
c4: '44444',
};
const needle = pick(original, ty);
expect(needle).toEqual({
c1: 'ccccc',
c2: {
b1: true,
b2: {
a1: 'bbbbb',
a2: 22,
a3: ['1', '2'],
a4: true,
},
},
c3: {
a1: 'aaaaa',
a2: 2,
a3: ['11', '12'],
a4: false,
},
} as any);
needle.c1 = '!ccccc!';
needle.c4 = '!44444!';
(needle as any).c5 = true;
(needle as any).c2.b2.a3 = ['!1!'];
(needle as any).c2.b2.a4 = false;
(needle as any).c2.b2.a5 = 999;
(needle as any).c3.a4 = true;
const patched = patch(original, needle, ty);
expect(patched).toEqual({
c1: '!ccccc!',
c2: {
b1: true,
b2: {
a1: 'bbbbb',
a2: 22,
a3: ['!1!'],
a4: false,
a5: 999,
},
b3: '33333',
},
c3: {
a1: 'aaaaa',
a2: 2,
a3: ['11', '12'],
a4: true,
},
c4: '44444',
} as any);
});
});