1226 lines
40 KiB
JavaScript
1226 lines
40 KiB
JavaScript
"use strict";
|
|
var assert = require("assert");
|
|
var testUtils = require("./helpers/util.js");
|
|
var defaultThis = function() {return this}();
|
|
|
|
function timedThenableOf(value) {
|
|
return {
|
|
then: function(onFulfilled) {
|
|
setTimeout(function() {
|
|
onFulfilled(value);
|
|
}, 1);
|
|
}
|
|
};
|
|
}
|
|
|
|
function timedPromiseOf(value) {
|
|
return Promise.delay(1, value);
|
|
}
|
|
|
|
function immediatePromiseOf(value) {
|
|
return Promise.resolve(value);
|
|
}
|
|
|
|
function immediateThenableOf(value) {
|
|
return {
|
|
then: function(onFulfilled) {
|
|
onFulfilled(value);
|
|
}
|
|
};
|
|
}
|
|
|
|
function timedRejectedThenableOf(value) {
|
|
return {
|
|
then: function(onFulfilled, onRejected) {
|
|
setTimeout(function() {
|
|
onRejected(value);
|
|
}, 1);
|
|
}
|
|
};
|
|
}
|
|
|
|
function timedRejectedPromiseOf(value) {
|
|
return Promise.delay(1).then(function() {
|
|
throw value;
|
|
});
|
|
}
|
|
|
|
function immediateRejectedPromiseOf(value) {
|
|
return Promise.reject(value);
|
|
}
|
|
|
|
function immediateRejectedThenableOf(value) {
|
|
return {
|
|
then: function(onFulfilled, onRejected) {
|
|
onRejected(value);
|
|
}
|
|
};
|
|
}
|
|
|
|
function toValue(valueOrPromise) {
|
|
if (valueOrPromise && typeof valueOrPromise.value === "function") {
|
|
return valueOrPromise.value();
|
|
}
|
|
return valueOrPromise
|
|
}
|
|
|
|
var THIS = {name: "this"};
|
|
|
|
function CustomError1() {}
|
|
CustomError1.prototype = Object.create(Error.prototype);
|
|
function CustomError2() {}
|
|
CustomError2.prototype = Object.create(Error.prototype);
|
|
|
|
|
|
describe("when using .bind", function() {
|
|
describe("with finally", function() {
|
|
describe("this should refer to the bound object", function() {
|
|
specify("in straight-forward handler", function() {
|
|
return Promise.resolve().bind(THIS).lastly(function(){
|
|
assert(this === THIS);
|
|
});
|
|
});
|
|
|
|
specify("after promise returned from finally resolves", function() {
|
|
var d = Promise.defer();
|
|
var promise = d.promise;
|
|
var waited = false;
|
|
|
|
setTimeout(function(){
|
|
waited = true;
|
|
d.fulfill();
|
|
}, 1);
|
|
|
|
return Promise.resolve().bind(THIS).lastly(function(){
|
|
return promise;
|
|
}).lastly(function(){
|
|
assert(waited);
|
|
assert(this === THIS);
|
|
});
|
|
});
|
|
})
|
|
|
|
});
|
|
|
|
describe("with tap", function() {
|
|
describe("this should refer to the bound object", function() {
|
|
specify("in straight-forward handler", function() {
|
|
return Promise.resolve().bind(THIS).tap(function(){
|
|
assert(this === THIS);
|
|
});
|
|
});
|
|
|
|
specify("after promise returned from tap resolves", function() {
|
|
var d = Promise.defer();
|
|
var promise = d.promise;
|
|
var waited = false;
|
|
setTimeout(function(){
|
|
waited = true;
|
|
d.fulfill();
|
|
}, 1);
|
|
|
|
return Promise.resolve().bind(THIS).tap(function(){
|
|
return promise;
|
|
}).tap(function(){
|
|
assert(waited);
|
|
assert(this === THIS);
|
|
});
|
|
});
|
|
})
|
|
|
|
});
|
|
|
|
describe("with timeout", function() {
|
|
describe("this should refer to the bound object", function() {
|
|
specify("in straight-forward handler", function() {
|
|
return Promise.resolve(3).bind(THIS).timeout(500).then(function(v) {
|
|
assert(v === 3);
|
|
assert(this === THIS);
|
|
});
|
|
});
|
|
specify("in rejected handler", function() {
|
|
return Promise.reject(3).bind(THIS).timeout(500).then(assert.fail, function(v){
|
|
assert(v === 3);
|
|
assert(this === THIS);
|
|
});
|
|
});
|
|
|
|
specify("in rejected handler after timeout", function() {
|
|
return new Promise(function(){})
|
|
.bind(THIS).timeout(10).caught(Promise.TimeoutError, function(err){
|
|
assert(this === THIS);
|
|
});
|
|
});
|
|
})
|
|
|
|
});
|
|
|
|
describe("With catch filters", function() {
|
|
describe("this should refer to the bound object", function() {
|
|
specify("in an immediately trapped catch handler", function() {
|
|
return Promise.resolve().bind(THIS).then(function(){
|
|
assert(THIS === this);
|
|
var a;
|
|
a.b();
|
|
}).caught(Error, function(e){
|
|
assert(THIS === this);
|
|
});
|
|
});
|
|
specify("in a later trapped catch handler", function() {
|
|
return Promise.resolve().bind(THIS).then(function(){
|
|
throw new CustomError1();
|
|
}).caught(CustomError2, assert.fail)
|
|
.caught(CustomError1, function(e){
|
|
assert(THIS === this);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("With .get promises", function(){
|
|
specify("this should refer to the bound object", function() {
|
|
return Promise.resolve({key: "value"}).bind(THIS).get("key").then(function(val){
|
|
assert(val === "value");
|
|
assert(this === THIS);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("With .call promises", function(){
|
|
specify("this should refer to the bound object", function() {
|
|
return Promise.resolve({key: function(){return "value";}}).bind(THIS).call("key").then(function(val){
|
|
assert(val === "value");
|
|
assert(this === THIS);
|
|
});
|
|
});
|
|
});
|
|
|
|
|
|
describe("With .done promises", function(){
|
|
|
|
describe("this should refer to the bound object", function() {
|
|
specify("when rejected", function() {
|
|
return Promise.reject().bind(THIS).done(assert.fail, function(){
|
|
assert(this === THIS);
|
|
});
|
|
});
|
|
specify("when fulfilled", function() {
|
|
return Promise.resolve().bind(THIS).done(function(){
|
|
assert(this === THIS);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("With .spread promises", function(){
|
|
|
|
describe("this should refer to the bound object", function() {
|
|
specify("when spreading immediate array", function() {
|
|
return Promise.resolve([1,2,3]).bind(THIS).spread(function(a, b, c){
|
|
assert(c === 3);
|
|
assert(this === THIS);
|
|
});
|
|
});
|
|
specify("when spreading eventual array", function() {
|
|
var d = Promise.defer();
|
|
var promise = d.promise;
|
|
|
|
setTimeout(function(){
|
|
d.fulfill([1,2,3]);
|
|
}, 1);
|
|
|
|
return promise.bind(THIS).spread(function(a, b, c){
|
|
assert(c === 3);
|
|
assert(this === THIS);
|
|
});
|
|
});
|
|
|
|
specify("when spreading eventual array of eventual values", function() {
|
|
var d = Promise.defer();
|
|
var promise = d.promise;
|
|
setTimeout(function(){
|
|
var d1 = Promise.defer();
|
|
var p1 = d1.promise;
|
|
|
|
var d2 = Promise.defer();
|
|
var p2 = d2.promise;
|
|
|
|
var d3 = Promise.defer();
|
|
var p3 = d3.promise;
|
|
d.fulfill([p1, p2, p3]);
|
|
|
|
setTimeout(function(){
|
|
d1.fulfill(1);
|
|
d2.fulfill(2);
|
|
d3.fulfill(3);
|
|
}, 3);
|
|
}, 1);
|
|
return promise.bind(THIS).all().spread(function(a, b, c){
|
|
assert(c === 3);
|
|
assert(this === THIS);
|
|
});
|
|
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("With nodeify", function() {
|
|
describe("this should refer to the bound object", function() {
|
|
specify("when the callback succeeeds", function() {
|
|
var spy = testUtils.getSpy();
|
|
Promise.resolve(3).bind(THIS).nodeify(spy(function(err, success){
|
|
assert(success === 3);
|
|
assert(this === THIS);
|
|
}));
|
|
return spy.promise;
|
|
});
|
|
specify("when the callback errs", function() {
|
|
var spy = testUtils.getSpy();
|
|
Promise.reject(3).bind(THIS).nodeify(spy(function(err, success){
|
|
assert(err === 3);
|
|
assert(this === THIS);
|
|
}));
|
|
return spy.promise;
|
|
});
|
|
});
|
|
});
|
|
|
|
|
|
describe("With map", function() {
|
|
describe("this should refer to the bound object", function() {
|
|
specify("inside the mapper with immediate values", function() {
|
|
return Promise.resolve([1,2,3]).bind(THIS).map(function(v, i){
|
|
if (i === 2) {
|
|
assert(this === THIS);
|
|
}
|
|
});
|
|
});
|
|
specify("inside the mapper with eventual values", function() {
|
|
var d1 = Promise.defer();
|
|
var p1 = d1.promise;
|
|
|
|
var d2 = Promise.defer();
|
|
var p2 = d2.promise;
|
|
|
|
var d3 = Promise.defer();
|
|
var p3 = d3.promise;
|
|
|
|
setTimeout(function(){
|
|
d1.fulfill(1);
|
|
d2.fulfill(2);
|
|
d3.fulfill(3);
|
|
}, 1);
|
|
|
|
return Promise.resolve([p1, p2, p3]).bind(THIS).map(function(v, i){
|
|
if (i === 2) {
|
|
assert(this === THIS);
|
|
}
|
|
});
|
|
});
|
|
|
|
specify("after the mapper with immediate values", function() {
|
|
return Promise.resolve([1,2,3]).bind(THIS).map(function(){
|
|
return 1;
|
|
}).then(function(){
|
|
assert(this === THIS);
|
|
});
|
|
});
|
|
|
|
specify("after the mapper with eventual values", function() {
|
|
var d1 = Promise.defer();
|
|
var p1 = d1.promise;
|
|
|
|
var d2 = Promise.defer();
|
|
var p2 = d2.promise;
|
|
|
|
var d3 = Promise.defer();
|
|
var p3 = d3.promise;
|
|
|
|
setTimeout(function(){
|
|
d1.fulfill(1);
|
|
d2.fulfill(2);
|
|
d3.fulfill(3);
|
|
}, 1);
|
|
|
|
return Promise.resolve([p1, p2, p3]).bind(THIS).map(function(){
|
|
return 1;
|
|
}).then(function(){
|
|
assert(this === THIS);
|
|
});
|
|
|
|
|
|
});
|
|
|
|
specify("after the mapper with immediate values when the map returns promises", function() {
|
|
var d1 = Promise.defer();
|
|
var p1 = d1.promise;
|
|
|
|
setTimeout(function(){
|
|
d1.fulfill(1);
|
|
}, 1);
|
|
|
|
return Promise.resolve([1,2,3]).bind(THIS).map(function(){
|
|
return p1;
|
|
}).then(function(){
|
|
assert(this === THIS);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("this should not refer to the bound object", function() {
|
|
specify("in the promises created within the handler", function() {
|
|
var d1 = Promise.defer();
|
|
var p1 = d1.promise;
|
|
|
|
setTimeout(function(){
|
|
d1.fulfill(1);
|
|
}, 1);
|
|
|
|
return Promise.resolve([1,2,3]).bind(THIS).map(function(){
|
|
return p1.then(function(){
|
|
assert(this !== THIS);
|
|
return 1;
|
|
})
|
|
}).then(function(){
|
|
assert(this === THIS);
|
|
});
|
|
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("With reduce", function() {
|
|
describe("this should refer to the bound object", function() {
|
|
specify("inside the reducer with immediate values", function() {
|
|
return Promise.resolve([1,2,3]).bind(THIS).reduce(function(prev, v, i){
|
|
if (i === 2) {
|
|
assert(this === THIS);
|
|
}
|
|
});
|
|
});
|
|
specify("inside the reducer with eventual values", function() {
|
|
var d1 = Promise.defer();
|
|
var p1 = d1.promise;
|
|
|
|
var d2 = Promise.defer();
|
|
var p2 = d2.promise;
|
|
|
|
var d3 = Promise.defer();
|
|
var p3 = d3.promise;
|
|
|
|
setTimeout(function(){
|
|
d1.fulfill(1);
|
|
d2.fulfill(2);
|
|
d3.fulfill(3);
|
|
}, 1);
|
|
|
|
return Promise.resolve([p1, p2, p3]).bind(THIS).reduce(function(prev, v, i){
|
|
if (i === 2) {
|
|
assert(this === THIS);
|
|
}
|
|
});
|
|
});
|
|
|
|
specify("after the reducer with immediate values", function() {
|
|
return Promise.resolve([1,2,3]).bind(THIS).reduce(function(){
|
|
return 1;
|
|
}).then(function(){
|
|
assert(this === THIS);
|
|
});
|
|
});
|
|
|
|
specify("after the reducer with eventual values", function() {
|
|
var d1 = Promise.defer();
|
|
var p1 = d1.promise;
|
|
|
|
var d2 = Promise.defer();
|
|
var p2 = d2.promise;
|
|
|
|
var d3 = Promise.defer();
|
|
var p3 = d3.promise;
|
|
setTimeout(function(){
|
|
d1.fulfill(1);
|
|
d2.fulfill(2);
|
|
d3.fulfill(3);
|
|
}, 1);
|
|
return Promise.resolve([p1, p2, p3]).bind(THIS).reduce(function(){
|
|
return 1;
|
|
}).then(function(){
|
|
assert(this === THIS);
|
|
});
|
|
|
|
});
|
|
|
|
specify("after the reducer with immediate values when the reducer returns promise", function() {
|
|
var d1 = Promise.defer();
|
|
var p1 = d1.promise;
|
|
|
|
setTimeout(function(){
|
|
d1.fulfill(1);
|
|
}, 1);
|
|
|
|
return Promise.resolve([1,2,3]).bind(THIS).reduce(function(){
|
|
return p1;
|
|
}).then(function(){
|
|
assert(this === THIS);
|
|
});
|
|
|
|
|
|
});
|
|
});
|
|
|
|
describe("this should not refer to the bound object", function() {
|
|
specify("in the promises created within the handler", function() {
|
|
var d1 = Promise.defer();
|
|
var p1 = d1.promise;
|
|
|
|
setTimeout(function(){
|
|
d1.fulfill(1);
|
|
}, 1);
|
|
|
|
return Promise.resolve([1,2,3]).bind(THIS).reduce(function(){
|
|
return p1.then(function(){
|
|
assert(this !== THIS);
|
|
return 1;
|
|
})
|
|
}).then(function(){
|
|
assert(this === THIS);
|
|
});
|
|
|
|
|
|
});
|
|
});
|
|
});
|
|
|
|
|
|
describe("With filter", function() {
|
|
describe("this should refer to the bound object", function() {
|
|
specify("inside the filterer with immediate values", function() {
|
|
return Promise.resolve([1,2,3]).bind(THIS).filter(function(v, i){
|
|
if (i === 2) {
|
|
assert(this === THIS);
|
|
}
|
|
});
|
|
});
|
|
specify("inside the filterer with eventual values", function() {
|
|
var d1 = Promise.defer();
|
|
var p1 = d1.promise;
|
|
|
|
var d2 = Promise.defer();
|
|
var p2 = d2.promise;
|
|
|
|
var d3 = Promise.defer();
|
|
var p3 = d3.promise;
|
|
|
|
setTimeout(function(){
|
|
d1.fulfill(1);
|
|
d2.fulfill(2);
|
|
d3.fulfill(3);
|
|
}, 1);
|
|
|
|
return Promise.resolve([p1, p2, p3]).bind(THIS).filter(function(v, i){
|
|
if (i === 2) {
|
|
assert(this === THIS);
|
|
}
|
|
});
|
|
});
|
|
|
|
specify("after the filterer with immediate values", function() {
|
|
return Promise.resolve([1,2,3]).bind(THIS).filter(function(){
|
|
return 1;
|
|
}).then(function(){
|
|
assert(this === THIS);
|
|
});
|
|
});
|
|
|
|
specify("after the filterer with eventual values", function() {
|
|
var d1 = Promise.defer();
|
|
var p1 = d1.promise;
|
|
|
|
var d2 = Promise.defer();
|
|
var p2 = d2.promise;
|
|
|
|
var d3 = Promise.defer();
|
|
var p3 = d3.promise;
|
|
|
|
setTimeout(function(){
|
|
d1.fulfill(1);
|
|
d2.fulfill(2);
|
|
d3.fulfill(3);
|
|
}, 1);
|
|
|
|
return Promise.resolve([p1, p2, p3]).bind(THIS).filter(function(){
|
|
return 1;
|
|
}).then(function(){
|
|
assert(this === THIS);
|
|
});
|
|
});
|
|
|
|
specify("after the filterer with immediate values when the filterer returns promises", function() {
|
|
var d1 = Promise.defer();
|
|
var p1 = d1.promise;
|
|
|
|
setTimeout(function(){
|
|
d1.fulfill(1);
|
|
}, 1);
|
|
|
|
return Promise.resolve([1,2,3]).bind(THIS).filter(function(){
|
|
return p1;
|
|
}).then(function(){
|
|
assert(this === THIS);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("this should not refer to the bound object", function() {
|
|
specify("in the promises created within the handler", function() {
|
|
var d1 = Promise.defer();
|
|
var p1 = d1.promise;
|
|
|
|
setTimeout(function(){
|
|
d1.fulfill(1);
|
|
}, 1);
|
|
|
|
return Promise.resolve([1,2,3]).bind(THIS).filter(function(){
|
|
return p1.then(function(){
|
|
assert(this !== THIS);
|
|
return 1;
|
|
})
|
|
}).then(function(){
|
|
assert(this === THIS);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("With all", function() {
|
|
describe("this should refer to the bound object", function() {
|
|
specify("after all with immediate values", function() {
|
|
return Promise.resolve([1,2,3]).bind(THIS).all().then(function(v){
|
|
assert(v.length === 3);
|
|
assert(this === THIS);
|
|
});
|
|
});
|
|
specify("after all with eventual values", function() {
|
|
var d1 = Promise.defer();
|
|
var p1 = d1.promise;
|
|
|
|
var d2 = Promise.defer();
|
|
var p2 = d2.promise;
|
|
|
|
var d3 = Promise.defer();
|
|
var p3 = d3.promise;
|
|
|
|
setTimeout(function(){
|
|
d1.fulfill(1);
|
|
d2.fulfill(2);
|
|
d3.fulfill(3);
|
|
}, 1);
|
|
|
|
return Promise.resolve([p1, p2, p3]).bind(THIS).all().then(function(v){
|
|
assert(v.length === 3);
|
|
assert(this === THIS);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("this should not refer to the bound object", function() {
|
|
specify("in the promises created within the handler", function() {
|
|
var d1 = Promise.defer();
|
|
var p1 = d1.promise;
|
|
|
|
setTimeout(function(){
|
|
d1.fulfill(1);
|
|
}, 1);
|
|
|
|
return Promise.resolve([1,2,3]).bind(THIS).filter(function(){
|
|
return Promise.all([p1]).then(function(){
|
|
assert(this !== THIS);
|
|
return 1;
|
|
})
|
|
}).then(function(){
|
|
assert(this === THIS);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("With any", function() {
|
|
describe("this should refer to the bound object", function() {
|
|
specify("after any with immediate values", function() {
|
|
Promise.resolve([1,2,3]).bind(THIS).any().then(function(v){
|
|
assert(v === 1);
|
|
assert(this === THIS);
|
|
});
|
|
});
|
|
specify("after any with eventual values", function() {
|
|
var d1 = Promise.defer();
|
|
var p1 = d1.promise;
|
|
|
|
var d2 = Promise.defer();
|
|
var p2 = d2.promise;
|
|
|
|
var d3 = Promise.defer();
|
|
var p3 = d3.promise;
|
|
|
|
setTimeout(function(){
|
|
d1.fulfill(1);
|
|
d2.fulfill(2);
|
|
d3.fulfill(3);
|
|
}, 1);
|
|
|
|
return Promise.resolve([p1, p2, p3]).bind(THIS).any().then(function(v){
|
|
assert(v === 1);
|
|
assert(this === THIS);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("this should not refer to the bound object", function() {
|
|
specify("in the promises created within the handler", function() {
|
|
var d1 = Promise.defer();
|
|
var p1 = d1.promise;
|
|
|
|
setTimeout(function(){
|
|
d1.fulfill(1);
|
|
}, 1);
|
|
|
|
return Promise.resolve([1,2,3]).bind(THIS).filter(function(){
|
|
return Promise.any([p1]).then(function(){
|
|
assert(this !== THIS);
|
|
return 1;
|
|
})
|
|
}).then(function(){
|
|
assert(this === THIS);
|
|
});
|
|
|
|
});
|
|
});
|
|
});
|
|
|
|
|
|
describe("With race", function() {
|
|
describe("this should refer to the bound object", function() {
|
|
specify("after race with immediate values", function() {
|
|
Promise.resolve([1,2,3]).bind(THIS).race().then(function(v){
|
|
assert(v === 1);
|
|
assert(this === THIS);
|
|
});
|
|
});
|
|
specify("after race with eventual values", function() {
|
|
var d1 = Promise.defer();
|
|
var p1 = d1.promise;
|
|
|
|
var d2 = Promise.defer();
|
|
var p2 = d2.promise;
|
|
|
|
var d3 = Promise.defer();
|
|
var p3 = d3.promise;
|
|
|
|
setTimeout(function(){
|
|
d1.fulfill(1);
|
|
d2.fulfill(2);
|
|
d3.fulfill(3);
|
|
}, 1);
|
|
|
|
return Promise.resolve([p1, p2, p3]).bind(THIS).race().then(function(v){
|
|
assert(v === 1);
|
|
assert(this === THIS);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("this should not refer to the bound object", function() {
|
|
specify("in the promises created within the handler", function() {
|
|
var d1 = Promise.defer();
|
|
var p1 = d1.promise;
|
|
|
|
setTimeout(function(){
|
|
d1.fulfill(1);
|
|
}, 1);
|
|
|
|
return Promise.resolve([1,2,3]).bind(THIS).filter(function(){
|
|
return Promise.race([p1]).then(function(){
|
|
assert(this !== THIS);
|
|
return 1;
|
|
})
|
|
}).then(function(){
|
|
assert(this === THIS);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("With delay", function() {
|
|
describe("this should refer to the bound object", function() {
|
|
specify("after race with immediate values", function() {
|
|
Promise.resolve([1,2,3]).bind(THIS).delay(1).then(function(v){
|
|
assert(v[0] === 1);
|
|
assert(this === THIS);
|
|
});
|
|
});
|
|
specify("after race with eventual values", function() {
|
|
var d1 = Promise.defer();
|
|
var p1 = d1.promise;
|
|
|
|
var d2 = Promise.defer();
|
|
var p2 = d2.promise;
|
|
|
|
var d3 = Promise.defer();
|
|
var p3 = d3.promise;
|
|
|
|
setTimeout(function(){
|
|
d1.fulfill(1);
|
|
d2.fulfill(2);
|
|
d3.fulfill(3);
|
|
}, 1);
|
|
|
|
return Promise.resolve([p1, p2, p3]).bind(THIS).delay(1).all().then(function(v){
|
|
assert(v[0] === 1);
|
|
assert(this === THIS);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("this should not refer to the bound object", function() {
|
|
specify("in the promises created within the handler", function() {
|
|
var d1 = Promise.defer();
|
|
var p1 = d1.promise;
|
|
|
|
setTimeout(function(){
|
|
d1.fulfill(1);
|
|
}, 1);
|
|
|
|
return Promise.resolve([1,2,3]).delay(1).bind(THIS).delay(1).filter(function(){
|
|
assert(this === THIS);
|
|
return Promise.delay(1).then(function(){
|
|
assert(this !== THIS);
|
|
return 1;
|
|
})
|
|
}).then(function(){
|
|
assert(this === THIS);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("With settle", function() {
|
|
describe("this should refer to the bound object", function() {
|
|
specify("after settle with immediate values", function() {
|
|
return Promise.resolve([1,2,3]).bind(THIS).settle().then(function(v){
|
|
assert(v.length === 3);
|
|
assert(this === THIS);
|
|
});
|
|
});
|
|
specify("after settle with eventual values", function() {
|
|
var d1 = Promise.defer();
|
|
var p1 = d1.promise;
|
|
|
|
var d2 = Promise.defer();
|
|
var p2 = d2.promise;
|
|
|
|
var d3 = Promise.defer();
|
|
var p3 = d3.promise;
|
|
|
|
setTimeout(function(){
|
|
d1.fulfill(1);
|
|
d2.fulfill(2);
|
|
d3.fulfill(3);
|
|
}, 1);
|
|
|
|
return Promise.resolve([p1, p2, p3]).bind(THIS).settle().then(function(v){
|
|
assert(v.length === 3);
|
|
assert(this === THIS);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("this should not refer to the bound object", function() {
|
|
specify("in the promises created within the handler", function() {
|
|
var d1 = Promise.defer();
|
|
var p1 = d1.promise;
|
|
|
|
setTimeout(function(){
|
|
d1.fulfill(1);
|
|
}, 1);
|
|
|
|
return Promise.resolve([1,2,3]).bind(THIS).filter(function(){
|
|
return Promise.settle([p1]).then(function(){
|
|
assert(this !== THIS);
|
|
return 1;
|
|
})
|
|
}).then(function(){
|
|
assert(this === THIS);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("With some", function() {
|
|
describe("this should refer to the bound object", function() {
|
|
specify("after some with immediate values", function() {
|
|
return Promise.resolve([1,2,3]).bind(THIS).some(2).then(function(v){
|
|
assert.deepEqual(v, [1,2]);
|
|
assert(this === THIS);
|
|
});
|
|
});
|
|
specify("after some with eventual values", function() {
|
|
var d1 = Promise.defer();
|
|
var p1 = d1.promise;
|
|
|
|
var d2 = Promise.defer();
|
|
var p2 = d2.promise;
|
|
|
|
var d3 = Promise.defer();
|
|
var p3 = d3.promise;
|
|
|
|
setTimeout(function(){
|
|
d1.fulfill(1);
|
|
d2.fulfill(2);
|
|
d3.fulfill(3);
|
|
}, 1);
|
|
|
|
return Promise.resolve([p1, p2, p3]).bind(THIS).some(2).then(function(v){
|
|
assert.deepEqual(v, [1,2]);
|
|
assert(this === THIS);
|
|
});
|
|
});
|
|
|
|
specify("after some with eventual array for eventual values", function() {
|
|
var d1 = Promise.defer();
|
|
var p1 = d1.promise;
|
|
|
|
var d2 = Promise.defer();
|
|
var p2 = d2.promise;
|
|
|
|
var d3 = Promise.defer();
|
|
var p3 = d3.promise;
|
|
|
|
var dArray = Promise.defer();
|
|
var arrayPromise = dArray.promise;
|
|
|
|
setTimeout(function(){
|
|
dArray.fulfill([p1, p2, p3]);
|
|
setTimeout(function(){
|
|
d1.fulfill(1);
|
|
d2.fulfill(2);
|
|
d3.fulfill(3);
|
|
}, 1);
|
|
}, 1);
|
|
|
|
return arrayPromise.bind(THIS).some(2).then(function(v){
|
|
assert.deepEqual(v, [1,2]);
|
|
assert(this === THIS);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("this should not refer to the bound object", function() {
|
|
specify("in the promises created within the handler", function() {
|
|
var d1 = Promise.defer();
|
|
var p1 = d1.promise;
|
|
|
|
setTimeout(function(){
|
|
d1.fulfill(1);
|
|
}, 1);
|
|
|
|
return Promise.resolve([1,2,3]).bind(THIS).filter(function(){
|
|
return Promise.some([p1], 1).then(function(){
|
|
assert(this !== THIS);
|
|
return 1;
|
|
})
|
|
}).then(function(){
|
|
assert(this === THIS);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
|
|
|
|
describe("With props", function() {
|
|
describe("this should refer to the bound object", function() {
|
|
specify("after props with immediate values", function() {
|
|
return Promise.resolve([1,2,3]).bind(THIS).props().then(function(v){
|
|
assert(v[2] === 3);
|
|
assert(this === THIS);
|
|
});
|
|
});
|
|
specify("after props with eventual values", function() {
|
|
var d1 = Promise.defer();
|
|
var p1 = d1.promise;
|
|
|
|
var d2 = Promise.defer();
|
|
var p2 = d2.promise;
|
|
|
|
var d3 = Promise.defer();
|
|
var p3 = d3.promise;
|
|
|
|
setTimeout(function(){
|
|
d1.fulfill(1);
|
|
d2.fulfill(2);
|
|
d3.fulfill(3);
|
|
}, 1);
|
|
|
|
return Promise.resolve([p1, p2, p3]).bind(THIS).props().then(function(v){
|
|
assert(v[2] === 3);
|
|
assert(this === THIS);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("this should not refer to the bound object", function() {
|
|
specify("in the promises created within the handler", function() {
|
|
var d1 = Promise.defer();
|
|
var p1 = d1.promise;
|
|
setTimeout(function(){
|
|
d1.fulfill(1);
|
|
}, 1);
|
|
return Promise.resolve([1,2,3]).bind(THIS).props(function(){
|
|
return Promise.settle([p1]).then(function(){
|
|
assert(this !== THIS);
|
|
return 1;
|
|
})
|
|
}).then(function(){
|
|
assert(this === THIS);
|
|
});
|
|
|
|
|
|
});
|
|
});
|
|
});
|
|
|
|
});
|
|
|
|
describe("When using .bind to gratuitously rebind", function() {
|
|
var a = {value: 1};
|
|
var b = {value: 2};
|
|
var c = {value: 3};
|
|
|
|
function makeTest(a, b, c) {
|
|
return function() {
|
|
return Promise.bind(a).then(function(){
|
|
assert(this.value === 1);
|
|
}).bind(b).then(function(){
|
|
assert(this.value === 2);
|
|
}).bind(c).then(function(){
|
|
assert(this.value === 3);
|
|
});
|
|
}
|
|
}
|
|
|
|
specify("should not get confused immediately", makeTest(a, b, c));
|
|
specify("should not get confused immediate thenable",
|
|
makeTest(immediateThenableOf(a), immediateThenableOf(b), immediateThenableOf(c)));
|
|
specify("should not get confused immediate promise",
|
|
makeTest(immediatePromiseOf(a), immediatePromiseOf(b), immediatePromiseOf(c)));
|
|
specify("should not get confused timed thenable",
|
|
makeTest(timedThenableOf(a), timedThenableOf(b), timedThenableOf(c)));
|
|
specify("should not get confused timed promise",
|
|
makeTest(timedPromiseOf(a), timedPromiseOf(b), timedPromiseOf(c)));
|
|
});
|
|
|
|
describe("Promised thisArg", function() {
|
|
|
|
var e = {value: 1};
|
|
|
|
specify("basic case, this first", function(done) {
|
|
var thisPromise = Promise.delay(1, 1);
|
|
var promise = thisPromise.delay(1).thenReturn(2);
|
|
promise.bind(thisPromise).then(function(val) {
|
|
assert(+this === 1);
|
|
assert(+val === 2);
|
|
done();
|
|
});
|
|
});
|
|
|
|
specify("bound value is not changed by returned promise", function() {
|
|
return Promise.resolve().then(function() {
|
|
return new Promise(function(resolve) {
|
|
resolve();
|
|
}).bind(THIS).then(function() {});
|
|
}).then(function() {
|
|
assert.strictEqual(this, defaultThis);
|
|
});
|
|
});
|
|
|
|
specify("basic case, main promise first", function() {
|
|
var promise = Promise.delay(1, 2);
|
|
var thisPromise = promise.thenReturn(1);
|
|
return promise.bind(thisPromise).then(function(val) {
|
|
assert.strictEqual(+this, 1);
|
|
assert.strictEqual(+val, 2);
|
|
});
|
|
});
|
|
|
|
specify("both reject, this rejects first", function(done) {
|
|
var e1 = new Error();
|
|
var e2 = new Error();
|
|
var thisPromise = Promise.delay(1, 0).thenThrow(e1);
|
|
var promise = Promise.delay(2, 56).thenThrow(e2);
|
|
promise.bind(thisPromise).then(null, function(reason) {
|
|
assert(this === defaultThis);
|
|
assert(reason === e1);
|
|
done();
|
|
});
|
|
});
|
|
|
|
specify("both reject, main promise rejects first", function(done) {
|
|
var e1 = new Error("first");
|
|
var e2 = new Error("second");
|
|
var thisPromise = Promise.delay(56, 1).thenThrow(e1);
|
|
var promise = Promise.delay(2, 0).thenThrow(e2);
|
|
promise.bind(thisPromise).then(null, function(reason) {
|
|
assert(this === defaultThis);
|
|
assert(reason === e2);
|
|
done();
|
|
});
|
|
});
|
|
|
|
specify("Immediate value waits for deferred this", function() {
|
|
var t = Promise.delay(1, THIS);
|
|
var t2 = {};
|
|
return Promise.resolve(t2).bind(t).then(function(value) {
|
|
assert.strictEqual(this, THIS);
|
|
assert.strictEqual(t2, value);
|
|
});
|
|
});
|
|
|
|
|
|
specify("Immediate error waits for deferred this", function() {
|
|
var t = Promise.delay(1, THIS);
|
|
var err = new Error();
|
|
return Promise.reject(err).bind(t).then(assert.fail, function(e) {
|
|
assert.strictEqual(this, THIS);
|
|
assert.strictEqual(err, e);
|
|
});
|
|
});
|
|
|
|
function makeThisArgRejectedTest(reason) {
|
|
return function() {
|
|
return Promise.bind(reason()).then(assert.fail, function(e) {
|
|
assert(this === defaultThis);
|
|
assert(e.value === 1);
|
|
})
|
|
};
|
|
}
|
|
|
|
specify("if thisArg is rejected timed promise, returned promise is rejected",
|
|
makeThisArgRejectedTest(function() { return timedRejectedPromiseOf(e); }));
|
|
specify("if thisArg is rejected immediate promise, returned promise is rejected",
|
|
makeThisArgRejectedTest(function() { return immediateRejectedPromiseOf(e); }));
|
|
specify("if thisArg is rejected timed thenable, returned promise is rejected",
|
|
makeThisArgRejectedTest(function() { return timedRejectedThenableOf(e); }));
|
|
specify("if thisArg is rejected immediate thenable, returned promise is rejected",
|
|
makeThisArgRejectedTest(function() { return immediateRejectedThenableOf(e); }));
|
|
|
|
function makeThisArgRejectedTestMethod(reason) {
|
|
return function() {
|
|
|
|
return Promise.resolve().bind(reason()).then(assert.fail, function(e) {
|
|
assert(this === defaultThis);
|
|
assert(e.value === 1);
|
|
})
|
|
};
|
|
}
|
|
|
|
specify("if thisArg is rejected timed promise, returned promise is rejected",
|
|
makeThisArgRejectedTestMethod(function() { return timedRejectedPromiseOf(e); }));
|
|
specify("if thisArg is rejected immediate promise, returned promise is rejected",
|
|
makeThisArgRejectedTestMethod(function() { return immediateRejectedPromiseOf(e); }));
|
|
specify("if thisArg is rejected timed thenable, returned promise is rejected",
|
|
makeThisArgRejectedTestMethod(function() { return timedRejectedThenableOf(e); }));
|
|
specify("if thisArg is rejected immediate thenable, returned promise is rejected",
|
|
makeThisArgRejectedTestMethod(function() { return immediateRejectedThenableOf(e); }));
|
|
});
|
|
|
|
describe("github issue", function() {
|
|
specify("gh-426", function() {
|
|
return Promise.all([Promise.delay(10)]).bind(THIS).spread(function() {
|
|
assert.equal(this, THIS);
|
|
});
|
|
});
|
|
|
|
specify("gh-702-1", function() {
|
|
return Promise.bind(Promise.delay(1, THIS)).then(function() {
|
|
assert.equal(this, THIS);
|
|
}).then(function() {
|
|
assert.equal(this, THIS);
|
|
});
|
|
});
|
|
|
|
specify("gh-702-2", function() {
|
|
return Promise.resolve().bind(Promise.delay(1, THIS)).then(function() {
|
|
assert.equal(this, THIS);
|
|
}).then(function() {
|
|
assert.equal(this, THIS);
|
|
});
|
|
});
|
|
});
|
|
|
|
|
|
describe("promised bind", function() {
|
|
specify("works after following", function() {
|
|
return Promise.bind(Promise.delay(1, THIS)).then(function() {
|
|
assert.equal(this, THIS);
|
|
return Promise.delay(1);
|
|
}).then(function() {
|
|
assert.equal(this, THIS);
|
|
return Promise.delay(1);
|
|
}).then(function() {
|
|
assert.equal(this, THIS);
|
|
});
|
|
});
|
|
|
|
specify("works with spread", function() {
|
|
return Promise.bind(Promise.delay(1, THIS), [1,2,3]).spread(function() {
|
|
assert.equal(this, THIS);
|
|
assert.deepEqual([1,2,3], [].slice.call(arguments));
|
|
return Promise.delay(1, [].slice.call(arguments));
|
|
}).spread(function() {
|
|
assert.deepEqual([1,2,3], [].slice.call(arguments));
|
|
assert.equal(this, THIS);
|
|
return Promise.delay(1, [].slice.call(arguments));
|
|
}).spread(function() {
|
|
assert.deepEqual([1,2,3], [].slice.call(arguments));
|
|
assert.equal(this, THIS);
|
|
});
|
|
});
|
|
|
|
specify("works with immediate finally", function() {
|
|
return Promise.bind(Promise.delay(1, THIS), [1,2,3]).lastly(function() {
|
|
assert.equal(this, THIS);
|
|
}).then(function() {
|
|
assert.equal(this, THIS);
|
|
});
|
|
});
|
|
|
|
specify("works with delayed finally", function() {
|
|
return Promise.bind(Promise.delay(1, THIS), [1,2,3]).lastly(function() {
|
|
assert.equal(this, THIS);
|
|
return Promise.delay(1);
|
|
}).then(function() {
|
|
assert.equal(this, THIS);
|
|
});
|
|
});
|
|
|
|
specify("works with immediate tap", function() {
|
|
return Promise.bind(Promise.delay(1, THIS), [1,2,3]).tap(function() {
|
|
assert.equal(this, THIS);
|
|
}).then(function() {
|
|
assert.equal(this, THIS);
|
|
});
|
|
});
|
|
|
|
specify("works with delayed tap", function() {
|
|
return Promise.bind(Promise.delay(1, THIS), [1,2,3]).tap(function() {
|
|
assert.equal(this, THIS);
|
|
return Promise.delay(1);
|
|
}).then(function() {
|
|
assert.equal(this, THIS);
|
|
});
|
|
});
|
|
});
|
|
|
|
|