UNPKG

iterates

Version:

Iterator and AsyncIterator helper functions with typings

859 lines (846 loc) 28.8 kB
"use strict"; var subject = _interopRequireWildcard(require("./async.cjs")); var _utils = require("./utils.cjs"); function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; } function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } if (typeof Symbol.asyncIterator === 'undefined') { ; Symbol.asyncIterator = Symbol(); } async function* asAsync(...items) { for (const item of items) { yield Promise.resolve(item); } } describe('async', () => { describe('asAsyncIterable', () => { it('should turn a syncronous iterable to an asynchronous iterable', async () => { const iterator = subject.asAsyncIterable([1, 2, 3])[Symbol.asyncIterator](); const firstValue = iterator.next(); expect(firstValue).toBeInstanceOf(Promise); expect(await firstValue).toEqual({ done: false, value: 1 }); expect(await iterator.next()).toEqual({ done: false, value: 2 }); expect(await iterator.next()).toEqual({ done: false, value: 3 }); expect(await iterator.next()).toEqual({ done: true, value: undefined }); }); it('should turn a syncronous iterator to an asynchronous iterable', async () => { const iterator = subject.asAsyncIterable([1, 2, 3][Symbol.iterator]())[Symbol.asyncIterator](); const firstValue = iterator.next(); expect(firstValue).toBeInstanceOf(Promise); expect(await firstValue).toEqual({ done: false, value: 1 }); expect(await iterator.next()).toEqual({ done: false, value: 2 }); expect(await iterator.next()).toEqual({ done: false, value: 3 }); expect(await iterator.next()).toEqual({ done: true, value: undefined }); }); }); describe('asArray', () => { it('should collect all values of an iterable in an array', async () => { const array = await subject.asArray(asAsync(1, 2, 3)); expect(array).toEqual([1, 2, 3]); }); it('should collect all values of an iterator in an array', async () => { const array = await subject.asArray(asAsync(1, 2, 3)[Symbol.asyncIterator]()); expect(array).toEqual([1, 2, 3]); }); }); describe('fromPromise', () => { it('should yield the value from the promise', async () => { const iterator = subject.fromPromise(Promise.resolve(42)); expect(await iterator.next()).toEqual({ done: false, value: 42 }); expect(await iterator.next()).toEqual({ done: true, value: undefined }); }); }); describe('Subject', () => { it('should buffer items', async () => { const controller = new subject.Subject(); const iterator1 = controller[Symbol.asyncIterator](); const iterator2 = controller[Symbol.asyncIterator](); controller.next('A'); controller.next('B'); controller.next('C'); controller.done(); expect(await iterator1.next()).toEqual({ done: false, value: 'A' }); expect(await iterator2.next()).toEqual({ done: false, value: 'A' }); expect(await iterator1.next()).toEqual({ done: false, value: 'B' }); expect(await iterator2.next()).toEqual({ done: false, value: 'B' }); expect(await iterator1.next()).toEqual({ done: false, value: 'C' }); expect(await iterator2.next()).toEqual({ done: false, value: 'C' }); expect(await iterator1.next()).toEqual({ done: true, value: undefined }); expect(await iterator2.next()).toEqual({ done: true, value: undefined }); }); it('should support items pushed after next', async () => { const controller = new subject.Subject(); const iterator = controller[Symbol.asyncIterator](); const aValue = iterator.next(); const bValue = iterator.next(); const cValue = iterator.next(); const doneValue = iterator.next(); controller.next('A'); controller.next('B'); controller.next('C'); controller.done(); expect(await aValue).toEqual({ done: false, value: 'A' }); expect(await bValue).toEqual({ done: false, value: 'B' }); expect(await cValue).toEqual({ done: false, value: 'C' }); expect(await doneValue).toEqual({ done: true, value: undefined }); }); it('should support errors', async () => { const controller = new subject.Subject(); const iterator = controller[Symbol.asyncIterator](); controller.throw(new Error('Thrown error')); await expect(iterator.next()).rejects.toEqual(Error('Thrown error')); }); it('should throw errors if called after beeing ended', async () => { const controller = new subject.Subject(); controller.done(); expect(() => controller.next('Value')).toThrow(); expect(() => controller.throw('Value')).toThrow(); expect(() => controller.done()).toThrow(); }); }); describe('enumerate', () => { it('should add the index to each element', async () => { const iterator = subject.enumerate(asAsync('one', 'two', 'three'))[Symbol.asyncIterator](); expect(await iterator.next()).toEqual({ done: false, value: { index: 0, item: 'one' } }); expect(await iterator.next()).toEqual({ done: false, value: { index: 1, item: 'two' } }); expect(await iterator.next()).toEqual({ done: false, value: { index: 2, item: 'three' } }); expect(await iterator.next()).toEqual({ done: true, value: undefined }); }); }); describe('map', () => { it('should apply the provided function over each item', async () => { const iterator = subject.map(item => item.toUpperCase(), asAsync('one', 'two', 'three'))[Symbol.asyncIterator](); expect(await iterator.next()).toEqual({ done: false, value: 'ONE' }); expect(await iterator.next()).toEqual({ done: false, value: 'TWO' }); expect(await iterator.next()).toEqual({ done: false, value: 'THREE' }); expect(await iterator.next()).toEqual({ done: true, value: undefined }); }); it('should be auto curried', async () => { const iterator = subject.map(item => item.toUpperCase())(asAsync('one', 'two', 'three'))[Symbol.asyncIterator](); expect(await iterator.next()).toEqual({ done: false, value: 'ONE' }); }); }); describe('filterMap', () => { it('should apply the provided function over each item and flatten it', async () => { const iterator = subject.filterMap(item => item.length === 3 ? item.toUpperCase() : undefined, asAsync('one', 'two', 'three'))[Symbol.asyncIterator](); expect(await iterator.next()).toEqual({ done: false, value: 'ONE' }); expect(await iterator.next()).toEqual({ done: false, value: 'TWO' }); expect(await iterator.next()).toEqual({ done: true, value: undefined }); }); it('should be auto curried', async () => { const iterator = subject.filterMap(item => item.length === 3 ? item.toUpperCase() : undefined)(asAsync('one', 'two', 'three'))[Symbol.asyncIterator](); expect(await iterator.next()).toEqual({ done: false, value: 'ONE' }); }); }); describe('flatMap', () => { it('should apply the provided function over each item and flatten it', async () => { const iterator = subject.flatMap(item => asAsync(item, Promise.resolve(item.toUpperCase())), asAsync('one', Promise.resolve('two'), 'three'))[Symbol.asyncIterator](); expect(await iterator.next()).toEqual({ done: false, value: 'one' }); expect(await iterator.next()).toEqual({ done: false, value: 'ONE' }); expect(await iterator.next()).toEqual({ done: false, value: 'two' }); expect(await iterator.next()).toEqual({ done: false, value: 'TWO' }); expect(await iterator.next()).toEqual({ done: false, value: 'three' }); expect(await iterator.next()).toEqual({ done: false, value: 'THREE' }); expect(await iterator.next()).toEqual({ done: true, value: undefined }); }); it('should be auto curried', async () => { const iterator = subject.flatMap(item => asAsync(item, item.toUpperCase()))(asAsync('one', 'two', 'three'))[Symbol.asyncIterator](); expect(await iterator.next()).toEqual({ done: false, value: 'one' }); }); }); describe('flatten', () => { it('should flatten the items', async () => { const iterator = subject.flatten(asAsync(asAsync(1, 'one'), asAsync(2, 'two'), asAsync(3, 'three')))[Symbol.asyncIterator](); expect(await iterator.next()).toEqual({ done: false, value: 1 }); expect(await iterator.next()).toEqual({ done: false, value: 'one' }); expect(await iterator.next()).toEqual({ done: false, value: 2 }); expect(await iterator.next()).toEqual({ done: false, value: 'two' }); expect(await iterator.next()).toEqual({ done: false, value: 3 }); expect(await iterator.next()).toEqual({ done: false, value: 'three' }); expect(await iterator.next()).toEqual({ done: true, value: undefined }); }); // it('should suport sync iterables', async () => { // const iterator = subject // .flatten(asAsync([1, 'one'], [2, 'two'], [3, 'three'])) // [Symbol.asyncIterator]() // expect(await iterator.next()).toEqual({ // done: false, // value: 1, // }) // expect(await iterator.next()).toEqual({ // done: false, // value: 'one', // }) // expect(await iterator.next()).toEqual({ // done: false, // value: 2, // }) // expect(await iterator.next()).toEqual({ // done: false, // value: 'two', // }) // expect(await iterator.next()).toEqual({ // done: false, // value: 3, // }) // expect(await iterator.next()).toEqual({ // done: false, // value: 'three', // }) // expect(await iterator.next()).toEqual({done: true, value: undefined}) // }) }); describe('filter', () => { it('should only pass through items that pass the test', async () => { const iterator = subject.filter(item => item !== 'two', asAsync('one', 'two', 'three'))[Symbol.asyncIterator](); expect(await iterator.next()).toEqual({ done: false, value: 'one' }); expect(await iterator.next()).toEqual({ done: false, value: 'three' }); expect(await iterator.next()).toEqual({ done: true, value: undefined }); }); it('should be auto curried', async () => { const iterator = subject.filter(item => item !== 'two')(asAsync('one', 'two', 'three'))[Symbol.asyncIterator](); expect(await iterator.next()).toEqual({ done: false, value: 'one' }); }); }); describe('fold', () => { it('should return the accumulated value', async () => { const item = await subject.fold(0, (sum, item) => sum + item, asAsync(1, 2, 3)); expect(item).toEqual(6); }); it('should return the initial value if there are no items', async () => { const item = await subject.fold(0, (sum, item) => sum + item, asAsync()); expect(item).toEqual(0); }); it('should be auto curried', async () => { const item = await subject.fold(0, (sum, item) => sum + item)(asAsync(1, 2, 3)); expect(item).toEqual(6); }); }); describe('scan', () => { it('should return the accumulated value', async () => { const iterator = await subject.scan(0, (sum, item) => sum + item, asAsync(1, 2, 3))[Symbol.asyncIterator](); expect(await iterator.next()).toEqual({ done: false, value: 1 }); expect(await iterator.next()).toEqual({ done: false, value: 3 }); expect(await iterator.next()).toEqual({ done: false, value: 6 }); expect(await iterator.next()).toEqual({ done: true, value: 6 }); }); it('should return the initial value if there are no items', async () => { const iterator = await subject.scan(0, (sum, item) => sum + item, asAsync())[Symbol.asyncIterator](); expect(await iterator.next()).toEqual({ done: true, value: 0 }); }); it('should be auto curried', async () => { const iterator = subject.scan(0, (sum, item) => sum + item)(asAsync(1, 2, 3)); expect(await iterator.next()).toEqual({ done: false, value: 1 }); }); }); describe('collect', () => { it('should collect the values of the iterator in a map', async () => { const map = await subject.collect(item => [item, item ** 2], asAsync(1, 2, 3)); expect(map).toEqual(new Map([[1, 1], [2, 4], [3, 9]])); }); it('should overwrite duplicate keys', async () => { const map = await subject.collect(item => [item ** 2, item], asAsync(1, 2, 3, -2)); expect(map).toEqual(new Map([[1, 1], [4, -2], [9, 3]])); }); it('should support a custom merge function', async () => { const map = await subject.collect(item => [item ** 2, item], asAsync(1, 2, 3, -2), { merge: (a, b) => a + b }); expect(map).toEqual(new Map([[1, 1], [4, 0], [9, 3]])); }); it('should be auto curried', async () => { const collectFn = item => (0, _utils.tuple)([item ** 2, item]); const values = () => asAsync(1, 2, 3, -2); const options = { merge: (a, b) => a + b }; const result = new Map([[1, 1], [4, 0], [9, 3]]); const map1 = await subject.collect(collectFn, values(), options); expect(map1).toEqual(result); const map2 = await subject.collect(collectFn)(values(), options); expect(map2).toEqual(result); const map3 = await subject.collect(collectFn, options)(values()); expect(map3).toEqual(result); }); }); describe('collectRecord', () => { it('should collect the values of the iterator in a map', async () => { const map = await subject.collectRecord(item => [`${item}`, item ** 2], asAsync(1, 2, 3)); expect(map).toEqual({ 1: 1, 2: 4, 3: 9 }); }); it('should overwrite duplicate keys', async () => { const map = await subject.collectRecord(item => [`${item ** 2}`, item], asAsync(1, 2, 3, -2)); expect(map).toEqual({ 1: 1, 4: -2, 9: 3 }); }); it('should support a custom merge function', async () => { const map = await subject.collectRecord(item => [`${item ** 2}`, item], asAsync(1, 2, 3, -2), { merge: (a, b) => a + b }); expect(map).toEqual({ 1: 1, 4: 0, 9: 3 }); }); it('should be auto curried', async () => { const collectFn = item => (0, _utils.tuple)([`${item ** 2}`, item]); const values = () => asAsync(1, 2, 3, -2); const options = { merge: (a, b) => a + b }; const result = { 1: 1, 4: 0, 9: 3 }; const map1 = await subject.collectRecord(collectFn, values(), options); expect(map1).toEqual(result); const map2 = await subject.collectRecord(collectFn)(values(), options); expect(map2).toEqual(result); const map3 = await subject.collectRecord(collectFn, options)(values()); expect(map3).toEqual(result); }); }); describe('zip', () => { it('should zip two iterators', async () => { const iterator = subject.zip(asAsync(1, 2, 3), asAsync('one', 'two', 'three'))[Symbol.asyncIterator](); expect(await iterator.next()).toEqual({ done: false, value: [1, 'one'] }); expect(await iterator.next()).toEqual({ done: false, value: [2, 'two'] }); expect(await iterator.next()).toEqual({ done: false, value: [3, 'three'] }); expect(await iterator.next()).toEqual({ done: true, value: undefined }); }); it('should return the continuing iterator', async () => { const longA = asAsync(1, 2, 3, 4); const longB = asAsync('one', 'two', 'three', 'four'); const iteratorA = subject.zip(longA, asAsync('one', 'two', 'three'))[Symbol.asyncIterator](); const iteratorB = subject.zip(asAsync(1, 2, 3), longB)[Symbol.asyncIterator](); expect(await iteratorA.next()).toEqual({ done: false, value: [1, 'one'] }); expect(await iteratorA.next()).toEqual({ done: false, value: [2, 'two'] }); expect(await iteratorA.next()).toEqual({ done: false, value: [3, 'three'] }); const doneA = await iteratorA.next(); expect(doneA).toEqual({ done: true, value: longA }); expect(await iteratorB.next()).toEqual({ done: false, value: [1, 'one'] }); expect(await iteratorB.next()).toEqual({ done: false, value: [2, 'two'] }); expect(await iteratorB.next()).toEqual({ done: false, value: [3, 'three'] }); const doneB = await iteratorB.next(); expect(doneB).toEqual({ done: true, value: longB }); }); }); describe('throttle', () => { let nowMock; let realNow = Date.now; async function setTime(time) { // First, force a spin of the event loop await new Promise(resolve => setTimeout(resolve)); nowMock.mockReturnValue(time); } beforeEach(() => { Date.now = nowMock = jest.fn(); }); afterEach(() => { Date.now = realNow; }); it('should skip items that arrive quicker than the duration', async () => { const controller = new subject.Subject(); const iterator = subject.throttle(10, controller)[Symbol.asyncIterator](); await setTime(10); const a = iterator.next(); controller.next('A'); controller.next('B'); await setTime(15); controller.next('C'); const b = iterator.next(); await setTime(20); const c = iterator.next(); await setTime(35); controller.next('D'); controller.next('E'); await setTime(40); controller.next('F'); await setTime(45); controller.next('G'); expect(await a).toEqual({ done: false, value: 'A' }); expect(await b).toEqual({ done: false, value: 'D' }); expect(await c).toEqual({ done: false, value: 'G' }); }); it("should emit the last item even though it's inside the duration", async () => { const controller = new subject.Subject(); const iterator = subject.throttle(10, controller)[Symbol.asyncIterator](); await setTime(10); const a = iterator.next(); const b = iterator.next(); const c = iterator.next(); controller.next('A'); controller.next('B'); controller.done(); expect(await a).toEqual({ done: false, value: 'A' }); expect(await b).toEqual({ done: false, value: 'B' }); expect(await c).toEqual({ done: true, value: undefined }); }); }); describe('all', () => { it('should return false if any item fails the test', async () => { const test1 = await subject.all(item => item.length === 3, asAsync('one', 'two', 'three')); const test2 = await subject.all(item => item.length === 5, asAsync('one', 'two', 'three')); expect(test1).toBe(false); expect(test2).toBe(false); }); it('should return true if all items pass the test', async () => { const test = await subject.all(item => item.length !== 4, asAsync('one', 'two', 'three')); expect(test).toBe(true); }); it('should return true if the iterator is empty', async () => { const test = await subject.all(item => item, asAsync()); expect(test).toBe(true); }); it('should be auto curried', async () => { const test = await subject.all(item => item.length === 3)(asAsync('one', 'two', 'three')); expect(test).toBe(false); }); }); describe('any', () => { it('should return true if any item pass the test', async () => { const test1 = await subject.any(item => item.length === 3, asAsync('one', 'two', 'three')); const test2 = await subject.any(item => item.length === 5, asAsync('one', 'two', 'three')); expect(test1).toBe(true); expect(test2).toBe(true); }); it('should return true if all items fails the test', async () => { const test = await subject.any(item => item.length === 4, asAsync('one', 'two', 'three')); expect(test).toBe(false); }); it('should return false if the iterator is empty', async () => { const test = await subject.any(item => item, asAsync()); expect(test).toBe(false); }); it('should be auto curried', async () => { const test = await subject.any(item => item.length === 3)(asAsync('one', 'two', 'three')); expect(test).toBe(true); }); }); describe('find', () => { it('should return the first item that pass the test', async () => { const item = await subject.find(item => item.length === 3, asAsync('one', 'two', 'three')); const item2 = await subject.find(item => item.length === 5, asAsync('one', 'two', 'three')); expect(item).toEqual('one'); expect(item2).toEqual('three'); }); it('should return undefined if no item pass the test', async () => { const item = await subject.find(item => item.length === 4, asAsync('one', 'two', 'three')); expect(item).toEqual(undefined); }); it('should be auto curried', async () => { const item = await subject.find(item => item.length === 3)(asAsync('one', 'two', 'three')); expect(item).toEqual('one'); }); }); describe('partition', () => { it('should return a tuple of passing and failing elements', async () => { const [even, odd] = await subject.partition(item => item % 2 === 0, asAsync(1, 2, 3, 4, 5)); expect(even).toEqual([2, 4]); expect(odd).toEqual([1, 3, 5]); }); it('should be auto curried', async () => { const [even, odd] = await subject.partition(item => item % 2 === 0)(asAsync(1, 2, 3)); expect(even).toEqual([2]); expect(odd).toEqual([1, 3]); }); }); describe('first', () => { it('should return the first item', async () => { const item = await subject.first(asAsync('one', 'two', 'three')); expect(item).toEqual('one'); }); it('should return undefined if there are no items', async () => { const item = await subject.first(asAsync()); expect(item).toEqual(undefined); }); }); describe('take', () => { it('should return the first n items', async () => { const iterator = subject.take(2, asAsync('one', 'two', 'three'))[Symbol.asyncIterator](); expect(await iterator.next()).toEqual({ done: false, value: 'one' }); expect(await iterator.next()).toEqual({ done: false, value: 'two' }); expect(await iterator.next()).toEqual({ done: true, value: undefined }); }); it('should return avalible items if n is greater than the size of the iterable', async () => { const iterator = subject.take(4, asAsync('one', 'two', 'three'))[Symbol.asyncIterator](); expect(await iterator.next()).toEqual({ done: false, value: 'one' }); expect(await iterator.next()).toEqual({ done: false, value: 'two' }); expect(await iterator.next()).toEqual({ done: false, value: 'three' }); expect(await iterator.next()).toEqual({ done: true, value: undefined }); }); it('should be auto curried', async () => { const iterator = subject.take(2)(asAsync('one', 'two', 'three'))[Symbol.asyncIterator](); expect(await iterator.next()).toEqual({ done: false, value: 'one' }); }); it('should end the iterator immediately after taking the requested number of items', async () => { const controller = new subject.Subject(); const iterator = subject.take(2, controller); const test1 = iterator.next(); const test2 = iterator.next(); controller.next(1); controller.next(2); await test1; await test2; expect(await iterator.next()).toEqual({ done: true }); }); it('should end the iterator immediately when taking zero items', async () => { const controller = new subject.Subject(); const iterator = subject.take(0, controller); const test = iterator.next(); controller.next(1); expect(await test).toEqual({ done: true }); }); }); describe('takeUntil', () => { it('should yield items until the notifier resolves', async () => { const controller = new subject.Subject(); const iterator = subject.takeUntil(controller, asAsync('one', 'two', 'three'))[Symbol.asyncIterator](); expect(await iterator.next()).toEqual({ done: false, value: 'one' }); expect(await iterator.next()).toEqual({ done: false, value: 'two' }); controller.done(); expect(await iterator.next()).toEqual({ done: true, value: { done: true, value: undefined } }); }); it('should return the notifiers IteratorResult', async () => { const controller = new subject.Subject(); const iterator = subject.takeUntil(controller, asAsync('one', 'two', 'three'))[Symbol.asyncIterator](); expect(await iterator.next()).toEqual({ done: false, value: 'one' }); expect(await iterator.next()).toEqual({ done: false, value: 'two' }); controller.next('end'); expect(await iterator.next()).toEqual({ done: true, value: { done: false, value: 'end' } }); }); it('should yield all items if the notifier does not resolve', async () => { const controller = new subject.Subject(); const iterator = subject.takeUntil(controller, asAsync('one', 'two', 'three'))[Symbol.asyncIterator](); expect(await iterator.next()).toEqual({ done: false, value: 'one' }); expect(await iterator.next()).toEqual({ done: false, value: 'two' }); expect(await iterator.next()).toEqual({ done: false, value: 'three' }); expect(await iterator.next()).toEqual({ done: true, value: undefined }); }); }); describe('last', () => { it('should return the last item', async () => { const item = await subject.last(asAsync('one', 'two', 'three')); expect(item).toEqual('three'); }); it('should return undefined if there are no items', async () => { const item = await subject.last(asAsync()); expect(item).toEqual(undefined); }); }); });