Нет описания

deferred.js 34KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165
  1. QUnit.module( "deferred", {
  2. afterEach: moduleTeardown
  3. } );
  4. ( function() {
  5. if ( !jQuery.Deferred ) {
  6. return;
  7. }
  8. jQuery.each( [ "", " - new operator" ], function( _, withNew ) {
  9. function createDeferred( fn ) {
  10. return withNew ? new jQuery.Deferred( fn ) : jQuery.Deferred( fn );
  11. }
  12. QUnit.test( "jQuery.Deferred" + withNew, function( assert ) {
  13. assert.expect( 23 );
  14. var defer = createDeferred();
  15. assert.ok( typeof defer.pipe === "function", "defer.pipe is a function" );
  16. defer.resolve().done( function() {
  17. assert.ok( true, "Success on resolve" );
  18. assert.strictEqual( defer.state(), "resolved", "Deferred is resolved (state)" );
  19. } ).fail( function() {
  20. assert.ok( false, "Error on resolve" );
  21. } ).always( function() {
  22. assert.ok( true, "Always callback on resolve" );
  23. } );
  24. defer = createDeferred();
  25. defer.reject().done( function() {
  26. assert.ok( false, "Success on reject" );
  27. } ).fail( function() {
  28. assert.ok( true, "Error on reject" );
  29. assert.strictEqual( defer.state(), "rejected", "Deferred is rejected (state)" );
  30. } ).always( function() {
  31. assert.ok( true, "Always callback on reject" );
  32. } );
  33. createDeferred( function( defer ) {
  34. assert.ok( this === defer, "Defer passed as this & first argument" );
  35. this.resolve( "done" );
  36. } ).done( function( value ) {
  37. assert.strictEqual( value, "done", "Passed function executed" );
  38. } );
  39. createDeferred( function( defer ) {
  40. var promise = defer.promise(),
  41. func = function() {},
  42. funcPromise = defer.promise( func );
  43. assert.strictEqual( defer.promise(), promise, "promise is always the same" );
  44. assert.strictEqual( funcPromise, func, "non objects get extended" );
  45. jQuery.each( promise, function( key ) {
  46. if ( typeof promise[ key ] !== "function" ) {
  47. assert.ok( false, key + " is a function (" + typeof( promise[ key ] ) + ")" );
  48. }
  49. if ( promise[ key ] !== func[ key ] ) {
  50. assert.strictEqual( func[ key ], promise[ key ], key + " is the same" );
  51. }
  52. } );
  53. } );
  54. jQuery.expandedEach = jQuery.each;
  55. jQuery.expandedEach( "resolve reject".split( " " ), function( _, change ) {
  56. createDeferred( function( defer ) {
  57. assert.strictEqual( defer.state(), "pending", "pending after creation" );
  58. var checked = 0;
  59. defer.progress( function( value ) {
  60. assert.strictEqual( value, checked, "Progress: right value (" + value + ") received" );
  61. } );
  62. for ( checked = 0; checked < 3; checked++ ) {
  63. defer.notify( checked );
  64. }
  65. assert.strictEqual( defer.state(), "pending", "pending after notification" );
  66. defer[ change ]();
  67. assert.notStrictEqual( defer.state(), "pending", "not pending after " + change );
  68. defer.notify();
  69. } );
  70. } );
  71. } );
  72. } );
  73. QUnit.test( "jQuery.Deferred - chainability", function( assert ) {
  74. var defer = jQuery.Deferred();
  75. assert.expect( 10 );
  76. jQuery.expandedEach = jQuery.each;
  77. jQuery.expandedEach( "resolve reject notify resolveWith rejectWith notifyWith done fail progress always".split( " " ), function( _, method ) {
  78. var object = {
  79. m: defer[ method ]
  80. };
  81. assert.strictEqual( object.m(), object, method + " is chainable" );
  82. } );
  83. } );
  84. QUnit.test( "jQuery.Deferred.then - filtering (done)", function( assert ) {
  85. assert.expect( 4 );
  86. var value1, value2, value3,
  87. defer = jQuery.Deferred(),
  88. piped = defer.then( function( a, b ) {
  89. return a * b;
  90. } ),
  91. done = jQuery.map( new Array( 3 ), function() { return assert.async(); } );
  92. piped.done( function( result ) {
  93. value3 = result;
  94. } );
  95. defer.done( function( a, b ) {
  96. value1 = a;
  97. value2 = b;
  98. } );
  99. defer.resolve( 2, 3 ).then( function() {
  100. assert.strictEqual( value1, 2, "first resolve value ok" );
  101. assert.strictEqual( value2, 3, "second resolve value ok" );
  102. assert.strictEqual( value3, 6, "result of filter ok" );
  103. done.pop().call();
  104. } );
  105. jQuery.Deferred().reject().then( function() {
  106. assert.ok( false, "then should not be called on reject" );
  107. } ).then( null, done.pop() );
  108. jQuery.Deferred().resolve().then( jQuery.noop ).done( function( value ) {
  109. assert.strictEqual( value, undefined, "then done callback can return undefined/null" );
  110. done.pop().call();
  111. } );
  112. } );
  113. QUnit.test( "jQuery.Deferred.then - filtering (fail)", function( assert ) {
  114. assert.expect( 4 );
  115. var value1, value2, value3,
  116. defer = jQuery.Deferred(),
  117. piped = defer.then( null, function( a, b ) {
  118. return a * b;
  119. } ),
  120. done = jQuery.map( new Array( 3 ), function() { return assert.async(); } );
  121. piped.done( function( result ) {
  122. value3 = result;
  123. } );
  124. defer.fail( function( a, b ) {
  125. value1 = a;
  126. value2 = b;
  127. } );
  128. defer.reject( 2, 3 ).then( null, function() {
  129. assert.strictEqual( value1, 2, "first reject value ok" );
  130. assert.strictEqual( value2, 3, "second reject value ok" );
  131. assert.strictEqual( value3, 6, "result of filter ok" );
  132. done.pop().call();
  133. } );
  134. jQuery.Deferred().resolve().then( null, function() {
  135. assert.ok( false, "then should not be called on resolve" );
  136. } ).then( done.pop() );
  137. jQuery.Deferred().reject().then( null, jQuery.noop ).done( function( value ) {
  138. assert.strictEqual( value, undefined, "then fail callback can return undefined/null" );
  139. done.pop().call();
  140. } );
  141. } );
  142. QUnit.test( "jQuery.Deferred.catch", function( assert ) {
  143. assert.expect( 4 );
  144. var value1, value2, value3,
  145. defer = jQuery.Deferred(),
  146. piped = defer[ "catch" ]( function( a, b ) {
  147. return a * b;
  148. } ),
  149. done = jQuery.map( new Array( 3 ), function() { return assert.async(); } );
  150. piped.done( function( result ) {
  151. value3 = result;
  152. } );
  153. defer.fail( function( a, b ) {
  154. value1 = a;
  155. value2 = b;
  156. } );
  157. defer.reject( 2, 3 )[ "catch" ]( function() {
  158. assert.strictEqual( value1, 2, "first reject value ok" );
  159. assert.strictEqual( value2, 3, "second reject value ok" );
  160. assert.strictEqual( value3, 6, "result of filter ok" );
  161. done.pop().call();
  162. } );
  163. jQuery.Deferred().resolve()[ "catch" ]( function() {
  164. assert.ok( false, "then should not be called on resolve" );
  165. } ).then( done.pop() );
  166. jQuery.Deferred().reject()[ "catch" ]( jQuery.noop ).done( function( value ) {
  167. assert.strictEqual( value, undefined, "then fail callback can return undefined/null" );
  168. done.pop().call();
  169. } );
  170. } );
  171. QUnit.test( "[PIPE ONLY] jQuery.Deferred.pipe - filtering (fail)", function( assert ) {
  172. assert.expect( 4 );
  173. var value1, value2, value3,
  174. defer = jQuery.Deferred(),
  175. piped = defer.pipe( null, function( a, b ) {
  176. return a * b;
  177. } ),
  178. done = jQuery.map( new Array( 3 ), function() { return assert.async(); } );
  179. piped.fail( function( result ) {
  180. value3 = result;
  181. } );
  182. defer.fail( function( a, b ) {
  183. value1 = a;
  184. value2 = b;
  185. } );
  186. defer.reject( 2, 3 ).pipe( null, function() {
  187. assert.strictEqual( value1, 2, "first reject value ok" );
  188. assert.strictEqual( value2, 3, "second reject value ok" );
  189. assert.strictEqual( value3, 6, "result of filter ok" );
  190. done.pop().call();
  191. } );
  192. jQuery.Deferred().resolve().pipe( null, function() {
  193. assert.ok( false, "then should not be called on resolve" );
  194. } ).then( done.pop() );
  195. jQuery.Deferred().reject().pipe( null, jQuery.noop ).fail( function( value ) {
  196. assert.strictEqual( value, undefined, "then fail callback can return undefined/null" );
  197. done.pop().call();
  198. } );
  199. } );
  200. QUnit.test( "jQuery.Deferred.then - filtering (progress)", function( assert ) {
  201. assert.expect( 3 );
  202. var value1, value2, value3,
  203. defer = jQuery.Deferred(),
  204. piped = defer.then( null, null, function( a, b ) {
  205. return a * b;
  206. } ),
  207. done = assert.async();
  208. piped.progress( function( result ) {
  209. value3 = result;
  210. } );
  211. defer.progress( function( a, b ) {
  212. value1 = a;
  213. value2 = b;
  214. } );
  215. defer.notify( 2, 3 ).then( null, null, function() {
  216. assert.strictEqual( value1, 2, "first progress value ok" );
  217. assert.strictEqual( value2, 3, "second progress value ok" );
  218. assert.strictEqual( value3, 6, "result of filter ok" );
  219. done();
  220. } );
  221. } );
  222. QUnit.test( "jQuery.Deferred.then - deferred (done)", function( assert ) {
  223. assert.expect( 3 );
  224. var value1, value2, value3,
  225. defer = jQuery.Deferred(),
  226. piped = defer.then( function( a, b ) {
  227. return jQuery.Deferred( function( defer ) {
  228. defer.reject( a * b );
  229. } );
  230. } ),
  231. done = assert.async();
  232. piped.fail( function( result ) {
  233. value3 = result;
  234. } );
  235. defer.done( function( a, b ) {
  236. value1 = a;
  237. value2 = b;
  238. } );
  239. defer.resolve( 2, 3 );
  240. piped.fail( function() {
  241. assert.strictEqual( value1, 2, "first resolve value ok" );
  242. assert.strictEqual( value2, 3, "second resolve value ok" );
  243. assert.strictEqual( value3, 6, "result of filter ok" );
  244. done();
  245. } );
  246. } );
  247. QUnit.test( "jQuery.Deferred.then - deferred (fail)", function( assert ) {
  248. assert.expect( 3 );
  249. var value1, value2, value3,
  250. defer = jQuery.Deferred(),
  251. piped = defer.then( null, function( a, b ) {
  252. return jQuery.Deferred( function( defer ) {
  253. defer.resolve( a * b );
  254. } );
  255. } ),
  256. done = assert.async();
  257. piped.done( function( result ) {
  258. value3 = result;
  259. } );
  260. defer.fail( function( a, b ) {
  261. value1 = a;
  262. value2 = b;
  263. } );
  264. defer.reject( 2, 3 );
  265. piped.done( function() {
  266. assert.strictEqual( value1, 2, "first reject value ok" );
  267. assert.strictEqual( value2, 3, "second reject value ok" );
  268. assert.strictEqual( value3, 6, "result of filter ok" );
  269. done();
  270. } );
  271. } );
  272. QUnit.test( "jQuery.Deferred.then - deferred (progress)", function( assert ) {
  273. assert.expect( 3 );
  274. var value1, value2, value3,
  275. defer = jQuery.Deferred(),
  276. piped = defer.then( null, null, function( a, b ) {
  277. return jQuery.Deferred( function( defer ) {
  278. defer.resolve( a * b );
  279. } );
  280. } ),
  281. done = assert.async();
  282. piped.progress( function( result ) {
  283. return jQuery.Deferred().resolve().then( function() {
  284. return result;
  285. } ).then( function( result ) {
  286. value3 = result;
  287. } );
  288. } );
  289. defer.progress( function( a, b ) {
  290. value1 = a;
  291. value2 = b;
  292. } );
  293. defer.notify( 2, 3 );
  294. piped.then( null, null, function( result ) {
  295. return jQuery.Deferred().resolve().then( function() {
  296. return result;
  297. } ).then( function() {
  298. assert.strictEqual( value1, 2, "first progress value ok" );
  299. assert.strictEqual( value2, 3, "second progress value ok" );
  300. assert.strictEqual( value3, 6, "result of filter ok" );
  301. done();
  302. } );
  303. } );
  304. } );
  305. QUnit.test( "[PIPE ONLY] jQuery.Deferred.pipe - deferred (progress)", function( assert ) {
  306. assert.expect( 3 );
  307. var value1, value2, value3,
  308. defer = jQuery.Deferred(),
  309. piped = defer.pipe( null, null, function( a, b ) {
  310. return jQuery.Deferred( function( defer ) {
  311. defer.resolve( a * b );
  312. } );
  313. } ),
  314. done = assert.async();
  315. piped.done( function( result ) {
  316. value3 = result;
  317. } );
  318. defer.progress( function( a, b ) {
  319. value1 = a;
  320. value2 = b;
  321. } );
  322. defer.notify( 2, 3 );
  323. piped.done( function() {
  324. assert.strictEqual( value1, 2, "first progress value ok" );
  325. assert.strictEqual( value2, 3, "second progress value ok" );
  326. assert.strictEqual( value3, 6, "result of filter ok" );
  327. done();
  328. } );
  329. } );
  330. QUnit.test( "jQuery.Deferred.then - context", function( assert ) {
  331. assert.expect( 11 );
  332. var defer, piped, defer2, piped2,
  333. context = { custom: true },
  334. done = jQuery.map( new Array( 5 ), function() { return assert.async(); } );
  335. jQuery.Deferred().resolveWith( context, [ 2 ] ).then( function( value ) {
  336. assert.strictEqual( this, context, "custom context received by .then handler" );
  337. return value * 3;
  338. } ).done( function( value ) {
  339. assert.notStrictEqual( this, context,
  340. "custom context not propagated through .then handler" );
  341. assert.strictEqual( value, 6, "proper value received" );
  342. done.pop().call();
  343. } );
  344. jQuery.Deferred().resolveWith( context, [ 2 ] ).then().done( function( value ) {
  345. assert.strictEqual( this, context,
  346. "custom context propagated through .then without handler" );
  347. assert.strictEqual( value, 2, "proper value received" );
  348. done.pop().call();
  349. } );
  350. jQuery.Deferred().resolve().then( function() {
  351. assert.strictEqual( this, window, "default context in .then handler" );
  352. return jQuery.Deferred().resolveWith( context );
  353. } ).done( function() {
  354. assert.strictEqual( this, context,
  355. "custom context of returned deferred correctly propagated" );
  356. done.pop().call();
  357. } );
  358. defer = jQuery.Deferred();
  359. piped = defer.then( function( value ) {
  360. return value * 3;
  361. } );
  362. defer.resolve( 2 );
  363. piped.done( function( value ) {
  364. assert.strictEqual( this, window, ".then handler does not introduce context" );
  365. assert.strictEqual( value, 6, "proper value received" );
  366. done.pop().call();
  367. } );
  368. defer2 = jQuery.Deferred();
  369. piped2 = defer2.then();
  370. defer2.resolve( 2 );
  371. piped2.done( function( value ) {
  372. assert.strictEqual( this, window, ".then without handler does not introduce context" );
  373. assert.strictEqual( value, 2, "proper value received (without passing function)" );
  374. done.pop().call();
  375. } );
  376. } );
  377. QUnit.test( "[PIPE ONLY] jQuery.Deferred.pipe - context", function( assert ) {
  378. assert.expect( 11 );
  379. var defer, piped, defer2, piped2,
  380. context = { custom: true },
  381. done = jQuery.map( new Array( 5 ), function() { return assert.async(); } );
  382. jQuery.Deferred().resolveWith( context, [ 2 ] ).pipe( function( value ) {
  383. assert.strictEqual( this, context, "custom context received by .pipe handler" );
  384. return value * 3;
  385. } ).done( function( value ) {
  386. assert.strictEqual( this, context,
  387. "[PIPE ONLY] custom context propagated through .pipe handler" );
  388. assert.strictEqual( value, 6, "proper value received" );
  389. done.pop().call();
  390. } );
  391. jQuery.Deferred().resolveWith( context, [ 2 ] ).pipe().done( function( value ) {
  392. assert.strictEqual( this, context,
  393. "[PIPE ONLY] custom context propagated through .pipe without handler" );
  394. assert.strictEqual( value, 2, "proper value received" );
  395. done.pop().call();
  396. } );
  397. jQuery.Deferred().resolve().pipe( function() {
  398. assert.strictEqual( this, window, "default context in .pipe handler" );
  399. return jQuery.Deferred().resolveWith( context );
  400. } ).done( function() {
  401. assert.strictEqual( this, context,
  402. "custom context of returned deferred correctly propagated" );
  403. done.pop().call();
  404. } );
  405. defer = jQuery.Deferred();
  406. piped = defer.pipe( function( value ) {
  407. return value * 3;
  408. } );
  409. defer.resolve( 2 );
  410. piped.done( function( value ) {
  411. assert.strictEqual( this, window, ".pipe handler does not introduce context" );
  412. assert.strictEqual( value, 6, "proper value received" );
  413. done.pop().call();
  414. } );
  415. defer2 = jQuery.Deferred();
  416. piped2 = defer2.pipe();
  417. defer2.resolve( 2 );
  418. piped2.done( function( value ) {
  419. assert.strictEqual( this, window, ".pipe without handler does not introduce context" );
  420. assert.strictEqual( value, 2, "proper value received (without passing function)" );
  421. done.pop().call();
  422. } );
  423. } );
  424. QUnit.test( "jQuery.Deferred.then - spec compatibility", function( assert ) {
  425. assert.expect( 1 );
  426. var done = assert.async(),
  427. defer = jQuery.Deferred();
  428. defer.done( function() {
  429. setTimeout( done );
  430. throw new Error();
  431. } );
  432. defer.then( function() {
  433. assert.ok( true, "errors in .done callbacks don't stop .then handlers" );
  434. } );
  435. try {
  436. defer.resolve();
  437. } catch ( _ ) {}
  438. } );
  439. QUnit[ typeof Symbol === "function" && Symbol.toStringTag ? "test" : "skip" ](
  440. "jQuery.Deferred.then - IsCallable determination (gh-3596)",
  441. function( assert ) {
  442. assert.expect( 1 );
  443. var done = assert.async(),
  444. defer = jQuery.Deferred();
  445. function faker() {
  446. assert.ok( true, "handler with non-'Function' @@toStringTag gets invoked" );
  447. }
  448. faker[ Symbol.toStringTag ] = "String";
  449. defer.then( faker ).then( done );
  450. defer.resolve();
  451. }
  452. );
  453. // Test fails in IE9 but is skipped there because console is not active
  454. QUnit[ window.console ? "test" : "skip" ]( "jQuery.Deferred.exceptionHook", function( assert ) {
  455. assert.expect( 2 );
  456. var done = assert.async(),
  457. defer = jQuery.Deferred(),
  458. oldWarn = window.console.warn;
  459. window.console.warn = function() {
  460. // Support: Chrome <=41 only
  461. // Some Chrome versions newer than 30 but older than 42 display the "undefined is
  462. // not a function" error, not mentioning the function name. This has been fixed
  463. // in Chrome 42. Relax this test there.
  464. // This affects our Android 5.0 & Yandex.Browser testing.
  465. var msg = Array.prototype.join.call( arguments, " " ),
  466. oldChromium = false;
  467. if ( /chrome/i.test( navigator.userAgent ) ) {
  468. oldChromium = parseInt(
  469. navigator.userAgent.match( /chrome\/(\d+)/i )[ 1 ], 10 ) < 42;
  470. }
  471. if ( oldChromium ) {
  472. assert.ok( /(?:barf|undefined)/.test( msg ), "Message (weak assertion): " + msg );
  473. } else {
  474. assert.ok( /barf/.test( msg ), "Message: " + msg );
  475. }
  476. };
  477. jQuery.when(
  478. defer.then( function() {
  479. // Should get an error
  480. jQuery.barf();
  481. } ).then( null, jQuery.noop ),
  482. defer.then( function() {
  483. // Should NOT get an error
  484. throw new Error( "Make me a sandwich" );
  485. } ).then( null, jQuery.noop )
  486. ).then( function barf( ) {
  487. jQuery.thisDiesToo();
  488. } ).then( null, function( ) {
  489. window.console.warn = oldWarn;
  490. done();
  491. } );
  492. defer.resolve();
  493. } );
  494. // Test fails in IE9 but is skipped there because console is not active
  495. QUnit[ window.console ? "test" : "skip" ]( "jQuery.Deferred.exceptionHook with stack hooks", function( assert ) {
  496. assert.expect( 2 );
  497. var done = assert.async(),
  498. defer = jQuery.Deferred(),
  499. oldWarn = window.console.warn;
  500. jQuery.Deferred.getStackHook = function() {
  501. // Default exceptionHook assumes the stack is in a form console.warn can log,
  502. // but a custom getStackHook+exceptionHook pair could save a raw form and
  503. // format it to a string only when an exception actually occurs.
  504. // For the unit test we just ensure the plumbing works.
  505. return "NO STACK FOR YOU";
  506. };
  507. window.console.warn = function() {
  508. // Support: Chrome <=41 only
  509. // Some Chrome versions newer than 30 but older than 42 display the "undefined is
  510. // not a function" error, not mentioning the function name. This has been fixed
  511. // in Chrome 42. Relax this test there.
  512. // This affects our Android 5.0 & Yandex.Browser testing.
  513. var msg = Array.prototype.join.call( arguments, " " ),
  514. oldChromium = false;
  515. if ( /chrome/i.test( navigator.userAgent ) ) {
  516. oldChromium = parseInt(
  517. navigator.userAgent.match( /chrome\/(\d+)/i )[ 1 ], 10 ) < 42;
  518. }
  519. if ( oldChromium ) {
  520. assert.ok( /(?:cough_up_hairball|undefined)/.test( msg ),
  521. "Function mentioned (weak assertion): " + msg );
  522. } else {
  523. assert.ok( /cough_up_hairball/.test( msg ), "Function mentioned: " + msg );
  524. }
  525. assert.ok( /NO STACK FOR YOU/.test( msg ), "Stack trace included: " + msg );
  526. };
  527. defer.then( function() {
  528. jQuery.cough_up_hairball();
  529. } ).then( null, function( ) {
  530. window.console.warn = oldWarn;
  531. delete jQuery.Deferred.getStackHook;
  532. done();
  533. } );
  534. defer.resolve();
  535. } );
  536. QUnit.test( "jQuery.Deferred - 1.x/2.x compatibility", function( assert ) {
  537. assert.expect( 8 );
  538. var context = { id: "callback context" },
  539. thenable = jQuery.Deferred().resolve( "thenable fulfillment" ).promise(),
  540. done = jQuery.map( new Array( 8 ), function() { return assert.async(); } );
  541. thenable.unwrapped = false;
  542. jQuery.Deferred().resolve( 1, 2 ).then( function() {
  543. assert.deepEqual( [].slice.call( arguments ), [ 1, 2 ],
  544. ".then fulfillment callbacks receive all resolution values" );
  545. done.pop().call();
  546. } );
  547. jQuery.Deferred().reject( 1, 2 ).then( null, function() {
  548. assert.deepEqual( [].slice.call( arguments ), [ 1, 2 ],
  549. ".then rejection callbacks receive all rejection values" );
  550. done.pop().call();
  551. } );
  552. jQuery.Deferred().notify( 1, 2 ).then( null, null, function() {
  553. assert.deepEqual( [].slice.call( arguments ), [ 1, 2 ],
  554. ".then progress callbacks receive all progress values" );
  555. done.pop().call();
  556. } );
  557. jQuery.Deferred().resolveWith( context ).then( function() {
  558. assert.deepEqual( this, context, ".then fulfillment callbacks receive context" );
  559. done.pop().call();
  560. } );
  561. jQuery.Deferred().rejectWith( context ).then( null, function() {
  562. assert.deepEqual( this, context, ".then rejection callbacks receive context" );
  563. done.pop().call();
  564. } );
  565. jQuery.Deferred().notifyWith( context ).then( null, null, function() {
  566. assert.deepEqual( this, context, ".then progress callbacks receive context" );
  567. done.pop().call();
  568. } );
  569. jQuery.Deferred().resolve( thenable ).done( function( value ) {
  570. assert.strictEqual( value, thenable, ".done doesn't unwrap thenables" );
  571. done.pop().call();
  572. } );
  573. jQuery.Deferred().notify( thenable ).then().then( null, null, function( value ) {
  574. assert.strictEqual( value, "thenable fulfillment",
  575. ".then implicit progress callbacks unwrap thenables" );
  576. done.pop().call();
  577. } );
  578. } );
  579. QUnit.test( "jQuery.Deferred.then - progress and thenables", function( assert ) {
  580. assert.expect( 2 );
  581. var trigger = jQuery.Deferred().notify(),
  582. expectedProgress = [ "baz", "baz" ],
  583. done = jQuery.map( new Array( 2 ), function() { return assert.async(); } ),
  584. failer = function( evt ) {
  585. return function() {
  586. assert.ok( false, "no unexpected " + evt );
  587. };
  588. };
  589. trigger.then( null, null, function() {
  590. var notifier = jQuery.Deferred().notify( "foo" );
  591. setTimeout( function() {
  592. notifier.notify( "bar" ).resolve( "baz" );
  593. } );
  594. return notifier;
  595. } ).then( failer( "fulfill" ), failer( "reject" ), function( v ) {
  596. assert.strictEqual( v, expectedProgress.shift(), "expected progress value" );
  597. done.pop().call();
  598. } );
  599. trigger.notify();
  600. } );
  601. QUnit.test( "jQuery.Deferred - notify and resolve", function( assert ) {
  602. assert.expect( 7 );
  603. var notifiedResolved = jQuery.Deferred().notify( "foo" )/*xxx .resolve( "bar" )*/,
  604. done = jQuery.map( new Array( 3 ), function() { return assert.async(); } );
  605. notifiedResolved.progress( function( v ) {
  606. assert.strictEqual( v, "foo", "progress value" );
  607. } );
  608. notifiedResolved.pipe().progress( function( v ) {
  609. assert.strictEqual( v, "foo", "piped progress value" );
  610. } );
  611. notifiedResolved.pipe( null, null, function() {
  612. return "baz";
  613. } ).progress( function( v ) {
  614. assert.strictEqual( v, "baz", "replaced piped progress value" );
  615. } );
  616. notifiedResolved.pipe( null, null, function() {
  617. return jQuery.Deferred().notify( "baz" ).resolve( "quux" );
  618. } ).progress( function( v ) {
  619. assert.strictEqual( v, "baz", "deferred replaced piped progress value" );
  620. } );
  621. notifiedResolved.then().progress( function( v ) {
  622. assert.strictEqual( v, "foo", "then'd progress value" );
  623. done.pop().call();
  624. } );
  625. notifiedResolved.then( null, null, function() {
  626. return "baz";
  627. } ).progress( function( v ) {
  628. assert.strictEqual( v, "baz", "replaced then'd progress value" );
  629. done.pop().call();
  630. } );
  631. notifiedResolved.then( null, null, function() {
  632. return jQuery.Deferred().notify( "baz" ).resolve( "quux" );
  633. } ).progress( function( v ) {
  634. // Progress from the surrogate deferred is ignored
  635. assert.strictEqual( v, "quux", "deferred replaced then'd progress value" );
  636. done.pop().call();
  637. } );
  638. } );
  639. QUnit.test( "jQuery.Deferred - resolved to a notifying deferred", function( assert ) {
  640. assert.expect( 2 );
  641. var deferred = jQuery.Deferred(),
  642. done = assert.async( 2 );
  643. deferred.resolve( jQuery.Deferred( function( notifyingDeferred ) {
  644. notifyingDeferred.notify( "foo", "bar" );
  645. notifyingDeferred.resolve( "baz", "quux" );
  646. } ) );
  647. // Apply an empty then to force thenable unwrapping.
  648. // See https://github.com/jquery/jquery/issues/3000 for more info.
  649. deferred.then().then( function() {
  650. assert.deepEqual(
  651. [].slice.call( arguments ),
  652. [ "baz", "quux" ],
  653. "The fulfilled handler receives proper params"
  654. );
  655. done();
  656. }, null, function() {
  657. assert.deepEqual(
  658. [].slice.call( arguments ),
  659. [ "foo", "bar" ],
  660. "The progress handler receives proper params"
  661. );
  662. done();
  663. } );
  664. } );
  665. QUnit.test( "jQuery.when(nonThenable) - like Promise.resolve", function( assert ) {
  666. "use strict";
  667. assert.expect( 44 );
  668. var
  669. // Support: Android 4.0 only
  670. // Strict mode functions invoked without .call/.apply get global-object context
  671. defaultContext = ( function getDefaultContext() { return this; } ).call(),
  672. done = assert.async( 20 );
  673. jQuery.when()
  674. .done( function( resolveValue ) {
  675. assert.strictEqual( arguments.length, 0, "Resolved .done with no arguments" );
  676. assert.strictEqual( this, defaultContext, "Default .done context with no arguments" );
  677. } )
  678. .then( function( resolveValue ) {
  679. assert.strictEqual( arguments.length, 0, "Resolved .then with no arguments" );
  680. assert.strictEqual( this, defaultContext, "Default .then context with no arguments" );
  681. } );
  682. jQuery.each( {
  683. "an empty string": "",
  684. "a non-empty string": "some string",
  685. "zero": 0,
  686. "a number other than zero": 1,
  687. "true": true,
  688. "false": false,
  689. "null": null,
  690. "undefined": undefined,
  691. "a plain object": {},
  692. "an array": [ 1, 2, 3 ]
  693. }, function( message, value ) {
  694. var code = "jQuery.when( " + message + " )",
  695. onFulfilled = function( method ) {
  696. var call = code + "." + method;
  697. return function( resolveValue ) {
  698. assert.strictEqual( resolveValue, value, call + " resolve" );
  699. assert.strictEqual( this, defaultContext, call + " context" );
  700. done();
  701. };
  702. },
  703. onRejected = function( method ) {
  704. var call = code + "." + method;
  705. return function() {
  706. assert.ok( false, call + " reject" );
  707. done();
  708. };
  709. };
  710. jQuery.when( value )
  711. .done( onFulfilled( "done" ) )
  712. .fail( onRejected( "done" ) )
  713. .then( onFulfilled( "then" ), onRejected( "then" ) );
  714. } );
  715. } );
  716. QUnit.test( "jQuery.when(thenable) - like Promise.resolve", function( assert ) {
  717. "use strict";
  718. var customToStringThen = {
  719. then: function( onFulfilled ) {
  720. // Support: Android 4.0 only
  721. // Strict mode functions invoked without .call/.apply get global-object context
  722. onFulfilled.call();
  723. }
  724. };
  725. if ( typeof Symbol === "function" ) {
  726. customToStringThen.then[ Symbol.toStringTag ] = "String";
  727. }
  728. var slice = [].slice,
  729. sentinel = { context: "explicit" },
  730. eventuallyFulfilled = jQuery.Deferred().notify( true ),
  731. eventuallyRejected = jQuery.Deferred().notify( true ),
  732. secondaryFulfilled = jQuery.Deferred().resolve( eventuallyFulfilled ),
  733. secondaryRejected = jQuery.Deferred().resolve( eventuallyRejected ),
  734. inputs = {
  735. promise: Promise.resolve( true ),
  736. customToStringThen: customToStringThen,
  737. rejectedPromise: Promise.reject( false ),
  738. deferred: jQuery.Deferred().resolve( true ),
  739. eventuallyFulfilled: eventuallyFulfilled,
  740. secondaryFulfilled: secondaryFulfilled,
  741. eventuallySecondaryFulfilled: jQuery.Deferred().notify( true ),
  742. multiDeferred: jQuery.Deferred().resolve( "foo", "bar" ),
  743. deferredWith: jQuery.Deferred().resolveWith( sentinel, [ true ] ),
  744. multiDeferredWith: jQuery.Deferred().resolveWith( sentinel, [ "foo", "bar" ] ),
  745. rejectedDeferred: jQuery.Deferred().reject( false ),
  746. eventuallyRejected: eventuallyRejected,
  747. secondaryRejected: secondaryRejected,
  748. eventuallySecondaryRejected: jQuery.Deferred().notify( true ),
  749. multiRejectedDeferred: jQuery.Deferred().reject( "baz", "quux" ),
  750. rejectedDeferredWith: jQuery.Deferred().rejectWith( sentinel, [ false ] ),
  751. multiRejectedDeferredWith: jQuery.Deferred().rejectWith( sentinel, [ "baz", "quux" ] )
  752. },
  753. contexts = {
  754. deferredWith: sentinel,
  755. multiDeferredWith: sentinel,
  756. rejectedDeferredWith: sentinel,
  757. multiRejectedDeferredWith: sentinel
  758. },
  759. willSucceed = {
  760. promise: [ true ],
  761. customToStringThen: [],
  762. deferred: [ true ],
  763. eventuallyFulfilled: [ true ],
  764. secondaryFulfilled: [ true ],
  765. eventuallySecondaryFulfilled: [ true ],
  766. multiDeferred: [ "foo", "bar" ],
  767. deferredWith: [ true ],
  768. multiDeferredWith: [ "foo", "bar" ]
  769. },
  770. willError = {
  771. rejectedPromise: [ false ],
  772. rejectedDeferred: [ false ],
  773. eventuallyRejected: [ false ],
  774. secondaryRejected: [ false ],
  775. eventuallySecondaryRejected: [ false ],
  776. multiRejectedDeferred: [ "baz", "quux" ],
  777. rejectedDeferredWith: [ false ],
  778. multiRejectedDeferredWith: [ "baz", "quux" ]
  779. },
  780. numCases = Object.keys( willSucceed ).length + Object.keys( willError ).length,
  781. // Support: Android 4.0 only
  782. // Strict mode functions invoked without .call/.apply get global-object context
  783. defaultContext = ( function getDefaultContext() { return this; } ).call(),
  784. done = assert.async( numCases * 2 );
  785. assert.expect( numCases * 4 );
  786. jQuery.each( inputs, function( message, value ) {
  787. var code = "jQuery.when( " + message + " )",
  788. shouldResolve = willSucceed[ message ],
  789. shouldError = willError[ message ],
  790. context = contexts[ message ] || defaultContext,
  791. onFulfilled = function( method ) {
  792. var call = code + "." + method;
  793. return function() {
  794. if ( shouldResolve ) {
  795. assert.deepEqual( slice.call( arguments ), shouldResolve,
  796. call + " resolve" );
  797. assert.strictEqual( this, context, call + " context" );
  798. } else {
  799. assert.ok( false, call + " resolve" );
  800. }
  801. done();
  802. };
  803. },
  804. onRejected = function( method ) {
  805. var call = code + "." + method;
  806. return function() {
  807. if ( shouldError ) {
  808. assert.deepEqual( slice.call( arguments ), shouldError, call + " reject" );
  809. assert.strictEqual( this, context, call + " context" );
  810. } else {
  811. assert.ok( false, call + " reject" );
  812. }
  813. done();
  814. };
  815. };
  816. jQuery.when( value )
  817. .done( onFulfilled( "done" ) )
  818. .fail( onRejected( "done" ) )
  819. .then( onFulfilled( "then" ), onRejected( "then" ) );
  820. } );
  821. setTimeout( function() {
  822. eventuallyFulfilled.resolve( true );
  823. eventuallyRejected.reject( false );
  824. inputs.eventuallySecondaryFulfilled.resolve( secondaryFulfilled );
  825. inputs.eventuallySecondaryRejected.resolve( secondaryRejected );
  826. }, 50 );
  827. } );
  828. QUnit.test( "jQuery.when(a, b) - like Promise.all", function( assert ) {
  829. "use strict";
  830. assert.expect( 196 );
  831. var slice = [].slice,
  832. deferreds = {
  833. rawValue: 1,
  834. fulfilled: jQuery.Deferred().resolve( 1 ),
  835. rejected: jQuery.Deferred().reject( 0 ),
  836. eventuallyFulfilled: jQuery.Deferred().notify( true ),
  837. eventuallyRejected: jQuery.Deferred().notify( true ),
  838. fulfilledStandardPromise: Promise.resolve( 1 ),
  839. rejectedStandardPromise: Promise.reject( 0 )
  840. },
  841. willSucceed = {
  842. rawValue: true,
  843. fulfilled: true,
  844. eventuallyFulfilled: true,
  845. fulfilledStandardPromise: true
  846. },
  847. willError = {
  848. rejected: true,
  849. eventuallyRejected: true,
  850. rejectedStandardPromise: true
  851. },
  852. // Support: Android 4.0 only
  853. // Strict mode functions invoked without .call/.apply get global-object context
  854. defaultContext = ( function getDefaultContext() { return this; } ).call(),
  855. done = assert.async( 98 );
  856. jQuery.each( deferreds, function( id1, v1 ) {
  857. jQuery.each( deferreds, function( id2, v2 ) {
  858. var code = "jQuery.when( " + id1 + ", " + id2 + " )",
  859. shouldResolve = willSucceed[ id1 ] && willSucceed[ id2 ],
  860. shouldError = willError[ id1 ] || willError[ id2 ],
  861. expected = shouldResolve ? [ 1, 1 ] : [ 0 ],
  862. context = shouldResolve ? [ defaultContext, defaultContext ] : defaultContext,
  863. onFulfilled = function( method ) {
  864. var call = code + "." + method;
  865. return function() {
  866. if ( shouldResolve ) {
  867. assert.deepEqual( slice.call( arguments ), expected,
  868. call + " resolve" );
  869. assert.deepEqual( this, context, code + " context" );
  870. } else {
  871. assert.ok( false, call + " resolve" );
  872. }
  873. done();
  874. };
  875. },
  876. onRejected = function( method ) {
  877. var call = code + "." + method;
  878. return function() {
  879. if ( shouldError ) {
  880. assert.deepEqual( slice.call( arguments ), expected, call + " reject" );
  881. assert.deepEqual( this, context, code + " context" );
  882. } else {
  883. assert.ok( false, call + " reject" );
  884. }
  885. done();
  886. };
  887. };
  888. jQuery.when( v1, v2 )
  889. .done( onFulfilled( "done" ) )
  890. .fail( onRejected( "done" ) )
  891. .then( onFulfilled( "then" ), onRejected( "then" ) );
  892. } );
  893. } );
  894. setTimeout( function() {
  895. deferreds.eventuallyFulfilled.resolve( 1 );
  896. deferreds.eventuallyRejected.reject( 0 );
  897. }, 50 );
  898. } );
  899. QUnit.test( "jQuery.when - always returns a new promise", function( assert ) {
  900. assert.expect( 42 );
  901. jQuery.each( {
  902. "no arguments": [],
  903. "non-thenable": [ "foo" ],
  904. "promise": [ Promise.resolve( "bar" ) ],
  905. "rejected promise": [ Promise.reject( "bar" ) ],
  906. "deferred": [ jQuery.Deferred().resolve( "baz" ) ],
  907. "rejected deferred": [ jQuery.Deferred().reject( "baz" ) ],
  908. "multi-resolved deferred": [ jQuery.Deferred().resolve( "qux", "quux" ) ],
  909. "multiple non-thenables": [ "corge", "grault" ],
  910. "multiple deferreds": [
  911. jQuery.Deferred().resolve( "garply" ),
  912. jQuery.Deferred().resolve( "waldo" )
  913. ]
  914. }, function( label, args ) {
  915. var result = jQuery.when.apply( jQuery, args );
  916. assert.ok( typeof result.then === "function", "Thenable returned from " + label );
  917. assert.strictEqual( result.resolve, undefined, "Non-deferred returned from " + label );
  918. assert.strictEqual( result.promise(), result, "Promise returned from " + label );
  919. jQuery.each( args, function( i, arg ) {
  920. assert.notStrictEqual( result, arg, "Returns distinct from arg " + i + " of " + label );
  921. if ( arg.promise ) {
  922. assert.notStrictEqual( result, arg.promise(),
  923. "Returns distinct from promise of arg " + i + " of " + label );
  924. }
  925. } );
  926. } );
  927. } );
  928. QUnit.test( "jQuery.when - notify does not affect resolved", function( assert ) {
  929. assert.expect( 3 );
  930. var a = jQuery.Deferred().notify( 1 ).resolve( 4 ),
  931. b = jQuery.Deferred().notify( 2 ).resolve( 5 ),
  932. c = jQuery.Deferred().notify( 3 ).resolve( 6 );
  933. jQuery.when( a, b, c ).done( function( a, b, c ) {
  934. assert.strictEqual( a, 4, "first resolve value ok" );
  935. assert.strictEqual( b, 5, "second resolve value ok" );
  936. assert.strictEqual( c, 6, "third resolve value ok" );
  937. } ).fail( function() {
  938. assert.ok( false, "Error on resolve" );
  939. } );
  940. } );
  941. QUnit.test( "jQuery.when(...) - opportunistically synchronous", function( assert ) {
  942. assert.expect( 5 );
  943. var when = "before",
  944. resolved = jQuery.Deferred().resolve( true ),
  945. rejected = jQuery.Deferred().reject( false ),
  946. validate = function( label ) {
  947. return function() {
  948. assert.equal( when, "before", label );
  949. };
  950. },
  951. done = assert.async( 5 );
  952. jQuery.when().done( validate( "jQuery.when()" ) ).always( done );
  953. jQuery.when( when ).done( validate( "jQuery.when(nonThenable)" ) ).always( done );
  954. jQuery.when( resolved ).done( validate( "jQuery.when(alreadyFulfilled)" ) ).always( done );
  955. jQuery.when( rejected ).fail( validate( "jQuery.when(alreadyRejected)" ) ).always( done );
  956. jQuery.when( resolved, rejected )
  957. .always( validate( "jQuery.when(alreadyFulfilled, alreadyRejected)" ) )
  958. .always( done );
  959. when = "after";
  960. } );
  961. } )();