feat(collections): add new collection APIs, iterator support, and tree serialization utilities
This commit is contained in:
118
test/test.backpressuredarray.both.ts
Normal file
118
test/test.backpressuredarray.both.ts
Normal file
@@ -0,0 +1,118 @@
|
||||
import { tap, expect } from '@push.rocks/tapbundle';
|
||||
import * as lik from '../ts/index.js';
|
||||
|
||||
let testArray: lik.BackpressuredArray<string>;
|
||||
|
||||
tap.test('should create a BackpressuredArray with default high water mark', async () => {
|
||||
testArray = new lik.BackpressuredArray<string>();
|
||||
expect(testArray).toBeInstanceOf(lik.BackpressuredArray);
|
||||
expect(testArray.length).toEqual(0);
|
||||
});
|
||||
|
||||
tap.test('should push items and return true while under high water mark', async () => {
|
||||
const arr = new lik.BackpressuredArray<number>(4);
|
||||
expect(arr.push(1)).toBeTrue();
|
||||
expect(arr.push(2)).toBeTrue();
|
||||
expect(arr.push(3)).toBeTrue();
|
||||
expect(arr.length).toEqual(3);
|
||||
});
|
||||
|
||||
tap.test('should return false when at high water mark', async () => {
|
||||
const arr = new lik.BackpressuredArray<number>(2);
|
||||
arr.push(1);
|
||||
const result = arr.push(2);
|
||||
expect(result).toBeFalse();
|
||||
expect(arr.length).toEqual(2);
|
||||
});
|
||||
|
||||
tap.test('should shift items correctly', async () => {
|
||||
const arr = new lik.BackpressuredArray<string>(4);
|
||||
arr.push('a');
|
||||
arr.push('b');
|
||||
expect(arr.shift()).toEqual('a');
|
||||
expect(arr.shift()).toEqual('b');
|
||||
expect(arr.shift()).toBeUndefined();
|
||||
});
|
||||
|
||||
tap.test('should peek without removing', async () => {
|
||||
const arr = new lik.BackpressuredArray<string>(4);
|
||||
arr.push('first');
|
||||
arr.push('second');
|
||||
expect(arr.peek()).toEqual('first');
|
||||
expect(arr.length).toEqual(2);
|
||||
});
|
||||
|
||||
tap.test('should peek return undefined on empty', async () => {
|
||||
const arr = new lik.BackpressuredArray<string>(4);
|
||||
expect(arr.peek()).toBeUndefined();
|
||||
});
|
||||
|
||||
tap.test('should pushMany items', async () => {
|
||||
const arr = new lik.BackpressuredArray<number>(10);
|
||||
const result = arr.pushMany([1, 2, 3]);
|
||||
expect(arr.length).toEqual(3);
|
||||
expect(result).toBeTrue();
|
||||
});
|
||||
|
||||
tap.test('checkHasItems returns correct boolean', async () => {
|
||||
const arr = new lik.BackpressuredArray<number>(4);
|
||||
expect(arr.checkHasItems()).toBeFalse();
|
||||
arr.push(1);
|
||||
expect(arr.checkHasItems()).toBeTrue();
|
||||
});
|
||||
|
||||
tap.test('checkSpaceAvailable works correctly', async () => {
|
||||
const arr = new lik.BackpressuredArray<number>(2);
|
||||
expect(arr.checkSpaceAvailable()).toBeTrue();
|
||||
arr.push(1);
|
||||
expect(arr.checkSpaceAvailable()).toBeTrue();
|
||||
arr.push(2);
|
||||
expect(arr.checkSpaceAvailable()).toBeFalse();
|
||||
});
|
||||
|
||||
tap.test('waitForItems resolves when items are pushed', async () => {
|
||||
const arr = new lik.BackpressuredArray<string>(4);
|
||||
let resolved = false;
|
||||
const waitPromise = arr.waitForItems().then(() => {
|
||||
resolved = true;
|
||||
});
|
||||
arr.push('hello');
|
||||
await waitPromise;
|
||||
expect(resolved).toBeTrue();
|
||||
});
|
||||
|
||||
tap.test('waitForSpace resolves when items are shifted', async () => {
|
||||
const arr = new lik.BackpressuredArray<number>(1);
|
||||
arr.push(1);
|
||||
let resolved = false;
|
||||
const waitPromise = arr.waitForSpace().then(() => {
|
||||
resolved = true;
|
||||
});
|
||||
arr.shift();
|
||||
await waitPromise;
|
||||
expect(resolved).toBeTrue();
|
||||
});
|
||||
|
||||
tap.test('Symbol.iterator works', async () => {
|
||||
const arr = new lik.BackpressuredArray<number>(10);
|
||||
arr.pushMany([10, 20, 30]);
|
||||
const collected: number[] = [];
|
||||
for (const item of arr) {
|
||||
collected.push(item);
|
||||
}
|
||||
expect(collected).toEqual([10, 20, 30]);
|
||||
});
|
||||
|
||||
tap.test('destroy completes subjects and unblocks waiters', async () => {
|
||||
const arr = new lik.BackpressuredArray<number>(1);
|
||||
arr.push(1);
|
||||
let spaceResolved = false;
|
||||
const waitPromise = arr.waitForSpace().then(() => {
|
||||
spaceResolved = true;
|
||||
});
|
||||
arr.destroy();
|
||||
await waitPromise;
|
||||
expect(spaceResolved).toBeTrue();
|
||||
});
|
||||
|
||||
export default tap.start();
|
||||
@@ -1,10 +1,10 @@
|
||||
import { tap, expect } from '@push.rocks/tapbundle';
|
||||
|
||||
import * as lik from '../ts/index.js';
|
||||
|
||||
tap.test('should create a valid fastmap', async () => {
|
||||
const fastmap = new lik.FastMap();
|
||||
expect(fastmap).toBeInstanceOf(lik.FastMap);
|
||||
expect(fastmap.size).toEqual(0);
|
||||
});
|
||||
|
||||
tap.test('should find an entry', async () => {
|
||||
@@ -27,4 +27,125 @@ tap.test('should find an entry', async () => {
|
||||
expect(result.value1).toEqual('heyho3');
|
||||
});
|
||||
|
||||
tap.test('isUniqueKey returns true for new key, false for existing', async () => {
|
||||
const map = new lik.FastMap<string>();
|
||||
expect(map.isUniqueKey('foo')).toBeTrue();
|
||||
map.addToMap('foo', 'bar');
|
||||
expect(map.isUniqueKey('foo')).toBeFalse();
|
||||
});
|
||||
|
||||
tap.test('has() works correctly', async () => {
|
||||
const map = new lik.FastMap<number>();
|
||||
expect(map.has('x')).toBeFalse();
|
||||
map.addToMap('x', 42);
|
||||
expect(map.has('x')).toBeTrue();
|
||||
});
|
||||
|
||||
tap.test('addToMap with force overwrites existing key', async () => {
|
||||
const map = new lik.FastMap<string>();
|
||||
map.addToMap('key1', 'original');
|
||||
const withoutForce = map.addToMap('key1', 'new');
|
||||
expect(withoutForce).toBeFalse();
|
||||
expect(map.getByKey('key1')).toEqual('original');
|
||||
|
||||
const withForce = map.addToMap('key1', 'new', { force: true });
|
||||
expect(withForce).toBeTrue();
|
||||
expect(map.getByKey('key1')).toEqual('new');
|
||||
});
|
||||
|
||||
tap.test('getByKey returns undefined for missing key', async () => {
|
||||
const map = new lik.FastMap<string>();
|
||||
expect(map.getByKey('nonexistent')).toBeUndefined();
|
||||
});
|
||||
|
||||
tap.test('removeFromMap removes and returns item', async () => {
|
||||
const map = new lik.FastMap<string>();
|
||||
map.addToMap('a', 'hello');
|
||||
const removed = map.removeFromMap('a');
|
||||
expect(removed).toEqual('hello');
|
||||
expect(map.has('a')).toBeFalse();
|
||||
expect(map.size).toEqual(0);
|
||||
});
|
||||
|
||||
tap.test('getKeys returns all keys', async () => {
|
||||
const map = new lik.FastMap<number>();
|
||||
map.addToMap('a', 1);
|
||||
map.addToMap('b', 2);
|
||||
map.addToMap('c', 3);
|
||||
const keys = map.getKeys();
|
||||
expect(keys.length).toEqual(3);
|
||||
expect(keys).toContain('a');
|
||||
expect(keys).toContain('b');
|
||||
expect(keys).toContain('c');
|
||||
});
|
||||
|
||||
tap.test('values returns all values', async () => {
|
||||
const map = new lik.FastMap<number>();
|
||||
map.addToMap('x', 10);
|
||||
map.addToMap('y', 20);
|
||||
const vals = map.values();
|
||||
expect(vals.length).toEqual(2);
|
||||
expect(vals).toContain(10);
|
||||
expect(vals).toContain(20);
|
||||
});
|
||||
|
||||
tap.test('entries returns key-value pairs', async () => {
|
||||
const map = new lik.FastMap<string>();
|
||||
map.addToMap('k1', 'v1');
|
||||
const entries = map.entries();
|
||||
expect(entries.length).toEqual(1);
|
||||
expect(entries[0][0]).toEqual('k1');
|
||||
expect(entries[0][1]).toEqual('v1');
|
||||
});
|
||||
|
||||
tap.test('clean empties the map', async () => {
|
||||
const map = new lik.FastMap<string>();
|
||||
map.addToMap('a', 'b');
|
||||
map.addToMap('c', 'd');
|
||||
map.clean();
|
||||
expect(map.size).toEqual(0);
|
||||
expect(map.getKeys().length).toEqual(0);
|
||||
});
|
||||
|
||||
tap.test('concat merges two maps', async () => {
|
||||
const map1 = new lik.FastMap<number>();
|
||||
map1.addToMap('a', 1);
|
||||
const map2 = new lik.FastMap<number>();
|
||||
map2.addToMap('b', 2);
|
||||
const merged = map1.concat(map2);
|
||||
expect(merged.size).toEqual(2);
|
||||
expect(merged.getByKey('a')).toEqual(1);
|
||||
expect(merged.getByKey('b')).toEqual(2);
|
||||
});
|
||||
|
||||
tap.test('addAllFromOther merges in place', async () => {
|
||||
const map1 = new lik.FastMap<number>();
|
||||
map1.addToMap('a', 1);
|
||||
const map2 = new lik.FastMap<number>();
|
||||
map2.addToMap('b', 2);
|
||||
map2.addToMap('a', 99);
|
||||
map1.addAllFromOther(map2);
|
||||
expect(map1.size).toEqual(2);
|
||||
expect(map1.getByKey('a')).toEqual(99);
|
||||
expect(map1.getByKey('b')).toEqual(2);
|
||||
});
|
||||
|
||||
tap.test('Symbol.iterator works with for...of', async () => {
|
||||
const map = new lik.FastMap<number>();
|
||||
map.addToMap('x', 1);
|
||||
map.addToMap('y', 2);
|
||||
const collected: [string, number][] = [];
|
||||
for (const entry of map) {
|
||||
collected.push(entry);
|
||||
}
|
||||
expect(collected.length).toEqual(2);
|
||||
});
|
||||
|
||||
tap.test('find returns undefined when no match', async () => {
|
||||
const map = new lik.FastMap<number>();
|
||||
map.addToMap('a', 1);
|
||||
const result = await map.find(async (item) => item === 999);
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
|
||||
export default tap.start();
|
||||
|
||||
@@ -15,11 +15,51 @@ tap.test('should create an interest', async () => {
|
||||
});
|
||||
|
||||
tap.test('should return an already existing interest', async () => {
|
||||
await testInterestmap.addInterest(3);
|
||||
const interest3a = await testInterestmap.addInterest(3);
|
||||
const interest3b = await testInterestmap.addInterest(3);
|
||||
expect(interest3a === interest3b).toBeTrue();
|
||||
});
|
||||
|
||||
tap.test('should be able to inform about a lost interest', async () => {
|
||||
testInterestmap.informLostInterest(3);
|
||||
});
|
||||
|
||||
tap.test('checkInterest returns true for existing', async () => {
|
||||
expect(testInterestmap.checkInterest(4)).toBeTrue();
|
||||
});
|
||||
|
||||
tap.test('checkInterest returns false for non-existing', async () => {
|
||||
expect(testInterestmap.checkInterest(999)).toBeFalse();
|
||||
});
|
||||
|
||||
tap.test('findInterest returns the interest', async () => {
|
||||
const interest = testInterestmap.findInterest(4);
|
||||
expect(interest).not.toBeNull();
|
||||
expect(interest.originalInterest).toEqual(4);
|
||||
});
|
||||
|
||||
tap.test('findInterest returns null for non-existing', async () => {
|
||||
const interest = testInterestmap.findInterest(888);
|
||||
expect(interest).toBeNull();
|
||||
});
|
||||
|
||||
tap.test('fullfillInterest resolves the promise', async () => {
|
||||
const im = new lik.InterestMap<string, string>((s) => s);
|
||||
const interest = await im.addInterest('hello');
|
||||
interest.fullfillInterest('world');
|
||||
const result = await interest.interestFullfilled;
|
||||
expect(result).toEqual('world');
|
||||
expect(interest.isFullfilled).toBeTrue();
|
||||
im.destroy();
|
||||
});
|
||||
|
||||
tap.test('destroy cleans up interestmap', async () => {
|
||||
const im = new lik.InterestMap<string, string>((s) => s);
|
||||
await im.addInterest('a');
|
||||
await im.addInterest('b');
|
||||
im.destroy();
|
||||
expect(im.checkInterest('a')).toBeFalse();
|
||||
expect(im.checkInterest('b')).toBeFalse();
|
||||
});
|
||||
|
||||
export default tap.start();
|
||||
|
||||
@@ -7,11 +7,83 @@ let testLimitedArray: LimitedArray<string>;
|
||||
tap.test('should create a LimitedArray', async () => {
|
||||
testLimitedArray = new LimitedArray(6);
|
||||
expect(testLimitedArray).toBeInstanceOf(LimitedArray);
|
||||
expect(testLimitedArray.length).toEqual(0);
|
||||
});
|
||||
|
||||
tap.test('should never be longer than the set length', async () => {
|
||||
testLimitedArray.addMany(['hi', 'this', 'is', 'quite', 'a', 'long', 'string', ':)']);
|
||||
expect(testLimitedArray.array.length < 7).toBeTrue();
|
||||
expect(testLimitedArray.length).toEqual(6);
|
||||
});
|
||||
|
||||
tap.test('addOne respects limit', async () => {
|
||||
const arr = new LimitedArray<number>(3);
|
||||
arr.addOne(1);
|
||||
arr.addOne(2);
|
||||
arr.addOne(3);
|
||||
arr.addOne(4);
|
||||
expect(arr.length).toEqual(3);
|
||||
});
|
||||
|
||||
tap.test('setLimit truncates when lowered', async () => {
|
||||
const arr = new LimitedArray<number>(5);
|
||||
arr.addMany([1, 2, 3, 4, 5]);
|
||||
expect(arr.length).toEqual(5);
|
||||
arr.setLimit(2);
|
||||
expect(arr.length).toEqual(2);
|
||||
});
|
||||
|
||||
tap.test('getAverage returns correct average for numbers', async () => {
|
||||
const arr = new LimitedArray<number>(10);
|
||||
arr.addMany([10, 20, 30]);
|
||||
expect(arr.getAverage()).toEqual(20);
|
||||
});
|
||||
|
||||
tap.test('getAverage returns 0 for empty array', async () => {
|
||||
const arr = new LimitedArray<number>(10);
|
||||
expect(arr.getAverage()).toEqual(0);
|
||||
});
|
||||
|
||||
tap.test('getAverage returns null for non-number array', async () => {
|
||||
const arr = new LimitedArray<string>(10);
|
||||
arr.addOne('hello');
|
||||
expect(arr.getAverage()).toBeNull();
|
||||
});
|
||||
|
||||
tap.test('remove removes an item', async () => {
|
||||
const arr = new LimitedArray<string>(10);
|
||||
arr.addMany(['a', 'b', 'c']);
|
||||
const removed = arr.remove('b');
|
||||
expect(removed).toBeTrue();
|
||||
expect(arr.length).toEqual(2);
|
||||
const notFound = arr.remove('zzz');
|
||||
expect(notFound).toBeFalse();
|
||||
});
|
||||
|
||||
tap.test('clear empties the array', async () => {
|
||||
const arr = new LimitedArray<number>(10);
|
||||
arr.addMany([1, 2, 3]);
|
||||
arr.clear();
|
||||
expect(arr.length).toEqual(0);
|
||||
});
|
||||
|
||||
tap.test('getArray returns a copy', async () => {
|
||||
const arr = new LimitedArray<number>(10);
|
||||
arr.addMany([1, 2, 3]);
|
||||
const copy = arr.getArray();
|
||||
expect(copy.length).toEqual(3);
|
||||
copy.push(99);
|
||||
expect(arr.length).toEqual(3);
|
||||
});
|
||||
|
||||
tap.test('Symbol.iterator enables for...of', async () => {
|
||||
const arr = new LimitedArray<number>(10);
|
||||
arr.addMany([5, 10, 15]);
|
||||
const collected: number[] = [];
|
||||
for (const item of arr) {
|
||||
collected.push(item);
|
||||
}
|
||||
expect(collected.length).toEqual(3);
|
||||
});
|
||||
|
||||
export default tap.start();
|
||||
|
||||
@@ -21,4 +21,14 @@ tap.test('should add objects once and return true', async () => {
|
||||
expect(myLoopTracker.checkAndTrack(object2)).toBeFalse();
|
||||
});
|
||||
|
||||
tap.test('reset allows re-tracking', async () => {
|
||||
myLoopTracker.reset();
|
||||
expect(myLoopTracker.checkAndTrack(object1)).toBeTrue();
|
||||
expect(myLoopTracker.checkAndTrack(object1)).toBeFalse();
|
||||
});
|
||||
|
||||
tap.test('destroy cleans up', async () => {
|
||||
myLoopTracker.destroy();
|
||||
});
|
||||
|
||||
export default tap.start();
|
||||
|
||||
@@ -22,6 +22,8 @@ let testObject2: ITestObject = {
|
||||
tap.test('new lik.Objectmap() -> should correctly instantiate an Objectmap', async () => {
|
||||
testObjectmap = new lik.ObjectMap<ITestObject>();
|
||||
expect(testObjectmap).toBeInstanceOf(lik.ObjectMap);
|
||||
expect(testObjectmap.length).toEqual(0);
|
||||
expect(testObjectmap.isEmpty()).toBeTrue();
|
||||
});
|
||||
|
||||
tap.test('lik.Objectmap.add() -> should correctly add an object to Objectmap', async () => {
|
||||
@@ -30,6 +32,14 @@ tap.test('lik.Objectmap.add() -> should correctly add an object to Objectmap', a
|
||||
expect(testObjectmap.checkForObject(testObject1)).toBeTrue();
|
||||
// tslint:disable-next-line:no-unused-expression
|
||||
expect(testObjectmap.checkForObject(testObject2)).toBeFalse();
|
||||
expect(testObjectmap.length).toEqual(1);
|
||||
});
|
||||
|
||||
tap.test('add same object twice returns same key', async () => {
|
||||
const key1 = testObjectmap.add(testObject1);
|
||||
const key2 = testObjectmap.add(testObject1);
|
||||
expect(key1).toEqual(key2);
|
||||
expect(testObjectmap.length).toEqual(1);
|
||||
});
|
||||
|
||||
tap.test('lik.Objectmap.remove() -> should correctly remove an object to Objectmap', async () => {
|
||||
@@ -73,4 +83,127 @@ tap.test('should get one object and then remove it', async () => {
|
||||
expect(testObjectmap.getArray()).not.toContain(oneObject);
|
||||
});
|
||||
|
||||
tap.test('addMappedUnique and getMappedUnique work', async () => {
|
||||
const map = new lik.ObjectMap<string>();
|
||||
map.addMappedUnique('myKey', 'myValue');
|
||||
expect(map.getMappedUnique('myKey')).toEqual('myValue');
|
||||
});
|
||||
|
||||
tap.test('removeMappedUnique works', async () => {
|
||||
const map = new lik.ObjectMap<string>();
|
||||
map.addMappedUnique('k1', 'v1');
|
||||
const removed = map.removeMappedUnique('k1');
|
||||
expect(removed).toEqual('v1');
|
||||
expect(map.getMappedUnique('k1')).toBeUndefined();
|
||||
});
|
||||
|
||||
tap.test('addArray adds multiple objects', async () => {
|
||||
const map = new lik.ObjectMap<{ id: number }>();
|
||||
const items = [{ id: 1 }, { id: 2 }, { id: 3 }];
|
||||
map.addArray(items);
|
||||
expect(map.length).toEqual(3);
|
||||
for (const item of items) {
|
||||
expect(map.checkForObject(item)).toBeTrue();
|
||||
}
|
||||
});
|
||||
|
||||
tap.test('findSync works', async () => {
|
||||
const map = new lik.ObjectMap<{ name: string }>();
|
||||
const obj = { name: 'target' };
|
||||
map.add({ name: 'other' });
|
||||
map.add(obj);
|
||||
const found = map.findSync((item) => item.name === 'target');
|
||||
expect(found).toEqual(obj);
|
||||
});
|
||||
|
||||
tap.test('findOneAndRemove works', async () => {
|
||||
const map = new lik.ObjectMap<{ val: number }>();
|
||||
map.add({ val: 1 });
|
||||
map.add({ val: 2 });
|
||||
const removed = await map.findOneAndRemove(async (item) => item.val === 1);
|
||||
expect(removed.val).toEqual(1);
|
||||
expect(map.length).toEqual(1);
|
||||
});
|
||||
|
||||
tap.test('isEmpty returns correct state', async () => {
|
||||
const map = new lik.ObjectMap<string>();
|
||||
expect(map.isEmpty()).toBeTrue();
|
||||
map.add('hello');
|
||||
expect(map.isEmpty()).toBeFalse();
|
||||
});
|
||||
|
||||
tap.test('wipe clears all entries', async () => {
|
||||
const map = new lik.ObjectMap<number>();
|
||||
map.add(1);
|
||||
map.add(2);
|
||||
map.add(3);
|
||||
map.wipe();
|
||||
expect(map.isEmpty()).toBeTrue();
|
||||
expect(map.length).toEqual(0);
|
||||
});
|
||||
|
||||
tap.test('concat creates merged map', async () => {
|
||||
const map1 = new lik.ObjectMap<string>();
|
||||
map1.add('a');
|
||||
const map2 = new lik.ObjectMap<string>();
|
||||
map2.add('b');
|
||||
const merged = map1.concat(map2);
|
||||
expect(merged.length).toEqual(2);
|
||||
});
|
||||
|
||||
tap.test('map/filter/reduce work', async () => {
|
||||
const map = new lik.ObjectMap<{ val: number }>();
|
||||
map.add({ val: 1 });
|
||||
map.add({ val: 2 });
|
||||
map.add({ val: 3 });
|
||||
|
||||
const mapped = map.map((item) => item.val * 2);
|
||||
expect(mapped.length).toEqual(3);
|
||||
expect(mapped).toContain(2);
|
||||
expect(mapped).toContain(4);
|
||||
expect(mapped).toContain(6);
|
||||
|
||||
const filtered = map.filter((item) => item.val > 1);
|
||||
expect(filtered.length).toEqual(2);
|
||||
|
||||
const sum = map.reduce((acc, item) => acc + item.val, 0);
|
||||
expect(sum).toEqual(6);
|
||||
});
|
||||
|
||||
tap.test('Symbol.iterator enables for...of', async () => {
|
||||
const map = new lik.ObjectMap<number>();
|
||||
map.add(10);
|
||||
map.add(20);
|
||||
const collected: number[] = [];
|
||||
for (const item of map) {
|
||||
collected.push(item);
|
||||
}
|
||||
expect(collected.length).toEqual(2);
|
||||
expect(collected).toContain(10);
|
||||
expect(collected).toContain(20);
|
||||
});
|
||||
|
||||
tap.test('eventSubject emits add and remove events', async () => {
|
||||
const map = new lik.ObjectMap<string>();
|
||||
const events: Array<{ operation: string; payload: string }> = [];
|
||||
map.eventSubject.subscribe((event) => {
|
||||
events.push(event);
|
||||
});
|
||||
map.add('x');
|
||||
map.remove('x');
|
||||
expect(events.length).toEqual(2);
|
||||
expect(events[0].operation).toEqual('add');
|
||||
expect(events[1].operation).toEqual('remove');
|
||||
});
|
||||
|
||||
tap.test('destroy wipes and completes eventSubject', async () => {
|
||||
const map = new lik.ObjectMap<number>();
|
||||
map.add(1);
|
||||
let completed = false;
|
||||
map.eventSubject.subscribe({ complete: () => { completed = true; } });
|
||||
map.destroy();
|
||||
expect(map.isEmpty()).toBeTrue();
|
||||
expect(completed).toBeTrue();
|
||||
});
|
||||
|
||||
export default tap.start();
|
||||
|
||||
@@ -4,20 +4,78 @@ import { expect, tap } from '@push.rocks/tapbundle';
|
||||
// import the module
|
||||
import * as lik from '../ts/index.js';
|
||||
|
||||
let testTimedAggregator: lik.TimedAggregtor<string>;
|
||||
|
||||
tap.test('should create a timed aggregaotor', async (tools) => {
|
||||
testTimedAggregator = new lik.TimedAggregtor<string>({
|
||||
aggregationIntervalInMillis: 1000,
|
||||
tap.test('should create a timed aggregator and aggregate items', async (tools) => {
|
||||
const batches: string[][] = [];
|
||||
const aggregator = new lik.TimedAggregtor<string>({
|
||||
aggregationIntervalInMillis: 200,
|
||||
functionForAggregation: (aggregation) => {
|
||||
console.log(aggregation);
|
||||
batches.push(aggregation);
|
||||
},
|
||||
});
|
||||
testTimedAggregator.add('This');
|
||||
testTimedAggregator.add('is a whole sentence.');
|
||||
await tools.delayFor(1001);
|
||||
testTimedAggregator.add('This one is another.');
|
||||
await tools.delayFor(2000);
|
||||
aggregator.add('first');
|
||||
aggregator.add('second');
|
||||
await tools.delayFor(300);
|
||||
expect(batches.length).toEqual(1);
|
||||
expect(batches[0]).toContain('first');
|
||||
expect(batches[0]).toContain('second');
|
||||
|
||||
aggregator.add('third');
|
||||
await tools.delayFor(300);
|
||||
expect(batches.length).toEqual(2);
|
||||
expect(batches[1]).toContain('third');
|
||||
aggregator.stop();
|
||||
});
|
||||
|
||||
tap.test('stop() prevents further aggregation', async (tools) => {
|
||||
const batches: number[][] = [];
|
||||
const aggregator = new lik.TimedAggregtor<number>({
|
||||
aggregationIntervalInMillis: 100,
|
||||
functionForAggregation: (items) => {
|
||||
batches.push(items);
|
||||
},
|
||||
});
|
||||
aggregator.add(1);
|
||||
aggregator.stop();
|
||||
aggregator.add(2);
|
||||
await tools.delayFor(200);
|
||||
expect(batches.length).toEqual(0);
|
||||
});
|
||||
|
||||
tap.test('stop(true) flushes remaining items', async () => {
|
||||
const batches: number[][] = [];
|
||||
const aggregator = new lik.TimedAggregtor<number>({
|
||||
aggregationIntervalInMillis: 5000,
|
||||
functionForAggregation: (items) => {
|
||||
batches.push(items);
|
||||
},
|
||||
});
|
||||
aggregator.add(10);
|
||||
aggregator.add(20);
|
||||
aggregator.stop(true);
|
||||
expect(batches.length).toEqual(1);
|
||||
expect(batches[0]).toEqual([10, 20]);
|
||||
});
|
||||
|
||||
tap.test('restart allows adding again after stop', async (tools) => {
|
||||
const batches: string[][] = [];
|
||||
const aggregator = new lik.TimedAggregtor<string>({
|
||||
aggregationIntervalInMillis: 100,
|
||||
functionForAggregation: (items) => {
|
||||
batches.push(items);
|
||||
},
|
||||
});
|
||||
aggregator.add('a');
|
||||
aggregator.stop();
|
||||
aggregator.restart();
|
||||
aggregator.add('b');
|
||||
await tools.delayFor(200);
|
||||
expect(batches.length).toEqual(1);
|
||||
expect(batches[0]).toContain('b');
|
||||
aggregator.stop();
|
||||
});
|
||||
|
||||
tap.test('TimedAggregator alias exists', async () => {
|
||||
expect(lik.TimedAggregator).toEqual(lik.TimedAggregtor);
|
||||
});
|
||||
|
||||
export default tap.start();
|
||||
|
||||
@@ -33,8 +33,119 @@ tap.test('should add other objects in a hierachy', async () => {
|
||||
testTree.appendChild(testInstance, testInstance4);
|
||||
});
|
||||
|
||||
tap.test('hasChildren returns correct value', async () => {
|
||||
expect(testTree.hasChildren(testInstance)).toBeTrue();
|
||||
expect(testTree.hasChildren(testInstance2)).toBeFalse();
|
||||
});
|
||||
|
||||
tap.test('firstChild and lastChild work', async () => {
|
||||
const first = testTree.firstChild(testInstance);
|
||||
expect(first.hey).toEqual('second');
|
||||
const last = testTree.lastChild(testInstance);
|
||||
expect(last.hey).toEqual('fourth');
|
||||
});
|
||||
|
||||
tap.test('parent returns correct parent', async () => {
|
||||
const parent = testTree.parent(testInstance2);
|
||||
expect(parent.hey).toEqual('first');
|
||||
});
|
||||
|
||||
tap.test('nextSibling and previousSibling work', async () => {
|
||||
const next = testTree.nextSibling(testInstance2);
|
||||
expect(next.hey).toEqual('third');
|
||||
const prev = testTree.previousSibling(testInstance3);
|
||||
expect(prev.hey).toEqual('second');
|
||||
});
|
||||
|
||||
tap.test('childrenCount returns correct count', async () => {
|
||||
expect(testTree.childrenCount(testInstance)).toEqual(3);
|
||||
expect(testTree.childrenCount(testInstance2)).toEqual(0);
|
||||
});
|
||||
|
||||
tap.test('index returns sibling index', async () => {
|
||||
expect(testTree.index(testInstance2)).toEqual(0);
|
||||
expect(testTree.index(testInstance3)).toEqual(1);
|
||||
expect(testTree.index(testInstance4)).toEqual(2);
|
||||
});
|
||||
|
||||
tap.test('childrenToArray returns children', async () => {
|
||||
const children = testTree.childrenToArray(testInstance, {});
|
||||
expect(children.length).toEqual(3);
|
||||
expect(children[0].hey).toEqual('second');
|
||||
});
|
||||
|
||||
tap.test('insertBefore works', async () => {
|
||||
testTree.initialize(testInstance5);
|
||||
testTree.insertBefore(testInstance3, testInstance5);
|
||||
const idx = testTree.index(testInstance5);
|
||||
expect(idx).toEqual(1);
|
||||
expect(testTree.nextSibling(testInstance5).hey).toEqual('third');
|
||||
});
|
||||
|
||||
tap.test('insertAfter works', async () => {
|
||||
testTree.initialize(testInstance6);
|
||||
testTree.insertAfter(testInstance3, testInstance6);
|
||||
expect(testTree.previousSibling(testInstance6).hey).toEqual('third');
|
||||
});
|
||||
|
||||
tap.test('remove detaches node', async () => {
|
||||
const countBefore = testTree.childrenCount(testInstance);
|
||||
testTree.remove(testInstance6);
|
||||
expect(testTree.childrenCount(testInstance)).toEqual(countBefore - 1);
|
||||
});
|
||||
|
||||
tap.test('treeIterator with options works', async () => {
|
||||
const items: TestClass[] = [];
|
||||
for (const item of testTree.treeIterator(testInstance, {})) {
|
||||
items.push(item);
|
||||
}
|
||||
expect(items.length).toBeGreaterThan(1);
|
||||
expect(items[0].hey).toEqual('first');
|
||||
});
|
||||
|
||||
tap.test('nextSiblingsIterator works (bug was fixed)', async () => {
|
||||
const siblings: TestClass[] = [];
|
||||
for (const item of testTree.nextSiblingsIterator(testInstance2)) {
|
||||
siblings.push(item);
|
||||
}
|
||||
expect(siblings.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
tap.test('ancestorsIterator works (bug was fixed)', async () => {
|
||||
const ancestors: TestClass[] = [];
|
||||
for (const item of testTree.ancestorsIterator(testInstance2)) {
|
||||
ancestors.push(item);
|
||||
}
|
||||
expect(ancestors.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
tap.test("should create a JSON object that reflects a tree's hierachy", async () => {
|
||||
const jsonTreet = testTree.toJsonWithHierachy(testInstance);
|
||||
const jsonTree = testTree.toJsonWithHierachy(testInstance);
|
||||
expect(jsonTree).toHaveProperty('data');
|
||||
expect(jsonTree).toHaveProperty('children');
|
||||
expect(jsonTree.data.hey).toEqual('first');
|
||||
expect(jsonTree.children.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
tap.test('fromJsonWithHierachy rebuilds a tree', async () => {
|
||||
const newTree = new lik.Tree<{ name: string }>();
|
||||
const jsonRoot = {
|
||||
data: { name: 'root' },
|
||||
children: [
|
||||
{ data: { name: 'child1' }, children: [] },
|
||||
{
|
||||
data: { name: 'child2' },
|
||||
children: [{ data: { name: 'grandchild' }, children: [] }],
|
||||
},
|
||||
],
|
||||
};
|
||||
const root = newTree.fromJsonWithHierachy(jsonRoot);
|
||||
expect(root.name).toEqual('root');
|
||||
expect(newTree.hasChildren(root)).toBeTrue();
|
||||
expect(newTree.childrenCount(root)).toEqual(2);
|
||||
const children = newTree.childrenToArray(root, {});
|
||||
expect(children[1].name).toEqual('child2');
|
||||
expect(newTree.hasChildren(children[1])).toBeTrue();
|
||||
});
|
||||
|
||||
export default tap.start();
|
||||
|
||||
Reference in New Issue
Block a user