Path: blob/main/client/test/ObjectObserver.basic.test.ts
1028 views
/**1ISC License (ISC)23Copyright 2015 Yuri Guller ([email protected])4Modifications 2021 Data Liberation Foundation56Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted,7provided that the above copyright notice and this permission notice appear in all copies.89THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE10INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.11IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES12OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,13NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.14*/15import IObservableChange from '@secret-agent/interfaces/IObservableChange';16import ObjectObserver from '../lib/ObjectObserver';1718// object19//20test('verify object - root - insert', () => {21let c;22const o: any = ObjectObserver.create({}, cs => {23c = cs[0];24});25o.some = 'new';2627expect(c).toEqual({28type: 'insert',29path: ['some'],30value: 'new',31});32});3334test('verify object - deep - insert', () => {35let c;36const o: any = ObjectObserver.create({ a: {} }, cs => {37c = cs[0];38});39o.a.some = 'new';40expect(c).toEqual({41type: 'insert',42path: ['a', 'some'],43value: 'new',44});45});4647test('verify object - root - update', () => {48let c: IObservableChange;49const o = ObjectObserver.create({ p: 'old' }, cs => {50c = cs[0];51});52o.p = 'new';53expect(c).toEqual({54type: 'update',55path: ['p'],56value: 'new',57});58});5960test('verify object - deep - update', () => {61let c;62const o = ObjectObserver.create({ a: { p: 'old' } }, cs => {63c = cs[0];64});65o.a.p = 'new';66expect(c).toEqual({67type: 'update',68path: ['a', 'p'],69value: 'new',70});71});7273test('verify object - root - delete', () => {74let c;75const o = ObjectObserver.create({ p: 'old' }, cs => {76c = cs[0];77});78delete o.p;79expect(c).toEqual({80type: 'delete',81path: ['p'],82});83});8485test('verify object - deep - delete', () => {86let c;87const o = ObjectObserver.create({ a: { p: 'old' } }, cs => {88c = cs[0];89});90delete o.a.p;91expect(c).toEqual({92type: 'delete',93path: ['a', 'p'],94});95});9697// array98//99test('verify array - root - insert', () => {100let c;101const o = ObjectObserver.create([], cs => {102c = cs[0];103});104o.push('new');105expect(c).toEqual({106type: 'insert',107path: [0],108value: 'new',109});110});111112test('verify array - deep - insert', () => {113let c;114const o = ObjectObserver.create([[]], cs => {115c = cs[0];116});117o[0].push('new');118expect(c).toEqual({119type: 'insert',120path: [0, 0],121value: 'new',122});123});124125test('verify array - root - update', () => {126let c;127const o = ObjectObserver.create(['old'], cs => {128c = cs[0];129});130o[0] = 'new';131expect(c).toEqual({132type: 'update',133path: [0],134value: 'new',135});136});137138test('verify array - deep - update', () => {139let c;140const o = ObjectObserver.create([['old']], cs => {141c = cs[0];142});143o[0][0] = 'new';144expect(c).toEqual({145type: 'update',146path: [0, 0],147value: 'new',148});149});150151test('verify array - root - delete', () => {152let c;153const o = ObjectObserver.create(['old'], cs => {154c = cs[0];155});156o.pop();157expect(c).toEqual({158type: 'delete',159path: [0],160});161});162163test('verify array - deep - delete', () => {164let c;165const o = ObjectObserver.create([['old']], cs => {166c = cs[0];167});168o[0].pop();169expect(c).toEqual({170type: 'delete',171path: [0, 0],172});173});174175// advanced types176//177test('verify date - changes', () => {178const changes = [];179const o = ObjectObserver.create([{ name: 'test', date: new Date() }], cs => {180changes.push(...cs);181});182expect(o[0].date).toEqual(expect.any(String));183const date2 = new Date();184date2.setTime(new Date().getTime() - 100000);185186o[0].date = date2 as any;187188expect(changes[0]).toEqual({189type: 'update',190path: [0, 'date'],191value: date2.toISOString(),192});193});194195test('buffer - records changes', () => {196const events = [];197const buffer = Buffer.from('This is a test');198const po = ObjectObserver.create({ buffer }, changes => events.push(...changes));199200expect(po.buffer).toBe(buffer.toString('base64'));201expect(po.buffer).not.toEqual(buffer);202203buffer.write('More');204expect(po.buffer).not.toBe(buffer.toString('base64'));205206po.buffer = buffer;207expect(events).toHaveLength(1);208expect(events[0]).toEqual({209type: 'update',210path: ['buffer'],211value: Buffer.from('More is a test').toString('base64'),212});213});214215// object tests216//217test('creating observable preserves original object keys order', () => {218const person = {219name: 'name',220age: 7,221street: 'street',222block: 9,223apt: 1,224};225const oPerson = ObjectObserver.create(person);226const sKeys = Object.keys(person);227const oKeys = Object.keys(oPerson);228229expect(sKeys).toStrictEqual(oKeys);230});231232test('plain object operations', () => {233const o: any = {234name: 'name',235age: 7,236address: null,237};238const events = [];239const tmpAddress = { street: 'some' };240241const po = ObjectObserver.create(o, changes => events.push(...changes));242243const v1 = (po.name = 'new name'); // eslint-disable-line no-multi-assign244const v2 = (po.age = 9); // eslint-disable-line no-multi-assign245const v3 = (po.address = tmpAddress); // eslint-disable-line no-multi-assign246expect(v1).toBe('new name');247expect(v2).toBe(9);248expect(v3).toBe(tmpAddress);249250expect(events).toHaveLength(3);251expect(events[0]).toEqual({252type: 'update',253path: ['name'],254value: 'new name',255});256expect(events[1]).toEqual({257type: 'update',258path: ['age'],259value: 9,260});261expect(events[2]).toEqual({262type: 'update',263path: ['address'],264value: po.address,265});266267const v4 = (po.address = null); // eslint-disable-line no-multi-assign268const v5 = (po.sex = 'male'); // eslint-disable-line no-multi-assign269delete po.sex;270expect(v4).toBe(null);271expect(v5).toBe('male');272273expect(events).toHaveLength(6);274275expect(events[3]).toEqual({276type: 'update',277path: ['address'],278value: null,279});280expect(events[4]).toEqual({281type: 'insert',282path: ['sex'],283value: 'male',284});285expect(events[5]).toEqual({286type: 'delete',287path: ['sex'],288});289});290291test('sub tree object operations', () => {292const person = {293name: 'name',294age: 7,295address: null,296addressB: {297street: {298name: 'street name',299apt: 123,300},301},302};303const events = [];304const newAddress = {};305306const po = ObjectObserver.create(person, changes => {307[].push.apply(events, changes);308});309310po.address = newAddress;311expect(events).toHaveLength(1);312expect(events[0]).toEqual({313type: 'update',314path: ['address'],315value: po.address,316});317318po.address.street = 'street';319po.addressB.street.name = 'new street name';320321expect(events).toHaveLength(3);322expect(events[1]).toEqual({323type: 'insert',324path: ['address', 'street'],325value: 'street',326});327expect(events[2]).toEqual({328type: 'update',329path: ['addressB', 'street', 'name'],330value: 'new street name',331});332});333334test('Object.assign with multiple properties yields many callbacks', () => {335const events = [];336let callbacks = 0;337const observable: any = ObjectObserver.create({}, changes => {338callbacks += 1;339events.push(...changes);340});341const newData = { a: 1, b: 2, c: 3 };342343Object.assign(observable, newData);344observable.a = 4;345expect(callbacks).toBe(4);346expect(events).toHaveLength(4);347});348349350