UNPKG

@civ-clone/core-data-object

Version:

A data exchange object for converting a subset of a class instances to plain JSON.

347 lines (265 loc) 7.91 kB
import AdditionalData from '../AdditionalData'; import AdditionalDataRegistry from '../AdditionalDataRegistry'; import DataObject from '../DataObject'; import EntityRegistry from '@civ-clone/core-registry/EntityRegistry'; import { expect } from 'chai'; import reconstituteData from '../lib/reconstituteData'; import { IConstructor } from '@civ-clone/core-registry/Registry'; class A extends DataObject { a: string = 'a'; b: string = 'b'; c: string = 'c'; constructor() { super(); this.addKey('a', 'c'); } } class B extends DataObject { a: A; b: A[]; constructor(a: A, ...b: A[]) { super(); this.addKey('a', 'b'); this.a = a; this.b = b; } } class C extends DataObject { a: ARegistry; constructor(a: ARegistry) { super(); this.addKey('a'); this.a = a; } } class D extends DataObject { #a: A; constructor(a: A) { super(); this.addKey('a'); this.#a = a; } a() { return this.#a; } } class E extends DataObject { #a: typeof A = A; constructor() { super(); this.addKey('a'); } a() { return this.#a; } } class F extends DataObject { a: Object = { a: true, b: 2, c: 'C', d: D, e: { a: new A(), }, }; constructor() { super(); this.addKey('a'); } } class G extends DataObject { #a: A[]; #b: B; constructor(b: B, ...a: A[]) { super(); this.#a = a; this.#b = b; this.addKey('a', 'b'); } a(): A[] { return this.#a; } b(): B { return this.#b; } } class ARegistry extends EntityRegistry<A> { constructor() { super(A); } } describe('DataObject', (): void => { it('should return expected keys', (): void => { const a = new A(), plainA = reconstituteData(a.toPlainObject()); expect(plainA).to.a('object').keys('_', '__', 'id', 'a', 'c'); expect(plainA._).to.equal('A'); expect(plainA.a).to.equal('a'); expect(plainA.c).to.equal('c'); }); it('should return nested DataObjects', (): void => { const b = new B(new A(), new A(), new A()), plainB = reconstituteData(b.toPlainObject()); expect(plainB).to.a('object').keys('_', '__', 'id', 'a', 'b'); expect(plainB.a).to.a('object').keys('_', '__', 'id', 'a', 'c'); expect(plainB.b).to.a('array').lengthOf(2); expect(plainB.b[0]).to.a('object').keys('_', '__', 'id', 'a', 'c'); expect(plainB.b[1]).to.a('object').keys('_', '__', 'id', 'a', 'c'); }); it('should return entries from Registries', (): void => { const aRegistry = new ARegistry(), c = new C(aRegistry), plainC = reconstituteData(c.toPlainObject()); expect(plainC).to.a('object').keys('_', '__', 'id', 'a'); expect(plainC.a).to.a('array').lengthOf(0); aRegistry.register(new A(), new A(), new A()); expect(aRegistry.entries().length).to.equal(3); expect(aRegistry.length).to.equal(3); const updatedPlainC = reconstituteData(c.toPlainObject()); expect(updatedPlainC.a).to.a('array').lengthOf(3); expect(updatedPlainC.a[0]).to.a('object').keys('_', '__', 'id', 'a', 'c'); expect(updatedPlainC.a[1]).to.a('object').keys('_', '__', 'id', 'a', 'c'); expect(updatedPlainC.a[2]).to.a('object').keys('_', '__', 'id', 'a', 'c'); }); it('should return values from getters', (): void => { const d = new D(new A()), plainD = reconstituteData(d.toPlainObject()); expect(plainD).to.a('object').keys('_', '__', 'id', 'a'); expect(plainD.a).to.a('object').keys('_', '__', 'id', 'a', 'c'); }); it('should return function name only from raw functions', (): void => { const e = new E(), plainE = reconstituteData(e.toPlainObject()); expect(plainE).to.a('object').keys('_', '__', 'id', 'a'); expect(plainE.a).to.a('object').keys('_', '__'); expect(plainE.a._).to.equal('A'); }); it('should return correctly handle nested objects', (): void => { const f = new F(), plainF = reconstituteData(f.toPlainObject()); expect(plainF).to.a('object').keys('_', '__', 'id', 'a'); expect(plainF.a).to.a('object').keys('a', 'b', 'c', 'd', 'e'); expect(plainF.a.a).to.true; expect(plainF.a.b).to.equal(2); expect(plainF.a.c).to.equal('C'); expect(plainF.a.d).to.a('object').keys('_', '__'); expect(plainF.a.d._).to.equal('D'); expect(plainF.a.e).to.a('object').keys('a'); expect(plainF.a.e.a).to.a('object').keys('_', '__', 'id', 'a', 'c'); }); it('should correctly apply the filter', (): void => { const g = new G(new B(new A()), new A(), new A()), plain = reconstituteData(g.toPlainObject()); expect(plain.b.a._).equal('A'); const plainFiltered = reconstituteData( g.toPlainObject((object) => { if (object instanceof A) { return new E(); } return object; }) ); expect(plainFiltered.b.a._).equal('E'); expect(plainFiltered.a[0]._).equal('E'); }); class World extends DataObject { #tiles = new EntityRegistry(Tile); constructor() { super(); this.addKey('tiles'); } tiles(): EntityRegistry { return this.#tiles; } } class Tile extends DataObject {} class Unit extends DataObject { #tile: Tile; constructor(tile: Tile) { super(); this.#tile = tile; this.addKey('tile'); } tile(): Tile { return this.#tile; } } class City extends DataObject { #tile: Tile; constructor(tile: Tile) { super(); this.#tile = tile; this.addKey('tile'); } tile(): Tile { return this.#tile; } } it('should provide a hierarchy and map of objects that allows nesting and recursion', (): void => { const additionalDataRegistry = new AdditionalDataRegistry(), cityRegistry = new EntityRegistry(City), unitRegistry = new EntityRegistry(Unit), world = new World(); additionalDataRegistry.register( new AdditionalData(Tile, 'units', (tile: Tile) => unitRegistry.getBy('tile', tile) ) ); additionalDataRegistry.register( new AdditionalData( Tile, 'city', (tile: Tile) => cityRegistry.getBy('tile', tile)[0] ) ); const tile1 = new Tile(), tile2 = new Tile(), tile3 = new Tile(), tile4 = new Tile(), tile5 = new Tile(); world.tiles().register(tile1, tile2, tile3, tile4, tile5); const unit1 = new Unit(tile1), unit2 = new Unit(tile4), unit3 = new Unit(tile4), unit4 = new Unit(tile4); unitRegistry.register(unit1, unit2, unit3, unit4); const city1 = new City(tile2), city2 = new City(tile4); cityRegistry.register(city1, city2); const plainWorld = world.toPlainObject( (object) => object, additionalDataRegistry ); expect(Object.keys(plainWorld.objects).length).equal(12); const reconstitutedWorld = reconstituteData(plainWorld); expect(reconstitutedWorld).an('object').keys('_', '__', 'id', 'tiles'); expect(reconstitutedWorld.tiles).an('array').length(5); expect(reconstitutedWorld.tiles[3]) .an('object') .keys('_', '__', 'id', 'units', 'city'); expect(reconstitutedWorld.tiles[3]).equal( reconstitutedWorld.tiles[3].units[0].tile ); expect(reconstitutedWorld.tiles[3].units[0]).equal( reconstitutedWorld.tiles[3].units[0].tile.units[0].tile.units[0] ); }); it('should provide expected classes for `sourceClass()`', (): void => { class A extends DataObject {} class B extends A {} class C extends B {} const a = new A(), b = new B(), c = new C(); expect(a.sourceClass()).equal(A); expect(b.sourceClass()).equal(B); expect(c.sourceClass()).equal(C); expect(reconstituteData(c.toPlainObject()).__).eql([ 'C', 'B', 'A', 'DataObject', ]); }); });