// Mock ENV (global as any).ENV = { API_URL: 'https://api.adalong.com' }; import { buildRequestPayload, trackOrder } from '../order'; import Api from '../../../services/api'; import { AnalyticsConsent } from '../../../services/analyticsConsent'; import { Identity } from '../../../services/identity'; // Mock the services jest.mock('../../../services/api'); jest.mock('../../../services/analyticsConsent'); jest.mock('../../../services/identity'); const mockApi = Api as jest.Mocked; const mockAnalyticsConsent = AnalyticsConsent as jest.Mocked; const mockIdentity = Identity as jest.Mocked; describe('buildRequestPayload', () => { beforeEach(() => { jest.clearAllMocks(); }); it('should build payload with minimal required fields', () => { const input = { orderId: 'order123', revenue: 100, currency: 'usd', }; const result = buildRequestPayload(input); expect(result).toEqual({ order_id: 'order123', revenue: 100, currency: 'USD', page_location: 'http://localhost/', }); }); it('should build payload with items', () => { const input = { orderId: 'order123', revenue: 100, currency: 'usd', items: [ { productId: 'prod1', quantity: 2, price: 50 }, { product_id: 'prod2', quantity: 1, price: 25 }, ], }; const result = buildRequestPayload(input); expect(result).toEqual({ order_id: 'order123', revenue: 100, currency: 'USD', items: [ { productId: 'prod1', quantity: 2, price: 50 }, { productId: 'prod2', quantity: 1, price: 25 }, ], page_location: 'http://localhost/', }); }); it('should build payload with identity when consent is given', () => { mockAnalyticsConsent.has.mockReturnValue(true); mockIdentity.getIdentity.mockReturnValue({ visitorId: 'visitor123', sessionId: 'session123', }); mockIdentity.touchSession.mockImplementation(() => {}); const input = { orderId: 'order123', revenue: 100, currency: 'usd', }; const result = buildRequestPayload(input); expect(result).toEqual({ order_id: 'order123', revenue: 100, currency: 'USD', visitor_id: 'visitor123', session_id: 'session123', page_location: 'http://localhost/', }); }); it('should use custom pageLocation and pageReferrer', () => { Object.defineProperty(document, 'referrer', { value: 'http://referrer.com', writable: true, }); const input = { orderId: 'order123', revenue: 100, currency: 'usd', pageLocation: 'http://custom.com', pageReferrer: 'http://customref.com', }; const result = buildRequestPayload(input); expect(result.page_location).toBe('http://custom.com'); expect(result.page_referrer).toBe('http://customref.com'); }); it('should round revenue to 2 decimals', () => { const input = { orderId: 'order123', revenue: 100.123, currency: 'usd', }; const result = buildRequestPayload(input); expect(result.revenue).toBe(100.12); }); it('should sanitize and truncate strings', () => { const longString = 'a'.repeat(200); const input = { orderId: longString, revenue: 100, currency: 'usd', }; const result = buildRequestPayload(input); expect(result.order_id).toBe('a'.repeat(128)); }); it('should throw error for missing orderId', () => { const input = { revenue: 100, currency: 'usd', } as any; expect(() => buildRequestPayload(input)).toThrow('trackOrder: orderId is required'); }); it('should throw error for invalid revenue', () => { const input = { orderId: 'order123', revenue: 'invalid', currency: 'usd', } as any; expect(() => buildRequestPayload(input)).toThrow('trackOrder: revenue must be a number'); }); it('should throw error for missing currency', () => { const input = { orderId: 'order123', revenue: 100, } as any; expect(() => buildRequestPayload(input)).toThrow('trackOrder: currency is required'); }); it('should filter out invalid items', () => { const input = { orderId: 'order123', revenue: 100, currency: 'usd', items: [ { productId: 'prod1', quantity: 2, price: 50 }, { productId: '', quantity: 1, price: 25 }, // invalid { quantity: 1, price: 25 }, // no productId ], }; const result = buildRequestPayload(input); expect(result.items).toEqual([ { productId: 'prod1', quantity: 2, price: 50 }, ]); }); it('should handle products array instead of items', () => { const input = { orderId: 'order123', revenue: 100, currency: 'usd', products: [ { productId: 'prod1', quantity: 2, price: 50 }, ], }; const result = buildRequestPayload(input); expect(result.items).toEqual([ { productId: 'prod1', quantity: 2, price: 50 }, ]); }); it('should limit items to MAX_ITEMS', () => { const items = Array.from({ length: 60 }, (_, i) => ({ productId: `prod${i}`, quantity: 1, price: 10, })); const input = { orderId: 'order123', revenue: 100, currency: 'usd', items, }; const result = buildRequestPayload(input); expect(result.items).toHaveLength(50); }); }); describe('trackOrder', () => { beforeEach(() => { jest.clearAllMocks(); mockApi.trackOrder.mockResolvedValue(undefined); }); it('should return success on successful API call', async () => { const input = { orderId: 'order123', revenue: 100, currency: 'usd', }; const result = await trackOrder(input); expect(result.success).toBe(true); expect(mockApi.trackOrder).toHaveBeenCalledTimes(1); }); it('should return error on API failure', async () => { mockApi.trackOrder.mockRejectedValue(new Error('API error')); const input = { orderId: 'order123', revenue: 100, currency: 'usd', }; const result = await trackOrder(input); expect(result.success).toBe(false); expect(result.error).toBe('API error'); }); it('should return error on invalid input', async () => { const input = { revenue: 100, currency: 'usd', } as any; const result = await trackOrder(input); expect(result.success).toBe(false); expect(result.error).toBe('trackOrder: orderId is required'); expect(mockApi.trackOrder).not.toHaveBeenCalled(); }); });