@rsweeten/dropbox-sync
Version:
Reusable Dropbox synchronization module with framework adapters
180 lines (179 loc) • 7.88 kB
JavaScript
import { useNextDropboxSync, getCredentialsFromCookies, handleOAuthCallback, createNextDropboxApiHandlers, } from '../next';
import { createDropboxSyncClient } from '../../core/client';
import { cookies } from 'next/headers';
import { 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.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.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.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',
};
global.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.mockReturnValue(mockClient);
// Mock NextResponse
const mockResponseCookies = {
set: jest.fn(),
};
const mockResponse = {
cookies: mockResponseCookies,
};
NextResponse.redirect.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',
};
global.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';
NextResponse.redirect.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.mockResolvedValue(mockCookieStore);
NextResponse.json.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.mockReturnValue(mockClient);
NextResponse.redirect.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.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
});
});
});