@rsweeten/dropbox-sync
Version:
Reusable Dropbox synchronization module with framework adapters
241 lines (195 loc) • 8.36 kB
text/typescript
import {
useNextDropboxSync,
getCredentialsFromCookies,
handleOAuthCallback,
createNextDropboxApiHandlers,
} from '../next'
import { createDropboxSyncClient } from '../../core/client'
import { cookies } from 'next/headers'
import { NextRequest, NextResponse } from 'next/server'
// Mock dependencies
jest.mock('../../core/client')
jest.mock('next/headers')
jest.mock('next/server')
describe('Next.js adapter', () => {
beforeEach(() => {
jest.clearAllMocks()
// Mock process.env
process.env.DROPBOX_APP_KEY = 'test-app-key'
process.env.DROPBOX_APP_SECRET = 'test-app-secret'
process.env.NEXT_PUBLIC_APP_URL = 'https://example.com'
})
describe('useNextDropboxSync', () => {
it('should create a Dropbox sync client with provided credentials', () => {
const mockCredentials = {
clientId: 'custom-client-id',
clientSecret: 'custom-client-secret',
}
;(createDropboxSyncClient as jest.Mock).mockReturnValue({
mock: 'client',
})
const result = useNextDropboxSync(mockCredentials)
expect(createDropboxSyncClient).toHaveBeenCalledWith(
mockCredentials
)
expect(result).toEqual({ mock: 'client' })
})
})
describe('getCredentialsFromCookies', () => {
it('should get credentials from cookies', async () => {
const mockCookieStore = {
get: jest.fn((name) => {
if (name === 'dropbox_access_token')
return { value: 'test-access-token' }
if (name === 'dropbox_refresh_token')
return { value: 'test-refresh-token' }
return null
}),
}
;(cookies as jest.Mock).mockResolvedValue(mockCookieStore)
const credentials = await getCredentialsFromCookies()
expect(cookies).toHaveBeenCalled()
expect(credentials).toEqual({
clientId: 'test-app-key',
clientSecret: 'test-app-secret',
accessToken: 'test-access-token',
refreshToken: 'test-refresh-token',
})
})
it('should handle missing cookie values', async () => {
const mockCookieStore = {
get: jest.fn().mockReturnValue(null),
}
;(cookies as jest.Mock).mockResolvedValue(mockCookieStore)
const credentials = await getCredentialsFromCookies()
expect(credentials).toEqual({
clientId: 'test-app-key',
clientSecret: 'test-app-secret',
accessToken: undefined,
refreshToken: undefined,
})
})
})
describe('handleOAuthCallback', () => {
it('should handle successful OAuth callback', async () => {
// Mock URL with auth code
const mockRequest = {
url: 'https://example.com/callback?code=test-auth-code',
} as unknown as NextRequest
// Mock URL constructor behavior
;(global as any).URL = jest.fn().mockImplementation(() => {
return {
searchParams: {
get: jest.fn().mockReturnValue('test-auth-code'),
},
}
})
// Mock Dropbox client
const mockClient = {
auth: {
exchangeCodeForToken: jest.fn().mockResolvedValue({
accessToken: 'new-access-token',
refreshToken: 'new-refresh-token',
expiresAt: Date.now() + 14400 * 1000,
}),
},
}
;(createDropboxSyncClient as jest.Mock).mockReturnValue(mockClient)
// Mock NextResponse
const mockResponseCookies = {
set: jest.fn(),
}
const mockResponse = {
cookies: mockResponseCookies,
}
;(NextResponse.redirect as jest.Mock).mockReturnValue(mockResponse)
await handleOAuthCallback(mockRequest)
expect(createDropboxSyncClient).toHaveBeenCalled()
expect(mockClient.auth.exchangeCodeForToken).toHaveBeenCalledWith(
'test-auth-code',
expect.any(String)
)
expect(NextResponse.redirect).toHaveBeenCalled()
expect(mockResponseCookies.set).toHaveBeenCalledTimes(3) // Access, refresh, and connected cookies
})
it('should redirect to error page if code is missing', async () => {
const mockRequest = {
url: 'https://example.com/callback',
} as unknown as NextRequest
;(global as any).URL = jest.fn().mockImplementation(() => {
return {
searchParams: {
get: jest.fn().mockReturnValue(null),
},
}
})
// Create a proper URL string for the mock response
const errorUrl = 'https://example.com/auth/error'
// Mock the redirect implementation to return this URL
;(NextResponse.redirect as jest.Mock).mockReturnValue({
url: errorUrl,
})
const result = await handleOAuthCallback(mockRequest)
// Now we'll check that the redirect was called, and the result contains our URL
expect(NextResponse.redirect).toHaveBeenCalled()
expect(result.url).toBe(errorUrl)
})
})
describe('createNextDropboxApiHandlers', () => {
it('should create API handlers with necessary methods', () => {
const handlers = createNextDropboxApiHandlers()
expect(handlers).toHaveProperty('status')
expect(handlers).toHaveProperty('oauthStart')
expect(handlers).toHaveProperty('logout')
})
it('should check status from cookies', async () => {
const mockCookieStore = {
get: jest.fn().mockReturnValue({ value: 'test-token' }),
}
;(cookies as jest.Mock).mockResolvedValue(mockCookieStore)
;(NextResponse.json as jest.Mock).mockReturnValue({
json: 'response',
})
const handlers = createNextDropboxApiHandlers()
const response = await handlers.status()
expect(cookies).toHaveBeenCalled()
expect(mockCookieStore.get).toHaveBeenCalledWith(
'dropbox_access_token'
)
expect(NextResponse.json).toHaveBeenCalledWith({ connected: true })
})
it('should start OAuth flow', async () => {
const mockClient = {
auth: {
getAuthUrl: jest
.fn()
.mockResolvedValue('https://dropbox.com/oauth'),
},
}
;(createDropboxSyncClient as jest.Mock).mockReturnValue(mockClient)
;(NextResponse.redirect as jest.Mock).mockReturnValue({
redirect: 'response',
})
const handlers = createNextDropboxApiHandlers()
const response = await handlers.oauthStart()
expect(createDropboxSyncClient).toHaveBeenCalled()
expect(mockClient.auth.getAuthUrl).toHaveBeenCalled()
expect(NextResponse.redirect).toHaveBeenCalledWith(
'https://dropbox.com/oauth'
)
})
it('should handle logout', async () => {
const mockResponseCookies = {
delete: jest.fn(),
}
const mockResponse = {
cookies: mockResponseCookies,
}
;(NextResponse.json as jest.Mock).mockReturnValue(mockResponse)
const handlers = createNextDropboxApiHandlers()
const response = await handlers.logout()
expect(NextResponse.json).toHaveBeenCalledWith({ success: true })
expect(mockResponseCookies.delete).toHaveBeenCalledTimes(3) // Should delete three cookies
})
})
})