trikl
Version:
The antidote for "callback hell"
275 lines (233 loc) • 7.04 kB
JavaScript
const trikl = require('../src/trikl')
describe('trikl()', () => {
test('it returns an object that implements the Promise interface', () => {
let trickle = trikl()
expect(typeof trickle.then).toBe('function')
expect(typeof trickle.catch).toBe('function')
})
test('trickles contain an underlying promise chain', () => {
let trickle = trikl()
expect(trickle.promise).toBeInstanceOf(Promise)
expect(trickle.lastPromise).toBe(trickle.promise)
})
test('the `lastPromise` property advances down the underlying promise chain as links are added', () => {
let trickle = trikl()
trickle.then(() => {})
expect(trickle.lastPromise).toBeInstanceOf(Promise)
expect(trickle.lastPromise).not.toBe(trickle.promise)
})
test('the underlying promise can be resolved manually', () => {
return trikl()
.resolve('result')
.promise
.then(result => {
expect(result).toBe('result')
})
})
test('the underlying promise can be rejected manually', () => {
let trickle = trikl(drip => {})
trikl()
.catch(trickle.resolve)
.reject('rejected')
return trickle.promise
.then(result => {
expect(result).toBe('rejected')
})
})
test('drops can be added to the trickle via `trickle.drop()`', () => {
return trikl()
.drop(drip => drip('dripped'))
.promise
.then(result => {
expect(result).toBe('dripped')
})
})
test('drops must be functions', () => {
expect(trikl().drop.bind(null, 'non-function')).toThrowError(/must be a function/i)
})
test('accepts an optional first drop as an argument', () => {
return trikl(drip => drip('trikl drop'))
.promise
.then(result => {
expect(result).toBe('trikl drop')
})
})
test('drops can be added indefinitely', () => {
return trikl()
.drop(drip => drip(1))
.drop((drip, val) => drip(val + 1))
.drop((drip, val) => drip(val + 1))
.promise
.then(result => {
expect(result).toBe(3)
})
})
test('trickles can be used with Promise.all()', () => {
return Promise.all([
trikl(drip => drip('result 0')),
trikl(drip => drip('result 1'))
]).then(result => {
expect(result[0]).toBe('result 0')
expect(result[1]).toBe('result 1')
})
})
test('trickles can be used with Promise.race()', () => {
return Promise.race([
trikl(drip => setTimeout(drip.bind(null, 'result 00'))),
trikl(drip => drip('result 11'))
]).then(result => {
expect(result).toBe('result 11')
})
})
test('a drop can skip the next drop via `drip.skip()()`', () => {
return trikl(drip => drip.skip()(1))
.drop(drip => drip(2))
.promise
.then(result => {
expect(result).toBe(1)
})
})
test('a drop can skip any number of drops via `drip.skip(num)()`', () => {
return trikl(drip => drip.skip(3)(1))
.drop(drip => drip(2))
.drop(drip => drip(3))
.drop(drip => drip(4))
.promise
.then(result => {
expect(result).toBe(1)
})
})
test('attempting to skip less than one drop will result in one drop being skipped', () => {
return trikl(drip => drip.skip(-3)(1))
.drop((drip, val) => drip(val + 1))
.drop((drip, val) => drip(val + 1))
.drop((drip, val) => drip(val + 1))
.promise
.then(result => {
expect(result).toBe(3)
})
})
test('attempting to skip a drop with something that isNaN results in one drop being skipped', () => {
return trikl(drip => drip.skip('non-int-string')(1))
.drop((drip, val) => drip(val + 1))
.drop((drip, val) => drip(val + 1))
.promise
.then(result => {
expect(result).toBe(2)
})
})
test('a drop can drip through multiple drops', () => {
return trikl(drip => {
drip()
drip()
drip('done')
})
.drop(() => {})
.drop(() => {})
.promise
.then(result => {
expect(result).toBe('done')
})
})
test('throwing anything in a drop will reject the underlying promise', () => {
let trickle = trikl(drip => {})
trikl(drip => {throw 'err'})
.catch(trickle.resolve)
return trickle
.promise
.then(result => {
expect(result).toBe('err')
})
})
test('calling `drip.stop()` will resolve the underlying promise immediately', () => {
return trikl(drip => drip.stop('val'))
.drop(drip => drip.stop('other val'))
.promise
.then(result => {
expect(result).toBe('val')
})
})
test('calling `drip.stop()` with an instance of Error as the first argument will reject the underlying promise', () => {
let trickle = trikl(drip => {})
trikl(drip => drip.stop(new Error()))
.drop(drip => drip('non-error'))
.catch(trickle.resolve)
return trickle
.promise
.then(result => {
expect(result).toBeInstanceOf(Error)
})
})
test('`trikl()` and `trickle.drop()` can be passed an indefinite number of drops', () => {
return trikl(drip => drip('one'), (drip, val) => drip(val + 'two'))
.drop((drip, val) => drip(val + 'three'), (drip, val) => drip(val + 'four'))
.promise
.then(result => {
expect(result).toBe('onetwothreefour')
})
})
test('drop arguments can optionally be accessed via trickle.dropArgs', () => {
let trickle = trikl(drip => drip(1, 2, 3))
.drop(drip => {
let [one, two, three] = trickle.dropArgs
drip(one + two + three)
})
return trickle
.promise
.then(result => {
expect(result).toBe(6)
})
})
test('the `pure` flag prevents the `drip` function from being passed to drops', () => {
let trickle1 = trikl.pure(() => trickle1.drip(1))
.drop(val => trickle1.drip(val + 1))
let trickle2 = trikl.pure()
.drop(() => trickle2.drip(2))
.drop(val => trickle2.drip(val + 2))
return Promise.all([trickle1, trickle2])
.then(result => {
expect(result[0]).toBe(2)
expect(result[1]).toBe(4)
})
})
test('the `halt` flag prevents a trickle from dripping until `trickle.drip()` is called manually', () => {
let trickle = trikl.halt(drip => drip(1))
.drop((drip, val) => drip(val + 1))
setTimeout(trickle.drip)
return trickle
.promise
.then(result => {
expect(result).toBe(2)
})
})
test('the `halt` flag can be used to enforce synchronous execution', () => {
let val = 0
trikl.halt(() => val++)
.drop(() => val++)
.drip()
.drip()
expect(val).toBe(2)
})
test('the `hard` flag passes a wrapped version of `trickle.drip()` to each drop that can be called only once', () => {
return trikl.hard(drip => drip(1))
.drop(drip => drip(2) && drip(3))
.drop(drip => setTimeout(drip.bind(null, 4)))
.promise
.then(result => {
expect(result).toBe(4)
})
})
test('the `halt` and `pure` flags can be used together in any order', () => {
let trickle1 = trikl.pure.halt(() => trickle1.drip(1))
.drop(val => trickle1.drip(val + 1))
let trickle2 = trikl.halt.pure(() => trickle2.drip(2))
.drop(val => trickle2.drip(val + 2))
setTimeout(trickle1.drip)
setTimeout(trickle2.drip)
return Promise.all([trickle1, trickle2])
.then(result => {
expect(result[0]).toBe(2)
expect(result[1]).toBe(4)
})
})
})