1109 lines
35 KiB
JavaScript
1109 lines
35 KiB
JavaScript
// Licensed to the Software Freedom Conservancy (SFC) under one
|
|
// or more contributor license agreements. See the NOTICE file
|
|
// distributed with this work for additional information
|
|
// regarding copyright ownership. The SFC licenses this file
|
|
// to you under the Apache License, Version 2.0 (the
|
|
// "License"); you may not use this file except in compliance
|
|
// with the License. You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing,
|
|
// software distributed under the License is distributed on an
|
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
// KIND, either express or implied. See the License for the
|
|
// specific language governing permissions and limitations
|
|
// under the License.
|
|
|
|
'use strict';
|
|
|
|
const assert = require('assert');
|
|
|
|
const testutil = require('./testutil');
|
|
const promise = require('../../lib/promise');
|
|
const {enablePromiseManager, promiseManagerSuite} = require('../../lib/test/promise');
|
|
|
|
// Aliases for readability.
|
|
const NativePromise = Promise;
|
|
const StubError = testutil.StubError;
|
|
const assertIsStubError = testutil.assertIsStubError;
|
|
const callbackHelper = testutil.callbackHelper;
|
|
const callbackPair = testutil.callbackPair;
|
|
const throwStubError = testutil.throwStubError;
|
|
const fail = () => assert.fail();
|
|
|
|
// Refer to promise_aplus_test for promise compliance with standard behavior.
|
|
describe('promise', function() {
|
|
var app, uncaughtExceptions;
|
|
|
|
beforeEach(function setUp() {
|
|
if (promise.USE_PROMISE_MANAGER) {
|
|
promise.LONG_STACK_TRACES = false;
|
|
uncaughtExceptions = [];
|
|
|
|
app = promise.controlFlow();
|
|
app.on(promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION,
|
|
(e) => uncaughtExceptions.push(e));
|
|
}
|
|
});
|
|
|
|
afterEach(function tearDown() {
|
|
if (promise.USE_PROMISE_MANAGER) {
|
|
app.reset();
|
|
promise.setDefaultFlow(new promise.ControlFlow);
|
|
assert.deepEqual([], uncaughtExceptions,
|
|
'Did not expect any uncaught exceptions');
|
|
promise.LONG_STACK_TRACES = false;
|
|
}
|
|
});
|
|
|
|
const assertIsPromise = (p) => assert.ok(promise.isPromise(p));
|
|
const assertNotPromise = (v) => assert.ok(!promise.isPromise(v));
|
|
|
|
function defer() {
|
|
let d = {};
|
|
let promise = new Promise((resolve, reject) => {
|
|
Object.assign(d, {resolve, reject});
|
|
});
|
|
d.promise = promise;
|
|
return d;
|
|
}
|
|
|
|
function createRejectedPromise(reason) {
|
|
var p = Promise.reject(reason);
|
|
p.catch(function() {}); // Silence unhandled rejection handlers.
|
|
return p;
|
|
}
|
|
|
|
enablePromiseManager(() => {
|
|
it('testCanDetectPromiseLikeObjects', function() {
|
|
assertIsPromise(new promise.Promise(function(fulfill) {
|
|
fulfill();
|
|
}));
|
|
assertIsPromise(new promise.Deferred().promise);
|
|
assertIsPromise(Promise.resolve(123));
|
|
assertIsPromise({then:function() {}});
|
|
|
|
assertNotPromise(new promise.Deferred());
|
|
assertNotPromise(undefined);
|
|
assertNotPromise(null);
|
|
assertNotPromise('');
|
|
assertNotPromise(true);
|
|
assertNotPromise(false);
|
|
assertNotPromise(1);
|
|
assertNotPromise({});
|
|
assertNotPromise({then:1});
|
|
assertNotPromise({then:true});
|
|
assertNotPromise({then:''});
|
|
});
|
|
|
|
describe('then', function() {
|
|
it('returnsOwnPromiseIfNoCallbacksWereGiven', function() {
|
|
var deferred = new promise.Deferred();
|
|
assert.equal(deferred.promise, deferred.promise.then());
|
|
assert.equal(deferred.promise, deferred.promise.catch());
|
|
assert.equal(deferred.promise, promise.when(deferred.promise));
|
|
});
|
|
|
|
it('stillConsideredUnHandledIfNoCallbacksWereGivenOnCallsToThen', function() {
|
|
promise.rejected(new StubError).then();
|
|
var handler = callbackHelper(assertIsStubError);
|
|
|
|
// so tearDown() doesn't throw
|
|
app.removeAllListeners();
|
|
app.on(promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION, handler);
|
|
return NativePromise.resolve()
|
|
// Macro yield so the uncaught exception has a chance to trigger.
|
|
.then(() => new NativePromise(resolve => setTimeout(resolve, 0)))
|
|
.then(() => handler.assertCalled());
|
|
});
|
|
});
|
|
|
|
describe('finally', function() {
|
|
it('nonFailingCallbackDoesNotSuppressOriginalError', function() {
|
|
var done = callbackHelper(assertIsStubError);
|
|
return promise.rejected(new StubError).
|
|
finally(function() {}).
|
|
catch(done).
|
|
finally(done.assertCalled);
|
|
});
|
|
|
|
it('failingCallbackSuppressesOriginalError', function() {
|
|
var done = callbackHelper(assertIsStubError);
|
|
return promise.rejected(new Error('original')).
|
|
finally(throwStubError).
|
|
catch(done).
|
|
finally(done.assertCalled);
|
|
});
|
|
|
|
it('callbackThrowsAfterFulfilledPromise', function() {
|
|
var done = callbackHelper(assertIsStubError);
|
|
return promise.fulfilled().
|
|
finally(throwStubError).
|
|
catch(done).
|
|
finally(done.assertCalled);
|
|
});
|
|
|
|
it('callbackReturnsRejectedPromise', function() {
|
|
var done = callbackHelper(assertIsStubError);
|
|
return promise.fulfilled().
|
|
finally(function() {
|
|
return promise.rejected(new StubError);
|
|
}).
|
|
catch(done).
|
|
finally(done.assertCalled);
|
|
});
|
|
});
|
|
|
|
describe('cancel', function() {
|
|
it('passesTheCancellationReasonToReject', function() {
|
|
var d = new promise.Deferred();
|
|
var res = d.promise.then(assert.fail, function(e) {
|
|
assert.ok(e instanceof promise.CancellationError);
|
|
assert.equal('because i said so', e.message);
|
|
});
|
|
d.promise.cancel('because i said so');
|
|
return res;
|
|
});
|
|
|
|
describe('can cancel original promise from its child;', function() {
|
|
it('child created by then()', function() {
|
|
var d = new promise.Deferred();
|
|
var p = d.promise.then(assert.fail, function(e) {
|
|
assert.ok(e instanceof promise.CancellationError);
|
|
assert.equal('because i said so', e.message);
|
|
return 123;
|
|
});
|
|
|
|
p.cancel('because i said so');
|
|
return p.then(v => assert.equal(123, v));
|
|
});
|
|
|
|
it('child linked by resolving with parent', function() {
|
|
let parent = promise.defer();
|
|
let child = new promise.Promise(resolve => resolve(parent.promise));
|
|
child.cancel('all done');
|
|
|
|
return parent.promise.then(
|
|
() => assert.fail('expected a cancellation'),
|
|
e => {
|
|
assert.ok(e instanceof promise.CancellationError);
|
|
assert.equal('all done', e.message);
|
|
});
|
|
});
|
|
|
|
it('grand child through thenable chain', function() {
|
|
let p = new promise.Promise(function() {/* never resolve*/});
|
|
|
|
let noop = function() {};
|
|
let gc = p.then(noop).then(noop).then(noop);
|
|
gc.cancel('stop!');
|
|
|
|
return p.then(
|
|
() => assert.fail('expected to be cancelled'),
|
|
(e) => {
|
|
assert.ok(e instanceof promise.CancellationError);
|
|
assert.equal('stop!', e.message);
|
|
});
|
|
});
|
|
|
|
it('grand child through thenable chain started at resolve', function() {
|
|
function noop() {}
|
|
|
|
let parent = promise.defer();
|
|
let child = new promise.Promise(resolve => resolve(parent.promise));
|
|
let grandChild = child.then(noop).then(noop).then(noop);
|
|
grandChild.cancel('all done');
|
|
|
|
return parent.promise.then(
|
|
() => assert.fail('expected a cancellation'),
|
|
e => {
|
|
assert.ok(e instanceof promise.CancellationError);
|
|
assert.equal('all done', e.message);
|
|
});
|
|
});
|
|
|
|
it('"parent" is a CancellableThenable', function() {
|
|
function noop() {}
|
|
|
|
class FakeThenable {
|
|
constructor(p) {
|
|
this.promise = p;
|
|
}
|
|
|
|
cancel(reason) {
|
|
this.promise.cancel(reason);
|
|
}
|
|
|
|
then(cb, eb) {
|
|
let result = this.promise.then(cb, eb);
|
|
return new FakeThenable(result);
|
|
}
|
|
}
|
|
promise.CancellableThenable.addImplementation(FakeThenable);
|
|
|
|
let root = new promise.Promise(noop);
|
|
let thenable = new FakeThenable(root);
|
|
assert.ok(promise.Thenable.isImplementation(thenable));
|
|
assert.ok(promise.CancellableThenable.isImplementation(thenable));
|
|
|
|
let child = new promise.Promise(resolve => resolve(thenable));
|
|
assert.ok(child instanceof promise.Promise);
|
|
child.cancel('stop!');
|
|
|
|
function assertStopped(p) {
|
|
return p.then(
|
|
() => assert.fail('not stopped!'),
|
|
(e) => {
|
|
assert.ok(e instanceof promise.CancellationError);
|
|
assert.equal('stop!', e.message);
|
|
});
|
|
}
|
|
|
|
return assertStopped(child).then(() => assertStopped(root));
|
|
});
|
|
});
|
|
|
|
it('canCancelATimeout', function() {
|
|
var p = promise.delayed(25)
|
|
.then(assert.fail, (e) => e instanceof promise.CancellationError);
|
|
setTimeout(() => p.cancel(), 20);
|
|
p.cancel();
|
|
return p;
|
|
});
|
|
|
|
it('can cancel timeout from grandchild', function() {
|
|
});
|
|
|
|
it('cancelIsANoopOnceAPromiseHasBeenFulfilled', function() {
|
|
var p = promise.fulfilled(123);
|
|
p.cancel();
|
|
return p.then((v) => assert.equal(123, v));
|
|
});
|
|
|
|
it('cancelIsANoopOnceAPromiseHasBeenRejected', function() {
|
|
var p = promise.rejected(new StubError);
|
|
p.cancel();
|
|
|
|
var pair = callbackPair(null, assertIsStubError);
|
|
return p.then(assert.fail, assertIsStubError);
|
|
});
|
|
|
|
it('noopCancelTriggeredOnCallbackOfResolvedPromise', function() {
|
|
var d = promise.defer();
|
|
var p = d.promise.then();
|
|
|
|
d.fulfill();
|
|
p.cancel(); // This should not throw.
|
|
return p; // This should not trigger a failure.
|
|
});
|
|
});
|
|
});
|
|
|
|
promiseManagerSuite(() => {
|
|
describe('fulfilled', function() {
|
|
it('returns input value if it is already a valid promise', function() {
|
|
let p = promise.createPromise(function() {});
|
|
let r = promise.fulfilled(p);
|
|
assert.strictEqual(p, r);
|
|
});
|
|
|
|
it('creates a new promise fulfilled with input', function() {
|
|
return promise.fulfilled(1234).then(v => assert.equal(1234, v));
|
|
});
|
|
|
|
it('can convert thenables to valid promise', function() {
|
|
let thenable = {then: function(cb) {cb(1234)}};
|
|
let p = promise.fulfilled(thenable);
|
|
assert.notStrictEqual(thenable, p);
|
|
return p.then(v => assert.equal(1234, v));
|
|
});
|
|
});
|
|
|
|
describe('when', function() {
|
|
it('ReturnsAResolvedPromiseIfGivenANonPromiseValue', function() {
|
|
var ret = promise.when('abc');
|
|
assertIsPromise(ret);
|
|
return ret.then((value) => assert.equal('abc', value));
|
|
});
|
|
|
|
it('PassesRawErrorsToCallbacks', function() {
|
|
var error = new Error('boo!');
|
|
return promise.when(error, function(value) {
|
|
assert.equal(error, value);
|
|
});
|
|
});
|
|
|
|
it('WaitsForValueToBeResolvedBeforeInvokingCallback', function() {
|
|
let d = defer();
|
|
let callback;
|
|
let result = promise.when(d.promise, callback = callbackHelper(function(value) {
|
|
assert.equal('hi', value);
|
|
}));
|
|
callback.assertNotCalled();
|
|
d.resolve('hi');
|
|
return result.then(callback.assertCalled);
|
|
});
|
|
});
|
|
|
|
describe('fullyResolved', function() {
|
|
it('primitives', function() {
|
|
function runTest(value) {
|
|
var callback, errback;
|
|
return promise.fullyResolved(value)
|
|
.then((resolved) => assert.equal(value, resolved));
|
|
}
|
|
return runTest(true)
|
|
.then(() => runTest(function() {}))
|
|
.then(() => runTest(null))
|
|
.then(() => runTest(123))
|
|
.then(() => runTest('foo bar'))
|
|
.then(() => runTest(undefined));
|
|
});
|
|
|
|
it('arrayOfPrimitives', function() {
|
|
var fn = function() {};
|
|
var array = [true, fn, null, 123, '', undefined, 1];
|
|
return promise.fullyResolved(array).then(function(resolved) {
|
|
assert.equal(array, resolved);
|
|
assert.deepEqual([true, fn, null, 123, '', undefined, 1],
|
|
resolved);
|
|
});
|
|
});
|
|
|
|
it('nestedArrayOfPrimitives', function() {
|
|
var fn = function() {};
|
|
var array = [true, [fn, null, 123], '', undefined];
|
|
return promise.fullyResolved(array)
|
|
.then(function(resolved) {
|
|
assert.equal(array, resolved);
|
|
assert.deepEqual([true, [fn, null, 123], '', undefined], resolved);
|
|
assert.deepEqual([fn, null, 123], resolved[1]);
|
|
});
|
|
});
|
|
|
|
it('arrayWithPromisedPrimitive', function() {
|
|
return promise.fullyResolved([Promise.resolve(123)])
|
|
.then(function(resolved) {
|
|
assert.deepEqual([123], resolved);
|
|
});
|
|
});
|
|
|
|
it('promiseResolvesToPrimitive', function() {
|
|
return promise.fullyResolved(Promise.resolve(123))
|
|
.then((resolved) => assert.equal(123, resolved));
|
|
});
|
|
|
|
it('promiseResolvesToArray', function() {
|
|
var fn = function() {};
|
|
var array = [true, [fn, null, 123], '', undefined];
|
|
var aPromise = Promise.resolve(array);
|
|
|
|
var result = promise.fullyResolved(aPromise);
|
|
return result.then(function(resolved) {
|
|
assert.equal(array, resolved);
|
|
assert.deepEqual([true, [fn, null, 123], '', undefined],
|
|
resolved);
|
|
assert.deepEqual([fn, null, 123], resolved[1]);
|
|
});
|
|
});
|
|
|
|
it('promiseResolvesToArrayWithPromises', function() {
|
|
var nestedPromise = Promise.resolve(123);
|
|
var aPromise = Promise.resolve([true, nestedPromise]);
|
|
return promise.fullyResolved(aPromise)
|
|
.then(function(resolved) {
|
|
assert.deepEqual([true, 123], resolved);
|
|
});
|
|
});
|
|
|
|
it('rejectsIfArrayPromiseRejects', function() {
|
|
var nestedPromise = createRejectedPromise(new StubError);
|
|
var aPromise = Promise.resolve([true, nestedPromise]);
|
|
|
|
var pair = callbackPair(null, assertIsStubError);
|
|
return promise.fullyResolved(aPromise)
|
|
.then(assert.fail, assertIsStubError);
|
|
});
|
|
|
|
it('rejectsOnFirstArrayRejection', function() {
|
|
var e1 = new Error('foo');
|
|
var e2 = new Error('bar');
|
|
var aPromise = Promise.resolve([
|
|
createRejectedPromise(e1),
|
|
createRejectedPromise(e2)
|
|
]);
|
|
|
|
return promise.fullyResolved(aPromise)
|
|
.then(assert.fail, function(error) {
|
|
assert.strictEqual(e1, error);
|
|
});
|
|
});
|
|
|
|
it('rejectsIfNestedArrayPromiseRejects', function() {
|
|
var aPromise = Promise.resolve([
|
|
Promise.resolve([
|
|
createRejectedPromise(new StubError)
|
|
])
|
|
]);
|
|
|
|
return promise.fullyResolved(aPromise)
|
|
.then(assert.fail, assertIsStubError);
|
|
});
|
|
|
|
it('simpleHash', function() {
|
|
var hash = {'a': 123};
|
|
return promise.fullyResolved(hash)
|
|
.then(function(resolved) {
|
|
assert.strictEqual(hash, resolved);
|
|
assert.deepEqual(hash, {'a': 123});
|
|
});
|
|
});
|
|
|
|
it('nestedHash', function() {
|
|
var nestedHash = {'foo':'bar'};
|
|
var hash = {'a': 123, 'b': nestedHash};
|
|
|
|
return promise.fullyResolved(hash)
|
|
.then(function(resolved) {
|
|
assert.strictEqual(hash, resolved);
|
|
assert.deepEqual({'a': 123, 'b': {'foo': 'bar'}}, resolved);
|
|
assert.strictEqual(nestedHash, resolved['b']);
|
|
});
|
|
});
|
|
|
|
it('promiseResolvesToSimpleHash', function() {
|
|
var hash = {'a': 123};
|
|
var aPromise = Promise.resolve(hash);
|
|
|
|
return promise.fullyResolved(aPromise)
|
|
.then((resolved) => assert.strictEqual(hash, resolved));
|
|
});
|
|
|
|
it('promiseResolvesToNestedHash', function() {
|
|
var nestedHash = {'foo':'bar'};
|
|
var hash = {'a': 123, 'b': nestedHash};
|
|
var aPromise = Promise.resolve(hash);
|
|
|
|
return promise.fullyResolved(aPromise)
|
|
.then(function(resolved) {
|
|
assert.strictEqual(hash, resolved);
|
|
assert.strictEqual(nestedHash, resolved['b']);
|
|
assert.deepEqual(hash, {'a': 123, 'b': {'foo': 'bar'}});
|
|
});
|
|
});
|
|
|
|
it('promiseResolvesToHashWithPromises', function() {
|
|
var aPromise = Promise.resolve({
|
|
'a': Promise.resolve(123)
|
|
});
|
|
|
|
return promise.fullyResolved(aPromise)
|
|
.then(function(resolved) {
|
|
assert.deepEqual({'a': 123}, resolved);
|
|
});
|
|
});
|
|
|
|
it('rejectsIfHashPromiseRejects', function() {
|
|
var aPromise = Promise.resolve({
|
|
'a': createRejectedPromise(new StubError)
|
|
});
|
|
|
|
return promise.fullyResolved(aPromise)
|
|
.then(assert.fail, assertIsStubError);
|
|
});
|
|
|
|
it('rejectsIfNestedHashPromiseRejects', function() {
|
|
var aPromise = Promise.resolve({
|
|
'a': {'b': createRejectedPromise(new StubError)}
|
|
});
|
|
|
|
return promise.fullyResolved(aPromise)
|
|
.then(assert.fail, assertIsStubError);
|
|
});
|
|
|
|
it('instantiatedObject', function() {
|
|
function Foo() {
|
|
this.bar = 'baz';
|
|
}
|
|
var foo = new Foo;
|
|
|
|
return promise.fullyResolved(foo).then(function(resolvedFoo) {
|
|
assert.equal(foo, resolvedFoo);
|
|
assert.ok(resolvedFoo instanceof Foo);
|
|
assert.deepEqual(new Foo, resolvedFoo);
|
|
});
|
|
});
|
|
|
|
it('withEmptyArray', function() {
|
|
return promise.fullyResolved([]).then(function(resolved) {
|
|
assert.deepEqual([], resolved);
|
|
});
|
|
});
|
|
|
|
it('withEmptyHash', function() {
|
|
return promise.fullyResolved({}).then(function(resolved) {
|
|
assert.deepEqual({}, resolved);
|
|
});
|
|
});
|
|
|
|
it('arrayWithPromisedHash', function() {
|
|
var obj = {'foo': 'bar'};
|
|
var array = [Promise.resolve(obj)];
|
|
|
|
return promise.fullyResolved(array).then(function(resolved) {
|
|
assert.deepEqual(resolved, [obj]);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('checkedNodeCall', function() {
|
|
it('functionThrows', function() {
|
|
return promise.checkedNodeCall(throwStubError)
|
|
.then(assert.fail, assertIsStubError);
|
|
});
|
|
|
|
it('functionReturnsAnError', function() {
|
|
return promise.checkedNodeCall(function(callback) {
|
|
callback(new StubError);
|
|
}).then(assert.fail, assertIsStubError);
|
|
});
|
|
|
|
it('functionReturnsSuccess', function() {
|
|
var success = 'success!';
|
|
return promise.checkedNodeCall(function(callback) {
|
|
callback(null, success);
|
|
}).then((value) => assert.equal(success, value));
|
|
});
|
|
|
|
it('functionReturnsAndThrows', function() {
|
|
var error = new Error('boom');
|
|
var error2 = new Error('boom again');
|
|
return promise.checkedNodeCall(function(callback) {
|
|
callback(error);
|
|
throw error2;
|
|
}).then(assert.fail, (e) => assert.equal(error, e));
|
|
});
|
|
|
|
it('functionThrowsAndReturns', function() {
|
|
var error = new Error('boom');
|
|
var error2 = new Error('boom again');
|
|
return promise.checkedNodeCall(function(callback) {
|
|
setTimeout(() => callback(error), 10);
|
|
throw error2;
|
|
}).then(assert.fail, (e) => assert.equal(error2, e));
|
|
});
|
|
});
|
|
|
|
describe('all', function() {
|
|
it('(base case)', function() {
|
|
let deferredObjs = [defer(), defer()];
|
|
var a = [
|
|
0, 1,
|
|
deferredObjs[0].promise,
|
|
deferredObjs[1].promise,
|
|
4, 5, 6
|
|
];
|
|
delete a[5];
|
|
|
|
var pair = callbackPair(function(value) {
|
|
assert.deepEqual([0, 1, 2, 3, 4, undefined, 6], value);
|
|
});
|
|
|
|
var result = promise.all(a).then(pair.callback, pair.errback);
|
|
pair.assertNeither();
|
|
|
|
deferredObjs[0].resolve(2);
|
|
pair.assertNeither();
|
|
|
|
deferredObjs[1].resolve(3);
|
|
return result.then(() => pair.assertCallback());
|
|
});
|
|
|
|
it('empty array', function() {
|
|
return promise.all([]).then((a) => assert.deepEqual([], a));
|
|
});
|
|
|
|
it('usesFirstRejection', function() {
|
|
let deferredObjs = [defer(), defer()];
|
|
let a = [deferredObjs[0].promise, deferredObjs[1].promise];
|
|
|
|
var result = promise.all(a).then(assert.fail, assertIsStubError);
|
|
deferredObjs[1].reject(new StubError);
|
|
setTimeout(() => deferredObjs[0].reject(Error('ignored')), 0);
|
|
return result;
|
|
});
|
|
});
|
|
|
|
describe('map', function() {
|
|
it('(base case)', function() {
|
|
var a = [1, 2, 3];
|
|
return promise.map(a, function(value, index, a2) {
|
|
assert.equal(a, a2);
|
|
assert.equal('number', typeof index, 'not a number');
|
|
return value + 1;
|
|
}).then(function(value) {
|
|
assert.deepEqual([2, 3, 4], value);
|
|
});
|
|
});
|
|
|
|
it('omitsDeleted', function() {
|
|
var a = [0, 1, 2, 3, 4, 5, 6];
|
|
delete a[1];
|
|
delete a[3];
|
|
delete a[4];
|
|
delete a[6];
|
|
|
|
var expected = [0, 1, 4, 9, 16, 25, 36];
|
|
delete expected[1];
|
|
delete expected[3];
|
|
delete expected[4];
|
|
delete expected[6];
|
|
|
|
return promise.map(a, function(value) {
|
|
return value * value;
|
|
}).then(function(value) {
|
|
assert.deepEqual(expected, value);
|
|
});
|
|
});
|
|
|
|
it('emptyArray', function() {
|
|
return promise.map([], function(value) {
|
|
return value + 1;
|
|
}).then(function(value) {
|
|
assert.deepEqual([], value);
|
|
});
|
|
});
|
|
|
|
it('inputIsPromise', function() {
|
|
var input = defer();
|
|
var result = promise.map(input.promise, function(value) {
|
|
return value + 1;
|
|
});
|
|
|
|
var pair = callbackPair(function(value) {
|
|
assert.deepEqual([2, 3, 4], value);
|
|
});
|
|
result = result.then(pair.callback, pair.errback);
|
|
|
|
setTimeout(function() {
|
|
pair.assertNeither();
|
|
input.resolve([1, 2, 3]);
|
|
}, 10);
|
|
|
|
return result;
|
|
});
|
|
|
|
it('waitsForFunctionResultToResolve', function() {
|
|
var innerResults = [
|
|
defer(),
|
|
defer()
|
|
];
|
|
|
|
var result = promise.map([1, 2], function(value, index) {
|
|
return innerResults[index].promise;
|
|
});
|
|
|
|
var pair = callbackPair(function(value) {
|
|
assert.deepEqual(['a', 'b'], value);
|
|
});
|
|
result = result.then(pair.callback, pair.errback);
|
|
|
|
return NativePromise.resolve()
|
|
.then(function() {
|
|
pair.assertNeither();
|
|
innerResults[0].resolve('a');
|
|
})
|
|
.then(function() {
|
|
pair.assertNeither();
|
|
innerResults[1].resolve('b');
|
|
return result;
|
|
})
|
|
.then(pair.assertCallback);
|
|
});
|
|
|
|
it('rejectsPromiseIfFunctionThrows', function() {
|
|
return promise.map([1], throwStubError)
|
|
.then(assert.fail, assertIsStubError);
|
|
});
|
|
|
|
it('rejectsPromiseIfFunctionReturnsRejectedPromise', function() {
|
|
return promise.map([1], function() {
|
|
return createRejectedPromise(new StubError);
|
|
}).then(assert.fail, assertIsStubError);
|
|
});
|
|
|
|
it('stopsCallingFunctionIfPreviousIterationFailed', function() {
|
|
var count = 0;
|
|
return promise.map([1, 2, 3, 4], function() {
|
|
count++;
|
|
if (count == 3) {
|
|
throw new StubError;
|
|
}
|
|
}).then(assert.fail, function(e) {
|
|
assertIsStubError(e);
|
|
assert.equal(3, count);
|
|
});
|
|
});
|
|
|
|
it('rejectsWithFirstRejectedPromise', function() {
|
|
var innerResult = [
|
|
Promise.resolve(),
|
|
createRejectedPromise(new StubError),
|
|
createRejectedPromise(Error('should be ignored'))
|
|
];
|
|
var count = 0;
|
|
return promise.map([1, 2, 3, 4], function(value, index) {
|
|
count += 1;
|
|
return innerResult[index];
|
|
}).then(assert.fail, function(e) {
|
|
assertIsStubError(e);
|
|
assert.equal(2, count);
|
|
});
|
|
});
|
|
|
|
it('preservesOrderWhenMapReturnsPromise', function() {
|
|
var deferreds = [
|
|
defer(),
|
|
defer(),
|
|
defer(),
|
|
defer()
|
|
];
|
|
var result = promise.map(deferreds, function(value) {
|
|
return value.promise;
|
|
});
|
|
|
|
var pair = callbackPair(function(value) {
|
|
assert.deepEqual([0, 1, 2, 3], value);
|
|
});
|
|
result = result.then(pair.callback, pair.errback);
|
|
|
|
return Promise.resolve()
|
|
.then(function() {
|
|
pair.assertNeither();
|
|
for (let i = deferreds.length; i > 0; i -= 1) {
|
|
deferreds[i - 1].resolve(i - 1);
|
|
}
|
|
return result;
|
|
}).then(pair.assertCallback);
|
|
});
|
|
});
|
|
|
|
describe('filter', function() {
|
|
it('basicFiltering', function() {
|
|
var a = [0, 1, 2, 3];
|
|
return promise.filter(a, function(val, index, a2) {
|
|
assert.equal(a, a2);
|
|
assert.equal('number', typeof index, 'not a number');
|
|
return val > 1;
|
|
}).then(function(val) {
|
|
assert.deepEqual([2, 3], val);
|
|
});
|
|
});
|
|
|
|
it('omitsDeleted', function() {
|
|
var a = [0, 1, 2, 3, 4, 5, 6];
|
|
delete a[3];
|
|
delete a[4];
|
|
|
|
return promise.filter(a, function(value) {
|
|
return value > 1 && value < 6;
|
|
}).then(function(val) {
|
|
assert.deepEqual([2, 5], val);
|
|
});
|
|
});
|
|
|
|
it('preservesInputs', function() {
|
|
var a = [0, 1, 2, 3];
|
|
|
|
return promise.filter(a, function(value, i, a2) {
|
|
assert.equal(a, a2);
|
|
// Even if a function modifies the input array, the original value
|
|
// should be inserted into the new array.
|
|
a2[i] = a2[i] - 1;
|
|
return a2[i] >= 1;
|
|
}).then(function(val) {
|
|
assert.deepEqual([2, 3], val);
|
|
});
|
|
});
|
|
|
|
it('inputIsPromise', function() {
|
|
var input = defer();
|
|
var result = promise.filter(input.promise, function(value) {
|
|
return value > 1 && value < 3;
|
|
});
|
|
|
|
var pair = callbackPair(function(value) {
|
|
assert.deepEqual([2], value);
|
|
});
|
|
result = result.then(pair.callback, pair.errback);
|
|
return NativePromise.resolve()
|
|
.then(function() {
|
|
pair.assertNeither();
|
|
input.resolve([1, 2, 3]);
|
|
return result;
|
|
})
|
|
.then(pair.assertCallback);
|
|
});
|
|
|
|
it('waitsForFunctionResultToResolve', function() {
|
|
var innerResults = [
|
|
defer(),
|
|
defer()
|
|
];
|
|
|
|
var result = promise.filter([1, 2], function(value, index) {
|
|
return innerResults[index].promise;
|
|
});
|
|
|
|
var pair = callbackPair(function(value) {
|
|
assert.deepEqual([2], value);
|
|
});
|
|
result = result.then(pair.callback, pair.errback);
|
|
return NativePromise.resolve()
|
|
.then(function() {
|
|
pair.assertNeither();
|
|
innerResults[0].resolve(false);
|
|
})
|
|
.then(function() {
|
|
pair.assertNeither();
|
|
innerResults[1].resolve(true);
|
|
return result;
|
|
})
|
|
.then(pair.assertCallback);
|
|
});
|
|
|
|
it('rejectsPromiseIfFunctionReturnsRejectedPromise', function() {
|
|
return promise.filter([1], function() {
|
|
return createRejectedPromise(new StubError);
|
|
}).then(assert.fail, assertIsStubError);
|
|
});
|
|
|
|
it('stopsCallingFunctionIfPreviousIterationFailed', function() {
|
|
var count = 0;
|
|
return promise.filter([1, 2, 3, 4], function() {
|
|
count++;
|
|
if (count == 3) {
|
|
throw new StubError;
|
|
}
|
|
}).then(assert.fail, function(e) {
|
|
assertIsStubError(e);
|
|
assert.equal(3, count);
|
|
});
|
|
});
|
|
|
|
it('rejectsWithFirstRejectedPromise', function() {
|
|
var innerResult = [
|
|
Promise.resolve(),
|
|
createRejectedPromise(new StubError),
|
|
createRejectedPromise(Error('should be ignored'))
|
|
];
|
|
|
|
return promise.filter([1, 2, 3, 4], function(value, index) {
|
|
assert.ok(index < innerResult.length);
|
|
return innerResult[index];
|
|
}).then(assert.fail, assertIsStubError);
|
|
});
|
|
|
|
it('preservesOrderWhenFilterReturnsPromise', function() {
|
|
var deferreds = [
|
|
defer(),
|
|
defer(),
|
|
defer(),
|
|
defer()
|
|
];
|
|
var result = promise.filter([0, 1, 2, 3], function(value, index) {
|
|
return deferreds[index].promise;
|
|
});
|
|
|
|
var pair = callbackPair(function(value) {
|
|
assert.deepEqual([1, 2], value);
|
|
});
|
|
result = result.then(pair.callback, pair.errback);
|
|
|
|
return NativePromise.resolve()
|
|
.then(function() {
|
|
pair.assertNeither();
|
|
for (let i = deferreds.length - 1; i >= 0; i -= 1) {
|
|
deferreds[i].resolve(i > 0 && i < 3);
|
|
}
|
|
return result;
|
|
}).then(pair.assertCallback);
|
|
});
|
|
});
|
|
});
|
|
|
|
enablePromiseManager(() => {
|
|
it('firesUncaughtExceptionEventIfRejectionNeverHandled', function() {
|
|
promise.rejected(new StubError);
|
|
var handler = callbackHelper(assertIsStubError);
|
|
|
|
// so tearDown() doesn't throw
|
|
app.removeAllListeners();
|
|
app.on(promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION, handler);
|
|
|
|
return NativePromise.resolve()
|
|
// Macro yield so the uncaught exception has a chance to trigger.
|
|
.then(() => new NativePromise(resolve => setTimeout(resolve, 0)))
|
|
.then(handler.assertCalled);
|
|
});
|
|
|
|
it('cannotResolveADeferredWithItself', function() {
|
|
var deferred = new promise.Deferred();
|
|
assert.throws(() => deferred.fulfill(deferred));
|
|
assert.throws(() => deferred.reject(deferred));
|
|
});
|
|
|
|
describe('testLongStackTraces', function() {
|
|
beforeEach(() => promise.LONG_STACK_TRACES = false);
|
|
afterEach(() => promise.LONG_STACK_TRACES = false);
|
|
|
|
it('doesNotAppendStackIfFeatureDisabled', function() {
|
|
promise.LONG_STACK_TRACES = false;
|
|
|
|
var error = Error('hello');
|
|
var originalStack = error.stack;
|
|
return promise.rejected(error).
|
|
then(fail).
|
|
then(fail).
|
|
then(fail).
|
|
then(fail, function(e) {
|
|
assert.equal(error, e);
|
|
assert.equal(originalStack, e.stack);
|
|
});
|
|
});
|
|
|
|
function getStackMessages(error) {
|
|
return error.stack.split(/\n/).filter(function(line) {
|
|
return /^From: /.test(line);
|
|
});
|
|
}
|
|
|
|
it('appendsInitialPromiseCreation_resolverThrows', function() {
|
|
promise.LONG_STACK_TRACES = true;
|
|
|
|
var error = Error('hello');
|
|
var originalStack = '(placeholder; will be overwritten later)';
|
|
|
|
return new promise.Promise(function() {
|
|
try {
|
|
throw error;
|
|
} catch (e) {
|
|
originalStack = e.stack;
|
|
throw e;
|
|
}
|
|
}).then(fail, function(e) {
|
|
assert.strictEqual(error, e);
|
|
if (typeof originalStack !== 'string') {
|
|
return;
|
|
}
|
|
assert.notEqual(originalStack, e.stack);
|
|
assert.equal(e.stack.indexOf(originalStack), 0,
|
|
'should start with original stack');
|
|
assert.deepEqual(['From: ManagedPromise: new'], getStackMessages(e));
|
|
});
|
|
});
|
|
|
|
it('appendsInitialPromiseCreation_rejectCalled', function() {
|
|
promise.LONG_STACK_TRACES = true;
|
|
|
|
var error = Error('hello');
|
|
var originalStack = error.stack;
|
|
|
|
return new promise.Promise(function(_, reject) {
|
|
reject(error);
|
|
}).then(fail, function(e) {
|
|
assert.equal(error, e);
|
|
if (typeof originalStack !== 'string') {
|
|
return;
|
|
}
|
|
assert.notEqual(originalStack, e.stack);
|
|
assert.equal(e.stack.indexOf(originalStack), 0,
|
|
'should start with original stack');
|
|
assert.deepEqual(['From: ManagedPromise: new'], getStackMessages(e));
|
|
});
|
|
});
|
|
|
|
it('appendsEachStepToRejectionError', function() {
|
|
promise.LONG_STACK_TRACES = true;
|
|
|
|
var error = Error('hello');
|
|
var originalStack = '(placeholder; will be overwritten later)';
|
|
|
|
return new promise.Promise(function() {
|
|
try {
|
|
throw error;
|
|
} catch (e) {
|
|
originalStack = e.stack;
|
|
throw e;
|
|
}
|
|
}).
|
|
then(fail).
|
|
catch(function(e) { throw e; }).
|
|
then(fail).
|
|
catch(function(e) { throw e; }).
|
|
then(fail, function(e) {
|
|
assert.equal(error, e);
|
|
if (typeof originalStack !== 'string') {
|
|
return;
|
|
}
|
|
assert.notEqual(originalStack, e.stack);
|
|
assert.equal(e.stack.indexOf(originalStack), 0,
|
|
'should start with original stack');
|
|
assert.deepEqual([
|
|
'From: ManagedPromise: new',
|
|
'From: Promise: then',
|
|
'From: Promise: catch',
|
|
'From: Promise: then',
|
|
'From: Promise: catch',
|
|
], getStackMessages(e));
|
|
});
|
|
});
|
|
|
|
it('errorOccursInCallbackChain', function() {
|
|
promise.LONG_STACK_TRACES = true;
|
|
|
|
var error = Error('hello');
|
|
var originalStack = '(placeholder; will be overwritten later)';
|
|
|
|
return promise.fulfilled().
|
|
then(function() {}).
|
|
then(function() {}).
|
|
then(function() {
|
|
try {
|
|
throw error;
|
|
} catch (e) {
|
|
originalStack = e.stack;
|
|
throw e;
|
|
}
|
|
}).
|
|
catch(function(e) { throw e; }).
|
|
then(fail, function(e) {
|
|
assert.equal(error, e);
|
|
if (typeof originalStack !== 'string') {
|
|
return;
|
|
}
|
|
assert.notEqual(originalStack, e.stack);
|
|
assert.equal(e.stack.indexOf(originalStack), 0,
|
|
'should start with original stack');
|
|
assert.deepEqual([
|
|
'From: Promise: then',
|
|
'From: Promise: catch',
|
|
], getStackMessages(e));
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
it('testAddThenableImplementation', function() {
|
|
function tmp() {}
|
|
assert.ok(!promise.Thenable.isImplementation(new tmp()));
|
|
promise.Thenable.addImplementation(tmp);
|
|
assert.ok(promise.Thenable.isImplementation(new tmp()));
|
|
|
|
class tmpClass {}
|
|
assert.ok(!promise.Thenable.isImplementation(new tmpClass()));
|
|
promise.Thenable.addImplementation(tmpClass);
|
|
assert.ok(promise.Thenable.isImplementation(new tmpClass()));
|
|
});
|
|
});
|