@@ -105,6 +105,31 @@ describe('GoTrueClient in browser environment', () => {
105
105
expect ( signinError ) . toBeNull ( )
106
106
expect ( signinData ?. session ) . toBeDefined ( )
107
107
} )
108
+
109
+ it ( 'should handle _handleVisibilityChange error handling' , async ( ) => {
110
+ const client = getClientWithSpecificStorage ( {
111
+ getItem : jest . fn ( ) ,
112
+ setItem : jest . fn ( ) ,
113
+ removeItem : jest . fn ( ) ,
114
+ } )
115
+
116
+ // Mock window.addEventListener to throw error
117
+ const originalAddEventListener = window . addEventListener
118
+ window . addEventListener = jest . fn ( ) . mockImplementation ( ( ) => {
119
+ throw new Error ( 'addEventListener failed' )
120
+ } )
121
+
122
+ try {
123
+ // Initialize client to trigger _handleVisibilityChange
124
+ await client . initialize ( )
125
+
126
+ // Should not throw error, should handle it gracefully
127
+ expect ( client ) . toBeDefined ( )
128
+ } finally {
129
+ // Restore original addEventListener
130
+ window . addEventListener = originalAddEventListener
131
+ }
132
+ } )
108
133
} )
109
134
110
135
describe ( 'Browser-specific helper functions' , ( ) => {
@@ -234,6 +259,65 @@ describe('Callback URL handling', () => {
234
259
} = await client . getSession ( )
235
260
expect ( session ) . toBeNull ( )
236
261
} )
262
+
263
+ it ( 'should handle _initialize with detectSessionInUrl' , async ( ) => {
264
+ // Mock window.location with session parameters
265
+ Object . defineProperty ( window , 'location' , {
266
+ value : {
267
+ href : 'http://localhost:9999/callback?access_token=test&refresh_token=test&expires_in=3600&token_type=bearer&type=recovery' ,
268
+ assign : jest . fn ( ) ,
269
+ replace : jest . fn ( ) ,
270
+ reload : jest . fn ( ) ,
271
+ toString : ( ) => 'http://localhost:9999/callback' ,
272
+ } ,
273
+ writable : true ,
274
+ } )
275
+
276
+ const client = new ( require ( '../src/GoTrueClient' ) . default ) ( {
277
+ url : 'http://localhost:9999' ,
278
+ detectSessionInUrl : true ,
279
+ autoRefreshToken : false ,
280
+ } )
281
+
282
+ // Initialize client to trigger _initialize with detectSessionInUrl
283
+ await client . initialize ( )
284
+
285
+ expect ( client ) . toBeDefined ( )
286
+ } )
287
+
288
+ it ( 'should handle _initialize with PKCE flow mismatch' , async ( ) => {
289
+ // Mock window.location with PKCE parameters
290
+ Object . defineProperty ( window , 'location' , {
291
+ value : {
292
+ href : 'http://localhost:9999/callback?code=test-code' ,
293
+ assign : jest . fn ( ) ,
294
+ replace : jest . fn ( ) ,
295
+ reload : jest . fn ( ) ,
296
+ toString : ( ) => 'http://localhost:9999/callback' ,
297
+ } ,
298
+ writable : true ,
299
+ } )
300
+
301
+ // Mock storage to return code verifier
302
+ const mockStorage = {
303
+ getItem : jest . fn ( ) . mockResolvedValue ( 'test-code-verifier' ) ,
304
+ setItem : jest . fn ( ) ,
305
+ removeItem : jest . fn ( ) ,
306
+ }
307
+
308
+ const client = new ( require ( '../src/GoTrueClient' ) . default ) ( {
309
+ url : 'http://localhost:9999' ,
310
+ detectSessionInUrl : true ,
311
+ autoRefreshToken : false ,
312
+ storage : mockStorage ,
313
+ flowType : 'implicit' , // Mismatch with PKCE flow
314
+ } )
315
+
316
+ // Initialize client to trigger flow mismatch
317
+ await client . initialize ( )
318
+
319
+ expect ( client ) . toBeDefined ( )
320
+ } )
237
321
} )
238
322
239
323
describe ( 'GoTrueClient BroadcastChannel' , ( ) => {
@@ -334,6 +418,30 @@ describe('Browser locks functionality', () => {
334
418
335
419
expect ( client ) . toBeDefined ( )
336
420
} )
421
+
422
+ it ( 'should handle _acquireLock with empty pendingInLock' , async ( ) => {
423
+ const client = getClientWithSpecificStorage ( {
424
+ getItem : jest . fn ( ) ,
425
+ setItem : jest . fn ( ) ,
426
+ removeItem : jest . fn ( ) ,
427
+ } )
428
+
429
+ // Mock navigator.locks
430
+ const mockLock = { name : 'test-lock' }
431
+ const mockRequest = jest . fn ( ) . mockImplementation ( ( _ , __ , callback ) =>
432
+ Promise . resolve ( callback ( mockLock ) )
433
+ )
434
+
435
+ Object . defineProperty ( navigator , 'locks' , {
436
+ value : { request : mockRequest } ,
437
+ writable : true ,
438
+ } )
439
+
440
+ // Initialize client to trigger lock acquisition
441
+ await client . initialize ( )
442
+
443
+ expect ( client ) . toBeDefined ( )
444
+ } )
337
445
} )
338
446
339
447
describe ( 'Web3 functionality in browser' , ( ) => {
@@ -376,3 +484,236 @@ describe('GoTrueClient constructor edge cases', () => {
376
484
expect ( ( client as any ) . userStorage ) . toBe ( customUserStorage )
377
485
} )
378
486
} )
487
+
488
+ describe ( 'linkIdentity with skipBrowserRedirect false' , ( ) => {
489
+
490
+ it ( 'should linkIdentity with skipBrowserRedirect false' , async ( ) => {
491
+ Object . defineProperty ( window , 'location' , {
492
+ value : {
493
+ href : 'http://localhost:9999' ,
494
+ assign : jest . fn ( ) ,
495
+ replace : jest . fn ( ) ,
496
+ reload : jest . fn ( ) ,
497
+ toString : ( ) => 'http://localhost:9999' ,
498
+ } ,
499
+ writable : true ,
500
+ } )
501
+ // Mock successful session
502
+ const mockSession = {
503
+ access_token : 'test-access-token' ,
504
+ refresh_token : 'test-refresh-token' ,
505
+ expires_in : 3600 ,
506
+ token_type : 'bearer' ,
507
+ user : { id : 'test-user' } ,
508
+ }
509
+
510
+ // Mock storage to return session
511
+ const mockStorage = {
512
+ getItem : jest . fn ( ) . mockResolvedValue ( JSON . stringify ( mockSession ) ) ,
513
+ setItem : jest . fn ( ) ,
514
+ removeItem : jest . fn ( ) ,
515
+ }
516
+
517
+ // Create client with custom fetch
518
+ const mockFetch = jest . fn ( ) . mockResolvedValue ( {
519
+ ok : true ,
520
+ status : 200 ,
521
+ json : ( ) => Promise . resolve ( { url : 'http://localhost:9999/oauth/callback' } ) ,
522
+ text : ( ) => Promise . resolve ( '{"url": "http://localhost:9999/oauth/callback"}' ) ,
523
+ headers : new Headers ( ) ,
524
+ } as Response )
525
+
526
+ const clientWithSession = new ( require ( '../src/GoTrueClient' ) . default ) ( {
527
+ url : 'http://localhost:9999' ,
528
+ storageKey : 'test-specific-storage' ,
529
+ autoRefreshToken : false ,
530
+ persistSession : true ,
531
+ storage : mockStorage ,
532
+ fetch : mockFetch ,
533
+ } )
534
+
535
+ // Mock window.location.assign
536
+ const mockAssign = jest . fn ( )
537
+ Object . defineProperty ( window , 'location' , {
538
+ value : {
539
+ href : 'http://localhost:9999' ,
540
+ assign : mockAssign ,
541
+ replace : jest . fn ( ) ,
542
+ reload : jest . fn ( ) ,
543
+ toString : ( ) => 'http://localhost:9999' ,
544
+ } ,
545
+ writable : true ,
546
+ } )
547
+
548
+ try {
549
+ const result = await clientWithSession . linkIdentity ( {
550
+ provider : 'github' ,
551
+ options : {
552
+ skipBrowserRedirect : false ,
553
+ } ,
554
+ } )
555
+
556
+ expect ( result . data ?. url ) . toBeDefined ( )
557
+ expect ( mockFetch ) . toHaveBeenCalled ( )
558
+ // Note: linkIdentity might not always call window.location.assign depending on the response
559
+ // So we just verify the result is defined
560
+ } catch ( error ) {
561
+ console . error ( 'Test error:' , error )
562
+ throw error
563
+ }
564
+ } )
565
+ } )
566
+
567
+ describe ( 'Session Management Tests' , ( ) => {
568
+ it ( 'should handle _recoverAndRefresh with Infinity expires_at' , async ( ) => {
569
+ // Mock session with null expires_at
570
+ const mockSession = {
571
+ access_token : 'test-access-token' ,
572
+ refresh_token : 'test-refresh-token' ,
573
+ expires_in : 3600 ,
574
+ expires_at : null ,
575
+ token_type : 'bearer' ,
576
+ user : { id : 'test-user' } ,
577
+ }
578
+
579
+ const mockStorage = {
580
+ getItem : jest . fn ( ) . mockResolvedValue ( JSON . stringify ( mockSession ) ) ,
581
+ setItem : jest . fn ( ) ,
582
+ removeItem : jest . fn ( ) ,
583
+ }
584
+
585
+ const client = getClientWithSpecificStorage ( mockStorage )
586
+
587
+ // Initialize client to trigger _recoverAndRefresh with Infinity expires_at
588
+ await client . initialize ( )
589
+
590
+ expect ( client ) . toBeDefined ( )
591
+ } )
592
+
593
+ it ( 'should handle _recoverAndRefresh with refresh token error' , async ( ) => {
594
+ // Mock session
595
+ const mockSession = {
596
+ access_token : 'test-access-token' ,
597
+ refresh_token : 'test-refresh-token' ,
598
+ expires_in : 3600 ,
599
+ expires_at : Math . floor ( Date . now ( ) / 1000 ) - 100 , // Expired
600
+ token_type : 'bearer' ,
601
+ user : { id : 'test-user' } ,
602
+ }
603
+
604
+ const mockStorage = {
605
+ getItem : jest . fn ( ) . mockResolvedValue ( JSON . stringify ( mockSession ) ) ,
606
+ setItem : jest . fn ( ) ,
607
+ removeItem : jest . fn ( ) ,
608
+ }
609
+
610
+ const mockFetch = jest . fn ( ) . mockResolvedValue ( {
611
+ ok : false ,
612
+ status : 401 ,
613
+ json : ( ) => Promise . resolve ( { error : 'invalid_grant' } ) ,
614
+ text : ( ) => Promise . resolve ( '{"error": "invalid_grant"}' ) ,
615
+ headers : new Headers ( ) ,
616
+ } as Response )
617
+
618
+ const client = new ( require ( '../src/GoTrueClient' ) . default ) ( {
619
+ url : 'http://localhost:9999' ,
620
+ autoRefreshToken : true ,
621
+ persistSession : true ,
622
+ storage : mockStorage ,
623
+ fetch : mockFetch ,
624
+ } )
625
+
626
+ // Initialize client to trigger refresh token error
627
+ await client . initialize ( )
628
+
629
+ expect ( client ) . toBeDefined ( )
630
+ } )
631
+
632
+ it ( 'should handle _recoverAndRefresh with user proxy' , async ( ) => {
633
+ // Mock session with proxy user
634
+ const mockSession = {
635
+ access_token : 'test-access-token' ,
636
+ refresh_token : 'test-refresh-token' ,
637
+ expires_in : 3600 ,
638
+ expires_at : Math . floor ( Date . now ( ) / 1000 ) + 3600 , // Valid
639
+ token_type : 'bearer' ,
640
+ user : { __isUserNotAvailableProxy : true } ,
641
+ }
642
+
643
+ const mockStorage = {
644
+ getItem : jest . fn ( ) . mockResolvedValue ( JSON . stringify ( mockSession ) ) ,
645
+ setItem : jest . fn ( ) ,
646
+ removeItem : jest . fn ( ) ,
647
+ }
648
+
649
+ // Mock fetch to return user data
650
+ const mockFetch = jest . fn ( ) . mockResolvedValue ( {
651
+ ok : true ,
652
+ status : 200 ,
653
+ json :
( ) => Promise . resolve ( { user :
{ id :
'test-user' , email :
'[email protected] ' } } ) ,
654
+ text :
( ) => Promise . resolve ( '{"user": {"id": "test-user", "email": "[email protected] "}}' ) ,
655
+ headers : new Headers ( ) ,
656
+ } as Response )
657
+
658
+ const client = new ( require ( '../src/GoTrueClient' ) . default ) ( {
659
+ url : 'http://localhost:9999' ,
660
+ autoRefreshToken : true ,
661
+ persistSession : true ,
662
+ storage : mockStorage ,
663
+ fetch : mockFetch ,
664
+ } )
665
+
666
+ // Initialize client to trigger user proxy handling
667
+ await client . initialize ( )
668
+
669
+ expect ( client ) . toBeDefined ( )
670
+ } )
671
+ } )
672
+
673
+ describe ( 'Additional Tests' , ( ) => {
674
+ it ( 'should handle _initialize with storage returning boolean' , async ( ) => {
675
+ // Mock storage to return boolean
676
+ const mockStorage = {
677
+ getItem : jest . fn ( ) . mockResolvedValue ( true ) ,
678
+ setItem : jest . fn ( ) ,
679
+ removeItem : jest . fn ( ) ,
680
+ }
681
+
682
+ const client = new ( require ( '../src/GoTrueClient' ) . default ) ( {
683
+ url : 'http://localhost:9999' ,
684
+ autoRefreshToken : false ,
685
+ persistSession : true ,
686
+ storage : mockStorage ,
687
+ } )
688
+
689
+ // Initialize client to trigger boolean handling
690
+ await client . initialize ( )
691
+
692
+ expect ( client ) . toBeDefined ( )
693
+ } )
694
+
695
+ it ( 'should handle _initialize with expires_at parameter' , async ( ) => {
696
+ // Mock window.location with expires_at parameter
697
+ Object . defineProperty ( window , 'location' , {
698
+ value : {
699
+ href : 'http://localhost:9999/callback?access_token=test&refresh_token=test&expires_in=3600&expires_at=1234567890&token_type=bearer' ,
700
+ assign : jest . fn ( ) ,
701
+ replace : jest . fn ( ) ,
702
+ reload : jest . fn ( ) ,
703
+ toString : ( ) => 'http://localhost:9999/callback' ,
704
+ } ,
705
+ writable : true ,
706
+ } )
707
+
708
+ const client = new ( require ( '../src/GoTrueClient' ) . default ) ( {
709
+ url : 'http://localhost:9999' ,
710
+ detectSessionInUrl : true ,
711
+ autoRefreshToken : false ,
712
+ } )
713
+
714
+ // Initialize client to trigger _initialize with expires_at
715
+ await client . initialize ( )
716
+
717
+ expect ( client ) . toBeDefined ( )
718
+ } )
719
+ } )
0 commit comments