aboutsummaryrefslogtreecommitdiffstats
path: root/test
diff options
context:
space:
mode:
authorRichard Gibson <richard.gibson@gmail.com>2016-04-14 23:59:30 -0400
committerRichard Gibson <richard.gibson@gmail.com>2016-05-02 12:30:31 -0400
commit356a3bccb0e7468a2c8ce7d8c9c6cd0c5d436b8b (patch)
treeb0d1a8401252ac37b7254d4157c2e350a6cbc24b /test
parent0bd98b1b13872255225358f328bee1f980755483 (diff)
downloadjquery-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.js51
-rw-r--r--test/unit/deferred.js401
-rw-r--r--test/unit/effects.js2
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 );
} );
} );
} );