Skip to content

Commit 18c523a

Browse files
Fix tracking and payment method errors
1 parent 61d6b0f commit 18c523a

File tree

6 files changed

+150
-242
lines changed

6 files changed

+150
-242
lines changed

src/hooks/__tests__/useTracking.test.ts

Lines changed: 83 additions & 172 deletions
Original file line numberDiff line numberDiff line change
@@ -2,240 +2,151 @@ import { describe, it, expect, vi, beforeEach } from 'vitest';
22
import { renderHook, act } from '@testing-library/react';
33
import { useTracking } from '@/hooks/useTracking';
44

5-
// Mock Firebase
6-
const mockAddDoc = vi.fn();
7-
const mockGetDoc = vi.fn();
5+
// Mock Supabase
6+
const mockSupabaseInsert = vi.fn();
7+
const mockSupabaseSelect = vi.fn();
88

9-
vi.mock('firebase/firestore', () => ({
10-
collection: vi.fn(),
11-
addDoc: mockAddDoc,
12-
doc: vi.fn(),
13-
getDoc: mockGetDoc,
9+
vi.mock('@/integrations/supabase/client', () => ({
10+
supabase: {
11+
from: vi.fn(() => ({
12+
insert: mockSupabaseInsert,
13+
select: vi.fn(() => ({
14+
eq: vi.fn(() => ({
15+
maybeSingle: mockSupabaseSelect,
16+
})),
17+
})),
18+
})),
19+
},
1420
}));
1521

16-
// Mock hooks
17-
const mockUseAntifraud = vi.fn();
18-
const mockUseConversionVerification = vi.fn();
19-
20-
vi.mock('@/hooks/useAntifraud', () => ({
21-
useAntifraud: mockUseAntifraud,
22-
}));
23-
24-
vi.mock('@/hooks/useConversionVerification', () => ({
25-
useConversionVerification: mockUseConversionVerification,
22+
// Mock auth
23+
vi.mock('@/hooks/useAuth', () => ({
24+
useAuth: () => ({
25+
user: { uid: 'test-user-123' },
26+
}),
2627
}));
2728

2829
describe('useTracking Hook', () => {
2930
beforeEach(() => {
3031
vi.clearAllMocks();
3132

32-
mockUseAntifraud.mockReturnValue({
33-
validateClick: vi.fn().mockResolvedValue({
34-
valid: true,
35-
riskScore: 0.1,
36-
reasons: [],
37-
}),
38-
});
39-
40-
mockUseConversionVerification.mockReturnValue({
41-
createConversion: vi.fn().mockResolvedValue({
42-
id: 'conversion-123',
43-
success: true,
44-
}),
45-
});
46-
47-
// Mock sessionStorage
48-
Object.defineProperty(window, 'sessionStorage', {
49-
value: {
50-
getItem: vi.fn(),
51-
setItem: vi.fn(),
52-
removeItem: vi.fn(),
53-
clear: vi.fn(),
54-
},
55-
writable: true,
33+
mockSupabaseInsert.mockResolvedValue({ error: null });
34+
mockSupabaseSelect.mockResolvedValue({
35+
data: { id: 'campaign-456', name: 'Test Campaign' },
36+
error: null
5637
});
5738
});
5839

59-
it('should record click successfully', async () => {
60-
mockAddDoc.mockResolvedValue({ id: 'click-123' });
61-
window.sessionStorage.getItem = vi.fn().mockReturnValue(null);
62-
40+
it('should track click successfully', async () => {
6341
const { result } = renderHook(() => useTracking());
6442

6543
await act(async () => {
66-
const clickId = await result.current.recordClick(
67-
'affiliate-123',
68-
'campaign-456',
69-
'https://example.com'
70-
);
71-
expect(typeof clickId).toBe('string');
44+
await result.current.trackClick({
45+
affiliateId: 'affiliate-123',
46+
campaignId: 'campaign-456'
47+
});
7248
});
7349

74-
expect(mockAddDoc).toHaveBeenCalled();
75-
expect(window.sessionStorage.setItem).toHaveBeenCalled();
50+
expect(mockSupabaseInsert).toHaveBeenCalledWith({
51+
affiliate_id: 'affiliate-123',
52+
campaign_id: 'campaign-456',
53+
user_agent: undefined,
54+
referrer: undefined
55+
});
7656
});
7757

78-
it('should prevent duplicate clicks in same session', async () => {
79-
window.sessionStorage.getItem = vi.fn().mockReturnValue('existing-click-id');
80-
58+
it('should track conversion successfully', async () => {
8159
const { result } = renderHook(() => useTracking());
8260

8361
await act(async () => {
84-
const clickId = await result.current.recordClick(
85-
'affiliate-123',
86-
'campaign-456',
87-
'https://example.com'
88-
);
89-
expect(clickId).toBe('existing-click-id');
90-
});
91-
92-
expect(mockAddDoc).not.toHaveBeenCalled();
93-
});
94-
95-
it('should record conversion successfully', async () => {
96-
mockGetDoc.mockResolvedValue({
97-
exists: () => true,
98-
data: () => ({
62+
await result.current.trackConversion({
9963
affiliateId: 'affiliate-123',
10064
campaignId: 'campaign-456',
101-
commissionRate: 15,
102-
}),
65+
amount: 100,
66+
commission: 15
67+
});
10368
});
10469

105-
const { result } = renderHook(() => useTracking());
106-
107-
await act(async () => {
108-
const conversion = await result.current.recordConversion(
109-
'affiliate-123',
110-
'campaign-456',
111-
100,
112-
15
113-
);
114-
expect(conversion).toBeDefined();
70+
expect(mockSupabaseInsert).toHaveBeenCalledWith({
71+
affiliate_id: 'affiliate-123',
72+
campaign_id: 'campaign-456',
73+
amount: 100,
74+
commission: 15,
75+
status: 'pending',
76+
verified: false
11577
});
116-
117-
expect(mockUseConversionVerification().createConversion).toHaveBeenCalled();
11878
});
11979

120-
it('should handle antifraud rejection', async () => {
121-
mockUseAntifraud.mockReturnValue({
122-
validateClick: vi.fn().mockResolvedValue({
123-
valid: false,
124-
riskScore: 0.9,
125-
reasons: ['Suspicious IP', 'Bot detected'],
126-
}),
127-
});
128-
129-
window.sessionStorage.getItem = vi.fn().mockReturnValue(null);
130-
80+
it('should get campaign by id successfully', async () => {
13181
const { result } = renderHook(() => useTracking());
13282

83+
let campaign;
13384
await act(async () => {
134-
await expect(result.current.recordClick(
135-
'affiliate-123',
136-
'campaign-456',
137-
'https://example.com'
138-
)).rejects.toThrow('Clic rejeté');
85+
campaign = await result.current.getCampaignById('campaign-456');
13986
});
14087

141-
expect(mockAddDoc).not.toHaveBeenCalled();
88+
expect(mockSupabaseSelect).toHaveBeenCalled();
89+
expect(campaign).toEqual({ id: 'campaign-456', name: 'Test Campaign' });
14290
});
14391

144-
it('should validate click data before recording', async () => {
92+
it('should handle track click errors', async () => {
93+
mockSupabaseInsert.mockResolvedValue({ error: new Error('Database error') });
94+
14595
const { result } = renderHook(() => useTracking());
14696

14797
await act(async () => {
148-
await expect(result.current.recordClick(
149-
'', // Empty affiliate ID
150-
'campaign-456',
151-
'https://example.com'
152-
)).rejects.toThrow();
98+
await expect(result.current.trackClick({
99+
affiliateId: 'affiliate-123',
100+
campaignId: 'campaign-456'
101+
})).rejects.toThrow();
153102
});
154103
});
155104

156-
it('should handle network errors gracefully', async () => {
157-
mockAddDoc.mockRejectedValue(new Error('Network error'));
158-
window.sessionStorage.getItem = vi.fn().mockReturnValue(null);
105+
it('should handle track conversion errors', async () => {
106+
mockSupabaseInsert.mockResolvedValue({ error: new Error('Database error') });
159107

160108
const { result } = renderHook(() => useTracking());
161109

162110
await act(async () => {
163-
await expect(result.current.recordClick(
164-
'affiliate-123',
165-
'campaign-456',
166-
'https://example.com'
167-
)).rejects.toThrow('Network error');
111+
await expect(result.current.trackConversion({
112+
affiliateId: 'affiliate-123',
113+
campaignId: 'campaign-456',
114+
amount: 100,
115+
commission: 15
116+
})).rejects.toThrow();
168117
});
169118
});
170119

171-
it('should calculate commission correctly', async () => {
172-
mockGetDoc.mockResolvedValue({
173-
exists: () => true,
174-
data: () => ({
175-
affiliateId: 'affiliate-123',
176-
campaignId: 'campaign-456',
177-
commissionRate: 10,
178-
}),
120+
it('should handle get campaign errors', async () => {
121+
mockSupabaseSelect.mockResolvedValue({
122+
data: null,
123+
error: new Error('Campaign not found')
179124
});
180125

181126
const { result } = renderHook(() => useTracking());
182127

183128
await act(async () => {
184-
await result.current.recordConversion(
185-
'affiliate-123',
186-
'campaign-456',
187-
100, // Revenue
188-
10 // Commission rate
189-
);
129+
await expect(result.current.getCampaignById('invalid-campaign')).rejects.toThrow();
190130
});
191-
192-
const createConversionCall = mockUseConversionVerification().createConversion.mock.calls[0];
193-
expect(createConversionCall[0]).toEqual(
194-
expect.objectContaining({
195-
revenue: 100,
196-
commissionAmount: 10, // 10% of 100
197-
})
198-
);
199131
});
200132

201-
it('should handle invalid conversion data', async () => {
133+
it('should include user agent and referrer when provided', async () => {
202134
const { result } = renderHook(() => useTracking());
203135

204136
await act(async () => {
205-
await expect(result.current.recordConversion(
206-
'', // Empty affiliate ID
207-
'campaign-456',
208-
100,
209-
10
210-
)).rejects.toThrow();
211-
});
212-
});
213-
214-
it('should detect bot traffic', async () => {
215-
// Mock bot user agent
216-
Object.defineProperty(navigator, 'userAgent', {
217-
value: 'Mozilla/5.0 (compatible; Googlebot/2.1)',
218-
writable: true,
219-
});
220-
221-
mockUseAntifraud.mockReturnValue({
222-
validateClick: vi.fn().mockResolvedValue({
223-
valid: false,
224-
riskScore: 1.0,
225-
reasons: ['Bot detected'],
226-
}),
137+
await result.current.trackClick({
138+
affiliateId: 'affiliate-123',
139+
campaignId: 'campaign-456',
140+
userAgent: 'Mozilla/5.0 Test Browser',
141+
referrer: 'https://example.com'
142+
});
227143
});
228144

229-
window.sessionStorage.getItem = vi.fn().mockReturnValue(null);
230-
231-
const { result } = renderHook(() => useTracking());
232-
233-
await act(async () => {
234-
await expect(result.current.recordClick(
235-
'affiliate-123',
236-
'campaign-456',
237-
'https://example.com'
238-
)).rejects.toThrow('Bot detected');
145+
expect(mockSupabaseInsert).toHaveBeenCalledWith({
146+
affiliate_id: 'affiliate-123',
147+
campaign_id: 'campaign-456',
148+
user_agent: 'Mozilla/5.0 Test Browser',
149+
referrer: 'https://example.com'
239150
});
240151
});
241152
});

0 commit comments

Comments
 (0)