react-fathom 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +199 -0
- package/dist/cjs/index.cjs +410 -0
- package/dist/cjs/index.cjs.map +1 -0
- package/dist/cjs/next/index.cjs +910 -0
- package/dist/cjs/next/index.cjs.map +1 -0
- package/dist/es/index.js +381 -0
- package/dist/es/index.js.map +1 -0
- package/dist/es/next/index.js +885 -0
- package/dist/es/next/index.js.map +1 -0
- package/dist/react-fathom.js +413 -0
- package/dist/react-fathom.js.map +1 -0
- package/dist/react-fathom.min.js +3 -0
- package/dist/react-fathom.min.js.map +1 -0
- package/package.json +127 -0
- package/src/FathomContext.tsx +5 -0
- package/src/FathomProvider.test.tsx +532 -0
- package/src/FathomProvider.tsx +122 -0
- package/src/components/TrackClick.test.tsx +191 -0
- package/src/components/TrackClick.tsx +62 -0
- package/src/components/TrackPageview.test.tsx +111 -0
- package/src/components/TrackPageview.tsx +36 -0
- package/src/components/TrackVisible.test.tsx +311 -0
- package/src/components/TrackVisible.tsx +105 -0
- package/src/components/index.ts +3 -0
- package/src/hooks/index.ts +4 -0
- package/src/hooks/useFathom.test.tsx +51 -0
- package/src/hooks/useFathom.ts +11 -0
- package/src/hooks/useTrackOnClick.test.tsx +197 -0
- package/src/hooks/useTrackOnClick.ts +65 -0
- package/src/hooks/useTrackOnMount.test.tsx +79 -0
- package/src/hooks/useTrackOnMount.ts +24 -0
- package/src/hooks/useTrackOnVisible.test.tsx +313 -0
- package/src/hooks/useTrackOnVisible.ts +99 -0
- package/src/index.ts +4 -0
- package/src/next/NextFathomProvider.test.tsx +131 -0
- package/src/next/NextFathomProvider.tsx +62 -0
- package/src/next/NextFathomProviderApp.test.tsx +308 -0
- package/src/next/NextFathomProviderApp.tsx +106 -0
- package/src/next/NextFathomProviderPages.test.tsx +330 -0
- package/src/next/NextFathomProviderPages.tsx +112 -0
- package/src/next/compositions/withAppRouter.test.tsx +113 -0
- package/src/next/compositions/withAppRouter.tsx +48 -0
- package/src/next/compositions/withPagesRouter.test.tsx +113 -0
- package/src/next/compositions/withPagesRouter.tsx +44 -0
- package/src/next/index.ts +7 -0
- package/src/next/types.ts +19 -0
- package/src/types.ts +37 -0
- package/types/FathomContext.d.ts +3 -0
- package/types/FathomContext.d.ts.map +1 -0
- package/types/FathomProvider.d.ts +5 -0
- package/types/FathomProvider.d.ts.map +1 -0
- package/types/components/TrackClick.d.ts +39 -0
- package/types/components/TrackClick.d.ts.map +1 -0
- package/types/components/TrackPageview.d.ts +21 -0
- package/types/components/TrackPageview.d.ts.map +1 -0
- package/types/components/TrackVisible.d.ts +39 -0
- package/types/components/TrackVisible.d.ts.map +1 -0
- package/types/components/index.d.ts +4 -0
- package/types/components/index.d.ts.map +1 -0
- package/types/hooks/index.d.ts +5 -0
- package/types/hooks/index.d.ts.map +1 -0
- package/types/hooks/useFathom.d.ts +6 -0
- package/types/hooks/useFathom.d.ts.map +1 -0
- package/types/hooks/useTrackOnClick.d.ts +39 -0
- package/types/hooks/useTrackOnClick.d.ts.map +1 -0
- package/types/hooks/useTrackOnMount.d.ts +14 -0
- package/types/hooks/useTrackOnMount.d.ts.map +1 -0
- package/types/hooks/useTrackOnVisible.d.ts +43 -0
- package/types/hooks/useTrackOnVisible.d.ts.map +1 -0
- package/types/index.d.ts +5 -0
- package/types/index.d.ts.map +1 -0
- package/types/next/AppRouterProvider.d.ts +7 -0
- package/types/next/AppRouterProvider.d.ts.map +1 -0
- package/types/next/NextFathomProvider.d.ts +34 -0
- package/types/next/NextFathomProvider.d.ts.map +1 -0
- package/types/next/NextFathomProviderApp.d.ts +6 -0
- package/types/next/NextFathomProviderApp.d.ts.map +1 -0
- package/types/next/NextFathomProviderPages.d.ts +6 -0
- package/types/next/NextFathomProviderPages.d.ts.map +1 -0
- package/types/next/PagesRouterProvider.d.ts +7 -0
- package/types/next/PagesRouterProvider.d.ts.map +1 -0
- package/types/next/compositions/withAppRouter.d.ts +29 -0
- package/types/next/compositions/withAppRouter.d.ts.map +1 -0
- package/types/next/compositions/withPagesRouter.d.ts +25 -0
- package/types/next/compositions/withPagesRouter.d.ts.map +1 -0
- package/types/next/index.d.ts +6 -0
- package/types/next/index.d.ts.map +1 -0
- package/types/next/types.d.ts +16 -0
- package/types/next/types.d.ts.map +1 -0
- package/types/test-setup.d.ts +2 -0
- package/types/test-setup.d.ts.map +1 -0
- package/types/types.d.ts +34 -0
- package/types/types.d.ts.map +1 -0
- package/types/useFathom.d.ts +7 -0
- package/types/useFathom.d.ts.map +1 -0
|
@@ -0,0 +1,532 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
|
|
3
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
4
|
+
|
|
5
|
+
import { renderHook, waitFor } from '@testing-library/react'
|
|
6
|
+
|
|
7
|
+
import { FathomProvider } from './FathomProvider'
|
|
8
|
+
import { useFathom } from './hooks/useFathom'
|
|
9
|
+
|
|
10
|
+
// Mock fathom-client
|
|
11
|
+
vi.mock('fathom-client', () => {
|
|
12
|
+
const mockFathomClient = {
|
|
13
|
+
trackEvent: vi.fn(),
|
|
14
|
+
trackPageview: vi.fn(),
|
|
15
|
+
trackGoal: vi.fn(),
|
|
16
|
+
load: vi.fn(),
|
|
17
|
+
setSite: vi.fn(),
|
|
18
|
+
blockTrackingForMe: vi.fn(),
|
|
19
|
+
enableTrackingForMe: vi.fn(),
|
|
20
|
+
isTrackingEnabled: vi.fn(() => true),
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return {
|
|
24
|
+
default: mockFathomClient,
|
|
25
|
+
load: mockFathomClient.load,
|
|
26
|
+
}
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
describe('FathomProvider', () => {
|
|
30
|
+
beforeEach(() => {
|
|
31
|
+
vi.clearAllMocks()
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
it('should provide default Fathom client when no client is provided', () => {
|
|
35
|
+
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
|
36
|
+
<FathomProvider>{children}</FathomProvider>
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
const { result } = renderHook(() => useFathom(), { wrapper })
|
|
40
|
+
|
|
41
|
+
expect(result.current.client).toBeDefined()
|
|
42
|
+
expect(result.current.trackEvent).toBeDefined()
|
|
43
|
+
expect(result.current.trackPageview).toBeDefined()
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
it('should use provided client', () => {
|
|
47
|
+
const customClient = {
|
|
48
|
+
trackEvent: vi.fn(),
|
|
49
|
+
trackPageview: vi.fn(),
|
|
50
|
+
trackGoal: vi.fn(),
|
|
51
|
+
load: vi.fn(),
|
|
52
|
+
setSite: vi.fn(),
|
|
53
|
+
blockTrackingForMe: vi.fn(),
|
|
54
|
+
enableTrackingForMe: vi.fn(),
|
|
55
|
+
isTrackingEnabled: vi.fn(() => true),
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
|
59
|
+
<FathomProvider client={customClient}>{children}</FathomProvider>
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
const { result } = renderHook(() => useFathom(), { wrapper })
|
|
63
|
+
|
|
64
|
+
expect(result.current.client).toBe(customClient)
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
it('should load Fathom when siteId is provided', async () => {
|
|
68
|
+
const loadSpy = vi.fn()
|
|
69
|
+
const mockClient = {
|
|
70
|
+
trackEvent: vi.fn(),
|
|
71
|
+
trackPageview: vi.fn(),
|
|
72
|
+
trackGoal: vi.fn(),
|
|
73
|
+
load: loadSpy,
|
|
74
|
+
setSite: vi.fn(),
|
|
75
|
+
blockTrackingForMe: vi.fn(),
|
|
76
|
+
enableTrackingForMe: vi.fn(),
|
|
77
|
+
isTrackingEnabled: vi.fn(() => true),
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
|
81
|
+
<FathomProvider client={mockClient} siteId="TEST_SITE_ID">
|
|
82
|
+
{children}
|
|
83
|
+
</FathomProvider>
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
renderHook(() => useFathom(), { wrapper })
|
|
87
|
+
|
|
88
|
+
await waitFor(() => {
|
|
89
|
+
expect(loadSpy).toHaveBeenCalledWith('TEST_SITE_ID', undefined)
|
|
90
|
+
})
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
it('should load Fathom with clientOptions when provided', async () => {
|
|
94
|
+
const loadSpy = vi.fn()
|
|
95
|
+
const clientOptions = { honorDNT: true }
|
|
96
|
+
const mockClient = {
|
|
97
|
+
trackEvent: vi.fn(),
|
|
98
|
+
trackPageview: vi.fn(),
|
|
99
|
+
trackGoal: vi.fn(),
|
|
100
|
+
load: loadSpy,
|
|
101
|
+
setSite: vi.fn(),
|
|
102
|
+
blockTrackingForMe: vi.fn(),
|
|
103
|
+
enableTrackingForMe: vi.fn(),
|
|
104
|
+
isTrackingEnabled: vi.fn(() => true),
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
|
108
|
+
<FathomProvider
|
|
109
|
+
client={mockClient}
|
|
110
|
+
siteId="TEST_SITE_ID"
|
|
111
|
+
clientOptions={clientOptions}
|
|
112
|
+
>
|
|
113
|
+
{children}
|
|
114
|
+
</FathomProvider>
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
renderHook(() => useFathom(), { wrapper })
|
|
118
|
+
|
|
119
|
+
await waitFor(() => {
|
|
120
|
+
expect(loadSpy).toHaveBeenCalledWith('TEST_SITE_ID', clientOptions)
|
|
121
|
+
})
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
it('should provide trackEvent function', () => {
|
|
125
|
+
const mockClient = {
|
|
126
|
+
trackEvent: vi.fn(),
|
|
127
|
+
trackPageview: vi.fn(),
|
|
128
|
+
trackGoal: vi.fn(),
|
|
129
|
+
load: vi.fn(),
|
|
130
|
+
setSite: vi.fn(),
|
|
131
|
+
blockTrackingForMe: vi.fn(),
|
|
132
|
+
enableTrackingForMe: vi.fn(),
|
|
133
|
+
isTrackingEnabled: vi.fn(() => true),
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
|
137
|
+
<FathomProvider client={mockClient}>{children}</FathomProvider>
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
const { result } = renderHook(() => useFathom(), { wrapper })
|
|
141
|
+
|
|
142
|
+
result.current.trackEvent?.('test-event', { id: 'test-id' })
|
|
143
|
+
|
|
144
|
+
expect(mockClient.trackEvent).toHaveBeenCalledWith('test-event', {
|
|
145
|
+
id: 'test-id',
|
|
146
|
+
})
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
it('should merge defaultEventOptions in trackEvent', () => {
|
|
150
|
+
const mockClient = {
|
|
151
|
+
trackEvent: vi.fn(),
|
|
152
|
+
trackPageview: vi.fn(),
|
|
153
|
+
trackGoal: vi.fn(),
|
|
154
|
+
load: vi.fn(),
|
|
155
|
+
setSite: vi.fn(),
|
|
156
|
+
blockTrackingForMe: vi.fn(),
|
|
157
|
+
enableTrackingForMe: vi.fn(),
|
|
158
|
+
isTrackingEnabled: vi.fn(() => true),
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
|
162
|
+
<FathomProvider
|
|
163
|
+
client={mockClient}
|
|
164
|
+
defaultEventOptions={{ id: 'default-id' }}
|
|
165
|
+
>
|
|
166
|
+
{children}
|
|
167
|
+
</FathomProvider>
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
const { result } = renderHook(() => useFathom(), { wrapper })
|
|
171
|
+
|
|
172
|
+
result.current.trackEvent?.('test-event', { value: 100 })
|
|
173
|
+
|
|
174
|
+
expect(mockClient.trackEvent).toHaveBeenCalledWith('test-event', {
|
|
175
|
+
id: 'default-id',
|
|
176
|
+
value: 100,
|
|
177
|
+
})
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
it('should override defaultEventOptions with provided options', () => {
|
|
181
|
+
const mockClient = {
|
|
182
|
+
trackEvent: vi.fn(),
|
|
183
|
+
trackPageview: vi.fn(),
|
|
184
|
+
trackGoal: vi.fn(),
|
|
185
|
+
load: vi.fn(),
|
|
186
|
+
setSite: vi.fn(),
|
|
187
|
+
blockTrackingForMe: vi.fn(),
|
|
188
|
+
enableTrackingForMe: vi.fn(),
|
|
189
|
+
isTrackingEnabled: vi.fn(() => true),
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
|
193
|
+
<FathomProvider
|
|
194
|
+
client={mockClient}
|
|
195
|
+
defaultEventOptions={{ id: 'default-id' }}
|
|
196
|
+
>
|
|
197
|
+
{children}
|
|
198
|
+
</FathomProvider>
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
const { result } = renderHook(() => useFathom(), { wrapper })
|
|
202
|
+
|
|
203
|
+
result.current.trackEvent?.('test-event', { id: 'override-id' })
|
|
204
|
+
|
|
205
|
+
expect(mockClient.trackEvent).toHaveBeenCalledWith('test-event', {
|
|
206
|
+
id: 'override-id',
|
|
207
|
+
})
|
|
208
|
+
})
|
|
209
|
+
|
|
210
|
+
it('should provide trackPageview function', () => {
|
|
211
|
+
const mockClient = {
|
|
212
|
+
trackEvent: vi.fn(),
|
|
213
|
+
trackPageview: vi.fn(),
|
|
214
|
+
trackGoal: vi.fn(),
|
|
215
|
+
load: vi.fn(),
|
|
216
|
+
setSite: vi.fn(),
|
|
217
|
+
blockTrackingForMe: vi.fn(),
|
|
218
|
+
enableTrackingForMe: vi.fn(),
|
|
219
|
+
isTrackingEnabled: vi.fn(() => true),
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
|
223
|
+
<FathomProvider client={mockClient}>{children}</FathomProvider>
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
const { result } = renderHook(() => useFathom(), { wrapper })
|
|
227
|
+
|
|
228
|
+
result.current.trackPageview?.({ url: '/test-page' })
|
|
229
|
+
|
|
230
|
+
expect(mockClient.trackPageview).toHaveBeenCalledWith({ url: '/test-page' })
|
|
231
|
+
})
|
|
232
|
+
|
|
233
|
+
it('should merge defaultPageviewOptions in trackPageview', () => {
|
|
234
|
+
const mockClient = {
|
|
235
|
+
trackEvent: vi.fn(),
|
|
236
|
+
trackPageview: vi.fn(),
|
|
237
|
+
trackGoal: vi.fn(),
|
|
238
|
+
load: vi.fn(),
|
|
239
|
+
setSite: vi.fn(),
|
|
240
|
+
blockTrackingForMe: vi.fn(),
|
|
241
|
+
enableTrackingForMe: vi.fn(),
|
|
242
|
+
isTrackingEnabled: vi.fn(() => true),
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
|
246
|
+
<FathomProvider
|
|
247
|
+
client={mockClient}
|
|
248
|
+
defaultPageviewOptions={{ url: '/default' }}
|
|
249
|
+
>
|
|
250
|
+
{children}
|
|
251
|
+
</FathomProvider>
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
const { result } = renderHook(() => useFathom(), { wrapper })
|
|
255
|
+
|
|
256
|
+
result.current.trackPageview?.({ referrer: 'https://example.com' })
|
|
257
|
+
|
|
258
|
+
expect(mockClient.trackPageview).toHaveBeenCalledWith({
|
|
259
|
+
url: '/default',
|
|
260
|
+
referrer: 'https://example.com',
|
|
261
|
+
})
|
|
262
|
+
})
|
|
263
|
+
|
|
264
|
+
it('should override defaultPageviewOptions with provided options', () => {
|
|
265
|
+
const mockClient = {
|
|
266
|
+
trackEvent: vi.fn(),
|
|
267
|
+
trackPageview: vi.fn(),
|
|
268
|
+
trackGoal: vi.fn(),
|
|
269
|
+
load: vi.fn(),
|
|
270
|
+
setSite: vi.fn(),
|
|
271
|
+
blockTrackingForMe: vi.fn(),
|
|
272
|
+
enableTrackingForMe: vi.fn(),
|
|
273
|
+
isTrackingEnabled: vi.fn(() => true),
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
|
277
|
+
<FathomProvider
|
|
278
|
+
client={mockClient}
|
|
279
|
+
defaultPageviewOptions={{ url: '/default' }}
|
|
280
|
+
>
|
|
281
|
+
{children}
|
|
282
|
+
</FathomProvider>
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
const { result } = renderHook(() => useFathom(), { wrapper })
|
|
286
|
+
|
|
287
|
+
result.current.trackPageview?.({ url: '/override' })
|
|
288
|
+
|
|
289
|
+
expect(mockClient.trackPageview).toHaveBeenCalledWith({ url: '/override' })
|
|
290
|
+
})
|
|
291
|
+
|
|
292
|
+
it('should provide trackGoal function', () => {
|
|
293
|
+
const mockClient = {
|
|
294
|
+
trackEvent: vi.fn(),
|
|
295
|
+
trackPageview: vi.fn(),
|
|
296
|
+
trackGoal: vi.fn(),
|
|
297
|
+
load: vi.fn(),
|
|
298
|
+
setSite: vi.fn(),
|
|
299
|
+
blockTrackingForMe: vi.fn(),
|
|
300
|
+
enableTrackingForMe: vi.fn(),
|
|
301
|
+
isTrackingEnabled: vi.fn(() => true),
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
|
305
|
+
<FathomProvider client={mockClient}>{children}</FathomProvider>
|
|
306
|
+
)
|
|
307
|
+
|
|
308
|
+
const { result } = renderHook(() => useFathom(), { wrapper })
|
|
309
|
+
|
|
310
|
+
result.current.trackGoal?.('GOAL_CODE', 1000)
|
|
311
|
+
|
|
312
|
+
expect(mockClient.trackGoal).toHaveBeenCalledWith('GOAL_CODE', 1000)
|
|
313
|
+
})
|
|
314
|
+
|
|
315
|
+
it('should provide other Fathom methods', () => {
|
|
316
|
+
const loadSpy = vi.fn()
|
|
317
|
+
const setSiteSpy = vi.fn()
|
|
318
|
+
const blockTrackingSpy = vi.fn()
|
|
319
|
+
const enableTrackingSpy = vi.fn()
|
|
320
|
+
const isTrackingEnabledSpy = vi.fn(() => true)
|
|
321
|
+
|
|
322
|
+
const mockClient = {
|
|
323
|
+
trackEvent: vi.fn(),
|
|
324
|
+
trackPageview: vi.fn(),
|
|
325
|
+
trackGoal: vi.fn(),
|
|
326
|
+
load: loadSpy,
|
|
327
|
+
setSite: setSiteSpy,
|
|
328
|
+
blockTrackingForMe: blockTrackingSpy,
|
|
329
|
+
enableTrackingForMe: enableTrackingSpy,
|
|
330
|
+
isTrackingEnabled: isTrackingEnabledSpy,
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
|
334
|
+
<FathomProvider client={mockClient}>{children}</FathomProvider>
|
|
335
|
+
)
|
|
336
|
+
|
|
337
|
+
const { result } = renderHook(() => useFathom(), { wrapper })
|
|
338
|
+
|
|
339
|
+
result.current.load?.('SITE_ID')
|
|
340
|
+
result.current.setSite?.('SITE_ID')
|
|
341
|
+
result.current.blockTrackingForMe?.()
|
|
342
|
+
result.current.enableTrackingForMe?.()
|
|
343
|
+
result.current.isTrackingEnabled?.()
|
|
344
|
+
|
|
345
|
+
expect(loadSpy).toHaveBeenCalledWith('SITE_ID', undefined)
|
|
346
|
+
expect(setSiteSpy).toHaveBeenCalledWith('SITE_ID')
|
|
347
|
+
expect(blockTrackingSpy).toHaveBeenCalled()
|
|
348
|
+
expect(enableTrackingSpy).toHaveBeenCalled()
|
|
349
|
+
expect(isTrackingEnabledSpy).toHaveBeenCalled()
|
|
350
|
+
})
|
|
351
|
+
|
|
352
|
+
it('should compose nested providers - child uses parent client', () => {
|
|
353
|
+
const parentClient = {
|
|
354
|
+
trackEvent: vi.fn(),
|
|
355
|
+
trackPageview: vi.fn(),
|
|
356
|
+
trackGoal: vi.fn(),
|
|
357
|
+
load: vi.fn(),
|
|
358
|
+
setSite: vi.fn(),
|
|
359
|
+
blockTrackingForMe: vi.fn(),
|
|
360
|
+
enableTrackingForMe: vi.fn(),
|
|
361
|
+
isTrackingEnabled: vi.fn(() => true),
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
|
365
|
+
<FathomProvider client={parentClient}>
|
|
366
|
+
<FathomProvider>{children}</FathomProvider>
|
|
367
|
+
</FathomProvider>
|
|
368
|
+
)
|
|
369
|
+
|
|
370
|
+
const { result } = renderHook(() => useFathom(), { wrapper })
|
|
371
|
+
|
|
372
|
+
expect(result.current.client).toBe(parentClient)
|
|
373
|
+
})
|
|
374
|
+
|
|
375
|
+
it('should compose nested providers - child overrides parent client', () => {
|
|
376
|
+
const parentClient = {
|
|
377
|
+
trackEvent: vi.fn(),
|
|
378
|
+
trackPageview: vi.fn(),
|
|
379
|
+
trackGoal: vi.fn(),
|
|
380
|
+
load: vi.fn(),
|
|
381
|
+
setSite: vi.fn(),
|
|
382
|
+
blockTrackingForMe: vi.fn(),
|
|
383
|
+
enableTrackingForMe: vi.fn(),
|
|
384
|
+
isTrackingEnabled: vi.fn(() => true),
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
const childClient = {
|
|
388
|
+
trackEvent: vi.fn(),
|
|
389
|
+
trackPageview: vi.fn(),
|
|
390
|
+
trackGoal: vi.fn(),
|
|
391
|
+
load: vi.fn(),
|
|
392
|
+
setSite: vi.fn(),
|
|
393
|
+
blockTrackingForMe: vi.fn(),
|
|
394
|
+
enableTrackingForMe: vi.fn(),
|
|
395
|
+
isTrackingEnabled: vi.fn(() => true),
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
|
399
|
+
<FathomProvider client={parentClient}>
|
|
400
|
+
<FathomProvider client={childClient}>{children}</FathomProvider>
|
|
401
|
+
</FathomProvider>
|
|
402
|
+
)
|
|
403
|
+
|
|
404
|
+
const { result } = renderHook(() => useFathom(), { wrapper })
|
|
405
|
+
|
|
406
|
+
expect(result.current.client).toBe(childClient)
|
|
407
|
+
})
|
|
408
|
+
|
|
409
|
+
it('should compose nested providers - child inherits parent defaultPageviewOptions', () => {
|
|
410
|
+
const mockClient = {
|
|
411
|
+
trackEvent: vi.fn(),
|
|
412
|
+
trackPageview: vi.fn(),
|
|
413
|
+
trackGoal: vi.fn(),
|
|
414
|
+
load: vi.fn(),
|
|
415
|
+
setSite: vi.fn(),
|
|
416
|
+
blockTrackingForMe: vi.fn(),
|
|
417
|
+
enableTrackingForMe: vi.fn(),
|
|
418
|
+
isTrackingEnabled: vi.fn(() => true),
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
|
422
|
+
<FathomProvider
|
|
423
|
+
client={mockClient}
|
|
424
|
+
defaultPageviewOptions={{ url: '/parent' }}
|
|
425
|
+
>
|
|
426
|
+
<FathomProvider>{children}</FathomProvider>
|
|
427
|
+
</FathomProvider>
|
|
428
|
+
)
|
|
429
|
+
|
|
430
|
+
const { result } = renderHook(() => useFathom(), { wrapper })
|
|
431
|
+
|
|
432
|
+
result.current.trackPageview?.()
|
|
433
|
+
|
|
434
|
+
expect(mockClient.trackPageview).toHaveBeenCalledWith({ url: '/parent' })
|
|
435
|
+
})
|
|
436
|
+
|
|
437
|
+
it('should compose nested providers - child overrides parent defaultPageviewOptions', () => {
|
|
438
|
+
const mockClient = {
|
|
439
|
+
trackEvent: vi.fn(),
|
|
440
|
+
trackPageview: vi.fn(),
|
|
441
|
+
trackGoal: vi.fn(),
|
|
442
|
+
load: vi.fn(),
|
|
443
|
+
setSite: vi.fn(),
|
|
444
|
+
blockTrackingForMe: vi.fn(),
|
|
445
|
+
enableTrackingForMe: vi.fn(),
|
|
446
|
+
isTrackingEnabled: vi.fn(() => true),
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
|
450
|
+
<FathomProvider
|
|
451
|
+
client={mockClient}
|
|
452
|
+
defaultPageviewOptions={{ url: '/parent' }}
|
|
453
|
+
>
|
|
454
|
+
<FathomProvider defaultPageviewOptions={{ url: '/child' }}>
|
|
455
|
+
{children}
|
|
456
|
+
</FathomProvider>
|
|
457
|
+
</FathomProvider>
|
|
458
|
+
)
|
|
459
|
+
|
|
460
|
+
const { result } = renderHook(() => useFathom(), { wrapper })
|
|
461
|
+
|
|
462
|
+
result.current.trackPageview?.()
|
|
463
|
+
|
|
464
|
+
expect(mockClient.trackPageview).toHaveBeenCalledWith({ url: '/child' })
|
|
465
|
+
})
|
|
466
|
+
|
|
467
|
+
it('should compose nested providers - child inherits parent defaultEventOptions', () => {
|
|
468
|
+
const mockClient = {
|
|
469
|
+
trackEvent: vi.fn(),
|
|
470
|
+
trackPageview: vi.fn(),
|
|
471
|
+
trackGoal: vi.fn(),
|
|
472
|
+
load: vi.fn(),
|
|
473
|
+
setSite: vi.fn(),
|
|
474
|
+
blockTrackingForMe: vi.fn(),
|
|
475
|
+
enableTrackingForMe: vi.fn(),
|
|
476
|
+
isTrackingEnabled: vi.fn(() => true),
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
|
480
|
+
<FathomProvider
|
|
481
|
+
client={mockClient}
|
|
482
|
+
defaultEventOptions={{ id: 'parent-id' }}
|
|
483
|
+
>
|
|
484
|
+
<FathomProvider>{children}</FathomProvider>
|
|
485
|
+
</FathomProvider>
|
|
486
|
+
)
|
|
487
|
+
|
|
488
|
+
const { result } = renderHook(() => useFathom(), { wrapper })
|
|
489
|
+
|
|
490
|
+
result.current.trackEvent?.('test-event')
|
|
491
|
+
|
|
492
|
+
expect(mockClient.trackEvent).toHaveBeenCalledWith('test-event', {
|
|
493
|
+
id: 'parent-id',
|
|
494
|
+
})
|
|
495
|
+
})
|
|
496
|
+
|
|
497
|
+
it('should compose nested providers - child overrides parent defaultEventOptions', () => {
|
|
498
|
+
const mockClient = {
|
|
499
|
+
trackEvent: vi.fn(),
|
|
500
|
+
trackPageview: vi.fn(),
|
|
501
|
+
trackGoal: vi.fn(),
|
|
502
|
+
load: vi.fn(),
|
|
503
|
+
setSite: vi.fn(),
|
|
504
|
+
blockTrackingForMe: vi.fn(),
|
|
505
|
+
enableTrackingForMe: vi.fn(),
|
|
506
|
+
isTrackingEnabled: vi.fn(() => true),
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
|
510
|
+
<FathomProvider
|
|
511
|
+
client={mockClient}
|
|
512
|
+
defaultEventOptions={{ id: 'parent-id' }}
|
|
513
|
+
>
|
|
514
|
+
<FathomProvider defaultEventOptions={{ id: 'child-id' }}>
|
|
515
|
+
{children}
|
|
516
|
+
</FathomProvider>
|
|
517
|
+
</FathomProvider>
|
|
518
|
+
)
|
|
519
|
+
|
|
520
|
+
const { result } = renderHook(() => useFathom(), { wrapper })
|
|
521
|
+
|
|
522
|
+
result.current.trackEvent?.('test-event')
|
|
523
|
+
|
|
524
|
+
expect(mockClient.trackEvent).toHaveBeenCalledWith('test-event', {
|
|
525
|
+
id: 'child-id',
|
|
526
|
+
})
|
|
527
|
+
})
|
|
528
|
+
|
|
529
|
+
it('should have displayName', () => {
|
|
530
|
+
expect(FathomProvider.displayName).toBe('FathomProvider')
|
|
531
|
+
})
|
|
532
|
+
})
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import React, { useCallback, useContext, useEffect, useMemo } from 'react'
|
|
2
|
+
|
|
3
|
+
import * as Fathom from 'fathom-client'
|
|
4
|
+
import type { EventOptions, LoadOptions, PageViewOptions } from 'fathom-client'
|
|
5
|
+
|
|
6
|
+
import { FathomContext } from './FathomContext'
|
|
7
|
+
import type { FathomProviderProps } from './types'
|
|
8
|
+
|
|
9
|
+
const FathomProvider: React.FC<FathomProviderProps> = ({
|
|
10
|
+
children,
|
|
11
|
+
client: providedClient,
|
|
12
|
+
clientOptions,
|
|
13
|
+
disableDefaultTrack,
|
|
14
|
+
siteId,
|
|
15
|
+
defaultPageviewOptions: providedDefaultPageviewOptions,
|
|
16
|
+
defaultEventOptions: providedDefaultEventOptions,
|
|
17
|
+
}) => {
|
|
18
|
+
// Read parent context if it exists
|
|
19
|
+
const parentContext = useContext(FathomContext)
|
|
20
|
+
|
|
21
|
+
// Use provided client or fall back to parent client or default Fathom
|
|
22
|
+
const client = useMemo(
|
|
23
|
+
() => providedClient ?? parentContext.client ?? Fathom,
|
|
24
|
+
[providedClient, parentContext.client],
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
// Merge defaultPageviewOptions: provided > parent > undefined
|
|
28
|
+
const defaultPageviewOptions = useMemo(
|
|
29
|
+
() =>
|
|
30
|
+
providedDefaultPageviewOptions ?? parentContext.defaultPageviewOptions,
|
|
31
|
+
[providedDefaultPageviewOptions, parentContext.defaultPageviewOptions],
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
// Merge defaultEventOptions: provided > parent > undefined
|
|
35
|
+
const defaultEventOptions = useMemo(
|
|
36
|
+
() => providedDefaultEventOptions ?? parentContext.defaultEventOptions,
|
|
37
|
+
[providedDefaultEventOptions, parentContext.defaultEventOptions],
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
const blockTrackingForMe = useCallback(() => {
|
|
41
|
+
client.blockTrackingForMe()
|
|
42
|
+
}, [client])
|
|
43
|
+
|
|
44
|
+
const enableTrackingForMe = useCallback(() => {
|
|
45
|
+
client.enableTrackingForMe()
|
|
46
|
+
}, [client])
|
|
47
|
+
|
|
48
|
+
const isTrackingEnabled = useCallback(() => {
|
|
49
|
+
return client.isTrackingEnabled() ?? false
|
|
50
|
+
}, [client])
|
|
51
|
+
|
|
52
|
+
const load = useCallback(
|
|
53
|
+
(siteId: string, clientOptions?: LoadOptions) => {
|
|
54
|
+
client.load(siteId, clientOptions)
|
|
55
|
+
},
|
|
56
|
+
[client],
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
const setSite = useCallback(
|
|
60
|
+
(siteId: string) => {
|
|
61
|
+
client.setSite(siteId)
|
|
62
|
+
},
|
|
63
|
+
[client],
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
const trackEvent = useCallback(
|
|
67
|
+
(category: string, options?: EventOptions) => {
|
|
68
|
+
client.trackEvent(category, {
|
|
69
|
+
...defaultEventOptions,
|
|
70
|
+
...options,
|
|
71
|
+
})
|
|
72
|
+
},
|
|
73
|
+
[client, defaultEventOptions],
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
const trackPageview = useCallback(
|
|
77
|
+
(options?: PageViewOptions) => {
|
|
78
|
+
client.trackPageview({
|
|
79
|
+
...defaultPageviewOptions,
|
|
80
|
+
...options,
|
|
81
|
+
})
|
|
82
|
+
},
|
|
83
|
+
[client, defaultPageviewOptions],
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
const trackGoal = useCallback(
|
|
87
|
+
(code: string, cents: number) => {
|
|
88
|
+
client.trackGoal(code, cents)
|
|
89
|
+
},
|
|
90
|
+
[client],
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
useEffect(() => {
|
|
94
|
+
if (siteId !== undefined) {
|
|
95
|
+
load(siteId, clientOptions)
|
|
96
|
+
}
|
|
97
|
+
}, [clientOptions, load, siteId])
|
|
98
|
+
|
|
99
|
+
return (
|
|
100
|
+
<FathomContext.Provider
|
|
101
|
+
value={{
|
|
102
|
+
blockTrackingForMe,
|
|
103
|
+
enableTrackingForMe,
|
|
104
|
+
isTrackingEnabled,
|
|
105
|
+
load,
|
|
106
|
+
setSite,
|
|
107
|
+
trackEvent,
|
|
108
|
+
trackGoal,
|
|
109
|
+
trackPageview,
|
|
110
|
+
client,
|
|
111
|
+
defaultPageviewOptions,
|
|
112
|
+
defaultEventOptions,
|
|
113
|
+
}}
|
|
114
|
+
>
|
|
115
|
+
{children}
|
|
116
|
+
</FathomContext.Provider>
|
|
117
|
+
)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
FathomProvider.displayName = 'FathomProvider'
|
|
121
|
+
|
|
122
|
+
export { FathomProvider }
|