diff --git a/.gitignore b/.gitignore index 65abc3f..47010c9 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ experiments/ node_modules/ coverage/ +perf/logs/ diff --git a/package.json b/package.json index f6419d4..74dfb83 100644 --- a/package.json +++ b/package.json @@ -27,12 +27,13 @@ ], "scripts": { "build-dist": "mkdirp dist && rollup -c", - "build": "npm run build-dist && uglifyjs -c 'warnings=false' -m -o dist/creed.min.js -- dist/creed.js", + "build-es6": "mkdirp dist && rollup -f cjs -o dist/creed.js src/main.js", + "build": "npm run build-dist && uglifyjs -c \"warnings=false\" -m -o dist/creed.min.js -- dist/creed.js", "preversion": "npm run build", "check-coverage": "istanbul check-coverage --statements 100 --branches 100 --lines 100 --functions 100 coverage/coverage*.json", "lint": "jsinspect src && eslint src", "pretest": "npm run lint", - "test": "istanbul cover _mocha", + "test": "istanbul cover node_modules/mocha/bin/_mocha", "posttest": "npm run test-aplus", "test-aplus": "promises-aplus-tests test/aplus.js --reporter dot" }, diff --git a/perf/doxbee-sequential-errors/promises-creed-algebraic.js b/perf/doxbee-sequential-errors/promises-creed-algebraic.js index 8df2c47..736ad7f 100644 --- a/perf/doxbee-sequential-errors/promises-creed-algebraic.js +++ b/perf/doxbee-sequential-errors/promises-creed-algebraic.js @@ -2,7 +2,7 @@ global.useCreed = true; global.useQ = false; global.useBluebird = false; -var creed = require('../../dist/creed'); +var creed = require('../..'); require('../lib/fakesP'); diff --git a/perf/doxbee-sequential-errors/promises-creed-generator.js b/perf/doxbee-sequential-errors/promises-creed-generator.js index 6eac22f..8ee5607 100644 --- a/perf/doxbee-sequential-errors/promises-creed-generator.js +++ b/perf/doxbee-sequential-errors/promises-creed-generator.js @@ -2,7 +2,7 @@ global.useCreed = true; global.useQ = false; global.useBluebird = false; -var creed = require('../../dist/creed'); +var creed = require('../..'); require('../lib/fakesP'); diff --git a/perf/doxbee-sequential-errors/promises-creed.js b/perf/doxbee-sequential-errors/promises-creed.js index dce1b70..733e745 100644 --- a/perf/doxbee-sequential-errors/promises-creed.js +++ b/perf/doxbee-sequential-errors/promises-creed.js @@ -2,7 +2,7 @@ global.useCreed = true; global.useQ = false; global.useBluebird = false; -var creed = require('../../dist/creed'); +var creed = require('../..'); require('../lib/fakesP'); diff --git a/perf/doxbee-sequential/promises-creed-algebraic.js b/perf/doxbee-sequential/promises-creed-algebraic.js index 9ab6b5d..74b5a95 100644 --- a/perf/doxbee-sequential/promises-creed-algebraic.js +++ b/perf/doxbee-sequential/promises-creed-algebraic.js @@ -2,7 +2,7 @@ global.useCreed = true; global.useQ = false; global.useBluebird = false; -var creed = require('../../dist/creed'); +var creed = require('../..'); require('../lib/fakesP'); @@ -52,7 +52,7 @@ module.exports = function upload(stream, idOrPath, tag, done) { }).chain(function() { return File.whereUpdate({id: fileId}, {version: version.id}) .execWithin(tx); - }).map(function() { + }).then(function() { tx.commit(); return done(); }, function(err) { diff --git a/perf/doxbee-sequential/promises-creed-generator.js b/perf/doxbee-sequential/promises-creed-generator.js index d594b87..5ed6323 100644 --- a/perf/doxbee-sequential/promises-creed-generator.js +++ b/perf/doxbee-sequential/promises-creed-generator.js @@ -1,7 +1,7 @@ global.useBluebird = false; global.useQ = false; global.useCreed = true; -var creed = require('../../dist/creed'); +var creed = require('../..'); require('../lib/fakesP'); module.exports = creed.coroutine(function* upload(stream, idOrPath, tag, done) { diff --git a/perf/doxbee-sequential/promises-creed.js b/perf/doxbee-sequential/promises-creed.js index d582818..c0bf159 100644 --- a/perf/doxbee-sequential/promises-creed.js +++ b/perf/doxbee-sequential/promises-creed.js @@ -2,7 +2,7 @@ global.useCreed = true; global.useQ = false; global.useBluebird = false; -var creed = require('../../dist/creed'); +var creed = require('../..'); require('../lib/fakesP'); diff --git a/perf/lib/fakesP.js b/perf/lib/fakesP.js index 9fd13d6..7e8d3ff 100644 --- a/perf/lib/fakesP.js +++ b/perf/lib/fakesP.js @@ -83,7 +83,7 @@ else if (global.useNative) { }; } else if (global.useCreed) { - var lifter = require('../../dist/creed').fromNode; + var lifter = require('../..').fromNode; } else { var lifter = require('when/node').lift; diff --git a/perf/madeup-parallel/promises-creed-generator.js b/perf/madeup-parallel/promises-creed-generator.js index 7dd5ba2..4ce86f7 100644 --- a/perf/madeup-parallel/promises-creed-generator.js +++ b/perf/madeup-parallel/promises-creed-generator.js @@ -3,7 +3,7 @@ global.useQ = false; global.useWhen = false; global.useCreed = true; -var creed = require('../../dist/creed'); +var creed = require('../..'); require('../lib/fakesP'); module.exports = creed.coroutine(function* upload(stream, idOrPath, tag, done) { diff --git a/perf/madeup-parallel/promises-creed.js b/perf/madeup-parallel/promises-creed.js index 301ca5d..c8487e0 100644 --- a/perf/madeup-parallel/promises-creed.js +++ b/perf/madeup-parallel/promises-creed.js @@ -4,7 +4,7 @@ global.useWhen = false; global.useCreed = true; -var creed = require('../../dist/creed'); +var creed = require('../..'); require('../lib/fakesP'); diff --git a/perf/performance.js b/perf/performance.js index 8d42ce8..0bca2b6 100644 --- a/perf/performance.js +++ b/perf/performance.js @@ -1,6 +1,5 @@ var args = require('optimist').argv; - var path = require('path'); global.LIKELIHOOD_OF_REJECTION = args.e || 0.1; @@ -138,7 +137,7 @@ function measure(files, requests, time, parg, callback) { async.mapSeries(files, function(f, done) { console.log("benchmarking", f); var logFile = path.basename(f) + ".log"; - var profileFlags = ["--prof", "--logfile=C:/etc/v8/" + logFile]; + var profileFlags = ["--prof", "--logfile=logs/" + logFile]; var argsFork = [__filename, '--n', requests, diff --git a/src/Action.js b/src/Action.js new file mode 100644 index 0000000..19a360a --- /dev/null +++ b/src/Action.js @@ -0,0 +1,29 @@ +export default class Action { + constructor (promise) { + this.promise = promise + } + + // default onFulfilled action + /* istanbul ignore next */ + fulfilled (p) { + this.promise._become(p) + } + + // default onRejected action + rejected (p) { + this.promise._become(p) + return false + } + + tryCall (f, x) { + let result + // test if `f` (and only it) throws + try { + result = f(x) + } catch (e) { + this.promise._reject(e) + return + } // else + this.handle(result) + } +} diff --git a/src/chain.js b/src/chain.js index 9f48b5a..ae46bcf 100644 --- a/src/chain.js +++ b/src/chain.js @@ -1,3 +1,4 @@ +import Action from './Action' import maybeThenable from './maybeThenable' export default function (f, p, promise) { @@ -5,30 +6,21 @@ export default function (f, p, promise) { return promise } -class Chain { +class Chain extends Action { constructor (f, promise) { + super(promise) this.f = f - this.promise = promise } fulfilled (p) { - try { - runChain(this.f, p.value, this.promise) - } catch (e) { - this.promise._reject(e) - } + this.tryCall(this.f, p.value) } - rejected (p) { - this.promise._become(p) - } -} + handle (y) { + if (!(maybeThenable(y) && typeof y.then === 'function')) { + this.promise._reject(new TypeError('f must return a promise')) + } -function runChain (f, x, p) { - const y = f(x) - if (!(maybeThenable(y) && typeof y.then === 'function')) { - throw new TypeError('f must return a promise') + this.promise._resolve(y) } - - p._resolve(y) } diff --git a/src/coroutine.js b/src/coroutine.js index cee3dc4..e405754 100644 --- a/src/coroutine.js +++ b/src/coroutine.js @@ -1,25 +1,31 @@ +import Action from './Action' + export default function (resolve, iterator, promise) { new Coroutine(resolve, iterator, promise).run() return promise } -class Coroutine { +class Coroutine extends Action { constructor (resolve, iterator, promise) { + super(promise) this.resolve = resolve - this.iterator = iterator - this.promise = promise + this.generator = iterator } run () { - this.step(this.iterator.next, void 0) + this.tryStep(this.generator.next, void 0) } - step (continuation, x) { + tryStep (resume, x) { + let result + // test if `resume` (and only it) throws try { - this.handle(continuation.call(this.iterator, x)) + result = resume.call(this.generator, x) } catch (e) { this.promise._reject(e) - } + return + } // else + this.handle(result) } handle (result) { @@ -27,15 +33,15 @@ class Coroutine { return this.promise._resolve(result.value) } - this.resolve(result.value)._runAction(this) + this.resolve(result.value)._when(this) } fulfilled (ref) { - this.step(this.iterator.next, ref.value) + this.tryStep(this.generator.next, ref.value) } rejected (ref) { - this.step(this.iterator.throw, ref.value) + this.tryStep(this.generator.throw, ref.value) return true } } diff --git a/src/delay.js b/src/delay.js index fdb5827..cef6765 100644 --- a/src/delay.js +++ b/src/delay.js @@ -1,22 +1,20 @@ +import Action from './Action' + export default function (ms, p, promise) { p._runAction(new Delay(ms, promise)) return promise } -class Delay { +class Delay extends Action { constructor (time, promise) { + super(promise) this.time = time - this.promise = promise } fulfilled (p) { /*global setTimeout*/ setTimeout(become, this.time, p, this.promise) } - - rejected (p) { - this.promise._become(p) - } } function become (p, promise) { diff --git a/src/inspect.js b/src/inspect.js index 951de89..54c074e 100644 --- a/src/inspect.js +++ b/src/inspect.js @@ -47,6 +47,7 @@ export function silenceError (p) { p._runAction(silencer) } +// implements Action const silencer = { fulfilled () {}, rejected: setHandled diff --git a/src/iterable.js b/src/iterable.js index d3eeec2..a9d3c8b 100644 --- a/src/iterable.js +++ b/src/iterable.js @@ -1,4 +1,5 @@ import { isFulfilled, isRejected, silenceError } from './inspect' +import Action from './Action' import maybeThenable from './maybeThenable' export function resultsArray (iterable) { @@ -58,18 +59,22 @@ function handleItem (resolve, handler, x, i, promise) { } else if (isRejected(p)) { handler.rejectAt(p, i, promise) } else { - settleAt(p, handler, i, promise) + p._runAction(new Indexed(handler, i, promise)) } } -function settleAt (p, handler, i, promise) { - p._runAction({handler, i, promise, fulfilled, rejected}) -} +class Indexed extends Action { + constructor (handler, i, promise) { + super(promise) + this.i = i + this.handler = handler + } -function fulfilled (p) { - this.handler.fulfillAt(p, this.i, this.promise) -} + fulfilled (p) { + this.handler.fulfillAt(p, this.i, this.promise) + } -function rejected (p) { - return this.handler.rejectAt(p, this.i, this.promise) + rejected (p) { + return this.handler.rejectAt(p, this.i, this.promise) + } } diff --git a/src/main.js b/src/main.js index afee964..e4b0b86 100644 --- a/src/main.js +++ b/src/main.js @@ -30,7 +30,7 @@ export { // coroutine :: Generator e a -> (...* -> Promise e a) // Make a coroutine from a promise-yielding generator export function coroutine (generator) { - return function (...args) { + return function coroutinified (...args) { return runGenerator(generator, this, args) } } @@ -50,7 +50,7 @@ function runGenerator (generator, thisArg, args) { // fromNode :: NodeApi e a -> (...args -> Promise e a) // Turn a Node API into a promise API export function fromNode (f) { - return function (...args) { + return function promisified (...args) { return runResolver(_runNode, f, this, args, new Future()) } } diff --git a/src/map.js b/src/map.js index e0469b5..8ec6f6b 100644 --- a/src/map.js +++ b/src/map.js @@ -1,24 +1,21 @@ +import Action from './Action' + export default function (f, p, promise) { p._when(new Map(f, promise)) return promise } -class Map { +class Map extends Action { constructor (f, promise) { + super(promise) this.f = f - this.promise = promise } fulfilled (p) { - try { - const f = this.f - this.promise._fulfill(f(p.value)) - } catch (e) { - this.promise._reject(e) - } + this.tryCall(this.f, p.value) } - rejected (p) { - this.promise._become(p) + handle (result) { + this.promise._fulfill(result) } } diff --git a/src/then.js b/src/then.js index 435fd54..3f47e24 100644 --- a/src/then.js +++ b/src/then.js @@ -1,38 +1,35 @@ +import Action from './Action' + export default function then (f, r, p, promise) { p._when(new Then(f, r, promise)) return promise } -class Then { +class Then extends Action { constructor (f, r, promise) { + super(promise) this.f = f this.r = r - this.promise = promise } fulfilled (p) { - runThen(this.f, p, this.promise) + this.runThen(this.f, p) } rejected (p) { - return runThen(this.r, p, this.promise) + return this.runThen(this.r, p) } -} -function runThen (f, p, promise) { - if (typeof f !== 'function') { - promise._become(p) - return false + runThen (f, p) { + if (typeof f !== 'function') { + this.promise._become(p) + return false + } + this.tryCall(f, p.value) + return true } - tryMapNext(f, p.value, promise) - return true -} - -function tryMapNext (f, x, promise) { - try { - promise._resolve(f(x)) - } catch (e) { - promise._reject(e) + handle (result) { + this.promise._resolve(result) } } diff --git a/src/timeout.js b/src/timeout.js index adce7de..75b8e66 100644 --- a/src/timeout.js +++ b/src/timeout.js @@ -1,3 +1,4 @@ +import Action from './Action' import TimeoutError from './TimeoutError' export default function (ms, p, promise) { @@ -6,10 +7,10 @@ export default function (ms, p, promise) { return promise } -class Timeout { +class Timeout extends Action { constructor (timer, promise) { + super(promise) this.timer = timer - this.promise = promise } fulfilled (p) { @@ -19,8 +20,7 @@ class Timeout { rejected (p) { clearTimeout(this.timer) - this.promise._become(p) - return false + return super.rejected(p) } } diff --git a/test/Promise-test.js b/test/Promise-test.js index 6b4dc5e..e9b288e 100644 --- a/test/Promise-test.js +++ b/test/Promise-test.js @@ -14,32 +14,58 @@ describe('Promise', () => { }) it('should reject if resolver throws synchronously', () => { - let expected = new Error() + const expected = new Error() return new Promise(() => { throw expected }) .then(assert.ifError, x => assert.strictEqual(expected, x)) }) - it('should fulfill with value', () => { - let expected = {} - return new Promise(resolve => resolve(expected)) - .then(x => assert.strictEqual(expected, x)) - }) + describe('resolvers', () => { + it('should fulfill with value', () => { + const expected = {} + return new Promise(resolve => resolve(expected)) + .then(x => assert.strictEqual(expected, x)) + }) - it('should resolve to fulfilled promise', () => { - let expected = {} - return new Promise(resolve => resolve(fulfill(expected))) - .then(x => assert.strictEqual(expected, x)) - }) + it('should resolve to fulfilled promise', () => { + const expected = {} + return new Promise(resolve => resolve(fulfill(expected))) + .then(x => assert.strictEqual(expected, x)) + }) - it('should resolve to rejected promise', () => { - let expected = {} - return new Promise(resolve => resolve(reject(expected))) - .then(assert.ifError, x => assert.strictEqual(expected, x)) - }) + it('should resolve to rejected promise', () => { + const expected = new Error() + return new Promise(resolve => resolve(reject(expected))) + .then(assert.ifError, x => assert.strictEqual(expected, x)) + }) - it('should reject with value', () => { - let expected = {} - return new Promise((resolve, reject) => reject(expected)) - .then(assert.ifError, x => assert.strictEqual(expected, x)) + it('should reject with value', () => { + const expected = new Error() + return new Promise((resolve, reject) => reject(expected)) + .then(assert.ifError, x => assert.strictEqual(expected, x)) + }) + + it('should asynchronously fulfill with value', () => { + const expected = {} + return new Promise(resolve => setTimeout(resolve, 1, expected)) + .then(x => assert.strictEqual(expected, x)) + }) + + it('should asynchronously resolve to fulfilled promise', () => { + const expected = {} + return new Promise(resolve => setTimeout(resolve, 1, fulfill(expected))) + .then(x => assert.strictEqual(expected, x)) + }) + + it('should asynchronously resolve to rejected promise', () => { + const expected = new Error() + return new Promise(resolve => setTimeout(resolve, 1, reject(expected))) + .then(assert.ifError, x => assert.strictEqual(expected, x)) + }) + + it('should asynchronously reject with value', () => { + const expected = new Error() + return new Promise((resolve, reject) => setTimeout(reject, 1, reject(expected))) + .then(assert.ifError, x => assert.strictEqual(expected, x)) + }) }) }) diff --git a/test/concat-test.js b/test/concat-test.js index afa61dd..dc22460 100644 --- a/test/concat-test.js +++ b/test/concat-test.js @@ -22,26 +22,32 @@ describe('concat', function () { assert.strictEqual(p2, p1.concat(p2)) }) - it('should return earlier future', () => { + it('should behave like earlier future', () => { + const expected = {} + const p = delay(1, expected).concat(delay(10)) + return assertSame(p, fulfill(expected)) + }) + + it('should behave like other earlier future', () => { const expected = {} const p = delay(10).concat(delay(1, expected)) return assertSame(p, fulfill(expected)) }) - it('should behave like fulfilled', () => { + it('should return other with fulfilled', () => { const expected = {} const p = fulfill(expected) return assert.strictEqual(delay(10).concat(p), p) }) - it('should behave like rejected', () => { + it('should return other with rejected', () => { const expected = {} const p = reject(expected) silenceError(p) return assert.strictEqual(delay(10).concat(p), p) }) - it('should behave like never', () => { + it('should be identity with never', () => { const p2 = never() const p1 = delay(10) return assert.strictEqual(p1.concat(p2), p1) diff --git a/test/fulfill-test.js b/test/fulfill-test.js index ad5d11d..c6342a2 100644 --- a/test/fulfill-test.js +++ b/test/fulfill-test.js @@ -30,7 +30,7 @@ describe('fulfill', () => { assert.strictEqual(p, p.catch(assert.ifError)) }) - it('then should be identity when typeof f !== function', () => { + it('then should be identity without f callback', () => { const p = fulfill(true) assert.strictEqual(p, p.then()) }) diff --git a/test/future-test.js b/test/future-test.js index 7691849..c453a01 100644 --- a/test/future-test.js +++ b/test/future-test.js @@ -1,11 +1,14 @@ import { describe, it } from 'mocha' -import { future, reject, fulfill, never, Future } from '../src/Promise' +import { future, reject, fulfill, isSettled, isPending, never } from '../src/main' +import { Future } from '../src/Promise' import { silenceError } from '../src/inspect' import { assertSame } from './lib/test-util' import assert from 'assert' +const silenced = p => (silenceError(p), p) const f = x => x + 1 const fp = x => fulfill(x + 1) +const rp = x => silenced(reject(x)) describe('future', () => { it('should return { resolve, promise }', () => { @@ -50,77 +53,117 @@ describe('future', () => { describe('state', () => { it('should have fulfilled state', () => { const { resolve, promise } = future() - const p = fulfill(1) resolve(p) - assert.equal(p.state(), promise.state()) + assert.strictEqual(p.state(), promise.state()) }) it('should have rejected state', () => { const { resolve, promise } = future() - - const p = reject(1) - silenceError(p) + const p = silenced(reject(1)) resolve(p) - assert.equal(p.state(), promise.state()) + assert.strictEqual(p.state(), promise.state()) }) it('should have never state', () => { const { resolve, promise } = future() - const p = never() resolve(p) - assert.equal(p.state(), promise.state()) + assert.strictEqual(p.state(), promise.state()) }) }) describe('inspect', () => { it('should have fulfilled state', () => { const { resolve, promise } = future() - const p = fulfill(1) resolve(p) - assert.equal(p.inspect(), promise.inspect()) + assert.strictEqual(p.inspect(), promise.inspect()) }) it('should have rejected state', () => { const { resolve, promise } = future() - - const p = reject(1) - silenceError(p) + const p = silenced(reject(1)) resolve(p) - assert.equal(p.inspect(), promise.inspect()) + assert.strictEqual(p.inspect(), promise.inspect()) }) it('should have never state', () => { const { resolve, promise } = future() + const p = never() + resolve(p) + assert.strictEqual(p.inspect(), promise.inspect()) + }) + }) + + describe('then', () => { + it('should behave like mapped for fulfill', () => { + const { resolve, promise } = future() + const p = fulfill(1) + resolve(p) + return assertSame(p.map(f), promise.then(f)) + }) + + it('should behave like chained for fulfill', () => { + const { resolve, promise } = future() + const p = fulfill(1) + resolve(p) + return assertSame(p.chain(fp), promise.then(fp)) + }) + + it('should behave like rejection chained for fulfill', () => { + const { resolve, promise } = future() + const p = fulfill(1) + resolve(p) + return assertSame(p.chain(rp), promise.then(rp)) + }) + + it('should be identity for reject', () => { + const { resolve, promise } = future() + const p = silenced(reject(1)) + resolve(p) + assert.strictEqual(p, promise.then(f)) + }) + it('should be identity for never', () => { + const { resolve, promise } = future() const p = never() resolve(p) - assert.equal(p.inspect(), promise.inspect()) + assert.strictEqual(p, promise.then(f)) }) }) describe('catch', () => { - it('should behave like fulfilled', () => { + it('should be identity for fulfill', () => { const { resolve, promise } = future() - const p = fulfill(1) resolve(p) assert.strictEqual(p, promise.catch(f)) }) - it('should have rejected state', () => { + it('should behave like mapped for reject', () => { const { resolve, promise } = future() - const p = reject(1) resolve(p) return assertSame(p.catch(f), promise.catch(f)) }) - it('should have never state', () => { + it('should behave like chained for reject', () => { const { resolve, promise } = future() + const p = reject(1) + resolve(p) + return assertSame(p.catch(fp), promise.catch(fp)) + }) + it('should behave like rejection chained for reject', () => { + const { resolve, promise } = future() + const p = reject(1) + resolve(p) + return assertSame(p.catch(rp), promise.catch(rp)) + }) + + it('should be identity for never', () => { + const { resolve, promise } = future() const p = never() resolve(p) assert.strictEqual(p, promise.catch(f)) @@ -128,26 +171,22 @@ describe('future', () => { }) describe('map', () => { - it('should behave like fulfilled', () => { + it('should behave like mapped for fulfill', () => { const { resolve, promise } = future() - const p = fulfill(1) resolve(p) return assertSame(p.map(f), promise.map(f)) }) - it('should have rejected state', () => { + it('should be identity for reject', () => { const { resolve, promise } = future() - - const p = reject(1) - silenceError(p) + const p = silenced(reject(1)) resolve(p) assert.strictEqual(p, promise.map(f)) }) - it('should have never state', () => { + it('should be identity for never', () => { const { resolve, promise } = future() - const p = never() resolve(p) assert.strictEqual(p, promise.map(f)) @@ -155,26 +194,29 @@ describe('future', () => { }) describe('chain', () => { - it('should behave like fulfilled', () => { + it('should behave like chained for fulfill', () => { const { resolve, promise } = future() - const p = fulfill(1) resolve(p) return assertSame(p.chain(fp), promise.chain(fp)) }) - it('should have rejected state', () => { + it('should behave like rejection chained for fulfill', () => { const { resolve, promise } = future() + const p = fulfill(1) + resolve(p) + return assertSame(p.chain(rp), promise.chain(rp)) + }) - const p = reject(1) - silenceError(p) + it('should be identity for reject', () => { + const { resolve, promise } = future() + const p = silenced(reject(1)) resolve(p) assert.strictEqual(p, promise.chain(fp)) }) - it('should have never state', () => { + it('should be identity for never', () => { const { resolve, promise } = future() - const p = never() resolve(p) assert.strictEqual(p, promise.chain(fp)) @@ -182,27 +224,23 @@ describe('future', () => { }) describe('ap', () => { - it('should behave like fulfilled', () => { + it('should behave like apply for fulfill', () => { const { resolve, promise } = future() - const p = fulfill(f) const q = fulfill(1) resolve(p) return assertSame(p.ap(q), promise.ap(q)) }) - it('should behave like rejected', () => { + it('should be identity for reject', () => { const { resolve, promise } = future() - - const p = reject(f) - silenceError(p) + const p = silenced(reject(f)) resolve(p) assert.strictEqual(p, promise.ap(fulfill(1))) }) - it('should behave like never', () => { + it('should be identity for never', () => { const { resolve, promise } = future() - const p = never() resolve(p) return assert.strictEqual(p, promise.ap(fulfill(1))) @@ -210,36 +248,267 @@ describe('future', () => { }) describe('concat', () => { - it('should behave like fulfilled', () => { + it('should be identity for fulfill', () => { const { resolve, promise } = future() - const p1 = fulfill(1) const p2 = fulfill(2) - resolve(p1) - return assertSame(p1.concat(p2), promise.concat(p2)) + assert.strictEqual(p1, promise.concat(p2)) }) - it('should behave like rejected', () => { + it('should be identity for reject', () => { const { resolve, promise } = future() + const p1 = silenced(reject(new Error())) + const p2 = silenced(reject(new Error())) + resolve(p1) + assert.strictEqual(p1, promise.concat(p2)) + }) - const p1 = reject(new Error()) - const p2 = reject(new Error()) - silenceError(p1) - silenceError(p2) - + it('should return other for never', () => { + const { resolve, promise } = future() + const p1 = never() + const p2 = fulfill(2) resolve(p1) assert.strictEqual(p1.concat(p2), promise.concat(p2)) }) + }) + }) + + describe('before being resolved to another promise', () => { + describe('state', () => { + it('should be pending', () => { + const { promise } = future() + assert(isPending(promise)) + }) - it('should behave like never', () => { + it('should not be settled', () => { + const { promise } = future() + assert(!isSettled(promise)) + }) + }) + + describe('inspect', () => { + it('should not be fulfilled', () => { + const { promise } = future() + assert.notStrictEqual(fulfill().inspect(), promise.inspect()) + }) + + it('should not be rejected', () => { + const { promise } = future() + assert.notStrictEqual(silenced(reject()).inspect(), promise.inspect()) + }) + }) + + describe('then', () => { + it('should behave like mapped for fulfill', () => { const { resolve, promise } = future() + const p = fulfill(1) + const res = promise.then(f) + resolve(p) + return assertSame(p.map(f), res) + }) - const p1 = never() - const p2 = fulfill(2) + it('should behave like chained for fulfill', () => { + const { resolve, promise } = future() + const p = fulfill(1) + const res = promise.then(fp) + resolve(p) + return assertSame(p.chain(fp), res) + }) - resolve(p1) - return assertSame(p1.concat(p2), promise.concat(p2)) + it('should behave like rejection chained for fulfill', () => { + const { resolve, promise } = future() + const p = fulfill(1) + const res = promise.then(rp) + resolve(p) + return assertSame(p.chain(rp), res) + }) + + it('should behave like rejected for reject', () => { + const { resolve, promise } = future() + const p = silenced(reject(1)) + const res = promise.then(f) + resolve(p) + return assertSame(p, res) + }) + + /* it('should have never state for never (#30)', () => { + const { resolve, promise } = future() + const p = never() + const res = promise.then(f) + resolve(p) + assert(isNever(res)) + }) */ + }) + + describe('catch', () => { + it('should behave like fulfilled for fulfill', () => { + const { resolve, promise } = future() + const p = fulfill(1) + const res = promise.catch(f) + resolve(p) + return assertSame(p, res) + }) + + it('should behave like mapped for reject', () => { + const { resolve, promise } = future() + const p = reject(1) + const res = promise.catch(f) + resolve(p) + return assertSame(p.catch(f), res) + }) + + it('should behave like chained for reject', () => { + const { resolve, promise } = future() + const p = reject(1) + const res = promise.catch(fp) + resolve(p) + return assertSame(p.catch(fp), res) + }) + + it('should behave like rejection chained for reject', () => { + const { resolve, promise } = future() + const p = reject(1) + const res = promise.catch(rp) + resolve(p) + return assertSame(p.catch(rp), res) + }) + + /* it('should have never state for never (#30)', () => { + const { resolve, promise } = future() + const p = never() + const res = promise.catch(f) + resolve(p) + assert(isNever(res)) + }) */ + }) + + describe('map', () => { + it('should behave like mapped for fulfill', () => { + const { resolve, promise } = future() + const p = fulfill(1) + const res = promise.map(f) + resolve(p) + return assertSame(p.map(f), res) + }) + + it('should behave like rejection for reject', () => { + const { resolve, promise } = future() + const p = silenced(reject(1)) + const res = promise.map(f) + resolve(p) + return assertSame(p, res) + }) + + /* it('should have never state for never (#30)', () => { + const { resolve, promise } = future() + const p = never() + const res = promise.map(f) + resolve(p) + assert(isNever(res)) + }) */ + }) + + describe('ap', () => { + it('should behave like apply for fulfill', () => { + const { resolve, promise } = future() + const p = fulfill(f) + const q = fulfill(1) + const res = promise.ap(q) + resolve(p) + return assertSame(p.ap(q), res) + }) + + it('should behave like rejected for reject', () => { + const { resolve, promise } = future() + const p = silenced(reject(f)) + const res = promise.ap(fulfill(1)) + resolve(p) + return assertSame(p, res) + }) + + /* it('should have never state for never (#30)', () => { + const { resolve, promise } = future() + const p = never() + const res = promise.ap(fulfill(1)) + resolve(p) + assert(isNever(res)) + }) */ + }) + + describe('chain', () => { + it('should behave like chained for fulfill', () => { + const { resolve, promise } = future() + const p = fulfill(1) + const res = promise.chain(fp) + resolve(p) + return assertSame(p.chain(fp), res) + }) + + it('should behave like rejection chained for fulfill', () => { + const { resolve, promise } = future() + const p = fulfill(1) + const res = promise.chain(rp) + resolve(p) + return assertSame(p.chain(rp), res) + }) + + it('should behave like rejected for reject', () => { + const { resolve, promise } = future() + const p = silenced(reject(1)) + const res = promise.chain(fp) + resolve(p) + return assertSame(p, res) + }) + + /* it('should have never state for never (#30)', () => { + const { resolve, promise } = future() + const p = never() + const res = promise.chain(fp) + resolve(p) + assert(isNever(res)) + }) */ + }) + + describe('concat', () => { + it('should behave like fulfilled other for fulfill', () => { + const { resolve, promise } = future() + const p = fulfill(2) + const res = promise.concat(p) + resolve(fulfill(1)) + return assertSame(p, res) + }) + + it('should behave like rejected other for fulfill', () => { + const { resolve, promise } = future() + const p = silenced(reject(2)) + const res = promise.concat(p) + resolve(fulfill(1)) + return assertSame(p, res) + }) + + it('should behave like fulfilled other for reject', () => { + const { resolve, promise } = future() + const p = fulfill(2) + const res = promise.concat(p) + resolve(silenced(reject(1))) + return assertSame(p, res) + }) + + it('should behave like rejected other for reject', () => { + const { resolve, promise } = future() + const p = silenced(reject(2)) + const res = promise.concat(p) + resolve(silenced(reject(1))) + return assertSame(p, res) + }) + + it('should behave like other for never', () => { + const { resolve, promise } = future() + const p = fulfill(2) + const res = promise.concat(p) + resolve(never()) + return assertSame(p, res) }) }) }) diff --git a/test/lib/test-util.js b/test/lib/test-util.js index f050f15..d1bd76b 100644 --- a/test/lib/test-util.js +++ b/test/lib/test-util.js @@ -3,7 +3,8 @@ import assert from 'assert' export function assertSame (ap, bp) { - return ap.then(a => bp.then(b => assert(a === b))) + return ap.then(a => bp.then(b => assert.strictEqual(a, b)), + a => bp.then(x => { throw x }, b => assert.strictEqual(a, b))) } export function throwingIterable (e) { diff --git a/test/of-test.js b/test/of-test.js index e2bc785..d46d0c0 100644 --- a/test/of-test.js +++ b/test/of-test.js @@ -1,27 +1,27 @@ import { describe, it } from 'mocha' -import { Future, reject } from '../src/Promise' +import { Promise, reject } from '../src/main' import { silenceError, getValue } from '../src/inspect' import assert from 'assert' -describe('fulfill', () => { +describe('of', () => { it('should wrap value', () => { const x = {} - return Future.of(x).then(y => assert(x === y)) + return Promise.of(x).then(y => assert.strictEqual(x, y)) }) it('should be immediately fulfilled', () => { - let x = {} - assert.strictEqual(x, getValue(Future.of(x))) + const x = {} + assert.strictEqual(x, getValue(Promise.of(x))) }) it('should wrap promise', () => { - const x = Future.of({}) - return Future.of(x).then(y => assert(x === y)) + const x = Promise.of({}) + return Promise.of(x).then(y => assert.strictEqual(x, y)) }) it('should wrap rejected promise', () => { const x = reject({}) silenceError(x) - return Future.of(x).then(y => assert(x === y)) + return Promise.of(x).then(y => assert.strictEqual(x, y)) }) }) diff --git a/test/reject-test.js b/test/reject-test.js index 1227dd9..2819d58 100644 --- a/test/reject-test.js +++ b/test/reject-test.js @@ -4,26 +4,26 @@ import { silenceError } from '../src/inspect' import assert from 'assert' describe('reject', () => { - it('then should be identity without f', () => { + it('then should be identity without r callback', () => { const p = reject(true) silenceError(p) assert.strictEqual(p, p.then(assert.ifError)) }) it('map should be identity', () => { - var p = reject(true) + const p = reject(true) silenceError(p) assert.strictEqual(p, p.map(assert.ifError)) }) it('ap should be identity', () => { - var p = reject(assert.ifError) + const p = reject(assert.ifError) silenceError(p) assert.strictEqual(p, p.ap(fulfill(true))) }) it('chain should be identity', () => { - var p = reject() + const p = reject() silenceError(p) assert.strictEqual(p, p.chain(fulfill)) }) diff --git a/test/then-test.js b/test/then-test.js index b3a5d75..0132906 100644 --- a/test/then-test.js +++ b/test/then-test.js @@ -2,7 +2,7 @@ import { describe, it } from 'mocha' import { delay, reject } from '../src/main' import assert from 'assert' -describe('map', function () { +describe('then', function () { it('should not change value when f is not a function', () => { let expected = {} return delay(1, expected).then()