diff options
Diffstat (limited to 'node_modules/selenium-webdriver/test/lib')
8 files changed, 4082 insertions, 4013 deletions
diff --git a/node_modules/selenium-webdriver/test/lib/http_test.js b/node_modules/selenium-webdriver/test/lib/http_test.js index 343a04800..1c6c073ad 100644 --- a/node_modules/selenium-webdriver/test/lib/http_test.js +++ b/node_modules/selenium-webdriver/test/lib/http_test.js @@ -84,12 +84,14 @@ describe('http', function() { describe('command routing', function() { it('rejects unrecognized commands', function() { - assert.throws( - () => executor.execute(new Command('fake-name')), - function (err) { - return err instanceof error.UnknownCommandError - && 'Unrecognized command: fake-name' === err.message; - }); + return executor.execute(new Command('fake-name')) + .then(assert.fail, err => { + if (err instanceof error.UnknownCommandError + && 'Unrecognized command: fake-name' === err.message) { + return; + } + throw err; + }) }); it('rejects promise if client fails to send request', function() { diff --git a/node_modules/selenium-webdriver/test/lib/promise_aplus_test.js b/node_modules/selenium-webdriver/test/lib/promise_aplus_test.js index a89391590..207f490a1 100644 --- a/node_modules/selenium-webdriver/test/lib/promise_aplus_test.js +++ b/node_modules/selenium-webdriver/test/lib/promise_aplus_test.js @@ -18,57 +18,61 @@ 'use strict'; const promise = require('../../lib/promise'); +const {enablePromiseManager} = require('../../lib/test/promise'); describe('Promises/A+ Compliance Tests', function() { - // The promise spec does not define behavior for unhandled rejections and - // assumes they are effectively swallowed. This is not the case with our - // implementation, so we have to disable error propagation to test that the - // rest of our behavior is compliant. - // We run the tests with a separate instance of the control flow to ensure - // disablign error propagation does not impact other tests. - var flow = new promise.ControlFlow(); - flow.setPropagateUnhandledRejections(false); + enablePromiseManager(() => { + // The promise spec does not define behavior for unhandled rejections and + // assumes they are effectively swallowed. This is not the case with our + // implementation, so we have to disable error propagation to test that the + // rest of our behavior is compliant. + // We run the tests with a separate instance of the control flow to ensure + // disablign error propagation does not impact other tests. + var flow = new promise.ControlFlow(); + flow.setPropagateUnhandledRejections(false); - // Skip the tests in 2.2.6.1/2. We are not compliant in these scenarios. - var realDescribe = global.describe; - global.describe = function(name, fn) { - realDescribe(name, function() { - var prefix = 'Promises/A+ Compliance Tests 2.2.6: ' - + '`then` may be called multiple times on the same promise.'; - var suffix = 'even when one handler is added inside another handler'; - if (this.fullTitle().startsWith(prefix) - && this.fullTitle().endsWith(suffix)) { - var realSpecify = global.specify; - try { - global.specify = function(name) { - realSpecify(name); - }; + // Skip the tests in 2.2.6.1/2. We are not compliant in these scenarios. + var realDescribe = global.describe; + global.describe = function(name, fn) { + realDescribe(name, function() { + var prefix = 'Promises/A+ Compliance Tests ' + + 'SELENIUM_PROMISE_MANAGER=true 2.2.6: ' + + '`then` may be called multiple times on the same promise.'; + var suffix = 'even when one handler is added inside another handler'; + if (this.fullTitle().startsWith(prefix) + && this.fullTitle().endsWith(suffix)) { + var realSpecify = global.specify; + try { + global.specify = function(name) { + realSpecify(name); + }; + fn(); + } finally { + global.specify = realSpecify; + } + } else { fn(); - } finally { - global.specify = realSpecify; } - } else { - fn(); + }); + }; + + require('promises-aplus-tests').mocha({ + resolved: function(value) { + return new promise.Promise((fulfill) => fulfill(value), flow); + }, + rejected: function(error) { + return new promise.Promise((_, reject) => reject(error), flow); + }, + deferred: function() { + var d = new promise.Deferred(flow); + return { + resolve: d.fulfill, + reject: d.reject, + promise: d.promise + }; } }); - }; - require('promises-aplus-tests').mocha({ - resolved: function(value) { - return new promise.Promise((fulfill) => fulfill(value), flow); - }, - rejected: function(error) { - return new promise.Promise((_, reject) => reject(error), flow); - }, - deferred: function() { - var d = new promise.Deferred(flow); - return { - resolve: d.fulfill, - reject: d.reject, - promise: d.promise - }; - } + global.describe = realDescribe; }); - - global.describe = realDescribe; }); diff --git a/node_modules/selenium-webdriver/test/lib/promise_error_test.js b/node_modules/selenium-webdriver/test/lib/promise_error_test.js index e6de3cb92..b89a2f875 100644 --- a/node_modules/selenium-webdriver/test/lib/promise_error_test.js +++ b/node_modules/selenium-webdriver/test/lib/promise_error_test.js @@ -27,6 +27,7 @@ const testutil = require('./testutil'); const assert = require('assert'); const promise = require('../../lib/promise'); +const {enablePromiseManager} = require('../../lib/test/promise'); const NativePromise = Promise; const StubError = testutil.StubError; @@ -34,244 +35,178 @@ const throwStubError = testutil.throwStubError; const assertIsStubError = testutil.assertIsStubError; describe('promise error handling', function() { - var flow, uncaughtExceptions; - - beforeEach(function setUp() { - flow = promise.controlFlow(); - uncaughtExceptions = []; - flow.on('uncaughtException', onUncaughtException); - }); - - afterEach(function tearDown() { - return waitForIdle(flow).then(function() { - assert.deepEqual( - [], uncaughtExceptions, 'There were uncaught exceptions'); - flow.reset(); - }); - }); - - function onUncaughtException(e) { - uncaughtExceptions.push(e); - } - - function waitForAbort(opt_flow, opt_n) { - var n = opt_n || 1; - var theFlow = opt_flow || flow; - theFlow.removeAllListeners( - promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION); - return new NativePromise(function(fulfill, reject) { - theFlow.once('idle', function() { - reject(Error('expected flow to report an unhandled error')); - }); - - var errors = []; - theFlow.on('uncaughtException', onError); - function onError(e) { - errors.push(e); - if (errors.length === n) { - theFlow.removeListener('uncaughtException', onError); - fulfill(n === 1 ? errors[0] : errors); - } + enablePromiseManager(() => { + var flow, uncaughtExceptions; + + beforeEach(function setUp() { + if (promise.USE_PROMISE_MANAGER) { + flow = promise.controlFlow(); + uncaughtExceptions = []; + flow.on('uncaughtException', onUncaughtException); } }); - } - - function waitForIdle(opt_flow) { - var theFlow = opt_flow || flow; - return new NativePromise(function(fulfill, reject) { - if (theFlow.isIdle()) { - fulfill(); - return; - } - theFlow.once('idle', fulfill); - theFlow.once('uncaughtException', reject); - }); - } - - it('testRejectedPromiseTriggersErrorCallback', function() { - return promise.rejected(new StubError). - then(assert.fail, assertIsStubError); - }); - describe('callback throws trigger subsequent error callback', function() { - it('fulfilled promise', function() { - return promise.fulfilled(). - then(throwStubError). - then(assert.fail, assertIsStubError); - }); - - it('rejected promise', function() { - var e = Error('not the droids you are looking for'); - return promise.rejected(e). - then(assert.fail, throwStubError). - then(assert.fail, assertIsStubError); - }); - }); - - describe('callback returns rejected promise triggers subsequent errback', function() { - it('from fulfilled callback', function() { - return promise.fulfilled().then(function() { - return promise.rejected(new StubError); - }).then(assert.fail, assertIsStubError); - }); - - it('from rejected callback', function() { - var e = Error('not the droids you are looking for'); - return promise.rejected(e). - then(assert.fail, function() { - return promise.rejected(new StubError); - }). - then(assert.fail, assertIsStubError); + afterEach(function tearDown() { + if (promise.USE_PROMISE_MANAGER) { + return waitForIdle(flow).then(function() { + assert.deepEqual( + [], uncaughtExceptions, 'There were uncaught exceptions'); + flow.reset(); + }); + } }); - }); - - it('testReportsUnhandledRejectionsThroughTheControlFlow', function() { - promise.rejected(new StubError); - return waitForAbort().then(assertIsStubError); - }); - describe('multiple unhandled rejections outside a task', function() { - it('are reported in order they occurred', function() { - var e1 = Error('error 1'); - var e2 = Error('error 2'); + function onUncaughtException(e) { + uncaughtExceptions.push(e); + } - promise.rejected(e1); - promise.rejected(e2); + function waitForAbort(opt_flow, opt_n) { + var n = opt_n || 1; + var theFlow = opt_flow || flow; + theFlow.removeAllListeners( + promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION); + return new NativePromise(function(fulfill, reject) { + theFlow.once('idle', function() { + reject(Error('expected flow to report an unhandled error')); + }); - return waitForAbort(flow).then(function(error) { - assert.ok( - error instanceof promise.MultipleUnhandledRejectionError); - // TODO: switch to Array.from when we drop node 0.12.x var errors = []; - for (var e of error.errors) { + theFlow.on('uncaughtException', onError); + function onError(e) { errors.push(e); + if (errors.length === n) { + theFlow.removeListener('uncaughtException', onError); + fulfill(n === 1 ? errors[0] : errors); + } } - assert.deepEqual([e1, e2], errors); }); - }); - }); - - describe('does not report unhandled rejection when', function() { - it('handler added before next tick', function() { - promise.rejected(new StubError).then(assert.fail, assertIsStubError); - return waitForIdle(); - }); + } - it('added async but before next tick', function() { - var called = false; + function waitForIdle(opt_flow) { + var theFlow = opt_flow || flow; return new NativePromise(function(fulfill, reject) { - var aPromise; - NativePromise.resolve().then(function() { - aPromise.then(assert.fail, function(e) { - called = true; - assertIsStubError(e); - }); - waitForIdle().then(fulfill, reject); - }); - aPromise = promise.rejected(new StubError); - }).then(function() { - assert.ok(called); - }) + if (theFlow.isIdle()) { + fulfill(); + return; + } + theFlow.once('idle', fulfill); + theFlow.once('uncaughtException', reject); + }); + } + + it('testRejectedPromiseTriggersErrorCallback', function() { + return promise.rejected(new StubError). + then(assert.fail, assertIsStubError); }); - }); - it('testTaskThrows', function() { - return flow.execute(throwStubError).then(assert.fail, assertIsStubError); - }); + describe('callback throws trigger subsequent error callback', function() { + it('fulfilled promise', function() { + return promise.fulfilled(). + then(throwStubError). + then(assert.fail, assertIsStubError); + }); - it('testTaskReturnsRejectedPromise', function() { - return flow.execute(function() { - return promise.rejected(new StubError) - }).then(assert.fail, assertIsStubError); - }); + it('rejected promise', function() { + var e = Error('not the droids you are looking for'); + return promise.rejected(e). + then(assert.fail, throwStubError). + then(assert.fail, assertIsStubError); + }); + }); - it('testTaskHasUnhandledRejection', function() { - return flow.execute(function() { - promise.rejected(new StubError) - }).then(assert.fail, assertIsStubError); - }); + describe('callback returns rejected promise triggers subsequent errback', function() { + it('from fulfilled callback', function() { + return promise.fulfilled().then(function() { + return promise.rejected(new StubError); + }).then(assert.fail, assertIsStubError); + }); - it('testTaskfails_returnedPromiseIsUnhandled', function() { - flow.execute(throwStubError); - return waitForAbort().then(assertIsStubError); - }); + it('from rejected callback', function() { + var e = Error('not the droids you are looking for'); + return promise.rejected(e). + then(assert.fail, function() { + return promise.rejected(new StubError); + }). + then(assert.fail, assertIsStubError); + }); + }); - it('testTaskHasUnhandledRejection_cancelsRemainingSubTasks', function() { - var seen = []; - flow.execute(function() { + it('testReportsUnhandledRejectionsThroughTheControlFlow', function() { promise.rejected(new StubError); - - flow.execute(() => seen.push('a')) - .then(() => seen.push('b'), (e) => seen.push(e)); - flow.execute(() => seen.push('c')) - .then(() => seen.push('b'), (e) => seen.push(e)); + return waitForAbort().then(assertIsStubError); }); - return waitForAbort() - .then(assertIsStubError) - .then(() => assert.deepEqual([], seen)); - }); + describe('multiple unhandled rejections outside a task', function() { + it('are reported in order they occurred', function() { + var e1 = Error('error 1'); + var e2 = Error('error 2'); - describe('nested task failures', function() { - it('returns up to paren', function() { - return flow.execute(function() { - return flow.execute(function() { - return flow.execute(throwStubError); - }); - }).then(assert.fail, assertIsStubError); - }); + promise.rejected(e1); + promise.rejected(e2); - it('task throws; uncaught error bubbles up', function() { - flow.execute(function() { - flow.execute(function() { - flow.execute(throwStubError); + return waitForAbort(flow).then(function(error) { + assert.ok( + error instanceof promise.MultipleUnhandledRejectionError); + // TODO: switch to Array.from when we drop node 0.12.x + var errors = []; + for (var e of error.errors) { + errors.push(e); + } + assert.deepEqual([e1, e2], errors); }); }); - return waitForAbort().then(assertIsStubError); }); - it('task throws; uncaught error bubbles up; is caught at root', function() { - flow.execute(function() { - flow.execute(function() { - flow.execute(throwStubError); - }); - }).then(assert.fail, assertIsStubError); - return waitForIdle(); - }); + describe('does not report unhandled rejection when', function() { + it('handler added before next tick', function() { + promise.rejected(new StubError).then(assert.fail, assertIsStubError); + return waitForIdle(); + }); - it('unhandled rejection bubbles up', function() { - flow.execute(function() { - flow.execute(function() { - flow.execute(function() { - promise.rejected(new StubError); + it('added async but before next tick', function() { + var called = false; + return new NativePromise(function(fulfill, reject) { + var aPromise; + NativePromise.resolve().then(function() { + aPromise.then(assert.fail, function(e) { + called = true; + assertIsStubError(e); + }); + waitForIdle().then(fulfill, reject); }); - }); + aPromise = promise.rejected(new StubError); + }).then(function() { + assert.ok(called); + }) }); - return waitForAbort().then(assertIsStubError); }); - it('unhandled rejection bubbles up; caught at root', function() { - flow.execute(function() { - flow.execute(function() { - promise.rejected(new StubError); - }); + it('testTaskThrows', function() { + return flow.execute(throwStubError).then(assert.fail, assertIsStubError); + }); + + it('testTaskReturnsRejectedPromise', function() { + return flow.execute(function() { + return promise.rejected(new StubError) }).then(assert.fail, assertIsStubError); - return waitForIdle(); }); - it('mixtureof hanging and free subtasks', function() { - flow.execute(function() { - return flow.execute(function() { - flow.execute(throwStubError); - }); - }); + it('testTaskHasUnhandledRejection', function() { + return flow.execute(function() { + promise.rejected(new StubError) + }).then(assert.fail, assertIsStubError); + }); + + it('testTaskfails_returnedPromiseIsUnhandled', function() { + flow.execute(throwStubError); return waitForAbort().then(assertIsStubError); }); - it('cancels remaining tasks', function() { + it('testTaskHasUnhandledRejection_cancelsRemainingSubTasks', function() { var seen = []; flow.execute(function() { - flow.execute(() => promise.rejected(new StubError)); + promise.rejected(new StubError); + flow.execute(() => seen.push('a')) .then(() => seen.push('b'), (e) => seen.push(e)); flow.execute(() => seen.push('c')) @@ -282,598 +217,667 @@ describe('promise error handling', function() { .then(assertIsStubError) .then(() => assert.deepEqual([], seen)); }); - }); - - it('testTaskReturnsPromiseLikeObjectThatInvokesErrback', function() { - return flow.execute(function() { - return { - 'then': function(_, errback) { - errback('abc123'); - } - }; - }).then(assert.fail, function(value) { - assert.equal('abc123', value); - }); - }); - - describe('ControlFlow#wait();', function() { - describe('condition throws;', function() { - it('failure is caught', function() { - return flow.wait(throwStubError, 50).then(assert.fail, assertIsStubError); - }); - - it('failure is not caught', function() { - flow.wait(throwStubError, 50); - return waitForAbort().then(assertIsStubError); - }); - }); - describe('condition returns promise', function() { - it('failure is caught', function() { - return flow.wait(function() { - return promise.rejected(new StubError); - }, 50).then(assert.fail, assertIsStubError); + describe('nested task failures', function() { + it('returns up to paren', function() { + return flow.execute(function() { + return flow.execute(function() { + return flow.execute(throwStubError); + }); + }).then(assert.fail, assertIsStubError); }); - it('failure is not caught', function() { - flow.wait(function() { - return promise.rejected(new StubError); - }, 50); + it('task throws; uncaught error bubbles up', function() { + flow.execute(function() { + flow.execute(function() { + flow.execute(throwStubError); + }); + }); return waitForAbort().then(assertIsStubError); }); - }); - describe('condition has unhandled promise rejection', function() { - it('failure is caught', function() { - return flow.wait(function() { - promise.rejected(new StubError); - }, 50).then(assert.fail, assertIsStubError); + it('task throws; uncaught error bubbles up; is caught at root', function() { + flow.execute(function() { + flow.execute(function() { + flow.execute(throwStubError); + }); + }).then(assert.fail, assertIsStubError); + return waitForIdle(); }); - it('failure is not caught', function() { - flow.wait(function() { - promise.rejected(new StubError); - }, 50); + it('unhandled rejection bubbles up', function() { + flow.execute(function() { + flow.execute(function() { + flow.execute(function() { + promise.rejected(new StubError); + }); + }); + }); return waitForAbort().then(assertIsStubError); }); - }); - describe('condition has subtask failure', function() { - it('failure is caught', function() { - return flow.wait(function() { + it('unhandled rejection bubbles up; caught at root', function() { + flow.execute(function() { flow.execute(function() { - flow.execute(throwStubError); + promise.rejected(new StubError); }); - }, 50).then(assert.fail, assertIsStubError); + }).then(assert.fail, assertIsStubError); + return waitForIdle(); }); - it('failure is not caught', function() { - flow.wait(function() { - flow.execute(function() { + it('mixtureof hanging and free subtasks', function() { + flow.execute(function() { + return flow.execute(function() { flow.execute(throwStubError); }); - }, 50); + }); return waitForAbort().then(assertIsStubError); }); - }); - }); - describe('errback throws a new error', function() { - it('start with normal promise', function() { - var error = Error('an error'); - return promise.rejected(error). - catch(function(e) { - assert.equal(e, error); - throw new StubError; - }). - catch(assertIsStubError); + it('cancels remaining tasks', function() { + var seen = []; + flow.execute(function() { + flow.execute(() => promise.rejected(new StubError)); + flow.execute(() => seen.push('a')) + .then(() => seen.push('b'), (e) => seen.push(e)); + flow.execute(() => seen.push('c')) + .then(() => seen.push('b'), (e) => seen.push(e)); + }); + + return waitForAbort() + .then(assertIsStubError) + .then(() => assert.deepEqual([], seen)); + }); }); - it('start with task result', function() { - var error = Error('an error'); + it('testTaskReturnsPromiseLikeObjectThatInvokesErrback', function() { return flow.execute(function() { - throw error; - }). - catch(function(e) { - assert.equal(e, error); - throw new StubError; - }). - catch(assertIsStubError); + return { + 'then': function(_, errback) { + errback('abc123'); + } + }; + }).then(assert.fail, function(value) { + assert.equal('abc123', value); + }); }); - it('start with normal promise; uncaught error', function() { - var error = Error('an error'); - promise.rejected(error). - catch(function(e) { - assert.equal(e, error); - throw new StubError; - }); - return waitForAbort().then(assertIsStubError); - }); + describe('ControlFlow#wait();', function() { + describe('condition throws;', function() { + it('failure is caught', function() { + return flow.wait(throwStubError, 50).then(assert.fail, assertIsStubError); + }); - it('start with task result; uncaught error', function() { - var error = Error('an error'); - flow.execute(function() { - throw error; - }). - catch(function(e) { - assert.equal(e, error); - throw new StubError; + it('failure is not caught', function() { + flow.wait(throwStubError, 50); + return waitForAbort().then(assertIsStubError); + }); }); - return waitForAbort().then(assertIsStubError); - }); - }); - it('thrownPromiseCausesCallbackRejection', function() { - let p = promise.fulfilled(1234); - return promise.fulfilled().then(function() { - throw p; - }).then(assert.fail, function(value) { - assert.strictEqual(p, value); - }); - }); + describe('condition returns promise', function() { + it('failure is caught', function() { + return flow.wait(function() { + return promise.rejected(new StubError); + }, 50).then(assert.fail, assertIsStubError); + }); - describe('task throws promise', function() { - it('promise was fulfilled', function() { - var toThrow = promise.fulfilled(1234); - flow.execute(function() { - throw toThrow; - }).then(assert.fail, function(value) { - assert.equal(toThrow, value); - return toThrow; - }).then(function(value) { - assert.equal(1234, value); + it('failure is not caught', function() { + flow.wait(function() { + return promise.rejected(new StubError); + }, 50); + return waitForAbort().then(assertIsStubError); + }); }); - return waitForIdle(); - }); - - it('promise was rejected', function() { - var toThrow = promise.rejected(new StubError); - toThrow.catch(function() {}); // For tearDown. - flow.execute(function() { - throw toThrow; - }).then(assert.fail, function(e) { - assert.equal(toThrow, e); - return e; - }).then(assert.fail, assertIsStubError); - return waitForIdle(); - }); - }); - it('testFailsTaskIfThereIsAnUnhandledErrorWhileWaitingOnTaskResult', function() { - var d = promise.defer(); - flow.execute(function() { - promise.rejected(new StubError); - return d.promise; - }).then(assert.fail, assertIsStubError); + describe('condition has unhandled promise rejection', function() { + it('failure is caught', function() { + return flow.wait(function() { + promise.rejected(new StubError); + }, 50).then(assert.fail, assertIsStubError); + }); - return waitForIdle().then(function() { - return d.promise; - }).then(assert.fail, function(e) { - assert.equal('CancellationError: StubError', e.toString()); - }); - }); + it('failure is not caught', function() { + flow.wait(function() { + promise.rejected(new StubError); + }, 50); + return waitForAbort().then(assertIsStubError); + }); + }); - it('testFailsParentTaskIfAsyncScheduledTaskFails', function() { - var d = promise.defer(); - flow.execute(function() { - flow.execute(throwStubError); - return d.promise; - }).then(assert.fail, assertIsStubError); + describe('condition has subtask failure', function() { + it('failure is caught', function() { + return flow.wait(function() { + flow.execute(function() { + flow.execute(throwStubError); + }); + }, 50).then(assert.fail, assertIsStubError); + }); - return waitForIdle().then(function() { - return d.promise; - }).then(assert.fail, function(e) { - assert.equal('CancellationError: StubError', e.toString()); + it('failure is not caught', function() { + flow.wait(function() { + flow.execute(function() { + flow.execute(throwStubError); + }); + }, 50); + return waitForAbort().then(assertIsStubError); + }); + }); }); - }); - describe('long stack traces', function() { - afterEach(() => promise.LONG_STACK_TRACES = false); + describe('errback throws a new error', function() { + it('start with normal promise', function() { + var error = Error('an error'); + return promise.rejected(error). + catch(function(e) { + assert.equal(e, error); + throw new StubError; + }). + catch(assertIsStubError); + }); - it('always includes task stacks in failures', function() { - promise.LONG_STACK_TRACES = false; - flow.execute(function() { - flow.execute(function() { - flow.execute(throwStubError, 'throw error'); - }, 'two'); - }, 'three'). - then(assert.fail, function(e) { - assertIsStubError(e); - if (typeof e.stack !== 'string') { - return; - } - var messages = e.stack.split(/\n/).filter(function(line, index) { - return /^From: /.test(line); - }); - assert.deepEqual([ - 'From: Task: throw error', - 'From: Task: two', - 'From: Task: three' - ], messages); + it('start with task result', function() { + var error = Error('an error'); + return flow.execute(function() { + throw error; + }). + catch(function(e) { + assert.equal(e, error); + throw new StubError; + }). + catch(assertIsStubError); + }); + + it('start with normal promise; uncaught error', function() { + var error = Error('an error'); + promise.rejected(error). + catch(function(e) { + assert.equal(e, error); + throw new StubError; + }); + return waitForAbort().then(assertIsStubError); }); - return waitForIdle(); - }); - it('does not include completed tasks', function () { - flow.execute(function() {}, 'succeeds'); - flow.execute(throwStubError, 'kaboom').then(assert.fail, function(e) { - assertIsStubError(e); - if (typeof e.stack !== 'string') { - return; - } - var messages = e.stack.split(/\n/).filter(function(line, index) { - return /^From: /.test(line); + it('start with task result; uncaught error', function() { + var error = Error('an error'); + flow.execute(function() { + throw error; + }). + catch(function(e) { + assert.equal(e, error); + throw new StubError; }); - assert.deepEqual(['From: Task: kaboom'], messages); + return waitForAbort().then(assertIsStubError); }); - return waitForIdle(); }); - it('does not include promise chain when disabled', function() { - promise.LONG_STACK_TRACES = false; - flow.execute(function() { - flow.execute(function() { - return promise.fulfilled(). - then(function() {}). - then(function() {}). - then(throwStubError); - }, 'eventually assert.fails'); - }, 'start'). - then(assert.fail, function(e) { - assertIsStubError(e); - if (typeof e.stack !== 'string') { - return; - } - var messages = e.stack.split(/\n/).filter(function(line, index) { - return /^From: /.test(line); - }); - assert.deepEqual([ - 'From: Task: eventually assert.fails', - 'From: Task: start' - ], messages); + it('thrownPromiseCausesCallbackRejection', function() { + let p = promise.fulfilled(1234); + return promise.fulfilled().then(function() { + throw p; + }).then(assert.fail, function(value) { + assert.strictEqual(p, value); }); - return waitForIdle(); }); - it('includes promise chain when enabled', function() { - promise.LONG_STACK_TRACES = true; - flow.execute(function() { + describe('task throws promise', function() { + it('promise was fulfilled', function() { + var toThrow = promise.fulfilled(1234); flow.execute(function() { - return promise.fulfilled(). - then(function() {}). - then(function() {}). - then(throwStubError); - }, 'eventually assert.fails'); - }, 'start'). - then(assert.fail, function(e) { - assertIsStubError(e); - if (typeof e.stack !== 'string') { - return; - } - var messages = e.stack.split(/\n/).filter(function(line, index) { - return /^From: /.test(line); + throw toThrow; + }).then(assert.fail, function(value) { + assert.equal(toThrow, value); + return toThrow; + }).then(function(value) { + assert.equal(1234, value); }); - assert.deepEqual([ - 'From: Promise: then', - 'From: Task: eventually assert.fails', - 'From: Task: start' - ], messages); + return waitForIdle(); }); - return waitForIdle(); - }); - }); - describe('frame cancels remaining tasks', function() { - it('on unhandled task failure', function() { - var run = false; - return flow.execute(function() { - flow.execute(throwStubError); - flow.execute(function() { run = true; }); - }).then(assert.fail, function(e) { - assertIsStubError(e); - assert.ok(!run); + it('promise was rejected', function() { + var toThrow = promise.rejected(new StubError); + toThrow.catch(function() {}); // For tearDown. + flow.execute(function() { + throw toThrow; + }).then(assert.fail, function(e) { + assert.equal(toThrow, e); + return e; + }).then(assert.fail, assertIsStubError); + return waitForIdle(); }); }); - it('on unhandled promise rejection', function() { - var run = false; - return flow.execute(function() { + it('testFailsTaskIfThereIsAnUnhandledErrorWhileWaitingOnTaskResult', function() { + var d = promise.defer(); + flow.execute(function() { promise.rejected(new StubError); - flow.execute(function() { run = true; }); + return d.promise; + }).then(assert.fail, assertIsStubError); + + return waitForIdle().then(function() { + return d.promise; }).then(assert.fail, function(e) { - assertIsStubError(e); - assert.ok(!run); + assert.equal('CancellationError: StubError', e.toString()); }); }); - it('if task throws', function() { - var run = false; - return flow.execute(function() { - flow.execute(function() { run = true; }); - throw new StubError; + it('testFailsParentTaskIfAsyncScheduledTaskFails', function() { + var d = promise.defer(); + flow.execute(function() { + flow.execute(throwStubError); + return d.promise; + }).then(assert.fail, assertIsStubError); + + return waitForIdle().then(function() { + return d.promise; }).then(assert.fail, function(e) { - assertIsStubError(e); - assert.ok(!run); + assert.equal('CancellationError: StubError', e.toString()); }); }); - describe('task callbacks scheduled in another frame', function() { - flow = promise.controlFlow(); - function noop() {} - - let subTask; + describe('long stack traces', function() { + afterEach(() => promise.LONG_STACK_TRACES = false); - before(function() { + it('always includes task stacks in failures', function() { + promise.LONG_STACK_TRACES = false; flow.execute(function() { - // This task will be discarded and never run because of the error below. - subTask = flow.execute(() => 'abc'); - throw new StubError('stub'); - }).catch(noop); - }); - - function assertCancellation(e) { - assert.ok(e instanceof promise.CancellationError); - assert.equal( - 'Task was discarded due to a previous failure: stub', e.message); - } - - it('are rejected with cancellation error', function() { - let result; - return Promise.resolve().then(function() { - return flow.execute(function() { - result = subTask.then(assert.fail); + flow.execute(function() { + flow.execute(throwStubError, 'throw error'); + }, 'two'); + }, 'three'). + then(assert.fail, function(e) { + assertIsStubError(e); + if (typeof e.stack !== 'string') { + return; + } + var messages = e.stack.split(/\n/).filter(function(line, index) { + return /^From: /.test(line); }); - }) - .then(() => result) - .then(assert.fail, assertCancellation); + assert.deepEqual([ + 'From: Task: throw error', + 'From: Task: two', + 'From: Task: three' + ], messages); + }); + return waitForIdle(); + }); + + it('does not include completed tasks', function () { + flow.execute(function() {}, 'succeeds'); + flow.execute(throwStubError, 'kaboom').then(assert.fail, function(e) { + assertIsStubError(e); + if (typeof e.stack !== 'string') { + return; + } + var messages = e.stack.split(/\n/).filter(function(line, index) { + return /^From: /.test(line); + }); + assert.deepEqual(['From: Task: kaboom'], messages); + }); + return waitForIdle(); }); - it('cancellation errors propagate through callbacks (1)', function() { - let result; - return Promise.resolve().then(function() { - return flow.execute(function() { - result = subTask - .then(assert.fail, assertCancellation) - .then(() => 'abc123'); + it('does not include promise chain when disabled', function() { + promise.LONG_STACK_TRACES = false; + flow.execute(function() { + flow.execute(function() { + return promise.fulfilled(). + then(function() {}). + then(function() {}). + then(throwStubError); + }, 'eventually assert.fails'); + }, 'start'). + then(assert.fail, function(e) { + assertIsStubError(e); + if (typeof e.stack !== 'string') { + return; + } + var messages = e.stack.split(/\n/).filter(function(line, index) { + return /^From: /.test(line); }); - }) - .then(() => result) - .then(value => assert.equal('abc123', value)); + assert.deepEqual([ + 'From: Task: eventually assert.fails', + 'From: Task: start' + ], messages); + }); + return waitForIdle(); }); - it('cancellation errors propagate through callbacks (2)', function() { - let result; - return Promise.resolve().then(function() { - return flow.execute(function() { - result = subTask.then(assert.fail) - .then(noop, assertCancellation) - .then(() => 'fin'); + it('includes promise chain when enabled', function() { + promise.LONG_STACK_TRACES = true; + flow.execute(function() { + flow.execute(function() { + return promise.fulfilled(). + then(function() {}). + then(function() {}). + then(throwStubError); + }, 'eventually assert.fails'); + }, 'start'). + then(assert.fail, function(e) { + assertIsStubError(e); + if (typeof e.stack !== 'string') { + return; + } + var messages = e.stack.split(/\n/).filter(function(line, index) { + return /^From: /.test(line); }); - }) - // Verify result actually computed successfully all the way through. - .then(() => result) - .then(value => assert.equal('fin', value)); + assert.deepEqual([ + 'From: Promise: then', + 'From: Task: eventually assert.fails', + 'From: Task: start' + ], messages); + }); + return waitForIdle(); }); }); - }); - it('testRegisteredTaskCallbacksAreDroppedWhenTaskIsCancelled_return', function() { - var seen = []; - return flow.execute(function() { - flow.execute(throwStubError); + describe('frame cancels remaining tasks', function() { + it('on unhandled task failure', function() { + var run = false; + return flow.execute(function() { + flow.execute(throwStubError); + flow.execute(function() { run = true; }); + }).then(assert.fail, function(e) { + assertIsStubError(e); + assert.ok(!run); + }); + }); - flow.execute(function() { - seen.push(1); - }).then(function() { - seen.push(2); - }, function() { - seen.push(3); - }); - }).then(assert.fail, function(e) { - assertIsStubError(e); - assert.deepEqual([], seen); - }); - }); + it('on unhandled promise rejection', function() { + var run = false; + return flow.execute(function() { + promise.rejected(new StubError); + flow.execute(function() { run = true; }); + }).then(assert.fail, function(e) { + assertIsStubError(e); + assert.ok(!run); + }); + }); - it('testRegisteredTaskCallbacksAreDroppedWhenTaskIsCancelled_withReturn', function() { - var seen = []; - return flow.execute(function() { - flow.execute(throwStubError); + it('if task throws', function() { + var run = false; + return flow.execute(function() { + flow.execute(function() { run = true; }); + throw new StubError; + }).then(assert.fail, function(e) { + assertIsStubError(e); + assert.ok(!run); + }); + }); - return flow.execute(function() { - seen.push(1); - }).then(function() { - seen.push(2); - }, function() { - seen.push(3); - }); - }).then(assert.fail, function(e) { - assertIsStubError(e); - assert.deepEqual([], seen); - }); - }); + describe('task callbacks scheduled in another frame', function() { + flow = promise.controlFlow(); + function noop() {} - it('testTasksWithinACallbackAreDroppedIfContainingTaskIsAborted', function() { - var seen = []; - return flow.execute(function() { - flow.execute(throwStubError); + let subTask; - // None of the callbacks on this promise should execute because the - // task assert.failure above is never handled, causing the containing task to - // abort. - promise.fulfilled().then(function() { - seen.push(1); - return flow.execute(function() { - seen.push(2); + before(function() { + flow.execute(function() { + // This task will be discarded and never run because of the error below. + subTask = flow.execute(() => 'abc'); + throw new StubError('stub'); + }).catch(noop); }); - }).finally(function() { - seen.push(3); - }); - }).then(assert.fail, function(e) { - assertIsStubError(e); - assert.deepEqual([], seen); - }); - }); + function assertCancellation(e) { + assert.ok(e instanceof promise.CancellationError); + assert.equal( + 'Task was discarded due to a previous failure: stub', e.message); + } - it('testTaskIsCancelledAfterWaitTimeout', function() { - var seen = []; - return flow.execute(function() { - flow.wait(function() { - return promise.delayed(50); - }, 5); + it('are rejected with cancellation error', function() { + let result; + return Promise.resolve().then(function() { + return flow.execute(function() { + result = subTask.then(assert.fail); + }); + }) + .then(() => result) + .then(assert.fail, assertCancellation); + }); - return flow.execute(function() { - seen.push(1); - }).then(function() { - seen.push(2); - }, function() { - seen.push(3); - }); - }).then(assert.fail, function() { - assert.deepEqual([], seen); + it('cancellation errors propagate through callbacks (1)', function() { + let result; + return Promise.resolve().then(function() { + return flow.execute(function() { + result = subTask + .then(assert.fail, assertCancellation) + .then(() => 'abc123'); + }); + }) + .then(() => result) + .then(value => assert.equal('abc123', value)); + }); + + it('cancellation errors propagate through callbacks (2)', function() { + let result; + return Promise.resolve().then(function() { + return flow.execute(function() { + result = subTask.then(assert.fail) + .then(noop, assertCancellation) + .then(() => 'fin'); + }); + }) + // Verify result actually computed successfully all the way through. + .then(() => result) + .then(value => assert.equal('fin', value)); + }); + }); }); - }); - describe('task callbacks get cancellation error if registered after task was cancelled', function() { - it('(a)', function() { - var task; - flow.execute(function() { + it('testRegisteredTaskCallbacksAreDroppedWhenTaskIsCancelled_return', function() { + var seen = []; + return flow.execute(function() { flow.execute(throwStubError); - task = flow.execute(function() {}); - }).then(assert.fail, assertIsStubError); - return waitForIdle().then(function() { - return task.then(assert.fail, function(e) { - assert.ok(e instanceof promise.CancellationError); + + flow.execute(function() { + seen.push(1); + }).then(function() { + seen.push(2); + }, function() { + seen.push(3); }); + }).then(assert.fail, function(e) { + assertIsStubError(e); + assert.deepEqual([], seen); }); }); - it('(b)', function() { + it('testRegisteredTaskCallbacksAreDroppedWhenTaskIsCancelled_withReturn', function() { var seen = []; - - var task; - flow.execute(function() { + return flow.execute(function() { flow.execute(throwStubError); - task = flow.execute(function() {}); - task.then(() => seen.push(1)) - .then(() => seen.push(2)); - task.then(() => seen.push(3)) - .then(() => seen.push(4)); + return flow.execute(function() { + seen.push(1); + }).then(function() { + seen.push(2); + }, function() { + seen.push(3); + }); + }).then(assert.fail, function(e) { + assertIsStubError(e); + assert.deepEqual([], seen); + }); + }); - }).then(assert.fail, assertIsStubError); + it('testTasksWithinACallbackAreDroppedIfContainingTaskIsAborted', function() { + var seen = []; + return flow.execute(function() { + flow.execute(throwStubError); - return waitForIdle().then(function() { - return task.then(assert.fail, function(e) { - seen.push(5); - assert.ok(e instanceof promise.CancellationError); + // None of the callbacks on this promise should execute because the + // task assert.failure above is never handled, causing the containing task to + // abort. + promise.fulfilled().then(function() { + seen.push(1); + return flow.execute(function() { + seen.push(2); + }); + }).finally(function() { + seen.push(3); }); - }).then(() => assert.deepEqual([5], seen)); + + }).then(assert.fail, function(e) { + assertIsStubError(e); + assert.deepEqual([], seen); + }); }); - }); - it('unhandledRejectionInParallelTaskQueue', function() { - var seen = []; - function schedule(name) { - return flow.execute(() => seen.push(name), name); - } + it('testTaskIsCancelledAfterWaitTimeout', function() { + var seen = []; + return flow.execute(function() { + flow.wait(function() { + return promise.delayed(50); + }, 5); - flow.async(function() { - schedule('a.1'); - flow.execute(throwStubError, 'a.2 (throws)'); + return flow.execute(function() { + seen.push(1); + }).then(function() { + seen.push(2); + }, function() { + seen.push(3); + }); + }).then(assert.fail, function() { + assert.deepEqual([], seen); + }); }); - var b3; - flow.async(function() { - schedule('b.1'); - schedule('b.2'); - b3 = schedule('b.3'); - }); + describe('task callbacks get cancellation error if registered after task was cancelled', function() { + it('(a)', function() { + var task; + flow.execute(function() { + flow.execute(throwStubError); + task = flow.execute(function() {}); + }).then(assert.fail, assertIsStubError); + return waitForIdle().then(function() { + return task.then(assert.fail, function(e) { + assert.ok(e instanceof promise.CancellationError); + }); + }); + }); - var c3; - flow.async(function() { - schedule('c.1'); - schedule('c.2'); - c3 = schedule('c.3'); - }); + it('(b)', function() { + var seen = []; + + var task; + flow.execute(function() { + flow.execute(throwStubError); + task = flow.execute(function() {}); + + task.then(() => seen.push(1)) + .then(() => seen.push(2)); + task.then(() => seen.push(3)) + .then(() => seen.push(4)); + + }).then(assert.fail, assertIsStubError); - function assertWasCancelled(p) { - return p.then(assert.fail, function(e) { - assert.ok(e instanceof promise.CancellationError); + return waitForIdle().then(function() { + return task.then(assert.fail, function(e) { + seen.push(5); + assert.ok(e instanceof promise.CancellationError); + }); + }).then(() => assert.deepEqual([5], seen)); }); - } + }); - return waitForAbort() - .then(function() { - assert.deepEqual(['a.1', 'b.1', 'c.1', 'b.2', 'c.2'], seen); - assert.ok(!b3.isPending()); - assert.ok(!c3.isPending()); - }) - .then(() => assertWasCancelled(b3)) - .then(() => assertWasCancelled(c3)); - }); + it('unhandledRejectionInParallelTaskQueue', function() { + var seen = []; + function schedule(name) { + return flow.execute(() => seen.push(name), name); + } - it('errorsInAsyncFunctionsAreReportedAsUnhandledRejection', function() { - flow.removeAllListeners(); // For tearDown. + flow.async(function() { + schedule('a.1'); + flow.execute(throwStubError, 'a.2 (throws)'); + }); - var task; - return new Promise(function(fulfill) { - flow.once('uncaughtException', fulfill); + var b3; flow.async(function() { - task = flow.execute(function() {}); - throw Error('boom'); + schedule('b.1'); + schedule('b.2'); + b3 = schedule('b.3'); }); - }).then(function(error) { - assert.ok(error instanceof promise.CancellationError); - assert.ok(!task.isPending()); - return task.catch(function(error) { - assert.ok(error instanceof promise.CancellationError); + + var c3; + flow.async(function() { + schedule('c.1'); + schedule('c.2'); + c3 = schedule('c.3'); }); + + function assertWasCancelled(p) { + return p.then(assert.fail, function(e) { + assert.ok(e instanceof promise.CancellationError); + }); + } + + return waitForAbort() + .then(function() { + assert.deepEqual(['a.1', 'b.1', 'c.1', 'b.2', 'c.2'], seen); + }) + .then(() => assertWasCancelled(b3)) + .then(() => assertWasCancelled(c3)); }); - }); - describe('does not wait for values thrown from callbacks to be resolved', function() { - it('(a)', function() { - var p1 = promise.fulfilled(); - var reason = promise.fulfilled('should not see me'); - return p1.then(function() { - throw reason; - }).then(assert.fail, function(e) { - assert.equal(reason, e); + it('errorsInAsyncFunctionsAreReportedAsUnhandledRejection', function() { + flow.removeAllListeners(); // For tearDown. + + var task; + return new Promise(function(fulfill) { + flow.once('uncaughtException', fulfill); + flow.async(function() { + task = flow.execute(function() {}); + throw Error('boom'); + }); + }).then(function(error) { + assert.ok(error instanceof promise.CancellationError); + return task.catch(function(error) { + assert.ok(error instanceof promise.CancellationError); + }); }); }); - it('(b)', function() { - var p1 = promise.fulfilled(); - var reason = promise.rejected('should not see me'); - reason.catch(function() {}); // For tearDown. - return p1.then(function() { - throw reason; - }).then(assert.fail, function(e) { - assert.equal(reason, e); + describe('does not wait for values thrown from callbacks to be resolved', function() { + it('(a)', function() { + var p1 = promise.fulfilled(); + var reason = promise.fulfilled('should not see me'); + return p1.then(function() { + throw reason; + }).then(assert.fail, function(e) { + assert.equal(reason, e); + }); }); - }); - it('(c)', function() { - var p1 = promise.fulfilled(); - var reason = promise.defer(); - setTimeout(() => reason.fulfill('should not see me'), 100); - return p1.then(function() { - throw reason.promise; - }).then(assert.fail, function(e) { - assert.equal(reason.promise, e); + it('(b)', function() { + var p1 = promise.fulfilled(); + var reason = promise.rejected('should not see me'); + reason.catch(function() {}); // For tearDown. + return p1.then(function() { + throw reason; + }).then(assert.fail, function(e) { + assert.equal(reason, e); + }); }); - }); - it('(d)', function() { - var p1 = promise.fulfilled(); - var reason = {then: function() {}}; // A thenable like object. - return p1.then(function() { - throw reason; - }).then(assert.fail, function(e) { - assert.equal(reason, e); + it('(c)', function() { + var p1 = promise.fulfilled(); + var reason = promise.defer(); + setTimeout(() => reason.fulfill('should not see me'), 100); + return p1.then(function() { + throw reason.promise; + }).then(assert.fail, function(e) { + assert.equal(reason.promise, e); + }); + }); + + it('(d)', function() { + var p1 = promise.fulfilled(); + var reason = {then: function() {}}; // A thenable like object. + return p1.then(function() { + throw reason; + }).then(assert.fail, function(e) { + assert.equal(reason, e); + }); }); }); }); diff --git a/node_modules/selenium-webdriver/test/lib/promise_flow_test.js b/node_modules/selenium-webdriver/test/lib/promise_flow_test.js index b42ac5209..407fd547e 100644 --- a/node_modules/selenium-webdriver/test/lib/promise_flow_test.js +++ b/node_modules/selenium-webdriver/test/lib/promise_flow_test.js @@ -22,7 +22,9 @@ const fail = assert.fail; const sinon = require('sinon'); const testutil = require('./testutil'); +const {TimeoutError} = require('../../lib/error'); const promise = require('../../lib/promise'); +const {enablePromiseManager} = require('../../lib/test/promise'); const NativePromise = Promise; @@ -33,2252 +35,2254 @@ const callbackPair = testutil.callbackPair; const throwStubError = testutil.throwStubError; describe('promise control flow', function() { - let flow, flowHistory, messages, uncaughtExceptions; - - beforeEach(function setUp() { - promise.LONG_STACK_TRACES = false; - flow = new promise.ControlFlow(); - promise.setDefaultFlow(flow); - messages = []; - flowHistory = []; - - uncaughtExceptions = []; - flow.on(promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION, - onUncaughtException); - }); - - afterEach(function tearDown() { - flow.removeAllListeners( - promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION); - assert.deepEqual([], uncaughtExceptions, - 'There were uncaught exceptions'); - flow.reset(); - promise.LONG_STACK_TRACES = false; - }); + enablePromiseManager(() => { + let flow, flowHistory, messages, uncaughtExceptions; - function onUncaughtException(e) { - uncaughtExceptions.push(e); - } - - function waitForAbort(opt_flow) { - var theFlow = opt_flow || flow; - theFlow.removeAllListeners( - promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION); - return new NativePromise(function(fulfill, reject) { - theFlow.once(promise.ControlFlow.EventType.IDLE, function() { - reject(Error('expected flow to report an unhandled error')); - }); - theFlow.once( - promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION, - fulfill); - }); - } - - function waitForIdle(opt_flow) { - var theFlow = opt_flow || flow; - return new NativePromise(function(fulfill, reject) { - theFlow.once(promise.ControlFlow.EventType.IDLE, fulfill); - theFlow.once( - promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION, reject); - }); - } + beforeEach(function setUp() { + promise.LONG_STACK_TRACES = false; + flow = new promise.ControlFlow(); + promise.setDefaultFlow(flow); + messages = []; + flowHistory = []; - function timeout(ms) { - return new NativePromise(function(fulfill) { - setTimeout(fulfill, ms); + uncaughtExceptions = []; + flow.on(promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION, + onUncaughtException); }); - } - - function schedule(msg, opt_return) { - return scheduleAction(msg, function() { - return opt_return; - }); - } - - /** - * @param {string} value The value to push. - * @param {promise.Promise=} opt_taskPromise Promise to return from - * the task. - * @return {!promise.Promise} The result. - */ - function schedulePush(value, opt_taskPromise) { - return scheduleAction(value, function() { - messages.push(value); - return opt_taskPromise; - }); - } - - /** - * @param {string} msg Debug message. - * @param {!Function} actionFn The function. - * @return {!promise.Promise} The function result. - */ - function scheduleAction(msg, actionFn) { - return promise.controlFlow().execute(function() { - flowHistory.push(msg); - return actionFn(); - }, msg); - } - - /** - * @param {!Function} condition The condition function. - * @param {number=} opt_timeout The timeout. - * @param {string=} opt_message Optional message. - * @return {!promise.Promise} The wait result. - */ - function scheduleWait(condition, opt_timeout, opt_message) { - var msg = opt_message || ''; - // It's not possible to hook into when the wait itself is scheduled, so - // we record each iteration of the wait loop. - var count = 0; - return promise.controlFlow().wait(function() { - flowHistory.push((count++) + ': ' + msg); - return condition(); - }, opt_timeout, msg); - } - - function asyncRun(fn, opt_self) { - NativePromise.resolve().then(() => fn.call(opt_self)); - } - - function assertFlowHistory(var_args) { - var expected = Array.prototype.slice.call(arguments, 0); - assert.deepEqual(expected, flowHistory); - } - - function assertMessages(var_args) { - var expected = Array.prototype.slice.call(arguments, 0); - assert.deepEqual(expected, messages); - } - - function assertingMessages(var_args) { - var args = Array.prototype.slice.call(arguments, 0); - return () => assertMessages.apply(null, args); - } - - function assertFlowIs(flow) { - assert.equal(flow, promise.controlFlow()); - } - - describe('testScheduling', function() { - it('aSimpleFunction', function() { - schedule('go'); - return waitForIdle().then(function() { - assertFlowHistory('go'); - }); - }); - - it('aSimpleFunctionWithANonPromiseReturnValue', function() { - schedule('go', 123).then(function(value) { - assert.equal(123, value); - }); - return waitForIdle().then(function() { - assertFlowHistory('go'); - }); + afterEach(function tearDown() { + flow.removeAllListeners( + promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION); + assert.deepEqual([], uncaughtExceptions, + 'There were uncaught exceptions'); + flow.reset(); + promise.LONG_STACK_TRACES = false; }); - it('aSimpleSequence', function() { - schedule('a'); - schedule('b'); - schedule('c'); - return waitForIdle().then(function() { - assertFlowHistory('a', 'b', 'c'); - }); - }); + function onUncaughtException(e) { + uncaughtExceptions.push(e); + } - it('invokesCallbacksWhenTaskIsDone', function() { - var d = new promise.Deferred(); - var called = false; - var done = schedule('a', d.promise).then(function(value) { - called = true; - assert.equal(123, value); - }); - return timeout(5).then(function() { - assert.ok(!called); - d.fulfill(123); - return done; - }). - then(function() { - assertFlowHistory('a'); + function defer() { + let d = {}; + let promise = new Promise((resolve, reject) => { + Object.assign(d, {resolve, reject}); }); - }); - - it('blocksUntilPromiseReturnedByTaskIsResolved', function() { - var done = promise.defer(); - schedulePush('a', done.promise); - schedulePush('b'); - setTimeout(function() { - done.fulfill(); - messages.push('c'); - }, 25); - return waitForIdle().then(assertingMessages('a', 'c', 'b')); - }); + d.promise = promise; + return d; + } - it('waitsForReturnedPromisesToResolve', function() { - var d1 = new promise.Deferred(); - var d2 = new promise.Deferred(); - - var callback = sinon.spy(); - schedule('a', d1.promise).then(callback); - - return timeout(5).then(function() { - assert(!callback.called); - d1.fulfill(d2.promise); - return timeout(5); - }).then(function() { - assert(!callback.called); - d2.fulfill('fluffy bunny'); - return waitForIdle(); - }).then(function() { - assert(callback.called); - assert.equal('fluffy bunny', callback.getCall(0).args[0]); - assertFlowHistory('a'); + function waitForAbort(opt_flow) { + var theFlow = opt_flow || flow; + theFlow.removeAllListeners( + promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION); + return new NativePromise(function(fulfill, reject) { + theFlow.once(promise.ControlFlow.EventType.IDLE, function() { + reject(Error('expected flow to report an unhandled error')); + }); + theFlow.once( + promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION, + fulfill); }); - }); + } - it('executesTasksInAFutureTurnAfterTheyAreScheduled', function() { + function waitForIdle(opt_flow) { + var theFlow = opt_flow || flow; + return new NativePromise(function(fulfill, reject) { + theFlow.once(promise.ControlFlow.EventType.IDLE, fulfill); + theFlow.once( + promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION, reject); + }); + } + + function timeout(ms) { + return new NativePromise(function(fulfill) { + setTimeout(fulfill, ms); + }); + } + + + function schedule(msg, opt_return) { + return scheduleAction(msg, function() { + return opt_return; + }); + } + + /** + * @param {string} value The value to push. + * @param {promise.Promise=} opt_taskPromise Promise to return from + * the task. + * @return {!promise.Promise} The result. + */ + function schedulePush(value, opt_taskPromise) { + return scheduleAction(value, function() { + messages.push(value); + return opt_taskPromise; + }); + } + + /** + * @param {string} msg Debug message. + * @param {!Function} actionFn The function. + * @return {!promise.Promise} The function result. + */ + function scheduleAction(msg, actionFn) { + return promise.controlFlow().execute(function() { + flowHistory.push(msg); + return actionFn(); + }, msg); + } + + /** + * @param {!Function} condition The condition function. + * @param {number=} opt_timeout The timeout. + * @param {string=} opt_message Optional message. + * @return {!promise.Promise} The wait result. + */ + function scheduleWait(condition, opt_timeout, opt_message) { + var msg = opt_message || ''; + // It's not possible to hook into when the wait itself is scheduled, so + // we record each iteration of the wait loop. var count = 0; - function incr() { count++; } - - scheduleAction('', incr); - assert.equal(0, count); - return waitForIdle().then(function() { - assert.equal(1, count); + return promise.controlFlow().wait(function() { + flowHistory.push((count++) + ': ' + msg); + return condition(); + }, opt_timeout, msg); + } + + function asyncRun(fn, opt_self) { + NativePromise.resolve().then(() => fn.call(opt_self)); + } + + function assertFlowHistory(var_args) { + var expected = Array.prototype.slice.call(arguments, 0); + assert.deepEqual(expected, flowHistory); + } + + function assertMessages(var_args) { + var expected = Array.prototype.slice.call(arguments, 0); + assert.deepEqual(expected, messages); + } + + function assertingMessages(var_args) { + var args = Array.prototype.slice.call(arguments, 0); + return () => assertMessages.apply(null, args); + } + + function assertFlowIs(flow) { + assert.equal(flow, promise.controlFlow()); + } + + describe('testScheduling', function() { + it('aSimpleFunction', function() { + schedule('go'); + return waitForIdle().then(function() { + assertFlowHistory('go'); + }); }); - }); - it('executesOneTaskPerTurnOfTheEventLoop', function() { - var order = []; - function go() { - order.push(order.length / 2); - asyncRun(function() { - order.push('-'); + it('aSimpleFunctionWithANonPromiseReturnValue', function() { + schedule('go', 123).then(function(value) { + assert.equal(123, value); }); - } - - scheduleAction('', go); - scheduleAction('', go); - return waitForIdle().then(function() { - assert.deepEqual([0, '-', 1, '-'], order); - }) - }); + return waitForIdle().then(function() { + assertFlowHistory('go'); + }); + }); - it('firstScheduledTaskIsWithinACallback', function() { - promise.fulfilled().then(function() { + it('aSimpleSequence', function() { schedule('a'); schedule('b'); schedule('c'); - }).then(function() { - assertFlowHistory('a', 'b', 'c'); + return waitForIdle().then(function() { + assertFlowHistory('a', 'b', 'c'); + }); }); - return waitForIdle(); - }); - it('newTasksAddedWhileWaitingOnTaskReturnedPromise1', function() { - scheduleAction('a', function() { - var d = promise.defer(); - setTimeout(function() { - schedule('c'); - d.fulfill(); - }, 10); - return d.promise; - }); - schedule('b'); - return waitForIdle().then(function() { - assertFlowHistory('a', 'b', 'c'); + it('invokesCallbacksWhenTaskIsDone', function() { + var d = new promise.Deferred(); + var called = false; + var done = schedule('a', d.promise).then(function(value) { + called = true; + assert.equal(123, value); + }); + return timeout(5).then(function() { + assert.ok(!called); + d.fulfill(123); + return done; + }). + then(function() { + assertFlowHistory('a'); + }); }); - }); - it('newTasksAddedWhileWaitingOnTaskReturnedPromise2', function() { - scheduleAction('a', function() { - var d = promise.defer(); + it('blocksUntilPromiseReturnedByTaskIsResolved', function() { + var done = promise.defer(); + schedulePush('a', done.promise); + schedulePush('b'); setTimeout(function() { - schedule('c'); - asyncRun(d.fulfill); - }, 10); - return d.promise; - }); - schedule('b'); - return waitForIdle().then(function() { - assertFlowHistory('a', 'c', 'b'); + done.fulfill(); + messages.push('c'); + }, 25); + return waitForIdle().then(assertingMessages('a', 'c', 'b')); }); - }); - }); - describe('testFraming', function() { - it('callbacksRunInANewFrame', function() { - schedule('a').then(function() { - schedule('c'); + it('waitsForReturnedPromisesToResolve', function() { + var d1 = new promise.Deferred(); + var d2 = new promise.Deferred(); + + var callback = sinon.spy(); + schedule('a', d1.promise).then(callback); + + return timeout(5).then(function() { + assert(!callback.called); + d1.fulfill(d2.promise); + return timeout(5); + }).then(function() { + assert(!callback.called); + d2.fulfill('fluffy bunny'); + return waitForIdle(); + }).then(function() { + assert(callback.called); + assert.equal('fluffy bunny', callback.getCall(0).args[0]); + assertFlowHistory('a'); + }); }); - schedule('b'); - return waitForIdle().then(function() { - assertFlowHistory('a', 'c', 'b'); + + it('executesTasksInAFutureTurnAfterTheyAreScheduled', function() { + var count = 0; + function incr() { count++; } + + scheduleAction('', incr); + assert.equal(0, count); + return waitForIdle().then(function() { + assert.equal(1, count); + }); }); - }); - it('lotsOfNesting', function() { - schedule('a').then(function() { - schedule('c').then(function() { - schedule('e').then(function() { - schedule('g'); + it('executesOneTaskPerTurnOfTheEventLoop', function() { + var order = []; + function go() { + order.push(order.length / 2); + asyncRun(function() { + order.push('-'); }); - schedule('f'); - }); - schedule('d'); + } + + scheduleAction('', go); + scheduleAction('', go); + return waitForIdle().then(function() { + assert.deepEqual([0, '-', 1, '-'], order); + }) }); - schedule('b'); - return waitForIdle().then(function() { - assertFlowHistory('a', 'c', 'e', 'g', 'f', 'd', 'b'); + it('firstScheduledTaskIsWithinACallback', function() { + promise.fulfilled().then(function() { + schedule('a'); + schedule('b'); + schedule('c'); + }).then(function() { + assertFlowHistory('a', 'b', 'c'); + }); + return waitForIdle(); }); - }); - it('callbackReturnsPromiseThatDependsOnATask_1', function() { - schedule('a').then(function() { + it('newTasksAddedWhileWaitingOnTaskReturnedPromise1', function() { + scheduleAction('a', function() { + var d = promise.defer(); + setTimeout(function() { + schedule('c'); + d.fulfill(); + }, 10); + return d.promise; + }); schedule('b'); - return promise.delayed(5).then(function() { - return schedule('c'); + return waitForIdle().then(function() { + assertFlowHistory('a', 'b', 'c'); }); }); - schedule('d'); - return waitForIdle().then(function() { - assertFlowHistory('a', 'b', 'c', 'd'); + it('newTasksAddedWhileWaitingOnTaskReturnedPromise2', function() { + scheduleAction('a', function() { + var d = promise.defer(); + setTimeout(function() { + schedule('c'); + asyncRun(d.fulfill); + }, 10); + return d.promise; + }); + schedule('b'); + return waitForIdle().then(function() { + assertFlowHistory('a', 'c', 'b'); + }); }); }); - it('callbackReturnsPromiseThatDependsOnATask_2', function() { - schedule('a').then(function() { + describe('testFraming', function() { + it('callbacksRunInANewFrame', function() { + schedule('a').then(function() { + schedule('c'); + }); schedule('b'); - return promise.delayed(5). - then(function() { return promise.delayed(5) }). - then(function() { return promise.delayed(5) }). - then(function() { return promise.delayed(5) }). - then(function() { return schedule('c'); }); - }); - schedule('d'); - - return waitForIdle().then(function() { - assertFlowHistory('a', 'b', 'c', 'd'); + return waitForIdle().then(function() { + assertFlowHistory('a', 'c', 'b'); + }); }); - }); - it('eachCallbackWaitsForAllScheduledTasksToComplete', function() { - schedule('a'). - then(function() { - schedule('b'); - schedule('c'); - }). - then(function() { - schedule('d'); + it('lotsOfNesting', function() { + schedule('a').then(function() { + schedule('c').then(function() { + schedule('e').then(function() { + schedule('g'); + }); + schedule('f'); }); - schedule('e'); + schedule('d'); + }); + schedule('b'); - return waitForIdle().then(function() { - assertFlowHistory('a', 'b', 'c', 'd', 'e'); + return waitForIdle().then(function() { + assertFlowHistory('a', 'c', 'e', 'g', 'f', 'd', 'b'); + }); }); - }); - it('eachCallbackWaitsForReturnTasksToComplete', function() { - schedule('a'). - then(function() { - schedule('b'); + it('callbackReturnsPromiseThatDependsOnATask_1', function() { + schedule('a').then(function() { + schedule('b'); + return promise.delayed(5).then(function() { return schedule('c'); - }). - then(function() { - schedule('d'); }); - schedule('e'); + }); + schedule('d'); - return waitForIdle().then(function() { - assertFlowHistory('a', 'b', 'c', 'd', 'e'); + return waitForIdle().then(function() { + assertFlowHistory('a', 'b', 'c', 'd'); + }); }); - }); - it('callbacksOnAResolvedPromiseInsertIntoTheCurrentFlow', function() { - promise.fulfilled().then(function() { - schedule('b'); - }); - schedule('a'); + it('callbackReturnsPromiseThatDependsOnATask_2', function() { + schedule('a').then(function() { + schedule('b'); + return promise.delayed(5). + then(function() { return promise.delayed(5) }). + then(function() { return promise.delayed(5) }). + then(function() { return promise.delayed(5) }). + then(function() { return schedule('c'); }); + }); + schedule('d'); - return waitForIdle().then(function() { - assertFlowHistory('b', 'a'); + return waitForIdle().then(function() { + assertFlowHistory('a', 'b', 'c', 'd'); + }); }); - }); - it('callbacksInterruptTheFlowWhenPromiseIsResolved', function() { - schedule('a').then(function() { - schedule('c'); - }); - schedule('b'); + it('eachCallbackWaitsForAllScheduledTasksToComplete', function() { + schedule('a'). + then(function() { + schedule('b'); + schedule('c'); + }). + then(function() { + schedule('d'); + }); + schedule('e'); - return waitForIdle().then(function() { - assertFlowHistory('a', 'c', 'b'); + return waitForIdle().then(function() { + assertFlowHistory('a', 'b', 'c', 'd', 'e'); + }); }); - }); - it('allCallbacksInAFrameAreScheduledWhenPromiseIsResolved', function() { - var a = schedule('a'); - a.then(function() { schedule('b'); }); - schedule('c'); - a.then(function() { schedule('d'); }); - schedule('e'); + it('eachCallbackWaitsForReturnTasksToComplete', function() { + schedule('a'). + then(function() { + schedule('b'); + return schedule('c'); + }). + then(function() { + schedule('d'); + }); + schedule('e'); - return waitForIdle().then(function() { - assertFlowHistory('a', 'b', 'c', 'd', 'e'); + return waitForIdle().then(function() { + assertFlowHistory('a', 'b', 'c', 'd', 'e'); + }); }); - }); - it('tasksScheduledInInActiveFrameDoNotGetPrecedence', function() { - var d = promise.fulfilled(); - schedule('a'); - schedule('b'); - d.then(function() { schedule('c'); }); + it('callbacksOnAResolvedPromiseInsertIntoTheCurrentFlow', function() { + promise.fulfilled().then(function() { + schedule('b'); + }); + schedule('a'); - return waitForIdle().then(function() { - assertFlowHistory('a', 'b', 'c'); + return waitForIdle().then(function() { + assertFlowHistory('b', 'a'); + }); }); - }); - it('tasksScheduledInAFrameGetPrecedence_1', function() { - var a = schedule('a'); - schedule('b').then(function() { - a.then(function() { + it('callbacksInterruptTheFlowWhenPromiseIsResolved', function() { + schedule('a').then(function() { schedule('c'); - schedule('d'); - }); - var e = schedule('e'); - a.then(function() { - schedule('f'); - e.then(function() { - schedule('g'); - }); - schedule('h'); }); - schedule('i'); - }); - schedule('j'); + schedule('b'); - return waitForIdle().then(function() { - assertFlowHistory('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'); + return waitForIdle().then(function() { + assertFlowHistory('a', 'c', 'b'); + }); }); - }); - }); - describe('testErrorHandling', function() { - it('thrownErrorsArePassedToTaskErrback', function() { - scheduleAction('function that throws', throwStubError). - then(fail, assertIsStubError); - return waitForIdle(); - }); + it('allCallbacksInAFrameAreScheduledWhenPromiseIsResolved', function() { + var a = schedule('a'); + a.then(function() { schedule('b'); }); + schedule('c'); + a.then(function() { schedule('d'); }); + schedule('e'); - it('thrownErrorsPropagateThroughPromiseChain', function() { - scheduleAction('function that throws', throwStubError). - then(fail). - then(fail, assertIsStubError); - return waitForIdle(); - }); + return waitForIdle().then(function() { + assertFlowHistory('a', 'b', 'c', 'd', 'e'); + }); + }); - it('catchesErrorsFromFailedTasksInAFrame', function() { - schedule('a').then(function() { + it('tasksScheduledInInActiveFrameDoNotGetPrecedence', function() { + var d = promise.fulfilled(); + schedule('a'); schedule('b'); - scheduleAction('function that throws', throwStubError); - }). - then(fail, assertIsStubError); - return waitForIdle(); - }); + d.then(function() { schedule('c'); }); - it('abortsIfOnlyTaskReturnsAnUnhandledRejection', function() { - scheduleAction('function that returns rejected promise', function() { - return promise.rejected(new StubError); + return waitForIdle().then(function() { + assertFlowHistory('a', 'b', 'c'); + }); }); - return waitForAbort().then(assertIsStubError); - }); - it('abortsIfThereIsAnUnhandledRejection', function() { - promise.rejected(new StubError); - schedule('this should not run'); - return waitForAbort(). - then(assertIsStubError). - then(function() { - assertFlowHistory(/* none */); - }); - }); - - it('abortsSequenceIfATaskFails', function() { - schedule('a'); - schedule('b'); - scheduleAction('c', throwStubError); - schedule('d'); // Should never execute. - - return waitForAbort(). - then(assertIsStubError). - then(function() { - assertFlowHistory('a', 'b', 'c'); + it('tasksScheduledInAFrameGetPrecedence_1', function() { + var a = schedule('a'); + schedule('b').then(function() { + a.then(function() { + schedule('c'); + schedule('d'); }); - }); - - it('abortsFromUnhandledFramedTaskFailures_1', function() { - schedule('outer task').then(function() { - scheduleAction('inner task', throwStubError); - }); - schedule('this should not run'); - return waitForAbort(). - then(assertIsStubError). - then(function() { - assertFlowHistory('outer task', 'inner task'); + var e = schedule('e'); + a.then(function() { + schedule('f'); + e.then(function() { + schedule('g'); + }); + schedule('h'); }); - }); + schedule('i'); + }); + schedule('j'); - it('abortsFromUnhandledFramedTaskFailures_2', function() { - schedule('a').then(function() { - schedule('b').then(function() { - scheduleAction('c', throwStubError); - // This should not execute. - schedule('d'); + return waitForIdle().then(function() { + assertFlowHistory('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'); }); }); - - return waitForAbort(). - then(assertIsStubError). - then(function() { - assertFlowHistory('a', 'b', 'c'); - }); }); - it('abortsWhenErrorBubblesUpFromFullyResolvingAnObject', function() { - var callback = sinon.spy(); + describe('testErrorHandling', function() { + it('thrownErrorsArePassedToTaskErrback', function() { + scheduleAction('function that throws', throwStubError). + then(fail, assertIsStubError); + return waitForIdle(); + }); - scheduleAction('', function() { - var obj = {'foo': promise.rejected(new StubError)}; - return promise.fullyResolved(obj).then(callback); + it('thrownErrorsPropagateThroughPromiseChain', function() { + scheduleAction('function that throws', throwStubError). + then(fail). + then(fail, assertIsStubError); + return waitForIdle(); }); - return waitForAbort(). - then(assertIsStubError). - then(() => assert(!callback.called)); - }); + it('catchesErrorsFromFailedTasksInAFrame', function() { + schedule('a').then(function() { + schedule('b'); + scheduleAction('function that throws', throwStubError); + }). + then(fail, assertIsStubError); + return waitForIdle(); + }); - it('abortsWhenErrorBubblesUpFromFullyResolvingAnObject_withCallback', function() { - var callback1 = sinon.spy(); - var callback2 = sinon.spy(); + it('abortsIfOnlyTaskReturnsAnUnhandledRejection', function() { + scheduleAction('function that returns rejected promise', function() { + return promise.rejected(new StubError); + }); + return waitForAbort().then(assertIsStubError); + }); - scheduleAction('', function() { - var obj = {'foo': promise.rejected(new StubError)}; - return promise.fullyResolved(obj).then(callback1); - }).then(callback2); + it('abortsIfThereIsAnUnhandledRejection', function() { + promise.rejected(new StubError); + schedule('this should not run'); + return waitForAbort(). + then(assertIsStubError). + then(function() { + assertFlowHistory(/* none */); + }); + }); - return waitForAbort(). - then(assertIsStubError). - then(() => assert(!callback1.called)). - then(() => assert(!callback2.called)); - }); + it('abortsSequenceIfATaskFails', function() { + schedule('a'); + schedule('b'); + scheduleAction('c', throwStubError); + schedule('d'); // Should never execute. - it('canCatchErrorsFromNestedTasks', function() { - var errback = sinon.spy(); - schedule('a'). - then(function() { - return scheduleAction('b', throwStubError); - }). - catch(errback); - return waitForIdle().then(function() { - assert(errback.called); - assertIsStubError(errback.getCall(0).args[0]); + return waitForAbort(). + then(assertIsStubError). + then(function() { + assertFlowHistory('a', 'b', 'c'); + }); }); - }); - it('nestedCommandFailuresCanBeCaughtAndSuppressed', function() { - var errback = sinon.spy(); - schedule('a').then(function() { - return schedule('b').then(function() { - return schedule('c').then(function() { - throw new StubError; - }); + it('abortsFromUnhandledFramedTaskFailures_1', function() { + schedule('outer task').then(function() { + scheduleAction('inner task', throwStubError); }); - }).catch(errback); - schedule('d'); - return waitForIdle(). - then(function() { - assert(errback.called); - assertIsStubError(errback.getCall(0).args[0]); - assertFlowHistory('a', 'b', 'c', 'd'); - }); - }); - - it('aTaskWithAnUnhandledPromiseRejection', function() { - schedule('a'); - scheduleAction('sub-tasks', function() { - promise.rejected(new StubError); + schedule('this should not run'); + return waitForAbort(). + then(assertIsStubError). + then(function() { + assertFlowHistory('outer task', 'inner task'); + }); }); - schedule('should never run'); - return waitForAbort(). - then(assertIsStubError). - then(function() { - assertFlowHistory('a', 'sub-tasks'); + it('abortsFromUnhandledFramedTaskFailures_2', function() { + schedule('a').then(function() { + schedule('b').then(function() { + scheduleAction('c', throwStubError); + // This should not execute. + schedule('d'); }); - }); + }); - it('aTaskThatReutrnsARejectedPromise', function() { - schedule('a'); - scheduleAction('sub-tasks', function() { - return promise.rejected(new StubError); + return waitForAbort(). + then(assertIsStubError). + then(function() { + assertFlowHistory('a', 'b', 'c'); + }); }); - schedule('should never run'); - - return waitForAbort(). - then(assertIsStubError). - then(function() { - assertFlowHistory('a', 'sub-tasks'); - }); - }); - it('discardsSubtasksIfTaskThrows', function() { - var pair = callbackPair(null, assertIsStubError); - scheduleAction('a', function() { - schedule('b'); - schedule('c'); - throwStubError(); - }).then(pair.callback, pair.errback); - schedule('d'); + it('abortsWhenErrorBubblesUpFromFullyResolvingAnObject', function() { + var callback = sinon.spy(); - return waitForIdle(). - then(pair.assertErrback). - then(function() { - assertFlowHistory('a', 'd'); - }); - }); + scheduleAction('', function() { + var obj = {'foo': promise.rejected(new StubError)}; + return promise.fullyResolved(obj).then(callback); + }); - it('discardsRemainingSubtasksIfASubtaskFails', function() { - var pair = callbackPair(null, assertIsStubError); - scheduleAction('a', function() { - schedule('b'); - scheduleAction('c', throwStubError); - schedule('d'); - }).then(pair.callback, pair.errback); - schedule('e'); + return waitForAbort(). + then(assertIsStubError). + then(() => assert(!callback.called)); + }); - return waitForIdle(). - then(pair.assertErrback). - then(function() { - assertFlowHistory('a', 'b', 'c', 'e'); - }); - }); - }); + it('abortsWhenErrorBubblesUpFromFullyResolvingAnObject_withCallback', function() { + var callback1 = sinon.spy(); + var callback2 = sinon.spy(); - describe('testTryModelingFinally', function() { - it('happyPath', function() { - /* Model: - try { - doFoo(); - doBar(); - } finally { - doBaz(); - } - */ - schedulePush('foo'). - then(() => schedulePush('bar')). - finally(() => schedulePush('baz')); - return waitForIdle().then(assertingMessages('foo', 'bar', 'baz')); - }); + scheduleAction('', function() { + var obj = {'foo': promise.rejected(new StubError)}; + return promise.fullyResolved(obj).then(callback1); + }).then(callback2); - it('firstTryFails', function() { - /* Model: - try { - doFoo(); - doBar(); - } finally { - doBaz(); - } - */ - - scheduleAction('doFoo and throw', function() { - messages.push('foo'); - throw new StubError; - }). - then(function() { schedulePush('bar'); }). - finally(function() { schedulePush('baz'); }); + return waitForAbort(). + then(assertIsStubError). + then(() => assert(!callback1.called)). + then(() => assert(!callback2.called)); + }); - return waitForAbort(). - then(assertIsStubError). - then(assertingMessages('foo', 'baz')); - }); + it('canCatchErrorsFromNestedTasks', function() { + var errback = sinon.spy(); + schedule('a'). + then(function() { + return scheduleAction('b', throwStubError); + }). + catch(errback); + return waitForIdle().then(function() { + assert(errback.called); + assertIsStubError(errback.getCall(0).args[0]); + }); + }); - it('secondTryFails', function() { - /* Model: - try { - doFoo(); - doBar(); - } finally { - doBaz(); - } - */ - - schedulePush('foo'). - then(function() { - return scheduleAction('doBar and throw', function() { - messages.push('bar'); + it('nestedCommandFailuresCanBeCaughtAndSuppressed', function() { + var errback = sinon.spy(); + schedule('a').then(function() { + return schedule('b').then(function() { + return schedule('c').then(function() { throw new StubError; }); - }). - finally(function() { - return schedulePush('baz'); }); - return waitForAbort(). - then(assertIsStubError). - then(assertingMessages('foo', 'bar', 'baz')); - }); - }); - - describe('testTaskCallbacksInterruptFlow', function() { - it('(base case)', function() { - schedule('a').then(function() { - schedule('b'); - }); - schedule('c'); - return waitForIdle().then(function() { - assertFlowHistory('a', 'b', 'c'); + }).catch(errback); + schedule('d'); + return waitForIdle(). + then(function() { + assert(errback.called); + assertIsStubError(errback.getCall(0).args[0]); + assertFlowHistory('a', 'b', 'c', 'd'); + }); }); - }); - it('taskDependsOnImmediatelyFulfilledPromise', function() { - scheduleAction('a', function() { - return promise.fulfilled(); - }).then(function() { - schedule('b'); - }); - schedule('c'); - return waitForIdle().then(function() { - assertFlowHistory('a', 'b', 'c'); - }); - }); + it('aTaskWithAnUnhandledPromiseRejection', function() { + schedule('a'); + scheduleAction('sub-tasks', function() { + promise.rejected(new StubError); + }); + schedule('should never run'); - it('taskDependsOnPreviouslyFulfilledPromise', function() { - var aPromise = promise.fulfilled(123); - scheduleAction('a', function() { - return aPromise; - }).then(function(value) { - assert.equal(123, value); - schedule('b'); - }); - schedule('c'); - return waitForIdle().then(function() { - assertFlowHistory('a', 'b', 'c'); + return waitForAbort(). + then(assertIsStubError). + then(function() { + assertFlowHistory('a', 'sub-tasks'); + }); }); - }); - it('taskDependsOnAsyncPromise', function() { - scheduleAction('a', function() { - return promise.delayed(25); - }).then(function() { - schedule('b'); - }); - schedule('c'); - return waitForIdle().then(function() { - assertFlowHistory('a', 'b', 'c'); - }); - }); + it('aTaskThatReutrnsARejectedPromise', function() { + schedule('a'); + scheduleAction('sub-tasks', function() { + return promise.rejected(new StubError); + }); + schedule('should never run'); - it('promiseChainedToTaskInterruptFlow', function() { - schedule('a').then(function() { - return promise.fulfilled(); - }).then(function() { - return promise.fulfilled(); - }).then(function() { - schedule('b'); - }); - schedule('c'); - return waitForIdle().then(function() { - assertFlowHistory('a', 'b', 'c'); + return waitForAbort(). + then(assertIsStubError). + then(function() { + assertFlowHistory('a', 'sub-tasks'); + }); }); - }); - it('nestedTaskCallbacksInterruptFlowWhenResolved', function() { - schedule('a').then(function() { - schedule('b').then(function() { + it('discardsSubtasksIfTaskThrows', function() { + var pair = callbackPair(null, assertIsStubError); + scheduleAction('a', function() { + schedule('b'); schedule('c'); - }); - }); - schedule('d'); - return waitForIdle().then(function() { - assertFlowHistory('a', 'b', 'c', 'd'); + throwStubError(); + }).then(pair.callback, pair.errback); + schedule('d'); + + return waitForIdle(). + then(pair.assertErrback). + then(function() { + assertFlowHistory('a', 'd'); + }); }); - }); - }); - describe('testDelayedNesting', function() { + it('discardsRemainingSubtasksIfASubtaskFails', function() { + var pair = callbackPair(null, assertIsStubError); + scheduleAction('a', function() { + schedule('b'); + scheduleAction('c', throwStubError); + schedule('d'); + }).then(pair.callback, pair.errback); + schedule('e'); - it('1', function() { - var a = schedule('a'); - schedule('b').then(function() { - a.then(function() { schedule('c'); }); - schedule('d'); + return waitForIdle(). + then(pair.assertErrback). + then(function() { + assertFlowHistory('a', 'b', 'c', 'e'); + }); }); - schedule('e'); + }); - return waitForIdle().then(function() { - assertFlowHistory('a', 'b', 'c', 'd', 'e'); + describe('testTryModelingFinally', function() { + it('happyPath', function() { + /* Model: + try { + doFoo(); + doBar(); + } finally { + doBaz(); + } + */ + schedulePush('foo'). + then(() => schedulePush('bar')). + finally(() => schedulePush('baz')); + return waitForIdle().then(assertingMessages('foo', 'bar', 'baz')); + }); + + it('firstTryFails', function() { + /* Model: + try { + doFoo(); + doBar(); + } finally { + doBaz(); + } + */ + + scheduleAction('doFoo and throw', function() { + messages.push('foo'); + throw new StubError; + }). + then(function() { schedulePush('bar'); }). + finally(function() { schedulePush('baz'); }); + + return waitForAbort(). + then(assertIsStubError). + then(assertingMessages('foo', 'baz')); + }); + + it('secondTryFails', function() { + /* Model: + try { + doFoo(); + doBar(); + } finally { + doBaz(); + } + */ + + schedulePush('foo'). + then(function() { + return scheduleAction('doBar and throw', function() { + messages.push('bar'); + throw new StubError; + }); + }). + finally(function() { + return schedulePush('baz'); + }); + return waitForAbort(). + then(assertIsStubError). + then(assertingMessages('foo', 'bar', 'baz')); }); }); - it('2', function() { - var a = schedule('a'); - schedule('b').then(function() { - a.then(function() { schedule('c'); }); - schedule('d'); - a.then(function() { schedule('e'); }); + describe('testTaskCallbacksInterruptFlow', function() { + it('(base case)', function() { + schedule('a').then(function() { + schedule('b'); + }); + schedule('c'); + return waitForIdle().then(function() { + assertFlowHistory('a', 'b', 'c'); + }); }); - schedule('f'); - return waitForIdle().then(function() { - assertFlowHistory('a', 'b', 'c', 'd', 'e', 'f'); + it('taskDependsOnImmediatelyFulfilledPromise', function() { + scheduleAction('a', function() { + return promise.fulfilled(); + }).then(function() { + schedule('b'); + }); + schedule('c'); + return waitForIdle().then(function() { + assertFlowHistory('a', 'b', 'c'); + }); }); - }); - it('3', function() { - var a = schedule('a'); - schedule('b').then(function() { - a.then(function() { schedule('c'); }); - a.then(function() { schedule('d'); }); + it('taskDependsOnPreviouslyFulfilledPromise', function() { + var aPromise = promise.fulfilled(123); + scheduleAction('a', function() { + return aPromise; + }).then(function(value) { + assert.equal(123, value); + schedule('b'); + }); + schedule('c'); + return waitForIdle().then(function() { + assertFlowHistory('a', 'b', 'c'); + }); }); - schedule('e'); - return waitForIdle().then(function() { - assertFlowHistory('a', 'b', 'c', 'd', 'e'); + it('taskDependsOnAsyncPromise', function() { + scheduleAction('a', function() { + return promise.delayed(25); + }).then(function() { + schedule('b'); + }); + schedule('c'); + return waitForIdle().then(function() { + assertFlowHistory('a', 'b', 'c'); + }); }); - }); - it('4', function() { - var a = schedule('a'); - schedule('b').then(function() { - a.then(function() { schedule('c'); }).then(function() { - schedule('d'); + it('promiseChainedToTaskInterruptFlow', function() { + schedule('a').then(function() { + return promise.fulfilled(); + }).then(function() { + return promise.fulfilled(); + }).then(function() { + schedule('b'); + }); + schedule('c'); + return waitForIdle().then(function() { + assertFlowHistory('a', 'b', 'c'); }); - a.then(function() { schedule('e'); }); }); - schedule('f'); - return waitForIdle().then(function() { - assertFlowHistory('a', 'b', 'c', 'd', 'e', 'f'); + it('nestedTaskCallbacksInterruptFlowWhenResolved', function() { + schedule('a').then(function() { + schedule('b').then(function() { + schedule('c'); + }); + }); + schedule('d'); + return waitForIdle().then(function() { + assertFlowHistory('a', 'b', 'c', 'd'); + }); }); }); - it('5', function() { - var a = schedule('a'); - schedule('b').then(function() { - var c; - a.then(function() { c = schedule('c'); }).then(function() { + describe('testDelayedNesting', function() { + + it('1', function() { + var a = schedule('a'); + schedule('b').then(function() { + a.then(function() { schedule('c'); }); schedule('d'); - a.then(function() { schedule('e'); }); - c.then(function() { schedule('f'); }); - schedule('g'); }); - a.then(function() { schedule('h'); }); - }); - schedule('i'); - - return waitForIdle().then(function() { - assertFlowHistory('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i'); - }); - }); - }); + schedule('e'); - describe('testWaiting', function() { - it('onAConditionThatIsAlwaysTrue', function() { - scheduleWait(function() { return true;}, 0, 'waiting on true'); - return waitForIdle().then(function() { - assertFlowHistory('0: waiting on true'); + return waitForIdle().then(function() { + assertFlowHistory('a', 'b', 'c', 'd', 'e'); + }); }); - }); - it('aSimpleCountingCondition', function() { - var count = 0; - scheduleWait(function() { - return ++count == 3; - }, 100, 'counting to 3'); + it('2', function() { + var a = schedule('a'); + schedule('b').then(function() { + a.then(function() { schedule('c'); }); + schedule('d'); + a.then(function() { schedule('e'); }); + }); + schedule('f'); - return waitForIdle().then(function() { - assert.equal(3, count); + return waitForIdle().then(function() { + assertFlowHistory('a', 'b', 'c', 'd', 'e', 'f'); + }); }); - }); - - it('aConditionThatReturnsAPromise', function() { - var d = new promise.Deferred(); - var count = 0; - scheduleWait(function() { - count += 1; - return d.promise; - }, 0, 'waiting for promise'); + it('3', function() { + var a = schedule('a'); + schedule('b').then(function() { + a.then(function() { schedule('c'); }); + a.then(function() { schedule('d'); }); + }); + schedule('e'); - return timeout(50).then(function() { - assert.equal(1, count); - d.fulfill(123); - return waitForIdle(); + return waitForIdle().then(function() { + assertFlowHistory('a', 'b', 'c', 'd', 'e'); + }); }); - }); - it('aConditionThatReturnsAPromise_2', function() { - var count = 0; - scheduleWait(function() { - return promise.fulfilled(++count == 3); - }, 100, 'waiting for promise'); + it('4', function() { + var a = schedule('a'); + schedule('b').then(function() { + a.then(function() { schedule('c'); }).then(function() { + schedule('d'); + }); + a.then(function() { schedule('e'); }); + }); + schedule('f'); - return waitForIdle().then(function() { - assert.equal(3, count); + return waitForIdle().then(function() { + assertFlowHistory('a', 'b', 'c', 'd', 'e', 'f'); + }); }); - }); - it('aConditionThatReturnsATaskResult', function() { - var count = 0; - scheduleWait(function() { - return scheduleAction('increment count', function() { - return ++count == 3; + it('5', function() { + var a = schedule('a'); + schedule('b').then(function() { + var c; + a.then(function() { c = schedule('c'); }).then(function() { + schedule('d'); + a.then(function() { schedule('e'); }); + c.then(function() { schedule('f'); }); + schedule('g'); + }); + a.then(function() { schedule('h'); }); }); - }, 100, 'counting to 3'); - schedule('post wait'); + schedule('i'); - return waitForIdle().then(function() { - assert.equal(3, count); - assertFlowHistory( - '0: counting to 3', 'increment count', - '1: counting to 3', 'increment count', - '2: counting to 3', 'increment count', - 'post wait'); + return waitForIdle().then(function() { + assertFlowHistory('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i'); + }); }); }); - it('conditionContainsASubtask', function() { - var count = 0; - scheduleWait(function() { - schedule('sub task'); - return ++count == 3; - }, 100, 'counting to 3'); - schedule('post wait'); - - return waitForIdle().then(function() { - assert.equal(3, count); - assertFlowHistory( - '0: counting to 3', 'sub task', - '1: counting to 3', 'sub task', - '2: counting to 3', 'sub task', - 'post wait'); + describe('testWaiting', function() { + it('onAConditionThatIsAlwaysTrue', function() { + scheduleWait(function() { return true;}, 0, 'waiting on true'); + return waitForIdle().then(function() { + assertFlowHistory('0: waiting on true'); + }); }); - }); - it('cancelsWaitIfScheduledTaskFails', function() { - var pair = callbackPair(null, assertIsStubError); - scheduleWait(function() { - scheduleAction('boom', throwStubError); - schedule('this should not run'); - return true; - }, 100, 'waiting to go boom').then(pair.callback, pair.errback); - schedule('post wait'); + it('aSimpleCountingCondition', function() { + var count = 0; + scheduleWait(function() { + return ++count == 3; + }, 100, 'counting to 3'); - return waitForIdle(). - then(pair.assertErrback). - then(function() { - assertFlowHistory( - '0: waiting to go boom', 'boom', - 'post wait'); - }); - }); + return waitForIdle().then(function() { + assert.equal(3, count); + }); + }); - it('failsIfConditionThrows', function() { - var callbacks = callbackPair(null, assertIsStubError); - scheduleWait(throwStubError, 0, 'goes boom'). - then(callbacks.callback, callbacks.errback); - schedule('post wait'); + it('aConditionThatReturnsAPromise', function() { + var d = new promise.Deferred(); + var count = 0; - return waitForIdle(). - then(callbacks.assertErrback). - then(function() { - assertFlowHistory('0: goes boom', 'post wait'); - }); - }); + scheduleWait(function() { + count += 1; + return d.promise; + }, 0, 'waiting for promise'); - it('failsIfConditionReturnsARejectedPromise', function() { - var callbacks = callbackPair(null, assertIsStubError); - scheduleWait(function() { - return promise.rejected(new StubError); - }, 0, 'goes boom').then(callbacks.callback, callbacks.errback); - schedule('post wait'); + return timeout(50).then(function() { + assert.equal(1, count); + d.fulfill(123); + return waitForIdle(); + }); + }); - return waitForIdle(). - then(callbacks.assertErrback). - then(function() { - assertFlowHistory('0: goes boom', 'post wait'); - }); - }); + it('aConditionThatReturnsAPromise_2', function() { + var count = 0; + scheduleWait(function() { + return promise.fulfilled(++count == 3); + }, 100, 'waiting for promise'); - it('failsIfConditionHasUnhandledRejection', function() { - var callbacks = callbackPair(null, assertIsStubError); - scheduleWait(function() { - promise.controlFlow().execute(throwStubError); - }, 0, 'goes boom').then(callbacks.callback, callbacks.errback); - schedule('post wait'); + return waitForIdle().then(function() { + assert.equal(3, count); + }); + }); - return waitForIdle(). - then(callbacks.assertErrback). - then(function() { - assertFlowHistory('0: goes boom', 'post wait'); + it('aConditionThatReturnsATaskResult', function() { + var count = 0; + scheduleWait(function() { + return scheduleAction('increment count', function() { + return ++count == 3; }); - }); + }, 100, 'counting to 3'); + schedule('post wait'); + + return waitForIdle().then(function() { + assert.equal(3, count); + assertFlowHistory( + '0: counting to 3', 'increment count', + '1: counting to 3', 'increment count', + '2: counting to 3', 'increment count', + 'post wait'); + }); + }); - it('failsIfConditionHasAFailedSubtask', function() { - var callbacks = callbackPair(null, assertIsStubError); - var count = 0; - scheduleWait(function() { - scheduleAction('maybe throw', function() { - if (++count == 2) { - throw new StubError; - } + it('conditionContainsASubtask', function() { + var count = 0; + scheduleWait(function() { + schedule('sub task'); + return ++count == 3; + }, 100, 'counting to 3'); + schedule('post wait'); + + return waitForIdle().then(function() { + assert.equal(3, count); + assertFlowHistory( + '0: counting to 3', 'sub task', + '1: counting to 3', 'sub task', + '2: counting to 3', 'sub task', + 'post wait'); }); - }, 100, 'waiting').then(callbacks.callback, callbacks.errback); - schedule('post wait'); + }); - return waitForIdle().then(function() { - assert.equal(2, count); - assertFlowHistory( - '0: waiting', 'maybe throw', - '1: waiting', 'maybe throw', - 'post wait'); + it('cancelsWaitIfScheduledTaskFails', function() { + var pair = callbackPair(null, assertIsStubError); + scheduleWait(function() { + scheduleAction('boom', throwStubError); + schedule('this should not run'); + return true; + }, 100, 'waiting to go boom').then(pair.callback, pair.errback); + schedule('post wait'); + + return waitForIdle(). + then(pair.assertErrback). + then(function() { + assertFlowHistory( + '0: waiting to go boom', 'boom', + 'post wait'); + }); }); - }); - it('pollingLoopWaitsForAllScheduledTasksInCondition', function() { - var count = 0; - scheduleWait(function() { - scheduleAction('increment count', function() { ++count; }); - return count >= 3; - }, 100, 'counting to 3'); - schedule('post wait'); + it('failsIfConditionThrows', function() { + var callbacks = callbackPair(null, assertIsStubError); + scheduleWait(throwStubError, 0, 'goes boom'). + then(callbacks.callback, callbacks.errback); + schedule('post wait'); - return waitForIdle().then(function() { - assert.equal(4, count); - assertFlowHistory( - '0: counting to 3', 'increment count', - '1: counting to 3', 'increment count', - '2: counting to 3', 'increment count', - '3: counting to 3', 'increment count', - 'post wait'); + return waitForIdle(). + then(callbacks.assertErrback). + then(function() { + assertFlowHistory('0: goes boom', 'post wait'); + }); }); - }); - it('waitsForeverOnAZeroTimeout', function() { - var done = false; - setTimeout(function() { - done = true; - }, 150); - var waitResult = scheduleWait(function() { - return done; - }, 0); + it('failsIfConditionReturnsARejectedPromise', function() { + var callbacks = callbackPair(null, assertIsStubError); + scheduleWait(function() { + return promise.rejected(new StubError); + }, 0, 'goes boom').then(callbacks.callback, callbacks.errback); + schedule('post wait'); - return timeout(75).then(function() { - assert.ok(!done); - return timeout(100); - }).then(function() { - assert.ok(done); - return waitResult; + return waitForIdle(). + then(callbacks.assertErrback). + then(function() { + assertFlowHistory('0: goes boom', 'post wait'); + }); }); - }); - it('waitsForeverIfTimeoutOmitted', function() { - var done = false; - setTimeout(function() { - done = true; - }, 150); - var waitResult = scheduleWait(function() { - return done; + it('failsIfConditionHasUnhandledRejection', function() { + var callbacks = callbackPair(null, assertIsStubError); + scheduleWait(function() { + promise.controlFlow().execute(throwStubError); + }, 0, 'goes boom').then(callbacks.callback, callbacks.errback); + schedule('post wait'); + + return waitForIdle(). + then(callbacks.assertErrback). + then(function() { + assertFlowHistory('0: goes boom', 'post wait'); + }); }); - return timeout(75).then(function() { - assert.ok(!done); - return timeout(100); - }).then(function() { - assert.ok(done); - return waitResult; + it('failsIfConditionHasAFailedSubtask', function() { + var callbacks = callbackPair(null, assertIsStubError); + var count = 0; + scheduleWait(function() { + scheduleAction('maybe throw', function() { + if (++count == 2) { + throw new StubError; + } + }); + }, 100, 'waiting').then(callbacks.callback, callbacks.errback); + schedule('post wait'); + + return waitForIdle().then(function() { + assert.equal(2, count); + assertFlowHistory( + '0: waiting', 'maybe throw', + '1: waiting', 'maybe throw', + 'post wait'); + }); }); - }); - it('timesOut_nonZeroTimeout', function() { - var count = 0; - scheduleWait(function() { - count += 1; - var ms = count === 2 ? 65 : 5; - return promise.delayed(ms).then(function() { - return false; - }); - }, 60, 'counting to 3'); - return waitForAbort().then(function(e) { - switch (count) { - case 1: - assertFlowHistory('0: counting to 3'); - break; - case 2: - assertFlowHistory('0: counting to 3', '1: counting to 3'); - break; - default: - fail('unexpected polling count: ' + count); - } - assert.ok( - /^counting to 3\nWait timed out after \d+ms$/.test(e.message)); + it('pollingLoopWaitsForAllScheduledTasksInCondition', function() { + var count = 0; + scheduleWait(function() { + scheduleAction('increment count', function() { ++count; }); + return count >= 3; + }, 100, 'counting to 3'); + schedule('post wait'); + + return waitForIdle().then(function() { + assert.equal(4, count); + assertFlowHistory( + '0: counting to 3', 'increment count', + '1: counting to 3', 'increment count', + '2: counting to 3', 'increment count', + '3: counting to 3', 'increment count', + 'post wait'); + }); }); - }); - it('shouldFailIfConditionReturnsARejectedPromise', function() { - scheduleWait(function() { - return promise.rejected(new StubError); - }, 100, 'returns rejected promise on first pass'); - return waitForAbort().then(assertIsStubError); - }); + it('waitsForeverOnAZeroTimeout', function() { + var done = false; + setTimeout(function() { + done = true; + }, 150); + var waitResult = scheduleWait(function() { + return done; + }, 0); + + return timeout(75).then(function() { + assert.ok(!done); + return timeout(100); + }).then(function() { + assert.ok(done); + return waitResult; + }); + }); - it('scheduleWithIntermittentWaits', function() { - schedule('a'); - scheduleWait(function() { return true; }, 0, 'wait 1'); - schedule('b'); - scheduleWait(function() { return true; }, 0, 'wait 2'); - schedule('c'); - scheduleWait(function() { return true; }, 0, 'wait 3'); + it('waitsForeverIfTimeoutOmitted', function() { + var done = false; + setTimeout(function() { + done = true; + }, 150); + var waitResult = scheduleWait(function() { + return done; + }); - return waitForIdle().then(function() { - assertFlowHistory('a', '0: wait 1', 'b', '0: wait 2', 'c', '0: wait 3'); + return timeout(75).then(function() { + assert.ok(!done); + return timeout(100); + }).then(function() { + assert.ok(done); + return waitResult; + }); }); - }); - it('scheduleWithIntermittentAndNestedWaits', function() { - schedule('a'); - scheduleWait(function() { return true; }, 0, 'wait 1'). - then(function() { - schedule('d'); - scheduleWait(function() { return true; }, 0, 'wait 2'); - schedule('e'); + it('timesOut_nonZeroTimeout', function() { + var count = 0; + scheduleWait(function() { + count += 1; + var ms = count === 2 ? 65 : 5; + return promise.delayed(ms).then(function() { + return false; }); - schedule('b'); - scheduleWait(function() { return true; }, 0, 'wait 3'); - schedule('c'); - scheduleWait(function() { return true; }, 0, 'wait 4'); - - return waitForIdle().then(function() { - assertFlowHistory( - 'a', '0: wait 1', 'd', '0: wait 2', 'e', 'b', '0: wait 3', 'c', - '0: wait 4'); + }, 60, 'counting to 3'); + return waitForAbort().then(function(e) { + switch (count) { + case 1: + assertFlowHistory('0: counting to 3'); + break; + case 2: + assertFlowHistory('0: counting to 3', '1: counting to 3'); + break; + default: + fail('unexpected polling count: ' + count); + } + assert.ok(e instanceof TimeoutError, 'Unexpected error: ' + e); + assert.ok( + /^counting to 3\nWait timed out after \d+ms$/.test(e.message)); + }); }); - }); - it('requiresConditionToBeAPromiseOrFunction', function() { - assert.throws(function() { - flow.wait(1234, 0); + it('shouldFailIfConditionReturnsARejectedPromise', function() { + scheduleWait(function() { + return promise.rejected(new StubError); + }, 100, 'returns rejected promise on first pass'); + return waitForAbort().then(assertIsStubError); }); - flow.wait(function() { return true;}, 0); - flow.wait(promise.fulfilled(), 0); - return waitForIdle(); - }); - it('promiseThatDoesNotResolveBeforeTimeout', function() { - var d = promise.defer(); - flow.wait(d.promise, 5).then(fail, function(e) { - assert.ok( - /Timed out waiting for promise to resolve after \d+ms/ - .test(e.message), - 'unexpected error message: ' + e.message); + it('scheduleWithIntermittentWaits', function() { + schedule('a'); + scheduleWait(function() { return true; }, 0, 'wait 1'); + schedule('b'); + scheduleWait(function() { return true; }, 0, 'wait 2'); + schedule('c'); + scheduleWait(function() { return true; }, 0, 'wait 3'); + + return waitForIdle().then(function() { + assertFlowHistory('a', '0: wait 1', 'b', '0: wait 2', 'c', '0: wait 3'); + }); }); - return waitForIdle().then(function() { - assert.ok('Promise should not be cancelled', d.promise.isPending()); + + it('scheduleWithIntermittentAndNestedWaits', function() { + schedule('a'); + scheduleWait(function() { return true; }, 0, 'wait 1'). + then(function() { + schedule('d'); + scheduleWait(function() { return true; }, 0, 'wait 2'); + schedule('e'); + }); + schedule('b'); + scheduleWait(function() { return true; }, 0, 'wait 3'); + schedule('c'); + scheduleWait(function() { return true; }, 0, 'wait 4'); + + return waitForIdle().then(function() { + assertFlowHistory( + 'a', '0: wait 1', 'd', '0: wait 2', 'e', 'b', '0: wait 3', 'c', + '0: wait 4'); + }); }); - }); - it('unboundedWaitOnPromiseResolution', function() { - var messages = []; - var d = promise.defer(); - var waitResult = flow.wait(d.promise).then(function(value) { - messages.push('b'); - assert.equal(1234, value); + it('requiresConditionToBeAPromiseOrFunction', function() { + assert.throws(function() { + flow.wait(1234, 0); + }); + flow.wait(function() { return true;}, 0); + flow.wait(promise.fulfilled(), 0); + return waitForIdle(); }); - setTimeout(function() { - messages.push('a'); - }, 5); - - timeout(10).then(function() { - assert.deepEqual(['a'], messages); - assert.ok(waitResult.isPending()); - d.fulfill(1234); - return waitResult; - }).then(function() { - assert.deepEqual(['a', 'b'], messages); + + it('promiseThatDoesNotResolveBeforeTimeout', function() { + var d = promise.defer(); + flow.wait(d.promise, 5).then(fail, function(e) { + assert.ok(e instanceof TimeoutError, 'Unexpected error: ' + e); + assert.ok( + /Timed out waiting for promise to resolve after \d+ms/ + .test(e.message), + 'unexpected error message: ' + e.message); + }); + return waitForIdle(); }); - return waitForIdle(); - }); - }); + it('unboundedWaitOnPromiseResolution', function() { + var messages = []; + var d = promise.defer(); + var waitResult = flow.wait(d.promise).then(function(value) { + messages.push('b'); + assert.equal(1234, value); + }); + setTimeout(function() { + messages.push('a'); + }, 5); - describe('testSubtasks', function() { - it('(base case)', function() { - schedule('a'); - scheduleAction('sub-tasks', function() { - schedule('c'); - schedule('d'); - }); - schedule('b'); + timeout(10).then(function() { + assert.deepEqual(['a'], messages); + d.fulfill(1234); + return waitResult; + }).then(function() { + assert.deepEqual(['a', 'b'], messages); + }); - return waitForIdle().then(function() { - assertFlowHistory('a', 'sub-tasks', 'c', 'd', 'b'); + return waitForIdle(); }); }); - it('nesting', function() { - schedule('a'); - scheduleAction('sub-tasks', function() { - schedule('b'); - scheduleAction('sub-sub-tasks', function() { + describe('testSubtasks', function() { + it('(base case)', function() { + schedule('a'); + scheduleAction('sub-tasks', function() { schedule('c'); schedule('d'); }); - schedule('e'); - }); - schedule('f'); + schedule('b'); - return waitForIdle().then(function() { - assertFlowHistory( - 'a', 'sub-tasks', 'b', 'sub-sub-tasks', 'c', 'd', 'e', 'f'); + return waitForIdle().then(function() { + assertFlowHistory('a', 'sub-tasks', 'c', 'd', 'b'); + }); }); - }); - it('taskReturnsSubTaskResult_1', function() { - schedule('a'); - scheduleAction('sub-tasks', function() { - return schedule('c'); - }); - schedule('b'); + it('nesting', function() { + schedule('a'); + scheduleAction('sub-tasks', function() { + schedule('b'); + scheduleAction('sub-sub-tasks', function() { + schedule('c'); + schedule('d'); + }); + schedule('e'); + }); + schedule('f'); - return waitForIdle().then(function() { - assertFlowHistory('a', 'sub-tasks', 'c', 'b'); + return waitForIdle().then(function() { + assertFlowHistory( + 'a', 'sub-tasks', 'b', 'sub-sub-tasks', 'c', 'd', 'e', 'f'); + }); }); - }); - it('taskReturnsSubTaskResult_2', function() { - let pair = callbackPair((value) => assert.equal(123, value)); - schedule('a'); - schedule('sub-tasks', promise.fulfilled(123)).then(pair.callback); - schedule('b'); + it('taskReturnsSubTaskResult_1', function() { + schedule('a'); + scheduleAction('sub-tasks', function() { + return schedule('c'); + }); + schedule('b'); - return waitForIdle().then(function() { - assertFlowHistory('a', 'sub-tasks','b'); - pair.assertCallback(); + return waitForIdle().then(function() { + assertFlowHistory('a', 'sub-tasks', 'c', 'b'); + }); }); - }); - it('taskReturnsPromiseThatDependsOnSubtask_1', function() { - scheduleAction('a', function() { - return promise.delayed(10).then(function() { - schedule('b'); + it('taskReturnsSubTaskResult_2', function() { + let pair = callbackPair((value) => assert.equal(123, value)); + schedule('a'); + schedule('sub-tasks', promise.fulfilled(123)).then(pair.callback); + schedule('b'); + + return waitForIdle().then(function() { + assertFlowHistory('a', 'sub-tasks','b'); + pair.assertCallback(); }); }); - schedule('c'); - return waitForIdle().then(function() { - assertFlowHistory('a', 'b', 'c'); - }); - }); - it('taskReturnsPromiseThatDependsOnSubtask_2', function() { - scheduleAction('a', function() { - return promise.fulfilled().then(function() { - schedule('b'); + it('taskReturnsPromiseThatDependsOnSubtask_1', function() { + scheduleAction('a', function() { + return promise.delayed(10).then(function() { + schedule('b'); + }); + }); + schedule('c'); + return waitForIdle().then(function() { + assertFlowHistory('a', 'b', 'c'); }); }); - schedule('c'); - return waitForIdle().then(function() { - assertFlowHistory('a', 'b', 'c'); - }); - }); - it('taskReturnsPromiseThatDependsOnSubtask_3', function() { - scheduleAction('a', function() { - return promise.delayed(10).then(function() { - return schedule('b'); + it('taskReturnsPromiseThatDependsOnSubtask_2', function() { + scheduleAction('a', function() { + return promise.fulfilled().then(function() { + schedule('b'); + }); + }); + schedule('c'); + return waitForIdle().then(function() { + assertFlowHistory('a', 'b', 'c'); }); }); - schedule('c'); - return waitForIdle().then(function() { - assertFlowHistory('a', 'b', 'c'); - }); - }); - it('taskReturnsPromiseThatDependsOnSubtask_4', function() { - scheduleAction('a', function() { - return promise.delayed(5).then(function() { - return promise.delayed(5).then(function() { + it('taskReturnsPromiseThatDependsOnSubtask_3', function() { + scheduleAction('a', function() { + return promise.delayed(10).then(function() { return schedule('b'); }); }); + schedule('c'); + return waitForIdle().then(function() { + assertFlowHistory('a', 'b', 'c'); + }); }); - schedule('c'); - return waitForIdle().then(function() { - assertFlowHistory('a', 'b', 'c'); + + it('taskReturnsPromiseThatDependsOnSubtask_4', function() { + scheduleAction('a', function() { + return promise.delayed(5).then(function() { + return promise.delayed(5).then(function() { + return schedule('b'); + }); + }); + }); + schedule('c'); + return waitForIdle().then(function() { + assertFlowHistory('a', 'b', 'c'); + }); }); - }); - it('taskReturnsPromiseThatDependsOnSubtask_5', function() { - scheduleAction('a', function() { - return promise.delayed(5).then(function() { + it('taskReturnsPromiseThatDependsOnSubtask_5', function() { + scheduleAction('a', function() { return promise.delayed(5).then(function() { return promise.delayed(5).then(function() { return promise.delayed(5).then(function() { - return schedule('b'); + return promise.delayed(5).then(function() { + return schedule('b'); + }); }); }); }); }); + schedule('c'); + return waitForIdle().then(function() { + assertFlowHistory('a', 'b', 'c'); + }); }); - schedule('c'); - return waitForIdle().then(function() { - assertFlowHistory('a', 'b', 'c'); - }); - }); - it('taskReturnsPromiseThatDependsOnSubtask_6', function() { - scheduleAction('a', function() { - return promise.delayed(5). - then(function() { return promise.delayed(5) }). - then(function() { return promise.delayed(5) }). - then(function() { return promise.delayed(5) }). - then(function() { return schedule('b'); }); - }); - schedule('c'); - return waitForIdle().then(function() { - assertFlowHistory('a', 'b', 'c'); + it('taskReturnsPromiseThatDependsOnSubtask_6', function() { + scheduleAction('a', function() { + return promise.delayed(5). + then(function() { return promise.delayed(5) }). + then(function() { return promise.delayed(5) }). + then(function() { return promise.delayed(5) }). + then(function() { return schedule('b'); }); + }); + schedule('c'); + return waitForIdle().then(function() { + assertFlowHistory('a', 'b', 'c'); + }); }); - }); - it('subTaskFails_1', function() { - schedule('a'); - scheduleAction('sub-tasks', function() { - scheduleAction('sub-task that fails', throwStubError); + it('subTaskFails_1', function() { + schedule('a'); + scheduleAction('sub-tasks', function() { + scheduleAction('sub-task that fails', throwStubError); + }); + schedule('should never execute'); + + return waitForAbort(). + then(assertIsStubError). + then(function() { + assertFlowHistory('a', 'sub-tasks', 'sub-task that fails'); + }); }); - schedule('should never execute'); - return waitForAbort(). - then(assertIsStubError). - then(function() { - assertFlowHistory('a', 'sub-tasks', 'sub-task that fails'); - }); - }); + it('subTaskFails_2', function() { + schedule('a'); + scheduleAction('sub-tasks', function() { + return promise.rejected(new StubError); + }); + schedule('should never execute'); - it('subTaskFails_2', function() { - schedule('a'); - scheduleAction('sub-tasks', function() { - return promise.rejected(new StubError); + return waitForAbort(). + then(assertIsStubError). + then(function() { + assertFlowHistory('a', 'sub-tasks'); + }); }); - schedule('should never execute'); - return waitForAbort(). - then(assertIsStubError). - then(function() { - assertFlowHistory('a', 'sub-tasks'); - }); - }); - - it('subTaskFails_3', function() { - var callbacks = callbackPair(null, assertIsStubError); + it('subTaskFails_3', function() { + var callbacks = callbackPair(null, assertIsStubError); - schedule('a'); - scheduleAction('sub-tasks', function() { - return promise.rejected(new StubError); - }).then(callbacks.callback, callbacks.errback); - schedule('b'); + schedule('a'); + scheduleAction('sub-tasks', function() { + return promise.rejected(new StubError); + }).then(callbacks.callback, callbacks.errback); + schedule('b'); - return waitForIdle(). - then(function() { - assertFlowHistory('a', 'sub-tasks', 'b'); - callbacks.assertErrback(); - }); + return waitForIdle(). + then(function() { + assertFlowHistory('a', 'sub-tasks', 'b'); + callbacks.assertErrback(); + }); + }); }); - }); - describe('testEventLoopWaitsOnPendingPromiseRejections', function() { - it('oneRejection', function() { - var d = new promise.Deferred; - scheduleAction('one', function() { - return d.promise; + describe('testEventLoopWaitsOnPendingPromiseRejections', function() { + it('oneRejection', function() { + var d = new promise.Deferred; + scheduleAction('one', function() { + return d.promise; + }); + scheduleAction('two', function() {}); + + return timeout(50).then(function() { + assertFlowHistory('one'); + d.reject(new StubError); + return waitForAbort(); + }). + then(assertIsStubError). + then(function() { + assertFlowHistory('one'); + }); }); - scheduleAction('two', function() {}); - return timeout(50).then(function() { - assertFlowHistory('one'); - d.reject(new StubError); - return waitForAbort(); - }). - then(assertIsStubError). - then(function() { - assertFlowHistory('one'); + it('multipleRejections', function() { + var once = Error('once'); + var twice = Error('twice'); + + scheduleAction('one', function() { + promise.rejected(once); + promise.rejected(twice); + }); + var twoResult = scheduleAction('two', function() {}); + + flow.removeAllListeners( + promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION); + return new NativePromise(function(fulfill, reject) { + setTimeout(function() { + reject(Error('Should have reported the two errors by now')); + }, 50); + flow.on( + promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION, + fulfill); + }).then(function(e) { + assert.ok(e instanceof promise.MultipleUnhandledRejectionError, + 'Not a MultipleUnhandledRejectionError'); + let errors = Array.from(e.errors); + assert.deepEqual([once, twice], errors); + assertFlowHistory('one'); + }); }); }); - it('multipleRejections', function() { - var once = Error('once'); - var twice = Error('twice'); + describe('testCancelsPromiseReturnedByCallbackIfFrameFails', function() { + it('promiseCallback', function() { + var chainPair = callbackPair(null, assertIsStubError); + var deferredPair = callbackPair(null, function(e) { + assert.equal('CancellationError: StubError', e.toString(), + 'callback result should be cancelled'); + }); - scheduleAction('one', function() { - promise.rejected(once); - promise.rejected(twice); + var d = new promise.Deferred(); + d.promise.then(deferredPair.callback, deferredPair.errback); + + promise.fulfilled(). + then(function() { + scheduleAction('boom', throwStubError); + schedule('this should not run'); + return d.promise; + }). + then(chainPair.callback, chainPair.errback); + + return waitForIdle().then(function() { + assertFlowHistory('boom'); + chainPair.assertErrback('chain errback not invoked'); + deferredPair.assertErrback('deferred errback not invoked'); + }); }); - var twoResult = scheduleAction('two', function() {}); - flow.removeAllListeners( - promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION); - return new NativePromise(function(fulfill, reject) { - setTimeout(function() { - reject(Error('Should have reported the two errors by now')); - }, 50); - flow.on( - promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION, - fulfill); - }).then(function(e) { - assert.ok(e instanceof promise.MultipleUnhandledRejectionError, - 'Not a MultipleUnhandledRejectionError'); - let errors = Array.from(e.errors); - assert.deepEqual([once, twice], errors); - assertFlowHistory('one'); - assert.ok(!twoResult.isPending(), 'Did not cancel the second task'); - }); - }); - }); + it('taskCallback', function() { + var chainPair = callbackPair(null, assertIsStubError); + var deferredPair = callbackPair(null, function(e) { + assert.equal('CancellationError: StubError', e.toString(), + 'callback result should be cancelled'); + }); - describe('testCancelsPromiseReturnedByCallbackIfFrameFails', function() { - it('promiseCallback', function() { - var chainPair = callbackPair(null, assertIsStubError); - var deferredPair = callbackPair(null, function(e) { - assert.equal('CancellationError: StubError', e.toString(), - 'callback result should be cancelled'); + var d = new promise.Deferred(); + d.promise.then(deferredPair.callback, deferredPair.errback); + + schedule('a'). + then(function() { + scheduleAction('boom', throwStubError); + schedule('this should not run'); + return d.promise; + }). + then(chainPair.callback, chainPair.errback); + + return waitForIdle().then(function() { + assertFlowHistory('a', 'boom'); + chainPair.assertErrback('chain errback not invoked'); + deferredPair.assertErrback('deferred errback not invoked'); + }); }); + }); - var d = new promise.Deferred(); - d.promise.then(deferredPair.callback, deferredPair.errback); - - promise.fulfilled(). + it('testMaintainsOrderInCallbacksWhenATaskReturnsAPromise', function() { + schedule('__start__', promise.fulfilled()). then(function() { - scheduleAction('boom', throwStubError); - schedule('this should not run'); - return d.promise; + messages.push('a'); + schedulePush('b'); + messages.push('c'); }). - then(chainPair.callback, chainPair.errback); + then(function() { + messages.push('d'); + }); + schedulePush('e'); return waitForIdle().then(function() { - assertFlowHistory('boom'); - chainPair.assertErrback('chain errback not invoked'); - deferredPair.assertErrback('deferred errback not invoked'); + assertFlowHistory('__start__', 'b', 'e'); + assertMessages('a', 'c', 'b', 'd', 'e'); }); }); - it('taskCallback', function() { - var chainPair = callbackPair(null, assertIsStubError); - var deferredPair = callbackPair(null, function(e) { - assert.equal('CancellationError: StubError', e.toString(), - 'callback result should be cancelled'); - }); + it('testOwningFlowIsActivatedForExecutingTasks', function() { + var defaultFlow = promise.controlFlow(); + var order = []; - var d = new promise.Deferred(); - d.promise.then(deferredPair.callback, deferredPair.errback); + promise.createFlow(function(flow) { + assertFlowIs(flow); + order.push(0); - schedule('a'). - then(function() { - scheduleAction('boom', throwStubError); - schedule('this should not run'); - return d.promise; - }). - then(chainPair.callback, chainPair.errback); + defaultFlow.execute(function() { + assertFlowIs(defaultFlow); + order.push(1); + }); + }); return waitForIdle().then(function() { - assertFlowHistory('a', 'boom'); - chainPair.assertErrback('chain errback not invoked'); - deferredPair.assertErrback('deferred errback not invoked'); + assertFlowIs(defaultFlow); + assert.deepEqual([0, 1], order); }); }); - }); - it('testMaintainsOrderInCallbacksWhenATaskReturnsAPromise', function() { - schedule('__start__', promise.fulfilled()). - then(function() { - messages.push('a'); - schedulePush('b'); - messages.push('c'); - }). - then(function() { - messages.push('d'); + it('testCreateFlowReturnsPromisePairedWithCreatedFlow', function() { + return new NativePromise(function(fulfill, reject) { + var newFlow; + promise.createFlow(function(flow) { + newFlow = flow; + assertFlowIs(newFlow); + }).then(function() { + assertFlowIs(newFlow); + waitForIdle(newFlow).then(fulfill, reject); }); - schedulePush('e'); - - return waitForIdle().then(function() { - assertFlowHistory('__start__', 'b', 'e'); - assertMessages('a', 'c', 'b', 'd', 'e'); + }); }); - }); - it('testOwningFlowIsActivatedForExecutingTasks', function() { - var defaultFlow = promise.controlFlow(); - var order = []; - - promise.createFlow(function(flow) { - assertFlowIs(flow); - order.push(0); - - defaultFlow.execute(function() { + it('testDeferredFactoriesCreateForActiveFlow_defaultFlow', function() { + var e = Error(); + var defaultFlow = promise.controlFlow(); + promise.fulfilled().then(function() { assertFlowIs(defaultFlow); - order.push(1); }); - }); - - return waitForIdle().then(function() { - assertFlowIs(defaultFlow); - assert.deepEqual([0, 1], order); - }); - }); - - it('testCreateFlowReturnsPromisePairedWithCreatedFlow', function() { - return new NativePromise(function(fulfill, reject) { - var newFlow; - promise.createFlow(function(flow) { - newFlow = flow; - assertFlowIs(newFlow); - }).then(function() { - assertFlowIs(newFlow); - waitForIdle(newFlow).then(fulfill, reject); + promise.rejected(e).then(null, function(err) { + assert.equal(e, err); + assertFlowIs(defaultFlow); + }); + promise.defer().promise.then(function() { + assertFlowIs(defaultFlow); }); - }); - }); - it('testDeferredFactoriesCreateForActiveFlow_defaultFlow', function() { - var e = Error(); - var defaultFlow = promise.controlFlow(); - promise.fulfilled().then(function() { - assertFlowIs(defaultFlow); - }); - promise.rejected(e).then(null, function(err) { - assert.equal(e, err); - assertFlowIs(defaultFlow); - }); - promise.defer().promise.then(function() { - assertFlowIs(defaultFlow); + return waitForIdle(); }); - return waitForIdle(); - }); + it('testDeferredFactoriesCreateForActiveFlow_newFlow', function() { + var e = Error(); + var newFlow = new promise.ControlFlow; + newFlow.execute(function() { + promise.fulfilled().then(function() { + assertFlowIs(newFlow); + }); - it('testDeferredFactoriesCreateForActiveFlow_newFlow', function() { - var e = Error(); - var newFlow = new promise.ControlFlow; - newFlow.execute(function() { - promise.fulfilled().then(function() { - assertFlowIs(newFlow); - }); + promise.rejected(e).then(null, function(err) { + assert.equal(e, err); + assertFlowIs(newFlow); + }); - promise.rejected(e).then(null, function(err) { - assert.equal(e, err); + let d = promise.defer(); + d.promise.then(function() { + assertFlowIs(newFlow); + }); + d.fulfill(); + }).then(function() { assertFlowIs(newFlow); }); - let d = promise.defer(); - d.promise.then(function() { - assertFlowIs(newFlow); - }); - d.fulfill(); - }).then(function() { - assertFlowIs(newFlow); + return waitForIdle(newFlow); }); - return waitForIdle(newFlow); - }); - - it('testFlowsSynchronizeWithThemselvesNotEachOther', function() { - var defaultFlow = promise.controlFlow(); - schedulePush('a', 'a'); - promise.controlFlow().timeout(250); - schedulePush('b', 'b'); + it('testFlowsSynchronizeWithThemselvesNotEachOther', function() { + var defaultFlow = promise.controlFlow(); + schedulePush('a', 'a'); + promise.controlFlow().timeout(500); + schedulePush('b', 'b'); - promise.createFlow(function() { - schedulePush('c', 'c'); - schedulePush('d', 'd'); - }); + promise.createFlow(function(flow2) { + assertFlowIs(flow2); + schedulePush('c', 'c'); + schedulePush('d', 'd'); + }); - return waitForIdle().then(function() { - assertMessages('a', 'c', 'd', 'b'); + return waitForIdle().then(function() { + assertMessages('a', 'c', 'd', 'b'); + }); }); - }); - it('testUnhandledErrorsAreReportedToTheOwningFlow', function() { - var error1 = Error('e1'); - var error2 = Error('e2'); + it('testUnhandledErrorsAreReportedToTheOwningFlow', function() { + var error1 = Error('e1'); + var error2 = Error('e2'); - var defaultFlow = promise.controlFlow(); - defaultFlow.removeAllListeners('uncaughtException'); + var defaultFlow = promise.controlFlow(); + defaultFlow.removeAllListeners('uncaughtException'); - var flow1Error = NativePromise.defer(); - flow1Error.promise.then(function(value) { - assert.equal(error2, value); - }); + var flow1Error = defer(); + flow1Error.promise.then(function(value) { + assert.equal(error2, value); + }); - var flow2Error = NativePromise.defer(); - flow2Error.promise.then(function(value) { - assert.equal(error1, value); - }); + var flow2Error = defer(); + flow2Error.promise.then(function(value) { + assert.equal(error1, value); + }); - promise.createFlow(function(flow) { - flow.once('uncaughtException', flow2Error.resolve); - promise.rejected(error1); + promise.createFlow(function(flow) { + flow.once('uncaughtException', flow2Error.resolve); + promise.rejected(error1); - defaultFlow.once('uncaughtException', flow1Error.resolve); - defaultFlow.execute(function() { - promise.rejected(error2); + defaultFlow.once('uncaughtException', flow1Error.resolve); + defaultFlow.execute(function() { + promise.rejected(error2); + }); }); - }); - return NativePromise.all([flow1Error.promise, flow2Error.promise]); - }); + return NativePromise.all([flow1Error.promise, flow2Error.promise]); + }); - it('testCanSynchronizeFlowsByReturningPromiseFromOneToAnother', function() { - var flow1 = new promise.ControlFlow; - var flow1Done = NativePromise.defer(); - flow1.once('idle', flow1Done.resolve); - flow1.once('uncaughtException', flow1Done.reject); + it('testCanSynchronizeFlowsByReturningPromiseFromOneToAnother', function() { + var flow1 = new promise.ControlFlow; + var flow1Done = defer(); + flow1.once('idle', flow1Done.resolve); + flow1.once('uncaughtException', flow1Done.reject); - var flow2 = new promise.ControlFlow; - var flow2Done = NativePromise.defer(); - flow2.once('idle', flow2Done.resolve); - flow2.once('uncaughtException', flow2Done.reject); + var flow2 = new promise.ControlFlow; + var flow2Done = defer(); + flow2.once('idle', flow2Done.resolve); + flow2.once('uncaughtException', flow2Done.reject); - flow1.execute(function() { - schedulePush('a', 'a'); - return promise.delayed(25); - }, 'start flow 1'); + flow1.execute(function() { + schedulePush('a', 'a'); + return promise.delayed(25); + }, 'start flow 1'); - flow2.execute(function() { - schedulePush('b', 'b'); - schedulePush('c', 'c'); flow2.execute(function() { - return flow1.execute(function() { - schedulePush('d', 'd'); - }, 'flow 1 task'); - }, 'inject flow1 result into flow2'); - schedulePush('e', 'e'); - }, 'start flow 2'); - - return NativePromise.all([flow1Done.promise, flow2Done.promise]). - then(function() { - assertMessages('a', 'b', 'c', 'd', 'e'); - }); - }); + schedulePush('b', 'b'); + schedulePush('c', 'c'); + flow2.execute(function() { + return flow1.execute(function() { + schedulePush('d', 'd'); + }, 'flow 1 task'); + }, 'inject flow1 result into flow2'); + schedulePush('e', 'e'); + }, 'start flow 2'); + + return NativePromise.all([flow1Done.promise, flow2Done.promise]). + then(function() { + assertMessages('a', 'b', 'c', 'd', 'e'); + }); + }); - it('testFramesWaitToCompleteForPendingRejections', function() { - return new NativePromise(function(fulfill, reject) { + it('testFramesWaitToCompleteForPendingRejections', function() { + return new NativePromise(function(fulfill, reject) { - promise.controlFlow().execute(function() { - promise.rejected(new StubError); - }).then(fulfill, reject); + promise.controlFlow().execute(function() { + promise.rejected(new StubError); + }).then(fulfill, reject); - }). - then(() => fail('expected to fail'), assertIsStubError); - }); + }). + then(() => fail('expected to fail'), assertIsStubError); + }); - it('testSynchronizeErrorsPropagateToOuterFlow', function() { - var outerFlow = new promise.ControlFlow; - var innerFlow = new promise.ControlFlow; + it('testSynchronizeErrorsPropagateToOuterFlow', function() { + var outerFlow = new promise.ControlFlow; + var innerFlow = new promise.ControlFlow; - var block = NativePromise.defer(); - innerFlow.execute(function() { - return block.promise; - }, 'block inner flow'); + var block = defer(); + innerFlow.execute(function() { + return block.promise; + }, 'block inner flow'); - outerFlow.execute(function() { - block.resolve(); - return innerFlow.execute(function() { - promise.rejected(new StubError); - }, 'trigger unhandled rejection error'); - }, 'run test'); + outerFlow.execute(function() { + block.resolve(); + return innerFlow.execute(function() { + promise.rejected(new StubError); + }, 'trigger unhandled rejection error'); + }, 'run test'); - return NativePromise.all([ - waitForIdle(innerFlow), - waitForAbort(outerFlow).then(assertIsStubError) - ]); - }); + return NativePromise.all([ + waitForIdle(innerFlow), + waitForAbort(outerFlow).then(assertIsStubError) + ]); + }); - it('testFailsIfErrbackThrows', function() { - promise.rejected('').then(null, throwStubError); - return waitForAbort().then(assertIsStubError); - }); + it('testFailsIfErrbackThrows', function() { + promise.rejected('').then(null, throwStubError); + return waitForAbort().then(assertIsStubError); + }); - it('testFailsIfCallbackReturnsRejectedPromise', function() { - promise.fulfilled().then(function() { - return promise.rejected(new StubError); + it('testFailsIfCallbackReturnsRejectedPromise', function() { + promise.fulfilled().then(function() { + return promise.rejected(new StubError); + }); + return waitForAbort().then(assertIsStubError); }); - return waitForAbort().then(assertIsStubError); - }); - it('testAbortsFrameIfTaskFails', function() { - promise.fulfilled().then(function() { - promise.controlFlow().execute(throwStubError); + it('testAbortsFrameIfTaskFails', function() { + promise.fulfilled().then(function() { + promise.controlFlow().execute(throwStubError); + }); + return waitForAbort().then(assertIsStubError); }); - return waitForAbort().then(assertIsStubError); - }); - it('testAbortsFramePromisedChainedFromTaskIsNotHandled', function() { - promise.fulfilled().then(function() { - promise.controlFlow().execute(function() {}). - then(throwStubError); + it('testAbortsFramePromisedChainedFromTaskIsNotHandled', function() { + promise.fulfilled().then(function() { + promise.controlFlow().execute(function() {}). + then(throwStubError); + }); + return waitForAbort().then(assertIsStubError); }); - return waitForAbort().then(assertIsStubError); - }); - it('testTrapsChainedUnhandledRejectionsWithinAFrame', function() { - var pair = callbackPair(null, assertIsStubError); - promise.fulfilled().then(function() { - promise.controlFlow().execute(function() {}). - then(throwStubError); - }).then(pair.callback, pair.errback); + it('testTrapsChainedUnhandledRejectionsWithinAFrame', function() { + var pair = callbackPair(null, assertIsStubError); + promise.fulfilled().then(function() { + promise.controlFlow().execute(function() {}). + then(throwStubError); + }).then(pair.callback, pair.errback); - return waitForIdle().then(pair.assertErrback); - }); + return waitForIdle().then(pair.assertErrback); + }); - it('testCancelsRemainingTasksIfFrameThrowsDuringScheduling', function() { - var task1, task2; - var pair = callbackPair(null, assertIsStubError); - var flow = promise.controlFlow(); - flow.execute(function() { - task1 = flow.execute(function() {}); - task2 = flow.execute(function() {}); - throw new StubError; - }).then(pair.callback, pair.errback); - - return waitForIdle(). - then(pair.assertErrback). - then(function() { - assert.ok(!task1.isPending()); - pair = callbackPair(); - return task1.then(pair.callback, pair.errback); - }). - then(function() { - pair.assertErrback(); - assert.ok(!task2.isPending()); - pair = callbackPair(); - return task2.then(pair.callback, pair.errback); - }). - then(function() { - pair.assertErrback(); - }); - }); + it('testCancelsRemainingTasksIfFrameThrowsDuringScheduling', function() { + var task1, task2; + var pair = callbackPair(null, assertIsStubError); + var flow = promise.controlFlow(); + flow.execute(function() { + task1 = flow.execute(function() {}); + task2 = flow.execute(function() {}); + throw new StubError; + }).then(pair.callback, pair.errback); - it('testCancelsRemainingTasksInFrameIfATaskFails', function() { - var task; - var pair = callbackPair(null, assertIsStubError); - var flow = promise.controlFlow(); - flow.execute(function() { - flow.execute(throwStubError); - task = flow.execute(function() {}); - }).then(pair.callback, pair.errback); - - return waitForIdle().then(pair.assertErrback).then(function() { - assert.ok(!task.isPending()); - pair = callbackPair(); - task.then(pair.callback, pair.errback); - }).then(function() { - pair.assertErrback(); + return waitForIdle(). + then(pair.assertErrback). + then(function() { + pair = callbackPair(); + return task1.then(pair.callback, pair.errback); + }). + then(function() { + pair.assertErrback(); + pair = callbackPair(); + return task2.then(pair.callback, pair.errback); + }). + then(function() { + pair.assertErrback(); + }); }); - }); - it('testDoesNotModifyRejectionErrorIfPromiseNotInsideAFlow', function() { - var error = Error('original message'); - var originalStack = error.stack; - var originalStr = error.toString(); + it('testCancelsRemainingTasksInFrameIfATaskFails', function() { + var task; + var pair = callbackPair(null, assertIsStubError); + var flow = promise.controlFlow(); + flow.execute(function() { + flow.execute(throwStubError); + task = flow.execute(function() {}); + }).then(pair.callback, pair.errback); - var pair = callbackPair(null, function(e) { - assert.equal(error, e); - assert.equal('original message', e.message); - assert.equal(originalStack, e.stack); - assert.equal(originalStr, e.toString()); + return waitForIdle().then(pair.assertErrback).then(function() { + pair = callbackPair(); + task.then(pair.callback, pair.errback); + }).then(function() { + pair.assertErrback(); + }); }); - promise.rejected(error).then(pair.callback, pair.errback); - return waitForIdle().then(pair.assertErrback); - }); + it('testDoesNotModifyRejectionErrorIfPromiseNotInsideAFlow', function() { + var error = Error('original message'); + var originalStack = error.stack; + var originalStr = error.toString(); - /** See https://github.com/SeleniumHQ/selenium/issues/444 */ - it('testMaintainsOrderWithPromiseChainsCreatedWithinAForeach_1', function() { - var messages = []; - flow.execute(function() { - return promise.fulfilled(['a', 'b', 'c', 'd']); - }, 'start').then(function(steps) { - steps.forEach(function(step) { - promise.fulfilled(step) - .then(function() { - messages.push(step + '.1'); - }).then(function() { - messages.push(step + '.2'); - }); - }) - }); - return waitForIdle().then(function() { - assert.deepEqual( - ['a.1', 'a.2', 'b.1', 'b.2', 'c.1', 'c.2', 'd.1', 'd.2'], - messages); + var pair = callbackPair(null, function(e) { + assert.equal(error, e); + assert.equal('original message', e.message); + assert.equal(originalStack, e.stack); + assert.equal(originalStr, e.toString()); + }); + + promise.rejected(error).then(pair.callback, pair.errback); + return waitForIdle().then(pair.assertErrback); }); - }); - /** See https://github.com/SeleniumHQ/selenium/issues/444 */ - it('testMaintainsOrderWithPromiseChainsCreatedWithinAForeach_2', function() { - var messages = []; - flow.execute(function() { - return promise.fulfilled(['a', 'b', 'c', 'd']); - }, 'start').then(function(steps) { - steps.forEach(function(step) { - promise.fulfilled(step) - .then(function() { - messages.push(step + '.1'); - }).then(function() { - flow.execute(function() {}, step + '.2').then(function() { + /** See https://github.com/SeleniumHQ/selenium/issues/444 */ + it('testMaintainsOrderWithPromiseChainsCreatedWithinAForeach_1', function() { + var messages = []; + flow.execute(function() { + return promise.fulfilled(['a', 'b', 'c', 'd']); + }, 'start').then(function(steps) { + steps.forEach(function(step) { + promise.fulfilled(step) + .then(function() { + messages.push(step + '.1'); + }).then(function() { messages.push(step + '.2'); }); - }); - }) - }); - return waitForIdle().then(function() { - assert.deepEqual( - ['a.1', 'a.2', 'b.1', 'b.2', 'c.1', 'c.2', 'd.1', 'd.2'], - messages); + }) + }); + return waitForIdle().then(function() { + assert.deepEqual( + ['a.1', 'a.2', 'b.1', 'b.2', 'c.1', 'c.2', 'd.1', 'd.2'], + messages); + }); }); - }); - /** See https://github.com/SeleniumHQ/selenium/issues/444 */ - it('testMaintainsOrderWithPromiseChainsCreatedWithinAForeach_3', function() { - var messages = []; - flow.execute(function() { - return promise.fulfilled(['a', 'b', 'c', 'd']); - }, 'start').then(function(steps) { - steps.forEach(function(step) { - promise.fulfilled(step) - .then(function(){}) - .then(function() { - messages.push(step + '.1'); - return flow.execute(function() {}, step + '.1'); - }).then(function() { - flow.execute(function() {}, step + '.2').then(function(text) { - messages.push(step + '.2'); + /** See https://github.com/SeleniumHQ/selenium/issues/444 */ + it('testMaintainsOrderWithPromiseChainsCreatedWithinAForeach_2', function() { + var messages = []; + flow.execute(function() { + return promise.fulfilled(['a', 'b', 'c', 'd']); + }, 'start').then(function(steps) { + steps.forEach(function(step) { + promise.fulfilled(step) + .then(function() { + messages.push(step + '.1'); + }).then(function() { + flow.execute(function() {}, step + '.2').then(function() { + messages.push(step + '.2'); + }); }); - }); - }) + }) + }); + return waitForIdle().then(function() { + assert.deepEqual( + ['a.1', 'a.2', 'b.1', 'b.2', 'c.1', 'c.2', 'd.1', 'd.2'], + messages); + }); }); - return waitForIdle().then(function() { - assert.deepEqual( - ['a.1', 'a.2', 'b.1', 'b.2', 'c.1', 'c.2', 'd.1', 'd.2'], - messages); + + /** See https://github.com/SeleniumHQ/selenium/issues/444 */ + it('testMaintainsOrderWithPromiseChainsCreatedWithinAForeach_3', function() { + var messages = []; + flow.execute(function() { + return promise.fulfilled(['a', 'b', 'c', 'd']); + }, 'start').then(function(steps) { + steps.forEach(function(step) { + promise.fulfilled(step) + .then(function(){}) + .then(function() { + messages.push(step + '.1'); + return flow.execute(function() {}, step + '.1'); + }).then(function() { + flow.execute(function() {}, step + '.2').then(function(text) { + messages.push(step + '.2'); + }); + }); + }) + }); + return waitForIdle().then(function() { + assert.deepEqual( + ['a.1', 'a.2', 'b.1', 'b.2', 'c.1', 'c.2', 'd.1', 'd.2'], + messages); + }); }); - }); - /** See https://github.com/SeleniumHQ/selenium/issues/363 */ - it('testTasksScheduledInASeparateTurnOfTheEventLoopGetASeparateTaskQueue_2', function() { - scheduleAction('a', () => promise.delayed(10)); - schedule('b'); - setTimeout(() => schedule('c'), 0); + /** See https://github.com/SeleniumHQ/selenium/issues/363 */ + it('testTasksScheduledInASeparateTurnOfTheEventLoopGetASeparateTaskQueue_2', function() { + scheduleAction('a', () => promise.delayed(10)); + schedule('b'); + setTimeout(() => schedule('c'), 0); - return waitForIdle().then(function() { - assertFlowHistory('a', 'c', 'b'); + return waitForIdle().then(function() { + assertFlowHistory('a', 'c', 'b'); + }); }); - }); - /** See https://github.com/SeleniumHQ/selenium/issues/363 */ - it('testTasksScheduledInASeparateTurnOfTheEventLoopGetASeparateTaskQueue_2', function() { - scheduleAction('a', () => promise.delayed(10)); - schedule('b'); - schedule('c'); - setTimeout(function() { - schedule('d'); - scheduleAction('e', () => promise.delayed(10)); - schedule('f'); - }, 0); - - return waitForIdle().then(function() { - assertFlowHistory('a', 'd', 'e', 'b', 'c', 'f'); - }); - }); + /** See https://github.com/SeleniumHQ/selenium/issues/363 */ + it('testTasksScheduledInASeparateTurnOfTheEventLoopGetASeparateTaskQueue_2', function() { + scheduleAction('a', () => promise.delayed(10)); + schedule('b'); + schedule('c'); + setTimeout(function() { + schedule('d'); + scheduleAction('e', () => promise.delayed(10)); + schedule('f'); + }, 0); - /** See https://github.com/SeleniumHQ/selenium/issues/363 */ - it('testCanSynchronizeTasksFromAdjacentTaskQueues', function() { - var task1 = scheduleAction('a', () => promise.delayed(10)); - schedule('b'); - setTimeout(function() { - scheduleAction('c', () => task1); - schedule('d'); - }, 0); - - return waitForIdle().then(function() { - assertFlowHistory('a', 'c', 'd', 'b'); + return waitForIdle().then(function() { + assertFlowHistory('a', 'd', 'e', 'b', 'c', 'f'); + }); }); - }); - describe('testCancellingAScheduledTask', function() { - it('1', function() { - var called = false; - var task1 = scheduleAction('a', () => called = true); - task1.cancel('no soup for you'); + /** See https://github.com/SeleniumHQ/selenium/issues/363 */ + it('testCanSynchronizeTasksFromAdjacentTaskQueues', function() { + var task1 = scheduleAction('a', () => promise.delayed(10)); + schedule('b'); + setTimeout(function() { + scheduleAction('c', () => task1); + schedule('d'); + }, 0); return waitForIdle().then(function() { - assert.ok(!called); - assertFlowHistory(); - return task1.catch(function(e) { - assert.ok(e instanceof promise.CancellationError); - assert.equal('no soup for you', e.message); - }); + assertFlowHistory('a', 'c', 'd', 'b'); }); }); - it('2', function() { - schedule('a'); - var called = false; - var task2 = scheduleAction('b', () => called = true); - schedule('c'); + describe('testCancellingAScheduledTask', function() { + it('1', function() { + var called = false; + var task1 = scheduleAction('a', () => called = true); + task1.cancel('no soup for you'); - task2.cancel('no soup for you'); - - return waitForIdle().then(function() { - assert.ok(!called); - assertFlowHistory('a', 'c'); - return task2.catch(function(e) { - assert.ok(e instanceof promise.CancellationError); - assert.equal('no soup for you', e.message); + return waitForIdle().then(function() { + assert.ok(!called); + assertFlowHistory(); + return task1.catch(function(e) { + assert.ok(e instanceof promise.CancellationError); + assert.equal('no soup for you', e.message); + }); }); }); - }); - it('3', function() { - var called = false; - var task = scheduleAction('a', () => called = true); - task.cancel(new StubError); + it('2', function() { + schedule('a'); + var called = false; + var task2 = scheduleAction('b', () => called = true); + schedule('c'); - return waitForIdle().then(function() { - assert.ok(!called); - assertFlowHistory(); - return task.catch(function(e) { - assert.ok(e instanceof promise.CancellationError); + task2.cancel('no soup for you'); + + return waitForIdle().then(function() { + assert.ok(!called); + assertFlowHistory('a', 'c'); + return task2.catch(function(e) { + assert.ok(e instanceof promise.CancellationError); + assert.equal('no soup for you', e.message); + }); }); }); - }); - it('4', function() { - var seen = []; - var task = scheduleAction('a', () => seen.push(1)) - .then(() => seen.push(2)) - .then(() => seen.push(3)) - .then(() => seen.push(4)) - .then(() => seen.push(5)); - task.cancel(new StubError); + it('3', function() { + var called = false; + var task = scheduleAction('a', () => called = true); + task.cancel(new StubError); - return waitForIdle().then(function() { - assert.deepEqual([], seen); - assertFlowHistory(); - return task.catch(function(e) { - assert.ok(e instanceof promise.CancellationError); + return waitForIdle().then(function() { + assert.ok(!called); + assertFlowHistory(); + return task.catch(function(e) { + assert.ok(e instanceof promise.CancellationError); + }); }); }); - }); - it('fromWithinAnExecutingTask', function() { - var called = false; - var task; - scheduleAction('a', function() { - task.cancel('no soup for you'); + it('4', function() { + var seen = []; + var task = scheduleAction('a', () => seen.push(1)) + .then(() => seen.push(2)) + .then(() => seen.push(3)) + .then(() => seen.push(4)) + .then(() => seen.push(5)); + task.cancel(new StubError); + + return waitForIdle().then(function() { + assert.deepEqual([], seen); + assertFlowHistory(); + return task.catch(function(e) { + assert.ok(e instanceof promise.CancellationError); + }); + }); }); - task = scheduleAction('b', () => called = true); - schedule('c'); - return waitForIdle().then(function() { - assert.ok(!called); - assertFlowHistory('a', 'c'); - return task.catch(function(e) { - assert.ok(e instanceof promise.CancellationError); - assert.equal('no soup for you', e.message); + it('fromWithinAnExecutingTask', function() { + var called = false; + var task; + scheduleAction('a', function() { + task.cancel('no soup for you'); + }); + task = scheduleAction('b', () => called = true); + schedule('c'); + + return waitForIdle().then(function() { + assert.ok(!called); + assertFlowHistory('a', 'c'); + return task.catch(function(e) { + assert.ok(e instanceof promise.CancellationError); + assert.equal('no soup for you', e.message); + }); }); }); }); - }); - it('testCancellingAPendingTask', function() { - var order = []; - var unresolved = promise.defer(); + it('testCancellingAPendingTask', function() { + var order = []; + var unresolved = promise.defer(); - var innerTask; - var outerTask = scheduleAction('a', function() { - order.push(1); + var innerTask; + var outerTask = scheduleAction('a', function() { + order.push(1); - // Schedule a task that will never finish. - innerTask = scheduleAction('a.1', function() { - return unresolved.promise; - }); + // Schedule a task that will never finish. + innerTask = scheduleAction('a.1', function() { + return unresolved.promise; + }); - // Since the outerTask is cancelled below, innerTask should be cancelled - // with a DiscardedTaskError, which means its callbacks are silently - // dropped - so this should never execute. - innerTask.catch(function(e) { - order.push(2); + // Since the outerTask is cancelled below, innerTask should be cancelled + // with a DiscardedTaskError, which means its callbacks are silently + // dropped - so this should never execute. + innerTask.catch(function(e) { + order.push(2); + }); }); - }); - schedule('b'); + schedule('b'); - outerTask.catch(function(e) { - order.push(3); - assert.ok(e instanceof promise.CancellationError); - assert.equal('no soup for you', e.message); - }); + outerTask.catch(function(e) { + order.push(3); + assert.ok(e instanceof promise.CancellationError); + assert.equal('no soup for you', e.message); + }); - unresolved.promise.catch(function(e) { - order.push(4); - assert.ok(e instanceof promise.CancellationError); - }); + unresolved.promise.catch(function(e) { + order.push(4); + assert.ok(e instanceof promise.CancellationError); + }); - return timeout(10).then(function() { - assert.deepEqual([1], order); - assert.ok(unresolved.promise.isPending()); + return timeout(10).then(function() { + assert.deepEqual([1], order); - outerTask.cancel('no soup for you'); - return waitForIdle(); - }).then(function() { - assertFlowHistory('a', 'a.1', 'b'); - assert.deepEqual([1, 3, 4], order); + outerTask.cancel('no soup for you'); + return waitForIdle(); + }).then(function() { + assertFlowHistory('a', 'a.1', 'b'); + assert.deepEqual([1, 3, 4], order); + }); }); - }); - it('testCancellingAPendingPromiseCallback', function() { - var called = false; + it('testCancellingAPendingPromiseCallback', function() { + var called = false; - var root = promise.fulfilled(); - root.then(function() { - cb2.cancel('no soup for you'); - }); + var root = promise.fulfilled(); + root.then(function() { + cb2.cancel('no soup for you'); + }); - var cb2 = root.then(fail, fail); // These callbacks should never be called. - cb2.then(fail, function(e) { - called = true; - assert.ok(e instanceof promise.CancellationError); - assert.equal('no soup for you', e.message); - }); + var cb2 = root.then(fail, fail); // These callbacks should never be called. + cb2.then(fail, function(e) { + called = true; + assert.ok(e instanceof promise.CancellationError); + assert.equal('no soup for you', e.message); + }); - return waitForIdle().then(function() { - assert.ok(called); + return waitForIdle().then(function() { + assert.ok(called); + }); }); - }); - describe('testResetFlow', function() { - it('1', function() { - var called = 0; - var task = flow.execute(() => called++); - task.finally(() => called++); + describe('testResetFlow', function() { + it('1', function() { + var called = 0; + var task = flow.execute(() => called++); + task.finally(() => called++); - return new Promise(function(fulfill) { - flow.once('reset', fulfill); - flow.reset(); + return new Promise(function(fulfill) { + flow.once('reset', fulfill); + flow.reset(); - }).then(function() { - assert.equal(0, called); - assert.ok(!task.isPending()); - return task; + }).then(function() { + assert.equal(0, called); + return task; - }).then(fail, function(e) { - assert.ok(e instanceof promise.CancellationError); - assert.equal('ControlFlow was reset', e.message); + }).then(fail, function(e) { + assert.ok(e instanceof promise.CancellationError); + assert.equal('ControlFlow was reset', e.message); + }); }); - }); - it('2', function() { - var called = 0; - var task1 = flow.execute(() => called++); - task1.finally(() => called++); + it('2', function() { + var called = 0; + var task1 = flow.execute(() => called++); + task1.finally(() => called++); - var task2 = flow.execute(() => called++); - task2.finally(() => called++); + var task2 = flow.execute(() => called++); + task2.finally(() => called++); - var task3 = flow.execute(() => called++); - task3.finally(() => called++); + var task3 = flow.execute(() => called++); + task3.finally(() => called++); - return new Promise(function(fulfill) { - flow.once('reset', fulfill); - flow.reset(); + return new Promise(function(fulfill) { + flow.once('reset', fulfill); + flow.reset(); - }).then(function() { - assert.equal(0, called); - assert.ok(!task1.isPending()); - assert.ok(!task2.isPending()); - assert.ok(!task3.isPending()); + }).then(function() { + assert.equal(0, called); + }); }); }); - }); - describe('testPromiseFulfilledInsideTask', function() { - it('1', function() { - var order = []; + describe('testPromiseFulfilledInsideTask', function() { + it('1', function() { + var order = []; - flow.execute(function() { - var d = promise.defer(); + flow.execute(function() { + var d = promise.defer(); - d.promise.then(() => order.push('a')); - d.promise.then(() => order.push('b')); - d.promise.then(() => order.push('c')); - d.fulfill(); + d.promise.then(() => order.push('a')); + d.promise.then(() => order.push('b')); + d.promise.then(() => order.push('c')); + d.fulfill(); - flow.execute(() => order.push('d')); + flow.execute(() => order.push('d')); - }).then(() => order.push('fin')); + }).then(() => order.push('fin')); - return waitForIdle().then(function() { - assert.deepEqual(['a', 'b', 'c', 'd', 'fin'], order); + return waitForIdle().then(function() { + assert.deepEqual(['a', 'b', 'c', 'd', 'fin'], order); + }); }); - }); - it('2', function() { - var order = []; + it('2', function() { + var order = []; - flow.execute(function() { - flow.execute(() => order.push('a')); - flow.execute(() => order.push('b')); + flow.execute(function() { + flow.execute(() => order.push('a')); + flow.execute(() => order.push('b')); + + var d = promise.defer(); + d.promise.then(() => order.push('c')); + d.promise.then(() => order.push('d')); + d.fulfill(); + flow.execute(() => order.push('e')); + + }).then(() => order.push('fin')); + + return waitForIdle().then(function() { + assert.deepEqual(['a', 'b', 'c', 'd', 'e', 'fin'], order); + }); + }); + + it('3', function() { + var order = []; var d = promise.defer(); d.promise.then(() => order.push('c')); d.promise.then(() => order.push('d')); - d.fulfill(); - flow.execute(() => order.push('e')); + flow.execute(function() { + flow.execute(() => order.push('a')); + flow.execute(() => order.push('b')); - }).then(() => order.push('fin')); + d.promise.then(() => order.push('e')); + d.fulfill(); - return waitForIdle().then(function() { - assert.deepEqual(['a', 'b', 'c', 'd', 'e', 'fin'], order); + flow.execute(() => order.push('f')); + + }).then(() => order.push('fin')); + + return waitForIdle().then(function() { + assert.deepEqual(['c', 'd', 'a', 'b', 'e', 'f', 'fin'], order); + }); }); - }); - it('3', function() { - var order = []; - var d = promise.defer(); - d.promise.then(() => order.push('c')); - d.promise.then(() => order.push('d')); + it('4', function() { + var order = []; + var d = promise.defer(); + d.promise.then(() => order.push('a')); + d.promise.then(() => order.push('b')); - flow.execute(function() { - flow.execute(() => order.push('a')); - flow.execute(() => order.push('b')); + flow.execute(function() { + flow.execute(function() { + order.push('c'); + flow.execute(() => order.push('d')); + d.promise.then(() => order.push('e')); + }); + flow.execute(() => order.push('f')); - d.promise.then(() => order.push('e')); - d.fulfill(); + d.promise.then(() => order.push('g')); + d.fulfill(); - flow.execute(() => order.push('f')); + flow.execute(() => order.push('h')); - }).then(() => order.push('fin')); + }).then(() => order.push('fin')); - return waitForIdle().then(function() { - assert.deepEqual(['c', 'd', 'a', 'b', 'e', 'f', 'fin'], order); + return waitForIdle().then(function() { + assert.deepEqual(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'fin'], order); + }); }); }); - it('4', function() { - var order = []; - var d = promise.defer(); - d.promise.then(() => order.push('a')); - d.promise.then(() => order.push('b')); + describe('testSettledPromiseCallbacksInsideATask', function() { + it('1', function() { + var order = []; + var p = promise.fulfilled(); - flow.execute(function() { flow.execute(function() { - order.push('c'); - flow.execute(() => order.push('d')); - d.promise.then(() => order.push('e')); + flow.execute(() => order.push('a')); + p.then(() => order.push('b')); + flow.execute(() => order.push('c')); + p.then(() => order.push('d')); + }).then(() => order.push('fin')); + + return waitForIdle().then(function() { + assert.deepEqual(['a', 'b', 'c', 'd', 'fin'], order); }); - flow.execute(() => order.push('f')); - - d.promise.then(() => order.push('g')); - d.fulfill(); + }); - flow.execute(() => order.push('h')); + it('2', function() { + var order = []; - }).then(() => order.push('fin')); + flow.execute(function() { + flow.execute(() => order.push('a')) + .then( () => order.push('c')); + flow.execute(() => order.push('b')); + }).then(() => order.push('fin')); - return waitForIdle().then(function() { - assert.deepEqual(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'fin'], order); + return waitForIdle().then(function() { + assert.deepEqual(['a', 'c', 'b', 'fin'], order); + }); }); }); - }); - describe('testSettledPromiseCallbacksInsideATask', function() { - it('1', function() { + it('testTasksDoNotWaitForNewlyCreatedPromises', function() { var order = []; - var p = promise.fulfilled(); flow.execute(function() { - flow.execute(() => order.push('a')); - p.then(() => order.push('b')); - flow.execute(() => order.push('c')); - p.then(() => order.push('d')); - }).then(() => order.push('fin')); - - return waitForIdle().then(function() { - assert.deepEqual(['a', 'b', 'c', 'd', 'fin'], order); - }); - }); + var d = promise.defer(); - it('2', function() { - var order = []; + // This is a normal promise, not a task, so the task for this callback is + // considered volatile. Volatile tasks should be skipped when they reach + // the front of the task queue. + d.promise.then(() => order.push('a')); - flow.execute(function() { - flow.execute(() => order.push('a')) - .then( () => order.push('c')); flow.execute(() => order.push('b')); + flow.execute(function() { + flow.execute(() => order.push('c')); + d.promise.then(() => order.push('d')); + d.fulfill(); + }); + flow.execute(() => order.push('e')); + }).then(() => order.push('fin')); return waitForIdle().then(function() { - assert.deepEqual(['a', 'c', 'b', 'fin'], order); + assert.deepEqual(['b', 'a', 'c', 'd', 'e', 'fin'], order); }); }); - }); - - it('testTasksDoNotWaitForNewlyCreatedPromises', function() { - var order = []; - flow.execute(function() { - var d = promise.defer(); - - // This is a normal promise, not a task, so the task for this callback is - // considered volatile. Volatile tasks should be skipped when they reach - // the front of the task queue. - d.promise.then(() => order.push('a')); - - flow.execute(() => order.push('b')); - flow.execute(function() { - flow.execute(() => order.push('c')); - d.promise.then(() => order.push('d')); - d.fulfill(); + it('testCallbackDependenciesDoNotDeadlock', function() { + var order = []; + var root = promise.defer(); + var dep = promise.fulfilled().then(function() { + order.push('a'); + return root.promise.then(function() { + order.push('b'); + }); }); - flow.execute(() => order.push('e')); - - }).then(() => order.push('fin')); + // This callback depends on |dep|, which depends on another callback + // attached to |root| via a chain. + root.promise.then(function() { + order.push('c'); + return dep.then(() => order.push('d')); + }).then(() => order.push('fin')); - return waitForIdle().then(function() { - assert.deepEqual(['b', 'a', 'c', 'd', 'e', 'fin'], order); - }); - }); + setTimeout(() => root.fulfill(), 20); - it('testCallbackDependenciesDoNotDeadlock', function() { - var order = []; - var root = promise.defer(); - var dep = promise.fulfilled().then(function() { - order.push('a'); - return root.promise.then(function() { - order.push('b'); + return waitForIdle().then(function() { + assert.deepEqual(['a', 'b', 'c', 'd', 'fin'], order); }); }); - // This callback depends on |dep|, which depends on another callback - // attached to |root| via a chain. - root.promise.then(function() { - order.push('c'); - return dep.then(() => order.push('d')); - }).then(() => order.push('fin')); - - setTimeout(() => root.fulfill(), 20); - - return waitForIdle().then(function() { - assert.deepEqual(['a', 'b', 'c', 'd', 'fin'], order); - }); }); }); diff --git a/node_modules/selenium-webdriver/test/lib/promise_generator_test.js b/node_modules/selenium-webdriver/test/lib/promise_generator_test.js index 5fdff9f80..b3388da78 100644 --- a/node_modules/selenium-webdriver/test/lib/promise_generator_test.js +++ b/node_modules/selenium-webdriver/test/lib/promise_generator_test.js @@ -19,237 +19,165 @@ const assert = require('assert'); const promise = require('../../lib/promise'); +const {enablePromiseManager, promiseManagerSuite} = require('../../lib/test/promise'); describe('promise.consume()', function() { - it('requires inputs to be generator functions', function() { - assert.throws(function() { - promise.consume(function() {}); + promiseManagerSuite(() => { + it('requires inputs to be generator functions', function() { + assert.throws(function() { + promise.consume(function() {}); + }); }); - }); - it('handles a basic generator with no yielded promises', function() { - var values = []; - return promise.consume(function* () { - var i = 0; - while (i < 4) { - i = yield i + 1; - values.push(i); - } - }).then(function() { - assert.deepEqual([1, 2, 3, 4], values); + it('handles a basic generator with no yielded promises', function() { + var values = []; + return promise.consume(function* () { + var i = 0; + while (i < 4) { + i = yield i + 1; + values.push(i); + } + }).then(function() { + assert.deepEqual([1, 2, 3, 4], values); + }); }); - }); - it('handles a promise yielding generator', function() { - var values = []; - return promise.consume(function* () { - var i = 0; - while (i < 4) { - // Test that things are actually async here. - setTimeout(function() { - values.push(i * 2); - }, 10); - - yield promise.delayed(10).then(function() { - values.push(i++); - }); - } - }).then(function() { - assert.deepEqual([0, 0, 2, 1, 4, 2, 6, 3], values); + it('handles a promise yielding generator', function() { + var values = []; + return promise.consume(function* () { + var i = 0; + while (i < 4) { + // Test that things are actually async here. + setTimeout(function() { + values.push(i * 2); + }, 10); + + yield promise.delayed(10).then(function() { + values.push(i++); + }); + } + }).then(function() { + assert.deepEqual([0, 0, 2, 1, 4, 2, 6, 3], values); + }); }); - }); - it('assignemnts to yielded promises get fulfilled value', function() { - return promise.consume(function* () { - var p = promise.fulfilled(2); - var x = yield p; - assert.equal(2, x); + it('assignments to yielded promises get fulfilled value', function() { + return promise.consume(function* () { + let x = yield Promise.resolve(2); + assert.equal(2, x); + }); }); - }); - it('is possible to cancel promise generators', function() { - var values = []; - var p = promise.consume(function* () { - var i = 0; - while (i < 3) { - yield promise.delayed(100).then(function() { - values.push(i++); - }); - } - }); - return promise.delayed(75).then(function() { - p.cancel(); - return p.catch(function() { - return promise.delayed(300); + it('uses final return value as fulfillment value', function() { + return promise.consume(function* () { + yield 1; + yield 2; + return 3; + }).then(function(value) { + assert.equal(3, value); }); - }).then(function() { - assert.deepEqual([0], values); }); - }); - it('uses final return value as fulfillment value', function() { - return promise.consume(function* () { - yield 1; - yield 2; - return 3; - }).then(function(value) { - assert.equal(3, value); + it('throws rejected promise errors within the generator', function() { + var values = []; + return promise.consume(function* () { + values.push('a'); + var e = Error('stub error'); + try { + yield Promise.reject(e); + values.push('b'); + } catch (ex) { + assert.equal(e, ex); + values.push('c'); + } + values.push('d'); + }).then(function() { + assert.deepEqual(['a', 'c', 'd'], values); + }); }); - }); - it('throws rejected promise errors within the generator', function() { - var values = []; - return promise.consume(function* () { - values.push('a'); + it('aborts the generator if there is an unhandled rejection', function() { + var values = []; var e = Error('stub error'); - try { + return promise.consume(function* () { + values.push(1); yield promise.rejected(e); - values.push('b'); - } catch (ex) { - assert.equal(e, ex); - values.push('c'); - } - values.push('d'); - }).then(function() { - assert.deepEqual(['a', 'c', 'd'], values); - }); - }); - - it('aborts the generator if there is an unhandled rejection', function() { - var values = []; - var e = Error('stub error'); - return promise.consume(function* () { - values.push(1); - yield promise.rejected(e); - values.push(2); - }).catch(function() { - assert.deepEqual([1], values); - }); - }); - - it('yield waits for promises', function() { - var values = []; - var d = promise.defer(); - - setTimeout(function() { - assert.deepEqual([1], values); - d.fulfill(2); - }, 100); - - return promise.consume(function* () { - values.push(1); - values.push((yield d.promise), 3); - }).then(function() { - assert.deepEqual([1, 2, 3], values); - }); - }); - - it('accepts custom scopes', function() { - return promise.consume(function* () { - return this.name; - }, {name: 'Bob'}).then(function(value) { - assert.equal('Bob', value); + values.push(2); + }).catch(function() { + assert.deepEqual([1], values); + }); }); - }); - it('accepts initial generator arguments', function() { - return promise.consume(function* (a, b) { - assert.equal('red', a); - assert.equal('apples', b); - }, null, 'red', 'apples'); - }); - - it('executes generator within the control flow', function() { - var promises = [ - promise.defer(), - promise.defer() - ]; - var values = []; - - setTimeout(function() { - assert.deepEqual([], values); - promises[0].fulfill(1); - }, 100); - - setTimeout(function() { - assert.deepEqual([1], values); - promises[1].fulfill(2); - }, 200); - - return promise.controlFlow().execute(function* () { - values.push(yield promises[0].promise); - values.push(yield promises[1].promise); - values.push('fin'); - }).then(function() { - assert.deepEqual([1, 2, 'fin'], values); - }); - }); - - it('handles tasks scheduled in generator', function() { - var flow = promise.controlFlow(); - return flow.execute(function* () { - var x = yield flow.execute(function() { - return promise.delayed(10).then(function() { - return 1; - }); + it('yield waits for promises', function() { + let values = []; + let blocker = promise.delayed(100).then(() => { + assert.deepEqual([1], values); + return 2; }); - var y = yield flow.execute(function() { - return 2; + return promise.consume(function* () { + values.push(1); + values.push(yield blocker, 3); + }).then(function() { + assert.deepEqual([1, 2, 3], values); }); + }); - return x + y; - }).then(function(value) { - assert.equal(3, value); + it('accepts custom scopes', function() { + return promise.consume(function* () { + return this.name; + }, {name: 'Bob'}).then(function(value) { + assert.equal('Bob', value); + }); }); - }); - it('blocks the control flow while processing generator', function() { - var values = []; - return promise.controlFlow().wait(function* () { - yield values.push(1); - values.push(yield promise.delayed(10).then(function() { - return 2; - })); - yield values.push(3); - return values.length === 6; - }, 250).then(function() { - assert.deepEqual([1, 2, 3, 1, 2, 3], values); + it('accepts initial generator arguments', function() { + return promise.consume(function* (a, b) { + assert.equal('red', a); + assert.equal('apples', b); + }, null, 'red', 'apples'); }); }); - it('ControlFlow.wait() will timeout on long generator', function() { - var values = []; - return promise.controlFlow().wait(function* () { - var i = 0; - while (i < 3) { - yield promise.delayed(100).then(function() { - values.push(i++); + enablePromiseManager(() => { + it('is possible to cancel promise generators', function() { + var values = []; + var p = promise.consume(function* () { + var i = 0; + while (i < 3) { + yield promise.delayed(100).then(function() { + values.push(i++); + }); + } + }); + return promise.delayed(75).then(function() { + p.cancel(); + return p.catch(function() { + return promise.delayed(300); }); - } - }, 75).catch(function() { - assert.deepEqual( - [0, 1, 2], values, 'Should complete one loop of wait condition'); + }).then(function() { + assert.deepEqual([0], values); + }); }); - }); - describe('generators in promise callbacks', function() { - it('works with no initial value', function() { + it('executes generator within the control flow', function() { var promises = [ - promise.defer(), - promise.defer() + promise.defer(), + promise.defer() ]; var values = []; setTimeout(function() { + assert.deepEqual([], values); promises[0].fulfill(1); - }, 50); + }, 100); setTimeout(function() { + assert.deepEqual([1], values); promises[1].fulfill(2); - }, 100); + }, 200); - return promise.fulfilled().then(function*() { + return promise.controlFlow().execute(function* () { values.push(yield promises[0].promise); values.push(yield promises[1].promise); values.push('fin'); @@ -258,49 +186,123 @@ describe('promise.consume()', function() { }); }); - it('starts the generator with promised value', function() { - var promises = [ - promise.defer(), - promise.defer() - ]; - var values = []; + it('handles tasks scheduled in generator', function() { + var flow = promise.controlFlow(); + return flow.execute(function* () { + var x = yield flow.execute(function() { + return promise.delayed(10).then(function() { + return 1; + }); + }); - setTimeout(function() { - promises[0].fulfill(1); - }, 50); + var y = yield flow.execute(function() { + return 2; + }); - setTimeout(function() { - promises[1].fulfill(2); - }, 100); + return x + y; + }).then(function(value) { + assert.equal(3, value); + }); + }); - return promise.fulfilled(3).then(function*(value) { - var p1 = yield promises[0].promise; - var p2 = yield promises[1].promise; - values.push(value + p1); - values.push(value + p2); - values.push('fin'); - }).then(function() { - assert.deepEqual([4, 5, 'fin'], values); + it('blocks the control flow while processing generator', function() { + var values = []; + return promise.controlFlow().wait(function* () { + yield values.push(1); + values.push(yield promise.delayed(10).then(function() { + return 2; + })); + yield values.push(3); + return values.length === 6; + }, 250).then(function() { + assert.deepEqual([1, 2, 3, 1, 2, 3], values); }); }); - it('throws yielded rejections within the generator callback', function() { - var d = promise.defer(); - var e = Error('stub'); + it('ControlFlow.wait() will timeout on long generator', function() { + var values = []; + return promise.controlFlow().wait(function* () { + var i = 0; + while (i < 3) { + yield promise.delayed(100).then(function() { + values.push(i++); + }); + } + }, 75).catch(function() { + assert.deepEqual( + [0, 1, 2], values, 'Should complete one loop of wait condition'); + }); + }); - setTimeout(function() { - d.reject(e); - }, 50); + describe('generators in promise callbacks', function() { + it('works with no initial value', function() { + var promises = [ + promise.defer(), + promise.defer() + ]; + var values = []; - return promise.fulfilled().then(function*() { - var threw = false; - try { - yield d.promise; - } catch (ex) { - threw = true; - assert.equal(e, ex); - } - assert.ok(threw); + setTimeout(function() { + promises[0].fulfill(1); + }, 50); + + setTimeout(function() { + promises[1].fulfill(2); + }, 100); + + return promise.fulfilled().then(function*() { + values.push(yield promises[0].promise); + values.push(yield promises[1].promise); + values.push('fin'); + }).then(function() { + assert.deepEqual([1, 2, 'fin'], values); + }); + }); + + it('starts the generator with promised value', function() { + var promises = [ + promise.defer(), + promise.defer() + ]; + var values = []; + + setTimeout(function() { + promises[0].fulfill(1); + }, 50); + + setTimeout(function() { + promises[1].fulfill(2); + }, 100); + + return promise.fulfilled(3).then(function*(value) { + var p1 = yield promises[0].promise; + var p2 = yield promises[1].promise; + values.push(value + p1); + values.push(value + p2); + values.push('fin'); + }).then(function() { + assert.deepEqual([4, 5, 'fin'], values); + }); + }); + + it('throws yielded rejections within the generator callback', function() { + var d = promise.defer(); + var e = Error('stub'); + + setTimeout(function() { + d.reject(e); + }, 50); + + return promise.fulfilled().then(function*() { + var threw = false; + try { + yield d.promise; + } catch (ex) { + threw = true; + assert.equal(e, ex); + } + assert.ok(threw); + }); }); }); }); diff --git a/node_modules/selenium-webdriver/test/lib/promise_test.js b/node_modules/selenium-webdriver/test/lib/promise_test.js index 51554ecd9..96d2ccc22 100644 --- a/node_modules/selenium-webdriver/test/lib/promise_test.js +++ b/node_modules/selenium-webdriver/test/lib/promise_test.js @@ -21,6 +21,7 @@ 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; @@ -36,1032 +37,1054 @@ describe('promise', function() { var app, uncaughtExceptions; beforeEach(function setUp() { - promise.LONG_STACK_TRACES = false; - uncaughtExceptions = []; + 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)); + app = promise.controlFlow(); + app.on(promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION, + (e) => uncaughtExceptions.push(e)); + } }); afterEach(function tearDown() { - app.reset(); - promise.setDefaultFlow(new promise.ControlFlow); - assert.deepEqual([], uncaughtExceptions, - 'Did not expect any uncaught exceptions'); - promise.LONG_STACK_TRACES = false; + 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.rejected(reason); - p.catch(function() {}); + var p = Promise.reject(reason); + p.catch(function() {}); // Silence unhandled rejection handlers. return p; } - it('testCanDetectPromiseLikeObjects', function() { - assertIsPromise(new promise.Promise(function(fulfill) { - fulfill(); - })); - assertIsPromise(new promise.Deferred().promise); - 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()); - }); - }); + 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)); + }); - describe('finally', function() { - it('nonFailingCallbackDoesNotSuppressOriginalError', function() { - var done = callbackHelper(assertIsStubError); - return promise.rejected(new StubError). - finally(function() {}). - catch(done). - finally(done.assertCalled); + 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()); + }); }); - it('failingCallbackSuppressesOriginalError', function() { - var done = callbackHelper(assertIsStubError); - return promise.rejected(new Error('original')). - finally(throwStubError). - catch(done). - finally(done.assertCalled); - }); + describe('finally', function() { + it('nonFailingCallbackDoesNotSuppressOriginalError', function() { + var done = callbackHelper(assertIsStubError); + return promise.rejected(new StubError). + finally(function() {}). + catch(done). + finally(done.assertCalled); + }); - it('callbackThrowsAfterFulfilledPromise', function() { - var done = callbackHelper(assertIsStubError); - return promise.fulfilled(). - finally(throwStubError). - 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('callbackReturnsRejectedPromise', function() { - var done = callbackHelper(assertIsStubError); - return promise.fulfilled(). - finally(function() { - return promise.rejected(new StubError); - }). - catch(done). - finally(done.assertCalled); - }); - }); + it('callbackThrowsAfterFulfilledPromise', function() { + var done = callbackHelper(assertIsStubError); + return promise.fulfilled(). + finally(throwStubError). + 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); + it('callbackReturnsRejectedPromise', function() { + var done = callbackHelper(assertIsStubError); + return promise.fulfilled(). + finally(function() { + return promise.rejected(new StubError); + }). + catch(done). + finally(done.assertCalled); }); - d.promise.cancel('because i said so'); - return res; }); - describe('can cancel original promise from its child;', function() { - it('child created by then()', function() { + describe('cancel', function() { + it('passesTheCancellationReasonToReject', function() { var d = new promise.Deferred(); - var p = d.promise.then(assert.fail, function(e) { + var res = 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)); + d.promise.cancel('because i said so'); + return res; }); - 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 Thenable', function() { - function noop() {} + 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; + }); - class FakeThenable { - constructor(p) { - this.promise = p; - } + p.cancel('because i said so'); + return p.then(v => assert.equal(123, v)); + }); - cancel(reason) { - this.promise.cancel(reason); - } + 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'); - then(cb, eb) { - let result = this.promise.then(cb, eb); - return new FakeThenable(result); - } - } - promise.Thenable.addImplementation(FakeThenable); + return parent.promise.then( + () => assert.fail('expected a cancellation'), + e => { + assert.ok(e instanceof promise.CancellationError); + assert.equal('all done', e.message); + }); + }); - let root = new promise.Promise(noop); - let thenable = new FakeThenable(root); - assert.ok(promise.Thenable.isImplementation(thenable)); + it('grand child through thenable chain', function() { + let p = new promise.Promise(function() {/* never resolve*/}); - let child = new promise.Promise(resolve => resolve(thenable)); - assert.ok(child instanceof promise.Promise); - child.cancel('stop!'); + let noop = function() {}; + let gc = p.then(noop).then(noop).then(noop); + gc.cancel('stop!'); - function assertStopped(p) { return p.then( - () => assert.fail('not stopped!'), + () => assert.fail('expected to be cancelled'), (e) => { assert.ok(e instanceof promise.CancellationError); assert.equal('stop!', e.message); }); - } + }); - return assertStopped(child).then(() => assertStopped(root)); - }); - }); + it('grand child through thenable chain started at resolve', function() { + function noop() {} - it('canCancelATimeout', function() { - var p = promise.delayed(25) - .then(assert.fail, (e) => e instanceof promise.CancellationError); - setTimeout(() => p.cancel(), 20); - p.cancel(); - return p; - }); + 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'); - 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)); - }); + return parent.promise.then( + () => assert.fail('expected a cancellation'), + e => { + assert.ok(e instanceof promise.CancellationError); + assert.equal('all done', e.message); + }); + }); - it('cancelIsANoopOnceAPromiseHasBeenRejected', function() { - var p = promise.rejected(new StubError); - p.cancel(); + it('"parent" is a CancellableThenable', function() { + function noop() {} - var pair = callbackPair(null, assertIsStubError); - return p.then(assert.fail, assertIsStubError); - }); + class FakeThenable { + constructor(p) { + this.promise = p; + } - it('noopCancelTriggeredOnCallbackOfResolvedPromise', function() { - var d = promise.defer(); - var p = d.promise.then(); + cancel(reason) { + this.promise.cancel(reason); + } - d.fulfill(); - p.cancel(); // This should not throw. - return p; // This should not trigger a failure. - }); - }); + 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); + }); + } - describe('when', function() { - it('ReturnsAResolvedPromiseIfGivenANonPromiseValue', function() { - var ret = promise.when('abc'); - assertIsPromise(ret); - return ret.then((value) => assert.equal('abc', value)); - }); + return assertStopped(child).then(() => assertStopped(root)); + }); + }); - it('PassesRawErrorsToCallbacks', function() { - var error = new Error('boo!'); - return promise.when(error, function(value) { - assert.equal(error, value); + 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('WaitsForValueToBeResolvedBeforeInvokingCallback', function() { - var d = new promise.Deferred(), callback; - let result = promise.when(d.promise, callback = callbackHelper(function(value) { - assert.equal('hi', value); - })); - callback.assertNotCalled(); - d.fulfill('hi'); - return result.then(callback.assertCalled); - }); - }); + it('can cancel timeout from grandchild', function() { + }); - it('firesUncaughtExceptionEventIfRejectionNeverHandled', function() { - promise.rejected(new StubError); - var handler = callbackHelper(assertIsStubError); + it('cancelIsANoopOnceAPromiseHasBeenFulfilled', function() { + var p = promise.fulfilled(123); + p.cancel(); + return p.then((v) => assert.equal(123, v)); + }); - // so tearDown() doesn't throw - app.removeAllListeners(); - app.on(promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION, handler); + it('cancelIsANoopOnceAPromiseHasBeenRejected', function() { + var p = promise.rejected(new StubError); + p.cancel(); - return NativePromise.resolve() - // Macro yield so the uncaught exception has a chance to trigger. - .then(() => new NativePromise(resolve => setTimeout(resolve, 0))) - .then(handler.assertCalled); - }); + var pair = callbackPair(null, assertIsStubError); + return p.then(assert.fail, assertIsStubError); + }); - it('cannotResolveADeferredWithItself', function() { - var deferred = new promise.Deferred(); - assert.throws(() => deferred.fulfill(deferred)); - assert.throws(() => deferred.reject(deferred)); - }); + it('noopCancelTriggeredOnCallbackOfResolvedPromise', function() { + var d = promise.defer(); + var p = d.promise.then(); - 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)); + d.fulfill(); + p.cancel(); // This should not throw. + return p; // This should not trigger a failure. + }); }); + }); - 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); + promiseManagerSuite(() => { + describe('when', function() { + it('ReturnsAResolvedPromiseIfGivenANonPromiseValue', function() { + var ret = promise.when('abc'); + assertIsPromise(ret); + return ret.then((value) => assert.equal('abc', value)); }); - }); - 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('PassesRawErrorsToCallbacks', function() { + var error = new Error('boo!'); + return promise.when(error, function(value) { + assert.equal(error, value); + }); + }); - it('arrayWithPromisedPrimitive', function() { - return promise.fullyResolved([promise.fulfilled(123)]) - .then(function(resolved) { - assert.deepEqual([123], resolved); - }); + 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); + }); }); - it('promiseResolvesToPrimitive', function() { - return promise.fullyResolved(promise.fulfilled(123)) - .then((resolved) => assert.equal(123, resolved)); - }); + 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('promiseResolvesToArray', function() { - var fn = function() {}; - var array = [true, [fn, null, 123], '', undefined]; - var aPromise = promise.fulfilled(array); + 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); + }); + }); - 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('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('promiseResolvesToArrayWithPromises', function() { - var nestedPromise = promise.fulfilled(123); - var aPromise = promise.fulfilled([true, nestedPromise]); - return promise.fullyResolved(aPromise) - .then(function(resolved) { - assert.deepEqual([true, 123], resolved); - }); - }); + it('arrayWithPromisedPrimitive', function() { + return promise.fullyResolved([Promise.resolve(123)]) + .then(function(resolved) { + assert.deepEqual([123], resolved); + }); + }); - it('rejectsIfArrayPromiseRejects', function() { - var nestedPromise = createRejectedPromise(new StubError); - var aPromise = promise.fulfilled([true, nestedPromise]); + it('promiseResolvesToPrimitive', function() { + return promise.fullyResolved(Promise.resolve(123)) + .then((resolved) => assert.equal(123, resolved)); + }); - var pair = callbackPair(null, assertIsStubError); - return promise.fullyResolved(aPromise) - .then(assert.fail, assertIsStubError); - }); + 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('rejectsOnFirstArrayRejection', function() { - var e1 = new Error('foo'); - var e2 = new Error('bar'); - var aPromise = promise.fulfilled([ - createRejectedPromise(e1), - createRejectedPromise(e2) - ]); - - return promise.fullyResolved(aPromise) - .then(assert.fail, function(error) { - assert.strictEqual(e1, error); - }); - }); + 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('rejectsIfNestedArrayPromiseRejects', function() { - var aPromise = promise.fulfilled([ - promise.fulfilled([ - createRejectedPromise(new StubError) - ]) - ]); + it('rejectsIfArrayPromiseRejects', function() { + var nestedPromise = createRejectedPromise(new StubError); + var aPromise = Promise.resolve([true, nestedPromise]); - return promise.fullyResolved(aPromise) - .then(assert.fail, assertIsStubError); - }); + var pair = callbackPair(null, assertIsStubError); + 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('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('nestedHash', function() { - var nestedHash = {'foo':'bar'}; - var hash = {'a': 123, 'b': nestedHash}; + it('rejectsIfNestedArrayPromiseRejects', function() { + var aPromise = Promise.resolve([ + Promise.resolve([ + createRejectedPromise(new StubError) + ]) + ]); - return promise.fullyResolved(hash) - .then(function(resolved) { - assert.strictEqual(hash, resolved); - assert.deepEqual({'a': 123, 'b': {'foo': 'bar'}}, resolved); - assert.strictEqual(nestedHash, resolved['b']); - }); - }); + return promise.fullyResolved(aPromise) + .then(assert.fail, assertIsStubError); + }); - it('promiseResolvesToSimpleHash', function() { - var hash = {'a': 123}; - var aPromise = promise.fulfilled(hash); + it('simpleHash', function() { + var hash = {'a': 123}; + return promise.fullyResolved(hash) + .then(function(resolved) { + assert.strictEqual(hash, resolved); + assert.deepEqual(hash, {'a': 123}); + }); + }); - return promise.fullyResolved(aPromise) - .then((resolved) => assert.strictEqual(hash, resolved)); - }); + it('nestedHash', function() { + var nestedHash = {'foo':'bar'}; + var hash = {'a': 123, 'b': nestedHash}; - it('promiseResolvesToNestedHash', function() { - var nestedHash = {'foo':'bar'}; - var hash = {'a': 123, 'b': nestedHash}; - var aPromise = promise.fulfilled(hash); + return promise.fullyResolved(hash) + .then(function(resolved) { + assert.strictEqual(hash, resolved); + assert.deepEqual({'a': 123, 'b': {'foo': 'bar'}}, resolved); + assert.strictEqual(nestedHash, resolved['b']); + }); + }); - 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('promiseResolvesToSimpleHash', function() { + var hash = {'a': 123}; + var aPromise = Promise.resolve(hash); - it('promiseResolvesToHashWithPromises', function() { - var aPromise = promise.fulfilled({ - 'a': promise.fulfilled(123) + return promise.fullyResolved(aPromise) + .then((resolved) => assert.strictEqual(hash, resolved)); }); - return promise.fullyResolved(aPromise) - .then(function(resolved) { - assert.deepEqual({'a': 123}, resolved); - }); - }); + it('promiseResolvesToNestedHash', function() { + var nestedHash = {'foo':'bar'}; + var hash = {'a': 123, 'b': nestedHash}; + var aPromise = Promise.resolve(hash); - it('rejectsIfHashPromiseRejects', function() { - var aPromise = promise.fulfilled({ - 'a': createRejectedPromise(new StubError) + return promise.fullyResolved(aPromise) + .then(function(resolved) { + assert.strictEqual(hash, resolved); + assert.strictEqual(nestedHash, resolved['b']); + assert.deepEqual(hash, {'a': 123, 'b': {'foo': 'bar'}}); + }); }); - return promise.fullyResolved(aPromise) - .then(assert.fail, assertIsStubError); - }); + it('promiseResolvesToHashWithPromises', function() { + var aPromise = Promise.resolve({ + 'a': Promise.resolve(123) + }); - it('rejectsIfNestedHashPromiseRejects', function() { - var aPromise = promise.fulfilled({ - 'a': {'b': createRejectedPromise(new StubError)} + return promise.fullyResolved(aPromise) + .then(function(resolved) { + assert.deepEqual({'a': 123}, resolved); + }); }); - return promise.fullyResolved(aPromise) - .then(assert.fail, assertIsStubError); - }); - - it('instantiatedObject', function() { - function Foo() { - this.bar = 'baz'; - } - var foo = new Foo; + it('rejectsIfHashPromiseRejects', function() { + var aPromise = Promise.resolve({ + 'a': createRejectedPromise(new StubError) + }); - return promise.fullyResolved(foo).then(function(resolvedFoo) { - assert.equal(foo, resolvedFoo); - assert.ok(resolvedFoo instanceof Foo); - assert.deepEqual(new Foo, resolvedFoo); + return promise.fullyResolved(aPromise) + .then(assert.fail, assertIsStubError); }); - }); - it('withEmptyArray', function() { - return promise.fullyResolved([]).then(function(resolved) { - assert.deepEqual([], resolved); - }); - }); + it('rejectsIfNestedHashPromiseRejects', function() { + var aPromise = Promise.resolve({ + 'a': {'b': createRejectedPromise(new StubError)} + }); - it('withEmptyHash', function() { - return promise.fullyResolved({}).then(function(resolved) { - assert.deepEqual({}, resolved); + return promise.fullyResolved(aPromise) + .then(assert.fail, assertIsStubError); }); - }); - it('arrayWithPromisedHash', function() { - var obj = {'foo': 'bar'}; - var array = [promise.fulfilled(obj)]; + it('instantiatedObject', function() { + function Foo() { + this.bar = 'baz'; + } + var foo = new Foo; - return promise.fullyResolved(array).then(function(resolved) { - assert.deepEqual(resolved, [obj]); + return promise.fullyResolved(foo).then(function(resolvedFoo) { + assert.equal(foo, resolvedFoo); + assert.ok(resolvedFoo instanceof Foo); + assert.deepEqual(new Foo, resolvedFoo); + }); }); - }); - }); - describe('checkedNodeCall', function() { - it('functionThrows', function() { - return promise.checkedNodeCall(throwStubError) - .then(assert.fail, assertIsStubError); - }); + it('withEmptyArray', function() { + return promise.fullyResolved([]).then(function(resolved) { + assert.deepEqual([], resolved); + }); + }); - it('functionReturnsAnError', function() { - return promise.checkedNodeCall(function(callback) { - callback(new StubError); - }).then(assert.fail, assertIsStubError); - }); + it('withEmptyHash', function() { + return promise.fullyResolved({}).then(function(resolved) { + assert.deepEqual({}, resolved); + }); + }); - it('functionReturnsSuccess', function() { - var success = 'success!'; - return promise.checkedNodeCall(function(callback) { - callback(null, success); - }).then((value) => assert.equal(success, value)); - }); + it('arrayWithPromisedHash', function() { + var obj = {'foo': 'bar'}; + var array = [Promise.resolve(obj)]; - 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)); + return promise.fullyResolved(array).then(function(resolved) { + assert.deepEqual(resolved, [obj]); + }); + }); }); - }); - describe('all', function() { - it('(base case)', function() { - let defer = [promise.defer(), promise.defer()]; - var a = [ - 0, 1, - defer[0].promise, - defer[1].promise, - 4, 5, 6 - ]; - delete a[5]; + describe('checkedNodeCall', function() { + it('functionThrows', function() { + return promise.checkedNodeCall(throwStubError) + .then(assert.fail, assertIsStubError); + }); - var pair = callbackPair(function(value) { - assert.deepEqual([0, 1, 2, 3, 4, undefined, 6], value); + it('functionReturnsAnError', function() { + return promise.checkedNodeCall(function(callback) { + callback(new StubError); + }).then(assert.fail, assertIsStubError); }); - var result = promise.all(a).then(pair.callback, pair.errback); - pair.assertNeither(); + it('functionReturnsSuccess', function() { + var success = 'success!'; + return promise.checkedNodeCall(function(callback) { + callback(null, success); + }).then((value) => assert.equal(success, value)); + }); - defer[0].fulfill(2); - pair.assertNeither(); + 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)); + }); - defer[1].fulfill(3); - return result.then(() => pair.assertCallback()); + 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)); + }); }); - it('empty array', function() { - return promise.all([]).then((a) => assert.deepEqual([], a)); - }); + 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]; - it('usesFirstRejection', function() { - let defer = [promise.defer(), promise.defer()]; - let a = [defer[0].promise, defer[1].promise]; + var pair = callbackPair(function(value) { + assert.deepEqual([0, 1, 2, 3, 4, undefined, 6], value); + }); - var result = promise.all(a).then(assert.fail, assertIsStubError); - defer[1].reject(new StubError); - setTimeout(() => defer[0].reject(Error('ignored')), 0); - return result; - }); - }); + var result = promise.all(a).then(pair.callback, pair.errback); + pair.assertNeither(); + + deferredObjs[0].resolve(2); + pair.assertNeither(); - 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); + deferredObjs[1].resolve(3); + return result.then(() => pair.assertCallback()); }); - }); - it('omitsDeleted', function() { - var a = [0, 1, 2, 3, 4, 5, 6]; - delete a[1]; - delete a[3]; - delete a[4]; - delete a[6]; + it('empty array', function() { + return promise.all([]).then((a) => assert.deepEqual([], a)); + }); - var expected = [0, 1, 4, 9, 16, 25, 36]; - delete expected[1]; - delete expected[3]; - delete expected[4]; - delete expected[6]; + it('usesFirstRejection', function() { + let deferredObjs = [defer(), defer()]; + let a = [deferredObjs[0].promise, deferredObjs[1].promise]; - return promise.map(a, function(value) { - return value * value; - }).then(function(value) { - assert.deepEqual(expected, value); + var result = promise.all(a).then(assert.fail, assertIsStubError); + deferredObjs[1].reject(new StubError); + setTimeout(() => deferredObjs[0].reject(Error('ignored')), 0); + return result; }); }); - it('emptyArray', function() { - return promise.map([], function(value) { - return value + 1; - }).then(function(value) { - assert.deepEqual([], value); + 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('inputIsPromise', function() { - var input = promise.defer(); - var result = promise.map(input.promise, function(value) { - return value + 1; + 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); + }); }); - var pair = callbackPair(function(value) { - assert.deepEqual([2, 3, 4], value); + it('emptyArray', function() { + return promise.map([], function(value) { + return value + 1; + }).then(function(value) { + assert.deepEqual([], value); + }); }); - result = result.then(pair.callback, pair.errback); - setTimeout(function() { - pair.assertNeither(); - input.fulfill([1, 2, 3]); - }, 10); + it('inputIsPromise', function() { + var input = defer(); + var result = promise.map(input.promise, function(value) { + return value + 1; + }); - return result; - }); + var pair = callbackPair(function(value) { + assert.deepEqual([2, 3, 4], value); + }); + result = result.then(pair.callback, pair.errback); - it('waitsForFunctionResultToResolve', function() { - var innerResults = [ - promise.defer(), - promise.defer() - ]; + setTimeout(function() { + pair.assertNeither(); + input.resolve([1, 2, 3]); + }, 10); - var result = promise.map([1, 2], function(value, index) { - return innerResults[index].promise; + return result; }); - var pair = callbackPair(function(value) { - assert.deepEqual(['a', 'b'], value); - }); - result = result.then(pair.callback, pair.errback); + it('waitsForFunctionResultToResolve', function() { + var innerResults = [ + defer(), + defer() + ]; - return NativePromise.resolve() - .then(function() { - pair.assertNeither(); - innerResults[0].fulfill('a'); - }) - .then(function() { - pair.assertNeither(); - innerResults[1].fulfill('b'); - return result; - }) - .then(pair.assertCallback); - }); - - it('rejectsPromiseIfFunctionThrows', function() { - return promise.map([1], throwStubError) - .then(assert.fail, assertIsStubError); - }); + var result = promise.map([1, 2], function(value, index) { + return innerResults[index].promise; + }); - it('rejectsPromiseIfFunctionReturnsRejectedPromise', function() { - return promise.map([1], function() { - return promise.rejected(new StubError); - }).then(assert.fail, assertIsStubError); - }); + 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('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('rejectsPromiseIfFunctionThrows', function() { + return promise.map([1], throwStubError) + .then(assert.fail, assertIsStubError); }); - }); - it('rejectsWithFirstRejectedPromise', function() { - var innerResult = [ - promise.fulfilled(), - 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('rejectsPromiseIfFunctionReturnsRejectedPromise', function() { + return promise.map([1], function() { + return createRejectedPromise(new StubError); + }).then(assert.fail, assertIsStubError); }); - }); - it('preservesOrderWhenMapReturnsPromise', function() { - var deferreds = [ - promise.defer(), - promise.defer(), - promise.defer(), - promise.defer() - ]; - var result = promise.map(deferreds, function(value) { - return value.promise; + 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); + }); }); - var pair = callbackPair(function(value) { - assert.deepEqual([0, 1, 2, 3], value); + 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); + }); }); - result = result.then(pair.callback, pair.errback); - return NativePromise.resolve() - .then(function() { - pair.assertNeither(); - for (let i = deferreds.length; i > 0; i -= 1) { - deferreds[i - 1].fulfill(i - 1); - } - return result; - }).then(pair.assertCallback); - }); - }); + it('preservesOrderWhenMapReturnsPromise', function() { + var deferreds = [ + defer(), + defer(), + defer(), + defer() + ]; + var result = promise.map(deferreds, function(value) { + return value.promise; + }); - 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); + 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); }); }); - 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); + 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('preservesInputs', function() { - var a = [0, 1, 2, 3]; + it('omitsDeleted', function() { + var a = [0, 1, 2, 3, 4, 5, 6]; + delete a[3]; + delete a[4]; - 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); + return promise.filter(a, function(value) { + return value > 1 && value < 6; + }).then(function(val) { + assert.deepEqual([2, 5], val); + }); }); - }); - it('inputIsPromise', function() { - var input = promise.defer(); - var result = promise.filter(input.promise, function(value) { - return value > 1 && value < 3; + 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); + }); }); - var pair = callbackPair(function(value) { - assert.deepEqual([2], value); + 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); }); - result = result.then(pair.callback, pair.errback); - return NativePromise.resolve() - .then(function() { - pair.assertNeither(); - input.fulfill([1, 2, 3]); - return result; - }) - .then(pair.assertCallback); - }); - it('waitsForFunctionResultToResolve', function() { - var innerResults = [ - promise.defer(), - promise.defer() - ]; + it('waitsForFunctionResultToResolve', function() { + var innerResults = [ + defer(), + defer() + ]; - var result = promise.filter([1, 2], function(value, index) { - return innerResults[index].promise; - }); + var result = promise.filter([1, 2], function(value, index) { + return innerResults[index].promise; + }); - var pair = callbackPair(function(value) { - assert.deepEqual([2], value); + 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); }); - result = result.then(pair.callback, pair.errback); - return NativePromise.resolve() - .then(function() { - pair.assertNeither(); - innerResults[0].fulfill(false); - }) - .then(function() { - pair.assertNeither(); - innerResults[1].fulfill(true); - return result; - }) - .then(pair.assertCallback); - }); - it('rejectsPromiseIfFunctionReturnsRejectedPromise', function() { - return promise.filter([1], function() { - return promise.rejected(new StubError); - }).then(assert.fail, assertIsStubError); - }); + 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('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.fulfilled(), - 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('rejectsWithFirstRejectedPromise', function() { + var innerResult = [ + Promise.resolve(), + createRejectedPromise(new StubError), + createRejectedPromise(Error('should be ignored')) + ]; - it('preservesOrderWhenFilterReturnsPromise', function() { - var deferreds = [ - promise.defer(), - promise.defer(), - promise.defer(), - promise.defer() - ]; - var result = promise.filter([0, 1, 2, 3], function(value, index) { - return deferreds[index].promise; + return promise.filter([1, 2, 3, 4], function(value, index) { + assert.ok(index < innerResult.length); + return innerResult[index]; + }).then(assert.fail, assertIsStubError); }); - var pair = callbackPair(function(value) { - assert.deepEqual([1, 2], value); - }); - result = result.then(pair.callback, pair.errback); + it('preservesOrderWhenFilterReturnsPromise', function() { + var deferreds = [ + defer(), + defer(), + defer(), + defer() + ]; + var result = promise.filter([0, 1, 2, 3], function(value, index) { + return deferreds[index].promise; + }); - return NativePromise.resolve() - .then(function() { - pair.assertNeither(); - for (let i = deferreds.length - 1; i >= 0; i -= 1) { - deferreds[i].fulfill(i > 0 && i < 3); - } - return result; - }).then(pair.assertCallback); + 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); + }); }); }); - 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())); - }); - - describe('testLongStackTraces', function() { - beforeEach(() => promise.LONG_STACK_TRACES = false); - afterEach(() => promise.LONG_STACK_TRACES = false); + enablePromiseManager(() => { + it('firesUncaughtExceptionEventIfRejectionNeverHandled', function() { + promise.rejected(new StubError); + var handler = callbackHelper(assertIsStubError); - it('doesNotAppendStackIfFeatureDisabled', function() { - promise.LONG_STACK_TRACES = false; + // so tearDown() doesn't throw + app.removeAllListeners(); + app.on(promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION, handler); - 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); - }); + return NativePromise.resolve() + // Macro yield so the uncaught exception has a chance to trigger. + .then(() => new NativePromise(resolve => setTimeout(resolve, 0))) + .then(handler.assertCalled); }); - function getStackMessages(error) { - return error.stack.split(/\n/).filter(function(line) { - return /^From: /.test(line); + 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); + }); }); - } - it('appendsInitialPromiseCreation_resolverThrows', function() { - promise.LONG_STACK_TRACES = true; + function getStackMessages(error) { + return error.stack.split(/\n/).filter(function(line) { + return /^From: /.test(line); + }); + } - var error = Error('hello'); - var originalStack = '(placeholder; will be overwritten later)'; + it('appendsInitialPromiseCreation_resolverThrows', function() { + promise.LONG_STACK_TRACES = true; - 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)); + 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; + it('appendsInitialPromiseCreation_rejectCalled', function() { + promise.LONG_STACK_TRACES = true; - var error = Error('hello'); - var originalStack = error.stack; + 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)); + 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; + it('appendsEachStepToRejectionError', function() { + promise.LONG_STACK_TRACES = true; - var error = Error('hello'); - var originalStack = '(placeholder; will be overwritten later)'; + 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)); + 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('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())); + }); }); diff --git a/node_modules/selenium-webdriver/test/lib/until_test.js b/node_modules/selenium-webdriver/test/lib/until_test.js index d3e81ec18..31b2b32ad 100644 --- a/node_modules/selenium-webdriver/test/lib/until_test.js +++ b/node_modules/selenium-webdriver/test/lib/until_test.js @@ -84,7 +84,7 @@ describe('until', function() { it('byWebElementPromise', function() { executor.on(CommandName.SWITCH_TO_FRAME, () => true); var el = new webdriver.WebElementPromise(driver, - promise.fulfilled(new webdriver.WebElement(driver, {ELEMENT: 1234}))); + Promise.resolve(new webdriver.WebElement(driver, {ELEMENT: 1234}))); return driver.wait(until.ableToSwitchToFrame(el), 100); }); diff --git a/node_modules/selenium-webdriver/test/lib/webdriver_test.js b/node_modules/selenium-webdriver/test/lib/webdriver_test.js index d2d87ca3e..0a124530e 100644 --- a/node_modules/selenium-webdriver/test/lib/webdriver_test.js +++ b/node_modules/selenium-webdriver/test/lib/webdriver_test.js @@ -29,6 +29,8 @@ const Key = require('../../lib/input').Key; const logging = require('../../lib/logging'); const Session = require('../../lib/session').Session; const promise = require('../../lib/promise'); +const {enablePromiseManager, promiseManagerSuite} = require('../../lib/test/promise'); +const until = require('../../lib/until'); const Alert = require('../../lib/webdriver').Alert; const AlertPromise = require('../../lib/webdriver').AlertPromise; const UnhandledAlertError = require('../../lib/webdriver').UnhandledAlertError; @@ -73,6 +75,9 @@ describe('WebDriver', function() { }); afterEach(function tearDown() { + if (!promise.USE_PROMISE_MANAGER) { + return; + } return waitForIdle(flow).then(function() { assert.deepEqual([], uncaughtExceptions); flow.reset(); @@ -83,7 +88,19 @@ describe('WebDriver', function() { uncaughtExceptions.push(e); } + function defer() { + let d = {}; + let promise = new Promise((resolve, reject) => { + Object.assign(d, {resolve, reject}); + }); + d.promise = promise; + return d; + } + function waitForIdle(opt_flow) { + if (!promise.USE_PROMISE_MANAGER) { + return Promise.resolve(); + } var theFlow = opt_flow || flow; return new Promise(function(fulfill, reject) { if (theFlow.isIdle()) { @@ -300,19 +317,21 @@ describe('WebDriver', function() { return waitForIdle(driver.controlFlow()); }); - it('canAttachInCustomFlow', function() { - let executor = new FakeExecutor(). - expect(CName.DESCRIBE_SESSION). - withParameters({'sessionId': SESSION_ID}). - andReturnSuccess({}). - end(); + enablePromiseManager(() => { + it('canAttachInCustomFlow', function() { + let executor = new FakeExecutor(). + expect(CName.DESCRIBE_SESSION). + withParameters({'sessionId': SESSION_ID}). + andReturnSuccess({}). + end(); - var otherFlow = new promise.ControlFlow(); - var driver = WebDriver.attachToSession(executor, SESSION_ID, otherFlow); - assert.equal(otherFlow, driver.controlFlow()); - assert.notEqual(otherFlow, promise.controlFlow()); + var otherFlow = new promise.ControlFlow(); + var driver = WebDriver.attachToSession(executor, SESSION_ID, otherFlow); + assert.equal(otherFlow, driver.controlFlow()); + assert.notEqual(otherFlow, promise.controlFlow()); - return waitForIdle(otherFlow); + return waitForIdle(otherFlow); + }); }); }); @@ -345,7 +364,7 @@ describe('WebDriver', function() { return driver.getSession().then(v => assert.strictEqual(v, aSession)); }); - it('handles desired and requried capabilities', function() { + it('handles desired and required capabilities', function() { let aSession = new Session(SESSION_ID, {'browserName': 'firefox'}); let executor = new FakeExecutor(). expect(CName.NEW_SESSION). @@ -374,6 +393,23 @@ describe('WebDriver', function() { return driver.getSession().then(fail, assertIsStubError); }); + it('invokes quit callback if it fails to create a session', function() { + let called = false; + let executor = new FakeExecutor() + .expect(CName.NEW_SESSION) + .withParameters({'desiredCapabilities': {'browserName': 'firefox'}}) + .andReturnError(new StubError()) + .end(); + + var driver = + WebDriver.createSession(executor, {'browserName': 'firefox'}, + null, null, () => called = true); + return driver.getSession().then(fail, err => { + assert.ok(called); + assertIsStubError(err); + }); + }); + it('usesActiveFlowByDefault', function() { let executor = new FakeExecutor(). expect(CName.NEW_SESSION). @@ -387,26 +423,60 @@ describe('WebDriver', function() { return waitForIdle(driver.controlFlow()); }); - it('canCreateInCustomFlow', function() { - let executor = new FakeExecutor(). - expect(CName.NEW_SESSION). - withParameters({'desiredCapabilities': {}}). - andReturnSuccess({}). - end(); + enablePromiseManager(() => { + it('canCreateInCustomFlow', function() { + let executor = new FakeExecutor(). + expect(CName.NEW_SESSION). + withParameters({'desiredCapabilities': {}}). + andReturnSuccess({}). + end(); - var otherFlow = new promise.ControlFlow(); - var driver = WebDriver.createSession(executor, {}, otherFlow); - assert.equal(otherFlow, driver.controlFlow()); - assert.notEqual(otherFlow, promise.controlFlow()); + var otherFlow = new promise.ControlFlow(); + var driver = WebDriver.createSession(executor, {}, otherFlow); + assert.equal(otherFlow, driver.controlFlow()); + assert.notEqual(otherFlow, promise.controlFlow()); + + return waitForIdle(otherFlow); + }); - return waitForIdle(otherFlow); + describe('creation failures bubble up in control flow', function() { + function runTest(...args) { + let executor = new FakeExecutor() + .expect(CName.NEW_SESSION) + .withParameters({'desiredCapabilities': {'browserName': 'firefox'}}) + .andReturnError(new StubError()) + .end(); + + WebDriver.createSession( + executor, {'browserName': 'firefox'}, ...args); + return waitForAbort().then(assertIsStubError); + } + + it('no onQuit callback', () => runTest()); + it('has onQuit callback', () => runTest(null, null, function() {})); + + it('onQuit callback failure suppress creation failure', function() { + let e = new Error('hi!'); + let executor = new FakeExecutor() + .expect(CName.NEW_SESSION) + .withParameters({'desiredCapabilities': {'browserName': 'firefox'}}) + .andReturnError(new StubError()) + .end(); + + WebDriver.createSession( + executor, {'browserName': 'firefox'}, null, null, + () => {throw e}); + return waitForAbort().then(err => assert.strictEqual(err, e)); + }); + }); }); }); it('testDoesNotExecuteCommandIfSessionDoesNotResolve', function() { var session = Promise.reject(new StubError); - new FakeExecutor().createDriver(session).getTitle(); - return waitForAbort().then(assertIsStubError); + return new FakeExecutor().createDriver(session) + .getTitle() + .then(_ => assert.fail('should have failed'), assertIsStubError); }); it('testCommandReturnValuesArePassedToFirstCallback', function() { @@ -415,9 +485,8 @@ describe('WebDriver', function() { end(); var driver = executor.createDriver(); - return driver.getTitle().then(function(title) { - assert.equal('Google Search', title); - }); + return driver.getTitle() + .then(title => assert.equal('Google Search', title)); }); it('testStopsCommandExecutionWhenAnErrorOccurs', function() { @@ -431,11 +500,11 @@ describe('WebDriver', function() { andReturnError(e). end(); - var driver = executor.createDriver(); - driver.switchTo().window('foo'); - driver.getTitle(); // mock should blow if this gets executed - - return waitForAbort().then(v => assert.strictEqual(v, e)); + let driver = executor.createDriver(); + return driver.switchTo().window('foo') + .then( + _ => driver.getTitle(), // mock should blow if this gets executed + v => assert.strictEqual(v, e)); }); it('testCanSuppressCommandFailures', function() { @@ -469,8 +538,9 @@ describe('WebDriver', function() { andReturnError(e). end(); - executor.createDriver().switchTo().window('foo'); - return waitForAbort().then(v => assert.strictEqual(v, e)); + return executor.createDriver() + .switchTo().window('foo') + .then(_ => assert.fail(), v => assert.strictEqual(v, e)); }); it('testErrbacksThatReturnErrorsStillSwitchToCallbackChain', function() { @@ -486,7 +556,7 @@ describe('WebDriver', function() { var driver = executor.createDriver(); return driver.switchTo().window('foo'). catch(function() { return new StubError; }); - then(assertIsStubError); + then(assertIsStubError, () => assert.fail()); }); it('testErrbacksThrownCanOverrideOriginalError', function() { @@ -499,27 +569,9 @@ describe('WebDriver', function() { end(); var driver = executor.createDriver(); - driver.switchTo().window('foo').catch(throwStubError); - - return waitForAbort().then(assertIsStubError); - }); - - it('testCannotScheduleCommandsIfTheSessionIdHasBeenDeleted', function() { - var driver = new FakeExecutor().createDriver(); - delete driver.session_; - assert.throws(() => driver.get('http://www.google.com')); - }); - - it('testDeletesSessionIdAfterQuitting', function() { - var driver; - let executor = new FakeExecutor(). - expect(CName.QUIT). - end(); - - driver = executor.createDriver(); - return driver.quit().then(function() { - assert.equal(void 0, driver.session_); - }); + return driver.switchTo().window('foo') + .catch(throwStubError) + .then(assert.fail, assertIsStubError); }); it('testReportsErrorWhenExecutingCommandsAfterExecutingAQuit', function() { @@ -527,14 +579,15 @@ describe('WebDriver', function() { expect(CName.QUIT). end(); - var driver = executor.createDriver(); - driver.quit(); - driver.get('http://www.google.com'); - return waitForAbort(). - then(expectedError( - error.NoSuchSessionError, - 'This driver instance does not have a valid session ID ' + - '(did you call WebDriver.quit()?) and may no longer be used.')); + let verifyError = expectedError( + error.NoSuchSessionError, + 'This driver instance does not have a valid session ID ' + + '(did you call WebDriver.quit()?) and may no longer be used.'); + + let driver = executor.createDriver(); + return driver.quit() + .then(_ => driver.get('http://www.google.com')) + .then(assert.fail, verifyError); }); it('testCallbackCommandsExecuteBeforeNextCommand', function() { @@ -556,114 +609,29 @@ describe('WebDriver', function() { return waitForIdle(); }); - it('testEachCallbackFrameRunsToCompletionBeforeTheNext', function() { - let executor = new FakeExecutor(). - expect(CName.GET_TITLE). - expect(CName.GET_CURRENT_URL). - expect(CName.GET_CURRENT_WINDOW_HANDLE). - expect(CName.CLOSE). - expect(CName.QUIT). - end(); - - var driver = executor.createDriver(); - driver.getTitle(). - // Everything in this callback... - then(function() { - driver.getCurrentUrl(); - driver.getWindowHandle(); - }). - // ...should execute before everything in this callback. - then(function() { - driver.close(); - }); - // This should execute after everything above - driver.quit(); - - return waitForIdle(); - }); - - describe('nestedCommandFailures', function() { - it('bubbleUpToGlobalHandlerIfUnsuppressed', function() { - let e = new error.NoSuchWindowError('window not found'); - let executor = new FakeExecutor(). - expect(CName.GET_TITLE). - expect(CName.SWITCH_TO_WINDOW, { - 'name': 'foo', - 'handle': 'foo' - }). - andReturnError(e). - end(); - - var driver = executor.createDriver(); - driver.getTitle().then(function() { - driver.switchTo().window('foo'); - }); - - return waitForAbort().then(v => assert.strictEqual(v, e)); - }); - - it('canBeSuppressWhenTheyOccur', function() { + enablePromiseManager(() => { + it('testEachCallbackFrameRunsToCompletionBeforeTheNext', function() { let executor = new FakeExecutor(). expect(CName.GET_TITLE). - expect(CName.SWITCH_TO_WINDOW, { - 'name': 'foo', - 'handle': 'foo' - }). - andReturnError(new error.NoSuchWindowError('window not found')). + expect(CName.GET_CURRENT_URL). + expect(CName.GET_CURRENT_WINDOW_HANDLE). expect(CName.CLOSE). - end(); - - var driver = executor.createDriver(); - driver.getTitle().then(function() { - driver.switchTo().window('foo').catch(function() {}); - }); - driver.close(); - - return waitForIdle(); - }); - - it('bubbleUpThroughTheFrameStack', function() { - let e = new error.NoSuchWindowError('window not found'); - let executor = new FakeExecutor(). - expect(CName.GET_TITLE). - expect(CName.SWITCH_TO_WINDOW, { - 'name': 'foo', - 'handle': 'foo' - }). - andReturnError(e). + expect(CName.QUIT). end(); var driver = executor.createDriver(); driver.getTitle(). + // Everything in this callback... then(function() { - return driver.switchTo().window('foo'); - }). - catch(v => assert.strictEqual(v, e)); - - return waitForIdle(); - }); - - it('canBeCaughtAndSuppressed', function() { - let executor = new FakeExecutor(). - expect(CName.GET_TITLE). - expect(CName.GET_CURRENT_URL). - expect(CName.SWITCH_TO_WINDOW, { - 'name': 'foo', - 'handle': 'foo' + driver.getCurrentUrl(); + driver.getWindowHandle(); }). - andReturnError(new StubError()). - expect(CName.CLOSE). - end(); - - var driver = executor.createDriver(); - driver.getTitle().then(function() { - driver.getCurrentUrl(). - then(function() { - return driver.switchTo().window('foo'); - }). - catch(function() {}); - driver.close(); - }); + // ...should execute before everything in this callback. + then(function() { + driver.close(); + }); + // This should execute after everything above + driver.quit(); return waitForIdle(); }); @@ -678,18 +646,16 @@ describe('WebDriver', function() { end(); var driver = executor.createDriver(); - driver.getTitle(). + return driver.getTitle(). then(function() { return driver.getCurrentUrl(); }). then(function(value) { assert.equal('http://www.google.com', value); }); - return waitForIdle(); }); it('fromAnErrbackSuppressesTheError', function() { - var count = 0; let executor = new FakeExecutor(). expect(CName.SWITCH_TO_WINDOW, { 'name': 'foo', @@ -701,19 +667,12 @@ describe('WebDriver', function() { end(); var driver = executor.createDriver(); - driver.switchTo().window('foo'). + return driver.switchTo().window('foo'). catch(function(e) { assertIsStubError(e); - count += 1; return driver.getCurrentUrl(); }). - then(function(url) { - count += 1; - assert.equal('http://www.google.com', url); - }); - return waitForIdle().then(function() { - assert.equal(2, count); - }); + then(url => assert.equal('http://www.google.com', url)); }); }); @@ -725,23 +684,25 @@ describe('WebDriver', function() { }); }); - it('executionOrderWithCustomFunctions', function() { - var msg = []; - let executor = new FakeExecutor(). - expect(CName.GET_TITLE).andReturnSuccess('cheese '). - expect(CName.GET_CURRENT_URL).andReturnSuccess('tasty'). - end(); + enablePromiseManager(() => { + it('executionOrderWithCustomFunctions', function() { + var msg = []; + let executor = new FakeExecutor(). + expect(CName.GET_TITLE).andReturnSuccess('cheese '). + expect(CName.GET_CURRENT_URL).andReturnSuccess('tasty'). + end(); - var driver = executor.createDriver(); + var driver = executor.createDriver(); - var pushMsg = msg.push.bind(msg); - driver.getTitle().then(pushMsg); - driver.call(() => 'is ').then(pushMsg); - driver.getCurrentUrl().then(pushMsg); - driver.call(() => '!').then(pushMsg); + var pushMsg = msg.push.bind(msg); + driver.getTitle().then(pushMsg); + driver.call(() => 'is ').then(pushMsg); + driver.getCurrentUrl().then(pushMsg); + driver.call(() => '!').then(pushMsg); - return waitForIdle().then(function() { - assert.equal('cheese is tasty!', msg.join('')); + return waitForIdle().then(function() { + assert.equal('cheese is tasty!', msg.join('')); + }); }); }); @@ -756,7 +717,7 @@ describe('WebDriver', function() { }); it('passingPromisedArgumentsToACustomFunction', function() { - var promisedArg = promise.fulfilled(2); + var promisedArg = Promise.resolve(2); var add = function(a, b) { return a + b; }; @@ -834,30 +795,32 @@ describe('WebDriver', function() { }).then(fail, assertIsStubError); }); - it('doesNotCompleteUntilReturnedPromiseIsResolved', function() { - var order = []; - var driver = new FakeExecutor().createDriver(); + enablePromiseManager(() => { + it('doesNotCompleteUntilReturnedPromiseIsResolved', function() { + var order = []; + var driver = new FakeExecutor().createDriver(); - var d = promise.defer(); - d.promise.then(function() { - order.push('b'); - }); + var d = promise.defer(); + d.promise.then(function() { + order.push('b'); + }); - driver.call(function() { - order.push('a'); - return d.promise; - }); - driver.call(function() { - order.push('c'); - }); + driver.call(function() { + order.push('a'); + return d.promise; + }); + driver.call(function() { + order.push('c'); + }); - // timeout to ensure the first function starts its execution before we - // trigger d's callbacks. - return new Promise(f => setTimeout(f, 0)).then(function() { - assert.deepEqual(['a'], order); - d.fulfill(); - return waitForIdle().then(function() { - assert.deepEqual(['a', 'b', 'c'], order); + // timeout to ensure the first function starts its execution before we + // trigger d's callbacks. + return new Promise(f => setTimeout(f, 0)).then(function() { + assert.deepEqual(['a'], order); + d.fulfill(); + return waitForIdle().then(function() { + assert.deepEqual(['a', 'b', 'c'], order); + }); }); }); }); @@ -878,36 +841,58 @@ describe('WebDriver', function() { }); describe('nestedCommands', function() { - it('commandExecutionOrder', function() { - var msg = []; - var driver = new FakeExecutor().createDriver(); - driver.call(msg.push, msg, 'a'); - driver.call(function() { - driver.call(msg.push, msg, 'c'); + enablePromiseManager(() => { + it('commandExecutionOrder', function() { + var msg = []; + var driver = new FakeExecutor().createDriver(); + driver.call(msg.push, msg, 'a'); driver.call(function() { - driver.call(msg.push, msg, 'e'); - driver.call(msg.push, msg, 'f'); + driver.call(msg.push, msg, 'c'); + driver.call(function() { + driver.call(msg.push, msg, 'e'); + driver.call(msg.push, msg, 'f'); + }); + driver.call(msg.push, msg, 'd'); + }); + driver.call(msg.push, msg, 'b'); + return waitForIdle().then(function() { + assert.equal('acefdb', msg.join('')); }); - driver.call(msg.push, msg, 'd'); - }); - driver.call(msg.push, msg, 'b'); - return waitForIdle().then(function() { - assert.equal('acefdb', msg.join('')); }); - }); - it('basicUsage', function() { - var msg = []; - var driver = new FakeExecutor().createDriver(); - var pushMsg = msg.push.bind(msg); - driver.call(() => 'cheese ').then(pushMsg); - driver.call(function() { - driver.call(() => 'is ').then(pushMsg); - driver.call(() => 'tasty').then(pushMsg); + it('basicUsage', function() { + var msg = []; + var driver = new FakeExecutor().createDriver(); + var pushMsg = msg.push.bind(msg); + driver.call(() => 'cheese ').then(pushMsg); + driver.call(function() { + driver.call(() => 'is ').then(pushMsg); + driver.call(() => 'tasty').then(pushMsg); + }); + driver.call(() => '!').then(pushMsg); + return waitForIdle().then(function() { + assert.equal('cheese is tasty!', msg.join('')); + }); }); - driver.call(() => '!').then(pushMsg); - return waitForIdle().then(function() { - assert.equal('cheese is tasty!', msg.join('')); + + it('normalCommandAfterNestedCommandThatReturnsAnAction', function() { + var msg = []; + let executor = new FakeExecutor(). + expect(CName.CLOSE). + end(); + var driver = executor.createDriver(); + driver.call(function() { + return driver.call(function() { + msg.push('a'); + return driver.call(() => 'foobar'); + }); + }); + driver.close().then(function() { + msg.push('b'); + }); + return waitForIdle().then(function() { + assert.equal('ab', msg.join('')); + }); }); }); @@ -922,26 +907,6 @@ describe('WebDriver', function() { }); }); - it('normalCommandAfterNestedCommandThatReturnsAnAction', function() { - var msg = []; - let executor = new FakeExecutor(). - expect(CName.CLOSE). - end(); - var driver = executor.createDriver(); - driver.call(function() { - return driver.call(function() { - msg.push('a'); - return driver.call(() => 'foobar'); - }); - }); - driver.close().then(function() { - msg.push('b'); - }); - return waitForIdle().then(function() { - assert.equal('ab', msg.join('')); - }); - }); - it('errorsBubbleUp_caught', function() { var driver = new FakeExecutor().createDriver(); var result = driver.call(function() { @@ -954,12 +919,12 @@ describe('WebDriver', function() { it('errorsBubbleUp_uncaught', function() { var driver = new FakeExecutor().createDriver(); - driver.call(function() { + return driver.call(function() { return driver.call(function() { return driver.call(throwStubError); }); - }); - return waitForAbort().then(assertIsStubError); + }) + .then(_ => assert.fail('should have failed'), assertIsStubError); }); it('canScheduleCommands', function() { @@ -980,29 +945,27 @@ describe('WebDriver', function() { }); describe('WebElementPromise', function() { + let driver = new FakeExecutor().createDriver(); + it('resolvesWhenUnderlyingElementDoes', function() { - var el = new WebElement({}, {'ELEMENT': 'foo'}); - return new WebElementPromise({}, promise.fulfilled(el)).then(function(e) { - assert.strictEqual(e, el); - }); + let el = new WebElement(driver, {'ELEMENT': 'foo'}); + return new WebElementPromise(driver, Promise.resolve(el)) + .then(e => assert.strictEqual(e, el)); }); it('resolvesBeforeCallbacksOnWireValueTrigger', function() { - var el = new promise.Deferred(); + var el = defer(); - var element = new WebElementPromise({}, el.promise); + var element = new WebElementPromise(driver, el.promise); var messages = []; - element.then(function() { - messages.push('element resolved'); - }); - element.getId().then(function() { - messages.push('wire value resolved'); - }); + let steps = [ + element.then(_ => messages.push('element resolved')), + element.getId().then(_ => messages.push('wire value resolved')) + ]; - assert.deepEqual([], messages); - el.fulfill(new WebElement({}, {'ELEMENT': 'foo'})); - return waitForIdle().then(function() { + el.resolve(new WebElement(driver, {'ELEMENT': 'foo'})); + return Promise.all(steps).then(function() { assert.deepEqual([ 'element resolved', 'wire value resolved' @@ -1011,7 +974,8 @@ describe('WebDriver', function() { }); it('isRejectedIfUnderlyingIdIsRejected', function() { - var element = new WebElementPromise({}, promise.rejected(new StubError)); + let element = + new WebElementPromise(driver, Promise.reject(new StubError)); return element.then(fail, assertIsStubError); }); }); @@ -1207,7 +1171,7 @@ describe('WebDriver', function() { it('failsIfArgumentIsARejectedPromise', function() { let executor = new FakeExecutor(); - var arg = promise.rejected(new StubError); + var arg = Promise.reject(new StubError); arg.catch(function() {}); // Suppress default handler. var driver = executor.createDriver(); @@ -1218,7 +1182,7 @@ describe('WebDriver', function() { describe('executeAsyncScript', function() { it('failsIfArgumentIsARejectedPromise', function() { - var arg = promise.rejected(new StubError); + var arg = Promise.reject(new StubError); arg.catch(function() {}); // Suppress default handler. var driver = new FakeExecutor().createDriver(); @@ -1236,9 +1200,8 @@ describe('WebDriver', function() { end(); var driver = executor.createDriver(); - var element = driver.findElement(By.id('foo')); - element.click(); // This should never execute. - return waitForAbort().then(assertIsStubError); + return driver.findElement(By.id('foo')) + .then(assert.fail, assertIsStubError); }); it('elementNotFoundInACallback', function() { @@ -1249,11 +1212,9 @@ describe('WebDriver', function() { end(); var driver = executor.createDriver(); - promise.fulfilled().then(function() { - var element = driver.findElement(By.id('foo')); - return element.click(); // Should not execute. - }); - return waitForAbort().then(assertIsStubError); + return Promise.resolve() + .then(_ => driver.findElement(By.id('foo'))) + .then(assert.fail, assertIsStubError); }); it('elementFound', function() { @@ -1261,7 +1222,7 @@ describe('WebDriver', function() { expect(CName.FIND_ELEMENT, {using: 'css selector', value: '*[id="foo"]'}). andReturnSuccess(WebElement.buildId('bar')). - expect(CName.CLICK_ELEMENT, {'id': 'bar'}). + expect(CName.CLICK_ELEMENT, {'id': WebElement.buildId('bar')}). andReturnSuccess(). end(); @@ -1276,7 +1237,7 @@ describe('WebDriver', function() { expect(CName.FIND_ELEMENT, {using: 'css selector', value: '*[id="foo"]'}). andReturnSuccess(WebElement.buildId('bar')). - expect(CName.CLICK_ELEMENT, {'id': 'bar'}). + expect(CName.CLICK_ELEMENT, {'id': WebElement.buildId('bar')}). andReturnSuccess(). end(); @@ -1294,7 +1255,7 @@ describe('WebDriver', function() { 'args': [] }). andReturnSuccess(WebElement.buildId('bar')). - expect(CName.CLICK_ELEMENT, {'id': 'bar'}). + expect(CName.CLICK_ELEMENT, {'id': WebElement.buildId('bar')}). end(); var driver = executor.createDriver(); @@ -1310,12 +1271,12 @@ describe('WebDriver', function() { end(); var driver = executor.createDriver(); - var element = driver.findElement(By.js('return 123')); - element.click(); // Should not execute. - return waitForAbort().then(function(e) { - assertIsInstance(TypeError, e); - assert.equal('Custom locator did not return a WebElement', e.message); - }); + return driver.findElement(By.js('return 123')) + .then(assert.fail, function(e) { + assertIsInstance(TypeError, e); + assert.equal( + 'Custom locator did not return a WebElement', e.message); + }); }); it('byJs_canPassArguments', function() { @@ -1325,7 +1286,7 @@ describe('WebDriver', function() { 'script': script, 'args': ['div'] }). - andReturnSuccess({'ELEMENT':'one'}). + andReturnSuccess(WebElement.buildId('one')). end(); var driver = executor.createDriver(); driver.findElement(By.js(script, 'div')); @@ -1338,7 +1299,7 @@ describe('WebDriver', function() { andReturnSuccess([ WebElement.buildId('foo'), WebElement.buildId('bar')]). - expect(CName.CLICK_ELEMENT, {'id': 'foo'}). + expect(CName.CLICK_ELEMENT, {'id': WebElement.buildId('foo')}). andReturnSuccess(). end(); @@ -1347,19 +1308,17 @@ describe('WebDriver', function() { assert.equal(driver, d); return d.findElements(By.tagName('a')); }); - element.click(); - return waitForIdle(); + return element.click(); }); it('customLocatorThrowsIfresultIsNotAWebElement', function() { var driver = new FakeExecutor().createDriver(); - driver.findElement(function() { - return 1; - }); - return waitForAbort().then(function(e) { - assertIsInstance(TypeError, e); - assert.equal('Custom locator did not return a WebElement', e.message); - }); + return driver.findElement(_ => 1) + .then(assert.fail, function(e) { + assertIsInstance(TypeError, e); + assert.equal( + 'Custom locator did not return a WebElement', e.message); + }); }); }); @@ -1486,7 +1445,8 @@ describe('WebDriver', function() { it('convertsVarArgsIntoStrings_simpleArgs', function() { let executor = new FakeExecutor(). expect(CName.SEND_KEYS_TO_ELEMENT, - {'id': 'one', 'value':'12abc3'.split('')}). + {'id': WebElement.buildId('one'), + 'value':'12abc3'.split('')}). andReturnSuccess(). end(); @@ -1502,16 +1462,17 @@ describe('WebDriver', function() { {'using':'css selector', 'value':'*[id="foo"]'}). andReturnSuccess(WebElement.buildId('one')). expect(CName.SEND_KEYS_TO_ELEMENT, - {'id':'one', 'value':'abc123def'.split('')}). + {'id':WebElement.buildId('one'), + 'value':'abc123def'.split('')}). andReturnSuccess(). end(); var driver = executor.createDriver(); var element = driver.findElement(By.id('foo')); - element.sendKeys( - promise.fulfilled('abc'), 123, - promise.fulfilled('def')); - return waitForIdle(); + return element.sendKeys( + Promise.resolve('abc'), + 123, + Promise.resolve('def')); }); it('sendKeysWithAFileDetector', function() { @@ -1520,7 +1481,8 @@ describe('WebDriver', function() { {'using':'css selector', 'value':'*[id="foo"]'}). andReturnSuccess(WebElement.buildId('one')). expect(CName.SEND_KEYS_TO_ELEMENT, - {'id': 'one', 'value':'modified/path'.split('')}). + {'id': WebElement.buildId('one'), + 'value':'modified/path'.split('')}). andReturnSuccess(). end(); @@ -1528,13 +1490,11 @@ describe('WebDriver', function() { let handleFile = function(d, path) { assert.strictEqual(driver, d); assert.equal(path, 'original/path'); - return promise.fulfilled('modified/path'); + return Promise.resolve('modified/path'); }; driver.setFileDetector({handleFile}); - var element = driver.findElement(By.id('foo')); - element.sendKeys('original/', 'path'); - return waitForIdle(); + return driver.findElement(By.id('foo')).sendKeys('original/', 'path'); }); }); @@ -1554,7 +1514,7 @@ describe('WebDriver', function() { return waitForIdle(); }); - it('should propogate exceptions', function() { + it('should propagate exceptions', function() { let e = new error.NoSuchWindowError('window not found'); let executor = new FakeExecutor(). expect(CName.SWITCH_TO_WINDOW). @@ -1565,21 +1525,21 @@ describe('WebDriver', function() { andReturnError(e). end(); - executor.createDriver().switchTo().window('foo'); - return waitForAbort().then(v => assert.strictEqual(v, e)); + return executor.createDriver() + .switchTo().window('foo') + .then(assert.fail, v => assert.strictEqual(v, e)); }); }); }); describe('elementEquality', function() { it('isReflexive', function() { - var a = new WebElement({}, 'foo'); + var a = new WebElement(new FakeExecutor().createDriver(), 'foo'); return WebElement.equals(a, a).then(assert.ok); }); it('failsIfAnInputElementCouldNotBeFound', function() { - var id = promise.rejected(new StubError); - id.catch(function() {}); // Suppress default handler. + let id = Promise.reject(new StubError); var driver = new FakeExecutor().createDriver(); var a = new WebElement(driver, 'foo'); @@ -1590,71 +1550,134 @@ describe('WebDriver', function() { }); describe('waiting', function() { - it('waitSucceeds', function() { - let executor = new FakeExecutor(). - expect(CName.FIND_ELEMENTS, - {using: 'css selector', value: '*[id="foo"]'}). - andReturnSuccess([]). - times(2). - expect(CName.FIND_ELEMENTS, - {using: 'css selector', value: '*[id="foo"]'}). - andReturnSuccess([WebElement.buildId('bar')]). - end(); + describe('supports custom wait functions', function() { + it('waitSucceeds', function() { + let executor = new FakeExecutor(). + expect(CName.FIND_ELEMENTS, + {using: 'css selector', value: '*[id="foo"]'}). + andReturnSuccess([]). + times(2). + expect(CName.FIND_ELEMENTS, + {using: 'css selector', value: '*[id="foo"]'}). + andReturnSuccess([WebElement.buildId('bar')]). + end(); - var driver = executor.createDriver(); - driver.wait(function() { - return driver.findElements(By.id('foo')).then(els => els.length > 0); - }, 200); - return waitForIdle(); + var driver = executor.createDriver(); + driver.wait(function() { + return driver.findElements(By.id('foo')).then(els => els.length > 0); + }, 200); + return waitForIdle(); + }); + + it('waitTimesout_timeoutCaught', function() { + let executor = new FakeExecutor(). + expect(CName.FIND_ELEMENTS, + {using: 'css selector', value: '*[id="foo"]'}). + andReturnSuccess([]). + anyTimes(). + end(); + + var driver = executor.createDriver(); + return driver.wait(function() { + return driver.findElements(By.id('foo')).then(els => els.length > 0); + }, 25).then(fail, function(e) { + assert.equal('Wait timed out after ', + e.message.substring(0, 'Wait timed out after '.length)); + }); + }); + + enablePromiseManager(() => { + it('waitTimesout_timeoutNotCaught', function() { + let executor = new FakeExecutor(). + expect(CName.FIND_ELEMENTS, + {using: 'css selector', value: '*[id="foo"]'}). + andReturnSuccess([]). + anyTimes(). + end(); + + var driver = executor.createDriver(); + driver.wait(function() { + return driver.findElements(By.id('foo')).then(els => els.length > 0); + }, 25); + return waitForAbort().then(function(e) { + assert.equal('Wait timed out after ', + e.message.substring(0, 'Wait timed out after '.length)); + }); + }); + }); }); - it('waitTimesout_timeoutCaught', function() { - let executor = new FakeExecutor(). - expect(CName.FIND_ELEMENTS, - {using: 'css selector', value: '*[id="foo"]'}). - andReturnSuccess([]). - anyTimes(). - end(); + describe('supports condition objects', function() { + it('wait succeeds', function() { + let executor = new FakeExecutor() + .expect(CName.FIND_ELEMENTS, + {using: 'css selector', value: '*[id="foo"]'}) + .andReturnSuccess([]) + .times(2) + .expect(CName.FIND_ELEMENTS, + {using: 'css selector', value: '*[id="foo"]'}) + .andReturnSuccess([WebElement.buildId('bar')]) + .end(); - var driver = executor.createDriver(); - return driver.wait(function() { - return driver.findElements(By.id('foo')).then(els => els.length > 0); - }, 25).then(fail, function(e) { - assert.equal('Wait timed out after ', - e.message.substring(0, 'Wait timed out after '.length)); + let driver = executor.createDriver(); + return driver.wait(until.elementLocated(By.id('foo')), 200); + }); + + it('wait times out', function() { + let executor = new FakeExecutor() + .expect(CName.FIND_ELEMENTS, + {using: 'css selector', value: '*[id="foo"]'}) + .andReturnSuccess([]) + .anyTimes() + .end(); + + let driver = executor.createDriver(); + return driver.wait(until.elementLocated(By.id('foo')), 5) + .then(fail, err => assert.ok(err instanceof error.TimeoutError)); }); }); - it('waitTimesout_timeoutNotCaught', function() { - let executor = new FakeExecutor(). - expect(CName.FIND_ELEMENTS, - {using: 'css selector', value: '*[id="foo"]'}). - andReturnSuccess([]). - anyTimes(). - end(); + describe('supports promise objects', function() { + it('wait succeeds', function() { + let promise = new Promise(resolve => { + setTimeout(() => resolve(1), 10); + }); - var driver = executor.createDriver(); - driver.wait(function() { - return driver.findElements(By.id('foo')).then(els => els.length > 0); - }, 25); - return waitForAbort().then(function(e) { - assert.equal('Wait timed out after ', - e.message.substring(0, 'Wait timed out after '.length)); + let driver = new FakeExecutor().createDriver(); + return driver.wait(promise, 200).then(v => assert.equal(v, 1)); + }); + + it('wait times out', function() { + let promise = new Promise(resolve => {/* never resolves */}); + + let driver = new FakeExecutor().createDriver(); + return driver.wait(promise, 5) + .then(fail, err => assert.ok(err instanceof error.TimeoutError)); + }); + + it('wait fails if promise is rejected', function() { + let err = Error('boom'); + let driver = new FakeExecutor().createDriver(); + return driver.wait(Promise.reject(err), 5) + .then(fail, e => assert.strictEqual(e, err)); }); }); + + it('fails if not supported condition type provided', function() { + let driver = new FakeExecutor().createDriver(); + assert.throws(() => driver.wait({}, 5), TypeError); + }); }); describe('alert handling', function() { it('alertResolvesWhenPromisedTextResolves', function() { - var deferredText = new promise.Deferred(); + let driver = new FakeExecutor().createDriver(); + let deferredText = defer(); - var alert = new AlertPromise({}, deferredText.promise); - assert.ok(alert.isPending()); + let alert = new AlertPromise(driver, deferredText.promise); - deferredText.fulfill(new Alert({}, 'foo')); - return alert.getText().then(function(text) { - assert.equal('foo', text); - }); + deferredText.resolve(new Alert(driver, 'foo')); + return alert.getText().then(text => assert.equal(text, 'foo')); }); it('cannotSwitchToAlertThatIsNotPresent', function() { @@ -1664,32 +1687,33 @@ describe('WebDriver', function() { .andReturnError(e) .end(); - var driver = executor.createDriver(); - var alert = driver.switchTo().alert(); - alert.dismiss(); // Should never execute. - return waitForAbort().then(v => assert.strictEqual(v, e)); + return executor.createDriver() + .switchTo().alert() + .then(assert.fail, v => assert.strictEqual(v, e)); }); - it('alertsBelongToSameFlowAsParentDriver', function() { - let executor = new FakeExecutor() - .expect(CName.GET_ALERT_TEXT).andReturnSuccess('hello') - .end(); + enablePromiseManager(() => { + it('alertsBelongToSameFlowAsParentDriver', function() { + let executor = new FakeExecutor() + .expect(CName.GET_ALERT_TEXT).andReturnSuccess('hello') + .end(); - var driver = executor.createDriver(); - var otherFlow = new promise.ControlFlow(); - otherFlow.execute(function() { - driver.switchTo().alert().then(function() { - assert.strictEqual( - driver.controlFlow(), promise.controlFlow(), - 'Alert should belong to the same flow as its parent driver'); + var driver = executor.createDriver(); + var otherFlow = new promise.ControlFlow(); + otherFlow.execute(function() { + driver.switchTo().alert().then(function() { + assert.strictEqual( + driver.controlFlow(), promise.controlFlow(), + 'Alert should belong to the same flow as its parent driver'); + }); }); - }); - assert.notEqual(otherFlow, driver.controlFlow); - return Promise.all([ - waitForIdle(otherFlow), - waitForIdle(driver.controlFlow()) - ]); + assert.notEqual(otherFlow, driver.controlFlow); + return Promise.all([ + waitForIdle(otherFlow), + waitForIdle(driver.controlFlow()) + ]); + }); }); it('commandsFailIfAlertNotPresent', function() { @@ -1715,26 +1739,28 @@ describe('WebDriver', function() { }); }); - it('testWebElementsBelongToSameFlowAsParentDriver', function() { - let executor = new FakeExecutor() - .expect(CName.FIND_ELEMENT, - {using: 'css selector', value: '*[id="foo"]'}) - .andReturnSuccess(WebElement.buildId('abc123')) - .end(); + enablePromiseManager(() => { + it('testWebElementsBelongToSameFlowAsParentDriver', function() { + let executor = new FakeExecutor() + .expect(CName.FIND_ELEMENT, + {using: 'css selector', value: '*[id="foo"]'}) + .andReturnSuccess(WebElement.buildId('abc123')) + .end(); - var driver = executor.createDriver(); - var otherFlow = new promise.ControlFlow(); - otherFlow.execute(function() { - driver.findElement({id: 'foo'}).then(function() { - assert.equal(driver.controlFlow(), promise.controlFlow()); + var driver = executor.createDriver(); + var otherFlow = new promise.ControlFlow(); + otherFlow.execute(function() { + driver.findElement({id: 'foo'}).then(function() { + assert.equal(driver.controlFlow(), promise.controlFlow()); + }); }); - }); - assert.notEqual(otherFlow, driver.controlFlow); - return Promise.all([ - waitForIdle(otherFlow), - waitForIdle(driver.controlFlow()) - ]); + assert.notEqual(otherFlow, driver.controlFlow); + return Promise.all([ + waitForIdle(otherFlow), + waitForIdle(driver.controlFlow()) + ]); + }); }); it('testFetchingLogs', function() { @@ -1763,7 +1789,7 @@ describe('WebDriver', function() { }); it('testCommandsFailIfInitialSessionCreationFailed', function() { - var session = promise.rejected(new StubError); + var session = Promise.reject(new StubError); var driver = new FakeExecutor().createDriver(session); var navigateResult = driver.get('some-url').then(fail, assertIsStubError); @@ -1811,10 +1837,10 @@ describe('WebDriver', function() { describe('actions()', function() { it('failsIfInitialDriverCreationFailed', function() { - let session = promise.rejected(new StubError); - session.catch(function() {}); - return new FakeExecutor(). - createDriver(session). + let session = Promise.reject(new StubError('no session for you')); + let driver = new FakeExecutor().createDriver(session); + driver.getSession().catch(function() {}); + return driver. actions(). mouseDown(). mouseUp(). @@ -1900,10 +1926,10 @@ describe('WebDriver', function() { describe('touchActions()', function() { it('failsIfInitialDriverCreationFailed', function() { - let session = promise.rejected(new StubError); - session.catch(function() {}); - return new FakeExecutor(). - createDriver(session). + let session = Promise.reject(new StubError); + let driver = new FakeExecutor().createDriver(session); + driver.getSession().catch(function() {}); + return driver. touchActions(). scroll({x: 3, y: 4}). perform(). @@ -1937,8 +1963,8 @@ describe('WebDriver', function() { it('canUseGeneratorsWithWebDriverCall', function() { return driver.call(function* () { - var x = yield promise.fulfilled(1); - var y = yield promise.fulfilled(2); + var x = yield Promise.resolve(1); + var y = yield Promise.resolve(2); return x + y; }).then(function(value) { assert.deepEqual(3, value); @@ -1947,7 +1973,7 @@ describe('WebDriver', function() { it('canDefineScopeOnGeneratorCall', function() { return driver.call(function* () { - var x = yield promise.fulfilled(1); + var x = yield Promise.resolve(1); return this.name + x; }, {name: 'Bob'}).then(function(value) { assert.deepEqual('Bob1', value); @@ -1956,8 +1982,8 @@ describe('WebDriver', function() { it('canSpecifyArgsOnGeneratorCall', function() { return driver.call(function* (a, b) { - var x = yield promise.fulfilled(1); - var y = yield promise.fulfilled(2); + var x = yield Promise.resolve(1); + var y = yield Promise.resolve(2); return [x + y, a, b]; }, null, 'abc', 123).then(function(value) { assert.deepEqual([3, 'abc', 123], value); @@ -1992,6 +2018,8 @@ describe('WebDriver', function() { }); describe('wire format', function() { + const FAKE_DRIVER = new FakeExecutor().createDriver(); + describe('can serialize', function() { function runSerializeTest(input, want) { let executor = new FakeExecutor(). @@ -2035,14 +2063,15 @@ describe('WebDriver', function() { it('WebElement', function() { return runSerializeTest( - new WebElement({}, 'fefifofum'), + new WebElement(FAKE_DRIVER, 'fefifofum'), WebElement.buildId('fefifofum')); }); it('WebElementPromise', function() { return runSerializeTest( new WebElementPromise( - {}, promise.fulfilled(new WebElement({}, 'fefifofum'))), + FAKE_DRIVER, + Promise.resolve(new WebElement(FAKE_DRIVER, 'fefifofum'))), WebElement.buildId('fefifofum')); }); @@ -2053,14 +2082,15 @@ describe('WebDriver', function() { it('with WebElement', function() { return runSerializeTest( - [new WebElement({}, 'fefifofum')], + [new WebElement(FAKE_DRIVER, 'fefifofum')], [WebElement.buildId('fefifofum')]); }); it('with WebElementPromise', function() { return runSerializeTest( [new WebElementPromise( - {}, promise.fulfilled(new WebElement({}, 'fefifofum')))], + FAKE_DRIVER, + Promise.resolve(new WebElement(FAKE_DRIVER, 'fefifofum')))], [WebElement.buildId('fefifofum')]); }); @@ -2070,7 +2100,7 @@ describe('WebDriver', function() { [123, {'foo': 'bar'}] ]; - var element = new WebElement({}, 'fefifofum'); + var element = new WebElement(FAKE_DRIVER, 'fefifofum'); var input = ['abc', 123, true, element, [123, {'foo': 'bar'}]]; return runSerializeTest(input, expected); }); @@ -2114,7 +2144,7 @@ describe('WebDriver', function() { 'sessionId': 'foo' }; - var element = new WebElement({}, 'fefifofum'); + var element = new WebElement(FAKE_DRIVER, 'fefifofum'); var parameters = { 'script': 'return 1', 'args':['abc', 123, true, element, [123, {'foo': 'bar'}]], |