///
import assert = require('assert');
import SyncTasks = require('../SyncTasks');
describe('SyncTasks', function () {
function noop() {/*noop*/}
// Amount of time to wait to ensure all sync and trivially async (e.g. setTimeout(..., 0)) things have finished.
// Useful to do something 'later'.
const waitTime = 25;
it('Simple - null resolve after then', (done) => {
const task = SyncTasks.Defer();
task.promise().then(val => {
assert.equal(val, null);
done();
}, err => {
assert(false);
});
task.resolve(null);
});
it('Simple - null then after resolve', (done) => {
const task = SyncTasks.Defer();
task.resolve(null);
task.promise().then(val => {
assert.equal(val, null);
done();
}, err => {
assert(false);
});
});
it('Simple - reject', (done) => {
const task = SyncTasks.Defer();
task.reject(2);
task.promise().then(val => {
assert(false);
}, err => {
assert.equal(err, 2);
done();
});
});
it('Chain from success to success with value', (done) => {
const task = SyncTasks.Defer();
task.promise().then(val => {
assert.equal(val, 3);
return 4;
}, err => {
assert(false);
return null;
}).then(val => {
assert.equal(val, 4);
done();
}, err => {
assert(false);
});
task.resolve(3);
});
it('Chain from error to success with value', (done) => {
const task = SyncTasks.Defer();
task.promise().then(val => {
assert(false);
return -1;
}, err => {
assert.equal(err, 2);
return 4;
}).then(val => {
assert.equal(val, 4);
done();
}, err => {
assert(false);
});
task.reject(2);
});
it('Chain from success to success with promise', (done) => {
const task = SyncTasks.Defer();
task.promise().then(val => {
assert.equal(val, 3);
return SyncTasks.Resolved(4);
}, err => {
assert(false);
return -1;
}).then(val => {
assert.equal(val, 4);
done();
}, err => {
assert(false);
});
task.resolve(3);
});
it('Chain from error to success with promise', (done) => {
const task = SyncTasks.Defer();
task.promise().then(val => {
assert(false);
return -1;
}, err => {
assert.equal(err, 3);
return SyncTasks.Resolved(4);
}).then(val => {
assert.equal(val, 4);
done();
}, err => {
assert(false);
});
task.reject(3);
});
it('Chain from success to error with promise', (done) => {
const task = SyncTasks.Defer();
task.promise().then(val => {
assert.equal(val, 2);
return SyncTasks.Rejected(4);
}, err => {
assert(false);
return -1;
}).then(val => {
assert(false);
}, err => {
assert.equal(err, 4);
done();
});
task.resolve(2);
});
it('Chain from error to error with promise', (done) => {
const task = SyncTasks.Defer();
task.promise().then(val => {
assert(false);
return -1;
}, err => {
assert.equal(err, 2);
return SyncTasks.Rejected(4);
}).then(val => {
assert(false);
}, err => {
assert.equal(err, 4);
done();
});
task.reject(2);
});
it('Chain from success to promise to success with promise', (done) => {
const task = SyncTasks.Defer();
task.promise().then(val => {
assert.equal(val, 3);
const itask = SyncTasks.Resolved(4);
return itask.then(val2 => {
assert.equal(val2, 4, 'inner');
return 5;
});
}, err => {
assert(false);
return 2;
}).then(val => {
assert.equal(val, 5, 'outer');
done();
}, err => {
assert(false);
});
task.resolve(3);
});
it('Exception in success to error', (done) => {
const task = SyncTasks.Defer();
SyncTasks.config.exceptionsToConsole = false;
task.promise().then(val => {
const blah: any = null;
blah.blowup();
}, err => {
assert(false);
}).then(val => {
assert(false);
}, err => {
SyncTasks.config.exceptionsToConsole = true;
done();
});
task.resolve(3);
});
it('Exception in error to error', (done) => {
const task = SyncTasks.Defer();
SyncTasks.config.exceptionsToConsole = false;
task.promise().then(val => {
assert(false);
}, err => {
const blah: any = null;
blah.blowup();
}).then(val => {
assert(false);
}, err => {
SyncTasks.config.exceptionsToConsole = true;
done();
});
task.reject(3);
});
it('"done" basic', (done) => {
const task = SyncTasks.Defer();
task.promise().then(val => {
return 4;
}, err => {
assert(false);
return -1;
}).done(val => {
assert.equal(val, 4);
return 2; // should be ignored
}).then(val => {
assert.equal(val, 4);
done();
}, err => {
assert(false);
});
task.resolve(3);
});
it('"done" does not chain', (done) => {
const task = SyncTasks.Defer();
const innertask = SyncTasks.Defer();
let innerFinished = false;
task.promise().then(val => {
return 4;
}, err => {
assert(false);
return -1;
}).done(val => {
assert.equal(val, 4);
return innertask.promise().then(() => {
innerFinished = true;
return 2; // should be ignored
});
}).then(val => {
assert(!innerFinished);
assert.equal(val, 4);
done();
}, err => {
assert(false);
});
task.resolve(3);
innertask.resolve(1);
});
it('Finally basic', (done) => {
const task = SyncTasks.Defer();
task.promise().then(val => {
return 4;
}, err => {
assert(false);
return -1;
}).finally(val => {
assert.equal(val, 4);
return 2; // should be ignored
}).then(val => {
assert.equal(val, 4);
done();
}, err => {
assert(false);
});
task.resolve(3);
});
it('Finally does not chain', (done) => {
const task = SyncTasks.Defer();
const innertask = SyncTasks.Defer();
let innerFinished = false;
task.promise().then(val => {
return 4;
}, err => {
assert(false);
return -1;
}).finally(val => {
assert.equal(val, 4);
return innertask.promise().then(() => {
innerFinished = true;
return 2; // should be ignored
});
}).then(val => {
assert(!innerFinished);
assert.equal(val, 4);
done();
}, err => {
assert(false);
});
task.resolve(3);
innertask.resolve(1);
});
it('"all" basic success', (done) => {
const task = SyncTasks.Defer();
const task2 = SyncTasks.Defer();
const task3 = SyncTasks.Defer();
const task4 = SyncTasks.Defer();
SyncTasks.all([task.promise(), task2.promise(), task3.promise(), task4.promise()]).then(rets => {
assert.equal(rets.length, 4);
assert.equal(rets[0], 1);
assert.equal(rets[1], 2);
assert.equal(rets[2], 3);
assert.equal(rets[3], 4);
done();
}, err => {
assert(false);
});
task.resolve(1);
task2.resolve(2);
task3.resolve(3);
task4.resolve(4);
});
it('"all" basic failure', (done) => {
const task = SyncTasks.Defer();
const task2 = SyncTasks.Defer();
SyncTasks.all([task.promise(), task2.promise()]).then(rets => {
assert(false);
}, err => {
done();
});
task.resolve(1);
task2.reject(2);
});
it('"all" zero tasks', (done) => {
SyncTasks.all([]).then(rets => {
assert.equal(rets.length, 0);
done();
}, err => {
assert(false);
});
});
it('"all" single null task', (done) => {
SyncTasks.all([null]).then(rets => {
assert.equal(rets.length, 1);
done();
}, err => {
assert(false);
});
});
it('"all" tasks and nulls', (done) => {
const task = SyncTasks.Defer();
SyncTasks.all([null, task.promise()]).then(rets => {
assert.equal(rets.length, 2);
done();
}, err => {
assert(false);
});
task.resolve(1);
});
it('"race" basic success', (done) => {
const task = SyncTasks.Defer();
const task2 = SyncTasks.Defer();
const task3 = SyncTasks.Defer();
const task4 = SyncTasks.Defer();
SyncTasks.race([task.promise(), task2.promise(), task3.promise(), task4.promise()]).then(ret => {
assert.equal(ret, 1);
done();
}, err => {
assert(false);
});
task.resolve(1);
task2.resolve(2);
task3.resolve(3);
task4.resolve(4);
});
it('"race" basic failure', (done) => {
const task = SyncTasks.Defer();
const task2 = SyncTasks.Defer();
SyncTasks.race([task.promise(), task2.promise()]).then(ret => {
assert(false);
}, err => {
assert.equal(err, 1);
done();
});
task.reject(1);
task2.resolve(2);
});
it('"race" zero tasks', (done) => {
SyncTasks.race([]).then(ret => {
assert(false);
}, err => {
assert(false);
});
setTimeout(() => done(), 20);
});
it('"race" single null task', (done) => {
SyncTasks.race([null]).then(ret => {
assert.equal(ret, null);
done();
}, err => {
assert(false);
});
});
it('"race" tasks and nulls', (done) => {
const task = SyncTasks.Defer();
SyncTasks.race([null, task.promise()]).then(ret => {
assert.equal(ret, null);
done();
}, err => {
assert(false);
});
task.resolve(2);
});
it('Callbacks resolve synchronously', (done) => {
const task = SyncTasks.Defer();
let resolvedCount = 0;
task.promise().then(() => {
++resolvedCount;
}, err => {
assert(false);
});
task.resolve(1);
assert(resolvedCount === 1);
done();
});
it('Callbacks resolve in order added', (done) => {
const task = SyncTasks.Defer();
let resolvedCount = 0;
task.promise().then(() => {
assert(resolvedCount === 0);
++resolvedCount;
}, err => {
assert(false);
});
task.promise().then(() => {
assert(resolvedCount === 1);
++resolvedCount;
}, err => {
assert(false);
});
task.resolve(1);
assert(resolvedCount === 2);
done();
});
it('Failure callbacks resolve in order added', (done) => {
const task = SyncTasks.Defer();
let rejectedCount = 0;
task.promise().then(() => {
assert(false);
}, err => {
assert(rejectedCount === 0);
++rejectedCount;
});
task.promise().then(() => {
assert(false);
}, err => {
assert(rejectedCount === 1);
++rejectedCount;
});
task.reject(1);
assert(rejectedCount === 2);
done();
});
it('"unhandledErrorHandler": Failure without any callback', (done) => {
let unhandledErrorHandlerCalled = false;
const oldUnhandledErrorHandler = SyncTasks.config.unhandledErrorHandler;
SyncTasks.config.unhandledErrorHandler = () => {
unhandledErrorHandlerCalled = true;
};
SyncTasks.Rejected();
setTimeout(() => {
SyncTasks.config.unhandledErrorHandler = oldUnhandledErrorHandler;
assert(unhandledErrorHandlerCalled);
done();
}, 20);
});
it('"unhandledErrorHandler": Failure with only success callback', (done) => {
let unhandledErrorHandlerCalled = false;
const oldUnhandledErrorHandler = SyncTasks.config.unhandledErrorHandler;
SyncTasks.config.unhandledErrorHandler = () => {
unhandledErrorHandlerCalled = true;
};
SyncTasks.Rejected().then(() => {
assert(false);
});
setTimeout(() => {
SyncTasks.config.unhandledErrorHandler = oldUnhandledErrorHandler;
assert(unhandledErrorHandlerCalled);
done();
}, 20);
});
it('"unhandledErrorHandler": Failure with success callback with failure callback', (done) => {
let catchBlockReached = false;
SyncTasks.Rejected().then(() => {
assert(false);
}).catch(() => {
catchBlockReached = true;
});
setTimeout(() => {
assert(catchBlockReached);
done();
}, 20);
});
it('"unhandledErrorHandler": Success to inner failure without any callback', (done) => {
let unhandledErrorHandlerCalled = false;
const oldUnhandledErrorHandler = SyncTasks.config.unhandledErrorHandler;
SyncTasks.config.unhandledErrorHandler = () => {
unhandledErrorHandlerCalled = true;
};
SyncTasks.Resolved(4).then(() => {
return SyncTasks.Rejected();
});
setTimeout(() => {
SyncTasks.config.unhandledErrorHandler = oldUnhandledErrorHandler;
assert(unhandledErrorHandlerCalled);
done();
}, 20);
});
it('"unhandledErrorHandler": Failure to inner failure without any callback', (done) => {
let unhandledErrorHandlerCalled = 0;
const oldUnhandledErrorHandler = SyncTasks.config.unhandledErrorHandler;
SyncTasks.config.unhandledErrorHandler = (n: number) => {
unhandledErrorHandlerCalled = n;
};
SyncTasks.Rejected(1).catch(() => {
return SyncTasks.Rejected(2);
});
// Note: the outer "catch" has no failure handling so the inner error leaks out.
setTimeout(() => {
SyncTasks.config.unhandledErrorHandler = oldUnhandledErrorHandler;
assert.equal(unhandledErrorHandlerCalled, 2);
done();
}, 20);
});
it('"unhandledErrorHandler": Each chained promise must handle', (done) => {
let unhandledErrorHandlerCalled = false;
let catchBlockReached = false;
const oldUnhandledErrorHandler = SyncTasks.config.unhandledErrorHandler;
SyncTasks.config.unhandledErrorHandler = () => {
unhandledErrorHandlerCalled = true;
};
const task = SyncTasks.Rejected();
task.catch(() => {
catchBlockReached = true;
});
// Does not handle failure.
task.then(() => {
assert(false);
});
setTimeout(() => {
SyncTasks.config.unhandledErrorHandler = oldUnhandledErrorHandler;
assert(unhandledErrorHandlerCalled);
assert(catchBlockReached);
done();
}, 20);
});
it('"unhandledErrorHandler": "fail" never "handles" the failure', (done) => {
let unhandledErrorHandlerCalled = false;
let failBlockReached = false;
const oldUnhandledErrorHandler = SyncTasks.config.unhandledErrorHandler;
SyncTasks.config.unhandledErrorHandler = () => {
unhandledErrorHandlerCalled = true;
};
SyncTasks.Rejected().fail(() => {
failBlockReached = true;
// If this was .catch, it would resolve the promise (with undefined) and the failure would be handled.
});
setTimeout(() => {
SyncTasks.config.unhandledErrorHandler = oldUnhandledErrorHandler;
assert(unhandledErrorHandlerCalled);
assert(failBlockReached);
done();
}, 20);
});
it('"unhandledErrorHandler": "done" does not create another "unhandled"', (done) => {
let unhandledErrorHandlerCalled = false;
let catchBlockReached = false;
const oldUnhandledErrorHandler = SyncTasks.config.unhandledErrorHandler;
SyncTasks.config.unhandledErrorHandler = () => {
unhandledErrorHandlerCalled = true;
};
SyncTasks.Rejected().done(() => {
// Should not create a separate "unhandled" error since there is no way to "handle" it from here.
// The existing "unhandled" error should continue to be "unhandled", as other tests have verified.
}).catch(() => {
// "Handle" the failure.
catchBlockReached = true;
});
setTimeout(() => {
SyncTasks.config.unhandledErrorHandler = oldUnhandledErrorHandler;
assert(!unhandledErrorHandlerCalled);
assert(catchBlockReached);
done();
}, 20);
});
it('"unhandledErrorHandler": "fail" does not create another "unhandled"', (done) => {
let unhandledErrorHandlerCalled = false;
let catchBlockReached = false;
const oldUnhandledErrorHandler = SyncTasks.config.unhandledErrorHandler;
SyncTasks.config.unhandledErrorHandler = () => {
unhandledErrorHandlerCalled = true;
};
SyncTasks.Rejected().fail(() => {
// Should not create a separate "unhandled" error since there is no way to "handle" it from here.
// The existing "unhandled" error should continue to be "unhandled", as other tests have verified.
}).catch(() => {
// "Handle" the failure.
catchBlockReached = true;
});
setTimeout(() => {
SyncTasks.config.unhandledErrorHandler = oldUnhandledErrorHandler;
assert(!unhandledErrorHandlerCalled);
assert(catchBlockReached);
done();
}, 20);
});
it('Add callback while resolving', (done) => {
const task = SyncTasks.Defer();
const promise = task.promise();
let resolvedCount = 0;
const innerTask1 = SyncTasks.Defer();
const innerTask2 = SyncTasks.Defer();
promise.then(() => {
// While resolving: add callback to same promise.
promise.then(() => {
innerTask2.resolve(++resolvedCount);
}, err => {
assert(false);
});
// This line should be reached before innerTask2 resolves.
innerTask1.resolve(++resolvedCount);
}, err => {
assert(false);
});
task.resolve(1);
SyncTasks.all([innerTask1.promise(), innerTask2.promise()]).then(rets => {
assert(rets.length === 2);
assert(rets[0] === 1);
assert(rets[1] === 2);
done();
}, err => {
assert(false);
});
});
it('Add callback while rejecting', (done) => {
const task = SyncTasks.Defer();
const promise = task.promise();
let rejectedCount = 0;
const innerTask1 = SyncTasks.Defer();
const innerTask2 = SyncTasks.Defer();
promise.then(() => {
assert(false);
}, err => {
// While resolving: add callback to same promise.
promise.then(() => {
assert(false);
}, err => {
innerTask2.resolve(++rejectedCount);
});
// This line should be reached before innerTask2 resolves.
innerTask1.resolve(++rejectedCount);
});
task.reject(1);
SyncTasks.all([innerTask1.promise(), innerTask2.promise()]).then(rets => {
assert(rets.length === 2);
assert(rets[0] === 1);
assert(rets[1] === 2);
done();
}, err => {
assert(false);
});
});
it('Cancel task (happy path)', () => {
let canceled = false;
let cancelContext: any;
const task = SyncTasks.Defer();
const promise = task.promise();
task.onCancel((context) => {
canceled = true;
cancelContext = context;
task.reject(5);
});
promise.cancel(4);
// Check the cancel caused rejection.
return promise.then(() => {
assert(false);
return SyncTasks.Rejected();
}, (err) => {
assert.equal(err, 5);
assert(canceled);
assert.equal(cancelContext, 4);
return SyncTasks.Resolved(1);
});
});
it('Cancel chain cancels task (bubble up)', () => {
let canceled = false;
let cancelContext: any;
const task = SyncTasks.Defer();
const promise = task.promise();
task.onCancel((context) => {
canceled = true;
cancelContext = context;
task.reject(5);
});
const chain = promise.then(() => {
assert(false);
return SyncTasks.Rejected();
}, (err) => {
assert.equal(err, 5);
assert(canceled);
assert.equal(cancelContext, 4);
return -1;
});
chain.cancel(4);
// Check the chain cancel caused task rejection.
return promise.then(() => {
assert(false);
return SyncTasks.Rejected();
}, (err) => {
assert.equal(err, 5);
assert(canceled);
assert.equal(cancelContext, 4);
return SyncTasks.Resolved(1);
});
});
it('Cancel deep chain cancels task (bubble up)', () => {
let canceled = false;
let cancelContext: any;
const task = SyncTasks.Defer();
const promise = task.promise();
task.onCancel((context) => {
canceled = true;
cancelContext = context;
task.reject(5);
});
const chain = promise.then(() => {
assert(false);
return SyncTasks.Rejected();
}, (err) => {
assert.equal(err, 5);
assert(canceled);
assert.equal(cancelContext, 4);
return -1;
})
// Make the chain longer to further separate the cancel from the task.
.always(noop)
.always(noop)
.always(noop);
chain.cancel(4);
// Check the chain cancel caused task rejection.
return promise.then(() => {
assert(false);
return SyncTasks.Rejected();
}, (err) => {
assert.equal(err, 5);
assert(canceled);
assert.equal(cancelContext, 4);
return SyncTasks.Resolved(1);
});
});
it('Cancel finished task does not call cancellation handlers', () => {
const task = SyncTasks.Defer();
const promise = task.promise();
task.onCancel((context) => {
assert(false);
});
task.resolve(2);
promise.cancel(4);
});
it('Cancel finished task does not cancel inner', () => {
const task = SyncTasks.Defer();
const promise = task.promise();
task.onCancel((context) => {
assert(false);
});
promise.then(() => {
const inner = SyncTasks.Defer();
inner.onCancel((context) => {
assert(false);
});
return inner.promise();
});
task.resolve(2);
promise.cancel(4);
});
it('Cancel task then resolve during cancellation then does not call further handlers', () => {
let canceled = false;
const task = SyncTasks.Defer();
const promise = task.promise();
task.onCancel(() => {
canceled = true;
});
task.onCancel(() => {
task.resolve(2);
});
task.onCancel(() => {
assert(false);
});
promise.cancel();
assert(canceled);
// Check the onCancel caused task resolution.
return promise;
});
it('Cancel task then reject during cancellation then does not call further handlers', () => {
let canceled = false;
const task = SyncTasks.Defer();
const promise = task.promise();
task.onCancel(() => {
canceled = true;
});
task.onCancel(() => {
task.reject(5);
});
task.onCancel(() => {
assert(false);
});
promise.cancel();
assert(canceled);
// Check the onCancel caused task rejection.
return promise.then(() => {
assert(false);
return SyncTasks.Rejected();
}, (err) => {
assert.equal(err, 5);
return SyncTasks.Resolved(2);
});
});
it('Cancel inner does not cancel root task (no bubble out)', () => {
let canceled = false;
let cancelContext: any;
const task = SyncTasks.Defer();
const promise = task.promise();
task.onCancel((context) => {
assert(false);
});
const inner = SyncTasks.Defer();
inner.onCancel((context) => {
canceled = true;
cancelContext = context;
inner.reject(5);
});
const innerPromise = inner.promise();
const chain = promise.then(() => {
return innerPromise;
}, (err) => {
assert(false);
return SyncTasks.Rejected();
}).catch(err => {
assert.equal(err, 5);
assert(canceled);
assert.equal(cancelContext, 4);
return SyncTasks.Resolved(2);
});
innerPromise.cancel(4);
task.resolve(2);
return chain;
});
it('Cancel chain cancels inner task (bubble in), with task resolved late', () => {
let canceled = false;
let cancelContext: any;
const task = SyncTasks.Defer();
const promise = task.promise();
const chain = promise.then(() => {
const inner = SyncTasks.Defer();
inner.onCancel((context) => {
canceled = true;
cancelContext = context;
inner.reject(5);
});
return inner.promise();
}, (err) => {
assert(false);
return SyncTasks.Rejected();
}).catch(err => {
assert.equal(err, 5);
assert(canceled);
assert.equal(cancelContext, 4);
return SyncTasks.Resolved(2);
});
chain.cancel(4);
task.resolve(1);
return chain;
});
it('Cancel chain cancels inner task (bubble in), with task resolved early', () => {
let canceled = false;
let cancelContext: any;
const task = SyncTasks.Defer();
const promise = task.promise();
const chain = promise.then(() => {
const inner = SyncTasks.Defer();
inner.onCancel((context) => {
canceled = true;
cancelContext = context;
inner.reject(5);
});
setTimeout(() => {
chain.cancel(4);
}, waitTime);
return inner.promise();
}, (err) => {
assert(false);
return SyncTasks.Rejected();
}).catch(err => {
assert.equal(err, 5);
assert(canceled);
assert.equal(cancelContext, 4);
return SyncTasks.Resolved(2);
});
task.resolve(1);
return chain;
});
it('Cancel chain cancels inner chained task, with task resolved late', () => {
let canceled = false;
let cancelContext: any;
const task = SyncTasks.Defer();
const promise = task.promise();
const chain = promise.then(() => {
const inner = SyncTasks.Defer();
inner.onCancel((context) => {
canceled = true;
cancelContext = context;
inner.reject(5);
});
return inner.promise().then(() => {
// Chain another promise in place to make sure it works its way up to inner at some point.
return 6;
});
}, (err) => {
assert(false);
return SyncTasks.Rejected();
}).catch(err => {
assert.equal(err, 5);
assert(canceled);
assert.equal(cancelContext, 4);
return SyncTasks.Resolved(2);
});
chain.cancel(4);
task.resolve(1);
return chain;
});
it('Cancel chain cancels inner chained task, with task resolved early', () => {
let canceled = false;
let cancelContext: any;
const task = SyncTasks.Defer();
const promise = task.promise();
const ret = promise.then(() => {
const newTask = SyncTasks.Defer();
newTask.onCancel((context) => {
canceled = true;
cancelContext = context;
newTask.reject(5);
});
setTimeout(() => {
ret.cancel(4);
}, waitTime);
return newTask.promise().then(() => {
// Chain another promise in place to make sure it works its way up to the newTask at some point.
return 6;
});
}, (err) => {
assert(false);
return SyncTasks.Rejected();
}).catch(err => {
assert.equal(err, 5);
assert(canceled);
assert.equal(cancelContext, 4);
return SyncTasks.Resolved(2);
});
task.resolve(1);
return ret;
});
it('Cancel .all task cancels array of tasks', () => {
let canceled1 = false;
let canceled2 = false;
const task1 = SyncTasks.Defer();
const promise1 = task1.promise();
task1.onCancel((context) => {
canceled1 = true;
assert.equal(context, 4);
});
const task2 = SyncTasks.Defer();
const promise2 = task2.promise();
task2.onCancel((context) => {
canceled2 = true;
assert.equal(context, 4);
});
const allPromise = SyncTasks.all([promise1, promise2]);
allPromise.cancel(4);
assert(canceled1);
assert(canceled2);
});
it('Cancel chain from .all cancels array of tasks', () => {
let canceled1 = false;
const task1 = SyncTasks.Defer();
const promise1 = task1.promise();
task1.onCancel((context) => {
canceled1 = true;
assert.equal(context, 4);
});
const chain = SyncTasks.all([promise1])
.always(noop);
chain.cancel(4);
assert(canceled1);
});
it('Cancel .race task cancels array of tasks', () => {
let canceled1 = false;
let canceled2 = false;
const task1 = SyncTasks.Defer();
const promise1 = task1.promise();
task1.onCancel((context) => {
canceled1 = true;
assert.equal(context, 4);
});
const task2 = SyncTasks.Defer();
const promise2 = task2.promise();
task2.onCancel((context) => {
canceled2 = true;
assert.equal(context, 4);
});
const allPromise = SyncTasks.race([promise1, promise2]);
allPromise.cancel(4);
assert(canceled1);
assert(canceled2);
});
it('Cancel chain from .race cancels array of task', () => {
let canceled1 = false;
const task1 = SyncTasks.Defer();
const promise1 = task1.promise();
task1.onCancel((context) => {
canceled1 = true;
assert.equal(context, 4);
});
const chain = SyncTasks.race([promise1])
.always(noop);
chain.cancel(4);
assert(canceled1);
});
it('Cancel chain cancels inner chained .all task, with task resolved late', () => {
let canceled = false;
let cancelContext: any;
const task = SyncTasks.Defer();
const promise = task.promise();
const ret = promise.then(() => {
const newTask = SyncTasks.Defer();
newTask.onCancel((context) => {
canceled = true;
cancelContext = context;
newTask.reject(5);
});
return SyncTasks.all([newTask.promise()]).then(() => {
// Chain another promise in place to make sure it works its way up to the newTask at some point.
return 6;
});
}, (err) => {
assert(false);
return SyncTasks.Rejected();
}).catch(err => {
assert.equal(err, 5);
assert(canceled);
assert.equal(cancelContext, 4);
return SyncTasks.Resolved(2);
});
ret.cancel(4);
task.resolve(1);
return ret;
});
it('Cancel chain cancels inner chained .all task, with task resolved early', () => {
let canceled = false;
let cancelContext: any;
const task = SyncTasks.Defer();
const promise = task.promise();
const ret = promise.then(() => {
const newTask = SyncTasks.Defer();
newTask.onCancel((context) => {
canceled = true;
cancelContext = context;
newTask.reject(5);
});
setTimeout(() => {
ret.cancel(4);
}, waitTime);
return SyncTasks.all([newTask.promise()]).then(() => {
// Chain another promise in place to make sure it works its way up to the newTask at some point.
return 6;
});
}, (err) => {
assert(false);
return SyncTasks.Rejected();
}).catch(err => {
assert.equal(err, 5);
assert(canceled);
assert.equal(cancelContext, 4);
return SyncTasks.Resolved(2);
});
task.resolve(1);
return ret;
});
it('Cancel shared task does not cancel children (no bubble down)', () => {
const task = SyncTasks.Defer();
const promise = task.promise();
const inner1 = SyncTasks.Defer();
inner1.onCancel((context) => {
assert(false);
});
const inner2 = SyncTasks.Defer();
inner2.onCancel((context) => {
assert(false);
});
promise.then(() => inner1.promise());
promise.then(() => inner2.promise());
promise.cancel(4);
task.resolve(1);
});
it('Cancel chain of shared task does not cancel other chain (no bubble across)', () => {
let canceled = false;
let cancelContext: any;
const task = SyncTasks.Defer();
const promise = task.promise();
const inner1 = SyncTasks.Defer();
inner1.onCancel((context) => {
canceled = true;
cancelContext = context;
});
const inner2 = SyncTasks.Defer();
inner2.onCancel((context) => {
assert(false);
});
const chain1 = promise.then(() => inner1.promise());
promise.then(() => inner2.promise());
chain1.cancel(4);
task.resolve(1);
assert(canceled);
assert.equal(cancelContext, 4);
});
it('Cancel throws for "double cancel"', () => {
const oldCatchExceptions = SyncTasks.config.catchExceptions;
SyncTasks.config.catchExceptions = false;
const promise = SyncTasks.Defer().promise();
promise.cancel();
try {
promise.cancel();
assert.ok(false);
} catch (e) {
// Expected.
SyncTasks.config.catchExceptions = oldCatchExceptions;
}
});
it('Cancel does not throw for "double cancel" due to bubble up', () => {
const oldCatchExceptions = SyncTasks.config.catchExceptions;
SyncTasks.config.catchExceptions = false;
let countCancels = 0;
const task = SyncTasks.Defer();
task.onCancel(context => {
countCancels++;
});
const root = task.promise();
const promise1 = root.then(noop);
const promise2 = root.then(noop);
try {
promise1.cancel();
promise2.cancel();
SyncTasks.config.catchExceptions = oldCatchExceptions;
} catch (e) {
assert.ok(false);
}
// Make sure the root's cancel was called, but we should not be called back more than once.
assert.equal(countCancels, 1);
});
it('Cancel does not throw for "double cancel" due to bubble in', () => {
const oldCatchExceptions = SyncTasks.config.catchExceptions;
SyncTasks.config.catchExceptions = false;
const task = SyncTasks.Defer();
const promise = task.promise();
const chain = promise.then(() => {
const inner = SyncTasks.Defer();
const innerPromise = inner.promise();
innerPromise.cancel();
return innerPromise;
});
try {
chain.cancel(32);
task.resolve(1);
SyncTasks.config.catchExceptions = oldCatchExceptions;
} catch (e) {
assert.ok(false);
}
});
it('Cancel .all does not throw for "double cancel" due to bubble up', () => {
const oldCatchExceptions = SyncTasks.config.catchExceptions;
SyncTasks.config.catchExceptions = false;
const promise1 = SyncTasks.Defer().promise();
const promise2 = SyncTasks.Defer().promise();
const sink = SyncTasks.all([promise1, promise2]);
try {
sink.cancel();
promise1.cancel();
SyncTasks.config.catchExceptions = oldCatchExceptions;
} catch (e) {
assert.ok(false);
}
});
it('Cancel .all does not throw for "double cancel" due to bubble up for already canceled promise', () => {
const oldCatchExceptions = SyncTasks.config.catchExceptions;
SyncTasks.config.catchExceptions = false;
const promise1 = SyncTasks.Defer().promise();
const promise2 = SyncTasks.Defer().promise();
const sink = SyncTasks.all([promise1, promise2]);
try {
promise1.cancel();
sink.cancel();
SyncTasks.config.catchExceptions = oldCatchExceptions;
} catch (e) {
assert.ok(false);
}
});
it('Cancel .race does not throw for "double cancel" due to bubble up', () => {
const oldCatchExceptions = SyncTasks.config.catchExceptions;
SyncTasks.config.catchExceptions = false;
const promise1 = SyncTasks.Defer().promise();
const promise2 = SyncTasks.Defer().promise();
const sink = SyncTasks.race([promise1, promise2]);
try {
sink.cancel();
promise1.cancel();
SyncTasks.config.catchExceptions = oldCatchExceptions;
} catch (e) {
assert.ok(false);
}
});
it('Cancel .race does not throw for "double cancel" due to bubble up for already canceled promise', () => {
const oldCatchExceptions = SyncTasks.config.catchExceptions;
SyncTasks.config.catchExceptions = false;
const promise1 = SyncTasks.Defer().promise();
const promise2 = SyncTasks.Defer().promise();
const sink = SyncTasks.race([promise1, promise2]);
try {
promise1.cancel();
sink.cancel();
SyncTasks.config.catchExceptions = oldCatchExceptions;
} catch (e) {
assert.ok(false);
}
});
it('Cancel resolved promise does not call cancellation handlers', () => {
const defer = SyncTasks.Defer();
const promise = defer.promise();
defer.onCancel(() => {
assert(false, 'Handler should not be called');
});
defer.resolve(void 0);
promise.cancel();
});
it('Multiple bubble promise cancellation results in single cancel handler callbacks', () => {
const defer = SyncTasks.Defer();
const promise1 = defer.promise().then(() => { /* noop */});
const promise2 = defer.promise().then(() => { /* noop */});
let callbackCount = 0;
defer.onCancel(() => {
callbackCount++;
});
promise1.cancel();
promise2.cancel();
assert.equal(callbackCount, 1, 'onCancel handler not called correct number of times');
});
it('deferCallback', (done) => {
let got = false;
let got2 = false;
SyncTasks.asyncCallback(() => {
got = true;
});
setTimeout(() => {
assert(got);
assert(got2);
done();
}, 1);
SyncTasks.asyncCallback(() => {
got2 = true;
});
assert(!got);
assert(!got2);
});
it('thenDeferred Simple', (done) => {
const task = SyncTasks.Defer();
let tooEarly = true;
task.promise().then(val => {
assert.equal(val, 1);
return 2;
}, err => {
assert(false);
return null;
}).thenAsync(val => {
assert.equal(val, 2);
assert(!tooEarly);
done();
}, err => {
assert(false);
});
SyncTasks.asyncCallback(() => {
tooEarly = false;
});
task.resolve(1);
assert(tooEarly);
});
it('thenDeferred Failure', (done) => {
const task = SyncTasks.Defer();
let tooEarly = true;
task.promise().then(val => {
assert.equal(val, 1);
return SyncTasks.Rejected(4);
}, err => {
assert(false);
return 5;
}).thenAsync(val => {
assert(false);
}, err => {
assert.equal(err, 4);
assert(!tooEarly);
done();
});
SyncTasks.asyncCallback(() => {
tooEarly = false;
});
task.resolve(1);
assert(tooEarly);
});
it('toEs6Promise Simple', (done) => {
const task = SyncTasks.Defer();
let tooEarly = true;
task.promise().toEs6Promise().then(val => {
assert.equal(val, 3.50);
done();
}, err => {
assert(false);
});
SyncTasks.asyncCallback(() => {
tooEarly = false;
});
task.resolve(3.50);
assert(tooEarly);
});
it('toEs6Promise Resolved', (done) => {
const resolved = SyncTasks.Resolved(42);
let tooEarly = true;
resolved.toEs6Promise().then(val => {
assert.equal(val, 42);
assert(tooEarly);
done();
}, err => {
assert(false);
});
SyncTasks.asyncCallback(() => {
tooEarly = false;
});
assert(tooEarly);
});
it('toEs6Promise Rejected', (done) => {
const rejected = SyncTasks.Rejected(42);
let tooEarly = true;
rejected.toEs6Promise().then(val => {
assert(false);
}, err => {
assert.equal(err, 42);
assert(tooEarly);
done();
});
SyncTasks.asyncCallback(() => {
tooEarly = false;
});
assert(tooEarly);
});
it('toEs6Promise and back', (done) => {
const task = SyncTasks.Defer();
const stPromise = task.promise();
const esPromise = stPromise.toEs6Promise();
const stPromiseAgain = SyncTasks.fromThenable(esPromise);
stPromiseAgain.then(val => {
assert.equal(val, 100500);
done();
}, err => {
assert(false);
});
task.resolve(100500);
});
});