promise_test.js 35 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109
  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. 'use strict';
  18. const assert = require('assert');
  19. const testutil = require('./testutil');
  20. const promise = require('../../lib/promise');
  21. const {enablePromiseManager, promiseManagerSuite} = require('../../lib/test/promise');
  22. // Aliases for readability.
  23. const NativePromise = Promise;
  24. const StubError = testutil.StubError;
  25. const assertIsStubError = testutil.assertIsStubError;
  26. const callbackHelper = testutil.callbackHelper;
  27. const callbackPair = testutil.callbackPair;
  28. const throwStubError = testutil.throwStubError;
  29. const fail = () => assert.fail();
  30. // Refer to promise_aplus_test for promise compliance with standard behavior.
  31. describe('promise', function() {
  32. var app, uncaughtExceptions;
  33. beforeEach(function setUp() {
  34. if (promise.USE_PROMISE_MANAGER) {
  35. promise.LONG_STACK_TRACES = false;
  36. uncaughtExceptions = [];
  37. app = promise.controlFlow();
  38. app.on(promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION,
  39. (e) => uncaughtExceptions.push(e));
  40. }
  41. });
  42. afterEach(function tearDown() {
  43. if (promise.USE_PROMISE_MANAGER) {
  44. app.reset();
  45. promise.setDefaultFlow(new promise.ControlFlow);
  46. assert.deepEqual([], uncaughtExceptions,
  47. 'Did not expect any uncaught exceptions');
  48. promise.LONG_STACK_TRACES = false;
  49. }
  50. });
  51. const assertIsPromise = (p) => assert.ok(promise.isPromise(p));
  52. const assertNotPromise = (v) => assert.ok(!promise.isPromise(v));
  53. function defer() {
  54. let d = {};
  55. let promise = new Promise((resolve, reject) => {
  56. Object.assign(d, {resolve, reject});
  57. });
  58. d.promise = promise;
  59. return d;
  60. }
  61. function createRejectedPromise(reason) {
  62. var p = Promise.reject(reason);
  63. p.catch(function() {}); // Silence unhandled rejection handlers.
  64. return p;
  65. }
  66. enablePromiseManager(() => {
  67. it('testCanDetectPromiseLikeObjects', function() {
  68. assertIsPromise(new promise.Promise(function(fulfill) {
  69. fulfill();
  70. }));
  71. assertIsPromise(new promise.Deferred().promise);
  72. assertIsPromise(Promise.resolve(123));
  73. assertIsPromise({then:function() {}});
  74. assertNotPromise(new promise.Deferred());
  75. assertNotPromise(undefined);
  76. assertNotPromise(null);
  77. assertNotPromise('');
  78. assertNotPromise(true);
  79. assertNotPromise(false);
  80. assertNotPromise(1);
  81. assertNotPromise({});
  82. assertNotPromise({then:1});
  83. assertNotPromise({then:true});
  84. assertNotPromise({then:''});
  85. });
  86. describe('then', function() {
  87. it('returnsOwnPromiseIfNoCallbacksWereGiven', function() {
  88. var deferred = new promise.Deferred();
  89. assert.equal(deferred.promise, deferred.promise.then());
  90. assert.equal(deferred.promise, deferred.promise.catch());
  91. assert.equal(deferred.promise, promise.when(deferred.promise));
  92. });
  93. it('stillConsideredUnHandledIfNoCallbacksWereGivenOnCallsToThen', function() {
  94. promise.rejected(new StubError).then();
  95. var handler = callbackHelper(assertIsStubError);
  96. // so tearDown() doesn't throw
  97. app.removeAllListeners();
  98. app.on(promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION, handler);
  99. return NativePromise.resolve()
  100. // Macro yield so the uncaught exception has a chance to trigger.
  101. .then(() => new NativePromise(resolve => setTimeout(resolve, 0)))
  102. .then(() => handler.assertCalled());
  103. });
  104. });
  105. describe('finally', function() {
  106. it('nonFailingCallbackDoesNotSuppressOriginalError', function() {
  107. var done = callbackHelper(assertIsStubError);
  108. return promise.rejected(new StubError).
  109. finally(function() {}).
  110. catch(done).
  111. finally(done.assertCalled);
  112. });
  113. it('failingCallbackSuppressesOriginalError', function() {
  114. var done = callbackHelper(assertIsStubError);
  115. return promise.rejected(new Error('original')).
  116. finally(throwStubError).
  117. catch(done).
  118. finally(done.assertCalled);
  119. });
  120. it('callbackThrowsAfterFulfilledPromise', function() {
  121. var done = callbackHelper(assertIsStubError);
  122. return promise.fulfilled().
  123. finally(throwStubError).
  124. catch(done).
  125. finally(done.assertCalled);
  126. });
  127. it('callbackReturnsRejectedPromise', function() {
  128. var done = callbackHelper(assertIsStubError);
  129. return promise.fulfilled().
  130. finally(function() {
  131. return promise.rejected(new StubError);
  132. }).
  133. catch(done).
  134. finally(done.assertCalled);
  135. });
  136. });
  137. describe('cancel', function() {
  138. it('passesTheCancellationReasonToReject', function() {
  139. var d = new promise.Deferred();
  140. var res = d.promise.then(assert.fail, function(e) {
  141. assert.ok(e instanceof promise.CancellationError);
  142. assert.equal('because i said so', e.message);
  143. });
  144. d.promise.cancel('because i said so');
  145. return res;
  146. });
  147. describe('can cancel original promise from its child;', function() {
  148. it('child created by then()', function() {
  149. var d = new promise.Deferred();
  150. var p = d.promise.then(assert.fail, function(e) {
  151. assert.ok(e instanceof promise.CancellationError);
  152. assert.equal('because i said so', e.message);
  153. return 123;
  154. });
  155. p.cancel('because i said so');
  156. return p.then(v => assert.equal(123, v));
  157. });
  158. it('child linked by resolving with parent', function() {
  159. let parent = promise.defer();
  160. let child = new promise.Promise(resolve => resolve(parent.promise));
  161. child.cancel('all done');
  162. return parent.promise.then(
  163. () => assert.fail('expected a cancellation'),
  164. e => {
  165. assert.ok(e instanceof promise.CancellationError);
  166. assert.equal('all done', e.message);
  167. });
  168. });
  169. it('grand child through thenable chain', function() {
  170. let p = new promise.Promise(function() {/* never resolve*/});
  171. let noop = function() {};
  172. let gc = p.then(noop).then(noop).then(noop);
  173. gc.cancel('stop!');
  174. return p.then(
  175. () => assert.fail('expected to be cancelled'),
  176. (e) => {
  177. assert.ok(e instanceof promise.CancellationError);
  178. assert.equal('stop!', e.message);
  179. });
  180. });
  181. it('grand child through thenable chain started at resolve', function() {
  182. function noop() {}
  183. let parent = promise.defer();
  184. let child = new promise.Promise(resolve => resolve(parent.promise));
  185. let grandChild = child.then(noop).then(noop).then(noop);
  186. grandChild.cancel('all done');
  187. return parent.promise.then(
  188. () => assert.fail('expected a cancellation'),
  189. e => {
  190. assert.ok(e instanceof promise.CancellationError);
  191. assert.equal('all done', e.message);
  192. });
  193. });
  194. it('"parent" is a CancellableThenable', function() {
  195. function noop() {}
  196. class FakeThenable {
  197. constructor(p) {
  198. this.promise = p;
  199. }
  200. cancel(reason) {
  201. this.promise.cancel(reason);
  202. }
  203. then(cb, eb) {
  204. let result = this.promise.then(cb, eb);
  205. return new FakeThenable(result);
  206. }
  207. }
  208. promise.CancellableThenable.addImplementation(FakeThenable);
  209. let root = new promise.Promise(noop);
  210. let thenable = new FakeThenable(root);
  211. assert.ok(promise.Thenable.isImplementation(thenable));
  212. assert.ok(promise.CancellableThenable.isImplementation(thenable));
  213. let child = new promise.Promise(resolve => resolve(thenable));
  214. assert.ok(child instanceof promise.Promise);
  215. child.cancel('stop!');
  216. function assertStopped(p) {
  217. return p.then(
  218. () => assert.fail('not stopped!'),
  219. (e) => {
  220. assert.ok(e instanceof promise.CancellationError);
  221. assert.equal('stop!', e.message);
  222. });
  223. }
  224. return assertStopped(child).then(() => assertStopped(root));
  225. });
  226. });
  227. it('canCancelATimeout', function() {
  228. var p = promise.delayed(25)
  229. .then(assert.fail, (e) => e instanceof promise.CancellationError);
  230. setTimeout(() => p.cancel(), 20);
  231. p.cancel();
  232. return p;
  233. });
  234. it('can cancel timeout from grandchild', function() {
  235. });
  236. it('cancelIsANoopOnceAPromiseHasBeenFulfilled', function() {
  237. var p = promise.fulfilled(123);
  238. p.cancel();
  239. return p.then((v) => assert.equal(123, v));
  240. });
  241. it('cancelIsANoopOnceAPromiseHasBeenRejected', function() {
  242. var p = promise.rejected(new StubError);
  243. p.cancel();
  244. var pair = callbackPair(null, assertIsStubError);
  245. return p.then(assert.fail, assertIsStubError);
  246. });
  247. it('noopCancelTriggeredOnCallbackOfResolvedPromise', function() {
  248. var d = promise.defer();
  249. var p = d.promise.then();
  250. d.fulfill();
  251. p.cancel(); // This should not throw.
  252. return p; // This should not trigger a failure.
  253. });
  254. });
  255. });
  256. promiseManagerSuite(() => {
  257. describe('fulfilled', function() {
  258. it('returns input value if it is already a valid promise', function() {
  259. let p = promise.createPromise(function() {});
  260. let r = promise.fulfilled(p);
  261. assert.strictEqual(p, r);
  262. });
  263. it('creates a new promise fulfilled with input', function() {
  264. return promise.fulfilled(1234).then(v => assert.equal(1234, v));
  265. });
  266. it('can convert thenables to valid promise', function() {
  267. let thenable = {then: function(cb) {cb(1234)}};
  268. let p = promise.fulfilled(thenable);
  269. assert.notStrictEqual(thenable, p);
  270. return p.then(v => assert.equal(1234, v));
  271. });
  272. });
  273. describe('when', function() {
  274. it('ReturnsAResolvedPromiseIfGivenANonPromiseValue', function() {
  275. var ret = promise.when('abc');
  276. assertIsPromise(ret);
  277. return ret.then((value) => assert.equal('abc', value));
  278. });
  279. it('PassesRawErrorsToCallbacks', function() {
  280. var error = new Error('boo!');
  281. return promise.when(error, function(value) {
  282. assert.equal(error, value);
  283. });
  284. });
  285. it('WaitsForValueToBeResolvedBeforeInvokingCallback', function() {
  286. let d = defer();
  287. let callback;
  288. let result = promise.when(d.promise, callback = callbackHelper(function(value) {
  289. assert.equal('hi', value);
  290. }));
  291. callback.assertNotCalled();
  292. d.resolve('hi');
  293. return result.then(callback.assertCalled);
  294. });
  295. });
  296. describe('fullyResolved', function() {
  297. it('primitives', function() {
  298. function runTest(value) {
  299. var callback, errback;
  300. return promise.fullyResolved(value)
  301. .then((resolved) => assert.equal(value, resolved));
  302. }
  303. return runTest(true)
  304. .then(() => runTest(function() {}))
  305. .then(() => runTest(null))
  306. .then(() => runTest(123))
  307. .then(() => runTest('foo bar'))
  308. .then(() => runTest(undefined));
  309. });
  310. it('arrayOfPrimitives', function() {
  311. var fn = function() {};
  312. var array = [true, fn, null, 123, '', undefined, 1];
  313. return promise.fullyResolved(array).then(function(resolved) {
  314. assert.equal(array, resolved);
  315. assert.deepEqual([true, fn, null, 123, '', undefined, 1],
  316. resolved);
  317. });
  318. });
  319. it('nestedArrayOfPrimitives', function() {
  320. var fn = function() {};
  321. var array = [true, [fn, null, 123], '', undefined];
  322. return promise.fullyResolved(array)
  323. .then(function(resolved) {
  324. assert.equal(array, resolved);
  325. assert.deepEqual([true, [fn, null, 123], '', undefined], resolved);
  326. assert.deepEqual([fn, null, 123], resolved[1]);
  327. });
  328. });
  329. it('arrayWithPromisedPrimitive', function() {
  330. return promise.fullyResolved([Promise.resolve(123)])
  331. .then(function(resolved) {
  332. assert.deepEqual([123], resolved);
  333. });
  334. });
  335. it('promiseResolvesToPrimitive', function() {
  336. return promise.fullyResolved(Promise.resolve(123))
  337. .then((resolved) => assert.equal(123, resolved));
  338. });
  339. it('promiseResolvesToArray', function() {
  340. var fn = function() {};
  341. var array = [true, [fn, null, 123], '', undefined];
  342. var aPromise = Promise.resolve(array);
  343. var result = promise.fullyResolved(aPromise);
  344. return result.then(function(resolved) {
  345. assert.equal(array, resolved);
  346. assert.deepEqual([true, [fn, null, 123], '', undefined],
  347. resolved);
  348. assert.deepEqual([fn, null, 123], resolved[1]);
  349. });
  350. });
  351. it('promiseResolvesToArrayWithPromises', function() {
  352. var nestedPromise = Promise.resolve(123);
  353. var aPromise = Promise.resolve([true, nestedPromise]);
  354. return promise.fullyResolved(aPromise)
  355. .then(function(resolved) {
  356. assert.deepEqual([true, 123], resolved);
  357. });
  358. });
  359. it('rejectsIfArrayPromiseRejects', function() {
  360. var nestedPromise = createRejectedPromise(new StubError);
  361. var aPromise = Promise.resolve([true, nestedPromise]);
  362. var pair = callbackPair(null, assertIsStubError);
  363. return promise.fullyResolved(aPromise)
  364. .then(assert.fail, assertIsStubError);
  365. });
  366. it('rejectsOnFirstArrayRejection', function() {
  367. var e1 = new Error('foo');
  368. var e2 = new Error('bar');
  369. var aPromise = Promise.resolve([
  370. createRejectedPromise(e1),
  371. createRejectedPromise(e2)
  372. ]);
  373. return promise.fullyResolved(aPromise)
  374. .then(assert.fail, function(error) {
  375. assert.strictEqual(e1, error);
  376. });
  377. });
  378. it('rejectsIfNestedArrayPromiseRejects', function() {
  379. var aPromise = Promise.resolve([
  380. Promise.resolve([
  381. createRejectedPromise(new StubError)
  382. ])
  383. ]);
  384. return promise.fullyResolved(aPromise)
  385. .then(assert.fail, assertIsStubError);
  386. });
  387. it('simpleHash', function() {
  388. var hash = {'a': 123};
  389. return promise.fullyResolved(hash)
  390. .then(function(resolved) {
  391. assert.strictEqual(hash, resolved);
  392. assert.deepEqual(hash, {'a': 123});
  393. });
  394. });
  395. it('nestedHash', function() {
  396. var nestedHash = {'foo':'bar'};
  397. var hash = {'a': 123, 'b': nestedHash};
  398. return promise.fullyResolved(hash)
  399. .then(function(resolved) {
  400. assert.strictEqual(hash, resolved);
  401. assert.deepEqual({'a': 123, 'b': {'foo': 'bar'}}, resolved);
  402. assert.strictEqual(nestedHash, resolved['b']);
  403. });
  404. });
  405. it('promiseResolvesToSimpleHash', function() {
  406. var hash = {'a': 123};
  407. var aPromise = Promise.resolve(hash);
  408. return promise.fullyResolved(aPromise)
  409. .then((resolved) => assert.strictEqual(hash, resolved));
  410. });
  411. it('promiseResolvesToNestedHash', function() {
  412. var nestedHash = {'foo':'bar'};
  413. var hash = {'a': 123, 'b': nestedHash};
  414. var aPromise = Promise.resolve(hash);
  415. return promise.fullyResolved(aPromise)
  416. .then(function(resolved) {
  417. assert.strictEqual(hash, resolved);
  418. assert.strictEqual(nestedHash, resolved['b']);
  419. assert.deepEqual(hash, {'a': 123, 'b': {'foo': 'bar'}});
  420. });
  421. });
  422. it('promiseResolvesToHashWithPromises', function() {
  423. var aPromise = Promise.resolve({
  424. 'a': Promise.resolve(123)
  425. });
  426. return promise.fullyResolved(aPromise)
  427. .then(function(resolved) {
  428. assert.deepEqual({'a': 123}, resolved);
  429. });
  430. });
  431. it('rejectsIfHashPromiseRejects', function() {
  432. var aPromise = Promise.resolve({
  433. 'a': createRejectedPromise(new StubError)
  434. });
  435. return promise.fullyResolved(aPromise)
  436. .then(assert.fail, assertIsStubError);
  437. });
  438. it('rejectsIfNestedHashPromiseRejects', function() {
  439. var aPromise = Promise.resolve({
  440. 'a': {'b': createRejectedPromise(new StubError)}
  441. });
  442. return promise.fullyResolved(aPromise)
  443. .then(assert.fail, assertIsStubError);
  444. });
  445. it('instantiatedObject', function() {
  446. function Foo() {
  447. this.bar = 'baz';
  448. }
  449. var foo = new Foo;
  450. return promise.fullyResolved(foo).then(function(resolvedFoo) {
  451. assert.equal(foo, resolvedFoo);
  452. assert.ok(resolvedFoo instanceof Foo);
  453. assert.deepEqual(new Foo, resolvedFoo);
  454. });
  455. });
  456. it('withEmptyArray', function() {
  457. return promise.fullyResolved([]).then(function(resolved) {
  458. assert.deepEqual([], resolved);
  459. });
  460. });
  461. it('withEmptyHash', function() {
  462. return promise.fullyResolved({}).then(function(resolved) {
  463. assert.deepEqual({}, resolved);
  464. });
  465. });
  466. it('arrayWithPromisedHash', function() {
  467. var obj = {'foo': 'bar'};
  468. var array = [Promise.resolve(obj)];
  469. return promise.fullyResolved(array).then(function(resolved) {
  470. assert.deepEqual(resolved, [obj]);
  471. });
  472. });
  473. });
  474. describe('checkedNodeCall', function() {
  475. it('functionThrows', function() {
  476. return promise.checkedNodeCall(throwStubError)
  477. .then(assert.fail, assertIsStubError);
  478. });
  479. it('functionReturnsAnError', function() {
  480. return promise.checkedNodeCall(function(callback) {
  481. callback(new StubError);
  482. }).then(assert.fail, assertIsStubError);
  483. });
  484. it('functionReturnsSuccess', function() {
  485. var success = 'success!';
  486. return promise.checkedNodeCall(function(callback) {
  487. callback(null, success);
  488. }).then((value) => assert.equal(success, value));
  489. });
  490. it('functionReturnsAndThrows', function() {
  491. var error = new Error('boom');
  492. var error2 = new Error('boom again');
  493. return promise.checkedNodeCall(function(callback) {
  494. callback(error);
  495. throw error2;
  496. }).then(assert.fail, (e) => assert.equal(error, e));
  497. });
  498. it('functionThrowsAndReturns', function() {
  499. var error = new Error('boom');
  500. var error2 = new Error('boom again');
  501. return promise.checkedNodeCall(function(callback) {
  502. setTimeout(() => callback(error), 10);
  503. throw error2;
  504. }).then(assert.fail, (e) => assert.equal(error2, e));
  505. });
  506. });
  507. describe('all', function() {
  508. it('(base case)', function() {
  509. let deferredObjs = [defer(), defer()];
  510. var a = [
  511. 0, 1,
  512. deferredObjs[0].promise,
  513. deferredObjs[1].promise,
  514. 4, 5, 6
  515. ];
  516. delete a[5];
  517. var pair = callbackPair(function(value) {
  518. assert.deepEqual([0, 1, 2, 3, 4, undefined, 6], value);
  519. });
  520. var result = promise.all(a).then(pair.callback, pair.errback);
  521. pair.assertNeither();
  522. deferredObjs[0].resolve(2);
  523. pair.assertNeither();
  524. deferredObjs[1].resolve(3);
  525. return result.then(() => pair.assertCallback());
  526. });
  527. it('empty array', function() {
  528. return promise.all([]).then((a) => assert.deepEqual([], a));
  529. });
  530. it('usesFirstRejection', function() {
  531. let deferredObjs = [defer(), defer()];
  532. let a = [deferredObjs[0].promise, deferredObjs[1].promise];
  533. var result = promise.all(a).then(assert.fail, assertIsStubError);
  534. deferredObjs[1].reject(new StubError);
  535. setTimeout(() => deferredObjs[0].reject(Error('ignored')), 0);
  536. return result;
  537. });
  538. });
  539. describe('map', function() {
  540. it('(base case)', function() {
  541. var a = [1, 2, 3];
  542. return promise.map(a, function(value, index, a2) {
  543. assert.equal(a, a2);
  544. assert.equal('number', typeof index, 'not a number');
  545. return value + 1;
  546. }).then(function(value) {
  547. assert.deepEqual([2, 3, 4], value);
  548. });
  549. });
  550. it('omitsDeleted', function() {
  551. var a = [0, 1, 2, 3, 4, 5, 6];
  552. delete a[1];
  553. delete a[3];
  554. delete a[4];
  555. delete a[6];
  556. var expected = [0, 1, 4, 9, 16, 25, 36];
  557. delete expected[1];
  558. delete expected[3];
  559. delete expected[4];
  560. delete expected[6];
  561. return promise.map(a, function(value) {
  562. return value * value;
  563. }).then(function(value) {
  564. assert.deepEqual(expected, value);
  565. });
  566. });
  567. it('emptyArray', function() {
  568. return promise.map([], function(value) {
  569. return value + 1;
  570. }).then(function(value) {
  571. assert.deepEqual([], value);
  572. });
  573. });
  574. it('inputIsPromise', function() {
  575. var input = defer();
  576. var result = promise.map(input.promise, function(value) {
  577. return value + 1;
  578. });
  579. var pair = callbackPair(function(value) {
  580. assert.deepEqual([2, 3, 4], value);
  581. });
  582. result = result.then(pair.callback, pair.errback);
  583. setTimeout(function() {
  584. pair.assertNeither();
  585. input.resolve([1, 2, 3]);
  586. }, 10);
  587. return result;
  588. });
  589. it('waitsForFunctionResultToResolve', function() {
  590. var innerResults = [
  591. defer(),
  592. defer()
  593. ];
  594. var result = promise.map([1, 2], function(value, index) {
  595. return innerResults[index].promise;
  596. });
  597. var pair = callbackPair(function(value) {
  598. assert.deepEqual(['a', 'b'], value);
  599. });
  600. result = result.then(pair.callback, pair.errback);
  601. return NativePromise.resolve()
  602. .then(function() {
  603. pair.assertNeither();
  604. innerResults[0].resolve('a');
  605. })
  606. .then(function() {
  607. pair.assertNeither();
  608. innerResults[1].resolve('b');
  609. return result;
  610. })
  611. .then(pair.assertCallback);
  612. });
  613. it('rejectsPromiseIfFunctionThrows', function() {
  614. return promise.map([1], throwStubError)
  615. .then(assert.fail, assertIsStubError);
  616. });
  617. it('rejectsPromiseIfFunctionReturnsRejectedPromise', function() {
  618. return promise.map([1], function() {
  619. return createRejectedPromise(new StubError);
  620. }).then(assert.fail, assertIsStubError);
  621. });
  622. it('stopsCallingFunctionIfPreviousIterationFailed', function() {
  623. var count = 0;
  624. return promise.map([1, 2, 3, 4], function() {
  625. count++;
  626. if (count == 3) {
  627. throw new StubError;
  628. }
  629. }).then(assert.fail, function(e) {
  630. assertIsStubError(e);
  631. assert.equal(3, count);
  632. });
  633. });
  634. it('rejectsWithFirstRejectedPromise', function() {
  635. var innerResult = [
  636. Promise.resolve(),
  637. createRejectedPromise(new StubError),
  638. createRejectedPromise(Error('should be ignored'))
  639. ];
  640. var count = 0;
  641. return promise.map([1, 2, 3, 4], function(value, index) {
  642. count += 1;
  643. return innerResult[index];
  644. }).then(assert.fail, function(e) {
  645. assertIsStubError(e);
  646. assert.equal(2, count);
  647. });
  648. });
  649. it('preservesOrderWhenMapReturnsPromise', function() {
  650. var deferreds = [
  651. defer(),
  652. defer(),
  653. defer(),
  654. defer()
  655. ];
  656. var result = promise.map(deferreds, function(value) {
  657. return value.promise;
  658. });
  659. var pair = callbackPair(function(value) {
  660. assert.deepEqual([0, 1, 2, 3], value);
  661. });
  662. result = result.then(pair.callback, pair.errback);
  663. return Promise.resolve()
  664. .then(function() {
  665. pair.assertNeither();
  666. for (let i = deferreds.length; i > 0; i -= 1) {
  667. deferreds[i - 1].resolve(i - 1);
  668. }
  669. return result;
  670. }).then(pair.assertCallback);
  671. });
  672. });
  673. describe('filter', function() {
  674. it('basicFiltering', function() {
  675. var a = [0, 1, 2, 3];
  676. return promise.filter(a, function(val, index, a2) {
  677. assert.equal(a, a2);
  678. assert.equal('number', typeof index, 'not a number');
  679. return val > 1;
  680. }).then(function(val) {
  681. assert.deepEqual([2, 3], val);
  682. });
  683. });
  684. it('omitsDeleted', function() {
  685. var a = [0, 1, 2, 3, 4, 5, 6];
  686. delete a[3];
  687. delete a[4];
  688. return promise.filter(a, function(value) {
  689. return value > 1 && value < 6;
  690. }).then(function(val) {
  691. assert.deepEqual([2, 5], val);
  692. });
  693. });
  694. it('preservesInputs', function() {
  695. var a = [0, 1, 2, 3];
  696. return promise.filter(a, function(value, i, a2) {
  697. assert.equal(a, a2);
  698. // Even if a function modifies the input array, the original value
  699. // should be inserted into the new array.
  700. a2[i] = a2[i] - 1;
  701. return a2[i] >= 1;
  702. }).then(function(val) {
  703. assert.deepEqual([2, 3], val);
  704. });
  705. });
  706. it('inputIsPromise', function() {
  707. var input = defer();
  708. var result = promise.filter(input.promise, function(value) {
  709. return value > 1 && value < 3;
  710. });
  711. var pair = callbackPair(function(value) {
  712. assert.deepEqual([2], value);
  713. });
  714. result = result.then(pair.callback, pair.errback);
  715. return NativePromise.resolve()
  716. .then(function() {
  717. pair.assertNeither();
  718. input.resolve([1, 2, 3]);
  719. return result;
  720. })
  721. .then(pair.assertCallback);
  722. });
  723. it('waitsForFunctionResultToResolve', function() {
  724. var innerResults = [
  725. defer(),
  726. defer()
  727. ];
  728. var result = promise.filter([1, 2], function(value, index) {
  729. return innerResults[index].promise;
  730. });
  731. var pair = callbackPair(function(value) {
  732. assert.deepEqual([2], value);
  733. });
  734. result = result.then(pair.callback, pair.errback);
  735. return NativePromise.resolve()
  736. .then(function() {
  737. pair.assertNeither();
  738. innerResults[0].resolve(false);
  739. })
  740. .then(function() {
  741. pair.assertNeither();
  742. innerResults[1].resolve(true);
  743. return result;
  744. })
  745. .then(pair.assertCallback);
  746. });
  747. it('rejectsPromiseIfFunctionReturnsRejectedPromise', function() {
  748. return promise.filter([1], function() {
  749. return createRejectedPromise(new StubError);
  750. }).then(assert.fail, assertIsStubError);
  751. });
  752. it('stopsCallingFunctionIfPreviousIterationFailed', function() {
  753. var count = 0;
  754. return promise.filter([1, 2, 3, 4], function() {
  755. count++;
  756. if (count == 3) {
  757. throw new StubError;
  758. }
  759. }).then(assert.fail, function(e) {
  760. assertIsStubError(e);
  761. assert.equal(3, count);
  762. });
  763. });
  764. it('rejectsWithFirstRejectedPromise', function() {
  765. var innerResult = [
  766. Promise.resolve(),
  767. createRejectedPromise(new StubError),
  768. createRejectedPromise(Error('should be ignored'))
  769. ];
  770. return promise.filter([1, 2, 3, 4], function(value, index) {
  771. assert.ok(index < innerResult.length);
  772. return innerResult[index];
  773. }).then(assert.fail, assertIsStubError);
  774. });
  775. it('preservesOrderWhenFilterReturnsPromise', function() {
  776. var deferreds = [
  777. defer(),
  778. defer(),
  779. defer(),
  780. defer()
  781. ];
  782. var result = promise.filter([0, 1, 2, 3], function(value, index) {
  783. return deferreds[index].promise;
  784. });
  785. var pair = callbackPair(function(value) {
  786. assert.deepEqual([1, 2], value);
  787. });
  788. result = result.then(pair.callback, pair.errback);
  789. return NativePromise.resolve()
  790. .then(function() {
  791. pair.assertNeither();
  792. for (let i = deferreds.length - 1; i >= 0; i -= 1) {
  793. deferreds[i].resolve(i > 0 && i < 3);
  794. }
  795. return result;
  796. }).then(pair.assertCallback);
  797. });
  798. });
  799. });
  800. enablePromiseManager(() => {
  801. it('firesUncaughtExceptionEventIfRejectionNeverHandled', function() {
  802. promise.rejected(new StubError);
  803. var handler = callbackHelper(assertIsStubError);
  804. // so tearDown() doesn't throw
  805. app.removeAllListeners();
  806. app.on(promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION, handler);
  807. return NativePromise.resolve()
  808. // Macro yield so the uncaught exception has a chance to trigger.
  809. .then(() => new NativePromise(resolve => setTimeout(resolve, 0)))
  810. .then(handler.assertCalled);
  811. });
  812. it('cannotResolveADeferredWithItself', function() {
  813. var deferred = new promise.Deferred();
  814. assert.throws(() => deferred.fulfill(deferred));
  815. assert.throws(() => deferred.reject(deferred));
  816. });
  817. describe('testLongStackTraces', function() {
  818. beforeEach(() => promise.LONG_STACK_TRACES = false);
  819. afterEach(() => promise.LONG_STACK_TRACES = false);
  820. it('doesNotAppendStackIfFeatureDisabled', function() {
  821. promise.LONG_STACK_TRACES = false;
  822. var error = Error('hello');
  823. var originalStack = error.stack;
  824. return promise.rejected(error).
  825. then(fail).
  826. then(fail).
  827. then(fail).
  828. then(fail, function(e) {
  829. assert.equal(error, e);
  830. assert.equal(originalStack, e.stack);
  831. });
  832. });
  833. function getStackMessages(error) {
  834. return error.stack.split(/\n/).filter(function(line) {
  835. return /^From: /.test(line);
  836. });
  837. }
  838. it('appendsInitialPromiseCreation_resolverThrows', function() {
  839. promise.LONG_STACK_TRACES = true;
  840. var error = Error('hello');
  841. var originalStack = '(placeholder; will be overwritten later)';
  842. return new promise.Promise(function() {
  843. try {
  844. throw error;
  845. } catch (e) {
  846. originalStack = e.stack;
  847. throw e;
  848. }
  849. }).then(fail, function(e) {
  850. assert.strictEqual(error, e);
  851. if (typeof originalStack !== 'string') {
  852. return;
  853. }
  854. assert.notEqual(originalStack, e.stack);
  855. assert.equal(e.stack.indexOf(originalStack), 0,
  856. 'should start with original stack');
  857. assert.deepEqual(['From: ManagedPromise: new'], getStackMessages(e));
  858. });
  859. });
  860. it('appendsInitialPromiseCreation_rejectCalled', function() {
  861. promise.LONG_STACK_TRACES = true;
  862. var error = Error('hello');
  863. var originalStack = error.stack;
  864. return new promise.Promise(function(_, reject) {
  865. reject(error);
  866. }).then(fail, function(e) {
  867. assert.equal(error, e);
  868. if (typeof originalStack !== 'string') {
  869. return;
  870. }
  871. assert.notEqual(originalStack, e.stack);
  872. assert.equal(e.stack.indexOf(originalStack), 0,
  873. 'should start with original stack');
  874. assert.deepEqual(['From: ManagedPromise: new'], getStackMessages(e));
  875. });
  876. });
  877. it('appendsEachStepToRejectionError', function() {
  878. promise.LONG_STACK_TRACES = true;
  879. var error = Error('hello');
  880. var originalStack = '(placeholder; will be overwritten later)';
  881. return new promise.Promise(function() {
  882. try {
  883. throw error;
  884. } catch (e) {
  885. originalStack = e.stack;
  886. throw e;
  887. }
  888. }).
  889. then(fail).
  890. catch(function(e) { throw e; }).
  891. then(fail).
  892. catch(function(e) { throw e; }).
  893. then(fail, function(e) {
  894. assert.equal(error, e);
  895. if (typeof originalStack !== 'string') {
  896. return;
  897. }
  898. assert.notEqual(originalStack, e.stack);
  899. assert.equal(e.stack.indexOf(originalStack), 0,
  900. 'should start with original stack');
  901. assert.deepEqual([
  902. 'From: ManagedPromise: new',
  903. 'From: Promise: then',
  904. 'From: Promise: catch',
  905. 'From: Promise: then',
  906. 'From: Promise: catch',
  907. ], getStackMessages(e));
  908. });
  909. });
  910. it('errorOccursInCallbackChain', function() {
  911. promise.LONG_STACK_TRACES = true;
  912. var error = Error('hello');
  913. var originalStack = '(placeholder; will be overwritten later)';
  914. return promise.fulfilled().
  915. then(function() {}).
  916. then(function() {}).
  917. then(function() {
  918. try {
  919. throw error;
  920. } catch (e) {
  921. originalStack = e.stack;
  922. throw e;
  923. }
  924. }).
  925. catch(function(e) { throw e; }).
  926. then(fail, function(e) {
  927. assert.equal(error, e);
  928. if (typeof originalStack !== 'string') {
  929. return;
  930. }
  931. assert.notEqual(originalStack, e.stack);
  932. assert.equal(e.stack.indexOf(originalStack), 0,
  933. 'should start with original stack');
  934. assert.deepEqual([
  935. 'From: Promise: then',
  936. 'From: Promise: catch',
  937. ], getStackMessages(e));
  938. });
  939. });
  940. });
  941. });
  942. it('testAddThenableImplementation', function() {
  943. function tmp() {}
  944. assert.ok(!promise.Thenable.isImplementation(new tmp()));
  945. promise.Thenable.addImplementation(tmp);
  946. assert.ok(promise.Thenable.isImplementation(new tmp()));
  947. class tmpClass {}
  948. assert.ok(!promise.Thenable.isImplementation(new tmpClass()));
  949. promise.Thenable.addImplementation(tmpClass);
  950. assert.ok(promise.Thenable.isImplementation(new tmpClass()));
  951. });
  952. });