Core: restore enumeration behavior in isPlainObject
[jquery.git] / test / unit / deferred.js
blobe2683fd92fc30e5bcc6ad8b9169624a6d2614c32
1 QUnit.module( "deferred", {
2         teardown: moduleTeardown
3 } );
5 jQuery.each( [ "", " - new operator" ], function( _, withNew ) {
7         function createDeferred( fn ) {
8                 return withNew ? new jQuery.Deferred( fn ) : jQuery.Deferred( fn );
9         }
11         QUnit.test( "jQuery.Deferred" + withNew, function( assert ) {
13                 assert.expect( 23 );
15                 var defer = createDeferred();
17                 assert.ok( jQuery.isFunction( defer.pipe ), "defer.pipe is a function" );
19                 createDeferred().resolve().done( function() {
20                         assert.ok( true, "Success on resolve" );
21                         assert.strictEqual( this.state(), "resolved", "Deferred is resolved (state)" );
22                 } ).fail( function() {
23                         assert.ok( false, "Error on resolve" );
24                 } ).always( function() {
25                         assert.ok( true, "Always callback on resolve" );
26                 } );
28                 createDeferred().reject().done( function() {
29                         assert.ok( false, "Success on reject" );
30                 } ).fail( function() {
31                         assert.ok( true, "Error on reject" );
32                         assert.strictEqual( this.state(), "rejected", "Deferred is rejected (state)" );
33                 } ).always( function() {
34                         assert.ok( true, "Always callback on reject" );
35                 } );
37                 createDeferred( function( defer ) {
38                         assert.ok( this === defer, "Defer passed as this & first argument" );
39                         this.resolve( "done" );
40                 } ).done( function( value ) {
41                         assert.strictEqual( value, "done", "Passed function executed" );
42                 } );
44                 createDeferred( function( defer ) {
45                         var promise = defer.promise(),
46                                 func = function() {},
47                                 funcPromise = defer.promise( func );
48                         assert.strictEqual( defer.promise(), promise, "promise is always the same" );
49                         assert.strictEqual( funcPromise, func, "non objects get extended" );
50                         jQuery.each( promise, function( key ) {
51                                 if ( !jQuery.isFunction( promise[ key ] ) ) {
52                                         assert.ok( false, key + " is a function (" + jQuery.type( promise[ key ] ) + ")" );
53                                 }
54                                 if ( promise[ key ] !== func[ key ] ) {
55                                         assert.strictEqual( func[ key ], promise[ key ], key + " is the same" );
56                                 }
57                         } );
58                 } );
60                 jQuery.expandedEach = jQuery.each;
61                 jQuery.expandedEach( "resolve reject".split( " " ), function( _, change ) {
62                         createDeferred( function( defer ) {
63                                 assert.strictEqual( defer.state(), "pending", "pending after creation" );
64                                 var checked = 0;
65                                 defer.progress( function( value ) {
66                                         assert.strictEqual( value, checked, "Progress: right value (" + value + ") received" );
67                                 } );
68                                 for ( checked = 0; checked < 3; checked++ ) {
69                                         defer.notify( checked );
70                                 }
71                                 assert.strictEqual( defer.state(), "pending", "pending after notification" );
72                                 defer[ change ]();
73                                 assert.notStrictEqual( defer.state(), "pending", "not pending after " + change );
74                                 defer.notify();
75                         } );
76                 } );
77         } );
78 } );
80 QUnit.test( "jQuery.Deferred - chainability", function( assert ) {
82         var defer = jQuery.Deferred();
84         assert.expect( 10 );
86         jQuery.expandedEach = jQuery.each;
87         jQuery.expandedEach( "resolve reject notify resolveWith rejectWith notifyWith done fail progress always".split( " " ), function( _, method ) {
88                 var object = {
89                         m: defer[ method ]
90                 };
91                 assert.strictEqual( object.m(), object, method + " is chainable" );
92         } );
93 } );
95 QUnit.test( "jQuery.Deferred.then - filtering (done)", function( assert ) {
97         assert.expect( 4 );
99         var value1, value2, value3,
100                 defer = jQuery.Deferred(),
101                 piped = defer.then( function( a, b ) {
102                         return a * b;
103                 } ),
104                 done = jQuery.map( new Array( 3 ), function() { return assert.async(); } );
106         piped.done( function( result ) {
107                 value3 = result;
108         } );
110         defer.done( function( a, b ) {
111                 value1 = a;
112                 value2 = b;
113         } );
115         defer.resolve( 2, 3 ).then( function() {
116                 assert.strictEqual( value1, 2, "first resolve value ok" );
117                 assert.strictEqual( value2, 3, "second resolve value ok" );
118                 assert.strictEqual( value3, 6, "result of filter ok" );
119                 done.pop().call();
120         } );
122         jQuery.Deferred().reject().then( function() {
123                 assert.ok( false, "then should not be called on reject" );
124         } ).then( null, done.pop() );
126         jQuery.Deferred().resolve().then( jQuery.noop ).done( function( value ) {
127                 assert.strictEqual( value, undefined, "then done callback can return undefined/null" );
128                 done.pop().call();
129         } );
130 } );
132 QUnit.test( "jQuery.Deferred.then - filtering (fail)", function( assert ) {
134         assert.expect( 4 );
136         var value1, value2, value3,
137                 defer = jQuery.Deferred(),
138                 piped = defer.then( null, function( a, b ) {
139                         return a * b;
140                 } ),
141                 done = jQuery.map( new Array( 3 ), function() { return assert.async(); } );
143         piped.done( function( result ) {
144                 value3 = result;
145         } );
147         defer.fail( function( a, b ) {
148                 value1 = a;
149                 value2 = b;
150         } );
152         defer.reject( 2, 3 ).then( null, function() {
153                 assert.strictEqual( value1, 2, "first reject value ok" );
154                 assert.strictEqual( value2, 3, "second reject value ok" );
155                 assert.strictEqual( value3, 6, "result of filter ok" );
156                 done.pop().call();
157         } );
159         jQuery.Deferred().resolve().then( null, function() {
160                 assert.ok( false, "then should not be called on resolve" );
161         } ).then( done.pop() );
163         jQuery.Deferred().reject().then( null, jQuery.noop ).done( function( value ) {
164                 assert.strictEqual( value, undefined, "then fail callback can return undefined/null" );
165                 done.pop().call();
166         } );
167 } );
169 QUnit.test( "jQuery.Deferred.catch", function( assert ) {
170         assert.expect( 4 );
172         var value1, value2, value3,
173                 defer = jQuery.Deferred(),
174                 piped = defer[ "catch" ]( function( a, b ) {
175                         return a * b;
176                 } ),
177                 done = jQuery.map( new Array( 3 ), function() { return assert.async(); } );
179         piped.done( function( result ) {
180                 value3 = result;
181         } );
183         defer.fail( function( a, b ) {
184                 value1 = a;
185                 value2 = b;
186         } );
188         defer.reject( 2, 3 )[ "catch" ]( function() {
189                 assert.strictEqual( value1, 2, "first reject value ok" );
190                 assert.strictEqual( value2, 3, "second reject value ok" );
191                 assert.strictEqual( value3, 6, "result of filter ok" );
192                 done.pop().call();
193         } );
195         jQuery.Deferred().resolve()[ "catch" ]( function() {
196                 assert.ok( false, "then should not be called on resolve" );
197         } ).then( done.pop() );
199         jQuery.Deferred().reject()[ "catch" ]( jQuery.noop ).done( function( value ) {
200                 assert.strictEqual( value, undefined, "then fail callback can return undefined/null" );
201                 done.pop().call();
202         } );
203 } );
205 QUnit.test( "[PIPE ONLY] jQuery.Deferred.pipe - filtering (fail)", function( assert ) {
207         assert.expect( 4 );
209         var value1, value2, value3,
210                 defer = jQuery.Deferred(),
211                 piped = defer.pipe( null, function( a, b ) {
212                         return a * b;
213                 } ),
214                 done = jQuery.map( new Array( 3 ), function() { return assert.async(); } );
216         piped.fail( function( result ) {
217                 value3 = result;
218         } );
220         defer.fail( function( a, b ) {
221                 value1 = a;
222                 value2 = b;
223         } );
225         defer.reject( 2, 3 ).pipe( null, function() {
226                 assert.strictEqual( value1, 2, "first reject value ok" );
227                 assert.strictEqual( value2, 3, "second reject value ok" );
228                 assert.strictEqual( value3, 6, "result of filter ok" );
229                 done.pop().call();
230         } );
232         jQuery.Deferred().resolve().pipe( null, function() {
233                 assert.ok( false, "then should not be called on resolve" );
234         } ).then( done.pop() );
236         jQuery.Deferred().reject().pipe( null, jQuery.noop ).fail( function( value ) {
237                 assert.strictEqual( value, undefined, "then fail callback can return undefined/null" );
238                 done.pop().call();
239         } );
240 } );
242 QUnit.test( "jQuery.Deferred.then - filtering (progress)", function( assert ) {
244         assert.expect( 3 );
246         var value1, value2, value3,
247                 defer = jQuery.Deferred(),
248                 piped = defer.then( null, null, function( a, b ) {
249                         return a * b;
250                 } ),
251                 done = assert.async();
253         piped.progress( function( result ) {
254                 value3 = result;
255         } );
257         defer.progress( function( a, b ) {
258                 value1 = a;
259                 value2 = b;
260         } );
262         defer.notify( 2, 3 ).then( null, null, function() {
263                 assert.strictEqual( value1, 2, "first progress value ok" );
264                 assert.strictEqual( value2, 3, "second progress value ok" );
265                 assert.strictEqual( value3, 6, "result of filter ok" );
266                 done();
267         } );
268 } );
270 QUnit.test( "jQuery.Deferred.then - deferred (done)", function( assert ) {
272         assert.expect( 3 );
274         var value1, value2, value3,
275                 defer = jQuery.Deferred(),
276                 piped = defer.then( function( a, b ) {
277                         return jQuery.Deferred( function( defer ) {
278                                 defer.reject( a * b );
279                         } );
280                 } ),
281                 done = assert.async();
283         piped.fail( function( result ) {
284                 value3 = result;
285         } );
287         defer.done( function( a, b ) {
288                 value1 = a;
289                 value2 = b;
290         } );
292         defer.resolve( 2, 3 );
294         piped.fail( function() {
295                 assert.strictEqual( value1, 2, "first resolve value ok" );
296                 assert.strictEqual( value2, 3, "second resolve value ok" );
297                 assert.strictEqual( value3, 6, "result of filter ok" );
298                 done();
299         } );
300 } );
302 QUnit.test( "jQuery.Deferred.then - deferred (fail)", function( assert ) {
304         assert.expect( 3 );
306         var value1, value2, value3,
307                 defer = jQuery.Deferred(),
308                 piped = defer.then( null, function( a, b ) {
309                         return jQuery.Deferred( function( defer ) {
310                                 defer.resolve( a * b );
311                         } );
312                 } ),
313                 done = assert.async();
315         piped.done( function( result ) {
316                 value3 = result;
317         } );
319         defer.fail( function( a, b ) {
320                 value1 = a;
321                 value2 = b;
322         } );
324         defer.reject( 2, 3 );
326         piped.done( function() {
327                 assert.strictEqual( value1, 2, "first reject value ok" );
328                 assert.strictEqual( value2, 3, "second reject value ok" );
329                 assert.strictEqual( value3, 6, "result of filter ok" );
330                 done();
331         } );
332 } );
334 QUnit.test( "jQuery.Deferred.then - deferred (progress)", function( assert ) {
336         assert.expect( 3 );
338         var value1, value2, value3,
339                 defer = jQuery.Deferred(),
340                 piped = defer.then( null, null, function( a, b ) {
341                         return jQuery.Deferred( function( defer ) {
342                                 defer.resolve( a * b );
343                         } );
344                 } ),
345                 done = assert.async();
347         piped.progress( function( result ) {
348                 return jQuery.Deferred().resolve().then( function() {
349                         return result;
350                 } ).then( function( result ) {
351                         value3 = result;
352                 } );
353         } );
355         defer.progress( function( a, b ) {
356                 value1 = a;
357                 value2 = b;
358         } );
360         defer.notify( 2, 3 );
362         piped.then( null, null, function( result ) {
363                 return jQuery.Deferred().resolve().then( function() {
364                         return result;
365                 } ).then( function() {
366                         assert.strictEqual( value1, 2, "first progress value ok" );
367                         assert.strictEqual( value2, 3, "second progress value ok" );
368                         assert.strictEqual( value3, 6, "result of filter ok" );
369                         done();
370                 } );
371         } );
372 } );
374 QUnit.test( "[PIPE ONLY] jQuery.Deferred.pipe - deferred (progress)", function( assert ) {
376         assert.expect( 3 );
378         var value1, value2, value3,
379                 defer = jQuery.Deferred(),
380                 piped = defer.pipe( null, null, function( a, b ) {
381                         return jQuery.Deferred( function( defer ) {
382                                 defer.resolve( a * b );
383                         } );
384                 } ),
385                 done = assert.async();
387         piped.done( function( result ) {
388                 value3 = result;
389         } );
391         defer.progress( function( a, b ) {
392                 value1 = a;
393                 value2 = b;
394         } );
396         defer.notify( 2, 3 );
398         piped.done( function() {
399                 assert.strictEqual( value1, 2, "first progress value ok" );
400                 assert.strictEqual( value2, 3, "second progress value ok" );
401                 assert.strictEqual( value3, 6, "result of filter ok" );
402                 done();
403         } );
404 } );
406 QUnit.test( "jQuery.Deferred.then - context", function( assert ) {
408         assert.expect( 7 );
410         var defer, piped, defer2, piped2,
411                 context = {},
412                 done = jQuery.map( new Array( 4 ), function() { return assert.async(); } );
414         jQuery.Deferred().resolveWith( context, [ 2 ] ).then( function( value ) {
415                 return value * 3;
416         } ).done( function( value ) {
417                 assert.notStrictEqual( this, context, "custom context not propagated through .then" );
418                 assert.strictEqual( value, 6, "proper value received" );
419                 done.pop().call();
420         } );
422         jQuery.Deferred().resolve().then( function() {
423                 return jQuery.Deferred().resolveWith( context );
424         } ).done( function() {
425                 assert.strictEqual( this, context,
426                         "custom context of returned deferred correctly propagated" );
427                 done.pop().call();
428         } );
430         defer = jQuery.Deferred();
431         piped = defer.then( function( value ) {
432                 return value * 3;
433         } );
435         defer.resolve( 2 );
437         piped.done( function( value ) {
438                 assert.strictEqual( this, piped,
439                         "default context gets updated to latest promise in the chain" );
440                 assert.strictEqual( value, 6, "proper value received" );
441                 done.pop().call();
442         } );
444         defer2 = jQuery.Deferred();
445         piped2 = defer2.then();
447         defer2.resolve( 2 );
449         piped2.done( function( value ) {
450                 assert.strictEqual( this, piped2,
451                         "default context updated to latest promise in the chain (without passing function)" );
452                 assert.strictEqual( value, 2, "proper value received (without passing function)" );
453                 done.pop().call();
454         } );
455 } );
457 QUnit.test( "[PIPE ONLY] jQuery.Deferred.pipe - context", function( assert ) {
459         assert.expect( 7 );
461         var defer, piped, defer2, piped2,
462                 context = {},
463                 done = jQuery.map( new Array( 4 ), function() { return assert.async(); } );
465         jQuery.Deferred().resolveWith( context, [ 2 ] ).pipe( function( value ) {
466                 return value * 3;
467         } ).done( function( value ) {
468                 assert.strictEqual( this, context, "[PIPE ONLY] custom context correctly propagated" );
469                 assert.strictEqual( value, 6, "proper value received" );
470                 done.pop().call();
471         } );
473         jQuery.Deferred().resolve().pipe( function() {
474                 return jQuery.Deferred().resolveWith( context );
475         } ).done( function() {
476                 assert.strictEqual( this, context,
477                         "custom context of returned deferred correctly propagated" );
478                 done.pop().call();
479         } );
481         defer = jQuery.Deferred();
482         piped = defer.pipe( function( value ) {
483                 return value * 3;
484         } );
486         defer.resolve( 2 );
488         piped.done( function( value ) {
489                 assert.strictEqual( this, piped,
490                         "default context gets updated to latest promise in the chain" );
491                 assert.strictEqual( value, 6, "proper value received" );
492                 done.pop().call();
493         } );
495         defer2 = jQuery.Deferred();
496         piped2 = defer2.pipe();
498         defer2.resolve( 2 );
500         piped2.done( function( value ) {
501                 assert.strictEqual( this, piped2,
502                         "default context updated to latest promise in the chain (without passing function)" );
503                 assert.strictEqual( value, 2, "proper value received (without passing function)" );
504                 done.pop().call();
505         } );
506 } );
508 QUnit.test( "jQuery.Deferred.then - spec compatibility", function( assert ) {
510         assert.expect( 1 );
512         var done = assert.async();
514         var defer = jQuery.Deferred().done( function() {
515                 setTimeout( done );
516                 throw new Error();
517         } );
519         defer.then( function() {
520                 assert.ok( true, "errors in .done callbacks don't stop .then handlers" );
521         } );
523         try {
524                 defer.resolve();
525         } catch ( _ ) {}
526 } );
528 QUnit[ window.console ? "test" : "skip" ]( "jQuery.Deferred.exceptionHook", function( assert ) {
530         assert.expect( 1 );
532         var done = assert.async(),
533                 defer = jQuery.Deferred(),
534                 oldWarn = window.console.warn;
536         window.console.warn = function( msg ) {
538                 // Support: Chrome < 42
539                 // Some Chrome versions newer than 30 but older than 42 display the "undefined is
540                 // not a function" error, not mentioning the function name. This has been fixed
541                 // in Chrome 42. Relax this test there.
542                 // This affects our Android 5.0 & Yandex.Browser testing.
543                 var oldChromium = false;
544                 if ( /chrome/i.test( navigator.userAgent ) ) {
545                         oldChromium = parseInt(
546                                         navigator.userAgent.match( /chrome\/(\d+)/i )[ 1 ], 10 ) < 42;
547                 }
548                 if ( oldChromium ) {
549                         assert.ok( /(?:barf|undefined)/.test( msg ), "Message: " + msg );
550                 } else {
551                         assert.ok( /barf/.test( msg ), "Message: " + msg );
552                 }
553         };
554         jQuery.when(
555                 defer.then( function() {
557                         // Should get an error
558                         jQuery.barf();
559                 } ).then( null, jQuery.noop ),
561                 defer.then( function() {
563                         // Should NOT get an error
564                         throw new Error( "Make me a sandwich" );
565                 } ).then( null, jQuery.noop )
566         ).then( function( ) {
567                 window.console.warn = oldWarn;
568                 done();
569         } );
571         defer.resolve();
572 } );
574 QUnit[ window.console ? "test" : "skip" ]( "jQuery.Deferred.exceptionHook with stack hooks", function( assert ) {
576         assert.expect( 2 );
578         var done = assert.async(),
579                 defer = jQuery.Deferred(),
580                 oldWarn = window.console.warn;
582         jQuery.Deferred.getStackHook = function() {
584                 // Default exceptionHook assumes the stack is in a form console.warn can log,
585                 // but a custom getStackHook+exceptionHook pair could save a raw form and
586                 // format it to a string only when an exception actually occurs.
587                 // For the unit test we just ensure the plumbing works.
588                 return "NO STACK FOR YOU";
589         };
591         window.console.warn = function( msg, stack ) {
593                 // Support: Chrome < 42
594                 // Some Chrome versions newer than 30 but older than 42 display the "undefined is
595                 // not a function" error, not mentioning the function name. This has been fixed
596                 // in Chrome 42. Relax this test there.
597                 // This affects our Android 5.0 & Yandex.Browser testing.
598                 var oldChromium = false;
599                 if ( /chrome/i.test( navigator.userAgent ) ) {
600                         oldChromium = parseInt(
601                                         navigator.userAgent.match( /chrome\/(\d+)/i )[ 1 ], 10 ) < 42;
602                 }
603                 if ( oldChromium ) {
604                         assert.ok( /(?:cough_up_hairball|undefined)/.test( msg ), "Function mentioned: " + msg );
605                 } else {
606                         assert.ok( /cough_up_hairball/.test( msg ), "Function mentioned: " + msg );
607                 }
608                 assert.ok( /NO STACK FOR YOU/.test( stack ), "Stack trace included: " + stack );
609         };
610         defer.then( function() {
611                 jQuery.cough_up_hairball();
612         } ).then( null, function( ) {
613                 window.console.warn = oldWarn;
614                 delete jQuery.Deferred.getStackHook;
615                 done();
616         } );
618         defer.resolve();
619 } );
621 QUnit.test( "jQuery.Deferred - 1.x/2.x compatibility", function( assert ) {
623         assert.expect( 8 );
625         var context = { id: "callback context" },
626                 thenable = jQuery.Deferred().resolve( "thenable fulfillment" ).promise(),
627                 done = jQuery.map( new Array( 8 ), function() { return assert.async(); } );
629         thenable.unwrapped = false;
631         jQuery.Deferred().resolve( 1, 2 ).then( function() {
632                 assert.deepEqual( [].slice.call( arguments ), [ 1, 2 ],
633                         ".then fulfillment callbacks receive all resolution values" );
634                 done.pop().call();
635         } );
636         jQuery.Deferred().reject( 1, 2 ).then( null, function() {
637                 assert.deepEqual( [].slice.call( arguments ), [ 1, 2 ],
638                         ".then rejection callbacks receive all rejection values" );
639                 done.pop().call();
640         } );
641         jQuery.Deferred().notify( 1, 2 ).then( null, null, function() {
642                 assert.deepEqual( [].slice.call( arguments ), [ 1, 2 ],
643                         ".then progress callbacks receive all progress values" );
644                 done.pop().call();
645         } );
647         jQuery.Deferred().resolveWith( context ).then( function() {
648                 assert.deepEqual( this, context, ".then fulfillment callbacks receive context" );
649                 done.pop().call();
650         } );
651         jQuery.Deferred().rejectWith( context ).then( null, function() {
652                 assert.deepEqual( this, context, ".then rejection callbacks receive context" );
653                 done.pop().call();
654         } );
655         jQuery.Deferred().notifyWith( context ).then( null, null, function() {
656                 assert.deepEqual( this, context, ".then progress callbacks receive context" );
657                 done.pop().call();
658         } );
660         jQuery.Deferred().resolve( thenable ).done( function( value ) {
661                 assert.strictEqual( value, thenable, ".done doesn't unwrap thenables" );
662                 done.pop().call();
663         } );
665         jQuery.Deferred().notify( thenable ).then().then( null, null, function( value ) {
666                 assert.strictEqual( value, "thenable fulfillment",
667                         ".then implicit progress callbacks unwrap thenables" );
668                 done.pop().call();
669         } );
670 } );
672 QUnit.test( "jQuery.Deferred.then - progress and thenables", function( assert ) {
674         assert.expect( 2 );
676         var trigger = jQuery.Deferred().notify(),
677                 expectedProgress = [ "baz", "baz" ],
678                 done = jQuery.map( new Array( 2 ), function() { return assert.async(); } ),
679                 failer = function( evt ) {
680                         return function() {
681                                 assert.ok( false, "no unexpected " + evt );
682                         };
683                 };
685         trigger.then( null, null, function() {
686                 var notifier = jQuery.Deferred().notify( "foo" );
687                 setTimeout( function() {
688                         notifier.notify( "bar" ).resolve( "baz" );
689                 } );
690                 return notifier;
691         } ).then( failer( "fulfill" ), failer( "reject" ), function( v ) {
692                 assert.strictEqual( v, expectedProgress.shift(), "expected progress value" );
693                 done.pop().call();
694         } );
695         trigger.notify();
696 } );
698 QUnit.test( "jQuery.Deferred - notify and resolve", function( assert ) {
700         assert.expect( 7 );
702         var notifiedResolved = jQuery.Deferred().notify( "foo" )/*xxx .resolve( "bar" )*/,
703                 done = jQuery.map( new Array( 3 ), function() { return assert.async(); } );
705         notifiedResolved.progress( function( v ) {
706                 assert.strictEqual( v, "foo", "progress value" );
707         } );
709         notifiedResolved.pipe().progress( function( v ) {
710                 assert.strictEqual( v, "foo", "piped progress value" );
711         } );
713         notifiedResolved.pipe( null, null, function() {
714                 return "baz";
715         } ).progress( function( v ) {
716                 assert.strictEqual( v, "baz", "replaced piped progress value" );
717         } );
719         notifiedResolved.pipe( null, null, function() {
720                 return jQuery.Deferred().notify( "baz" ).resolve( "quux" );
721         } ).progress( function( v ) {
722                 assert.strictEqual( v, "baz", "deferred replaced piped progress value" );
723         } );
725         notifiedResolved.then().progress( function( v ) {
726                 assert.strictEqual( v, "foo", "then'd progress value" );
727                 done.pop().call();
728         } );
730         notifiedResolved.then( null, null, function() {
731                 return "baz";
732         } ).progress( function( v ) {
733                 assert.strictEqual( v, "baz", "replaced then'd progress value" );
734                 done.pop().call();
735         } );
737         notifiedResolved.then( null, null, function() {
738                 return jQuery.Deferred().notify( "baz" ).resolve( "quux" );
739         } ).progress( function( v ) {
741                 // Progress from the surrogate deferred is ignored
742                 assert.strictEqual( v, "quux", "deferred replaced then'd progress value" );
743                 done.pop().call();
744         } );
745 } );
747 QUnit.test( "jQuery.when", function( assert ) {
749         assert.expect( 37 );
751         // Some other objects
752         jQuery.each( {
753                 "an empty string": "",
754                 "a non-empty string": "some string",
755                 "zero": 0,
756                 "a number other than zero": 1,
757                 "true": true,
758                 "false": false,
759                 "null": null,
760                 "undefined": undefined,
761                 "a plain object": {},
762                 "an array": [ 1, 2, 3 ]
763         }, function( message, value ) {
764                 assert.ok(
765                         jQuery.isFunction(
766                                 jQuery.when( value ).done( function( resolveValue ) {
767                                         assert.strictEqual( this, window, "Context is the global object with " + message );
768                                         assert.strictEqual( resolveValue, value, "Test the promise was resolved with " + message );
769                                 } ).promise
770                         ),
771                         "Test " + message + " triggers the creation of a new Promise"
772                 );
773         } );
775         assert.ok(
776                 jQuery.isFunction(
777                         jQuery.when().done( function( resolveValue ) {
778                                 assert.strictEqual( this, window, "Test the promise was resolved with window as its context" );
779                                 assert.strictEqual( resolveValue, undefined, "Test the promise was resolved with no parameter" );
780                         } ).promise
781                 ),
782                 "Test calling when with no parameter triggers the creation of a new Promise"
783         );
785         var cache,
786                 context = {};
788         jQuery.when( jQuery.Deferred().resolveWith( context ) ).done( function() {
789                 assert.strictEqual( this, context, "when( promise ) propagates context" );
790         } );
792         jQuery.each( [ 1, 2, 3 ], function( k, i ) {
793                 jQuery.when( cache || jQuery.Deferred( function() {
794                                 this.resolve( i );
795                         } )
796                 ).done( function( value ) {
797                         assert.strictEqual( value, 1, "Function executed" + ( i > 1 ? " only once" : "" ) );
798                         cache = value;
799                 } );
801         } );
802 } );
804 QUnit.test( "jQuery.when - joined", function( assert ) {
806         assert.expect( 81 );
808         var deferreds = {
809                         rawValue: 1,
810                         fulfilled: jQuery.Deferred().resolve( 1 ),
811                         rejected: jQuery.Deferred().reject( 0 ),
812                         eventuallyFulfilled: jQuery.Deferred().notify( true ),
813                         eventuallyRejected: jQuery.Deferred().notify( true ),
814                         fulfilledStandardPromise: Promise.resolve( 1 ),
815                         rejectedStandardPromise: Promise.reject( 0 )
816                 },
817                 willSucceed = {
818                         rawValue: true,
819                         fulfilled: true,
820                         eventuallyFulfilled: true,
821                         fulfilledStandardPromise: true
822                 },
823                 willError = {
824                         rejected: true,
825                         eventuallyRejected: true,
826                         rejectedStandardPromise: true
827                 },
828                 counter = 49;
830         QUnit.stop();
832         function restart() {
833                 if ( !--counter ) {
834                         QUnit.start();
835                 }
836         }
838         jQuery.each( deferreds, function( id1, defer1 ) {
839                 jQuery.each( deferreds, function( id2, defer2 ) {
840                         var shouldResolve = willSucceed[ id1 ] && willSucceed[ id2 ],
841                                 shouldError = willError[ id1 ] || willError[ id2 ],
842                                 expected = shouldResolve ? [ 1, 1 ] : [ 0, undefined ],
843                                 code = "jQuery.when( " + id1 + ", " + id2 + " )",
844                                 context1 = defer1 && jQuery.isFunction( defer1.promise ) ? defer1.promise() : window,
845                                 context2 = defer2 && jQuery.isFunction( defer2.promise ) ? defer2.promise() : window;
847                         jQuery.when( defer1, defer2 ).done( function( a, b ) {
848                                 if ( shouldResolve ) {
849                                         assert.deepEqual( [ a, b ], expected, code + " => resolve" );
850                                         assert.strictEqual( this[ 0 ], context1, code + " => first context OK" );
851                                         assert.strictEqual( this[ 1 ], context2, code + " => second context OK" );
852                                 } else {
853                                         assert.ok( false,  code + " => resolve" );
854                                 }
855                         } ).fail( function( a, b ) {
856                                 if ( shouldError ) {
857                                         assert.deepEqual( [ a, b ], expected, code + " => reject" );
858                                 } else {
859                                         assert.ok( false, code + " => reject" );
860                                 }
861                         } ).always( restart );
862                 } );
863         } );
864         deferreds.eventuallyFulfilled.resolve( 1 );
865         deferreds.eventuallyRejected.reject( 0 );
866 } );
868 QUnit.test( "jQuery.when - notify does not affect resolved", function( assert ) {
870         assert.expect( 3 );
872         var a = jQuery.Deferred().notify( 1 ).resolve( 4 ),
873                 b = jQuery.Deferred().notify( 2 ).resolve( 5 ),
874                 c = jQuery.Deferred().notify( 3 ).resolve( 6 );
876         jQuery.when( a, b, c ).done( function( a, b, c ) {
877                 assert.strictEqual( a, 4, "first resolve value ok" );
878                 assert.strictEqual( b, 5, "second resolve value ok" );
879                 assert.strictEqual( c, 6, "third resolve value ok" );
880         } ).fail( function() {
881                 assert.ok( false, "Error on resolve" );
882         } );
883 } );
885 QUnit.test( "jQuery.when - filtering", function( assert ) {
887         assert.expect( 2 );
889         function increment( x ) {
890                 return x + 1;
891         }
893         QUnit.stop();
895         jQuery.when(
896                 jQuery.Deferred().resolve( 3 ).then( increment ),
897                 jQuery.Deferred().reject( 5 ).then( null, increment )
898         ).done( function( four, six ) {
899                 assert.strictEqual( four, 4, "resolved value incremented" );
900                 assert.strictEqual( six, 6, "rejected value incremented" );
901                 QUnit.start();
902         } );
903 } );
905 QUnit.test( "jQuery.when - exceptions", function( assert ) {
907         assert.expect( 2 );
909         function woops() {
910                 throw "exception thrown";
911         }
913         QUnit.stop();
915         jQuery.Deferred().resolve().then( woops ).fail( function( doneException ) {
916                 assert.strictEqual( doneException, "exception thrown", "throwing in done handler" );
917                 jQuery.Deferred().reject().then( null, woops ).fail( function( failException ) {
918                         assert.strictEqual( failException, "exception thrown", "throwing in fail handler" );
919                         QUnit.start();
920                 } );
921         } );
922 } );
924 QUnit.test( "jQuery.when - chaining", function( assert ) {
926         assert.expect( 4 );
928         var defer = jQuery.Deferred();
930         function chain() {
931                 return defer;
932         }
934         function chainStandard() {
935                 return Promise.resolve( "std deferred" );
936         }
938         QUnit.stop();
940         jQuery.when(
941                 jQuery.Deferred().resolve( 3 ).then( chain ),
942                 jQuery.Deferred().reject( 5 ).then( null, chain ),
943                 jQuery.Deferred().resolve( 3 ).then( chainStandard ),
944                 jQuery.Deferred().reject( 5 ).then( null, chainStandard )
945         ).done( function( v1, v2, s1, s2 ) {
946                 assert.strictEqual( v1, "other deferred", "chaining in done handler" );
947                 assert.strictEqual( v2, "other deferred", "chaining in fail handler" );
948                 assert.strictEqual( s1, "std deferred", "chaining thenable in done handler" );
949                 assert.strictEqual( s2, "std deferred", "chaining thenable in fail handler" );
950                 QUnit.start();
951         } );
953         defer.resolve( "other deferred" );
954 } );
956 QUnit.test( "jQuery.when - solitary thenables", function( assert ) {
958         assert.expect( 1 );
960         var done = assert.async(),
961                 rejected = new Promise( function( resolve, reject ) {
962                         setTimeout( function() {
963                                 reject( "rejected" );
964                         }, 100 );
965                 } );
967         jQuery.when( rejected ).then(
968                 function() {
969                         assert.ok( false, "Rejected, solitary, non-Deferred thenable should not resolve" );
970                         done();
971                 },
972                 function() {
973                         assert.ok( true, "Rejected, solitary, non-Deferred thenable rejected properly" );
974                         done();
975                 }
976         );
977 } );
979 QUnit.test( "jQuery.when does not reuse a solitary jQuery Deferred (gh-2018)", function( assert ) {
981         assert.expect( 2 );
982         var defer = jQuery.Deferred().resolve(),
983                 promise = jQuery.when( defer );
985         assert.equal( promise.state(), "resolved", "Master Deferred is immediately resolved" );
986         assert.notStrictEqual( defer.promise(), promise, "jQuery.when returns the master deferred's promise" );
987 } );