2020-05-26 18:17:24 +02:00

637 lines
16 KiB
JavaScript

"use strict"
var o = require("../../ospec/ospec")
var callAsync = require("../../test-utils/callAsync")
var Promise = require("../../promise/promise")
o.spec("promise", function() {
o.spec("constructor", function() {
o("constructor throws if called without new", function(done) {
try {Promise(function() {})} catch(e) {done()}
})
o("constructor throws if called without executor", function(done) {
try {new Promise()} catch(e) {done()}
})
o("constructor has correct methods", function() {
o(typeof Promise.prototype.then).equals("function")
o(typeof Promise.prototype.catch).equals("function")
o(typeof Promise.resolve).equals("function")
o(typeof Promise.reject).equals("function")
o(typeof Promise.race).equals("function")
o(typeof Promise.all).equals("function")
})
})
o.spec("return value", function() {
o("static resolve returns promise", function() {
var promise = Promise.resolve()
o(promise instanceof Promise).equals(true)
})
o("static reject returns promise", function() {
var promise = Promise.reject()
promise.catch(function() {})
o(promise instanceof Promise).equals(true)
})
o("static resolve with promise input returns same promise", function() {
var resolved = Promise.resolve(1)
var promise = Promise.resolve(resolved)
o(promise).equals(resolved)
})
o("then returns promise", function(done) {
var promise = Promise.resolve(1)
promise.then(function(value) {
o(value).equals(1)
}).then(done)
})
o("catch returns promise", function(done) {
var promise = Promise.reject(1)
promise.catch(function(value) {
o(value).equals(1)
}).then(done)
})
})
o.spec("resolve", function() {
o("resolves once", function(done) {
var callCount = 0
var promise = new Promise(function(resolve, reject) {
resolve(1)
resolve(2)
callAsync(function() {resolve(3)})
})
promise.then(function(value) {
callCount++
o(value).equals(1)
o(callCount).equals(1)
done()
})
})
o("does not reject after resolve", function(done) {
var promise = new Promise(function(resolve, reject) {
resolve(1)
reject(2)
callAsync(function() {reject(3)})
})
promise.then(function(value) {
o(value).equals(1)
done()
})
})
o("resolves asynchronously", function(done) {
var state = 0
var promise = Promise.resolve()
state = 1
promise.then(function(value) {
o(state).equals(2)
done()
})
state = 2
})
o("resolves via static method", function(done) {
var promise = Promise.resolve(1)
promise.then(function(value) {
o(value).equals(1)
done()
})
})
o("resolves asynchronously via executor", function(done) {
var promise = new Promise(function(resolve, reject) {
callAsync(function() {resolve(1)})
})
promise.then(function(value) {
o(value).equals(1)
done()
})
})
o("downstreams correctly", function(done) {
var promise = Promise.resolve(1)
var a = promise.then(function(value) {return value + 1})
var b = promise.then(function(value) {return value + 2})
a.then(function(aValue) {
b.then(function(bValue) {
o(aValue).equals(2)
o(bValue).equals(3)
done()
})
})
})
o("cannot resolve to itself", function(done) {
var promise = new Promise(function(resolve) {
callAsync(function() {resolve(promise)})
})
promise.then(null, done)
})
o("non-function onFulfilled is ignored", function(done) {
var promise = Promise.resolve(1)
promise.then(null, null).then(function(value) {
o(value).equals(1)
done()
})
})
o("non-function onFulfilled with no second param is ignored", function(done) {
var promise = Promise.resolve(1)
promise.then(null).then(function(value) {
o(value).equals(1)
done()
})
})
})
o.spec("reject", function() {
o("rejects once", function(done) {
var callCount = 0
var promise = new Promise(function(resolve, reject) {
reject(1)
reject(2)
callAsync(function() {reject(3)})
})
promise.then(null, function(value) {
callCount++
o(value).equals(1)
o(callCount).equals(1)
done()
})
})
o("does not resolve after reject", function(done) {
var promise = new Promise(function(resolve, reject) {
reject(1)
resolve(2)
callAsync(function() {resolve(3)})
})
promise.then(null, function(value) {
o(value).equals(1)
done()
})
})
o("rejects asynchronously", function(done) {
var state = 0
var promise = Promise.reject()
state = 1
promise.then(null, function(value) {
o(state).equals(2)
done()
})
state = 2
})
o("does not catch itself", function(done) {
var callCount = 0
var promise = Promise.resolve().then(function() {throw 1}, function() {callCount++})
promise.then(null, function() {
o(callCount).equals(0)
done()
})
})
o("rejects via static method", function(done) {
var promise = Promise.reject(1)
promise.then(null, function(value) {
o(value).equals(1)
return value
}).then(function(value) {
o(value).equals(1)
done()
})
})
o("rejects synchronously via executor", function(done) {
var promise = new Promise(function(resolve, reject) {
reject(1)
})
promise.then(null, function(value) {
o(value).equals(1)
done()
})
})
o("rejects asynchronously via executor", function(done) {
var promise = new Promise(function(resolve, reject) {
callAsync(function() {reject(1)})
})
promise.then(null, function(value) {
o(value).equals(1)
done()
})
})
o("rejects via executor on error", function(done) {
var promise = new Promise(function(resolve, reject) {
throw 1
})
promise.then(null, function(value) {
o(value).equals(1)
done()
})
})
o("rejects on fulfillment error", function(done) {
var promise = Promise.resolve()
promise.then(function() {
throw 1
}).then(null, function(value) {
o(value).equals(1)
done()
})
})
o("rejects on rejection error", function(done) {
var promise = Promise.resolve()
promise.then(function() {
throw 1
}).then(null, function() {
throw 2
}).then(null, function(value) {
o(value).equals(2)
done()
})
})
o("non-function onRejected is ignored", function(done) {
var promise = Promise.reject(1)
promise.then(function() {}, null).then(null, function(value) {
o(value).equals(1)
done()
})
})
})
o.spec("promise absorption", function() {
o("absorbs resolved promise via static resolver", function(done) {
var promise = Promise.resolve(Promise.resolve(1))
promise.then(function(value) {
o(value).equals(1)
}).then(done)
})
o("absorbs resolved promise in executor resolve", function(done) {
var promise = new Promise(function(resolve, reject) {
var p = Promise.resolve(1)
resolve(p)
})
promise.then(function(value) {
o(value).equals(1)
done()
})
})
o("absorbs resolved promise on fulfillment", function(done) {
var promise = Promise.resolve()
promise.then(function() {
return Promise.resolve(1)
}).then(function(value) {
o(value).equals(1)
done()
})
})
o("absorbs rejected promise via static resolver", function(done) {
var promise = Promise.resolve(Promise.reject(1))
promise.then(null, function(value) {
o(value).equals(1)
done()
})
})
o("absorbs rejected promise in executor resolve", function(done) {
var promise = new Promise(function(resolve, reject) {
resolve(Promise.reject(1))
})
promise.then(null, function(value) {
o(value).equals(1)
done()
})
})
o("absorbs rejected promise on fulfillment", function(done) {
var promise = Promise.resolve()
promise.then(function() {
return Promise.reject(1)
}).then(null, function(value) {
o(value).equals(1)
done()
})
})
o("absorbs pending promise that resolves via static resolver", function(done) {
var pending = new Promise(function(resolve, reject) {
setTimeout(function() {resolve(1)}, 10)
})
var promise = Promise.resolve(pending)
promise.then(function(value) {
o(value).equals(1)
done()
})
})
o("absorbs pending promise that resolves in executor resolve", function(done) {
var pending = new Promise(function(resolve, reject) {
setTimeout(function() {resolve(1)}, 10)
})
var promise = new Promise(function(resolve, reject) {
resolve(pending)
})
promise.then(function(value) {
o(value).equals(1)
done()
})
})
o("absorbs pending promise that resolves on fulfillment", function(done) {
var pending = new Promise(function(resolve, reject) {
setTimeout(function() {resolve(1)}, 10)
})
var promise = Promise.resolve()
promise.then(function() {
return pending
}).then(function(value) {
o(value).equals(1)
done()
})
})
o("absorbs pending promise that rejects via static resolver", function(done) {
var pending = new Promise(function(resolve, reject) {
setTimeout(function() {reject(1)}, 10)
})
var promise = Promise.resolve(pending)
promise.then(null, function(value) {
o(value).equals(1)
done()
})
})
o("absorbs pending promise that rejects in executor resolve", function(done) {
var pending = new Promise(function(resolve, reject) {
setTimeout(function() {reject(1)}, 10)
})
var promise = new Promise(function(resolve, reject) {
resolve(pending)
})
promise.then(null, function(value) {
o(value).equals(1)
done()
})
})
o("absorbs pending promise that rejects on fulfillment", function(done) {
var pending = new Promise(function(resolve, reject) {
setTimeout(function() {reject(1)}, 10)
})
var promise = Promise.resolve()
promise.then(function() {
return pending
}).then(null, function(value) {
o(value).equals(1)
done()
})
})
o("triggers all branched rejection handlers upon rejection", function(done) {
var promise = Promise.reject()
var then = o.spy()
var catch1 = o.spy()
var catch2 = o.spy()
var catch3 = o.spy()
promise.catch(catch1)
promise.then(then, catch2)
promise.then(then).catch(catch3)
callAsync(function() {
callAsync(function() {
o(catch1.callCount).equals(1)
o(then.callCount).equals(0)
o(catch2.callCount).equals(1)
o(catch3.callCount).equals(1)
done()
})
})
})
o("does not absorb resolved promise via static rejector", function(done) {
var promise = Promise.reject(Promise.resolve(1))
promise.then(null, function(value) {
o(value instanceof Promise).equals(true)
done()
})
})
o("does not absorb rejected promise via static rejector", function(done) {
var rejected = Promise.reject(1)
rejected.catch(function() {})
var promise = Promise.reject(rejected)
promise.then(null, function(value) {
o(value instanceof Promise).equals(true)
done()
})
})
o("does not absorb resolved promise in executor reject", function(done) {
var promise = new Promise(function(resolve, reject) {
reject(Promise.resolve(1))
})
promise.then(null, function(value) {
o(value instanceof Promise).equals(true)
done()
})
})
o("does not absorb rejected promise in executor reject", function(done) {
var promise = new Promise(function(resolve, reject) {
var rejected = Promise.reject(1)
rejected.catch(function() {})
reject(rejected)
})
promise.then(null, function(value) {
o(value instanceof Promise).equals(true)
done()
})
})
o("does not absorb resolved promise on fulfillment error", function(done) {
var promise = Promise.resolve()
promise.then(function() {
throw Promise.resolve(1)
}).then(null, function(value) {
o(value instanceof Promise).equals(true)
done()
})
})
o("does not absorb rejected promise on fulfillment error", function(done) {
var promise = Promise.resolve()
promise.then(function() {
var rejected = Promise.reject(1)
rejected.catch(function() {})
throw rejected
}).then(null, function(value) {
o(value instanceof Promise).equals(true)
done()
})
})
o("promise stays pending if absorbed promise is pending", function(done) {
var promise = new Promise(function(resolve) {resolve()})
var fulfilled = false, rejected = false
promise.then(function() {
return new Promise(function() {})
}).then(function() {
fulfilled = true
}, function() {
rejected = false
})
setTimeout(function() {
o(fulfilled).equals(false)
o(rejected).equals(false)
done()
}, 10)
})
o("absorbs early resolved promise", function(done) {
var resolved = Promise.resolve(1)
var promise = new Promise(function(resolve) {
setTimeout(function() {
resolve(resolved)
}, 10)
})
promise.then(function(value) {
o(value).equals(1)
done()
})
})
})
o.spec("race", function() {
o("resolves to first resolved", function(done) {
var a = Promise.resolve(1)
var b = new Promise(function(resolve, reject) {
callAsync(function() {resolve(2)})
})
Promise.race([a, b]).then(function(value) {
o(value).equals(1)
done()
})
})
o("rejects to first rejected", function(done) {
var a = Promise.reject(1)
var b = new Promise(function(resolve, reject) {
callAsync(function() {reject(2)})
})
Promise.race([a, b]).then(null, function(value) {
o(value).equals(1)
done()
})
})
})
o.spec("all", function() {
o("resolves to array", function(done) {
var a = new Promise(function(resolve, reject) {
callAsync(function() {resolve(1)})
})
var b = Promise.resolve(2)
Promise.all([a, b]).then(function(value) {
o(value).deepEquals([1, 2])
done()
})
})
o("resolves empty array", function(done) {
Promise.all([]).then(function(value) {
o(value).deepEquals([])
done()
})
})
o("resolves non-promise to itself", function(done) {
var a = new Promise(function(resolve, reject) {
callAsync(function() {resolve(1)})
})
var b = Promise.resolve(2)
var c = 3
Promise.all([a, b, c]).then(function(value) {
o(value).deepEquals([1, 2, 3])
done()
})
})
o("rejects to first rejected", function(done) {
var a = Promise.reject(1)
var b = new Promise(function(resolve, reject) {
callAsync(function() {reject(2)})
})
Promise.all([a, b]).then(null, function(value) {
o(value).equals(1)
done()
})
})
})
o.spec("A+ compliance", function() {
o("accesses then only once", function(done) {
var readCount = 0
var promise = Promise.resolve(1).then(function() {
return Object.create(null, {
then: {
get: function () {
++readCount
return function(onFulfilled) {
onFulfilled()
}
}
}
})
})
promise.then(function(value) {
o(readCount).equals(1)
done()
})
})
o("works if thennable resolves twice", function(done) {
var promise = Promise.resolve({
then: function(res) {
res({
then: function(resolve) {
setTimeout(function() {resolve(2)})
}
})
res(1)
}
})
promise.then(function(value) {
o(value).equals(2)
done()
})
})
o("works if thennable resolves async rejection then throws", function(done) {
var promise = new Promise(function(res) {
res({
then: function(resolve, reject) {
setTimeout(function() {reject(2)})
}
})
throw 3
})
promise.then(null, function(value) {
o(value).equals(2)
done()
})
})
})
})