Deferred: pass lint in new catch tests
[jquery.git] / test / unit / deferred.js
blob1aa15435fe9a67910f2b63d98acaf495a86d1b5d
1 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         test( "jQuery.Deferred" + withNew, function() {
13                 expect( 23 );
15                 var defer = createDeferred();
17                 ok( jQuery.isFunction( defer.pipe ), "defer.pipe is a function" );
19                 createDeferred().resolve().done(function() {
20                         ok( true, "Success on resolve" );
21                         strictEqual( this.state(), "resolved", "Deferred is resolved (state)" );
22                 }).fail(function() {
23                         ok( false, "Error on resolve" );
24                 }).always(function() {
25                         ok( true, "Always callback on resolve" );
26                 });
28                 createDeferred().reject().done(function() {
29                         ok( false, "Success on reject" );
30                 }).fail(function() {
31                         ok( true, "Error on reject" );
32                         strictEqual( this.state(), "rejected", "Deferred is rejected (state)" );
33                 }).always(function() {
34                         ok( true, "Always callback on reject" );
35                 });
37                 createDeferred(function( defer ) {
38                         ok( this === defer, "Defer passed as this & first argument" );
39                         this.resolve("done");
40                 }).done(function( value ) {
41                         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                         strictEqual( defer.promise(), promise, "promise is always the same" );
49                         strictEqual( funcPromise, func, "non objects get extended" );
50                         jQuery.each( promise, function( key ) {
51                                 if ( !jQuery.isFunction( promise[ key ] ) ) {
52                                         ok( false, key + " is a function (" + jQuery.type( promise[ key ] ) + ")" );
53                                 }
54                                 if ( promise[ key ] !== func[ key ] ) {
55                                         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                                 strictEqual( defer.state(), "pending", "pending after creation" );
64                                 var checked = 0;
65                                 defer.progress(function( value ) {
66                                         strictEqual( value, checked, "Progress: right value (" + value + ") received" );
67                                 });
68                                 for ( checked = 0; checked < 3; checked++ ) {
69                                         defer.notify( checked );
70                                 }
71                                 strictEqual( defer.state(), "pending", "pending after notification" );
72                                 defer[ change ]();
73                                 notStrictEqual( defer.state(), "pending", "not pending after " + change );
74                                 defer.notify();
75                         });
76                 });
77         });
78 });
81 test( "jQuery.Deferred - chainability", function() {
83         var defer = jQuery.Deferred();
85         expect( 10 );
87         jQuery.expandedEach = jQuery.each;
88         jQuery.expandedEach( "resolve reject notify resolveWith rejectWith notifyWith done fail progress always".split(" "), function( _, method ) {
89                 var object = {
90                         m: defer[ method ]
91                 };
92                 strictEqual( object.m(), object, method + " is chainable" );
93         });
94 });
96 test( "jQuery.Deferred.then - filtering (done)", function( assert ) {
98         assert.expect( 4 );
100         var value1, value2, value3,
101                 defer = jQuery.Deferred(),
102                 piped = defer.then(function( a, b ) {
103                         return a * b;
104                 }),
105                 done = jQuery.map( new Array( 3 ), function() { return assert.async(); } );
107         piped.done(function( result ) {
108                 value3 = result;
109         });
111         defer.done(function( a, b ) {
112                 value1 = a;
113                 value2 = b;
114         });
116         defer.resolve( 2, 3 ).then(function() {
117                 assert.strictEqual( value1, 2, "first resolve value ok" );
118                 assert.strictEqual( value2, 3, "second resolve value ok" );
119                 assert.strictEqual( value3, 6, "result of filter ok" );
120                 done.pop().call();
121         });
123         jQuery.Deferred().reject().then(function() {
124                 assert.ok( false, "then should not be called on reject" );
125         }).then( null, done.pop() );
127         jQuery.Deferred().resolve().then( jQuery.noop ).done(function( value ) {
128                 assert.strictEqual( value, undefined, "then done callback can return undefined/null" );
129                 done.pop().call();
130         });
133 test( "jQuery.Deferred.then - filtering (fail)", function( assert ) {
135         assert.expect( 4 );
137         var value1, value2, value3,
138                 defer = jQuery.Deferred(),
139                 piped = defer.then( null, function( a, b ) {
140                         return a * b;
141                 }),
142                 done = jQuery.map( new Array( 3 ), function() { return assert.async(); } );
144         piped.done(function( result ) {
145                 value3 = result;
146         });
148         defer.fail(function( a, b ) {
149                 value1 = a;
150                 value2 = b;
151         });
153         defer.reject( 2, 3 ).then( null, function() {
154                 assert.strictEqual( value1, 2, "first reject value ok" );
155                 assert.strictEqual( value2, 3, "second reject value ok" );
156                 assert.strictEqual( value3, 6, "result of filter ok" );
157                 done.pop().call();
158         });
160         jQuery.Deferred().resolve().then( null, function() {
161                 assert.ok( false, "then should not be called on resolve" );
162         }).then( done.pop() );
164         jQuery.Deferred().reject().then( null, jQuery.noop ).done(function( value ) {
165                 assert.strictEqual( value, undefined, "then fail callback can return undefined/null" );
166                 done.pop().call();
167         });
170 test( "jQuery.Deferred.catch", function( assert ) {
171         assert.expect( 4 );
173         var value1, value2, value3,
174                 defer = jQuery.Deferred(),
175                 piped = defer[ "catch" ](function( a, b ) {
176                         return a * b;
177                 }),
178                 done = jQuery.map( new Array( 3 ), function() { return assert.async(); } );
180         piped.done(function( result ) {
181                 value3 = result;
182         });
184         defer.fail(function( a, b ) {
185                 value1 = a;
186                 value2 = b;
187         });
189         defer.reject( 2, 3 )[ "catch" ](function() {
190                 assert.strictEqual( value1, 2, "first reject value ok" );
191                 assert.strictEqual( value2, 3, "second reject value ok" );
192                 assert.strictEqual( value3, 6, "result of filter ok" );
193                 done.pop().call();
194         });
196         jQuery.Deferred().resolve()[ "catch" ](function() {
197                 assert.ok( false, "then should not be called on resolve" );
198         }).then( done.pop() );
200         jQuery.Deferred().reject()[ "catch" ]( jQuery.noop ).done(function( value ) {
201                 assert.strictEqual( value, undefined, "then fail callback can return undefined/null" );
202                 done.pop().call();
203         });
206 test( "[PIPE ONLY] jQuery.Deferred.pipe - filtering (fail)", function( assert ) {
208         assert.expect( 4 );
210         var value1, value2, value3,
211                 defer = jQuery.Deferred(),
212                 piped = defer.pipe( null, function( a, b ) {
213                         return a * b;
214                 }),
215                 done = jQuery.map( new Array( 3 ), function() { return assert.async(); } );
217         piped.fail(function( result ) {
218                 value3 = result;
219         });
221         defer.fail(function( a, b ) {
222                 value1 = a;
223                 value2 = b;
224         });
226         defer.reject( 2, 3 ).pipe( null, function() {
227                 assert.strictEqual( value1, 2, "first reject value ok" );
228                 assert.strictEqual( value2, 3, "second reject value ok" );
229                 assert.strictEqual( value3, 6, "result of filter ok" );
230                 done.pop().call();
231         });
233         jQuery.Deferred().resolve().pipe( null, function() {
234                 assert.ok( false, "then should not be called on resolve" );
235         }).then( done.pop() );
237         jQuery.Deferred().reject().pipe( null, jQuery.noop ).fail(function( value ) {
238                 assert.strictEqual( value, undefined, "then fail callback can return undefined/null" );
239                 done.pop().call();
240         });
243 test( "jQuery.Deferred.then - filtering (progress)", function( assert ) {
245         assert.expect( 3 );
247         var value1, value2, value3,
248                 defer = jQuery.Deferred(),
249                 piped = defer.then( null, null, function( a, b ) {
250                         return a * b;
251                 }),
252                 done = assert.async();
254         piped.progress(function( result ) {
255                 value3 = result;
256         });
258         defer.progress(function( a, b ) {
259                 value1 = a;
260                 value2 = b;
261         });
263         defer.notify( 2, 3 ).then( null, null, function() {
264                 assert.strictEqual( value1, 2, "first progress value ok" );
265                 assert.strictEqual( value2, 3, "second progress value ok" );
266                 assert.strictEqual( value3, 6, "result of filter ok" );
267                 done();
268         });
271 test( "jQuery.Deferred.then - deferred (done)", function( assert ) {
273         assert.expect( 3 );
275         var value1, value2, value3,
276                 defer = jQuery.Deferred(),
277                 piped = defer.then(function( a, b ) {
278                         return jQuery.Deferred(function( defer ) {
279                                 defer.reject( a * b );
280                         });
281                 }),
282                 done = assert.async();
284         piped.fail(function( result ) {
285                 value3 = result;
286         });
288         defer.done(function( a, b ) {
289                 value1 = a;
290                 value2 = b;
291         });
293         defer.resolve( 2, 3 );
295         piped.fail(function() {
296                 assert.strictEqual( value1, 2, "first resolve value ok" );
297                 assert.strictEqual( value2, 3, "second resolve value ok" );
298                 assert.strictEqual( value3, 6, "result of filter ok" );
299                 done();
300         });
303 test( "jQuery.Deferred.then - deferred (fail)", function( assert ) {
305         assert.expect( 3 );
307         var value1, value2, value3,
308                 defer = jQuery.Deferred(),
309                 piped = defer.then( null, function( a, b ) {
310                         return jQuery.Deferred(function( defer ) {
311                                 defer.resolve( a * b );
312                         });
313                 }),
314                 done = assert.async();
316         piped.done(function( result ) {
317                 value3 = result;
318         });
320         defer.fail(function( a, b ) {
321                 value1 = a;
322                 value2 = b;
323         });
325         defer.reject( 2, 3 );
327         piped.done(function() {
328                 assert.strictEqual( value1, 2, "first reject value ok" );
329                 assert.strictEqual( value2, 3, "second reject value ok" );
330                 assert.strictEqual( value3, 6, "result of filter ok" );
331                 done();
332         });
335 test( "jQuery.Deferred.then - deferred (progress)", function( assert ) {
337         assert.expect( 3 );
339         var value1, value2, value3,
340                 defer = jQuery.Deferred(),
341                 piped = defer.then( null, null, function( a, b ) {
342                         return jQuery.Deferred(function( defer ) {
343                                 defer.resolve( a * b );
344                         });
345                 }),
346                 done = assert.async();
348         piped.progress(function( result ) {
349                 return jQuery.Deferred().resolve().then(function() {
350                         return result;
351                 }).then(function( result ) {
352                         value3 = result;
353                 });
354         });
356         defer.progress(function( a, b ) {
357                 value1 = a;
358                 value2 = b;
359         });
361         defer.notify( 2, 3 );
363         piped.then( null, null, function( result ) {
364                 return jQuery.Deferred().resolve().then(function() {
365                         return result;
366                 }).then(function() {
367                         assert.strictEqual( value1, 2, "first progress value ok" );
368                         assert.strictEqual( value2, 3, "second progress value ok" );
369                         assert.strictEqual( value3, 6, "result of filter ok" );
370                         done();
371                 });
372         });
375 test( "[PIPE ONLY] jQuery.Deferred.pipe - deferred (progress)", function( assert ) {
377         assert.expect( 3 );
379         var value1, value2, value3,
380                 defer = jQuery.Deferred(),
381                 piped = defer.pipe( null, null, function( a, b ) {
382                         return jQuery.Deferred(function( defer ) {
383                                 defer.resolve( a * b );
384                         });
385                 }),
386                 done = assert.async();
388         piped.done(function( result ) {
389                 value3 = result;
390         });
392         defer.progress(function( a, b ) {
393                 value1 = a;
394                 value2 = b;
395         });
397         defer.notify( 2, 3 );
399         piped.done(function() {
400                 assert.strictEqual( value1, 2, "first progress value ok" );
401                 assert.strictEqual( value2, 3, "second progress value ok" );
402                 assert.strictEqual( value3, 6, "result of filter ok" );
403                 done();
404         });
407 test( "jQuery.Deferred.then - context", function( assert ) {
409         assert.expect( 7 );
411         var defer, piped, defer2, piped2,
412                 context = {},
413                 done = jQuery.map( new Array( 4 ), function() { return assert.async(); } );
415         jQuery.Deferred().resolveWith( context, [ 2 ] ).then(function( value ) {
416                 return value * 3;
417         }).done(function( value ) {
418                 assert.notStrictEqual( this, context, "custom context not propagated through .then" );
419                 assert.strictEqual( value, 6, "proper value received" );
420                 done.pop().call();
421         });
423         jQuery.Deferred().resolve().then(function() {
424                 return jQuery.Deferred().resolveWith( context );
425         }).done(function() {
426                 assert.strictEqual( this, context,
427                         "custom context of returned deferred correctly propagated" );
428                 done.pop().call();
429         });
431         defer = jQuery.Deferred();
432         piped = defer.then(function( value ) {
433                 return value * 3;
434         });
436         defer.resolve( 2 );
438         piped.done(function( value ) {
439                 assert.strictEqual( this, piped,
440                         "default context gets updated to latest promise in the chain" );
441                 assert.strictEqual( value, 6, "proper value received" );
442                 done.pop().call();
443         });
445         defer2 = jQuery.Deferred();
446         piped2 = defer2.then();
448         defer2.resolve( 2 );
450         piped2.done(function( value ) {
451                 assert.strictEqual( this, piped2,
452                         "default context updated to latest promise in the chain (without passing function)" );
453                 assert.strictEqual( value, 2, "proper value received (without passing function)" );
454                 done.pop().call();
455         });
458 test( "[PIPE ONLY] jQuery.Deferred.pipe - context", function( assert ) {
460         assert.expect( 7 );
462         var defer, piped, defer2, piped2,
463                 context = {},
464                 done = jQuery.map( new Array( 4 ), function() { return assert.async(); } );
466         jQuery.Deferred().resolveWith( context, [ 2 ] ).pipe(function( value ) {
467                 return value * 3;
468         }).done(function( value ) {
469                 assert.strictEqual( this, context, "[PIPE ONLY] custom context correctly propagated" );
470                 assert.strictEqual( value, 6, "proper value received" );
471                 done.pop().call();
472         });
474         jQuery.Deferred().resolve().pipe(function() {
475                 return jQuery.Deferred().resolveWith(context);
476         }).done(function() {
477                 assert.strictEqual( this, context,
478                         "custom context of returned deferred correctly propagated" );
479                 done.pop().call();
480         });
482         defer = jQuery.Deferred();
483         piped = defer.pipe(function( value ) {
484                 return value * 3;
485         });
487         defer.resolve( 2 );
489         piped.done(function( value ) {
490                 assert.strictEqual( this, piped,
491                         "default context gets updated to latest promise in the chain" );
492                 assert.strictEqual( value, 6, "proper value received" );
493                 done.pop().call();
494         });
496         defer2 = jQuery.Deferred();
497         piped2 = defer2.pipe();
499         defer2.resolve( 2 );
501         piped2.done(function( value ) {
502                 assert.strictEqual( this, piped2,
503                         "default context updated to latest promise in the chain (without passing function)" );
504                 assert.strictEqual( value, 2, "proper value received (without passing function)" );
505                 done.pop().call();
506         });
509 asyncTest( "jQuery.Deferred.then - spec compatibility", function() {
511         expect( 1 );
513         var defer = jQuery.Deferred().done(function() {
514                 setTimeout( start );
515                 throw new Error();
516         });
518         defer.then(function() {
519                 ok( true, "errors in .done callbacks don't stop .then handlers" );
520         });
522         try {
523                 defer.resolve();
524         } catch ( _ ) {}
527 test( "jQuery.Deferred - 1.x/2.x compatibility", function( assert ) {
529         expect( 8 );
531         var context = { id: "callback context" },
532                 thenable = jQuery.Deferred().resolve( "thenable fulfillment" ).promise(),
533                 done = jQuery.map( new Array( 8 ), function() { return assert.async(); } );
535         thenable.unwrapped = false;
537         jQuery.Deferred().resolve( 1, 2 ).then(function() {
538                 assert.deepEqual( [].slice.call( arguments ), [ 1, 2 ],
539                         ".then fulfillment callbacks receive all resolution values" );
540                 done.pop().call();
541         });
542         jQuery.Deferred().reject( 1, 2 ).then( null, function() {
543                 assert.deepEqual( [].slice.call( arguments ), [ 1, 2 ],
544                         ".then rejection callbacks receive all rejection values" );
545                 done.pop().call();
546         });
547         jQuery.Deferred().notify( 1, 2 ).then( null, null, function() {
548                 assert.deepEqual( [].slice.call( arguments ), [ 1, 2 ],
549                         ".then progress callbacks receive all progress values" );
550                 done.pop().call();
551         });
553         jQuery.Deferred().resolveWith( context ).then(function() {
554                 assert.deepEqual( this, context, ".then fulfillment callbacks receive context" );
555                 done.pop().call();
556         });
557         jQuery.Deferred().rejectWith( context ).then( null, function() {
558                 assert.deepEqual( this, context, ".then rejection callbacks receive context" );
559                 done.pop().call();
560         });
561         jQuery.Deferred().notifyWith( context ).then( null, null, function() {
562                 assert.deepEqual( this, context, ".then progress callbacks receive context" );
563                 done.pop().call();
564         });
566         jQuery.Deferred().resolve( thenable ).done(function( value ) {
567                 assert.strictEqual( value, thenable, ".done doesn't unwrap thenables" );
568                 done.pop().call();
569         });
571         jQuery.Deferred().notify( thenable ).then().then( null, null, function( value ) {
572                 assert.strictEqual( value, "thenable fulfillment",
573                         ".then implicit progress callbacks unwrap thenables" );
574                 done.pop().call();
575         });
578 test( "jQuery.Deferred.then - progress and thenables", function( assert ) {
580         expect( 2 );
582         var trigger = jQuery.Deferred().notify(),
583                 expectedProgress = [ "baz", "baz" ],
584                 done = jQuery.map( new Array( 2 ), function() { return assert.async(); } ),
585                 failer = function( evt ) {
586                         return function() {
587                                 ok( false, "no unexpected " + evt );
588                         };
589                 };
591         trigger.then( null, null, function() {
592                 var notifier = jQuery.Deferred().notify( "foo" );
593                 setTimeout(function() {
594                         notifier.notify( "bar" ).resolve( "baz" );
595                 });
596                 return notifier;
597         }).then( failer( "fulfill" ), failer( "reject" ), function( v ) {
598                 assert.strictEqual( v, expectedProgress.shift(), "expected progress value" );
599                 done.pop().call();
600         });
601         trigger.notify();
604 test( "jQuery.Deferred - notify and resolve", function( assert ) {
606         expect( 7 );
608         var notifiedResolved = jQuery.Deferred().notify( "foo" )/*xxx .resolve( "bar" )*/,
609                 done = jQuery.map( new Array( 3 ), function() { return assert.async(); } );
611         notifiedResolved.progress( function( v ) {
612                 assert.strictEqual( v, "foo", "progress value" );
613         } );
615         notifiedResolved.pipe().progress( function( v ) {
616                 assert.strictEqual( v, "foo", "piped progress value" );
617         } );
619         notifiedResolved.pipe( null, null, function() {
620                 return "baz";
621         } ).progress( function( v ) {
622                 assert.strictEqual( v, "baz", "replaced piped progress value" );
623         } );
625         notifiedResolved.pipe( null, null, function() {
626                 return jQuery.Deferred().notify( "baz" ).resolve( "quux" );
627         } ).progress( function( v ) {
628                 assert.strictEqual( v, "baz", "deferred replaced piped progress value" );
629         } );
631         notifiedResolved.then().progress( function( v ) {
632                 assert.strictEqual( v, "foo", "then'd progress value" );
633                 done.pop().call();
634         } );
636         notifiedResolved.then( null, null, function() {
637                 return "baz";
638         } ).progress( function( v ) {
639                 assert.strictEqual( v, "baz", "replaced then'd progress value" );
640                 done.pop().call();
641         } );
643         notifiedResolved.then( null, null, function() {
644                 return jQuery.Deferred().notify( "baz" ).resolve( "quux" );
645         } ).progress( function( v ) {
646                 // Progress from the surrogate deferred is ignored
647                 assert.strictEqual( v, "quux", "deferred replaced then'd progress value" );
648                 done.pop().call();
649         } );
652 test( "jQuery.when", function() {
654         expect( 37 );
656         // Some other objects
657         jQuery.each({
658                 "an empty string": "",
659                 "a non-empty string": "some string",
660                 "zero": 0,
661                 "a number other than zero": 1,
662                 "true": true,
663                 "false": false,
664                 "null": null,
665                 "undefined": undefined,
666                 "a plain object": {},
667                 "an array": [ 1, 2, 3 ]
669         }, function( message, value ) {
670                 ok(
671                         jQuery.isFunction(
672                                 jQuery.when( value ).done(function( resolveValue ) {
673                                         strictEqual( this, window, "Context is the global object with " + message );
674                                         strictEqual( resolveValue, value, "Test the promise was resolved with " + message );
675                                 }).promise
676                         ),
677                         "Test " + message + " triggers the creation of a new Promise"
678                 );
679         });
681         ok(
682                 jQuery.isFunction(
683                         jQuery.when().done(function( resolveValue ) {
684                                 strictEqual( this, window, "Test the promise was resolved with window as its context" );
685                                 strictEqual( resolveValue, undefined, "Test the promise was resolved with no parameter" );
686                         }).promise
687                 ),
688                 "Test calling when with no parameter triggers the creation of a new Promise"
689         );
691         var cache,
692                 context = {};
694         jQuery.when( jQuery.Deferred().resolveWith( context ) ).done(function() {
695                 strictEqual( this, context, "when( promise ) propagates context" );
696         });
698         jQuery.each([ 1, 2, 3 ], function( k, i ) {
700                 jQuery.when( cache || jQuery.Deferred(function() {
701                                 this.resolve( i );
702                         })
703                 ).done(function( value ) {
705                         strictEqual( value, 1, "Function executed" + ( i > 1 ? " only once" : "" ) );
706                         cache = value;
707                 });
709         });
712 test( "jQuery.when - joined", function() {
714         expect( 195 );
716         var deferreds = {
717                         rawValue: 1,
718                         fulfilled: jQuery.Deferred().resolve( 1 ),
719                         rejected: jQuery.Deferred().reject( 0 ),
720                         notified: jQuery.Deferred().notify( true ),
721                         eventuallyFulfilled: jQuery.Deferred().notify( true ),
722                         eventuallyRejected: jQuery.Deferred().notify( true ),
723                         fulfilledStandardPromise: Promise.resolve( 1 ),
724                         rejectedStandardPromise: Promise.reject( 0 )
725                 },
726                 willSucceed = {
727                         rawValue: true,
728                         fulfilled: true,
729                         eventuallyFulfilled: true,
730                         fulfilledStandardPromise: true
731                 },
732                 willError = {
733                         rejected: true,
734                         eventuallyRejected: true,
735                         rejectedStandardPromise: true
736                 },
737                 willNotify = {
738                         notified: true,
739                         eventuallyFulfilled: true,
740                         eventuallyRejected: true
741                 },
742                 counter = 49;
744         stop();
746         function restart() {
747                 if ( !--counter ) {
748                         start();
749                 }
750         }
752         jQuery.each( deferreds, function( id1, defer1 ) {
753                 jQuery.each( deferreds, function( id2, defer2 ) {
754                         var shouldResolve = willSucceed[ id1 ] && willSucceed[ id2 ],
755                                 shouldError = willError[ id1 ] || willError[ id2 ],
756                                 shouldNotify = willNotify[ id1 ] || willNotify[ id2 ],
757                                 expected = shouldResolve ? [ 1, 1 ] : [ 0, undefined ],
758                                 expectedNotify = shouldNotify && [ willNotify[ id1 ], willNotify[ id2 ] ],
759                                 code = "jQuery.when( " + id1 + ", " + id2 + " )",
760                                 context1 = defer1 && jQuery.isFunction( defer1.promise ) ? defer1.promise() :
761                                         ( defer1.then ? window : undefined ),
762                                 context2 = defer2 && jQuery.isFunction( defer2.promise ) ? defer2.promise() :
763                                         ( defer2.then ? window : undefined );
765                         jQuery.when( defer1, defer2 ).done(function( a, b ) {
766                                 if ( shouldResolve ) {
767                                         deepEqual( [ a, b ], expected, code + " => resolve" );
768                                         strictEqual( this[ 0 ], context1, code + " => first context OK" );
769                                         strictEqual( this[ 1 ], context2, code + " => second context OK" );
770                                 } else {
771                                         ok( false,  code + " => resolve" );
772                                 }
773                         }).fail(function( a, b ) {
774                                 if ( shouldError ) {
775                                         deepEqual( [ a, b ], expected, code + " => reject" );
776                                 } else {
777                                         ok( false, code + " => reject" );
778                                 }
779                         }).progress(function( a, b ) {
780                                 deepEqual( [ a, b ], expectedNotify, code + " => progress" );
781                                 strictEqual( this[ 0 ], expectedNotify[ 0 ] ? context1 : undefined, code + " => first context OK" );
782                                 strictEqual( this[ 1 ], expectedNotify[ 1 ] ? context2 : undefined, code + " => second context OK" );
783                         }).always( restart );
784                 });
785         });
786         deferreds.eventuallyFulfilled.resolve( 1 );
787         deferreds.eventuallyRejected.reject( 0 );
790 test( "jQuery.when - resolved", function() {
792         expect( 6 );
794         var a = jQuery.Deferred().notify( 1 ).resolve( 4 ),
795                 b = jQuery.Deferred().notify( 2 ).resolve( 5 ),
796                 c = jQuery.Deferred().notify( 3 ).resolve( 6 );
798         jQuery.when( a, b, c ).progress(function( a, b, c ) {
799                 strictEqual( a, 1, "first notify value ok" );
800                 strictEqual( b, 2, "second notify value ok" );
801                 strictEqual( c, 3, "third notify value ok" );
802         }).done(function( a, b, c ) {
803                 strictEqual( a, 4, "first resolve value ok" );
804                 strictEqual( b, 5, "second resolve value ok" );
805                 strictEqual( c, 6, "third resolve value ok" );
806         }).fail(function() {
807                 ok( false, "Error on resolve" );
808         });
811 test( "jQuery.when - filtering", function() {
813         expect( 2 );
815         function increment( x ) {
816                 return x + 1;
817         }
819         stop();
821         jQuery.when(
822                 jQuery.Deferred().resolve( 3 ).then( increment ),
823                 jQuery.Deferred().reject( 5 ).then( null, increment )
824         ).done(function( four, six ) {
825                 strictEqual( four, 4, "resolved value incremented" );
826                 strictEqual( six, 6, "rejected value incremented" );
827                 start();
828         });
831 test( "jQuery.when - exceptions", function() {
833         expect( 2 );
835         function woops() {
836                 throw "exception thrown";
837         }
839         stop();
841         jQuery.Deferred().resolve().then( woops ).fail(function( doneException ) {
842                 strictEqual( doneException, "exception thrown", "throwing in done handler" );
843                 jQuery.Deferred().reject().then( null, woops ).fail(function( failException ) {
844                         strictEqual( failException, "exception thrown", "throwing in fail handler" );
845                         start();
846                 });
847         });
850 test( "jQuery.when - chaining", function() {
852         expect( 4 );
854         var defer = jQuery.Deferred();
856         function chain() {
857                 return defer;
858         }
860         function chainStandard() {
861                 return Promise.resolve( "std deferred" );
862         }
864         stop();
866         jQuery.when(
867                 jQuery.Deferred().resolve( 3 ).then( chain ),
868                 jQuery.Deferred().reject( 5 ).then( null, chain ),
869                 jQuery.Deferred().resolve( 3 ).then( chainStandard ),
870                 jQuery.Deferred().reject( 5 ).then( null, chainStandard )
871         ).done(function( v1, v2, s1, s2 ) {
872                 strictEqual( v1, "other deferred", "chaining in done handler" );
873                 strictEqual( v2, "other deferred", "chaining in fail handler" );
874                 strictEqual( s1, "std deferred", "chaining thenable in done handler" );
875                 strictEqual( s2, "std deferred", "chaining thenable in fail handler" );
876                 start();
877         });
879         defer.resolve( "other deferred" );