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(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(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({a: 3}, ty)).toEqual({value: {a: 3}}); expect(validate({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(0, ty)).toEqual({value: 0}); expect(validate(1, ty)).toEqual({value: 1}); expect(validate(BigInt(0), ty)).toEqual({value: BigInt(0)}); expect(validate(BigInt(1), ty)).toEqual({value: BigInt(1)}); expect(validate('', ty)).toEqual({value: ''}); expect(validate('1', ty)).toEqual({value: '1'}); expect(validate(false, ty)).toEqual({value: false}); expect(validate(true, ty)).toEqual({value: true}); expect(validate(null, ty)).toEqual({value: null}); expect(validate(void 0, ty)).toEqual({value: void 0}); expect(validate({}, ty)).toEqual({value: {}}); expect(validate([], ty)).toEqual({value: []}); expect(validate(3, ty)).toEqual({value: 3}); expect(validate(BigInt(7), ty)).toEqual({value: BigInt(7)}); expect(validate('XB', ty)).toEqual({value: 'XB'}); expect(validate(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(0, ty)).toEqual({value: 0}); expect(validate(1, ty)).toEqual({value: 1}); expect(validate(BigInt(0), ty)).toEqual({value: BigInt(0)}); expect(validate(BigInt(1), ty)).toEqual({value: BigInt(1)}); expect(validate('', ty)).toEqual({value: ''}); expect(validate('1', ty)).toEqual({value: '1'}); expect(validate(false, ty)).toEqual({value: false}); expect(validate(true, ty)).toEqual({value: true}); expect(validate(null, ty)).toEqual({value: null}); expect(validate(void 0, ty)).toEqual({value: void 0}); expect(validate({}, ty)).toEqual({value: {}}); expect(validate([], ty)).toEqual({value: []}); expect(validate(3, ty)).toEqual({value: 3}); expect(validate(BigInt(7), ty)).toEqual({value: BigInt(7)}); expect(validate('XB', ty)).toEqual({value: 'XB'}); expect(validate(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(0, ty)).toEqual(null); expect(validate(1, ty)).toEqual(null); expect(validate(BigInt(0), ty)).toEqual(null); expect(validate(BigInt(1), ty)).toEqual(null); expect(validate('', ty)).toEqual(null); expect(validate('1', ty)).toEqual(null); expect(validate(false, ty)).toEqual(null); expect(validate(true, ty)).toEqual(null); expect(validate(null, ty)).toEqual(null); expect(validate(void 0, ty)).toEqual(null); expect(validate({}, ty)).toEqual(null); expect(validate([], ty)).toEqual(null); expect(validate(3, ty)).toEqual(null); expect(validate(BigInt(7), ty)).toEqual(null); expect(validate('XB', ty)).toEqual(null); expect(validate(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); }); });