@@ -341,7 +341,142 @@ describe('Firebase Storage > Upload Task', () => {
341
341
} ) ;
342
342
343
343
task . pause ( ) ;
344
+ return promise ;
345
+ }
346
+
347
+ // This is to test to make sure that when you pause in the middle of a request that you do not get an error
348
+ async function runProgressPauseTest ( blob : FbsBlob ) : Promise < void > {
349
+ let callbackCount = 0 ;
350
+ // Usually the first request is to create the resumable upload and the second is to upload.
351
+ // Upload requests are not retryable, and this callback is to make sure we pause before the response comes back.
352
+ function shouldRespondCallback ( ) : boolean {
353
+ if ( callbackCount ++ === 1 ) {
354
+ task . pause ( ) ;
355
+ return false ;
356
+ }
357
+ return true ;
358
+ }
359
+ const storageService = storageServiceWithHandler (
360
+ fakeServerHandler ( ) ,
361
+ shouldRespondCallback
362
+ ) ;
363
+ const task = new UploadTask (
364
+ new Reference ( storageService , testLocation ) ,
365
+ blob
366
+ ) ;
367
+
368
+ // eslint-disable-next-line @typescript-eslint/ban-types
369
+ let resolve : Function , reject : Function ;
370
+ const promise = new Promise < void > ( ( innerResolve , innerReject ) => {
371
+ resolve = innerResolve ;
372
+ reject = innerReject ;
373
+ } ) ;
374
+
375
+ // Assert functions throw Errors, which means if we're not in the right stack
376
+ // the error might not get reported properly. This function wraps existing
377
+ // assert functions, returning a new function that calls reject with any
378
+ // caught errors. This should make sure errors are reported properly.
379
+ function promiseAssertWrapper < T > ( func : T ) : T {
380
+ function wrapped ( ..._args : any [ ] ) : void {
381
+ try {
382
+ ( func as any as ( ...args : any [ ] ) => void ) . apply ( null , _args ) ;
383
+ } catch ( e ) {
384
+ reject ( e ) ;
385
+ pausedStateCompleted . reject ( e ) ;
386
+ // also throw to further unwind the stack
387
+ throw e ;
388
+ }
389
+ }
390
+ return wrapped as any as T ;
391
+ }
392
+
393
+ const fixedAssertEquals = promiseAssertWrapper ( assert . equal ) ;
394
+ const fixedAssertFalse = promiseAssertWrapper ( assert . isFalse ) ;
395
+ const fixedAssertTrue = promiseAssertWrapper ( assert . isTrue ) ;
396
+ const fixedAssertFail = promiseAssertWrapper ( assert . fail ) ;
397
+
398
+ const events : string [ ] = [ ] ;
399
+ const progress : number [ ] [ ] = [ ] ;
400
+ // Promise for when we are finally in the pause state
401
+ const pausedStateCompleted = new Deferred ( ) ;
402
+ let complete = 0 ;
403
+ // Adds a callback for when the state has changed. The callback resolves the pausedStateCompleted promise
404
+ // to let our test know when to resume.
405
+ function addCallbacks ( task : UploadTask ) : void {
406
+ let lastState : string ;
407
+ task . on (
408
+ TaskEvent . STATE_CHANGED ,
409
+ snapshot => {
410
+ fixedAssertEquals ( complete , 0 ) ;
411
+
412
+ const state = snapshot . state ;
413
+ if ( lastState !== TaskState . RUNNING && state === TaskState . RUNNING ) {
414
+ events . push ( 'resume' ) ;
415
+ } else if (
416
+ lastState !== TaskState . PAUSED &&
417
+ state === TaskState . PAUSED
418
+ ) {
419
+ pausedStateCompleted . resolve ( ) ;
420
+ events . push ( 'pause' ) ;
421
+ }
422
+
423
+ const p = [ snapshot . bytesTransferred , snapshot . totalBytes ] ;
424
+
425
+ progress . push ( p ) ;
426
+
427
+ lastState = state ;
428
+ } ,
429
+ ( ) => {
430
+ fixedAssertFail ( 'Failed to upload' ) ;
431
+ } ,
432
+ ( ) => {
433
+ events . push ( 'complete' ) ;
434
+ complete ++ ;
435
+ }
436
+ ) ;
437
+ }
438
+
439
+ addCallbacks ( task ) ;
344
440
441
+ let completeTriggered = false ;
442
+
443
+ // We should clean this up and just add all callbacks in one function call.
444
+ // Keeps track of all events that were logged before and asserts on them.
445
+ task . on ( TaskEvent . STATE_CHANGED , undefined , undefined , ( ) => {
446
+ fixedAssertFalse ( completeTriggered ) ;
447
+ completeTriggered = true ;
448
+
449
+ fixedAssertEquals ( events . length , 4 ) ;
450
+ fixedAssertEquals ( events [ 0 ] , 'resume' ) ;
451
+ fixedAssertEquals ( events [ 1 ] , 'pause' ) ;
452
+ fixedAssertEquals ( events [ 2 ] , 'resume' ) ;
453
+ fixedAssertEquals ( events [ 3 ] , 'complete' ) ;
454
+
455
+ fixedAssertEquals ( complete , 1 ) ;
456
+
457
+ let increasing = true ;
458
+ let allTotalsTheSame = true ;
459
+ for ( let i = 0 ; i < progress . length - 1 ; i ++ ) {
460
+ increasing = increasing && progress [ i ] [ 0 ] <= progress [ i + 1 ] [ 0 ] ;
461
+ allTotalsTheSame =
462
+ allTotalsTheSame && progress [ i ] [ 1 ] === progress [ i + 1 ] [ 1 ] ;
463
+ }
464
+
465
+ let lastIsAll = false ;
466
+ if ( progress . length > 0 ) {
467
+ const last = progress [ progress . length - 1 ] ;
468
+ lastIsAll = last [ 0 ] === last [ 1 ] ;
469
+ } else {
470
+ lastIsAll = true ;
471
+ }
472
+
473
+ fixedAssertTrue ( increasing ) ;
474
+ fixedAssertTrue ( allTotalsTheSame ) ;
475
+ fixedAssertTrue ( lastIsAll ) ;
476
+ resolve ( null ) ;
477
+ } ) ;
478
+ await pausedStateCompleted . promise ;
479
+ task . resume ( ) ;
345
480
return promise ;
346
481
}
347
482
enum StateType {
@@ -593,6 +728,10 @@ describe('Firebase Storage > Upload Task', () => {
593
728
expect ( clock . countTimers ( ) ) . to . eq ( 0 ) ;
594
729
clock . restore ( ) ;
595
730
} ) ;
731
+ it ( 'does not error when pausing inflight request' , async ( ) => {
732
+ // Kick off upload
733
+ await runProgressPauseTest ( bigBlob ) ;
734
+ } ) ;
596
735
it ( 'tests if small requests that respond with 500 retry correctly' , async ( ) => {
597
736
clock = useFakeTimers ( ) ;
598
737
// Kick off upload
0 commit comments