12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165 |
- // Copyright 2013 The Closure Library Authors. All Rights Reserved.
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS-IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- goog.provide('goog.PromiseTest');
- goog.require('goog.Promise');
- goog.require('goog.Thenable');
- goog.require('goog.Timer');
- goog.require('goog.functions');
- goog.require('goog.testing.MockClock');
- goog.require('goog.testing.PropertyReplacer');
- goog.require('goog.testing.TestCase');
- goog.require('goog.testing.jsunit');
- goog.require('goog.testing.recordFunction');
- goog.require('goog.userAgent');
- goog.setTestOnly('goog.PromiseTest');
- function setUpPage() {
- goog.testing.TestCase.getActiveTestCase().promiseTimeout = 10000; // 10s
- }
- // TODO(brenneman):
- // - Add tests for interoperability with native Promises where available.
- // - Add tests for long stack traces.
- var SUPPORTS_ACCESSORS = !!window.Object.defineProperty &&
- // IE8 and Safari<5.1 have an Object.defineProperty which does not work on
- // some objects.
- (!goog.userAgent.IE || goog.userAgent.isVersionOrHigher('9')) &&
- (!goog.userAgent.SAFARI || goog.userAgent.isVersionOrHigher('534.48.3'));
- var mockClock = new goog.testing.MockClock();
- var stubs = new goog.testing.PropertyReplacer();
- var unhandledRejections;
- // Simple shared objects used as test values.
- var dummy = {toString: goog.functions.constant('[object dummy]')};
- var sentinel = {toString: goog.functions.constant('[object sentinel]')};
- function setUp() {
- unhandledRejections = goog.testing.recordFunction();
- goog.Promise.setUnhandledRejectionHandler(unhandledRejections);
- }
- function tearDown() {
- // The system should leave no pending unhandled rejections. Advance the mock
- // clock (if installed) to catch any rethrows waiting in the queue.
- mockClock.tick(Infinity);
- mockClock.uninstall();
- mockClock.reset();
- stubs.reset();
- }
- /**
- * Dummy onfulfilled or onrejected function that should not be called.
- *
- * @param {*} result The result passed into the callback.
- */
- function shouldNotCall(result) {
- fail('This should not have been called (result: ' + String(result) + ')');
- }
- function fulfillSoon(value, delay) {
- return new goog.Promise(function(resolve, reject) {
- window.setTimeout(function() { resolve(value); }, delay);
- });
- }
- function fulfillThenableSoon(value, delay) {
- return createThenable(value, delay, true /* fulfilled */);
- }
- function fulfillBuiltInSoon(value, delay) {
- // If the environment does not provide a built-in Promise, then just use
- // goog.Promise instead to allow tests which use this to continue.
- if (!window.Promise) {
- return fulfillSoon(value, delay);
- }
- return new window.Promise(function(resolve, reject) {
- window.setTimeout(function() { resolve(value); }, delay);
- });
- }
- function rejectSoon(reason, delay) {
- return new goog.Promise(function(resolve, reject) {
- window.setTimeout(function() { reject(reason); }, delay);
- });
- }
- function rejectThenableSoon(value, delay) {
- return createThenable(value, delay, false /* fulfilled */);
- }
- function rejectBuiltInSoon(value, delay) {
- // If the environment does not provide a built-in Promise, then just use
- // goog.Promise instead to allow tests which use this to continue.
- if (!window.Promise) {
- return rejectSoon(value, delay);
- }
- return new window.Promise(function(resolve, reject) {
- window.setTimeout(function() { reject(value); }, delay);
- });
- }
- /**
- * Creates a thenable which isn't formally a promise for testing non-Promise
- * thenables.
- */
- function createThenableResolver() {
- var resolver = goog.Promise.withResolver();
- var thenable = {};
- var then = function(onFulfilled, onRejected) {
- var next = createThenableResolver();
- next.resolve(resolver.promise.then(onFulfilled, onRejected));
- return next.thenable;
- };
- // Count accesses of the {@code then} property when possible. Otherwise, just
- // define the {@code then} method as a regular data property.
- if (SUPPORTS_ACCESSORS) {
- thenable.thenAccesses = 0;
- window.Object.defineProperty(thenable, 'then', {
- get: function() {
- thenable.thenAccesses++;
- return then;
- }
- });
- } else {
- thenable.then = then;
- }
- return {
- resolve: resolver.resolve,
- reject: resolver.reject,
- thenable: thenable
- };
- }
- /**
- * @param {*} value The value the thenable should be fulfilled/rejected with.
- * @param {number} delay The length of the delay until the thenable is resolved.
- * @param {boolean} fulfill Whether to fulfill or reject the thenable.
- * @return {!Thenable}
- */
- function createThenable(value, delay, fulfill) {
- var resolver = createThenableResolver();
- window.setTimeout(function() {
- if (fulfill) {
- resolver.resolve(value);
- } else {
- resolver.reject(value);
- }
- }, delay);
- return resolver.thenable;
- }
- /**
- * Creates a malicious thenable that throws when the {@code then} method is
- * accessed to ensure that it is caught and converted to a rejected promise
- * instead of allowed to cause a synchronous exception.
- * @param {*} value The value to throw.
- * @return {!Thenable}
- */
- function createThrowingThenable(value) {
- // If the environment does not provide Object.defineProperty, then just
- // use an immediately rejected promise to allow tests which use this to
- // continue.
- if (!SUPPORTS_ACCESSORS) {
- return rejectThenableSoon(value, 0);
- }
- var thenable = {};
- window.Object.defineProperty(
- thenable, 'then', {get: function() { throw value; }});
- return thenable;
- }
- function testThenIsFulfilled() {
- var timesCalled = 0;
- var p = new goog.Promise(function(resolve, reject) { resolve(sentinel); });
- p.then(function(value) {
- timesCalled++;
- assertEquals(sentinel, value);
- });
- assertEquals(
- 'then() must return before callbacks are invoked.', 0, timesCalled);
- return p.then(function() {
- assertEquals('onFulfilled must be called exactly once.', 1, timesCalled);
- });
- }
- function testThenVoidIsFulfilled() {
- var timesCalled = 0;
- var p = goog.Promise.resolve(sentinel);
- p.thenVoid(function(value) {
- timesCalled++;
- assertEquals(sentinel, value);
- });
- assertEquals(
- 'thenVoid() must return before callbacks are invoked.', 0, timesCalled);
- return p.then(function() {
- assertEquals('onFulfilled must be called exactly once.', 1, timesCalled);
- });
- }
- function testThenIsRejected() {
- var timesCalled = 0;
- var p = goog.Promise.reject(sentinel);
- p.then(shouldNotCall, function(value) {
- timesCalled++;
- assertEquals(sentinel, value);
- });
- assertEquals(
- 'then() must return before callbacks are invoked.', 0, timesCalled);
- return p.then(shouldNotCall, function() {
- assertEquals('onRejected must be called exactly once.', 1, timesCalled);
- });
- }
- function testThenVoidIsRejected() {
- var timesCalled = 0;
- var p = goog.Promise.reject(sentinel);
- p.thenVoid(shouldNotCall, function(value) {
- timesCalled++;
- assertEquals(sentinel, value);
- assertEquals('onRejected must be called exactly once.', 1, timesCalled);
- });
- assertEquals(
- 'thenVoid() must return before callbacks are invoked.', 0, timesCalled);
- return p.then(shouldNotCall, function() {
- assertEquals('onRejected must be called exactly once.', 1, timesCalled);
- });
- }
- function testThenAsserts() {
- var p = goog.Promise.resolve();
- var m = assertThrows(function() { p.then({}); });
- assertContains('opt_onFulfilled should be a function.', m.message);
- m = assertThrows(function() { p.then(function() {}, {}); });
- assertContains('opt_onRejected should be a function.', m.message);
- }
- function testThenVoidAsserts() {
- var p = goog.Promise.resolve();
- var m = assertThrows(function() { p.thenVoid({}); });
- assertContains('opt_onFulfilled should be a function.', m.message);
- m = assertThrows(function() { p.thenVoid(function() {}, {}); });
- assertContains('opt_onRejected should be a function.', m.message);
- }
- function testOptionalOnFulfilled() {
- return goog.Promise.resolve(sentinel)
- .then(null, null)
- .then(null, shouldNotCall)
- .then(function(value) { assertEquals(sentinel, value); });
- }
- function testOptionalOnRejected() {
- return goog.Promise.reject(sentinel)
- .then(null, null)
- .then(shouldNotCall)
- .then(null, function(reason) { assertEquals(sentinel, reason); });
- }
- function testMultipleResolves() {
- var timesCalled = 0;
- var resolvePromise;
- var p = new goog.Promise(function(resolve, reject) {
- resolvePromise = resolve;
- resolve('foo');
- resolve('bar');
- });
- p.then(function(value) {
- timesCalled++;
- assertEquals('onFulfilled must be called exactly once.', 1, timesCalled);
- });
- // Add one more test for fulfilling after a delay.
- return goog.Timer.promise(10).then(function() {
- resolvePromise('baz');
- assertEquals(1, timesCalled);
- });
- }
- function testMultipleRejects() {
- var timesCalled = 0;
- var rejectPromise;
- var p = new goog.Promise(function(resolve, reject) {
- rejectPromise = reject;
- reject('foo');
- reject('bar');
- });
- p.then(shouldNotCall, function(value) {
- timesCalled++;
- assertEquals('onRejected must be called exactly once.', 1, timesCalled);
- });
- // Add one more test for rejecting after a delay.
- return goog.Timer.promise(10).then(function() {
- rejectPromise('baz');
- assertEquals(1, timesCalled);
- });
- }
- function testAsynchronousThenCalls() {
- var timesCalled = [0, 0, 0, 0];
- var p = new goog.Promise(function(resolve, reject) {
- window.setTimeout(function() { resolve(); }, 30);
- });
- p.then(function() {
- timesCalled[0]++;
- assertArrayEquals([1, 0, 0, 0], timesCalled);
- });
- window.setTimeout(function() {
- p.then(function() {
- timesCalled[1]++;
- assertArrayEquals([1, 1, 0, 0], timesCalled);
- });
- }, 10);
- window.setTimeout(function() {
- p.then(function() {
- timesCalled[2]++;
- assertArrayEquals([1, 1, 1, 0], timesCalled);
- });
- }, 20);
- return goog.Timer.promise(40).then(function() {
- return p.then(function() {
- timesCalled[3]++;
- assertArrayEquals([1, 1, 1, 1], timesCalled);
- });
- });
- }
- function testResolveWithPromise() {
- var resolveBlocker;
- var hasFulfilled = false;
- var blocker =
- new goog.Promise(function(resolve, reject) { resolveBlocker = resolve; });
- var p = goog.Promise.resolve(blocker);
- p.then(function(value) {
- hasFulfilled = true;
- assertEquals(sentinel, value);
- }, shouldNotCall);
- assertFalse(hasFulfilled);
- resolveBlocker(sentinel);
- return p.then(function() { assertTrue(hasFulfilled); });
- }
- function testResolveWithRejectedPromise() {
- var rejectBlocker;
- var hasRejected = false;
- var blocker =
- new goog.Promise(function(resolve, reject) { rejectBlocker = reject; });
- var p = goog.Promise.resolve(blocker);
- var child = p.then(shouldNotCall, function(reason) {
- hasRejected = true;
- assertEquals(sentinel, reason);
- });
- assertFalse(hasRejected);
- rejectBlocker(sentinel);
- return child.thenCatch(function() { assertTrue(hasRejected); });
- }
- function testRejectWithPromise() {
- var resolveBlocker;
- var hasFulfilled = false;
- var blocker =
- new goog.Promise(function(resolve, reject) { resolveBlocker = resolve; });
- var p = goog.Promise.reject(blocker);
- var child = p.then(function(value) {
- hasFulfilled = true;
- assertEquals(sentinel, value);
- }, shouldNotCall);
- assertFalse(hasFulfilled);
- resolveBlocker(sentinel);
- return child.thenCatch(function() { assertTrue(hasRejected); });
- }
- function testRejectWithRejectedPromise() {
- var rejectBlocker;
- var hasRejected = false;
- var blocker =
- new goog.Promise(function(resolve, reject) { rejectBlocker = reject; });
- var p = goog.Promise.reject(blocker);
- var child = p.then(shouldNotCall, function(reason) {
- hasRejected = true;
- assertEquals(sentinel, reason);
- });
- assertFalse(hasRejected);
- rejectBlocker(sentinel);
- return child.thenCatch(function() { assertTrue(hasRejected); });
- }
- function testResolveAndReject() {
- var onFulfilledCalled = false;
- var onRejectedCalled = false;
- var p = new goog.Promise(function(resolve, reject) {
- resolve();
- reject();
- });
- p.then(
- function() { onFulfilledCalled = true; },
- function() { onRejectedCalled = true; });
- return p.then(function() {
- assertTrue(onFulfilledCalled);
- assertFalse(onRejectedCalled);
- });
- }
- function testResolveWithSelfRejects() {
- var r;
- var p = new goog.Promise(function(resolve) { r = resolve; });
- r(p);
- return p.then(shouldNotCall, function(e) {
- assertEquals(e.message, 'Promise cannot resolve to itself');
- });
- }
- function testResolveWithObjectStringResolves() {
- return goog.Promise.resolve('[object Object]').then(function(v) {
- assertEquals(v, '[object Object]');
- });
- }
- function testRejectAndResolve() {
- return new goog
- .Promise(function(resolve, reject) {
- reject();
- resolve();
- })
- .then(shouldNotCall, function() { return true; });
- }
- function testThenReturnsBeforeCallbackWithFulfill() {
- var thenHasReturned = false;
- var p = goog.Promise.resolve();
- var child = p.then(function() {
- assertTrue(
- 'Callback must be called only after then() has returned.',
- thenHasReturned);
- });
- thenHasReturned = true;
- return child;
- }
- function testThenReturnsBeforeCallbackWithReject() {
- var thenHasReturned = false;
- var p = goog.Promise.reject();
- var child = p.then(shouldNotCall, function() {
- assertTrue(
- 'Callback must be called only after then() has returned.',
- thenHasReturned);
- });
- thenHasReturned = true;
- return child;
- }
- function testResolutionOrder() {
- var callbacks = [];
- return goog.Promise.resolve()
- .then(function() { callbacks.push(1); }, shouldNotCall)
- .then(function() { callbacks.push(2); }, shouldNotCall)
- .then(function() { callbacks.push(3); }, shouldNotCall)
- .then(function() {
- assertArrayEquals([1, 2, 3], callbacks);
- });
- }
- function testResolutionOrderWithThrow() {
- var callbacks = [];
- var p = goog.Promise.resolve();
- p.then(function() { callbacks.push(1); }, shouldNotCall);
- var child = p.then(function() {
- callbacks.push(2);
- throw Error();
- }, shouldNotCall);
- child.then(shouldNotCall, function() {
- // The parent callbacks should be evaluated before the child.
- callbacks.push(4);
- });
- p.then(function() { callbacks.push(3); }, shouldNotCall);
- return child.then(shouldNotCall, function() {
- callbacks.push(5);
- assertArrayEquals([1, 2, 3, 4, 5], callbacks);
- });
- }
- function testResolutionOrderWithNestedThen() {
- var resolver = goog.Promise.withResolver();
- var callbacks = [];
- var p = goog.Promise.resolve();
- p.then(function() {
- callbacks.push(1);
- p.then(function() {
- callbacks.push(3);
- resolver.resolve();
- });
- });
- p.then(function() { callbacks.push(2); });
- return resolver.promise.then(function() {
- assertArrayEquals([1, 2, 3], callbacks);
- });
- }
- function testRejectionOrder() {
- var callbacks = [];
- var p = goog.Promise.reject();
- p.then(shouldNotCall, function() { callbacks.push(1); });
- p.then(shouldNotCall, function() { callbacks.push(2); });
- p.then(shouldNotCall, function() { callbacks.push(3); });
- return p.then(shouldNotCall, function() {
- assertArrayEquals([1, 2, 3], callbacks);
- });
- }
- function testRejectionOrderWithThrow() {
- var callbacks = [];
- var p = goog.Promise.reject();
- p.then(shouldNotCall, function() { callbacks.push(1); });
- p.then(shouldNotCall, function() {
- callbacks.push(2);
- throw Error();
- });
- p.then(shouldNotCall, function() { callbacks.push(3); });
- return p.then(shouldNotCall, function() {
- assertArrayEquals([1, 2, 3], callbacks);
- });
- }
- function testRejectionOrderWithNestedThen() {
- var resolver = goog.Promise.withResolver();
- var callbacks = [];
- var p = goog.Promise.reject();
- p.then(shouldNotCall, function() {
- callbacks.push(1);
- p.then(shouldNotCall, function() {
- callbacks.push(3);
- resolver.resolve();
- });
- });
- p.then(shouldNotCall, function() { callbacks.push(2); });
- return resolver.promise.then(function() {
- assertArrayEquals([1, 2, 3], callbacks);
- });
- }
- function testBranching() {
- var p = goog.Promise.resolve(2);
- var branch1 =
- p.then(function(value) {
- assertEquals('then functions should see the same value', 2, value);
- return value / 2;
- }).then(function(value) {
- assertEquals('branch should receive the returned value', 1, value);
- });
- var branch2 =
- p.then(function(value) {
- assertEquals('then functions should see the same value', 2, value);
- throw value + 1;
- }).then(shouldNotCall, function(reason) {
- assertEquals('branch should receive the thrown value', 3, reason);
- });
- var branch3 =
- p.then(function(value) {
- assertEquals('then functions should see the same value', 2, value);
- return value * 2;
- }).then(function(value) {
- assertEquals('branch should receive the returned value', 4, value);
- });
- return goog.Promise.all([branch1, branch2, branch3]);
- }
- function testThenReturnsPromise() {
- var parent = goog.Promise.resolve();
- var child = parent.then();
- assertTrue(child instanceof goog.Promise);
- assertNotEquals(
- 'The returned Promise must be different from the input.', parent, child);
- }
- function testThenVoidReturnsUndefined() {
- var parent = goog.Promise.resolve();
- var child = parent.thenVoid();
- assertUndefined(child);
- }
- function testBlockingPromise() {
- var p = goog.Promise.resolve();
- var wasFulfilled = false;
- var wasRejected = false;
- var p2 = p.then(function() {
- return new goog.Promise(function(resolve, reject) {});
- });
- p2.then(
- function() { wasFulfilled = true; }, function() { wasRejected = true; });
- return goog.Timer.promise(10).then(function() {
- assertFalse('p2 should be blocked on the returned Promise', wasFulfilled);
- assertFalse('p2 should be blocked on the returned Promise', wasRejected);
- });
- }
- function testBlockingPromiseFulfilled() {
- var blockingPromise = new goog.Promise(function(resolve, reject) {
- window.setTimeout(function() { resolve(sentinel); }, 0);
- });
- var p = goog.Promise.resolve(dummy);
- var p2 = p.then(function(value) { return blockingPromise; });
- return p2.then(function(value) { assertEquals(sentinel, value); });
- }
- function testBlockingPromiseRejected() {
- var blockingPromise = new goog.Promise(function(resolve, reject) {
- window.setTimeout(function() { reject(sentinel); }, 0);
- });
- var p = goog.Promise.resolve(blockingPromise);
- return p.then(
- shouldNotCall, function(reason) { assertEquals(sentinel, reason); });
- }
- function testBlockingThenableFulfilled() {
- var thenable = {then: function(onFulfill, onReject) { onFulfill(sentinel); }};
- return goog.Promise.resolve(thenable).then(function(reason) {
- assertEquals(sentinel, reason);
- });
- }
- function testBlockingThenableRejected() {
- var thenable = {then: function(onFulfill, onReject) { onReject(sentinel); }};
- return goog.Promise.resolve(thenable).then(
- shouldNotCall, function(reason) { assertEquals(sentinel, reason); });
- }
- function testBlockingThenableThrows() {
- var thenable = {then: function(onFulfill, onReject) { throw sentinel; }};
- return goog.Promise.resolve(thenable).then(
- shouldNotCall, function(reason) { assertEquals(sentinel, reason); });
- }
- function testBlockingThenableMisbehaves() {
- var thenable = {
- then: function(onFulfill, onReject) {
- onFulfill(sentinel);
- onFulfill(dummy);
- onReject(dummy);
- throw dummy;
- }
- };
- return goog.Promise.resolve(thenable).then(function(value) {
- assertEquals(
- 'Only the first resolution of the Thenable should have a result.',
- sentinel, value);
- });
- }
- function testNestingThenables() {
- var thenableA = {
- then: function(onFulfill, onReject) { onFulfill(sentinel); }
- };
- var thenableB = {
- then: function(onFulfill, onReject) { onFulfill(thenableA); }
- };
- var thenableC = {
- then: function(onFulfill, onReject) { onFulfill(thenableB); }
- };
- return goog.Promise.resolve(thenableC).then(function(value) {
- assertEquals(
- 'Should resolve to the fulfillment value of thenableA', sentinel,
- value);
- });
- }
- function testNestingThenablesRejected() {
- var thenableA = {then: function(onFulfill, onReject) { onReject(sentinel); }};
- var thenableB = {
- then: function(onFulfill, onReject) { onReject(thenableA); }
- };
- var thenableC = {
- then: function(onFulfill, onReject) { onReject(thenableB); }
- };
- return goog.Promise.reject(thenableC).then(shouldNotCall, function(reason) {
- assertEquals(
- 'Should resolve to rejection reason of thenableA', sentinel, reason);
- });
- }
- function testThenCatch() {
- var catchCalled = false;
- return goog.Promise.reject()
- .thenCatch(function(reason) {
- catchCalled = true;
- return sentinel;
- })
- .then(function(value) {
- assertTrue(catchCalled);
- assertEquals(sentinel, value);
- });
- }
- function testRaceWithEmptyList() {
- return goog.Promise.race([]).then(function(value) {
- assertUndefined(value);
- });
- }
- function testRaceWithFulfill() {
- var a = fulfillSoon('a', 40);
- var b = fulfillSoon('b', 30);
- var c = fulfillSoon('c', 10);
- var d = fulfillSoon('d', 20);
- return goog.Promise.race([a, b, c, d])
- .then(function(value) {
- assertEquals('c', value);
- // Return the slowest input promise to wait for it to complete.
- return a;
- })
- .then(function(value) {
- assertEquals(
- 'The slowest promise should resolve eventually.', 'a', value);
- });
- }
- function testRaceWithThenables() {
- var a = fulfillThenableSoon('a', 40);
- var b = fulfillThenableSoon('b', 30);
- var c = fulfillThenableSoon('c', 10);
- var d = fulfillThenableSoon('d', 20);
- return goog.Promise.race([a, b, c, d])
- .then(function(value) {
- assertEquals('c', value);
- // Ensure that the {@code then} property was only accessed once by
- // {@code goog.Promise.race}.
- if (SUPPORTS_ACCESSORS) {
- assertEquals(1, c.thenAccesses);
- }
- // Return the slowest input thenable to wait for it to complete.
- return a;
- })
- .then(function(value) {
- assertEquals(
- 'The slowest thenable should resolve eventually.', 'a', value);
- });
- }
- function testRaceWithBuiltIns() {
- var a = fulfillBuiltInSoon('a', 40);
- var b = fulfillBuiltInSoon('b', 30);
- var c = fulfillBuiltInSoon('c', 10);
- var d = fulfillBuiltInSoon('d', 20);
- return goog.Promise.race([a, b, c, d])
- .then(function(value) {
- assertEquals('c', value);
- // Return the slowest input promise to wait for it to complete.
- return a;
- })
- .then(function(value) {
- assertEquals(
- 'The slowest promise should resolve eventually.', 'a', value);
- });
- }
- function testRaceWithNonThenable() {
- var a = fulfillSoon('a', 40);
- var b = 'b';
- var c = fulfillSoon('c', 10);
- var d = fulfillSoon('d', 20);
- return goog.Promise.race([a, b, c, d])
- .then(function(value) {
- assertEquals('b', value);
- // Return the slowest input promise to wait for it to complete.
- return a;
- })
- .then(function(value) {
- assertEquals(
- 'The slowest promise should resolve eventually.', 'a', value);
- });
- }
- function testRaceWithFalseyNonThenable() {
- var a = fulfillSoon('a', 40);
- var b = 0;
- var c = fulfillSoon('c', 10);
- var d = fulfillSoon('d', 20);
- return goog.Promise.race([a, b, c, d])
- .then(function(value) {
- assertEquals(0, value);
- // Return the slowest input promise to wait for it to complete.
- return a;
- })
- .then(function(value) {
- assertEquals(
- 'The slowest promise should resolve eventually.', 'a', value);
- });
- }
- function testRaceWithFulfilledBeforeNonThenable() {
- var a = fulfillSoon('a', 40);
- var b = goog.Promise.resolve('b');
- var c = 'c';
- var d = fulfillSoon('d', 20);
- return goog.Promise.race([a, b, c, d])
- .then(function(value) {
- assertEquals('b', value);
- // Return the slowest input promise to wait for it to complete.
- return a;
- })
- .then(function(value) {
- assertEquals(
- 'The slowest promise should resolve eventually.', 'a', value);
- });
- }
- function testRaceWithReject() {
- var a = rejectSoon('rejected-a', 40);
- var b = rejectSoon('rejected-b', 30);
- var c = rejectSoon('rejected-c', 10);
- var d = rejectSoon('rejected-d', 20);
- return goog.Promise.race([a, b, c, d])
- .then(
- shouldNotCall,
- function(value) {
- assertEquals('rejected-c', value);
- return a;
- })
- .then(shouldNotCall, function(reason) {
- assertEquals(
- 'The slowest promise should resolve eventually.', 'rejected-a',
- reason);
- });
- }
- function testRaceWithRejectThenable() {
- var a = rejectThenableSoon('rejected-a', 40);
- var b = rejectThenableSoon('rejected-b', 30);
- var c = rejectThenableSoon('rejected-c', 10);
- var d = rejectThenableSoon('rejected-d', 20);
- return goog.Promise.race([a, b, c, d])
- .then(
- shouldNotCall,
- function(value) {
- assertEquals('rejected-c', value);
- return a;
- })
- .then(shouldNotCall, function(reason) {
- assertEquals(
- 'The slowest promise should resolve eventually.', 'rejected-a',
- reason);
- });
- }
- function testRaceWithRejectBuiltIn() {
- var a = rejectBuiltInSoon('rejected-a', 40);
- var b = rejectBuiltInSoon('rejected-b', 30);
- var c = rejectBuiltInSoon('rejected-c', 10);
- var d = rejectBuiltInSoon('rejected-d', 20);
- return goog.Promise.race([a, b, c, d])
- .then(
- shouldNotCall,
- function(value) {
- assertEquals('rejected-c', value);
- return a;
- })
- .then(shouldNotCall, function(reason) {
- assertEquals(
- 'The slowest promise should resolve eventually.', 'rejected-a',
- reason);
- });
- }
- function testRaceWithRejectAndThrowingThenable() {
- var a = rejectSoon('rejected-a', 40);
- var b = rejectThenableSoon('rejected-b', 30);
- var c = rejectBuiltInSoon('rejected-c', 10);
- var d = createThrowingThenable('rejected-d');
- return goog.Promise.race([a, b, c, d])
- .then(
- shouldNotCall,
- function(value) {
- assertEquals('rejected-d', value);
- return a;
- })
- .then(shouldNotCall, function(reason) {
- assertEquals(
- 'The slowest promise should resolve eventually.', 'rejected-a',
- reason);
- });
- }
- function testAllWithEmptyList() {
- return goog.Promise.all([]).then(function(value) {
- assertArrayEquals([], value);
- });
- }
- function testAllWithFulfill() {
- var a = fulfillSoon('a', 40);
- var b = fulfillSoon('b', 30);
- var c = fulfillSoon('c', 10);
- var d = fulfillSoon('d', 20);
- // Test a falsey value.
- var z = fulfillSoon(0, 30);
- return goog.Promise.all([a, b, c, d, z]).then(function(value) {
- assertArrayEquals(['a', 'b', 'c', 'd', 0], value);
- });
- }
- function testAllWithThenable() {
- var a = fulfillSoon('a', 40);
- var b = fulfillThenableSoon('b', 30);
- var c = fulfillSoon('c', 10);
- var d = fulfillSoon('d', 20);
- return goog.Promise.all([a, b, c, d]).then(function(value) {
- assertArrayEquals(['a', 'b', 'c', 'd'], value);
- // Ensure that the {@code then} property was only accessed once by
- // {@code goog.Promise.all}.
- if (SUPPORTS_ACCESSORS) {
- assertEquals(1, b.thenAccesses);
- }
- });
- }
- function testAllWithBuiltIn() {
- var a = fulfillSoon('a', 40);
- var b = fulfillBuiltInSoon('b', 30);
- var c = fulfillSoon('c', 10);
- var d = fulfillSoon('d', 20);
- return goog.Promise.all([a, b, c, d]).then(function(value) {
- assertArrayEquals(['a', 'b', 'c', 'd'], value);
- });
- }
- function testAllWithNonThenable() {
- var a = fulfillSoon('a', 40);
- var b = 'b';
- var c = fulfillSoon('c', 10);
- var d = fulfillSoon('d', 20);
- // Test a falsey value.
- var z = 0;
- return goog.Promise.all([a, b, c, d, z]).then(function(value) {
- assertArrayEquals(['a', 'b', 'c', 'd', 0], value);
- });
- }
- function testAllWithReject() {
- var a = fulfillSoon('a', 40);
- var b = rejectSoon('rejected-b', 30);
- var c = fulfillSoon('c', 10);
- var d = fulfillSoon('d', 20);
- return goog.Promise.all([a, b, c, d])
- .then(
- shouldNotCall,
- function(reason) {
- assertEquals('rejected-b', reason);
- return a;
- })
- .then(function(value) {
- assertEquals(
- 'Promise "a" should be fulfilled even though the all()' +
- 'was rejected.',
- 'a', value);
- });
- }
- function testAllSettledWithEmptyList() {
- return goog.Promise.allSettled([]).then(function(results) {
- assertArrayEquals([], results);
- });
- }
- function testAllSettledWithFulfillAndReject() {
- var a = fulfillSoon('a', 40);
- var b = rejectSoon('rejected-b', 30);
- var c = 'c';
- var d = rejectBuiltInSoon('rejected-d', 20);
- var e = fulfillThenableSoon('e', 40);
- var f = fulfillBuiltInSoon('f', 30);
- var g = rejectThenableSoon('rejected-g', 10);
- var h = createThrowingThenable('rejected-h');
- // Test a falsey value.
- var z = 0;
- return goog.Promise.allSettled([a, b, c, d, e, f, g, h, z])
- .then(function(results) {
- assertArrayEquals(
- [
- {fulfilled: true, value: 'a'},
- {fulfilled: false, reason: 'rejected-b'},
- {fulfilled: true, value: 'c'},
- {fulfilled: false, reason: 'rejected-d'},
- {fulfilled: true, value: 'e'}, {fulfilled: true, value: 'f'},
- {fulfilled: false, reason: 'rejected-g'},
- {fulfilled: false, reason: 'rejected-h'},
- {fulfilled: true, value: 0}
- ],
- results);
- // Ensure that the {@code then} property was only accessed once by
- // {@code goog.Promise.allSettled}.
- if (SUPPORTS_ACCESSORS) {
- assertEquals(1, e.thenAccesses);
- assertEquals(1, g.thenAccesses);
- }
- });
- }
- function testFirstFulfilledWithEmptyList() {
- return goog.Promise.firstFulfilled([]).then(function(value) {
- assertUndefined(value);
- });
- }
- function testFirstFulfilledWithFulfill() {
- var a = fulfillSoon('a', 40);
- var b = rejectSoon('rejected-b', 30);
- var c = rejectSoon('rejected-c', 10);
- var d = fulfillSoon('d', 20);
- return goog.Promise.firstFulfilled([a, b, c, d])
- .then(function(value) {
- assertEquals('d', value);
- return c;
- })
- .then(
- shouldNotCall,
- function(reason) {
- assertEquals(
- 'Promise "c" should be rejected before firstFulfilled() resolves.',
- 'rejected-c', reason);
- return a;
- })
- .then(function(value) {
- assertEquals(
- 'Promise "a" should be fulfilled after firstFulfilled() resolves.',
- 'a', value);
- });
- }
- function testFirstFulfilledWithThenables() {
- var a = fulfillThenableSoon('a', 40);
- var b = rejectThenableSoon('rejected-b', 30);
- var c = rejectThenableSoon('rejected-c', 10);
- var d = fulfillThenableSoon('d', 20);
- return goog.Promise.firstFulfilled([a, b, c, d])
- .then(function(value) {
- assertEquals('d', value);
- // Ensure that the {@code then} property was only accessed once by
- // {@code goog.Promise.firstFulfilled}.
- if (SUPPORTS_ACCESSORS) {
- assertEquals(1, d.thenAccesses);
- }
- return c;
- })
- .then(
- shouldNotCall,
- function(reason) {
- assertEquals(
- 'Thenable "c" should be rejected before firstFulfilled() resolves.',
- 'rejected-c', reason);
- return a;
- })
- .then(function(value) {
- assertEquals(
- 'Thenable "a" should be fulfilled after firstFulfilled() resolves.',
- 'a', value);
- });
- }
- function testFirstFulfilledWithBuiltIns() {
- var a = fulfillBuiltInSoon('a', 40);
- var b = rejectBuiltInSoon('rejected-b', 30);
- var c = rejectBuiltInSoon('rejected-c', 10);
- var d = fulfillBuiltInSoon('d', 20);
- return goog.Promise.firstFulfilled([a, b, c, d])
- .then(function(value) {
- assertEquals('d', value);
- return c;
- })
- .then(
- shouldNotCall,
- function(reason) {
- assertEquals(
- 'Promise "c" should be rejected before firstFulfilled() resolves.',
- 'rejected-c', reason);
- return a;
- })
- .then(function(value) {
- assertEquals(
- 'Promise "a" should be fulfilled after firstFulfilled() resolves.',
- 'a', value);
- });
- }
- function testFirstFulfilledWithNonThenable() {
- var a = fulfillSoon('a', 40);
- var b = rejectSoon('rejected-b', 30);
- var c = rejectSoon('rejected-c', 10);
- var d = 'd';
- return goog.Promise.firstFulfilled([a, b, c, d])
- .then(function(value) {
- assertEquals('d', value);
- // Return the slowest input promise to wait for it to complete.
- return a;
- })
- .then(function(value) {
- assertEquals(
- 'The slowest promise should resolve eventually.', 'a', value);
- });
- }
- function testFirstFulfilledWithFalseyNonThenable() {
- var a = fulfillSoon('a', 40);
- var b = rejectSoon('rejected-b', 30);
- var c = rejectSoon('rejected-c', 10);
- var d = 0;
- return goog.Promise.firstFulfilled([a, b, c, d])
- .then(function(value) {
- assertEquals(0, value);
- // Return the slowest input promise to wait for it to complete.
- return a;
- })
- .then(function(value) {
- assertEquals(
- 'The slowest promise should resolve eventually.', 'a', value);
- });
- }
- function testFirstFulfilledWithFulfilledBeforeNonThenable() {
- var a = fulfillSoon('a', 40);
- var b = goog.Promise.resolve('b');
- var c = rejectSoon('rejected-c', 10);
- var d = 'd';
- return goog.Promise.firstFulfilled([a, b, c, d])
- .then(function(value) {
- assertEquals('b', value);
- // Return the slowest input promise to wait for it to complete.
- return a;
- })
- .then(function(value) {
- assertEquals(
- 'The slowest promise should resolve eventually.', 'a', value);
- });
- }
- function testFirstFulfilledWithReject() {
- var a = rejectSoon('rejected-a', 40);
- var b = rejectThenableSoon('rejected-b', 30);
- var c = rejectBuiltInSoon('rejected-c', 10);
- var d = createThrowingThenable('rejected-d');
- return goog.Promise.firstFulfilled([a, b, c, d])
- .then(shouldNotCall, function(reason) {
- assertArrayEquals(
- ['rejected-a', 'rejected-b', 'rejected-c', 'rejected-d'], reason);
- // Ensure that the {@code then} property was only accessed once by
- // {@code goog.Promise.firstFulfilled}.
- if (SUPPORTS_ACCESSORS) {
- assertEquals(1, b.thenAccesses);
- }
- });
- }
- function testThenAlwaysWithFulfill() {
- var thenAlwaysCalled = false;
- return goog.Promise.resolve(sentinel)
- .thenAlways(function() {
- assertEquals(
- 'thenAlways should have no arguments', 0, arguments.length);
- thenAlwaysCalled = true;
- })
- .then(function(value) {
- assertEquals(sentinel, value);
- assertTrue(thenAlwaysCalled);
- });
- }
- function testThenAlwaysWithReject() {
- var thenAlwaysCalled = false;
- return goog.Promise.reject(sentinel)
- .thenAlways(function(arg) {
- assertEquals(
- 'thenAlways should have no arguments', 0, arguments.length);
- thenAlwaysCalled = true;
- })
- .then(shouldNotCall, function(err) {
- assertEquals(sentinel, err);
- return null;
- });
- }
- function testThenAlwaysCalledMultipleTimes() {
- var calls = [];
- var p = goog.Promise.resolve(sentinel);
- p.then(function(value) {
- assertEquals(sentinel, value);
- calls.push(1);
- return value;
- });
- p.thenAlways(function() {
- assertEquals(0, arguments.length);
- calls.push(2);
- throw Error('thenAlways throw');
- });
- p.then(function(value) {
- assertEquals(
- 'Promise result should not mutate after throw from thenAlways.',
- sentinel, value);
- calls.push(3);
- });
- p.thenAlways(function() { assertArrayEquals([1, 2, 3], calls); });
- p.thenAlways(function() {
- assertEquals(
- 'Should be one unhandled exception from the "thenAlways throw".', 1,
- unhandledRejections.getCallCount());
- var rejectionCall = unhandledRejections.popLastCall();
- assertEquals(1, rejectionCall.getArguments().length);
- var err = rejectionCall.getArguments()[0];
- assertEquals('thenAlways throw', err.message);
- assertEquals(goog.global, rejectionCall.getThis());
- });
- return p.thenAlways(function() { assertEquals(3, calls.length); });
- }
- function testContextWithInit() {
- var initContext;
- var p = new goog.Promise(function(resolve, reject) {
- initContext = this;
- }, sentinel);
- assertEquals(sentinel, initContext);
- }
- function testContextWithInitDefault() {
- var initContext;
- var p = new goog.Promise(function(resolve, reject) { initContext = this; });
- assertEquals(
- 'initFunc should default to being called in the global scope',
- goog.global, initContext);
- }
- function testContextWithFulfillment() {
- return goog.Promise.resolve()
- .then(function() {
- assertEquals(
- 'Call should be made in the global scope if no context is specified.',
- goog.global, this);
- })
- .then(
- function() { assertEquals(sentinel, this); }, shouldNotCall, sentinel)
- .thenAlways(function() { assertEquals(sentinel, this); }, sentinel);
- }
- function testContextWithRejection() {
- return goog.Promise.reject()
- .then(
- shouldNotCall,
- function() {
- assertEquals(
- 'Call should be in the default scope when no context is set.',
- goog.global, this);
- throw new Error('Intentional rejection');
- })
- .then(
- shouldNotCall, function() { assertEquals(sentinel, this); }, sentinel)
- .thenAlways(function() { assertEquals(sentinel, this); }, sentinel)
- .thenCatch(function() { assertEquals(sentinel, this); }, sentinel);
- }
- function testCancel() {
- var p = new goog.Promise(goog.nullFunction);
- var child = p.then(shouldNotCall, function(reason) {
- assertTrue(reason instanceof goog.Promise.CancellationError);
- assertEquals('cancellation message', reason.message);
- // Return a non-Error to resolve the cancellation rejection.
- return null;
- });
- p.cancel('cancellation message');
- return child;
- }
- function testThenVoidCancel() {
- var thenVoidCalled = false;
- var p = new goog.Promise(goog.nullFunction);
- p.thenVoid(shouldNotCall, function(reason) {
- assertTrue(reason instanceof goog.Promise.CancellationError);
- assertEquals('cancellation message', reason.message);
- thenVoidCalled = true;
- });
- p.cancel('cancellation message');
- assertFalse(thenVoidCalled);
- return p.thenCatch(function() {
- assertTrue(thenVoidCalled);
- // Return a non-Error to resolve the cancellation rejection.
- return null;
- });
- }
- function testCancelAfterResolve() {
- var p = goog.Promise.resolve();
- p.cancel();
- return p.then(null, shouldNotCall);
- }
- function testThenVoidCancelAfterResolve() {
- var p = goog.Promise.resolve();
- p.cancel();
- p.thenVoid(null, shouldNotCall);
- return p;
- }
- function testCancelAfterReject() {
- var p = goog.Promise.reject(sentinel);
- p.cancel();
- return p.then(
- shouldNotCall, function(reason) { assertEquals(sentinel, reason); });
- }
- function testThenVoidCancelAfterReject() {
- var thenVoidCalled = false;
- var p = goog.Promise.reject(sentinel);
- p.cancel();
- p.thenVoid(shouldNotCall, function(reason) {
- assertEquals(sentinel, reason);
- thenVoidCalled = true;
- });
- return p.thenCatch(function() { assertTrue(thenVoidCalled); });
- }
- function testCancelPropagation() {
- var cancelError;
- var p = new goog.Promise(goog.nullFunction);
- var p2 = p.then(shouldNotCall, function(reason) {
- cancelError = reason;
- assertTrue(reason instanceof goog.Promise.CancellationError);
- assertEquals('parent cancel message', reason.message);
- return sentinel;
- }).then(function(value) {
- assertEquals(
- 'Child promises should receive the returned value of the parent.',
- sentinel, value);
- }, shouldNotCall);
- var p3 = p.then(shouldNotCall, function(reason) {
- assertEquals(
- 'Every onRejected handler should receive the same cancel error.',
- cancelError, reason);
- assertEquals('parent cancel message', reason.message);
- // Return a non-Error to resolve the cancellation rejection.
- return null;
- });
- p.cancel('parent cancel message');
- return goog.Promise.all([p2, p3]);
- }
- function testThenVoidCancelPropagation() {
- var resolver = goog.Promise.withResolver();
- var toResolveCount = 2;
- var partialResolve = function() {
- if (--toResolveCount == 0) {
- resolver.resolve();
- }
- };
- var cancelError;
- var p = new goog.Promise(goog.nullFunction);
- var p2 = p.then(shouldNotCall, function(reason) {
- cancelError = reason;
- assertTrue(reason instanceof goog.Promise.CancellationError);
- assertEquals('parent cancel message', reason.message);
- return sentinel;
- });
- p2.thenVoid(function(value) {
- assertEquals(
- 'Child promises should receive the returned value of the parent.',
- sentinel, value);
- partialResolve();
- }, shouldNotCall);
- p.thenVoid(shouldNotCall, function(reason) {
- assertEquals(
- 'Every onRejected handler should receive the same cancel error.',
- cancelError, reason);
- assertEquals('parent cancel message', reason.message);
- partialResolve();
- });
- p.cancel('parent cancel message');
- return resolver.promise;
- }
- function testCancelPropagationUpward() {
- var cancelError;
- var cancelCalls = [];
- var parent = new goog.Promise(goog.nullFunction);
- var child = parent.then(shouldNotCall, function(reason) {
- assertTrue(reason instanceof goog.Promise.CancellationError);
- assertEquals('grandChild cancel message', reason.message);
- cancelError = reason;
- cancelCalls.push('parent');
- });
- var grandChild = child.then(shouldNotCall, function(reason) {
- assertEquals(
- 'Child should receive the same cancel error.', cancelError, reason);
- cancelCalls.push('child');
- });
- var descendant = grandChild.then(shouldNotCall, function(reason) {
- assertEquals(
- 'GrandChild should receive the same cancel error.', cancelError,
- reason);
- cancelCalls.push('grandChild');
- assertArrayEquals(
- 'Each promise in the hierarchy has a single child, so canceling the ' +
- 'grandChild should cancel each ancestor in order.',
- ['parent', 'child', 'grandChild'], cancelCalls);
- // Return a non-Error to resolve the cancellation rejection.
- return null;
- });
- grandChild.cancel('grandChild cancel message');
- return descendant;
- }
- function testThenVoidCancelPropagationUpward() {
- var cancelError;
- var cancelCalls = [];
- var parent = new goog.Promise(goog.nullFunction);
- var child = parent.then(shouldNotCall, function(reason) {
- assertTrue(reason instanceof goog.Promise.CancellationError);
- assertEquals('grandChild cancel message', reason.message);
- cancelError = reason;
- cancelCalls.push('parent');
- });
- var grandChild = child.then(shouldNotCall, function(reason) {
- assertEquals(
- 'Child should receive the same cancel error.', cancelError, reason);
- cancelCalls.push('child');
- });
- grandChild.thenVoid(shouldNotCall, function(reason) {
- assertEquals(
- 'GrandChild should receive the same cancel error.', cancelError,
- reason);
- cancelCalls.push('grandChild');
- });
- grandChild.cancel('grandChild cancel message');
- return grandChild.thenCatch(function(reason) {
- assertEquals(cancelError, reason);
- assertArrayEquals(
- 'Each promise in the hierarchy has a single child, so canceling the ' +
- 'grandChild should cancel each ancestor in order.',
- ['parent', 'child', 'grandChild'], cancelCalls);
- // Return a non-Error to resolve the cancellation rejection.
- return null;
- });
- }
- function testCancelPropagationUpwardWithMultipleChildren() {
- var cancelError;
- var cancelCalls = [];
- var parent = fulfillSoon(sentinel, 0);
- parent.then(function(value) {
- assertEquals(
- 'Non-canceled callbacks should be called after a sibling is canceled.',
- sentinel, value);
- });
- var child = parent.then(shouldNotCall, function(reason) {
- assertTrue(reason instanceof goog.Promise.CancellationError);
- assertEquals('grandChild cancel message', reason.message);
- cancelError = reason;
- cancelCalls.push('child');
- });
- var grandChild = child.then(shouldNotCall, function(reason) {
- assertEquals(reason, cancelError);
- cancelCalls.push('grandChild');
- });
- grandChild.cancel('grandChild cancel message');
- return grandChild.then(shouldNotCall, function(reason) {
- assertEquals(reason, cancelError);
- assertArrayEquals(
- 'The parent promise has multiple children, so only the child and ' +
- 'grandChild should be canceled.',
- ['child', 'grandChild'], cancelCalls);
- // Return a non-Error to resolve the cancellation rejection.
- return null;
- });
- }
- function testThenVoidCancelPropagationUpwardWithMultipleChildren() {
- var cancelError;
- var cancelCalls = [];
- var parent = fulfillSoon(sentinel, 0);
- parent.thenVoid(function(value) {
- assertEquals(
- 'Non-canceled callbacks should be called after a sibling is canceled.',
- sentinel, value);
- }, shouldNotCall);
- var child = parent.then(shouldNotCall, function(reason) {
- assertTrue(reason instanceof goog.Promise.CancellationError);
- assertEquals('grandChild cancel message', reason.message);
- cancelError = reason;
- cancelCalls.push('child');
- });
- var grandChild = child.then(shouldNotCall, function(reason) {
- assertEquals(reason, cancelError);
- cancelCalls.push('grandChild');
- });
- grandChild.cancel('grandChild cancel message');
- grandChild.thenVoid(shouldNotCall, function(reason) {
- assertEquals(reason, cancelError);
- cancelCalls.push('void grandChild');
- });
- return grandChild.then(shouldNotCall, function(reason) {
- assertEquals(reason, cancelError);
- assertArrayEquals(
- 'The parent promise has multiple children, so only the child and ' +
- 'grandChildren should be canceled.',
- ['child', 'grandChild', 'void grandChild'], cancelCalls);
- // Return a non-Error to resolve the cancellation rejection.
- return null;
- });
- }
- function testCancelRecovery() {
- var cancelError;
- var cancelCalls = [];
- var parent = fulfillSoon(sentinel, 100);
- var sibling1 = parent.then(function(value) {
- assertEquals(
- 'Non-canceled callbacks should be called after a sibling is canceled.',
- sentinel, value);
- });
- var sibling2 = parent.then(shouldNotCall, function(reason) {
- assertTrue(reason instanceof goog.Promise.CancellationError);
- cancelError = reason;
- cancelCalls.push('sibling2');
- return sentinel;
- });
- var grandChild = sibling2.then(function(value) {
- cancelCalls.push('child');
- assertEquals(
- 'Returning a non-cancel value should uncancel the grandChild.', value,
- sentinel);
- assertArrayEquals(['sibling2', 'child'], cancelCalls);
- }, shouldNotCall);
- grandChild.cancel();
- return goog.Promise.all([sibling1, grandChild]);
- }
- function testCancellationError() {
- var err = new goog.Promise.CancellationError('cancel message');
- assertTrue(err instanceof Error);
- assertTrue(err instanceof goog.Promise.CancellationError);
- assertEquals('cancel', err.name);
- assertEquals('cancel message', err.message);
- }
- function testMockClock() {
- mockClock.install();
- var resolveA;
- var resolveB;
- var calls = [];
- var p = new goog.Promise(function(resolve, reject) { resolveA = resolve; });
- p.then(function(value) {
- assertEquals(sentinel, value);
- calls.push('then');
- });
- var fulfilledChild = p.then(function(value) {
- assertEquals(sentinel, value);
- return goog.Promise.resolve(1);
- }).then(function(value) {
- assertEquals(1, value);
- calls.push('fulfilledChild');
- });
- var rejectedChild = p.then(function(value) {
- assertEquals(sentinel, value);
- return goog.Promise.reject(2);
- }).then(shouldNotCall, function(reason) {
- assertEquals(2, reason);
- calls.push('rejectedChild');
- });
- var unresolvedChild =
- p.then(function(value) {
- assertEquals(sentinel, value);
- return new goog.Promise(function(r) { resolveB = r; });
- }).then(function(value) {
- assertEquals(3, value);
- calls.push('unresolvedChild');
- });
- resolveA(sentinel);
- assertArrayEquals(
- 'Calls must not be resolved until the clock ticks.', [], calls);
- mockClock.tick();
- assertArrayEquals(
- 'All resolved Promises should execute in the same timestep.',
- ['then', 'fulfilledChild', 'rejectedChild'], calls);
- resolveB(3);
- assertArrayEquals(
- 'New calls must not resolve until the clock ticks.',
- ['then', 'fulfilledChild', 'rejectedChild'], calls);
- mockClock.tick();
- assertArrayEquals(
- 'All callbacks should have executed.',
- ['then', 'fulfilledChild', 'rejectedChild', 'unresolvedChild'], calls);
- }
- function testHandledRejection() {
- mockClock.install();
- goog.Promise.reject(sentinel).then(shouldNotCall, function(reason) {});
- mockClock.tick();
- assertEquals(0, unhandledRejections.getCallCount());
- }
- function testThenVoidHandledRejection() {
- mockClock.install();
- goog.Promise.reject(sentinel).thenVoid(shouldNotCall, function(reason) {});
- mockClock.tick();
- assertEquals(0, unhandledRejections.getCallCount());
- }
- function testUnhandledRejection1() {
- mockClock.install();
- goog.Promise.reject(sentinel);
- mockClock.tick();
- assertEquals(1, unhandledRejections.getCallCount());
- var rejectionCall = unhandledRejections.popLastCall();
- assertArrayEquals([sentinel], rejectionCall.getArguments());
- assertEquals(goog.global, rejectionCall.getThis());
- }
- function testUnhandledRejection2() {
- mockClock.install();
- goog.Promise.reject(sentinel).then(shouldNotCall);
- mockClock.tick();
- assertEquals(1, unhandledRejections.getCallCount());
- var rejectionCall = unhandledRejections.popLastCall();
- assertArrayEquals([sentinel], rejectionCall.getArguments());
- assertEquals(goog.global, rejectionCall.getThis());
- }
- function testThenVoidUnhandledRejection() {
- mockClock.install();
- goog.Promise.reject(sentinel).thenVoid(shouldNotCall);
- mockClock.tick();
- assertEquals(1, unhandledRejections.getCallCount());
- var rejectionCall = unhandledRejections.popLastCall();
- assertArrayEquals([sentinel], rejectionCall.getArguments());
- assertEquals(goog.global, rejectionCall.getThis());
- }
- function testUnhandledRejection() {
- var resolver = goog.Promise.withResolver();
- goog.Promise.setUnhandledRejectionHandler(function(err) {
- assertEquals(sentinel, err);
- resolver.resolve();
- });
- goog.Promise.reject(sentinel);
- return resolver.promise;
- }
- function testUnhandledThrow() {
- var resolver = goog.Promise.withResolver();
- goog.Promise.setUnhandledRejectionHandler(function(err) {
- assertEquals(sentinel, err);
- resolver.resolve();
- });
- goog.Promise.resolve().then(function() { throw sentinel; });
- return resolver.promise;
- }
- function testThenVoidUnhandledThrow() {
- var resolver = goog.Promise.withResolver();
- goog.Promise.setUnhandledRejectionHandler(function(error) {
- assertEquals(sentinel, error);
- resolver.resolve();
- });
- goog.Promise.resolve().thenVoid(function() { throw sentinel; });
- return resolver.promise;
- }
- function testUnhandledBlockingRejection() {
- mockClock.install();
- var blocker = goog.Promise.reject(sentinel);
- goog.Promise.resolve(blocker);
- mockClock.tick();
- assertEquals(1, unhandledRejections.getCallCount());
- var rejectionCall = unhandledRejections.popLastCall();
- assertArrayEquals([sentinel], rejectionCall.getArguments());
- assertEquals(goog.global, rejectionCall.getThis());
- }
- function testUnhandledRejectionAfterThenAlways() {
- mockClock.install();
- var resolver = goog.Promise.withResolver();
- resolver.promise.thenAlways(function() {});
- resolver.reject(sentinel);
- mockClock.tick();
- assertEquals(1, unhandledRejections.getCallCount());
- var rejectionCall = unhandledRejections.popLastCall();
- assertArrayEquals([sentinel], rejectionCall.getArguments());
- assertEquals(goog.global, rejectionCall.getThis());
- }
- function testHandledBlockingRejection() {
- mockClock.install();
- var blocker = goog.Promise.reject(sentinel);
- goog.Promise.resolve(blocker).then(shouldNotCall, function(reason) {});
- mockClock.tick();
- assertEquals(0, unhandledRejections.getCallCount());
- }
- function testThenVoidHandledBlockingRejection() {
- var shouldCall = goog.testing.recordFunction();
- mockClock.install();
- var blocker = goog.Promise.reject(sentinel);
- goog.Promise.resolve(blocker).thenVoid(shouldNotCall, shouldCall);
- mockClock.tick();
- assertEquals(0, unhandledRejections.getCallCount());
- assertEquals(1, shouldCall.getCallCount());
- }
- function testUnhandledRejectionWithTimeout() {
- mockClock.install();
- stubs.replace(goog.Promise, 'UNHANDLED_REJECTION_DELAY', 200);
- goog.Promise.reject(sentinel);
- mockClock.tick(199);
- assertEquals(0, unhandledRejections.getCallCount());
- mockClock.tick(1);
- assertEquals(1, unhandledRejections.getCallCount());
- }
- function testHandledRejectionWithTimeout() {
- mockClock.install();
- stubs.replace(goog.Promise, 'UNHANDLED_REJECTION_DELAY', 200);
- var p = goog.Promise.reject(sentinel);
- mockClock.tick(199);
- p.then(shouldNotCall, function(reason) {});
- mockClock.tick(1);
- assertEquals(0, unhandledRejections.getCallCount());
- }
- function testUnhandledRejectionDisabled() {
- mockClock.install();
- stubs.replace(goog.Promise, 'UNHANDLED_REJECTION_DELAY', -1);
- goog.Promise.reject(sentinel);
- mockClock.tick();
- assertEquals(0, unhandledRejections.getCallCount());
- }
- function testThenableInterface() {
- var promise = new goog.Promise(function(resolve, reject) {});
- assertTrue(goog.Thenable.isImplementedBy(promise));
- assertFalse(goog.Thenable.isImplementedBy({}));
- assertFalse(goog.Thenable.isImplementedBy('string'));
- assertFalse(goog.Thenable.isImplementedBy(1));
- assertFalse(goog.Thenable.isImplementedBy({then: function() {}}));
- function T() {}
- T.prototype.then = function(opt_a, opt_b, opt_c) {};
- goog.Thenable.addImplementation(T);
- assertTrue(goog.Thenable.isImplementedBy(new T));
- // Test COMPILED code path.
- try {
- COMPILED = true;
- function C() {}
- C.prototype.then = function(opt_a, opt_b, opt_c) {};
- goog.Thenable.addImplementation(C);
- assertTrue(goog.Thenable.isImplementedBy(new C));
- } finally {
- COMPILED = false;
- }
- }
- function testCreateWithResolver_Resolved() {
- mockClock.install();
- var timesCalled = 0;
- var resolver = goog.Promise.withResolver();
- resolver.promise.then(function(value) {
- timesCalled++;
- assertEquals(sentinel, value);
- }, fail);
- assertEquals(
- 'then() must return before callbacks are invoked.', 0, timesCalled);
- mockClock.tick();
- assertEquals(
- 'promise is not resolved until resolver is invoked.', 0, timesCalled);
- resolver.resolve(sentinel);
- assertEquals('resolution is delayed until the next tick', 0, timesCalled);
- mockClock.tick();
- assertEquals('onFulfilled must be called exactly once.', 1, timesCalled);
- }
- function testCreateWithResolver_Rejected() {
- mockClock.install();
- var timesCalled = 0;
- var resolver = goog.Promise.withResolver();
- resolver.promise.then(fail, function(reason) {
- timesCalled++;
- assertEquals(sentinel, reason);
- });
- assertEquals(
- 'then() must return before callbacks are invoked.', 0, timesCalled);
- mockClock.tick();
- assertEquals(
- 'promise is not resolved until resolver is invoked.', 0, timesCalled);
- resolver.reject(sentinel);
- assertEquals('resolution is delayed until the next tick', 0, timesCalled);
- mockClock.tick();
- assertEquals('onFulfilled must be called exactly once.', 1, timesCalled);
- }
- function testLinksBetweenParentsAndChildrenAreCutOnResolve() {
- mockClock.install();
- var parentResolver = goog.Promise.withResolver();
- var parent = parentResolver.promise;
- var child = parent.then(function() {});
- assertNotNull(child.parent_);
- assertEquals(null, parent.callbackEntries_.next);
- parentResolver.resolve();
- mockClock.tick();
- assertNull(child.parent_);
- assertEquals(null, parent.callbackEntries_);
- }
- function testLinksBetweenParentsAndChildrenAreCutWithUnresolvedChild() {
- mockClock.install();
- var parentResolver = goog.Promise.withResolver();
- var parent = parentResolver.promise;
- var child = parent.then(function() {
- // Will never resolve.
- return new goog.Promise(function() {});
- });
- assertNotNull(child.parent_);
- assertNull(parent.callbackEntries_.next);
- parentResolver.resolve();
- mockClock.tick();
- assertNull(child.parent_);
- assertEquals(null, parent.callbackEntries_);
- }
- function testLinksBetweenParentsAndChildrenAreCutOnCancel() {
- mockClock.install();
- var parent = new goog.Promise(function() {});
- var child = parent.then(function() {});
- var grandChild = child.then(function() {});
- assertEquals(null, child.callbackEntries_.next);
- assertNotNull(child.parent_);
- assertEquals(null, parent.callbackEntries_.next);
- parent.cancel();
- mockClock.tick();
- assertNull(child.parent_);
- assertNull(grandChild.parent_);
- assertEquals(null, parent.callbackEntries_);
- assertEquals(null, child.callbackEntries_);
- }
|