all.js 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. var failIfThrows = function (done) {
  2. 'use strict';
  3. return function (e) {
  4. done(e || new Error());
  5. };
  6. };
  7. describe('Promise.all', function () {
  8. 'use strict';
  9. it('should not be enumerable', function () {
  10. expect(Promise).ownPropertyDescriptor('all').to.have.property('enumerable', false);
  11. });
  12. it('fulfills if passed an empty array', function (done) {
  13. var iterable = [];
  14. Promise.all(iterable).then(function (value) {
  15. assert(Array.isArray(value));
  16. assert.deepEqual(value, []);
  17. }).then(done, failIfThrows(done));
  18. });
  19. it('fulfills if passed an empty array-like', function (done) {
  20. var f = function () {
  21. Promise.all(arguments).then(function (value) {
  22. assert(Array.isArray(value));
  23. assert.deepEqual(value, []);
  24. }).then(done, failIfThrows(done));
  25. };
  26. f();
  27. });
  28. it('fulfills if passed an array of mixed fulfilled promises and values', function (done) {
  29. var iterable = [0, Promise.resolve(1), 2, Promise.resolve(3)];
  30. Promise.all(iterable).then(function (value) {
  31. assert(Array.isArray(value));
  32. assert.deepEqual(value, [0, 1, 2, 3]);
  33. }).then(done, failIfThrows(done));
  34. });
  35. it('rejects if any passed promise is rejected', function (done) {
  36. var foreverPending = new Promise(function () { });
  37. var error = new Error('Rejected');
  38. var rejected = Promise.reject(error);
  39. var iterable = [foreverPending, rejected];
  40. Promise.all(iterable).then(
  41. function () {
  42. assert(false, 'should never get here');
  43. },
  44. function (reason) {
  45. assert.strictEqual(reason, error);
  46. }
  47. ).then(done, failIfThrows(done));
  48. });
  49. it('resolves foreign thenables', function (done) {
  50. var normal = Promise.resolve(1);
  51. var foreign = { then: function (f) { f(2); } };
  52. var iterable = [normal, foreign];
  53. Promise.all(iterable).then(function (value) {
  54. assert.deepEqual(value, [1, 2]);
  55. }).then(done, failIfThrows(done));
  56. });
  57. it('fulfills when passed an sparse array, giving `undefined` for the omitted values', function (done) {
  58. /* eslint-disable no-sparse-arrays */
  59. var iterable = [Promise.resolve(0), , , Promise.resolve(1)];
  60. /* eslint-enable no-sparse-arrays */
  61. Promise.all(iterable).then(function (value) {
  62. assert.deepEqual(value, [0, undefined, undefined, 1]);
  63. }).then(done, failIfThrows(done));
  64. });
  65. it('does not modify the input array', function (done) {
  66. var input = [0, 1];
  67. var iterable = input;
  68. Promise.all(iterable).then(function (value) {
  69. assert.notStrictEqual(input, value);
  70. }).then(done, failIfThrows(done));
  71. });
  72. it('should reject with a TypeError if given a non-iterable', function (done) {
  73. var notIterable = {};
  74. Promise.all(notIterable).then(
  75. function () {
  76. assert(false, 'should never get here');
  77. },
  78. function (reason) {
  79. assert(reason instanceof TypeError);
  80. }
  81. ).then(done, failIfThrows(done));
  82. });
  83. // test cases from
  84. // https://github.com/domenic/promises-unwrapping/issues/89#issuecomment-33110203
  85. var tamper = function (p) {
  86. // eslint-disable-next-line no-param-reassign
  87. p.then = function (fulfill, reject) {
  88. fulfill('tampered');
  89. return Promise.prototype.then.call(this, fulfill, reject);
  90. };
  91. return p;
  92. };
  93. it('should be robust against tampering (1)', function (done) {
  94. var g = [tamper(Promise.resolve(0))];
  95. // Prevent countdownHolder.[[Countdown]] from ever reaching zero
  96. Promise.all(g).then(
  97. function () { done(); },
  98. failIfThrows(done)
  99. );
  100. });
  101. it('should be robust against tampering (2)', function (done) {
  102. // Promise from Promise.all resolved before arguments
  103. var fulfillCalled = false;
  104. var g = [
  105. Promise.resolve(0),
  106. tamper(Promise.resolve(1)),
  107. Promise.resolve(2).then(function () {
  108. assert(!fulfillCalled, 'should be resolved before all()');
  109. }).then(function () {
  110. assert(!fulfillCalled, 'should be resolved before all()');
  111. })['catch'](failIfThrows(done))
  112. ];
  113. Promise.all(g).then(function () {
  114. assert(!fulfillCalled, 'should be resolved last');
  115. fulfillCalled = true;
  116. }).then(done, failIfThrows(done));
  117. });
  118. it('should be robust against tampering (3)', function (done) {
  119. var g = [
  120. Promise.resolve(0),
  121. tamper(Promise.resolve(1)),
  122. Promise.reject(2)
  123. ];
  124. // Promise from Promise.all resolved despite rejected promise in arguments
  125. Promise.all(g).then(function () {
  126. throw new Error('should not reach here!');
  127. }, function (e) {
  128. assert.strictEqual(e, 2);
  129. }).then(done, failIfThrows(done));
  130. });
  131. it('should be robust against tampering (4)', function (done) {
  132. var hijack = true;
  133. var actualArguments = [];
  134. var P = function (resolver) {
  135. var self;
  136. if (hijack) {
  137. hijack = false;
  138. self = new Promise(function (resolve, reject) {
  139. resolver(function (values) {
  140. // record arguments & # of times resolve function is called
  141. actualArguments.push(values.slice());
  142. return resolve(values);
  143. }, reject);
  144. });
  145. } else {
  146. self = new Promise(resolver);
  147. }
  148. Object.setPrototypeOf(self, P.prototype);
  149. return self;
  150. };
  151. if (!Object.setPrototypeOf) { return done(); } // skip test if on IE < 11
  152. Object.setPrototypeOf(P, Promise);
  153. P.prototype = Object.create(Promise.prototype, {
  154. constructor: { value: P }
  155. });
  156. P.resolve = function (p) { return p; };
  157. var g = [
  158. Promise.resolve(0),
  159. tamper(Promise.resolve(1)),
  160. Promise.resolve(2)
  161. ];
  162. // Promise.all calls resolver twice
  163. P.all(g)['catch'](failIfThrows(done));
  164. Promise.resolve().then(function () {
  165. assert.deepEqual(actualArguments, [[0, 'tampered', 2]]);
  166. }).then(done, failIfThrows(done));
  167. });
  168. });