diff options
author | Richard Gibson <richard.gibson@gmail.com> | 2016-04-14 23:59:30 -0400 |
---|---|---|
committer | Richard Gibson <richard.gibson@gmail.com> | 2016-05-02 12:30:31 -0400 |
commit | 356a3bccb0e7468a2c8ce7d8c9c6cd0c5d436b8b (patch) | |
tree | b0d1a8401252ac37b7254d4157c2e350a6cbc24b /test | |
parent | 0bd98b1b13872255225358f328bee1f980755483 (diff) | |
download | jquery-356a3bccb0e7468a2c8ce7d8c9c6cd0c5d436b8b.tar.gz jquery-356a3bccb0e7468a2c8ce7d8c9c6cd0c5d436b8b.zip |
Deferred: Separate the two paths in jQuery.when
Single- and no-argument calls act like Promise.resolve.
Multi-argument calls act like Promise.all.
Fixes gh-3029
Closes gh-3059
Diffstat (limited to 'test')
-rw-r--r-- | test/promises_aplus_adapter_deferred.js (renamed from test/promises_aplus_adapter.js) | 0 | ||||
-rw-r--r-- | test/promises_aplus_adapter_when.js | 51 | ||||
-rw-r--r-- | test/unit/deferred.js | 401 | ||||
-rw-r--r-- | test/unit/effects.js | 2 |
4 files changed, 275 insertions, 179 deletions
diff --git a/test/promises_aplus_adapter.js b/test/promises_aplus_adapter_deferred.js index c7440b969..c7440b969 100644 --- a/test/promises_aplus_adapter.js +++ b/test/promises_aplus_adapter_deferred.js diff --git a/test/promises_aplus_adapter_when.js b/test/promises_aplus_adapter_when.js new file mode 100644 index 000000000..0a5ec6756 --- /dev/null +++ b/test/promises_aplus_adapter_when.js @@ -0,0 +1,51 @@ +/* jshint node: true */ + +"use strict"; + +require( "jsdom" ).env( "", function( errors, window ) { + if ( errors ) { + console.error( errors ); + return; + } + + var jQuery = require( ".." )( window ); + + exports.deferred = function() { + var adopted, promised, + obj = { + resolve: function() { + if ( !adopted ) { + adopted = jQuery.when.apply( jQuery, arguments ); + if ( promised ) { + adopted.then( promised.resolve, promised.reject ); + } + } + return adopted; + }, + reject: function( value ) { + if ( !adopted ) { + adopted = jQuery.when( jQuery.Deferred().reject( value ) ); + if ( promised ) { + adopted.then( promised.resolve, promised.reject ); + } + } + return adopted; + }, + + // A manually-constructed thenable that works even if calls precede resolve/reject + promise: { + then: function() { + if ( !adopted ) { + if ( !promised ) { + promised = jQuery.Deferred(); + } + return promised.then.apply( promised, arguments ); + } + return adopted.then.apply( adopted, arguments ); + } + } + }; + + return obj; + }; +} ); diff --git a/test/unit/deferred.js b/test/unit/deferred.js index 305740fa4..830103eeb 100644 --- a/test/unit/deferred.js +++ b/test/unit/deferred.js @@ -761,11 +761,30 @@ QUnit.test( "jQuery.Deferred - notify and resolve", function( assert ) { } ); } ); -QUnit.test( "jQuery.when", function( assert ) { - assert.expect( 37 ); +QUnit.test( "jQuery.when(nonThenable) - like Promise.resolve", function( assert ) { + "use strict"; + + assert.expect( 44 ); + + var + + // Support: Android 4.0 only + // Strict mode functions invoked without .call/.apply get global-object context + defaultContext = (function getDefaultContext() { return this; }).call(), + + done = assert.async( 20 ); + + jQuery.when() + .done( function( resolveValue ) { + assert.strictEqual( resolveValue, undefined, "Resolved .done with no arguments" ); + assert.strictEqual( this, defaultContext, "Default .done context with no arguments" ); + } ) + .then( function( resolveValue ) { + assert.strictEqual( resolveValue, undefined, "Resolved .then with no arguments" ); + assert.strictEqual( this, defaultContext, "Default .then context with no arguments" ); + } ); - // Some other objects jQuery.each( { "an empty string": "", "a non-empty string": "some string", @@ -778,51 +797,136 @@ QUnit.test( "jQuery.when", function( assert ) { "a plain object": {}, "an array": [ 1, 2, 3 ] }, function( message, value ) { - assert.ok( - jQuery.isFunction( - jQuery.when( value ).done( function( resolveValue ) { - assert.strictEqual( this, window, "Context is the global object with " + message ); - assert.strictEqual( resolveValue, value, "Test the promise was resolved with " + message ); - } ).promise - ), - "Test " + message + " triggers the creation of a new Promise" - ); - } ); - - assert.ok( - jQuery.isFunction( - jQuery.when().done( function( resolveValue ) { - assert.strictEqual( this, window, "Test the promise was resolved with window as its context" ); - assert.strictEqual( resolveValue, undefined, "Test the promise was resolved with no parameter" ); - } ).promise - ), - "Test calling when with no parameter triggers the creation of a new Promise" - ); - - var cache, - context = {}; - - jQuery.when( jQuery.Deferred().resolveWith( context ) ).done( function() { - assert.strictEqual( this, context, "when( promise ) propagates context" ); - } ); - - jQuery.each( [ 1, 2, 3 ], function( k, i ) { - jQuery.when( cache || jQuery.Deferred( function() { - this.resolve( i ); - } ) - ).done( function( value ) { - assert.strictEqual( value, 1, "Function executed" + ( i > 1 ? " only once" : "" ) ); - cache = value; - } ); + var code = "jQuery.when( " + message + " )", + onFulfilled = function( method ) { + var call = code + "." + method; + return function( resolveValue ) { + assert.strictEqual( resolveValue, value, call + " resolve" ); + assert.strictEqual( this, defaultContext, call + " context" ); + done(); + }; + }, + onRejected = function( method ) { + var call = code + "." + method; + return function() { + assert.ok( false, call + " reject" ); + done(); + }; + }; + jQuery.when( value ) + .done( onFulfilled( "done" ) ) + .fail( onRejected( "done" ) ) + .then( onFulfilled( "then" ), onRejected( "then" ) ); } ); } ); -QUnit.test( "jQuery.when - joined", function( assert ) { +QUnit.test( "jQuery.when(thenable) - like Promise.resolve", function( assert ) { + "use strict"; + + assert.expect( 56 ); + + var slice = [].slice, + sentinel = { context: "explicit" }, + eventuallyFulfilled = jQuery.Deferred().notify( true ), + eventuallyRejected = jQuery.Deferred().notify( true ), + inputs = { + promise: Promise.resolve( true ), + rejectedPromise: Promise.reject( false ), + deferred: jQuery.Deferred().resolve( true ), + eventuallyFulfilled: eventuallyFulfilled, + secondaryFulfilled: jQuery.Deferred().resolve( eventuallyFulfilled ), + multiDeferred: jQuery.Deferred().resolve( "foo", "bar" ), + deferredWith: jQuery.Deferred().resolveWith( sentinel, [ true ] ), + multiDeferredWith: jQuery.Deferred().resolveWith( sentinel, [ "foo", "bar" ] ), + rejectedDeferred: jQuery.Deferred().reject( false ), + eventuallyRejected: eventuallyRejected, + secondaryRejected: jQuery.Deferred().resolve( eventuallyRejected ), + multiRejectedDeferred: jQuery.Deferred().reject( "baz", "quux" ), + rejectedDeferredWith: jQuery.Deferred().rejectWith( sentinel, [ false ] ), + multiRejectedDeferredWith: jQuery.Deferred().rejectWith( sentinel, [ "baz", "quux" ] ) + }, + contexts = { + deferredWith: sentinel, + multiDeferredWith: sentinel, + rejectedDeferredWith: sentinel, + multiRejectedDeferredWith: sentinel + }, + willSucceed = { + promise: [ true ], + deferred: [ true ], + eventuallyFulfilled: [ true ], + secondaryFulfilled: [ true ], + multiDeferred: [ "foo", "bar" ], + deferredWith: [ true ], + multiDeferredWith: [ "foo", "bar" ] + }, + willError = { + rejectedPromise: [ false ], + rejectedDeferred: [ false ], + eventuallyRejected: [ false ], + secondaryRejected: [ false ], + multiRejectedDeferred: [ "baz", "quux" ], + rejectedDeferredWith: [ false ], + multiRejectedDeferredWith: [ "baz", "quux" ] + }, + + // Support: Android 4.0 only + // Strict mode functions invoked without .call/.apply get global-object context + defaultContext = (function getDefaultContext() { return this; }).call(), + + done = assert.async( 28 ); + + jQuery.each( inputs, function( message, value ) { + var code = "jQuery.when( " + message + " )", + shouldResolve = willSucceed[ message ], + shouldError = willError[ message ], + context = contexts[ message ] || defaultContext, + onFulfilled = function( method ) { + var call = code + "." + method; + return function() { + if ( shouldResolve ) { + assert.deepEqual( slice.call( arguments ), shouldResolve, + call + " resolve" ); + assert.strictEqual( this, context, call + " context" ); + } else { + assert.ok( false, call + " resolve" ); + } + done(); + }; + }, + onRejected = function( method ) { + var call = code + "." + method; + return function() { + if ( shouldError ) { + assert.deepEqual( slice.call( arguments ), shouldError, call + " reject" ); + assert.strictEqual( this, context, call + " context" ); + } else { + assert.ok( false, call + " reject" ); + } + done(); + }; + }; + + jQuery.when( value ) + .done( onFulfilled( "done" ) ) + .fail( onRejected( "done" ) ) + .then( onFulfilled( "then" ), onRejected( "then" ) ); + } ); + + setTimeout( function() { + eventuallyFulfilled.resolve( true ); + eventuallyRejected.reject( false ); + }, 50 ); +} ); - assert.expect( 81 ); +QUnit.test( "jQuery.when(a, b) - like Promise.all", function( assert ) { + "use strict"; - var deferreds = { + assert.expect( 196 ); + + var slice = [].slice, + deferreds = { rawValue: 1, fulfilled: jQuery.Deferred().resolve( 1 ), rejected: jQuery.Deferred().reject( 0 ), @@ -842,46 +946,91 @@ QUnit.test( "jQuery.when - joined", function( assert ) { eventuallyRejected: true, rejectedStandardPromise: true }, - counter = 49, // Support: Android 4.0 only // Strict mode functions invoked without .call/.apply get global-object context - expectedContext = (function() { "use strict"; return this; }).call(); - - QUnit.stop(); + defaultContext = (function getDefaultContext() { return this; }).call(), - function restart() { - if ( !--counter ) { - QUnit.start(); - } - } + done = assert.async( 98 ); - jQuery.each( deferreds, function( id1, defer1 ) { - jQuery.each( deferreds, function( id2, defer2 ) { - var shouldResolve = willSucceed[ id1 ] && willSucceed[ id2 ], + jQuery.each( deferreds, function( id1, v1 ) { + jQuery.each( deferreds, function( id2, v2 ) { + var code = "jQuery.when( " + id1 + ", " + id2 + " )", + shouldResolve = willSucceed[ id1 ] && willSucceed[ id2 ], shouldError = willError[ id1 ] || willError[ id2 ], - expected = shouldResolve ? [ 1, 1 ] : [ 0, undefined ], - code = "jQuery.when( " + id1 + ", " + id2 + " )"; - - jQuery.when( defer1, defer2 ).done( function( a, b ) { - if ( shouldResolve ) { - assert.deepEqual( [ a, b ], expected, code + " => resolve" ); - assert.strictEqual( this[ 0 ], expectedContext, code + " => context[0] OK" ); - assert.strictEqual( this[ 1 ], expectedContext, code + " => context[1] OK" ); - } else { - assert.ok( false, code + " => resolve" ); - } - } ).fail( function( a, b ) { - if ( shouldError ) { - assert.deepEqual( [ a, b ], expected, code + " => reject" ); - } else { - assert.ok( false, code + " => reject" ); - } - } ).always( restart ); + expected = shouldResolve ? [ 1, 1 ] : [ 0 ], + context = shouldResolve ? [ defaultContext, defaultContext ] : defaultContext, + onFulfilled = function( method ) { + var call = code + "." + method; + return function() { + if ( shouldResolve ) { + assert.deepEqual( slice.call( arguments ), expected, + call + " resolve" ); + assert.deepEqual( this, context, code + " context" ); + } else { + assert.ok( false, call + " resolve" ); + } + done(); + }; + }, + onRejected = function( method ) { + var call = code + "." + method; + return function() { + if ( shouldError ) { + assert.deepEqual( slice.call( arguments ), expected, call + " reject" ); + assert.deepEqual( this, context, code + " context" ); + } else { + assert.ok( false, call + " reject" ); + } + done(); + }; + }; + + jQuery.when( v1, v2 ) + .done( onFulfilled( "done" ) ) + .fail( onRejected( "done" ) ) + .then( onFulfilled( "then" ), onRejected( "then" ) ); + } ); + } ); + + setTimeout( function() { + deferreds.eventuallyFulfilled.resolve( 1 ); + deferreds.eventuallyRejected.reject( 0 ); + }, 50 ); +} ); + +QUnit.test( "jQuery.when - always returns a new promise", function( assert ) { + + assert.expect( 42 ); + + jQuery.each( { + "no arguments": [], + "non-thenable": [ "foo" ], + "promise": [ Promise.resolve( "bar" ) ], + "rejected promise": [ Promise.reject( "bar" ) ], + "deferred": [ jQuery.Deferred().resolve( "baz" ) ], + "rejected deferred": [ jQuery.Deferred().reject( "baz" ) ], + "multi-resolved deferred": [ jQuery.Deferred().resolve( "qux", "quux" ) ], + "multiple non-thenables": [ "corge", "grault" ], + "multiple deferreds": [ + jQuery.Deferred().resolve( "garply" ), + jQuery.Deferred().resolve( "waldo" ) + ] + }, function( label, args ) { + var result = jQuery.when.apply( jQuery, args ); + + assert.ok( jQuery.isFunction( result.then ), "Thenable returned from " + label ); + assert.strictEqual( result.resolve, undefined, "Non-deferred returned from " + label ); + assert.strictEqual( result.promise(), result, "Promise returned from " + label ); + + jQuery.each( args, function( i, arg ) { + assert.notStrictEqual( result, arg, "Returns distinct from arg " + i + " of " + label ); + if ( arg.promise ) { + assert.notStrictEqual( result, arg.promise(), + "Returns distinct from promise of arg " + i + " of " + label ); + } } ); } ); - deferreds.eventuallyFulfilled.resolve( 1 ); - deferreds.eventuallyRejected.reject( 0 ); } ); QUnit.test( "jQuery.when - notify does not affect resolved", function( assert ) { @@ -900,107 +1049,3 @@ QUnit.test( "jQuery.when - notify does not affect resolved", function( assert ) assert.ok( false, "Error on resolve" ); } ); } ); - -QUnit.test( "jQuery.when - filtering", function( assert ) { - - assert.expect( 2 ); - - function increment( x ) { - return x + 1; - } - - QUnit.stop(); - - jQuery.when( - jQuery.Deferred().resolve( 3 ).then( increment ), - jQuery.Deferred().reject( 5 ).then( null, increment ) - ).done( function( four, six ) { - assert.strictEqual( four, 4, "resolved value incremented" ); - assert.strictEqual( six, 6, "rejected value incremented" ); - QUnit.start(); - } ); -} ); - -QUnit.test( "jQuery.when - exceptions", function( assert ) { - - assert.expect( 2 ); - - function woops() { - throw "exception thrown"; - } - - QUnit.stop(); - - jQuery.Deferred().resolve().then( woops ).fail( function( doneException ) { - assert.strictEqual( doneException, "exception thrown", "throwing in done handler" ); - jQuery.Deferred().reject().then( null, woops ).fail( function( failException ) { - assert.strictEqual( failException, "exception thrown", "throwing in fail handler" ); - QUnit.start(); - } ); - } ); -} ); - -QUnit.test( "jQuery.when - chaining", function( assert ) { - - assert.expect( 4 ); - - var defer = jQuery.Deferred(); - - function chain() { - return defer; - } - - function chainStandard() { - return Promise.resolve( "std deferred" ); - } - - QUnit.stop(); - - jQuery.when( - jQuery.Deferred().resolve( 3 ).then( chain ), - jQuery.Deferred().reject( 5 ).then( null, chain ), - jQuery.Deferred().resolve( 3 ).then( chainStandard ), - jQuery.Deferred().reject( 5 ).then( null, chainStandard ) - ).done( function( v1, v2, s1, s2 ) { - assert.strictEqual( v1, "other deferred", "chaining in done handler" ); - assert.strictEqual( v2, "other deferred", "chaining in fail handler" ); - assert.strictEqual( s1, "std deferred", "chaining thenable in done handler" ); - assert.strictEqual( s2, "std deferred", "chaining thenable in fail handler" ); - QUnit.start(); - } ); - - defer.resolve( "other deferred" ); -} ); - -QUnit.test( "jQuery.when - solitary thenables", function( assert ) { - - assert.expect( 1 ); - - var done = assert.async(), - rejected = new Promise( function( resolve, reject ) { - setTimeout( function() { - reject( "rejected" ); - }, 100 ); - } ); - - jQuery.when( rejected ).then( - function() { - assert.ok( false, "Rejected, solitary, non-Deferred thenable should not resolve" ); - done(); - }, - function() { - assert.ok( true, "Rejected, solitary, non-Deferred thenable rejected properly" ); - done(); - } - ); -} ); - -QUnit.test( "jQuery.when does not reuse a solitary jQuery Deferred (gh-2018)", function( assert ) { - - assert.expect( 2 ); - var defer = jQuery.Deferred().resolve(), - promise = jQuery.when( defer ); - - assert.equal( promise.state(), "resolved", "Master Deferred is immediately resolved" ); - assert.notStrictEqual( defer.promise(), promise, "jQuery.when returns the master deferred's promise" ); -} ); diff --git a/test/unit/effects.js b/test/unit/effects.js index 5f1913575..2b953cd35 100644 --- a/test/unit/effects.js +++ b/test/unit/effects.js @@ -1028,7 +1028,7 @@ jQuery.each( { jQuery( elem ).remove(); } ); - this.clock.tick( 50 ); + this.clock.tick( 100 ); } ); } ); } ); |