--- /dev/null
+var Q = require('../kew')
+var originalQ = require('q')
+
+// test that fin() works with a synchronous resolve
+exports.testSynchronousThenAndFin = function (test) {
+ var vals = ['a', 'b']
+ var counter = 0
+
+ var promise1 = Q.resolve(vals[0])
+ var promise2 = promise1.fin(function () {
+ counter++
+ })
+ var promise3 = promise2.then(function (data) {
+ if (data === vals[0]) return vals[1]
+ })
+ var promise4 = promise3.fin(function () {
+ counter++
+ })
+
+ Q.all([promise2, promise4])
+ .then(function (data) {
+ test.equal(counter, 2, "fin() should have been called twice")
+ test.equal(data[0], vals[0], "first fin() should return the first val")
+ test.equal(data[1], vals[1], "second fin() should return the second val")
+ test.done()
+ })
+}
+
+// test that fin() works with a synchronous reject
+exports.testSynchronousFailAndFin = function (test) {
+ var errs = []
+ errs.push(new Error('nope 1'))
+ errs.push(new Error('nope 2'))
+ var counter = 0
+
+ var promise1 = Q.reject(errs[0])
+ var promise2 = promise1.fin(function () {
+ counter++
+ })
+ var promise3 = promise2.fail(function (e) {
+ if (e === errs[0]) throw errs[1]
+ })
+ var promise4 = promise3.fin(function () {
+ counter++
+ })
+
+ Q.all([
+ promise2.fail(function (e) {
+ return e === errs[0]
+ }),
+ promise4.fail(function (e) {
+ return e === errs[1]
+ })
+ ])
+ .then(function (data) {
+ test.equal(counter, 2, "fin() should have been called twice")
+ test.equal(data[0] && data[1], true, "all promises should return true")
+ test.done()
+ })
+}
+
+// test that fin() works with an asynchrnous resolve
+exports.testAsynchronousThenAndFin = function (test) {
+ var vals = ['a', 'b']
+ var counter = 0
+
+ var defer = Q.defer()
+ setTimeout(function () {
+ defer.resolve(vals[0])
+ })
+ var promise1 = defer.promise
+ var promise2 = promise1.fin(function () {
+ counter++
+ })
+ var promise3 = promise2.then(function (data) {
+ if (data !== vals[0]) return
+
+ var defer = Q.defer()
+ setTimeout(function () {
+ defer.resolve(vals[1])
+ })
+ return defer.promise
+ })
+ var promise4 = promise3.fin(function () {
+ counter++
+ })
+
+ Q.all([promise2, promise4])
+ .then(function (data) {
+ test.equal(counter, 2, "fin() should have been called twice")
+ test.equal(data[0], vals[0], "first fin() should return the first val")
+ test.equal(data[1], vals[1], "second fin() should return the second val")
+ test.done()
+ })
+}
+
+// test that fin() works with an asynchronous reject
+exports.testAsynchronousFailAndFin = function (test) {
+ var errs = []
+ errs.push(new Error('nope 1'))
+ errs.push(new Error('nope 2'))
+ var counter = 0
+
+ var defer = Q.defer()
+ setTimeout(function () {
+ defer.reject(errs[0])
+ }, 10)
+ var promise1 = defer.promise
+ var promise2 = promise1.fin(function () {
+ counter++
+ })
+ var promise3 = promise2.fail(function (e) {
+ if (e !== errs[0]) return
+
+ var defer = Q.defer()
+ setTimeout(function () {
+ defer.reject(errs[1])
+ }, 10)
+
+ return defer.promise
+ })
+ var promise4 = promise3.fin(function () {
+ counter++
+ })
+
+ Q.all([
+ promise2.fail(function (e) {
+ return e === errs[0]
+ }),
+ promise4.fail(function (e) {
+ return e === errs[1]
+ })
+ ])
+ .then(function (data) {
+ test.equal(counter, 2, "fin() should have been called twice")
+ test.equal(data[0] && data[1], true, "all promises should return true")
+ test.done()
+ })
+}
+
+// test several thens chaining
+exports.testChainedThens = function (test) {
+ var promise1 = Q.resolve('a')
+ var promise2 = promise1.then(function(data) {
+ return data + 'b'
+ })
+ var promise3 = promise2.then(function (data) {
+ return data + 'c'
+ })
+ // testing the same promise again to make sure they can run side by side
+ var promise4 = promise2.then(function (data) {
+ return data + 'c'
+ })
+
+ Q.all([promise1, promise2, promise3, promise4])
+ .then(function (data) {
+ test.equal(data[0], 'a')
+ test.equal(data[1], 'ab')
+ test.equal(data[2], 'abc')
+ test.equal(data[3], 'abc')
+ test.done()
+ })
+}
+
+// test several fails chaining
+exports.testChainedFails = function (test) {
+ var errs = []
+ errs.push(new Error("first err"))
+ errs.push(new Error("second err"))
+ errs.push(new Error("third err"))
+
+ var promise1 = Q.reject(errs[0])
+ var promise2 = promise1.fail(function (e) {
+ if (e === errs[0]) throw errs[1]
+ })
+ var promise3 = promise2.fail(function (e) {
+ if (e === errs[1]) throw errs[2]
+ })
+ var promise4 = promise2.fail(function (e) {
+ if (e === errs[1]) throw errs[2]
+ })
+
+ Q.all([
+ promise1.fail(function (e) {
+ return e === errs[0]
+ }),
+ promise2.fail(function (e) {
+ return e === errs[1]
+ }),
+ promise3.fail(function (e) {
+ return e === errs[2]
+ }),
+ promise4.fail(function (e) {
+ return e === errs[2]
+ })
+ ])
+ .then(function (data) {
+ test.equal(data[0] && data[1] && data[2] && data[3], true)
+ test.done()
+ })
+}
+
+// test that we can call end without callbacks and not fail
+exports.testEndNoCallbacks = function (test) {
+ Q.resolve(true).end()
+ test.ok("Ended successfully")
+ test.done()
+}
+
+// test that we can call end with callbacks and fail
+exports.testEndNoCallbacksThrows = function (test) {
+ var testError = new Error('Testing')
+ try {
+ Q.reject(testError).end()
+ test.fail("Should throw an error")
+ } catch (e) {
+ test.equal(e, testError, "Should throw the correct error")
+ }
+ test.done()
+}
+
+// test chaining when a promise returns a promise
+exports.testChainedPromises = function (test) {
+ var err = new Error('nope')
+ var val = 'ok'
+
+ var shouldFail = Q.reject(err)
+ var shouldSucceed = Q.resolve(val)
+
+ Q.resolve("start")
+ .then(function () {
+ return shouldFail
+ })
+ .fail(function (e) {
+ if (e === err) return shouldSucceed
+ else throw e
+ })
+ .then(function (data) {
+ test.equal(data, val, "val should be returned")
+ test.done()
+ })
+}
+
+// test .end() is called with no parent scope (causing an uncaught exception)
+exports.testChainedEndUncaught = function (test) {
+ var uncaughtErrors = 0
+ var errs = []
+ errs.push(new Error('nope 1'))
+ errs.push(new Error('nope 2'))
+ errs.push(new Error('nope 3'))
+
+ var cb = function (e) {
+ uncaughtErrors++
+ if (e === errs[2]) {
+ test.equal(uncaughtErrors, 3, "Errors should be uncaught")
+ process.removeListener('uncaughtException', cb)
+ test.done()
+ }
+ }
+ process.on('uncaughtException', cb)
+
+ var defer = Q.defer()
+ defer.promise.end()
+
+ var promise1 = defer.promise
+ var promise2 = promise1.fail(function (e) {
+ if (e === errs[0]) throw errs[1]
+ })
+ var promise3 = promise2.fail(function (e) {
+ if (e === errs[1]) throw errs[2]
+ })
+
+ promise1.end()
+ promise2.end()
+ promise3.end()
+
+ setTimeout(function () {
+ defer.reject(errs[0])
+ }, 10)
+}
+
+// test .end() is called with a parent scope and is caught
+exports.testChainedCaught = function (test) {
+ var err = new Error('nope')
+
+ try {
+ Q.reject(err).end()
+ } catch (e) {
+ test.equal(e, err, "Error should be caught")
+ test.done()
+ }
+}
+
+// test a mix of fails and thens
+exports.testChainedMixed = function (test) {
+ var errs = []
+ errs.push(new Error('nope 1'))
+ errs.push(new Error('nope 2'))
+ errs.push(new Error('nope 3'))
+
+ var vals = [3, 2, 1]
+
+ var promise1 = Q.reject(errs[0])
+ var promise2 = promise1.fail(function (e) {
+ if (e === errs[0]) return vals[0]
+ })
+ var promise3 = promise2.then(function (data) {
+ if (data === vals[0]) throw errs[1]
+ })
+ var promise4 = promise3.fail(function (e) {
+ if (e === errs[1]) return vals[1]
+ })
+ var promise5 = promise4.then(function (data) {
+ if (data === vals[1]) throw errs[2]
+ })
+ var promise6 = promise5.fail(function (e) {
+ if (e === errs[2]) return vals[2]
+ })
+
+ Q.all([
+ promise1.fail(function (e) {
+ return e === errs[0]
+ }),
+ promise2.then(function (data) {
+ return data === vals[0]
+ }),
+ promise3.fail(function (e) {
+ return e === errs[1]
+ }),
+ promise4.then(function (data) {
+ return data === vals[1]
+ }),
+ promise5.fail(function (e) {
+ return e === errs[2]
+ }),
+ promise6.then(function (data) {
+ return data === vals[2]
+ })
+ ])
+ .then(function (data) {
+ test.equal(data[0] && data[1] && data[2] && data[3] && data[4] && data[5], true, "All values should return true")
+ test.done()
+ })
+}
+
+exports.testInteroperabilityWithOtherPromises = function(test) {
+ var promise1 = Q.defer()
+ promise1.then(function(value) {
+ return originalQ(1 + value)
+ }).then(function(result) {
+ test.equal(result, 11)
+ })
+
+ var promise2 = Q.defer(),
+ errToThrow = new Error('error')
+ promise2.then(function() {
+ return originalQ.reject(errToThrow)
+ }).fail(function(err) {
+ test.equal(err, errToThrow)
+ })
+
+ promise1.resolve(10)
+ promise2.resolve()
+
+ Q.all([promise1, promise2]).then(function() {
+ test.done()
+ })
+}
+
+exports.testAllSettled = function(test) {
+ var promise1 = Q.resolve('woot')
+ var promise2 = Q.reject(new Error('oops'))
+
+ Q.allSettled([promise1, promise2, 'just a string'])
+ .then(function (data) {
+ test.equals('fulfilled', data[0].state)
+ test.equals('woot', data[0].value)
+ test.equals('rejected', data[1].state)
+ test.equals('oops', data[1].reason.message)
+ test.equals('fulfilled', data[2].state)
+ test.equals('just a string', data[2].value)
+ })
+
+ Q.allSettled([])
+ .then(function (data) {
+ test.equals(0, data.length)
+ test.done()
+ })
+}
+
+exports.testTimeout = function(test) {
+ var promise = Q.delay(50).timeout(45, 'Timeout message')
+ promise.then(function () {
+ test.fail('The promise is supposed to be timeout')
+ })
+ .fail(function (e) {
+ test.equals('Timeout message', e.message, 'The error message should be the one passed into timeout()')
+ })
+ .fin(test.done)
+}
+
+exports.testNotTimeout = function(test) {
+ var promise = Q.delay('expected data', 40).timeout(45, 'Timeout message')
+ promise.then(function (data) {
+ test.equals('expected data', data, 'The data should be the data from the original promise')
+ })
+ .fail(function (e) {
+ test.fail('The promise is supposed to be resolved before the timeout')
+ })
+ .fin(test.done)
+}
+
+exports.testNotTimeoutButReject = function(test) {
+ var promise = Q.delay(40).then(function() {throw new Error('Reject message')}).timeout(45, 'Timeout message')
+ promise.then(function (data) {
+ test.fail('The promise is supposed to be rejected')
+ })
+ .fail(function (e) {
+ test.equals('Reject message', e.message, 'The error message should be from the original promise')
+ })
+ .fin(test.done)
+}
+
+exports.testDelay = function (test) {
+ var timePassed = false
+ setTimeout(function () {
+ timePassed = true
+ }, 10)
+ Q.resolve('expected').delay(20).then(function (result) {
+ test.equal('expected', result)
+ test.ok(timePassed)
+ test.done()
+ })
+}