import * as t from ".."; describe("converting to typescript", () => { test("converts to typescript", () => { expect(t.toTypescript(t.subtype({ hi: t.str, world: t.subtype({ foo: t.num, }), }))).toEqual("{\n hi: string,\n world: {\n foo: number,\n },\n}"); }); test("puts value comments above the line", () => { expect(t.toTypescript(t.subtype({ foo: t.subtype({ bar: t.str.comment("a comment"), }), }))).toEqual("{\n foo: {\n // a comment\n bar: string,\n },\n}"); }); test("puts multi-line value comments above the line with correct indentation", () => { expect(t.toTypescript(t.subtype({ foo: t.subtype({ bar: t.str.comment("a comment\nabout this"), }), }))).toEqual("{\n foo: {\n /*\n * a comment\n * about this\n */\n bar: string,\n },\n}"); }); test("separates out fields in the middle of a fieldset that have comments", () => { expect(t.toTypescript(t.subtype({ a: t.str, b: t.str.comment("sup"), c: t.str, }))).toEqual("{\n a: string,\n\n // sup\n b: string,\n\n c: string,\n}") }); test("doesn't double-separate fields in the middle of a fieldset that have comments", () => { expect(t.toTypescript(t.subtype({ a: t.str, b: t.str.comment("sup"), c: t.str.comment("yo"), }))).toEqual("{\n a: string,\n\n // sup\n b: string,\n\n // yo\n c: string,\n}") }); test("combines comments and validations into a single comment block", () => { expect(t.toTypescript(t.subtype({ a: t.num.validate("Must be between 1-20.", num => num >= 1 && num <= 20).comment("Level.") }))).toEqual("{\n /*\n * Level.\n * Must be between 1-20.\n */\n a: number,\n}"); }); }); describe("subtype", () => { test("accepts exact matches", () => { const check = t.subtype({ hi: t.str, }); check.assert({ hi: "world" }); }); test("accepts supertypes", () => { const check = t.subtype({ hi: t.str, }); check.assert({ hi: "world", foo: "bar" }); }); test("allows optional keys to be missing", () => { const check = t.subtype({ hi: t.str, opt: t.optional(t.bool), }); check.assert({ hi: "world" }) }); test("rejects allow-missing keys that exist, but are undefined", () => { const check = t.subtype({ hi: t.str, opt: t.allowMissing(t.bool), }); expect(() => { check.assert({ hi: "world", opt: undefined }) }).toThrow(); }); test("allows optional keys that exist, but are undefined", () => { const check = t.subtype({ hi: t.str, opt: t.optional(t.bool), }); check.assert({ hi: "world", opt: undefined }) }); test("type inference allows omitting optional keys", () => { const check = t.subtype({ hi: t.str, opt: t.optional(t.bool), }); type Derpus = t.GetType; const wat: Derpus = { hi: 'dog', }; check.assert(wat); }); test("type inference allows omitting optional keys with .t", () => { const check = t.subtype({ email: t.str, name: t.optional(t.str), }); check.literal({ email: 'bob@example.com' }); }); test("slice omits optional keys that are not defined", () => { const check = t.subtype({ email: t.str, name: t.optional(t.str), }); const result = check.slice({ email: 'bob@example.com' }); expect(Object.keys(result)).toEqual(['email']); }); test("rejects subtypes", () => { const check = t.subtype({ hi: t.str, foo: t.str, }); expect(() => { check.assert({ hi: "world" }); }).toThrow(); }); test("rejects non-matching values", () => { const check = t.subtype({ hi: t.str, }); expect(() => { check.assert({ hi: 5 }); }).toThrow(); }); test("rejects non-objects", () => { const check = t.subtype({}); expect(() => { check.assert(false); }).toThrow(); }); test("rejects null", () => { const check = t.subtype({}); expect(() => { check.assert(null); }).toThrow(); }); test("rejects arrays", () => { const check = t.subtype({}); expect(() => { check.assert([]); }).toThrow(); }); }); describe("exact", () => { test("accepts exact matches", () => { const check = t.exact({ hi: t.str, }); check.assert({ hi: "world" }); }); test("allows optional keys to be missing", () => { const check = t.exact({ hi: t.str, opt: t.allowMissing(t.bool), }); check.assert({ hi: "world" }); }); test("allows optional keys to be missing", () => { const check = t.exact({ hi: t.str, opt: t.optional(t.bool), }); check.assert({ hi: "world" }); }); test("rejects allow-missing keys that exist, but are undefined", () => { const check = t.exact({ hi: t.str, opt: t.allowMissing(t.bool), }); expect(() => { check.assert({ hi: "world", opt: undefined }) }).toThrow(); }); test("rejects optional keys that exist, but are undefined", () => { const check = t.exact({ hi: t.str, opt: t.optional(t.bool), }); check.assert({ hi: "world", opt: undefined }) }); test("rejects supertypes", () => { const check = t.exact({ hi: t.str, }); expect(() => { check.assert({ hi: "world", foo: "bar" }); }).toThrow(); }); test("rejects subtypes", () => { const check = t.exact({ hi: t.str, foo: t.str, }); expect(() => { check.assert({ hi: "world" }); }).toThrow(); }); test("rejects non-objects", () => { const check = t.exact({}); expect(() => { check.assert(null); }).toThrow(); }); test("rejects arrays", () => { const check = t.exact({}); expect(() => { check.assert([]); }).toThrow(); }); }); describe('slice', () => { test('slices out only the known keys', () => { const check = t.subtype({ foo: t.str, }); expect(check.slice({ foo: "bar", hello: "world", })).toEqual({ foo: "bar", }); }); test('throws errors when the type mismatches', () => { const check = t.subtype({ foo: t.str, }); expect(() => { check.slice({}); }).toThrow(); }); });