Build: Bump the github-actions group across 1 directory with 2 updates
[jquery.git] / test / unit / deferred.js
blob1ff0e56181aab1a4af5a5225582b2352e807a630
1 QUnit.module( "deferred", {
2 afterEach: moduleTeardown
3 } );
5 ( function() {
7 if ( !includesModule( "deferred" ) ) {
8 return;
11 jQuery.each( [ "", " - new operator" ], function( _, withNew ) {
13 function createDeferred( fn ) {
14 return withNew ? new jQuery.Deferred( fn ) : jQuery.Deferred( fn );
17 QUnit.test( "jQuery.Deferred" + withNew, function( assert ) {
19 assert.expect( 23 );
21 var defer = createDeferred();
23 assert.ok( typeof defer.pipe === "function", "defer.pipe is a function" );
25 defer.resolve().done( function() {
26 assert.ok( true, "Success on resolve" );
27 assert.strictEqual( defer.state(), "resolved", "Deferred is resolved (state)" );
28 } ).fail( function() {
29 assert.ok( false, "Error on resolve" );
30 } ).always( function() {
31 assert.ok( true, "Always callback on resolve" );
32 } );
34 defer = createDeferred();
35 defer.reject().done( function() {
36 assert.ok( false, "Success on reject" );
37 } ).fail( function() {
38 assert.ok( true, "Error on reject" );
39 assert.strictEqual( defer.state(), "rejected", "Deferred is rejected (state)" );
40 } ).always( function() {
41 assert.ok( true, "Always callback on reject" );
42 } );
44 createDeferred( function( defer ) {
45 assert.ok( this === defer, "Defer passed as this & first argument" );
46 this.resolve( "done" );
47 } ).done( function( value ) {
48 assert.strictEqual( value, "done", "Passed function executed" );
49 } );
51 createDeferred( function( defer ) {
52 var promise = defer.promise(),
53 func = function() {},
54 funcPromise = defer.promise( func );
55 assert.strictEqual( defer.promise(), promise, "promise is always the same" );
56 assert.strictEqual( funcPromise, func, "non objects get extended" );
57 jQuery.each( promise, function( key ) {
58 if ( typeof promise[ key ] !== "function" ) {
59 assert.ok( false, key + " is a function (" + typeof( promise[ key ] ) + ")" );
61 if ( promise[ key ] !== func[ key ] ) {
62 assert.strictEqual( func[ key ], promise[ key ], key + " is the same" );
64 } );
65 } );
67 jQuery.expandedEach = jQuery.each;
68 jQuery.expandedEach( "resolve reject".split( " " ), function( _, change ) {
69 createDeferred( function( defer ) {
70 assert.strictEqual( defer.state(), "pending", "pending after creation" );
71 var checked = 0;
72 defer.progress( function( value ) {
73 assert.strictEqual( value, checked, "Progress: right value (" + value + ") received" );
74 } );
75 for ( checked = 0; checked < 3; checked++ ) {
76 defer.notify( checked );
78 assert.strictEqual( defer.state(), "pending", "pending after notification" );
79 defer[ change ]();
80 assert.notStrictEqual( defer.state(), "pending", "not pending after " + change );
81 defer.notify();
82 } );
83 } );
84 } );
85 } );
87 QUnit.test( "jQuery.Deferred - chainability", function( assert ) {
89 var defer = jQuery.Deferred();
91 assert.expect( 10 );
93 jQuery.expandedEach = jQuery.each;
94 jQuery.expandedEach( "resolve reject notify resolveWith rejectWith notifyWith done fail progress always".split( " " ), function( _, method ) {
95 var object = {
96 m: defer[ method ]
98 assert.strictEqual( object.m(), object, method + " is chainable" );
99 } );
100 } );
102 QUnit.test( "jQuery.Deferred.then - filtering (done)", function( assert ) {
104 assert.expect( 4 );
106 var value1, value2, value3,
107 defer = jQuery.Deferred(),
108 piped = defer.then( function( a, b ) {
109 return a * b;
110 } ),
111 done = jQuery.map( new Array( 3 ), function() {
112 return assert.async();
113 } );
115 piped.done( function( result ) {
116 value3 = result;
117 } );
119 defer.done( function( a, b ) {
120 value1 = a;
121 value2 = b;
122 } );
124 defer.resolve( 2, 3 ).then( function() {
125 assert.strictEqual( value1, 2, "first resolve value ok" );
126 assert.strictEqual( value2, 3, "second resolve value ok" );
127 assert.strictEqual( value3, 6, "result of filter ok" );
128 done.pop().call();
129 } );
131 jQuery.Deferred().reject().then( function() {
132 assert.ok( false, "then should not be called on reject" );
133 } ).then( null, done.pop() );
135 jQuery.Deferred().resolve().then( jQuery.noop ).done( function( value ) {
136 assert.strictEqual( value, undefined, "then done callback can return undefined/null" );
137 done.pop().call();
138 } );
139 } );
141 QUnit.test( "jQuery.Deferred.then - filtering (fail)", function( assert ) {
143 assert.expect( 4 );
145 var value1, value2, value3,
146 defer = jQuery.Deferred(),
147 piped = defer.then( null, function( a, b ) {
148 return a * b;
149 } ),
150 done = jQuery.map( new Array( 3 ), function() {
151 return assert.async();
152 } );
154 piped.done( function( result ) {
155 value3 = result;
156 } );
158 defer.fail( function( a, b ) {
159 value1 = a;
160 value2 = b;
161 } );
163 defer.reject( 2, 3 ).then( null, function() {
164 assert.strictEqual( value1, 2, "first reject value ok" );
165 assert.strictEqual( value2, 3, "second reject value ok" );
166 assert.strictEqual( value3, 6, "result of filter ok" );
167 done.pop().call();
168 } );
170 jQuery.Deferred().resolve().then( null, function() {
171 assert.ok( false, "then should not be called on resolve" );
172 } ).then( done.pop() );
174 jQuery.Deferred().reject().then( null, jQuery.noop ).done( function( value ) {
175 assert.strictEqual( value, undefined, "then fail callback can return undefined/null" );
176 done.pop().call();
177 } );
178 } );
180 QUnit.test( "jQuery.Deferred.catch", function( assert ) {
181 assert.expect( 4 );
183 var value1, value2, value3,
184 defer = jQuery.Deferred(),
185 piped = defer.catch( function( a, b ) {
186 return a * b;
187 } ),
188 done = jQuery.map( new Array( 3 ), function() {
189 return assert.async();
190 } );
192 piped.done( function( result ) {
193 value3 = result;
194 } );
196 defer.fail( function( a, b ) {
197 value1 = a;
198 value2 = b;
199 } );
201 defer.reject( 2, 3 ).catch( function() {
202 assert.strictEqual( value1, 2, "first reject value ok" );
203 assert.strictEqual( value2, 3, "second reject value ok" );
204 assert.strictEqual( value3, 6, "result of filter ok" );
205 done.pop().call();
206 } );
208 jQuery.Deferred().resolve().catch( function() {
209 assert.ok( false, "then should not be called on resolve" );
210 } ).then( done.pop() );
212 jQuery.Deferred().reject().catch( jQuery.noop ).done( function( value ) {
213 assert.strictEqual( value, undefined, "then fail callback can return undefined/null" );
214 done.pop().call();
215 } );
216 } );
218 QUnit.test( "[PIPE ONLY] jQuery.Deferred.pipe - filtering (fail)", function( assert ) {
220 assert.expect( 4 );
222 var value1, value2, value3,
223 defer = jQuery.Deferred(),
224 piped = defer.pipe( null, function( a, b ) {
225 return a * b;
226 } ),
227 done = jQuery.map( new Array( 3 ), function() {
228 return assert.async();
229 } );
231 piped.fail( function( result ) {
232 value3 = result;
233 } );
235 defer.fail( function( a, b ) {
236 value1 = a;
237 value2 = b;
238 } );
240 defer.reject( 2, 3 ).pipe( null, function() {
241 assert.strictEqual( value1, 2, "first reject value ok" );
242 assert.strictEqual( value2, 3, "second reject value ok" );
243 assert.strictEqual( value3, 6, "result of filter ok" );
244 done.pop().call();
245 } );
247 jQuery.Deferred().resolve().pipe( null, function() {
248 assert.ok( false, "then should not be called on resolve" );
249 } ).then( done.pop() );
251 jQuery.Deferred().reject().pipe( null, jQuery.noop ).fail( function( value ) {
252 assert.strictEqual( value, undefined, "then fail callback can return undefined/null" );
253 done.pop().call();
254 } );
255 } );
257 QUnit.test( "jQuery.Deferred.then - filtering (progress)", function( assert ) {
259 assert.expect( 3 );
261 var value1, value2, value3,
262 defer = jQuery.Deferred(),
263 piped = defer.then( null, null, function( a, b ) {
264 return a * b;
265 } ),
266 done = assert.async();
268 piped.progress( function( result ) {
269 value3 = result;
270 } );
272 defer.progress( function( a, b ) {
273 value1 = a;
274 value2 = b;
275 } );
277 defer.notify( 2, 3 ).then( null, null, function() {
278 assert.strictEqual( value1, 2, "first progress value ok" );
279 assert.strictEqual( value2, 3, "second progress value ok" );
280 assert.strictEqual( value3, 6, "result of filter ok" );
281 done();
282 } );
283 } );
285 QUnit.test( "jQuery.Deferred.then - deferred (done)", function( assert ) {
287 assert.expect( 3 );
289 var value1, value2, value3,
290 defer = jQuery.Deferred(),
291 piped = defer.then( function( a, b ) {
292 return jQuery.Deferred( function( defer ) {
293 defer.reject( a * b );
294 } );
295 } ),
296 done = assert.async();
298 piped.fail( function( result ) {
299 value3 = result;
300 } );
302 defer.done( function( a, b ) {
303 value1 = a;
304 value2 = b;
305 } );
307 defer.resolve( 2, 3 );
309 piped.fail( function() {
310 assert.strictEqual( value1, 2, "first resolve value ok" );
311 assert.strictEqual( value2, 3, "second resolve value ok" );
312 assert.strictEqual( value3, 6, "result of filter ok" );
313 done();
314 } );
315 } );
317 QUnit.test( "jQuery.Deferred.then - deferred (fail)", function( assert ) {
319 assert.expect( 3 );
321 var value1, value2, value3,
322 defer = jQuery.Deferred(),
323 piped = defer.then( null, function( a, b ) {
324 return jQuery.Deferred( function( defer ) {
325 defer.resolve( a * b );
326 } );
327 } ),
328 done = assert.async();
330 piped.done( function( result ) {
331 value3 = result;
332 } );
334 defer.fail( function( a, b ) {
335 value1 = a;
336 value2 = b;
337 } );
339 defer.reject( 2, 3 );
341 piped.done( function() {
342 assert.strictEqual( value1, 2, "first reject value ok" );
343 assert.strictEqual( value2, 3, "second reject value ok" );
344 assert.strictEqual( value3, 6, "result of filter ok" );
345 done();
346 } );
347 } );
349 QUnit.test( "jQuery.Deferred.then - deferred (progress)", function( assert ) {
351 assert.expect( 3 );
353 var value1, value2, value3,
354 defer = jQuery.Deferred(),
355 piped = defer.then( null, null, function( a, b ) {
356 return jQuery.Deferred( function( defer ) {
357 defer.resolve( a * b );
358 } );
359 } ),
360 done = assert.async();
362 piped.progress( function( result ) {
363 return jQuery.Deferred().resolve().then( function() {
364 return result;
365 } ).then( function( result ) {
366 value3 = result;
367 } );
368 } );
370 defer.progress( function( a, b ) {
371 value1 = a;
372 value2 = b;
373 } );
375 defer.notify( 2, 3 );
377 piped.then( null, null, function( result ) {
378 return jQuery.Deferred().resolve().then( function() {
379 return result;
380 } ).then( function() {
381 assert.strictEqual( value1, 2, "first progress value ok" );
382 assert.strictEqual( value2, 3, "second progress value ok" );
383 assert.strictEqual( value3, 6, "result of filter ok" );
384 done();
385 } );
386 } );
387 } );
389 QUnit.test( "[PIPE ONLY] jQuery.Deferred.pipe - deferred (progress)", function( assert ) {
391 assert.expect( 3 );
393 var value1, value2, value3,
394 defer = jQuery.Deferred(),
395 piped = defer.pipe( null, null, function( a, b ) {
396 return jQuery.Deferred( function( defer ) {
397 defer.resolve( a * b );
398 } );
399 } ),
400 done = assert.async();
402 piped.done( function( result ) {
403 value3 = result;
404 } );
406 defer.progress( function( a, b ) {
407 value1 = a;
408 value2 = b;
409 } );
411 defer.notify( 2, 3 );
413 piped.done( function() {
414 assert.strictEqual( value1, 2, "first progress value ok" );
415 assert.strictEqual( value2, 3, "second progress value ok" );
416 assert.strictEqual( value3, 6, "result of filter ok" );
417 done();
418 } );
419 } );
421 QUnit.test( "jQuery.Deferred.then - context", function( assert ) {
423 assert.expect( 11 );
425 var defer, piped, defer2, piped2,
426 context = { custom: true },
427 done = jQuery.map( new Array( 5 ), function() {
428 return assert.async();
429 } );
431 jQuery.Deferred().resolveWith( context, [ 2 ] ).then( function( value ) {
432 assert.strictEqual( this, context, "custom context received by .then handler" );
433 return value * 3;
434 } ).done( function( value ) {
435 assert.notStrictEqual( this, context,
436 "custom context not propagated through .then handler" );
437 assert.strictEqual( value, 6, "proper value received" );
438 done.pop().call();
439 } );
441 jQuery.Deferred().resolveWith( context, [ 2 ] ).then().done( function( value ) {
442 assert.strictEqual( this, context,
443 "custom context propagated through .then without handler" );
444 assert.strictEqual( value, 2, "proper value received" );
445 done.pop().call();
446 } );
448 jQuery.Deferred().resolve().then( function() {
449 assert.strictEqual( this, window, "default context in .then handler" );
450 return jQuery.Deferred().resolveWith( context );
451 } ).done( function() {
452 assert.strictEqual( this, context,
453 "custom context of returned deferred correctly propagated" );
454 done.pop().call();
455 } );
457 defer = jQuery.Deferred();
458 piped = defer.then( function( value ) {
459 return value * 3;
460 } );
462 defer.resolve( 2 );
464 piped.done( function( value ) {
465 assert.strictEqual( this, window, ".then handler does not introduce context" );
466 assert.strictEqual( value, 6, "proper value received" );
467 done.pop().call();
468 } );
470 defer2 = jQuery.Deferred();
471 piped2 = defer2.then();
473 defer2.resolve( 2 );
475 piped2.done( function( value ) {
476 assert.strictEqual( this, window, ".then without handler does not introduce context" );
477 assert.strictEqual( value, 2, "proper value received (without passing function)" );
478 done.pop().call();
479 } );
480 } );
482 QUnit.test( "[PIPE ONLY] jQuery.Deferred.pipe - context", function( assert ) {
484 assert.expect( 11 );
486 var defer, piped, defer2, piped2,
487 context = { custom: true },
488 done = jQuery.map( new Array( 5 ), function() {
489 return assert.async();
490 } );
492 jQuery.Deferred().resolveWith( context, [ 2 ] ).pipe( function( value ) {
493 assert.strictEqual( this, context, "custom context received by .pipe handler" );
494 return value * 3;
495 } ).done( function( value ) {
496 assert.strictEqual( this, context,
497 "[PIPE ONLY] custom context propagated through .pipe handler" );
498 assert.strictEqual( value, 6, "proper value received" );
499 done.pop().call();
500 } );
502 jQuery.Deferred().resolveWith( context, [ 2 ] ).pipe().done( function( value ) {
503 assert.strictEqual( this, context,
504 "[PIPE ONLY] custom context propagated through .pipe without handler" );
505 assert.strictEqual( value, 2, "proper value received" );
506 done.pop().call();
507 } );
509 jQuery.Deferred().resolve().pipe( function() {
510 assert.strictEqual( this, window, "default context in .pipe handler" );
511 return jQuery.Deferred().resolveWith( context );
512 } ).done( function() {
513 assert.strictEqual( this, context,
514 "custom context of returned deferred correctly propagated" );
515 done.pop().call();
516 } );
518 defer = jQuery.Deferred();
519 piped = defer.pipe( function( value ) {
520 return value * 3;
521 } );
523 defer.resolve( 2 );
525 piped.done( function( value ) {
526 assert.strictEqual( this, window, ".pipe handler does not introduce context" );
527 assert.strictEqual( value, 6, "proper value received" );
528 done.pop().call();
529 } );
531 defer2 = jQuery.Deferred();
532 piped2 = defer2.pipe();
534 defer2.resolve( 2 );
536 piped2.done( function( value ) {
537 assert.strictEqual( this, window, ".pipe without handler does not introduce context" );
538 assert.strictEqual( value, 2, "proper value received (without passing function)" );
539 done.pop().call();
540 } );
541 } );
543 QUnit.test( "jQuery.Deferred.then - spec compatibility", function( assert ) {
545 assert.expect( 1 );
547 var done = assert.async(),
548 defer = jQuery.Deferred();
550 defer.done( function() {
551 setTimeout( done );
552 throw new Error();
553 } );
555 defer.then( function() {
556 assert.ok( true, "errors in .done callbacks don't stop .then handlers" );
557 } );
559 try {
560 defer.resolve();
561 } catch ( _ ) {}
562 } );
564 QUnit.testUnlessIE( "jQuery.Deferred.then - IsCallable determination (gh-3596)",
565 function( assert ) {
567 assert.expect( 1 );
569 var done = assert.async(),
570 defer = jQuery.Deferred();
572 function faker() {
573 assert.ok( true, "handler with non-'Function' @@toStringTag gets invoked" );
575 faker[ Symbol.toStringTag ] = "String";
577 defer.then( faker ).then( done );
579 defer.resolve();
580 } );
582 QUnit.test( "jQuery.Deferred.exceptionHook", function( assert ) {
584 assert.expect( 2 );
586 var done = assert.async(),
587 defer = jQuery.Deferred(),
588 oldWarn = window.console.warn;
590 window.console.warn = function( _intro, error ) {
591 assert.ok( /barf/.test( error.message + "\n" + error.stack ),
592 "Error mentions the method: " + error.message + "\n" + error.stack );
595 jQuery.when(
596 defer.then( function() {
598 // Should get an error
599 jQuery.barf();
600 } ).then( null, jQuery.noop ),
602 defer.then( function() {
604 // Should NOT get an error
605 throw new Error( "Make me a sandwich" );
606 } ).then( null, jQuery.noop )
607 ).then( function barf( ) {
608 jQuery.thisDiesToo();
609 } ).then( null, function( ) {
610 window.console.warn = oldWarn;
611 done();
612 } );
614 defer.resolve();
615 } );
617 QUnit.test( "jQuery.Deferred.exceptionHook with error hooks", function( assert ) {
619 assert.expect( 2 );
621 var done = assert.async(),
622 defer = jQuery.Deferred(),
623 oldWarn = window.console.warn;
625 jQuery.Deferred.getErrorHook = function() {
627 // Default exceptionHook assumes the stack is in a form console.warn can log,
628 // but a custom getErrorHook+exceptionHook pair could save a raw form and
629 // format it to a string only when an exception actually occurs.
630 // For the unit test we just ensure the plumbing works.
631 return "NO ERROR FOR YOU";
634 window.console.warn = function() {
635 var msg = Array.prototype.join.call( arguments, " " );
636 assert.ok( /cough_up_hairball/.test( msg ), "Function mentioned: " + msg );
637 assert.ok( /NO ERROR FOR YOU/.test( msg ), "Error included: " + msg );
640 defer.then( function() {
641 jQuery.cough_up_hairball();
642 } ).then( null, function( ) {
643 window.console.warn = oldWarn;
644 delete jQuery.Deferred.getErrorHook;
645 done();
646 } );
648 defer.resolve();
649 } );
651 QUnit.test( "jQuery.Deferred - 1.x/2.x compatibility", function( assert ) {
653 assert.expect( 8 );
655 var context = { id: "callback context" },
656 thenable = jQuery.Deferred().resolve( "thenable fulfillment" ).promise(),
657 done = jQuery.map( new Array( 8 ), function() {
658 return assert.async();
659 } );
661 thenable.unwrapped = false;
663 jQuery.Deferred().resolve( 1, 2 ).then( function() {
664 assert.deepEqual( [].slice.call( arguments ), [ 1, 2 ],
665 ".then fulfillment callbacks receive all resolution values" );
666 done.pop().call();
667 } );
668 jQuery.Deferred().reject( 1, 2 ).then( null, function() {
669 assert.deepEqual( [].slice.call( arguments ), [ 1, 2 ],
670 ".then rejection callbacks receive all rejection values" );
671 done.pop().call();
672 } );
673 jQuery.Deferred().notify( 1, 2 ).then( null, null, function() {
674 assert.deepEqual( [].slice.call( arguments ), [ 1, 2 ],
675 ".then progress callbacks receive all progress values" );
676 done.pop().call();
677 } );
679 jQuery.Deferred().resolveWith( context ).then( function() {
680 assert.deepEqual( this, context, ".then fulfillment callbacks receive context" );
681 done.pop().call();
682 } );
683 jQuery.Deferred().rejectWith( context ).then( null, function() {
684 assert.deepEqual( this, context, ".then rejection callbacks receive context" );
685 done.pop().call();
686 } );
687 jQuery.Deferred().notifyWith( context ).then( null, null, function() {
688 assert.deepEqual( this, context, ".then progress callbacks receive context" );
689 done.pop().call();
690 } );
692 jQuery.Deferred().resolve( thenable ).done( function( value ) {
693 assert.strictEqual( value, thenable, ".done doesn't unwrap thenables" );
694 done.pop().call();
695 } );
697 jQuery.Deferred().notify( thenable ).then().then( null, null, function( value ) {
698 assert.strictEqual( value, "thenable fulfillment",
699 ".then implicit progress callbacks unwrap thenables" );
700 done.pop().call();
701 } );
702 } );
704 QUnit.test( "jQuery.Deferred.then - progress and thenables", function( assert ) {
706 assert.expect( 2 );
708 var trigger = jQuery.Deferred().notify(),
709 expectedProgress = [ "baz", "baz" ],
710 done = jQuery.map( new Array( 2 ), function() {
711 return assert.async();
712 } ),
713 failer = function( evt ) {
714 return function() {
715 assert.ok( false, "no unexpected " + evt );
719 trigger.then( null, null, function() {
720 var notifier = jQuery.Deferred().notify( "foo" );
721 setTimeout( function() {
722 notifier.notify( "bar" ).resolve( "baz" );
723 } );
724 return notifier;
725 } ).then( failer( "fulfill" ), failer( "reject" ), function( v ) {
726 assert.strictEqual( v, expectedProgress.shift(), "expected progress value" );
727 done.pop().call();
728 } );
729 trigger.notify();
730 } );
732 QUnit.test( "jQuery.Deferred - notify and resolve", function( assert ) {
734 assert.expect( 7 );
736 var notifiedResolved = jQuery.Deferred().notify( "foo" )/*xxx .resolve( "bar" )*/,
737 done = jQuery.map( new Array( 3 ), function() {
738 return assert.async();
739 } );
741 notifiedResolved.progress( function( v ) {
742 assert.strictEqual( v, "foo", "progress value" );
743 } );
745 notifiedResolved.pipe().progress( function( v ) {
746 assert.strictEqual( v, "foo", "piped progress value" );
747 } );
749 notifiedResolved.pipe( null, null, function() {
750 return "baz";
751 } ).progress( function( v ) {
752 assert.strictEqual( v, "baz", "replaced piped progress value" );
753 } );
755 notifiedResolved.pipe( null, null, function() {
756 return jQuery.Deferred().notify( "baz" ).resolve( "quux" );
757 } ).progress( function( v ) {
758 assert.strictEqual( v, "baz", "deferred replaced piped progress value" );
759 } );
761 notifiedResolved.then().progress( function( v ) {
762 assert.strictEqual( v, "foo", "then'd progress value" );
763 done.pop().call();
764 } );
766 notifiedResolved.then( null, null, function() {
767 return "baz";
768 } ).progress( function( v ) {
769 assert.strictEqual( v, "baz", "replaced then'd progress value" );
770 done.pop().call();
771 } );
773 notifiedResolved.then( null, null, function() {
774 return jQuery.Deferred().notify( "baz" ).resolve( "quux" );
775 } ).progress( function( v ) {
777 // Progress from the surrogate deferred is ignored
778 assert.strictEqual( v, "quux", "deferred replaced then'd progress value" );
779 done.pop().call();
780 } );
781 } );
783 QUnit.test( "jQuery.Deferred - resolved to a notifying deferred", function( assert ) {
785 assert.expect( 2 );
787 var deferred = jQuery.Deferred(),
788 done = assert.async( 2 );
790 deferred.resolve( jQuery.Deferred( function( notifyingDeferred ) {
791 notifyingDeferred.notify( "foo", "bar" );
792 notifyingDeferred.resolve( "baz", "quux" );
793 } ) );
795 // Apply an empty then to force thenable unwrapping.
796 // See https://github.com/jquery/jquery/issues/3000 for more info.
797 deferred.then().then( function() {
798 assert.deepEqual(
799 [].slice.call( arguments ),
800 [ "baz", "quux" ],
801 "The fulfilled handler receives proper params"
803 done();
804 }, null, function() {
805 assert.deepEqual(
806 [].slice.call( arguments ),
807 [ "foo", "bar" ],
808 "The progress handler receives proper params"
810 done();
811 } );
812 } );
814 QUnit.test( "jQuery.when(nonThenable) - like Promise.resolve", function( assert ) {
815 "use strict";
817 assert.expect( 44 );
819 var defaultContext = ( function getDefaultContext() {
820 return this;
821 } )(),
823 done = assert.async( 20 );
825 jQuery.when()
826 .done( function() {
827 assert.strictEqual( arguments.length, 0, "Resolved .done with no arguments" );
828 assert.strictEqual( this, defaultContext, "Default .done context with no arguments" );
830 .then( function() {
831 assert.strictEqual( arguments.length, 0, "Resolved .then with no arguments" );
832 assert.strictEqual( this, defaultContext, "Default .then context with no arguments" );
833 } );
835 jQuery.each( {
836 "an empty string": "",
837 "a non-empty string": "some string",
838 "zero": 0,
839 "a number other than zero": 1,
840 "true": true,
841 "false": false,
842 "null": null,
843 "undefined": undefined,
844 "a plain object": {},
845 "an array": [ 1, 2, 3 ]
846 }, function( message, value ) {
847 var code = "jQuery.when( " + message + " )",
848 onFulfilled = function( method ) {
849 var call = code + "." + method;
850 return function( resolveValue ) {
851 assert.strictEqual( resolveValue, value, call + " resolve" );
852 assert.strictEqual( this, defaultContext, call + " context" );
853 done();
856 onRejected = function( method ) {
857 var call = code + "." + method;
858 return function() {
859 assert.ok( false, call + " reject" );
860 done();
864 jQuery.when( value )
865 .done( onFulfilled( "done" ) )
866 .fail( onRejected( "done" ) )
867 .then( onFulfilled( "then" ), onRejected( "then" ) );
868 } );
869 } );
871 QUnit.test( "jQuery.when(thenable) - like Promise.resolve", function( assert ) {
872 "use strict";
874 var customToStringThen = {
875 then: function( onFulfilled ) {
876 onFulfilled();
879 if ( typeof Symbol === "function" ) {
880 customToStringThen.then[ Symbol.toStringTag ] = "String";
883 var slice = [].slice,
884 sentinel = { context: "explicit" },
885 eventuallyFulfilled = jQuery.Deferred().notify( true ),
886 eventuallyRejected = jQuery.Deferred().notify( true ),
887 secondaryFulfilled = jQuery.Deferred().resolve( eventuallyFulfilled ),
888 secondaryRejected = jQuery.Deferred().resolve( eventuallyRejected ),
889 inputs = {
890 promise: Promise.resolve( true ),
891 customToStringThen: customToStringThen,
892 rejectedPromise: Promise.reject( false ),
893 deferred: jQuery.Deferred().resolve( true ),
894 eventuallyFulfilled: eventuallyFulfilled,
895 secondaryFulfilled: secondaryFulfilled,
896 eventuallySecondaryFulfilled: jQuery.Deferred().notify( true ),
897 multiDeferred: jQuery.Deferred().resolve( "foo", "bar" ),
898 deferredWith: jQuery.Deferred().resolveWith( sentinel, [ true ] ),
899 multiDeferredWith: jQuery.Deferred().resolveWith( sentinel, [ "foo", "bar" ] ),
900 rejectedDeferred: jQuery.Deferred().reject( false ),
901 eventuallyRejected: eventuallyRejected,
902 secondaryRejected: secondaryRejected,
903 eventuallySecondaryRejected: jQuery.Deferred().notify( true ),
904 multiRejectedDeferred: jQuery.Deferred().reject( "baz", "quux" ),
905 rejectedDeferredWith: jQuery.Deferred().rejectWith( sentinel, [ false ] ),
906 multiRejectedDeferredWith: jQuery.Deferred().rejectWith( sentinel, [ "baz", "quux" ] )
908 contexts = {
909 deferredWith: sentinel,
910 multiDeferredWith: sentinel,
911 rejectedDeferredWith: sentinel,
912 multiRejectedDeferredWith: sentinel
914 willSucceed = {
915 promise: [ true ],
916 customToStringThen: [],
917 deferred: [ true ],
918 eventuallyFulfilled: [ true ],
919 secondaryFulfilled: [ true ],
920 eventuallySecondaryFulfilled: [ true ],
921 multiDeferred: [ "foo", "bar" ],
922 deferredWith: [ true ],
923 multiDeferredWith: [ "foo", "bar" ]
925 willError = {
926 rejectedPromise: [ false ],
927 rejectedDeferred: [ false ],
928 eventuallyRejected: [ false ],
929 secondaryRejected: [ false ],
930 eventuallySecondaryRejected: [ false ],
931 multiRejectedDeferred: [ "baz", "quux" ],
932 rejectedDeferredWith: [ false ],
933 multiRejectedDeferredWith: [ "baz", "quux" ]
935 numCases = Object.keys( willSucceed ).length + Object.keys( willError ).length,
937 defaultContext = ( function getDefaultContext() {
938 return this;
939 } )(),
941 done = assert.async( numCases * 2 );
943 assert.expect( numCases * 4 );
945 jQuery.each( inputs, function( message, value ) {
946 var code = "jQuery.when( " + message + " )",
947 shouldResolve = willSucceed[ message ],
948 shouldError = willError[ message ],
949 context = contexts[ message ] || defaultContext,
950 onFulfilled = function( method ) {
951 var call = code + "." + method;
952 return function() {
953 if ( shouldResolve ) {
954 assert.deepEqual( slice.call( arguments ), shouldResolve,
955 call + " resolve" );
956 assert.strictEqual( this, context, call + " context" );
957 } else {
958 assert.ok( false, call + " resolve" );
960 done();
963 onRejected = function( method ) {
964 var call = code + "." + method;
965 return function() {
966 if ( shouldError ) {
967 assert.deepEqual( slice.call( arguments ), shouldError, call + " reject" );
968 assert.strictEqual( this, context, call + " context" );
969 } else {
970 assert.ok( false, call + " reject" );
972 done();
976 jQuery.when( value )
977 .done( onFulfilled( "done" ) )
978 .fail( onRejected( "done" ) )
979 .then( onFulfilled( "then" ), onRejected( "then" ) );
980 } );
982 setTimeout( function() {
983 eventuallyFulfilled.resolve( true );
984 eventuallyRejected.reject( false );
985 inputs.eventuallySecondaryFulfilled.resolve( secondaryFulfilled );
986 inputs.eventuallySecondaryRejected.resolve( secondaryRejected );
987 }, 50 );
988 } );
990 QUnit.test( "jQuery.when(a, b) - like Promise.all", function( assert ) {
991 "use strict";
993 assert.expect( 196 );
995 var slice = [].slice,
996 deferreds = {
997 rawValue: 1,
998 fulfilled: jQuery.Deferred().resolve( 1 ),
999 rejected: jQuery.Deferred().reject( 0 ),
1000 eventuallyFulfilled: jQuery.Deferred().notify( true ),
1001 eventuallyRejected: jQuery.Deferred().notify( true ),
1002 fulfilledStandardPromise: Promise.resolve( 1 ),
1003 rejectedStandardPromise: Promise.reject( 0 )
1005 willSucceed = {
1006 rawValue: true,
1007 fulfilled: true,
1008 eventuallyFulfilled: true,
1009 fulfilledStandardPromise: true
1011 willError = {
1012 rejected: true,
1013 eventuallyRejected: true,
1014 rejectedStandardPromise: true
1017 defaultContext = ( function getDefaultContext() {
1018 return this;
1019 } )(),
1021 done = assert.async( 98 );
1023 jQuery.each( deferreds, function( id1, v1 ) {
1024 jQuery.each( deferreds, function( id2, v2 ) {
1025 var code = "jQuery.when( " + id1 + ", " + id2 + " )",
1026 shouldResolve = willSucceed[ id1 ] && willSucceed[ id2 ],
1027 shouldError = willError[ id1 ] || willError[ id2 ],
1028 expected = shouldResolve ? [ 1, 1 ] : [ 0 ],
1029 context = shouldResolve ? [ defaultContext, defaultContext ] : defaultContext,
1030 onFulfilled = function( method ) {
1031 var call = code + "." + method;
1032 return function() {
1033 if ( shouldResolve ) {
1034 assert.deepEqual( slice.call( arguments ), expected,
1035 call + " resolve" );
1036 assert.deepEqual( this, context, code + " context" );
1037 } else {
1038 assert.ok( false, call + " resolve" );
1040 done();
1043 onRejected = function( method ) {
1044 var call = code + "." + method;
1045 return function() {
1046 if ( shouldError ) {
1047 assert.deepEqual( slice.call( arguments ), expected, call + " reject" );
1048 assert.deepEqual( this, context, code + " context" );
1049 } else {
1050 assert.ok( false, call + " reject" );
1052 done();
1056 jQuery.when( v1, v2 )
1057 .done( onFulfilled( "done" ) )
1058 .fail( onRejected( "done" ) )
1059 .then( onFulfilled( "then" ), onRejected( "then" ) );
1060 } );
1061 } );
1063 setTimeout( function() {
1064 deferreds.eventuallyFulfilled.resolve( 1 );
1065 deferreds.eventuallyRejected.reject( 0 );
1066 }, 50 );
1067 } );
1069 QUnit.test( "jQuery.when - always returns a new promise", function( assert ) {
1071 assert.expect( 42 );
1073 jQuery.each( {
1074 "no arguments": [],
1075 "non-thenable": [ "foo" ],
1076 "promise": [ Promise.resolve( "bar" ) ],
1077 "rejected promise": [ Promise.reject( "bar" ) ],
1078 "deferred": [ jQuery.Deferred().resolve( "baz" ) ],
1079 "rejected deferred": [ jQuery.Deferred().reject( "baz" ) ],
1080 "multi-resolved deferred": [ jQuery.Deferred().resolve( "qux", "quux" ) ],
1081 "multiple non-thenables": [ "corge", "grault" ],
1082 "multiple deferreds": [
1083 jQuery.Deferred().resolve( "garply" ),
1084 jQuery.Deferred().resolve( "waldo" )
1086 }, function( label, args ) {
1087 var result = jQuery.when.apply( jQuery, args );
1089 assert.ok( typeof result.then === "function", "Thenable returned from " + label );
1090 assert.strictEqual( result.resolve, undefined, "Non-deferred returned from " + label );
1091 assert.strictEqual( result.promise(), result, "Promise returned from " + label );
1093 jQuery.each( args, function( i, arg ) {
1094 assert.notStrictEqual( result, arg, "Returns distinct from arg " + i + " of " + label );
1095 if ( arg.promise ) {
1096 assert.notStrictEqual( result, arg.promise(),
1097 "Returns distinct from promise of arg " + i + " of " + label );
1099 } );
1100 } );
1101 } );
1103 QUnit.test( "jQuery.when - notify does not affect resolved", function( assert ) {
1105 assert.expect( 3 );
1107 var a = jQuery.Deferred().notify( 1 ).resolve( 4 ),
1108 b = jQuery.Deferred().notify( 2 ).resolve( 5 ),
1109 c = jQuery.Deferred().notify( 3 ).resolve( 6 );
1111 jQuery.when( a, b, c ).done( function( a, b, c ) {
1112 assert.strictEqual( a, 4, "first resolve value ok" );
1113 assert.strictEqual( b, 5, "second resolve value ok" );
1114 assert.strictEqual( c, 6, "third resolve value ok" );
1115 } ).fail( function() {
1116 assert.ok( false, "Error on resolve" );
1117 } );
1118 } );
1120 QUnit.test( "jQuery.when(...) - opportunistically synchronous", function( assert ) {
1122 assert.expect( 5 );
1124 var when = "before",
1125 resolved = jQuery.Deferred().resolve( true ),
1126 rejected = jQuery.Deferred().reject( false ),
1127 validate = function( label ) {
1128 return function() {
1129 assert.equal( when, "before", label );
1132 done = assert.async( 5 );
1134 jQuery.when().done( validate( "jQuery.when()" ) ).always( done );
1135 jQuery.when( when ).done( validate( "jQuery.when(nonThenable)" ) ).always( done );
1136 jQuery.when( resolved ).done( validate( "jQuery.when(alreadyFulfilled)" ) ).always( done );
1137 jQuery.when( rejected ).fail( validate( "jQuery.when(alreadyRejected)" ) ).always( done );
1138 jQuery.when( resolved, rejected )
1139 .always( validate( "jQuery.when(alreadyFulfilled, alreadyRejected)" ) )
1140 .always( done );
1142 when = "after";
1143 } );
1145 } )();