Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
341 changes: 341 additions & 0 deletions test/GoTrueClient.browser.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,31 @@ describe('GoTrueClient in browser environment', () => {
expect(signinError).toBeNull()
expect(signinData?.session).toBeDefined()
})

it('should handle _handleVisibilityChange error handling', async () => {
const client = getClientWithSpecificStorage({
getItem: jest.fn(),
setItem: jest.fn(),
removeItem: jest.fn(),
})

// Mock window.addEventListener to throw error
const originalAddEventListener = window.addEventListener
window.addEventListener = jest.fn().mockImplementation(() => {
throw new Error('addEventListener failed')
})

try {
// Initialize client to trigger _handleVisibilityChange
await client.initialize()

// Should not throw error, should handle it gracefully
expect(client).toBeDefined()
} finally {
// Restore original addEventListener
window.addEventListener = originalAddEventListener
}
})
})

describe('Browser-specific helper functions', () => {
Expand Down Expand Up @@ -234,6 +259,65 @@ describe('Callback URL handling', () => {
} = await client.getSession()
expect(session).toBeNull()
})

it('should handle _initialize with detectSessionInUrl', async () => {
// Mock window.location with session parameters
Object.defineProperty(window, 'location', {
value: {
href: 'http://localhost:9999/callback?access_token=test&refresh_token=test&expires_in=3600&token_type=bearer&type=recovery',
assign: jest.fn(),
replace: jest.fn(),
reload: jest.fn(),
toString: () => 'http://localhost:9999/callback',
},
writable: true,
})

const client = new (require('../src/GoTrueClient').default)({
url: 'http://localhost:9999',
detectSessionInUrl: true,
autoRefreshToken: false,
})

// Initialize client to trigger _initialize with detectSessionInUrl
await client.initialize()

expect(client).toBeDefined()
})

it('should handle _initialize with PKCE flow mismatch', async () => {
// Mock window.location with PKCE parameters
Object.defineProperty(window, 'location', {
value: {
href: 'http://localhost:9999/callback?code=test-code',
assign: jest.fn(),
replace: jest.fn(),
reload: jest.fn(),
toString: () => 'http://localhost:9999/callback',
},
writable: true,
})

// Mock storage to return code verifier
const mockStorage = {
getItem: jest.fn().mockResolvedValue('test-code-verifier'),
setItem: jest.fn(),
removeItem: jest.fn(),
}

const client = new (require('../src/GoTrueClient').default)({
url: 'http://localhost:9999',
detectSessionInUrl: true,
autoRefreshToken: false,
storage: mockStorage,
flowType: 'implicit', // Mismatch with PKCE flow
})

// Initialize client to trigger flow mismatch
await client.initialize()

expect(client).toBeDefined()
})
})

describe('GoTrueClient BroadcastChannel', () => {
Expand Down Expand Up @@ -334,6 +418,30 @@ describe('Browser locks functionality', () => {

expect(client).toBeDefined()
})

it('should handle _acquireLock with empty pendingInLock', async () => {
const client = getClientWithSpecificStorage({
getItem: jest.fn(),
setItem: jest.fn(),
removeItem: jest.fn(),
})

// Mock navigator.locks
const mockLock = { name: 'test-lock' }
const mockRequest = jest.fn().mockImplementation((_, __, callback) =>
Promise.resolve(callback(mockLock))
)

Object.defineProperty(navigator, 'locks', {
value: { request: mockRequest },
writable: true,
})

// Initialize client to trigger lock acquisition
await client.initialize()

expect(client).toBeDefined()
})
})

describe('Web3 functionality in browser', () => {
Expand Down Expand Up @@ -376,3 +484,236 @@ describe('GoTrueClient constructor edge cases', () => {
expect((client as any).userStorage).toBe(customUserStorage)
})
})

describe('linkIdentity with skipBrowserRedirect false', () => {

it('should linkIdentity with skipBrowserRedirect false', async () => {
Object.defineProperty(window, 'location', {
value: {
href: 'http://localhost:9999',
assign: jest.fn(),
replace: jest.fn(),
reload: jest.fn(),
toString: () => 'http://localhost:9999',
},
writable: true,
})
// Mock successful session
const mockSession = {
access_token: 'test-access-token',
refresh_token: 'test-refresh-token',
expires_in: 3600,
token_type: 'bearer',
user: { id: 'test-user' },
}

// Mock storage to return session
const mockStorage = {
getItem: jest.fn().mockResolvedValue(JSON.stringify(mockSession)),
setItem: jest.fn(),
removeItem: jest.fn(),
}

// Create client with custom fetch
const mockFetch = jest.fn().mockResolvedValue({
ok: true,
status: 200,
json: () => Promise.resolve({ url: 'http://localhost:9999/oauth/callback' }),
text: () => Promise.resolve('{"url": "http://localhost:9999/oauth/callback"}'),
headers: new Headers(),
} as Response)

const clientWithSession = new (require('../src/GoTrueClient').default)({
url: 'http://localhost:9999',
storageKey: 'test-specific-storage',
autoRefreshToken: false,
persistSession: true,
storage: mockStorage,
fetch: mockFetch,
})

// Mock window.location.assign
const mockAssign = jest.fn()
Object.defineProperty(window, 'location', {
value: {
href: 'http://localhost:9999',
assign: mockAssign,
replace: jest.fn(),
reload: jest.fn(),
toString: () => 'http://localhost:9999',
},
writable: true,
})

try {
const result = await clientWithSession.linkIdentity({
provider: 'github',
options: {
skipBrowserRedirect: false,
},
})

expect(result.data?.url).toBeDefined()
expect(mockFetch).toHaveBeenCalled()
// Note: linkIdentity might not always call window.location.assign depending on the response
// So we just verify the result is defined
} catch (error) {
console.error('Test error:', error)
throw error
}
})
})

