You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

554 lines
12 KiB

/*global describe,it */
/**
* Module dependencies.
*/
var assert = require('assert');
var MPromise = require('../');
/**
* Test.
*/
describe('promise', function () {
it('events fire right after fulfill()', function (done) {
var promise = new MPromise()
, called = 0;
promise.on('fulfill', function (a, b) {
assert.equal(a, '1');
assert.equal(b, '2');
called++;
});
promise.fulfill('1', '2');
promise.on('fulfill', function (a, b) {
assert.equal(a, '1');
assert.equal(b, '2');
called++;
});
assert.equal(2, called);
done();
});
it('events fire right after reject()', function (done) {
var promise = new MPromise()
, called = 0;
promise.on('reject', function (err) {
assert.ok(err instanceof Error);
called++;
});
promise.reject(new Error('booyah'));
promise.on('reject', function (err) {
assert.ok(err instanceof Error);
called++;
});
assert.equal(2, called);
done()
});
describe('onResolve()', function () {
it('from constructor works', function (done) {
var called = 0;
var promise = new MPromise(function (err) {
assert.ok(err instanceof Error);
called++;
});
promise.reject(new Error('dawg'));
assert.equal(1, called);
done();
});
it('after fulfill()', function (done) {
var promise = new MPromise()
, called = 0;
promise.fulfill('woot');
promise.onResolve(function (err, data) {
assert.equal(data, 'woot');
called++;
});
promise.onResolve(function (err) {
assert.strictEqual(err, null);
called++;
});
assert.equal(2, called);
done();
})
});
describe('onFulfill shortcut', function () {
it('works', function (done) {
var promise = new MPromise()
, called = 0;
promise.onFulfill(function (woot) {
assert.strictEqual(woot, undefined);
called++;
});
promise.fulfill();
assert.equal(1, called);
done();
});
});
describe('onReject shortcut', function () {
it('works', function (done) {
var promise = new MPromise()
, called = 0;
promise.onReject(function (err) {
assert.ok(err instanceof Error);
called++;
});
promise.reject(new Error);
assert.equal(1, called);
done();
})
});
describe('return values', function () {
it('on()', function (done) {
var promise = new MPromise();
assert.ok(promise.on('jump', function () {}) instanceof MPromise);
done()
});
it('onFulfill()', function (done) {
var promise = new MPromise();
assert.ok(promise.onFulfill(function () {}) instanceof MPromise);
done();
});
it('onReject()', function (done) {
var promise = new MPromise();
assert.ok(promise.onReject(function () {}) instanceof MPromise);
done();
});
it('onResolve()', function (done) {
var promise = new MPromise();
assert.ok(promise.onResolve(function () {}) instanceof MPromise);
done();
})
});
describe('casting errors', function () {
describe('reject()', function () {
it('does not cast arguments to Error', function (done) {
var p = new MPromise(function (err) {
assert.equal(3, err);
done();
});
p.reject(3);
})
})
});
describe('then', function () {
describe('catching', function () {
it('should not catch returned promise fulfillments', function (done) {
var errorSentinal
, p = new MPromise;
p.then(function () { throw errorSentinal = new Error("boo!") });
p.fulfill();
done();
});
it('should not catch returned promise fulfillments even async', function (done) {
var errorSentinal
, p = new MPromise;
p.then(function () { throw errorSentinal = new Error("boo!") });
setTimeout(function () {
p.fulfill();
done();
}, 10);
});
it('can be disabled using .end()', function (done) {
if (process.version.indexOf('v0.8') == 0) return done();
var errorSentinal
, overTimeout
, domain = require('domain').create();
domain.once('error', function (err) {
assert(err, errorSentinal);
clearTimeout(overTimeout);
done()
});
domain.run(function () {
var p = new MPromise;
var p2 = p.then(function () {
throw errorSentinal = new Error('shucks')
});
p2.end();
p.fulfill();
});
overTimeout = setTimeout(function () { done(new Error('error was swallowed')); }, 10);
});
it('can be disabled using .end() even when async', function (done) {
if (process.version.indexOf('v0.10') != 0) return done();
var errorSentinal
, overTimeout
, domain = require('domain').create();
domain.on('error', function (err) {
assert(err, errorSentinal);
clearTimeout(overTimeout);
done()
});
domain.run(function () {
var p = new MPromise;
var p2 = p.then(function () {
throw errorSentinal = new Error("boo!")
});
p2.end();
setTimeout(function () {p.fulfill();}, 10);
});
overTimeout = setTimeout(function () { done(new Error('error was swallowed')); }, 20);
});
it('can be handled using .end() so no throwing', function (done) {
var errorSentinal
, overTimeout
, domain = require('domain').create();
domain.run(function () {
var p = new MPromise;
var p2 = p.then(function () {
throw errorSentinal = new Error("boo!")
});
p2.end(function (err) {
assert.equal(err, errorSentinal);
clearTimeout(overTimeout);
done()
});
setTimeout(function () {p.fulfill();}, 10);
});
overTimeout = setTimeout(function () { done(new Error('error was swallowed')); }, 20);
});
});
it('persistent', function (done) {
var p = new MPromise;
v = null;
function ensure(val) {
v = v || val;
assert.equal(v, val);
}
function guard() {
throw new Error('onReject should not be called');
}
p.then(ensure, guard).end();
p.fulfill('foo');
p.fulfill('bar');
p.reject(new Error('baz'));
p.then(ensure, guard).end();
setTimeout(done, 0);
});
it('accepts multiple completion values', function (done) {
var p = new MPromise;
p.then(function (a, b) {
assert.equal(2, arguments.length);
assert.equal('hi', a);
assert.equal(4, b);
done();
}, done).end();
p.fulfill('hi', 4);
})
});
describe('fulfill values and splats', function () {
it('should handle multiple values', function (done) {
var p = new MPromise;
p.onFulfill(function (a, b, c) {
assert.equal('a', a);
assert.equal('b', b);
assert.equal('c', c);
done();
});
p.fulfill('a', 'b', 'c');
});
it('should handle multiple values from a then', function (done) {
MPromise.fulfilled().then(
function () {
return MPromise.fulfilled().then(
function () {
var p = new MPromise;
p.fulfill('a', 'b', 'c');
return p;
}
);
}
).onFulfill(
function (a, b, c) {
assert.equal('a', a);
assert.equal('b', b);
assert.equal('c', c);
done();
}
).end()
});
it('should work with `fulfilled` convenience method', function (done) {
MPromise.fulfilled('a', 'b', 'c').then(function (a, b, c) {
assert.equal('a', a);
assert.equal('b', b);
assert.equal('c', c);
done();
})
});
});
describe('end', function () {
it("should return the promise", function (done) {
var p = new MPromise;
var p1 = p.end();
assert.equal(p, p1);
done();
});
it("should throw for chain", function (done) {
var p = new MPromise;
p.then().then().then().then().end();
try {
p.reject('bad');
} catch (e) {
done();
}
});
it("should not throw for chain with reject handler", function (done) {
var p = new MPromise;
p.then().then().then().then().end(function () {
done();
});
try {
p.reject('bad');
} catch (e) {
done(e);
}
});
});
describe('chain', function () {
it('should propagate fulfillment', function (done) {
var varSentinel = {a: 'a'};
var p1 = new MPromise;
p1.chain(new MPromise(function (err, doc) {
assert.equal(doc, varSentinel);
done();
}));
p1.fulfill(varSentinel);
});
it('should propagate rejection', function (done) {
var e = new Error("gaga");
var p1 = new MPromise;
p1.chain(new MPromise(function (err) {
assert.equal(err, e);
done();
}));
p1.reject(e);
});
it('should propagate resolution err', function (done) {
var e = new Error("gaga");
var p1 = new MPromise;
p1.chain(new MPromise(function (err) {
assert.equal(err, e);
done();
}));
p1.resolve(e);
});
it('should propagate resolution val', function (done) {
var varSentinel = {a: 'a'};
var p1 = new MPromise;
p1.chain(new MPromise(function (err, val) {
assert.equal(val, varSentinel);
done();
}));
p1.resolve(null, varSentinel);
})
});
describe("all", function () {
it("works", function (done) {
var count = 0;
var p = new MPromise;
var p2 = p.all(function () {
return [
(function () {
var p = new MPromise();
count++;
p.resolve();
return p;
})()
, (function () {
var p = new MPromise();
count++;
p.resolve();
return p;
})()
];
});
p2.then(function () {
assert.equal(count, 2);
done();
});
p.resolve();
});
it("handles rejects", function (done) {
var count = 0;
var p = new MPromise;
var p2 = p.all(function () {
return [
(function () {
var p = new MPromise();
count++;
p.resolve();
return p;
})()
, (function () {
count++;
throw new Error("gaga");
})()
];
});
p2.onReject(function (err) {
assert(err.message, "gaga");
assert.equal(count, 2);
done();
});
p.resolve();
});
});
describe("deferred", function () {
it("works", function (done) {
var d = MPromise.deferred();
assert.ok(d.promise instanceof MPromise);
assert.ok(d.reject instanceof Function);
assert.ok(d.resolve instanceof Function);
assert.ok(d.callback instanceof Function);
done();
});
});
describe("hook", function () {
it("works", function (done) {
var run = 0;
var l1 = function (ser, par) {
run++;
ser();
par();
};
MPromise.hook([l1, l1, l1]).then(function () {
assert(run, 3);
done();
})
});
it("works with async serial hooks", function (done) {
this.timeout(800);
var run = 0;
var l1 = function (ser, par) {
run++;
setTimeout(function () {ser();}, 200);
par();
};
MPromise.hook([l1, l1, l1]).then(function () {
assert(run, 3);
done();
})
});
it("works with async parallel hooks", function (done) {
this.timeout(400);
var run = 0;
var l1 = function (ser, par) {
run++;
ser();
setTimeout(function () {par();}, 200);
};
MPromise.hook([l1, l1, l1]).then(function () {
assert(run, 3);
done();
})
});
it("catches errors in hook logic", function (done) {
var run = 0;
var l1 = function (ser, par) {
run++;
ser();
par();
};
var l2 = function (ser, par) {
run++;
ser();
par();
throw new Error("err")
};
MPromise.hook([l1, l2, l1]).end(function (err) {
assert(run, 2);
done();
});
});
});
});