UNPKG

@casual-simulation/aux-common

Version:
488 lines 19.4 kB
import { Subscription } from 'rxjs'; import { waitAsync } from '../../test/TestHelpers'; /** * Tests the given document implementation for various features. */ export function testDocumentImplementation(createDocument) { let document; let statuses; let version; let sub; beforeEach(async () => { sub = new Subscription(); document = await createDocument(); statuses = []; sub.add(document.onVersionUpdated.subscribe((v) => (version = v))); sub.add(document.onStatusUpdated.subscribe((update) => statuses.push(update))); }); afterEach(() => { sub.unsubscribe(); }); describe('getMap()', () => { it('should return a map', () => { expect(document.getMap('abc')).toBeTruthy(); }); it('should be able to get the same map multiple times', () => { const map1 = document.getMap('abc'); const map2 = document.getMap('abc'); expect(map1).toBe(map2); }); it('should return different maps for different keys', () => { const map1 = document.getMap('abc'); const map2 = document.getMap('def'); expect(map1).not.toBe(map2); }); it('should be able to store data in a map', () => { const map = document.getMap('abc'); map.set('hello', 'world'); expect(map.get('hello')).toBe('world'); }); it('should be able delete keys in a map', () => { const map = document.getMap('abc'); map.set('hello', 'world'); map.delete('hello'); expect(map.has('hello')).toBe(false); }); it('should be able list keys in a map', () => { const map = document.getMap('abc'); map.set('hello', 'world'); map.set('test', 'value'); expect([...map.keys()]).toEqual(['hello', 'test']); }); it('should be able to list entries in a a map', () => { const map = document.getMap('abc'); map.set('hello', 'world'); map.set('test', 'value'); expect([...map.entries()]).toEqual([ ['hello', 'world'], ['test', 'value'], ]); }); it('should be able to list values in a map', () => { const map = document.getMap('abc'); map.set('hello', 'world'); map.set('test', 'value'); expect([...map.values()]).toEqual(['world', 'value']); }); it('should be able to clear the map', () => { const map = document.getMap('abc'); map.set('hello', 'world'); map.set('test', 'value'); map.clear(); expect([...map.keys()]).toEqual([]); }); it('should be able to loop over the map', () => { const map = document.getMap('abc'); map.set('hello', 'world'); map.set('test', 'value'); const keys = []; const values = []; map.forEach((value, key) => { keys.push(key); values.push(value); }); expect(keys).toEqual(['hello', 'test']); expect(values).toEqual(['world', 'value']); }); it('should be able to iterate over the map', () => { const map = document.getMap('abc'); map.set('hello', 'world'); map.set('test', 'value'); const keys = []; const values = []; for (let [key, value] of map) { keys.push(key); values.push(value); } expect(keys).toEqual(['hello', 'test']); expect(values).toEqual(['world', 'value']); }); it('should be able to get the document from a map', () => { const map = document.getMap('abc'); map.set('hello', 'world'); expect(map.doc).toBe(document); }); it('should return undefined if the map doesnt have a parent', () => { const map = document.getMap('abc'); expect(map.parent).toBeUndefined(); }); it('should be able to set a map in a map', () => { const map1 = document.getMap('abc'); const map2 = document.createMap(); map2.set('hello', 'world'); map1.set('map', map2); expect(map1.get('map')).toBe(map2); expect(map2.parent).toBe(map1); expect(map2.doc).toBe(document); expect(map1.toJSON()).toEqual({ map: { hello: 'world', }, }); }); it('should throw an error if setting a top-level map inside another map', () => { const map1 = document.getMap('abc'); const map2 = document.getMap('top'); map2.set('hello', 'world'); expect(() => { map1.set('map', map2); }).toThrow('Cannot set a top-level map inside another map.'); }); // TODO: Currently YJS doesn't seem to actually clone the map it.skip('should be able to clone a map', () => { const map = document.getMap('abc'); map.set('hello', 'world'); map.set('test', 'value'); const c = map.clone(); expect([...c.keys()]).toEqual(['hello', 'test']); expect(c.toJSON()).toEqual({ hello: 'world', test: 'value', }); }); it('should emit changes', async () => { const map = document.getMap('abc'); let changes = []; sub.add(map.changes.subscribe((c) => changes.push(c))); map.set('hello', 'world'); map.set('hello', 'different'); map.delete('hello'); await waitAsync(); expect(changes.length).toBe(3); expect(changes[0].type).toBe('map'); expect(changes[0].target === map).toBe(true); expect(changes[0].changes).toEqual(new Map([ [ 'hello', { action: 'add', oldValue: undefined, }, ], ])); expect(changes[1].type).toBe('map'); expect(changes[1].target === map).toBe(true); expect(changes[1].changes).toEqual(new Map([ [ 'hello', { action: 'update', oldValue: 'world', }, ], ])); expect(changes[2].type).toBe('map'); expect(changes[2].target === map).toBe(true); expect(changes[2].changes).toEqual(new Map([ [ 'hello', { action: 'delete', oldValue: 'different', }, ], ])); }); }); describe('getArray()', () => { it('should return an array', () => { expect(document.getArray('abc')).toBeTruthy(); }); it('should be able to get the same array multiple times', () => { const array1 = document.getArray('abc'); const array2 = document.getArray('abc'); expect(array1).toBe(array2); }); it('should return different arrays for different keys', () => { const map1 = document.getArray('abc'); const map2 = document.getArray('def'); expect(map1).not.toBe(map2); }); it('should be able to store data in an array', () => { const array = document.getArray('abc'); array.push('hello'); expect(array.toArray()).toEqual(['hello']); }); it('should be able to push multiple items at once', () => { const array = document.getArray('abc'); array.push('hello', 'world'); expect(array.toArray()).toEqual(['hello', 'world']); }); it('should be able to delete items in an array', () => { const array = document.getArray('abc'); array.push('hello'); array.delete(0, 1); expect(array.toArray()).toEqual([]); }); it('should be able to pop items from the array', () => { const array = document.getArray('abc'); array.push('hello'); array.push('world'); expect(array.pop()).toBe('world'); expect(array.toArray()).toEqual(['hello']); }); it('should be able to add items to the beginning of the array', () => { const array = document.getArray('abc'); array.push('world'); array.unshift('hello'); expect(array.toArray()).toEqual(['hello', 'world']); }); it('should be able to shift items from the beginning of the array', () => { const array = document.getArray('abc'); array.push('hello'); array.push('world'); expect(array.shift()).toBe('hello'); expect(array.toArray()).toEqual(['world']); }); it('should be able to unshift items onto the beginning of the array', () => { const array = document.getArray('abc'); array.unshift('hello'); array.unshift('world'); expect(array.toArray()).toEqual(['world', 'hello']); }); it('should be able to use splice to delete items from the array', () => { const array = document.getArray('abc'); array.push('hello'); array.push('world'); array.push('123'); array.push('456'); const deleted = array.splice(2, 2); expect(deleted).toEqual(['123', '456']); expect(array.toArray()).toEqual(['hello', 'world']); }); it('should be able to use splice with negative start to delete items from end of the array', () => { const array = document.getArray('abc'); array.push('hello'); array.push('world'); array.push('123'); array.push('456'); const deleted = array.splice(-2, 2); expect(deleted).toEqual(['123', '456']); expect(array.toArray()).toEqual(['hello', 'world']); }); it('should be able to use splice to insert items in the array', () => { const array = document.getArray('abc'); array.push('hello'); array.push('world'); array.push('123'); array.push('456'); const deleted = array.splice(2, undefined, 'test', 'value'); expect(deleted).toEqual([]); expect(array.toArray()).toEqual([ 'hello', 'world', 'test', 'value', '123', '456', ]); }); it('should be able to use splice with negative start to insert items counted from the end of the array', () => { const array = document.getArray('abc'); array.push('hello'); array.push('world'); array.push('123'); array.push('456'); const deleted = array.splice(-2, undefined, 'test', 'value'); expect(deleted).toEqual([]); expect(array.toArray()).toEqual([ 'hello', 'world', 'test', 'value', '123', '456', ]); }); it('should be able to use splice to replace items in the array', () => { const array = document.getArray('abc'); array.push('hello'); array.push('world'); array.push('123'); array.push('456'); const deleted = array.splice(2, 2, 'test', 'value'); expect(deleted).toEqual(['123', '456']); expect(array.toArray()).toEqual([ 'hello', 'world', 'test', 'value', ]); }); it('should be able to map over the array', () => { const array = document.getArray('abc'); array.push('hello'); array.push('world'); const mapped = array.map((v) => v.toUpperCase()); expect(mapped).toEqual(['HELLO', 'WORLD']); }); it('should be able to filter the array', () => { const array = document.getArray('abc'); array.push('hello'); array.push('world'); const filtered = array.filter((v) => v === 'hello'); expect(filtered).toEqual(['hello']); }); it('should be able to iterate over the array', () => { const array = document.getArray('abc'); array.push('hello'); array.push('world'); const values = []; array.forEach((v) => values.push(v)); expect(values).toEqual(['hello', 'world']); }); it('should be able to get the document from an array', () => { const array = document.getArray('abc'); array.push('hello'); expect(array.doc === document).toBe(true); }); it('should return undefined if the array doesnt have a parent', () => { const array = document.getArray('abc'); expect(array.parent).toBeUndefined(); }); it('should be able to set an array in an array', () => { const array1 = document.getArray('abc'); const array2 = document.createArray(); array2.push('hello'); array1.push(array2); expect(array1.get(0) === array2).toBe(true); expect(array2.parent === array1).toBe(true); expect(array2.doc === document).toBe(true); expect(array1.toJSON()).toEqual([['hello']]); }); it('should be able to emit changes', async () => { const array = document.getArray('abc'); let changes = []; sub.add(array.changes.subscribe((c) => changes.push(c))); array.push('hello'); array.push('world'); array.delete(1, 1); await waitAsync(); expect(changes.length).toBe(3); expect(changes[0].type).toBe('array'); expect(changes[0].target === array).toBe(true); expect(changes[0].delta).toEqual([ { type: 'insert', values: ['hello'], }, ]); expect(changes[1].type).toBe('array'); expect(changes[1].target === array).toBe(true); expect(changes[1].delta).toEqual([ { type: 'preserve', count: 1, }, { type: 'insert', values: ['world'], }, ]); expect(changes[2].type).toBe('array'); expect(changes[2].target === array).toBe(true); expect(changes[2].delta).toEqual([ { type: 'preserve', count: 1, }, { type: 'delete', count: 1, }, ]); }); }); describe('getText()', () => { it('should return an array', () => { expect(document.getText('abc')).toBeTruthy(); }); it('should be able to get the same text multiple times', () => { const array1 = document.getText('abc'); const array2 = document.getText('abc'); expect(array1).toBe(array2); }); it('should return different text for different keys', () => { const map1 = document.getText('abc'); const map2 = document.getText('def'); expect(map1).not.toBe(map2); }); it('should be able to store strings in a text', () => { const array = document.getText('abc'); array.insert(0, 'hello'); expect(array.toString()).toEqual('hello'); }); it('should be able to insert text at the end', () => { const array = document.getText('abc'); array.insert(0, 'hello'); array.insert(5, ' world'); expect(array.toString()).toEqual('hello world'); }); it('should be able to insert text at the beginning', () => { const array = document.getText('abc'); array.insert(0, 'hello'); array.insert(0, 'world'); expect(array.toString()).toEqual('worldhello'); }); it('should be able to delete chars from the text', () => { const array = document.getText('abc'); array.insert(0, 'hello'); array.delete(0, 1); expect(array.toString()).toEqual('ello'); }); it('should be able to get a slice of text', () => { const array = document.getText('abc'); array.insert(0, 'hello'); expect(array.slice(0, 2)).toEqual('he'); }); }); describe('transact()', () => { it('should batch changes', async () => { const map = document.getMap('abc'); const array = document.getArray('def'); let mapChanges = []; let arrayChanges = []; sub.add(map.changes.subscribe((c) => mapChanges.push(c))); sub.add(array.changes.subscribe((c) => arrayChanges.push(c))); document.transact(() => { map.set('hello', 'world'); map.set('hello', 'different'); array.push('test'); }); await waitAsync(); expect(mapChanges.length).toBe(1); expect(arrayChanges.length).toBe(1); expect(mapChanges[0].type).toBe('map'); expect(mapChanges[0].target === map).toBe(true); expect(mapChanges[0].changes).toEqual(new Map([ [ 'hello', { action: 'add', oldValue: undefined, }, ], ])); expect(arrayChanges[0].type).toBe('array'); expect(arrayChanges[0].target === array).toBe(true); expect(arrayChanges[0].delta).toEqual([ { type: 'insert', values: ['test'], }, ]); }); }); describe('getStateUpdate()', () => { it('should be able to apply the update to another document', async () => { const map = document.getMap('abc'); map.set('hello', 'world'); const state = document.getStateUpdate(); const document2 = await createDocument(); document2.applyStateUpdates([state]); const map2 = document2.getMap('abc'); expect(map2.toJSON()).toEqual(map.toJSON()); }); }); } //# sourceMappingURL=DocumentTests.js.map