@@ -105,6 +105,31 @@ describe('GoTrueClient in browser environment', () => {
105105 expect ( signinError ) . toBeNull ( )
106106 expect ( signinData ?. session ) . toBeDefined ( )
107107 } )
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+ } )
108133} )
109134
110135describe ( 'Browser-specific helper functions' , ( ) => {
@@ -234,6 +259,65 @@ describe('Callback URL handling', () => {
234259 } = await client . getSession ( )
235260 expect ( session ) . toBeNull ( )
236261 } )
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+ } )
237321} )
238322
239323describe ( 'GoTrueClient BroadcastChannel' , ( ) => {
@@ -334,6 +418,30 @@ describe('Browser locks functionality', () => {
334418
335419 expect ( client ) . toBeDefined ( )
336420 } )
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+ } )
337445} )
338446
339447describe ( 'Web3 functionality in browser' , ( ) => {
@@ -376,3 +484,236 @@ describe('GoTrueClient constructor edge cases', () => {
376484 expect ( ( client as any ) . userStorage ) . toBe ( customUserStorage )
377485 } )
378486} )
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