import { assert } from 'chai'; import { create, assign } from '../../../../../../lib/core/object'; import { MetaContext, $CONSTRUCTOR, $NAME } from '../../../../../../lib/experimental/meta/MetaContext'; import { withDump } from '../../../../../../lib/experimental/meta/operations/base/$dump'; import { $SERIALIZE, withSerialize } from '../../../../../../lib/experimental/meta/operations/base/$serialize'; import { withRegisterClass } from '../../../../../../lib/experimental/meta/extensions/registerClass'; import { withRegisterFunction } from '../../../../../../lib/experimental/meta/extensions/registerFunction'; import dumpBuiltins from '../../../../../../lib/experimental/meta/operations/builtins/$dump'; import serializeBuiltins from '../../../../../../lib/experimental/meta/operations/builtins/$serialize'; const serialize = (function () { const context = new (withRegisterFunction(withRegisterClass(withSerialize(withDump(MetaContext)))))(); context.registerAll(dumpBuiltins); context.registerAll(serializeBuiltins); function serialize(obj: T) { return context[$SERIALIZE](obj); }; serialize.registerClass = function (props: any) { return context.registerClass(props); }; serialize.registerFunction = function (props: any) { return context.registerFunction(props); }; return serialize; })(); describe('experimental.meta.operations.builtins.$serialize', () => { describe('string', () => { it('string', () => { const value = 'abc'; const state = serialize(value); assert.strictEqual(typeof state, 'string'); assert.strictEqual(state, 'abc'); }); }); describe('String', () => { it('String', () => { const value = new String('abc'); const state = serialize(value); assert.deepEqual(state, { String: 'abc' }); }); }); describe('number', () => { it('number', () => { const value = 123; const state = serialize(value); assert.strictEqual(typeof state, 'number'); assert.strictEqual(state, 123); }); }); describe('Number', () => { it('Number', () => { const value = new Number(123); const state = serialize(value); assert.deepEqual(state, { Number: 123 }); }); }); describe('boolean', () => { it('boolean', () => { const value = true; const state = serialize(value); assert.strictEqual(typeof state, 'boolean'); assert.strictEqual(state, true); }); }); describe('Boolean', () => { it('Boolean', () => { const value = new Boolean(true); const state = serialize(value); assert.deepEqual(state, { Boolean: true }); }); }); describe('undefined', () => { it('undefined', () => { const value = undefined; const state = serialize(value); assert.deepEqual(state, { undefined: {} }); }); }); describe('null', () => { it('null', () => { const value = null; const state = serialize(value); assert.strictEqual(typeof state, 'object'); assert.strictEqual(state, null); }); }); describe('Object', () => { test.each([ [ {}, {}, (obj: any) => { obj.foo = 'bar'; } ], [ { a: 1, b: 2, c: 3 }, { a: 1, b: 2, c: 3 }, (obj: any) => { delete obj.a; obj.b = -2; } ], [ { 1: 'a' }, { '1': 'a' }, (obj: any) => { obj.foo = 'bar'; } ], [ { p: { x: 1, y: 2 }, foo: 'bar', a: [1, 2, 3] }, { p: { x: 1, y: 2 }, foo: 'bar', a: [1, 2, 3] }, (obj: any) => { obj.p.x = -1; obj.a[0] = -1; } ], ])('Object#%#', (obj: any, result: any, mutate: (obj: any) => void) => { const state = serialize(obj); assert.deepEqual(state, result); mutate(obj); assert.deepEqual(state, result); }); }); describe('object', () => { test.each([ [ {}, {}, (obj: any) => { obj.foo = 'bar'; } ], [ { a: 1, b: 2, c: 3 }, { a: 1, b: 2, c: 3 }, (obj: any) => { delete obj.a; obj.b = -2; } ], [ { 1: 'a' }, { '1': 'a' }, (obj: any) => { obj.foo = 'bar'; } ], [ { p: { x: 1, y: 2 }, foo: 'bar', a: [1, 2, 3] }, { p: { x: 1, y: 2 }, foo: 'bar', a: [1, 2, 3] }, (obj: any) => { obj.p.x = -1; obj.a[0] = -1; } ], ])('object#%#', (objData: any, resultData: any, mutate: (obj: any) => void) => { const obj = assign(create(), objData); const result = { object: resultData }; const state = serialize(obj); assert.deepEqual(state, result); mutate(obj); assert.deepEqual(state, result); }); }); describe('function', () => { it('function', () => { function add(a: number, b: number) { return a + b; } function mul(a: number, b: number) { return a * b; } serialize.registerFunction({ [$CONSTRUCTOR]: add, [$NAME]: 'add' }); assert.throws(() => { serialize(mul); }); const state = serialize(add); assert.deepEqual(state, { add: {} }); }); }); describe('Date', () => { it('Date', () => { const now = Date.now(); const date = new Date(now); const state = serialize(date); assert.deepEqual(state, { Date: now }); date.setTime(now - 100); assert.deepEqual(state, { Date: now }); }); }); describe('Array', () => { test.each([ [ [], [], (array: any[]) => { array.push(1); } ], [ [1, 2, 3], [1, 2, 3], (array: any[]) => { array.splice(1, 1); } ], [ [1, '2', 3], [1, '2', 3], (array: any[]) => { array.splice(1, 1); } ], [ [1, 'foo', [1, 2, 3], { x: 1, y: 2 }], [1, 'foo', [1, 2, 3], { x: 1, y: 2 }], (array: any[]) => { array[2][0] = -1; array[3].x = -1; } ], ])('Array#%#', (array: any[], result: any, mutate: (array: any[]) => void) => { const state = serialize(array); assert.deepEqual(state, result); mutate(array); assert.deepEqual(state, result); }); }); describe('Map', () => { test.each([ [ [], [], (map: Map) => { map.set('foo', 'bar'); } ], [ [['a', 1], ['b', 2], ['c', 3]], [['a', 1], ['b', 2], ['c', 3]], (map: Map) => { map.set('d', 4); map.delete('b'); map.set('a', -1); } ], [ [[1, 'a'], ['1', 'b']], [[1, 'a'], ['1', 'b']], (map: Map) => { map.set('2', 'c'); map.delete(1); } ], [ [['p', { x: 1, y: 2 }], ['foo', 'bar'], ['v', [1, 2, 3]]], [['p', { x: 1, y: 2 }], ['foo', 'bar'], ['v', [1, 2, 3]]], (map: Map) => { const p1 = map.get('p'); p1.x = 3; p1.y = 4; } ], [ [[{ x: 1, y: 2 }, 3], [[1, 2, 3], 6]], [[{ x: 1, y: 2 }, 3], [[1, 2, 3], 6]], (map: Map) => { const keys = [...map.keys()]; keys[0].x = -3; keys[0].y = -2; } ], ])('Map#%#', (mapData: [any, any][], resultData: [any, any][], mutate: (map: Map) => void) => { const map = new Map(mapData); const result = { Map: resultData }; const state = serialize(map); assert.deepEqual(state, result); mutate(map); assert.deepEqual(state, result); }); }); describe('Set', () => { test.each([ [ [], [], (set: Set) => { set.add('foo'); } ], [ ['a', 'b', 'c'], ['a', 'b', 'c'], (set: Set) => { set.add('d'); set.delete('b'); } ], [ ['1', 1, '2'], ['1', 1, '2'], (set: Set) => { set.add(2); set.delete('1'); } ], [ [{ x: 1, y: 2 }, 'foo', [1, 2, 3]], [{ x: 1, y: 2 }, 'foo', [1, 2, 3]], (set: Set) => { const values = [...set.values()]; values[0].x = -1; values[0].y = -2; } ], ])('Set#%#', (setData: any[], resultData: any[], mutate: (set: Set) => void) => { const set = new Set(setData); const result = { Set: resultData }; const state = serialize(set); assert.deepEqual(state, result); mutate(set); assert.deepEqual(state, result); }); }); describe('TypedArrays', () => { describe('Uint8Array', () => { test.each([ [ [], [], (_: Uint8Array) => { } ], [ [11, 21, 31], [11, 21, 31], (array: Uint8Array) => { array[0] = 41; } ] ])('Uint8Array#%#', (arrayData: number[], resultData: number[], mutate: (array: Uint8Array) => void) => { const array = new Uint8Array(arrayData); const result = { Uint8Array: resultData }; const state = serialize(array); assert.deepEqual(state, result); mutate(array); assert.deepEqual(state, result); }); }); }); describe('class', () => { class Point { constructor(public x = 0, public y = 0) { } } class Polygon { constructor(public points: Point[] = []) { } } class Triangle extends Polygon { constructor(p1: Point, p2: Point, p3: Point, public color = 'black') { super([p1, p2, p3]); } } serialize.registerClass({ [$CONSTRUCTOR]: Point, [$NAME]: 'Point' }); serialize.registerClass({ [$CONSTRUCTOR]: Polygon, [$NAME]: 'Polygon' }); serialize.registerClass({ [$CONSTRUCTOR]: Triangle, [$NAME]: 'Triangle' }); describe('Point', () => { test.each([ [ new Point(), { Point: { x: 0, y: 0 } }, (_: Point) => { } ], [ new Point(1, 2), { Point: { x: 1, y: 2 } }, (p: Point) => { p.x = -1; p.y = -2; } ] ])('Point#%#', (point: Point, result: any, mutate: (point: Point) => void) => { const state = serialize(point); assert.deepEqual(state, result); mutate(point); assert.deepEqual(state, result); }); }); describe('Polygon', () => { test.each([ [ new Polygon(), { Polygon: { points: [] } }, (_: Polygon) => { } ], [ new Polygon([new Point(1, 2), new Point(2, 3), new Point(3, 1)]), { Polygon: { points: [{ Point: { x: 1, y: 2 } }, { Point: { x: 2, y: 3 } }, { Point: { x: 3, y: 1 } }] } }, (p: Polygon) => { p.points[0].x = -1; p.points[0].y = -2; p.points.splice(1, 1); } ] ])('Polygon#%#', (poly: Polygon, result: any, mutate: (poly: Polygon) => void) => { const state = serialize(poly); assert.deepEqual(state, result); mutate(poly); assert.deepEqual(state, result); }); }); describe('Triangle', () => { test.each([ [ new Triangle(new Point(1, 2), new Point(2, 3), new Point(3, 1), 'red'), { Triangle: { points: [{ Point: { x: 1, y: 2 } }, { Point: { x: 2, y: 3 } }, { Point: { x: 3, y: 1 } }], color: 'red' } }, (p: Triangle) => { p.points[0].x = -1; p.points[0].y = -2; p.points.splice(1, 1); } ] ])('Triangle#%#', (tri: Triangle, result: any, mutate: (tri: Triangle) => void) => { const state = serialize(tri); assert.deepEqual(state, result); mutate(tri); assert.deepEqual(state, result); }); }); }); });