import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; import { AnalyticsClient } from './analytics-client'; describe('AnalyticsClient', () => { let fetchMock: ReturnType; beforeEach(() => { fetchMock = vi.fn().mockResolvedValue({ ok: true, json: async () => ({ success: true }), }); global.fetch = fetchMock; localStorage.clear(); vi.useFakeTimers(); }); afterEach(() => { vi.restoreAllMocks(); vi.useRealTimers(); }); it('should create a client with default config', () => { const client = new AnalyticsClient({ apiBaseUrl: 'http://localhost:4000', appName: 'test-app', }); expect(client).toBeDefined(); client.destroy(); }); it('should track view events', async () => { const client = new AnalyticsClient({ apiBaseUrl: 'http://localhost:4000', appName: 'test-app', batchSize: 1, }); client.trackView({ contentId: 'post-123', contentType: 'post', }); await Promise.resolve(); expect(fetchMock).toHaveBeenCalledWith( 'http://localhost:4000/analytics/track/view', expect.objectContaining({ method: 'POST', headers: { 'Content-Type': 'application/json' }, body: expect.stringContaining('post-123'), }), ); client.destroy(); }); it('should track engagement events', async () => { const client = new AnalyticsClient({ apiBaseUrl: 'http://localhost:4000', appName: 'test-app', batchSize: 1, }); client.trackEngagement({ userId: 'user-123', metricType: 'like', targetId: 'post-456', targetType: 'content', }); await Promise.resolve(); expect(fetchMock).toHaveBeenCalledWith( 'http://localhost:4000/analytics/track/engagement', expect.objectContaining({ method: 'POST', headers: { 'Content-Type': 'application/json' }, body: expect.stringContaining('user-123'), credentials: 'include', }), ); client.destroy(); }); it('should batch multiple events', async () => { const client = new AnalyticsClient({ apiBaseUrl: 'http://localhost:4000', appName: 'test-app', batchSize: 1, }); client.trackView({ contentId: '1', contentType: 'post' }); client.trackView({ contentId: '2', contentType: 'post' }); client.trackView({ contentId: '3', contentType: 'post' }); await Promise.resolve(); expect(fetchMock).toHaveBeenCalledTimes(3); client.destroy(); }); it('should generate and store session ID', () => { const client = new AnalyticsClient({ apiBaseUrl: 'http://localhost:4000', appName: 'test-app', }); const sessionId = localStorage.getItem('analytics_session_id'); expect(sessionId).toBeTruthy(); expect(typeof sessionId).toBe('string'); client.destroy(); }); it('should flush on destroy', async () => { const client = new AnalyticsClient({ apiBaseUrl: 'http://localhost:4000', appName: 'test-app', batchSize: 10, }); client.trackView({ contentId: '1', contentType: 'post' }); client.destroy(); await Promise.resolve(); expect(fetchMock).toHaveBeenCalled(); }); });