Skip to content

Commit bc2c985

Browse files
authored
chore: more tests (#1101)
## What kind of change does this PR introduce? PR is based on #1095. Please, merge the previous one first. Increased coverage
1 parent 570789e commit bc2c985

File tree

3 files changed

+968
-0
lines changed

3 files changed

+968
-0
lines changed

test/GoTrueClient.browser.test.ts

Lines changed: 341 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

110135
describe('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

239323
describe('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

339447
describe('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

Comments
 (0)