promise_error_test.js 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884
  1. // Licensed to the Software Freedom Conservancy (SFC) under one
  2. // or more contributor license agreements. See the NOTICE file
  3. // distributed with this work for additional information
  4. // regarding copyright ownership. The SFC licenses this file
  5. // to you under the Apache License, Version 2.0 (the
  6. // "License"); you may not use this file except in compliance
  7. // with the License. You may obtain a copy of the License at
  8. //
  9. // http://www.apache.org/licenses/LICENSE-2.0
  10. //
  11. // Unless required by applicable law or agreed to in writing,
  12. // software distributed under the License is distributed on an
  13. // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  14. // KIND, either express or implied. See the License for the
  15. // specific language governing permissions and limitations
  16. // under the License.
  17. /**
  18. * @fileoverview Contains tests against promise error handling. Many tests use
  19. * NativePromise to control test termination independent of promise
  20. * (and notably promise.ControlFlow).
  21. */
  22. 'use strict';
  23. const testutil = require('./testutil');
  24. const assert = require('assert');
  25. const promise = require('../../lib/promise');
  26. const {enablePromiseManager} = require('../../lib/test/promise');
  27. const NativePromise = Promise;
  28. const StubError = testutil.StubError;
  29. const throwStubError = testutil.throwStubError;
  30. const assertIsStubError = testutil.assertIsStubError;
  31. describe('promise error handling', function() {
  32. enablePromiseManager(() => {
  33. var flow, uncaughtExceptions;
  34. beforeEach(function setUp() {
  35. if (promise.USE_PROMISE_MANAGER) {
  36. flow = promise.controlFlow();
  37. uncaughtExceptions = [];
  38. flow.on('uncaughtException', onUncaughtException);
  39. }
  40. });
  41. afterEach(function tearDown() {
  42. if (promise.USE_PROMISE_MANAGER) {
  43. return waitForIdle(flow).then(function() {
  44. assert.deepEqual(
  45. [], uncaughtExceptions, 'There were uncaught exceptions');
  46. flow.reset();
  47. });
  48. }
  49. });
  50. function onUncaughtException(e) {
  51. uncaughtExceptions.push(e);
  52. }
  53. function waitForAbort(opt_flow, opt_n) {
  54. var n = opt_n || 1;
  55. var theFlow = opt_flow || flow;
  56. theFlow.removeAllListeners(
  57. promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION);
  58. return new NativePromise(function(fulfill, reject) {
  59. theFlow.once('idle', function() {
  60. reject(Error('expected flow to report an unhandled error'));
  61. });
  62. var errors = [];
  63. theFlow.on('uncaughtException', onError);
  64. function onError(e) {
  65. errors.push(e);
  66. if (errors.length === n) {
  67. theFlow.removeListener('uncaughtException', onError);
  68. fulfill(n === 1 ? errors[0] : errors);
  69. }
  70. }
  71. });
  72. }
  73. function waitForIdle(opt_flow) {
  74. var theFlow = opt_flow || flow;
  75. return new NativePromise(function(fulfill, reject) {
  76. if (theFlow.isIdle()) {
  77. fulfill();
  78. return;
  79. }
  80. theFlow.once('idle', fulfill);
  81. theFlow.once('uncaughtException', reject);
  82. });
  83. }
  84. it('testRejectedPromiseTriggersErrorCallback', function() {
  85. return promise.rejected(new StubError).
  86. then(assert.fail, assertIsStubError);
  87. });
  88. describe('callback throws trigger subsequent error callback', function() {
  89. it('fulfilled promise', function() {
  90. return promise.fulfilled().
  91. then(throwStubError).
  92. then(assert.fail, assertIsStubError);
  93. });
  94. it('rejected promise', function() {
  95. var e = Error('not the droids you are looking for');
  96. return promise.rejected(e).
  97. then(assert.fail, throwStubError).
  98. then(assert.fail, assertIsStubError);
  99. });
  100. });
  101. describe('callback returns rejected promise triggers subsequent errback', function() {
  102. it('from fulfilled callback', function() {
  103. return promise.fulfilled().then(function() {
  104. return promise.rejected(new StubError);
  105. }).then(assert.fail, assertIsStubError);
  106. });
  107. it('from rejected callback', function() {
  108. var e = Error('not the droids you are looking for');
  109. return promise.rejected(e).
  110. then(assert.fail, function() {
  111. return promise.rejected(new StubError);
  112. }).
  113. then(assert.fail, assertIsStubError);
  114. });
  115. });
  116. it('testReportsUnhandledRejectionsThroughTheControlFlow', function() {
  117. promise.rejected(new StubError);
  118. return waitForAbort().then(assertIsStubError);
  119. });
  120. describe('multiple unhandled rejections outside a task', function() {
  121. it('are reported in order they occurred', function() {
  122. var e1 = Error('error 1');
  123. var e2 = Error('error 2');
  124. promise.rejected(e1);
  125. promise.rejected(e2);
  126. return waitForAbort(flow).then(function(error) {
  127. assert.ok(
  128. error instanceof promise.MultipleUnhandledRejectionError);
  129. // TODO: switch to Array.from when we drop node 0.12.x
  130. var errors = [];
  131. for (var e of error.errors) {
  132. errors.push(e);
  133. }
  134. assert.deepEqual([e1, e2], errors);
  135. });
  136. });
  137. });
  138. describe('does not report unhandled rejection when', function() {
  139. it('handler added before next tick', function() {
  140. promise.rejected(new StubError).then(assert.fail, assertIsStubError);
  141. return waitForIdle();
  142. });
  143. it('added async but before next tick', function() {
  144. var called = false;
  145. return new NativePromise(function(fulfill, reject) {
  146. var aPromise;
  147. NativePromise.resolve().then(function() {
  148. aPromise.then(assert.fail, function(e) {
  149. called = true;
  150. assertIsStubError(e);
  151. });
  152. waitForIdle().then(fulfill, reject);
  153. });
  154. aPromise = promise.rejected(new StubError);
  155. }).then(function() {
  156. assert.ok(called);
  157. })
  158. });
  159. });
  160. it('testTaskThrows', function() {
  161. return flow.execute(throwStubError).then(assert.fail, assertIsStubError);
  162. });
  163. it('testTaskReturnsRejectedPromise', function() {
  164. return flow.execute(function() {
  165. return promise.rejected(new StubError)
  166. }).then(assert.fail, assertIsStubError);
  167. });
  168. it('testTaskHasUnhandledRejection', function() {
  169. return flow.execute(function() {
  170. promise.rejected(new StubError)
  171. }).then(assert.fail, assertIsStubError);
  172. });
  173. it('testTaskfails_returnedPromiseIsUnhandled', function() {
  174. flow.execute(throwStubError);
  175. return waitForAbort().then(assertIsStubError);
  176. });
  177. it('testTaskHasUnhandledRejection_cancelsRemainingSubTasks', function() {
  178. var seen = [];
  179. flow.execute(function() {
  180. promise.rejected(new StubError);
  181. flow.execute(() => seen.push('a'))
  182. .then(() => seen.push('b'), (e) => seen.push(e));
  183. flow.execute(() => seen.push('c'))
  184. .then(() => seen.push('b'), (e) => seen.push(e));
  185. });
  186. return waitForAbort()
  187. .then(assertIsStubError)
  188. .then(() => assert.deepEqual([], seen));
  189. });
  190. describe('nested task failures', function() {
  191. it('returns up to paren', function() {
  192. return flow.execute(function() {
  193. return flow.execute(function() {
  194. return flow.execute(throwStubError);
  195. });
  196. }).then(assert.fail, assertIsStubError);
  197. });
  198. it('task throws; uncaught error bubbles up', function() {
  199. flow.execute(function() {
  200. flow.execute(function() {
  201. flow.execute(throwStubError);
  202. });
  203. });
  204. return waitForAbort().then(assertIsStubError);
  205. });
  206. it('task throws; uncaught error bubbles up; is caught at root', function() {
  207. flow.execute(function() {
  208. flow.execute(function() {
  209. flow.execute(throwStubError);
  210. });
  211. }).then(assert.fail, assertIsStubError);
  212. return waitForIdle();
  213. });
  214. it('unhandled rejection bubbles up', function() {
  215. flow.execute(function() {
  216. flow.execute(function() {
  217. flow.execute(function() {
  218. promise.rejected(new StubError);
  219. });
  220. });
  221. });
  222. return waitForAbort().then(assertIsStubError);
  223. });
  224. it('unhandled rejection bubbles up; caught at root', function() {
  225. flow.execute(function() {
  226. flow.execute(function() {
  227. promise.rejected(new StubError);
  228. });
  229. }).then(assert.fail, assertIsStubError);
  230. return waitForIdle();
  231. });
  232. it('mixtureof hanging and free subtasks', function() {
  233. flow.execute(function() {
  234. return flow.execute(function() {
  235. flow.execute(throwStubError);
  236. });
  237. });
  238. return waitForAbort().then(assertIsStubError);
  239. });
  240. it('cancels remaining tasks', function() {
  241. var seen = [];
  242. flow.execute(function() {
  243. flow.execute(() => promise.rejected(new StubError));
  244. flow.execute(() => seen.push('a'))
  245. .then(() => seen.push('b'), (e) => seen.push(e));
  246. flow.execute(() => seen.push('c'))
  247. .then(() => seen.push('b'), (e) => seen.push(e));
  248. });
  249. return waitForAbort()
  250. .then(assertIsStubError)
  251. .then(() => assert.deepEqual([], seen));
  252. });
  253. });
  254. it('testTaskReturnsPromiseLikeObjectThatInvokesErrback', function() {
  255. return flow.execute(function() {
  256. return {
  257. 'then': function(_, errback) {
  258. errback('abc123');
  259. }
  260. };
  261. }).then(assert.fail, function(value) {
  262. assert.equal('abc123', value);
  263. });
  264. });
  265. describe('ControlFlow#wait();', function() {
  266. describe('condition throws;', function() {
  267. it('failure is caught', function() {
  268. return flow.wait(throwStubError, 50).then(assert.fail, assertIsStubError);
  269. });
  270. it('failure is not caught', function() {
  271. flow.wait(throwStubError, 50);
  272. return waitForAbort().then(assertIsStubError);
  273. });
  274. });
  275. describe('condition returns promise', function() {
  276. it('failure is caught', function() {
  277. return flow.wait(function() {
  278. return promise.rejected(new StubError);
  279. }, 50).then(assert.fail, assertIsStubError);
  280. });
  281. it('failure is not caught', function() {
  282. flow.wait(function() {
  283. return promise.rejected(new StubError);
  284. }, 50);
  285. return waitForAbort().then(assertIsStubError);
  286. });
  287. });
  288. describe('condition has unhandled promise rejection', function() {
  289. it('failure is caught', function() {
  290. return flow.wait(function() {
  291. promise.rejected(new StubError);
  292. }, 50).then(assert.fail, assertIsStubError);
  293. });
  294. it('failure is not caught', function() {
  295. flow.wait(function() {
  296. promise.rejected(new StubError);
  297. }, 50);
  298. return waitForAbort().then(assertIsStubError);
  299. });
  300. });
  301. describe('condition has subtask failure', function() {
  302. it('failure is caught', function() {
  303. return flow.wait(function() {
  304. flow.execute(function() {
  305. flow.execute(throwStubError);
  306. });
  307. }, 50).then(assert.fail, assertIsStubError);
  308. });
  309. it('failure is not caught', function() {
  310. flow.wait(function() {
  311. flow.execute(function() {
  312. flow.execute(throwStubError);
  313. });
  314. }, 50);
  315. return waitForAbort().then(assertIsStubError);
  316. });
  317. });
  318. });
  319. describe('errback throws a new error', function() {
  320. it('start with normal promise', function() {
  321. var error = Error('an error');
  322. return promise.rejected(error).
  323. catch(function(e) {
  324. assert.equal(e, error);
  325. throw new StubError;
  326. }).
  327. catch(assertIsStubError);
  328. });
  329. it('start with task result', function() {
  330. var error = Error('an error');
  331. return flow.execute(function() {
  332. throw error;
  333. }).
  334. catch(function(e) {
  335. assert.equal(e, error);
  336. throw new StubError;
  337. }).
  338. catch(assertIsStubError);
  339. });
  340. it('start with normal promise; uncaught error', function() {
  341. var error = Error('an error');
  342. promise.rejected(error).
  343. catch(function(e) {
  344. assert.equal(e, error);
  345. throw new StubError;
  346. });
  347. return waitForAbort().then(assertIsStubError);
  348. });
  349. it('start with task result; uncaught error', function() {
  350. var error = Error('an error');
  351. flow.execute(function() {
  352. throw error;
  353. }).
  354. catch(function(e) {
  355. assert.equal(e, error);
  356. throw new StubError;
  357. });
  358. return waitForAbort().then(assertIsStubError);
  359. });
  360. });
  361. it('thrownPromiseCausesCallbackRejection', function() {
  362. let p = promise.fulfilled(1234);
  363. return promise.fulfilled().then(function() {
  364. throw p;
  365. }).then(assert.fail, function(value) {
  366. assert.strictEqual(p, value);
  367. });
  368. });
  369. describe('task throws promise', function() {
  370. it('promise was fulfilled', function() {
  371. var toThrow = promise.fulfilled(1234);
  372. flow.execute(function() {
  373. throw toThrow;
  374. }).then(assert.fail, function(value) {
  375. assert.equal(toThrow, value);
  376. return toThrow;
  377. }).then(function(value) {
  378. assert.equal(1234, value);
  379. });
  380. return waitForIdle();
  381. });
  382. it('promise was rejected', function() {
  383. var toThrow = promise.rejected(new StubError);
  384. toThrow.catch(function() {}); // For tearDown.
  385. flow.execute(function() {
  386. throw toThrow;
  387. }).then(assert.fail, function(e) {
  388. assert.equal(toThrow, e);
  389. return e;
  390. }).then(assert.fail, assertIsStubError);
  391. return waitForIdle();
  392. });
  393. });
  394. it('testFailsTaskIfThereIsAnUnhandledErrorWhileWaitingOnTaskResult', function() {
  395. var d = promise.defer();
  396. flow.execute(function() {
  397. promise.rejected(new StubError);
  398. return d.promise;
  399. }).then(assert.fail, assertIsStubError);
  400. return waitForIdle().then(function() {
  401. return d.promise;
  402. }).then(assert.fail, function(e) {
  403. assert.equal('CancellationError: StubError', e.toString());
  404. });
  405. });
  406. it('testFailsParentTaskIfAsyncScheduledTaskFails', function() {
  407. var d = promise.defer();
  408. flow.execute(function() {
  409. flow.execute(throwStubError);
  410. return d.promise;
  411. }).then(assert.fail, assertIsStubError);
  412. return waitForIdle().then(function() {
  413. return d.promise;
  414. }).then(assert.fail, function(e) {
  415. assert.equal('CancellationError: StubError', e.toString());
  416. });
  417. });
  418. describe('long stack traces', function() {
  419. afterEach(() => promise.LONG_STACK_TRACES = false);
  420. it('always includes task stacks in failures', function() {
  421. promise.LONG_STACK_TRACES = false;
  422. flow.execute(function() {
  423. flow.execute(function() {
  424. flow.execute(throwStubError, 'throw error');
  425. }, 'two');
  426. }, 'three').
  427. then(assert.fail, function(e) {
  428. assertIsStubError(e);
  429. if (typeof e.stack !== 'string') {
  430. return;
  431. }
  432. var messages = e.stack.split(/\n/).filter(function(line, index) {
  433. return /^From: /.test(line);
  434. });
  435. assert.deepEqual([
  436. 'From: Task: throw error',
  437. 'From: Task: two',
  438. 'From: Task: three'
  439. ], messages);
  440. });
  441. return waitForIdle();
  442. });
  443. it('does not include completed tasks', function () {
  444. flow.execute(function() {}, 'succeeds');
  445. flow.execute(throwStubError, 'kaboom').then(assert.fail, function(e) {
  446. assertIsStubError(e);
  447. if (typeof e.stack !== 'string') {
  448. return;
  449. }
  450. var messages = e.stack.split(/\n/).filter(function(line, index) {
  451. return /^From: /.test(line);
  452. });
  453. assert.deepEqual(['From: Task: kaboom'], messages);
  454. });
  455. return waitForIdle();
  456. });
  457. it('does not include promise chain when disabled', function() {
  458. promise.LONG_STACK_TRACES = false;
  459. flow.execute(function() {
  460. flow.execute(function() {
  461. return promise.fulfilled().
  462. then(function() {}).
  463. then(function() {}).
  464. then(throwStubError);
  465. }, 'eventually assert.fails');
  466. }, 'start').
  467. then(assert.fail, function(e) {
  468. assertIsStubError(e);
  469. if (typeof e.stack !== 'string') {
  470. return;
  471. }
  472. var messages = e.stack.split(/\n/).filter(function(line, index) {
  473. return /^From: /.test(line);
  474. });
  475. assert.deepEqual([
  476. 'From: Task: eventually assert.fails',
  477. 'From: Task: start'
  478. ], messages);
  479. });
  480. return waitForIdle();
  481. });
  482. it('includes promise chain when enabled', function() {
  483. promise.LONG_STACK_TRACES = true;
  484. flow.execute(function() {
  485. flow.execute(function() {
  486. return promise.fulfilled().
  487. then(function() {}).
  488. then(function() {}).
  489. then(throwStubError);
  490. }, 'eventually assert.fails');
  491. }, 'start').
  492. then(assert.fail, function(e) {
  493. assertIsStubError(e);
  494. if (typeof e.stack !== 'string') {
  495. return;
  496. }
  497. var messages = e.stack.split(/\n/).filter(function(line, index) {
  498. return /^From: /.test(line);
  499. });
  500. assert.deepEqual([
  501. 'From: Promise: then',
  502. 'From: Task: eventually assert.fails',
  503. 'From: Task: start'
  504. ], messages);
  505. });
  506. return waitForIdle();
  507. });
  508. });
  509. describe('frame cancels remaining tasks', function() {
  510. it('on unhandled task failure', function() {
  511. var run = false;
  512. return flow.execute(function() {
  513. flow.execute(throwStubError);
  514. flow.execute(function() { run = true; });
  515. }).then(assert.fail, function(e) {
  516. assertIsStubError(e);
  517. assert.ok(!run);
  518. });
  519. });
  520. it('on unhandled promise rejection', function() {
  521. var run = false;
  522. return flow.execute(function() {
  523. promise.rejected(new StubError);
  524. flow.execute(function() { run = true; });
  525. }).then(assert.fail, function(e) {
  526. assertIsStubError(e);
  527. assert.ok(!run);
  528. });
  529. });
  530. it('if task throws', function() {
  531. var run = false;
  532. return flow.execute(function() {
  533. flow.execute(function() { run = true; });
  534. throw new StubError;
  535. }).then(assert.fail, function(e) {
  536. assertIsStubError(e);
  537. assert.ok(!run);
  538. });
  539. });
  540. describe('task callbacks scheduled in another frame', function() {
  541. flow = promise.controlFlow();
  542. function noop() {}
  543. let subTask;
  544. before(function() {
  545. flow.execute(function() {
  546. // This task will be discarded and never run because of the error below.
  547. subTask = flow.execute(() => 'abc');
  548. throw new StubError('stub');
  549. }).catch(noop);
  550. });
  551. function assertCancellation(e) {
  552. assert.ok(e instanceof promise.CancellationError);
  553. assert.equal(
  554. 'Task was discarded due to a previous failure: stub', e.message);
  555. }
  556. it('are rejected with cancellation error', function() {
  557. let result;
  558. return Promise.resolve().then(function() {
  559. return flow.execute(function() {
  560. result = subTask.then(assert.fail);
  561. });
  562. })
  563. .then(() => result)
  564. .then(assert.fail, assertCancellation);
  565. });
  566. it('cancellation errors propagate through callbacks (1)', function() {
  567. let result;
  568. return Promise.resolve().then(function() {
  569. return flow.execute(function() {
  570. result = subTask
  571. .then(assert.fail, assertCancellation)
  572. .then(() => 'abc123');
  573. });
  574. })
  575. .then(() => result)
  576. .then(value => assert.equal('abc123', value));
  577. });
  578. it('cancellation errors propagate through callbacks (2)', function() {
  579. let result;
  580. return Promise.resolve().then(function() {
  581. return flow.execute(function() {
  582. result = subTask.then(assert.fail)
  583. .then(noop, assertCancellation)
  584. .then(() => 'fin');
  585. });
  586. })
  587. // Verify result actually computed successfully all the way through.
  588. .then(() => result)
  589. .then(value => assert.equal('fin', value));
  590. });
  591. });
  592. });
  593. it('testRegisteredTaskCallbacksAreDroppedWhenTaskIsCancelled_return', function() {
  594. var seen = [];
  595. return flow.execute(function() {
  596. flow.execute(throwStubError);
  597. flow.execute(function() {
  598. seen.push(1);
  599. }).then(function() {
  600. seen.push(2);
  601. }, function() {
  602. seen.push(3);
  603. });
  604. }).then(assert.fail, function(e) {
  605. assertIsStubError(e);
  606. assert.deepEqual([], seen);
  607. });
  608. });
  609. it('testRegisteredTaskCallbacksAreDroppedWhenTaskIsCancelled_withReturn', function() {
  610. var seen = [];
  611. return flow.execute(function() {
  612. flow.execute(throwStubError);
  613. return flow.execute(function() {
  614. seen.push(1);
  615. }).then(function() {
  616. seen.push(2);
  617. }, function() {
  618. seen.push(3);
  619. });
  620. }).then(assert.fail, function(e) {
  621. assertIsStubError(e);
  622. assert.deepEqual([], seen);
  623. });
  624. });
  625. it('testTasksWithinACallbackAreDroppedIfContainingTaskIsAborted', function() {
  626. var seen = [];
  627. return flow.execute(function() {
  628. flow.execute(throwStubError);
  629. // None of the callbacks on this promise should execute because the
  630. // task assert.failure above is never handled, causing the containing task to
  631. // abort.
  632. promise.fulfilled().then(function() {
  633. seen.push(1);
  634. return flow.execute(function() {
  635. seen.push(2);
  636. });
  637. }).finally(function() {
  638. seen.push(3);
  639. });
  640. }).then(assert.fail, function(e) {
  641. assertIsStubError(e);
  642. assert.deepEqual([], seen);
  643. });
  644. });
  645. it('testTaskIsCancelledAfterWaitTimeout', function() {
  646. var seen = [];
  647. return flow.execute(function() {
  648. flow.wait(function() {
  649. return promise.delayed(50);
  650. }, 5);
  651. return flow.execute(function() {
  652. seen.push(1);
  653. }).then(function() {
  654. seen.push(2);
  655. }, function() {
  656. seen.push(3);
  657. });
  658. }).then(assert.fail, function() {
  659. assert.deepEqual([], seen);
  660. });
  661. });
  662. describe('task callbacks get cancellation error if registered after task was cancelled', function() {
  663. it('(a)', function() {
  664. var task;
  665. flow.execute(function() {
  666. flow.execute(throwStubError);
  667. task = flow.execute(function() {});
  668. }).then(assert.fail, assertIsStubError);
  669. return waitForIdle().then(function() {
  670. return task.then(assert.fail, function(e) {
  671. assert.ok(e instanceof promise.CancellationError);
  672. });
  673. });
  674. });
  675. it('(b)', function() {
  676. var seen = [];
  677. var task;
  678. flow.execute(function() {
  679. flow.execute(throwStubError);
  680. task = flow.execute(function() {});
  681. task.then(() => seen.push(1))
  682. .then(() => seen.push(2));
  683. task.then(() => seen.push(3))
  684. .then(() => seen.push(4));
  685. }).then(assert.fail, assertIsStubError);
  686. return waitForIdle().then(function() {
  687. return task.then(assert.fail, function(e) {
  688. seen.push(5);
  689. assert.ok(e instanceof promise.CancellationError);
  690. });
  691. }).then(() => assert.deepEqual([5], seen));
  692. });
  693. });
  694. it('unhandledRejectionInParallelTaskQueue', function() {
  695. var seen = [];
  696. function schedule(name) {
  697. return flow.execute(() => seen.push(name), name);
  698. }
  699. flow.async(function() {
  700. schedule('a.1');
  701. flow.execute(throwStubError, 'a.2 (throws)');
  702. });
  703. var b3;
  704. flow.async(function() {
  705. schedule('b.1');
  706. schedule('b.2');
  707. b3 = schedule('b.3');
  708. });
  709. var c3;
  710. flow.async(function() {
  711. schedule('c.1');
  712. schedule('c.2');
  713. c3 = schedule('c.3');
  714. });
  715. function assertWasCancelled(p) {
  716. return p.then(assert.fail, function(e) {
  717. assert.ok(e instanceof promise.CancellationError);
  718. });
  719. }
  720. return waitForAbort()
  721. .then(function() {
  722. assert.deepEqual(['a.1', 'b.1', 'c.1', 'b.2', 'c.2'], seen);
  723. })
  724. .then(() => assertWasCancelled(b3))
  725. .then(() => assertWasCancelled(c3));
  726. });
  727. it('errorsInAsyncFunctionsAreReportedAsUnhandledRejection', function() {
  728. flow.removeAllListeners(); // For tearDown.
  729. var task;
  730. return new Promise(function(fulfill) {
  731. flow.once('uncaughtException', fulfill);
  732. flow.async(function() {
  733. task = flow.execute(function() {});
  734. throw Error('boom');
  735. });
  736. }).then(function(error) {
  737. assert.ok(error instanceof promise.CancellationError);
  738. return task.catch(function(error) {
  739. assert.ok(error instanceof promise.CancellationError);
  740. });
  741. });
  742. });
  743. describe('does not wait for values thrown from callbacks to be resolved', function() {
  744. it('(a)', function() {
  745. var p1 = promise.fulfilled();
  746. var reason = promise.fulfilled('should not see me');
  747. return p1.then(function() {
  748. throw reason;
  749. }).then(assert.fail, function(e) {
  750. assert.equal(reason, e);
  751. });
  752. });
  753. it('(b)', function() {
  754. var p1 = promise.fulfilled();
  755. var reason = promise.rejected('should not see me');
  756. reason.catch(function() {}); // For tearDown.
  757. return p1.then(function() {
  758. throw reason;
  759. }).then(assert.fail, function(e) {
  760. assert.equal(reason, e);
  761. });
  762. });
  763. it('(c)', function() {
  764. var p1 = promise.fulfilled();
  765. var reason = promise.defer();
  766. setTimeout(() => reason.fulfill('should not see me'), 100);
  767. return p1.then(function() {
  768. throw reason.promise;
  769. }).then(assert.fail, function(e) {
  770. assert.equal(reason.promise, e);
  771. });
  772. });
  773. it('(d)', function() {
  774. var p1 = promise.fulfilled();
  775. var reason = {then: function() {}}; // A thenable like object.
  776. return p1.then(function() {
  777. throw reason;
  778. }).then(assert.fail, function(e) {
  779. assert.equal(reason, e);
  780. });
  781. });
  782. });
  783. });
  784. });