UNPKG

@beardedtim/lazy

Version:

A utility belt for async iterables

628 lines (479 loc) 14.5 kB
const { Lazy, Producer, operators } = require("./"); const EE = require("events"); describe("Lazy", () => { it("creates an async iterable out of a generator function", async done => { const gen = function*() { yield 1; yield 2; }; const lazy = new Lazy(gen); const values = []; for await (let v of lazy) { values.push(v); } expect(values).toEqual([1, 2]); return done(); }); }); describe("Producer", () => { it("is a Lazy", () => { const producer = new Producer(); expect(producer instanceof Lazy).toBe(true); }); it("produces a new value to the iteration when next is called", done => { const producer = new Producer(); const message = {}; operators.forEach(value => { expect(value).toBe(message); return done(); }, producer); producer.next(message); }); it("does not produce values after complete is called", done => { const producer = new Producer(); const message = {}; operators.toArray(producer).then(values => { expect(values.length).toBe(1); return done(); }); producer.next(message); producer.complete(); producer.next(message); }); it("does not produce values after error is called", done => { const producer = new Producer(); const message = {}; operators.toArray(producer).then(values => { expect(values.length).toBe(1); return done(); }); producer.next(message); producer.error(); producer.next(message); }); it("produces an error when error is called", async done => { const producer = new Producer(); const message = {}; setTimeout(() => producer.error(message), 100); try { for await (let v of producer) { expect(v).not.toBeDefined(); } } catch (e) { expect(e).toBe(message); } finally { done(); } }); }); describe("Operators", () => { describe("map", () => { it("returns a new Lazy", async done => { const gen = function*() { yield 1; yield 2; }; const lazy = new Lazy(gen); const mapped = operators.map(n => n * 2, lazy); expect(mapped instanceof Lazy).toBe(true); return done(); }); it("transform each iteration by the given function", async done => { const gen = function*() { yield 1; yield 2; }; const lazy = new Lazy(gen); const mapped = operators.map(n => n * 2, lazy); const values = []; for await (let v of mapped) { values.push(v); } expect(values).toEqual([2, 4]); return done(); }); }); describe("filter", () => { it("returns a new Lazy", async done => { const gen = function*() { yield 1; yield 2; }; const lazy = new Lazy(gen); const filtered = operators.filter(n => n > 1, lazy); expect(filtered instanceof Lazy).toBe(true); return done(); }); it("returns an iterator of only values that pass the predicate", async done => { const gen = function*() { yield 1; yield 2; }; const lazy = new Lazy(gen); const filtered = operators.filter(n => n > 1, lazy); const values = []; for await (let v of filtered) { values.push(v); } expect(values).toEqual([2]); return done(); }); }); describe("tap", () => { it("returns a new Lazy", async done => { const gen = function*() { yield 1; yield 2; }; const lazy = new Lazy(gen); const tapped = operators.tap(n => n > 1, lazy); expect(tapped instanceof Lazy).toBe(true); return done(); }); it("calls the function for each iteration", async done => { const gen = function*() { yield 1; yield 2; }; const fn = jest.fn(); const lazy = new Lazy(gen); const tapped = operators.tap(fn, lazy); for await (let _ of tapped) { } expect(fn).toHaveBeenCalledTimes(2); return done(); }); }); describe("take", () => { it("returns a Lazy", () => { const gen = function*() {}; const lazy = new Lazy(gen); const took = operators.take(2, lazy); expect(took instanceof Lazy).toBe(true); }); it("returns an iterable that only takes the first n values of the passed in iterable", async done => { const gen = function*() { yield 1; yield 2; yield 3; yield 4; }; const lazy = new Lazy(gen); const took = operators.take(2, lazy); const values = []; for await (let v of took) { values.push(v); } expect(values).toEqual([1, 2]); return done(); }); it("stops if n is larger than the given iterable", async done => { const gen = function*() { yield 1; yield 2; }; const lazy = new Lazy(gen); const took = operators.take(4, lazy); const values = []; for await (let v of took) { values.push(v); } expect(values).toEqual([1, 2]); return done(); }); }); describe("range", () => { it("returns a Lazy", () => { const lazy = operators.range(1); expect(lazy instanceof Lazy).toBe(true); }); it("defaults to an interable of start -> Infinity", async done => { const lazy = operators.range(1); const took = operators.take(1000, lazy); const values = []; for await (let v of took) { values.push(v); } expect(values.length).toBe(1000); return done(); }); it("creates an iterable from start to end inclusive", async done => { const lazy = operators.range(1, 5); const values = []; for await (let v of lazy) { values.push(v); } expect(values).toEqual([1, 2, 3, 4, 5]); return done(); }); it("maps each item given the transformer", async done => { const lazy = operators.range(1, 5, n => n * 2); const values = []; for await (let v of lazy) { values.push(v); } expect(values).toEqual([2, 4, 6, 8, 10]); return done(); }); }); describe("reduce", () => { it("returns a promise", () => { const lazy = operators.empty(); const reduced = operators.reduce(a => a, {}, lazy); expect(reduced.then).toBeDefined(); }); it("reduces the async iterator into a single value", async done => { const lazy = operators.range(1, 5); const reduced = operators.reduce((a, c) => a + c, 0, lazy); return reduced.then(value => expect(value).toBe(15)).then(done); }); }); describe("flatMap", () => { it("returns a Lazy", () => { const lazy = operators.range(1, 5); const flattened = operators.flatMap(num => range(0, num), lazy); expect(flattened instanceof Lazy).toBe(true); }); it("maps over an iterator, flattening the return value", async done => { const lazy = operators.range(1, 5); const flattened = operators.flatMap(num => operators.range(0, num), lazy); const values = []; for await (let v of flattened) { values.push(v); } expect(values).toEqual([ 0, 1, 0, 1, 2, 0, 1, 2, 3, 0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 5 ]); return done(); }); }); describe("skip", () => { it("returns a Lazy", () => { const lazy = operators.range(1); const skipped = operators.skip(5, lazy); expect(skipped instanceof Lazy).toBe(true); }); it("skips the first n numbers of items", async done => { const lazy = operators.range(1, 5); const skipped = operators.skip(1, lazy); const values = []; for await (let v of skipped) { values.push(v); } expect(values).toEqual([2, 3, 4, 5]); return done(); }); it("returns an empty Lazy if skipped too many", async done => { const lazy = operators.range(1, 2); const skipped = operators.skip(4, lazy); const values = []; for await (let v of skipped) { values.push(v); } expect(values).toEqual([]); return done(); }); }); describe("fromArray", () => { it("returns a Lazy", () => { const lazy = operators.fromArray([]); expect(lazy instanceof Lazy).toBe(true); }); it("creates a Lazy from the given array values", async done => { const arr = [1, 2, 3]; const lazy = operators.fromArray(arr); const values = []; for await (let v of lazy) { values.push(v); } expect(values).toEqual(arr); return done(); }); it("creates a Lazy from a given iterable", async done => { const arr = new Set([1, 2, 3]); const lazy = operators.fromArray(arr); const values = []; for await (let v of lazy) { values.push(v); } expect(values).toEqual([...arr]); return done(); }); }); describe("fromPromise", () => { it("returns a Lazy", () => { const lazy = operators.fromPromise(Promise.resolve({})); expect(lazy instanceof Lazy).toBe(true); }); it("returns a Lazy of the promise", async done => { const value = {}; const lazy = operators.fromPromise(Promise.resolve(value)); const values = []; for await (let v of lazy) { values.push(v); } expect(values).toEqual([value]); return done(); }); }); describe("forEach", () => { it("returns a Promise", () => { const lazy = operators.range(1, 5); const eached = operators.forEach(() => {}, lazy); expect(eached.then).toBeDefined(); }); it("calls a function for each Lazy item", async done => { const lazy = operators.range(1, 5); const fn = jest.fn(); return operators .forEach(fn, lazy) .then(() => expect(fn).toHaveBeenCalledTimes(5)) .then(done); }); }); describe("toArray", () => { it("returns an array of the Lazy values", async done => { const range = operators.range(1, 5); return operators .toArray(range) .then(list => expect(list).toEqual([1, 2, 3, 4, 5])) .then(done); }); }); describe("empty", () => { it("returns a Lazy", () => { const empty = operators.empty(); expect(empty instanceof Lazy).toBe(true); }); it("returns an empty iterable", done => { const empty = operators.empty(); operators .toArray(empty) .then(list => expect(list).toEqual([])) .then(done); }); }); describe("merge", () => { it("returns a Lazy", () => { const merged = operators.merge( (a, b) => a, operators.range(1, 2), operators.range(1, 2) ); expect(merged instanceof Lazy).toBe(true); }); it("merges the two iterators together by the given function", done => { const itera = operators.range(1, 2); const iterb = operators.range(1, 2); const merged = operators.merge((a, b) => a + b, itera, iterb); return operators .toArray(merged) .then(list => expect(list).toEqual([2, 4])) .then(done); }); it("will stop merging once a is done", done => { const itera = operators.range(1, 2); const iterb = operators.range(1); const merged = operators.merge((a, b) => a + b, itera, iterb); return operators .toArray(merged) .then(list => expect(list).toEqual([2, 4])) .then(done); }); }); describe("fromEvent", () => { it("returns a Lazy", () => { const lazy = operators.fromEvent("event", {}); expect(lazy instanceof Lazy).toBe(true); }); it("returns an iterator of the events of the given emitter", async done => { const event = new EE(); const iter = operators.fromEvent("event", event); const message = {}; operators.forEach(value => { expect(value).toBe(message); done(); }, iter); event.emit("event", message); }); }); describe("takeUntil", () => { it("returns a Lazy", () => { const iter = operators.takeUntil(a => true, operators.empty()); expect(iter instanceof Lazy).toBe(true); }); it('takes values from the iterator until the predicate returns true', done => { const pred = (num) => num ? true : false const base = operators.range(0) const iter = operators.takeUntil(pred, base) return operators.toArray(iter) .then(list => { expect(list.length).toBe(1) }).then(done) }) }); describe("takeWhile", () => { it("returns a Lazy", () => { const iter = operators.takeWhile(a => true, operators.empty()); expect(iter instanceof Lazy).toBe(true); }); it('takes values from the iterator while the predicate returns true', done => { const pred = (num) => num === 0 ? true : false const base = operators.range(0) const iter = operators.takeWhile(pred, base) return operators.toArray(iter) .then(list => { expect(list.length).toBe(1) }).then(done) }) }); describe("skipUntil", () => { it("returns a Lazy", () => { const iter = operators.skipUntil(a => true, operators.empty()); expect(iter instanceof Lazy).toBe(true); }); it('skips values until the predicate returns true', done => { const pred = (num) => num === 0 ? false : true const base = operators.range(0, 1) const iter = operators.skipUntil(pred, base) return operators.toArray(iter) .then(list => { expect(list.length).toBe(1) }).then(done) }) }); describe("skipWhile", () => { it("returns a Lazy", () => { const iter = operators.skipWhile(a => true, operators.empty()); expect(iter instanceof Lazy).toBe(true); }); it('skips values until the predicate returns false', done => { const pred = (num) => num === 0 ? true : false const base = operators.range(0, 1) const iter = operators.skipWhile(pred, base) return operators.toArray(iter) .then(list => { expect(list.length).toBe(1) }).then(done) }) }); });