describe('Session Management Tests', () => {
it('should handle _recoverAndRefresh with Infinity expires_at', async () => {
// Mock session with null expires_at
const mockSession = {
access_token: 'test-access-token',
refresh_token: 'test-refresh-token',
expires_in: 3600,
expires_at: null,
token_type: 'bearer',
user: { id: 'test-user' },
}

const mockStorage = {
getItem: jest.fn().mockResolvedValue(JSON.stringify(mockSession)),
setItem: jest.fn(),
removeItem: jest.fn(),
}

const client = getClientWithSpecificStorage(mockStorage)

// Initialize client to trigger _recoverAndRefresh with Infinity expires_at
await client.initialize()

expect(client).toBeDefined()
})

it('should handle _recoverAndRefresh with refresh token error', async () => {
// Mock session
const mockSession = {
access_token: 'test-access-token',
refresh_token: 'test-refresh-token',
expires_in: 3600,
expires_at: Math.floor(Date.now() / 1000) - 100, // Expired
token_type: 'bearer',
user: { id: 'test-user' },
}

const mockStorage = {
getItem: jest.fn().mockResolvedValue(JSON.stringify(mockSession)),
setItem: jest.fn(),
removeItem: jest.fn(),
}

const mockFetch = jest.fn().mockResolvedValue({
ok: false,
status: 401,
json: () => Promise.resolve({ error: 'invalid_grant' }),
text: () => Promise.resolve('{"error": "invalid_grant"}'),
headers: new Headers(),
} as Response)

const client = new (require('../src/GoTrueClient').default)({
url: 'http://localhost:9999',
autoRefreshToken: true,
persistSession: true,
storage: mockStorage,
fetch: mockFetch,
})

// Initialize client to trigger refresh token error
await client.initialize()

expect(client).toBeDefined()
})

it('should handle _recoverAndRefresh with user proxy', async () => {
// Mock session with proxy user
const mockSession = {
access_token: 'test-access-token',
refresh_token: 'test-refresh-token',
expires_in: 3600,
expires_at: Math.floor(Date.now() / 1000) + 3600, // Valid
token_type: 'bearer',
user: { __isUserNotAvailableProxy: true },
}

const mockStorage = {
getItem: jest.fn().mockResolvedValue(JSON.stringify(mockSession)),
setItem: jest.fn(),
removeItem: jest.fn(),
}

// Mock fetch to return user data
const mockFetch = jest.fn().mockResolvedValue({
ok: true,
status: 200,
json: () => Promise.resolve({ user: { id: 'test-user', email: '[email protected]' } }),
text: () => Promise.resolve('{"user": {"id": "test-user", "email": "[email protected]"}}'),
headers: new Headers(),
} as Response)

const client = new (require('../src/GoTrueClient').default)({
url: 'http://localhost:9999',
autoRefreshToken: true,
persistSession: true,
storage: mockStorage,
fetch: mockFetch,
})

// Initialize client to trigger user proxy handling
await client.initialize()

expect(client).toBeDefined()
})
})

describe('Additional Tests', () => {
it('should handle _initialize with storage returning boolean', async () => {
// Mock storage to return boolean
const mockStorage = {
getItem: jest.fn().mockResolvedValue(true),
setItem: jest.fn(),
removeItem: jest.fn(),
}

const client = new (require('../src/GoTrueClient').default)({
url: 'http://localhost:9999',
autoRefreshToken: false,
persistSession: true,
storage: mockStorage,
})

// Initialize client to trigger boolean handling
await client.initialize()

expect(client).toBeDefined()
})

it('should handle _initialize with expires_at parameter', async () => {
// Mock window.location with expires_at parameter
Object.defineProperty(window, 'location', {
value: {
href: 'http://localhost:9999/callback?access_token=test&refresh_token=test&expires_in=3600&expires_at=1234567890&token_type=bearer',
assign: jest.fn(),
replace: jest.fn(),
reload: jest.fn(),
toString: () => 'http://localhost:9999/callback',
},
writable: true,
})

const client = new (require('../src/GoTrueClient').default)({
url: 'http://localhost:9999',
detectSessionInUrl: true,
autoRefreshToken: false,
})

// Initialize client to trigger _initialize with expires_at
await client.initialize()

expect(client).toBeDefined()
})
})
Loading