import { describe, expect, it, mock, } from "https://code.thunderk.net/typescript/devtools/raw/1.3.0/testing.ts"; import { Serializer } from "./serializer.ts"; class TestSerializerObj1 { a: number; constructor(a = 0) { this.a = a; } } class TestSerializerObj2 { a = () => 1; b = [(obj: any) => 2]; } class TestSerializerObj3 { a = [1, 2]; postUnserialize() { this.a.push(3); } } class TestSerializerObj4 extends TestSerializerObj1 { } const TEST_NS = { TestSerializerObj1, TestSerializerObj2, TestSerializerObj3, }; const TEST_NS_FLAT = [ TestSerializerObj1, TestSerializerObj2, TestSerializerObj3, ]; const TEST_NS_RENAME = { ob1: TestSerializerObj1, ob2: TestSerializerObj2, ob3: TestSerializerObj3, }; function checkReversability( obj: any, namespace: any = TEST_NS, equality?: (loaded: any, orig: any) => void, ): any { var serializer = new Serializer(namespace); var data = serializer.serialize(obj); serializer = new Serializer(namespace); var loaded = serializer.unserialize(data); if (equality) { equality(loaded, obj); } else { expect(loaded).toEqual(obj); } return loaded; } describe(Serializer, () => { it("serializes simple objects", () => { var obj = { "a": 5, "b": null, "c": [{ "a": 2 }, "test"], "d": new Set([1, 4]), "e": new Map([["z", 8], ["t", 2]]), }; const result = checkReversability(obj); expect(result["a"]).toBe(5); expect(result["b"]).toBe(null); expect(result["c"][0]["a"]).toBe(2); expect(result["d"].has(1)).toBe(true); expect(result["d"].has(2)).toBe(false); expect(result["e"].get("z")).toBe(8); expect(result["e"].get("k")).toBeUndefined(); checkReversability(new Set(["a", new Set([1, "b"])])); checkReversability(new Map([["a", new Map([[1, "test"]])]])); }); it("restores objects constructed from class", () => { let loaded = checkReversability(new TestSerializerObj1(5)); expect(loaded.a).toBe(5); expect(loaded).toBeInstanceOf(TestSerializerObj1); loaded = checkReversability(new TestSerializerObj1(5), TEST_NS_FLAT); expect(loaded.a).toBe(5); expect(loaded).toBeInstanceOf(TestSerializerObj1); loaded = checkReversability(new TestSerializerObj1(5), TEST_NS_RENAME); expect(loaded.a).toBe(5); expect(loaded).toBeInstanceOf(TestSerializerObj1); }); it("serializes reference to class type", () => { let loaded = checkReversability(TestSerializerObj1); expect(loaded).toBe(TestSerializerObj1); }); it("stores one version of the same object", () => { var a = new TestSerializerObj1(8); var b = new TestSerializerObj1(8); var c = { "r": a, "s": ["test", a], "t": a, "u": b, }; var loaded = checkReversability(c); expect(loaded.t).toBe(loaded.r); expect(loaded.s[1]).toBe(loaded.r); expect(loaded.u).not.toBe(loaded.r); }); it("handles circular references", () => { var a: any = { b: {} }; a.b.c = a; checkReversability(a, undefined, (loaded) => { expect(Object.keys(loaded)).toEqual(["b"]); expect(Object.keys(loaded.b)).toEqual(["c"]); expect(Object.keys(loaded.b.c)).toEqual(["b"]); expect(Object.keys(loaded.b.c.b)).toEqual(["c"]); expect(loaded.b.c).toBe(loaded); expect(loaded.b.c.b).toBe(loaded.b); }); }); it("ignores some classes", () => { let serializer = new Serializer(TEST_NS); serializer.addIgnoredClass("TestSerializerObj1"); let data = serializer.serialize({ a: 5, b: new TestSerializerObj1() }); let loaded = serializer.unserialize(data); expect(loaded).toEqual({ a: 5, b: undefined }); serializer = new Serializer(TEST_NS); serializer.addIgnoredClass(TestSerializerObj1); data = serializer.serialize({ a: 5, b: new TestSerializerObj1() }); loaded = serializer.unserialize(data); expect(loaded).toEqual({ a: 5, b: undefined }); serializer = new Serializer(TEST_NS_RENAME); serializer.addIgnoredClass(TestSerializerObj1); data = serializer.serialize({ a: 5, b: new TestSerializerObj1() }); loaded = serializer.unserialize(data); expect(loaded).toEqual({ a: 5, b: undefined }); }); it("ignores functions", () => { let serializer = new Serializer(TEST_NS); let data = serializer.serialize({ obj: new TestSerializerObj2() }); let loaded = serializer.unserialize(data); let expected: any = new TestSerializerObj2(); expected.a = undefined; expected.b[0] = undefined; expect(loaded).toEqual({ obj: expected }); }); it("calls specific postUnserialize", () => { let serializer = new Serializer(TEST_NS); let data = serializer.serialize({ obj: new TestSerializerObj3() }); let loaded = serializer.unserialize(data); let expected = new TestSerializerObj3(); expected.a = [1, 2, 3]; expect(loaded).toEqual({ obj: expected }); }); it("handles missing classes", () => { mock(console, "error", undefined, (mock_error) => { let serializer = new Serializer(TEST_NS); let data = serializer.serialize({ obj: new TestSerializerObj4() }); let loaded = serializer.unserialize(data); expect(loaded).toEqual({ obj: { a: 0 } }); expect(mock_error).toHaveBeenCalledWith( "Can't find class", "TestSerializerObj4", ); }); }); it("uses namespace alias to protect from property mangling", () => { const data = { a: new TestSerializerObj1(1), b: new TestSerializerObj1(2), c: [new TestSerializerObj1(3)], }; const serializer1 = new Serializer(TEST_NS); const serializer2 = new Serializer(TEST_NS_RENAME); const dumped1 = serializer1.serialize(data); const dumped2 = serializer2.serialize(data); expect(dumped1.length).toBeGreaterThan(dumped2.length); expect(serializer1.unserialize(dumped1)).toEqual(data); expect(serializer2.unserialize(dumped2)).toEqual(data); }); });