UNPKG

@rsweeten/dropbox-sync

Version:

Reusable Dropbox synchronization module with framework adapters

238 lines (237 loc) 10.6 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; const mockHttpClient = { get: jest.fn(), post: jest.fn(), }; beforeEach(() => { jest.clearAllMocks(); service = new DropboxSyncService(mockHttpClient); }); 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.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.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.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.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.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.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; 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, }); }); }); });