UNPKG

@rsweeten/dropbox-sync

Version:

Reusable Dropbox synchronization module with framework adapters

339 lines (274 loc) 11.3 kB
// Mock Angular modules before importing the code under test jest.mock('@angular/core', () => ({ Injectable: () => jest.fn(), })) jest.mock('@angular/common/http', () => ({ HttpClient: jest.fn(), HttpHeaders: jest.fn(), })) // Now import the code that uses Angular modules import { DropboxSyncService, getCredentialsFromEnvironment } from '../angular' import { createDropboxSyncClient } from '../../core/client' import { of } from 'rxjs' import { Observable } from 'rxjs' // Mock the core client jest.mock('../../core/client') describe('Angular adapter', () => { let service: DropboxSyncService const mockHttpClient = { get: jest.fn(), post: jest.fn(), } beforeEach(() => { jest.clearAllMocks() service = new DropboxSyncService(mockHttpClient as any) }) describe('DropboxSyncService', () => { it('should be created', () => { expect(service).toBeTruthy() }) it('should initialize with credentials', () => { const mockCredentials = { clientId: 'test-client-id', clientSecret: 'test-client-secret', accessToken: 'test-access-token', refreshToken: 'test-refresh-token', } const mockClient = { mock: 'client' } ;(createDropboxSyncClient as jest.Mock).mockReturnValue(mockClient) const result = service.initialize(mockCredentials) expect(createDropboxSyncClient).toHaveBeenCalledWith( mockCredentials ) expect(result).toEqual(mockClient) }) it('should get client after initialization', () => { const mockCredentials = { clientId: 'test-id' } const mockClient = { mock: 'client' } ;(createDropboxSyncClient as jest.Mock).mockReturnValue(mockClient) service.initialize(mockCredentials) const client = service.getClient() expect(client).toEqual(mockClient) }) it('should throw error if getClient called before initialization', () => { expect(() => service.getClient()).toThrow( 'DropboxSyncService not initialized. Call initialize() first.' ) }) it('should check connection status via HTTP', () => { mockHttpClient.get.mockReturnValue(of({ connected: true })) service.checkConnection().subscribe((result) => { expect(result).toBe(true) }) expect(mockHttpClient.get).toHaveBeenCalledWith( '/api/dropbox/status' ) }) it('should handle connection error', () => { mockHttpClient.get.mockReturnValue(of(null)) // Simulate error service.checkConnection().subscribe((result) => { expect(result).toBe(false) }) }) it('should disconnect via HTTP', () => { mockHttpClient.post.mockReturnValue(of({ success: true })) service.disconnectDropbox().subscribe((result) => { expect(result).toBe(true) }) expect(mockHttpClient.post).toHaveBeenCalledWith( '/api/dropbox/logout', {} ) }) it('should start sync process', () => { const mockOptions = { localDir: '/test-dir' } const mockResult = { uploaded: [], downloaded: [] } const mockClient = { sync: { syncFiles: jest.fn().mockResolvedValue(mockResult), }, socket: { connect: jest.fn(), }, } ;(createDropboxSyncClient as jest.Mock).mockReturnValue(mockClient) service.initialize({ clientId: 'test-id' }) // Mock setupSocketListeners to return an observable service.setupSocketListeners = jest.fn().mockReturnValue(of({})) service.startSync(mockOptions).subscribe((result) => { expect(result).toEqual(mockResult) }) expect(mockClient.socket.connect).toHaveBeenCalled() expect(mockClient.sync.syncFiles).toHaveBeenCalledWith(mockOptions) expect(service.setupSocketListeners).toHaveBeenCalled() }) it('should cancel sync', () => { const mockClient = { sync: { cancelSync: jest.fn(), }, } ;(createDropboxSyncClient as jest.Mock).mockReturnValue(mockClient) service.initialize({ clientId: 'test-id' }) service.cancelSync() expect(mockClient.sync.cancelSync).toHaveBeenCalled() }) // Mock the implementation of setupSocketListeners for testing it('should setup socket listeners', () => { const mockSocket = { on: jest.fn(), off: jest.fn(), } const mockClient = { socket: mockSocket, } ;(createDropboxSyncClient as jest.Mock).mockReturnValue(mockClient) service.initialize({ clientId: 'test-id' }) const observable = service.setupSocketListeners() // Should return an Observable expect(observable).toBeInstanceOf(Observable) // Get the subscriber function that was used to create the Observable const observer = { next: jest.fn(), error: jest.fn(), complete: jest.fn(), } const subscription = observable.subscribe(observer) // Since we have direct access to the socket mock, we can verify its behavior expect(mockSocket.on).toHaveBeenCalledWith( 'sync:progress', expect.any(Function) ) expect(mockSocket.on).toHaveBeenCalledWith( 'sync:queue', expect.any(Function) ) expect(mockSocket.on).toHaveBeenCalledWith( 'sync:complete', expect.any(Function) ) expect(mockSocket.on).toHaveBeenCalledWith( 'sync:error', expect.any(Function) ) // Clean up the subscription subscription.unsubscribe() }) it('should handle OAuth callback', () => { const mockCode = 'test-auth-code' const mockTokens = { accessToken: 'new-access-token', refreshToken: 'new-refresh-token', } const mockClient = { auth: { exchangeCodeForToken: jest .fn() .mockResolvedValue(mockTokens), }, } ;(createDropboxSyncClient as jest.Mock).mockReturnValue(mockClient) service.initialize({ clientId: 'test-id' }) // Create a proper localStorage mock const mockLocalStorage = { setItem: jest.fn(), getItem: jest.fn(), removeItem: jest.fn(), } Object.defineProperty(global, 'localStorage', { value: mockLocalStorage, writable: true, }) // Mock window.location.origin Object.defineProperty(window, 'location', { value: { origin: 'http://localhost', }, writable: true, }) // Subscribe to the observable to trigger the code let receivedResult: any service.handleOAuthCallback(mockCode).subscribe((result) => { receivedResult = result }) // Verify the exchangeCodeForToken was called with the expected args expect(mockClient.auth.exchangeCodeForToken).toHaveBeenCalledWith( mockCode, 'http://localhost/api/dropbox/auth/callback' ) // Create and resolve the promise to trigger the localStorage set calls const resolvePromise = Promise.resolve(mockTokens) // Return a promise that will resolve after our mocked promise return resolvePromise.then(() => { // Now we can check localStorage was called expect(mockLocalStorage.setItem).toHaveBeenCalledWith( 'dropbox_access_token', mockTokens.accessToken ) expect(mockLocalStorage.setItem).toHaveBeenCalledWith( 'dropbox_refresh_token', mockTokens.refreshToken ) expect(mockLocalStorage.setItem).toHaveBeenCalledWith( 'dropbox_connected', 'true' ) // Verify we received the tokens expect(receivedResult).toEqual(mockTokens) }) }) }) describe('getCredentialsFromEnvironment', () => { it('should extract credentials from environment', () => { const mockEnvironment = { dropboxAppKey: 'env-app-key', dropboxAppSecret: 'env-app-secret', } // Mock localStorage const mockLocalStorage = { getItem: jest.fn().mockImplementation((key) => { if (key === 'dropbox_access_token') return 'storage-access-token' if (key === 'dropbox_refresh_token') return 'storage-refresh-token' return null }), } Object.defineProperty(global, 'localStorage', { value: mockLocalStorage, writable: true, }) const credentials = getCredentialsFromEnvironment(mockEnvironment) expect(credentials).toEqual({ clientId: 'env-app-key', clientSecret: 'env-app-secret', accessToken: 'storage-access-token', refreshToken: 'storage-refresh-token', }) expect(mockLocalStorage.getItem).toHaveBeenCalledWith( 'dropbox_access_token' ) expect(mockLocalStorage.getItem).toHaveBeenCalledWith( 'dropbox_refresh_token' ) }) it('should handle missing environment values', () => { const mockEnvironment = {} // Mock localStorage with no tokens const mockLocalStorage = { getItem: jest.fn().mockReturnValue(null), } Object.defineProperty(global, 'localStorage', { value: mockLocalStorage, writable: true, }) const credentials = getCredentialsFromEnvironment(mockEnvironment) expect(credentials).toEqual({ clientId: '', clientSecret: undefined, accessToken: undefined, refreshToken: undefined, }) }) }) })