/** * Unit Tests for Analytics Client * Tests tracking methods, batching, and error handling */ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; // Mock fetch const mockFetch = vi.fn(); global.fetch = mockFetch; import { AnalyticsClient } from './analytics-client'; describe('AnalyticsClient', () => { let client: AnalyticsClient; beforeEach(() => { mockFetch.mockClear(); mockFetch.mockResolvedValue({ ok: true, json: () => Promise.resolve({}) }); client = new AnalyticsClient({ apiBaseUrl: 'https://api.test.com', appName: 'test-app', batchSize: 10, batchInterval: 1000, }); }); afterEach(() => { client.destroy(); vi.clearAllTimers(); }); describe('constructor', () => { it('should create client with required options', () => { const testClient = new AnalyticsClient({ apiBaseUrl: 'https://api.test.com', appName: 'test', }); expect(testClient).toBeDefined(); testClient.destroy(); }); it('should accept custom batch options', () => { const customClient = new AnalyticsClient({ apiBaseUrl: 'https://custom.api.com', appName: 'custom-app', batchSize: 50, batchInterval: 5000, }); expect(customClient).toBeDefined(); customClient.destroy(); }); }); describe('trackView', () => { it('should track view event with path', () => { client.trackView({ path: '/dashboard', userId: 'user-123' }); expect(client).toBeDefined(); }); it('should track view with all optional fields', () => { client.trackView({ path: '/analytics/revenue', userId: 'user-456', referrer: 'https://google.com', }); expect(client).toBeDefined(); }); }); describe('trackEngagement', () => { it('should track engagement event', () => { client.trackEngagement({ type: 'click', target: 'submit-button', }); expect(client).toBeDefined(); }); it('should track engagement with metadata', () => { client.trackEngagement({ type: 'like', target: 'post-123', metadata: { source: 'feed' }, }); expect(client).toBeDefined(); }); }); describe('flush', () => { it('should send batched events', async () => { client.trackView({ path: '/test', userId: 'test-user' }); await client.flush(); expect(mockFetch).toHaveBeenCalled(); }); it('should handle flush errors gracefully', async () => { mockFetch.mockRejectedValueOnce(new Error('Network error')); client.trackView({ path: '/test', userId: 'test-user' }); // Should not throw await expect(client.flush()).resolves.not.toThrow(); }); }); describe('destroy', () => { it('should clean up resources', () => { const testClient = new AnalyticsClient({ apiBaseUrl: 'https://api.test.com', appName: 'test', }); expect(() => testClient.destroy()).not.toThrow(); }); }); }); describe('AnalyticsClient Session', () => { it('should generate session ID', () => { const client = new AnalyticsClient({ apiBaseUrl: 'https://api.test.com', appName: 'test', }); expect(client).toBeDefined(); client.destroy(); }); });