@envive-ai/react-hooks 0.3.19-alpha-marlo-4 → 0.3.19-alpha-marlo-5
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/package.json +1 -1
- package/src/contexts/amplitudeContext/__tests__/amplitudeContext.test.tsx +13 -8
- package/src/contexts/amplitudeContext/amplitudeContext.tsx +7 -13
- package/src/services/amplitudeService/__tests__/amplitudeService.test.ts +56 -6
- package/src/services/amplitudeService/amplitudeService.ts +14 -4
package/package.json
CHANGED
|
@@ -332,7 +332,7 @@ describe('AmplitudeProvider - React Context Integration', () => {
|
|
|
332
332
|
});
|
|
333
333
|
});
|
|
334
334
|
|
|
335
|
-
it('should
|
|
335
|
+
it('should render children with isReady=false when amplitude key is missing', async () => {
|
|
336
336
|
// Create a provider without required dependencies
|
|
337
337
|
const IncompleteWrapper: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
|
338
338
|
return (
|
|
@@ -357,13 +357,14 @@ describe('AmplitudeProvider - React Context Integration', () => {
|
|
|
357
357
|
|
|
358
358
|
render(
|
|
359
359
|
<IncompleteWrapper>
|
|
360
|
-
<
|
|
360
|
+
<MockAmplitudeComponent />
|
|
361
361
|
</IncompleteWrapper>,
|
|
362
362
|
);
|
|
363
363
|
|
|
364
|
-
// AmplitudeProvider
|
|
364
|
+
// AmplitudeProvider renders children even when not ready — isReady reflects tracking unavailability
|
|
365
365
|
await waitFor(() => {
|
|
366
|
-
expect(screen.getByTestId('
|
|
366
|
+
expect(screen.getByTestId('amplitude-component')).toBeInTheDocument();
|
|
367
|
+
expect(screen.getByTestId('is-ready').textContent).toBe('false');
|
|
367
368
|
});
|
|
368
369
|
});
|
|
369
370
|
});
|
|
@@ -446,11 +447,15 @@ describe('AmplitudeProvider - React Context Integration', () => {
|
|
|
446
447
|
});
|
|
447
448
|
|
|
448
449
|
describe('useAmplitude hook', () => {
|
|
449
|
-
it('should
|
|
450
|
-
|
|
450
|
+
it('should throw error when used outside provider', () => {
|
|
451
|
+
// Suppress console.error for this test
|
|
452
|
+
const consoleError = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
453
|
+
|
|
454
|
+
expect(() => {
|
|
455
|
+
render(<MockAmplitudeComponent />);
|
|
456
|
+
}).toThrow('useAmplitude must be used within AmplitudeProvider');
|
|
451
457
|
|
|
452
|
-
|
|
453
|
-
expect(screen.getByTestId('is-ready').textContent).toBe('false');
|
|
458
|
+
consoleError.mockRestore();
|
|
454
459
|
});
|
|
455
460
|
|
|
456
461
|
it('should provide amplitude context when used within provider', async () => {
|
|
@@ -26,13 +26,7 @@ interface AmplitudeContextType {
|
|
|
26
26
|
setSupplementalDefaultProps: (props: Record<string, unknown>) => void;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
const
|
|
30
|
-
trackEvent: async () => {},
|
|
31
|
-
isReady: false,
|
|
32
|
-
setSupplementalDefaultProps: () => {},
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
const AmplitudeContext = createContext<AmplitudeContextType>(noOpAmplitudeContext);
|
|
29
|
+
const AmplitudeContext = createContext<AmplitudeContextType | null>(null);
|
|
36
30
|
|
|
37
31
|
export const AmplitudeProvider: React.FC<{
|
|
38
32
|
externalAmplitudeService?: AmplitudeService;
|
|
@@ -124,13 +118,13 @@ export const AmplitudeProvider: React.FC<{
|
|
|
124
118
|
[service, isReady],
|
|
125
119
|
);
|
|
126
120
|
|
|
127
|
-
return
|
|
128
|
-
<AmplitudeContext.Provider value={isReady ? value : noOpAmplitudeContext}>
|
|
129
|
-
{children}
|
|
130
|
-
</AmplitudeContext.Provider>
|
|
131
|
-
);
|
|
121
|
+
return <AmplitudeContext.Provider value={value}>{children}</AmplitudeContext.Provider>;
|
|
132
122
|
};
|
|
133
123
|
|
|
134
124
|
export const useAmplitude = () => {
|
|
135
|
-
|
|
125
|
+
const context = useContext(AmplitudeContext);
|
|
126
|
+
if (!context) {
|
|
127
|
+
throw new Error('useAmplitude must be used within AmplitudeProvider');
|
|
128
|
+
}
|
|
129
|
+
return context;
|
|
136
130
|
};
|
|
@@ -85,10 +85,12 @@ describe('AmplitudeService', () => {
|
|
|
85
85
|
});
|
|
86
86
|
});
|
|
87
87
|
|
|
88
|
+
const validApiKey = 'abcdef1234567890abcdef1234567890';
|
|
89
|
+
|
|
88
90
|
const createService = (overrides?: Partial<AmplitudeServiceConfig>) => {
|
|
89
91
|
return new AmplitudeService({
|
|
90
92
|
userId: 'test-user-id',
|
|
91
|
-
amplitudeApiKey:
|
|
93
|
+
amplitudeApiKey: validApiKey,
|
|
92
94
|
dataResidency: 'US',
|
|
93
95
|
env: 'test',
|
|
94
96
|
orgId: 'test-org-id',
|
|
@@ -107,7 +109,7 @@ describe('AmplitudeService', () => {
|
|
|
107
109
|
createService();
|
|
108
110
|
|
|
109
111
|
expect(mockInit).toHaveBeenCalledWith(
|
|
110
|
-
|
|
112
|
+
validApiKey,
|
|
111
113
|
'test-user-id',
|
|
112
114
|
expect.objectContaining({
|
|
113
115
|
serverZone: 'US',
|
|
@@ -121,7 +123,7 @@ describe('AmplitudeService', () => {
|
|
|
121
123
|
it('should not be ready if userId is missing', () => {
|
|
122
124
|
const service = new AmplitudeService({
|
|
123
125
|
userId: '',
|
|
124
|
-
amplitudeApiKey:
|
|
126
|
+
amplitudeApiKey: validApiKey,
|
|
125
127
|
dataResidency: 'US',
|
|
126
128
|
env: 'test',
|
|
127
129
|
orgId: 'test-org-id',
|
|
@@ -157,7 +159,7 @@ describe('AmplitudeService', () => {
|
|
|
157
159
|
it('should not be ready if featureFlagService is missing', () => {
|
|
158
160
|
const service = new AmplitudeService({
|
|
159
161
|
userId: 'test-user-id',
|
|
160
|
-
amplitudeApiKey:
|
|
162
|
+
amplitudeApiKey: validApiKey,
|
|
161
163
|
dataResidency: 'US',
|
|
162
164
|
env: 'test',
|
|
163
165
|
orgId: 'test-org-id',
|
|
@@ -177,6 +179,54 @@ describe('AmplitudeService', () => {
|
|
|
177
179
|
|
|
178
180
|
expect(service.isReady).toBe(true);
|
|
179
181
|
});
|
|
182
|
+
|
|
183
|
+
it('should not set isMockMode when all required config is provided', () => {
|
|
184
|
+
const service = createService();
|
|
185
|
+
|
|
186
|
+
expect(service.isMockMode).toBe(false);
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
describe('Mock mode', () => {
|
|
191
|
+
it('should set isMockMode and not initialize the client when API key is not a valid hex string', () => {
|
|
192
|
+
const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
|
|
193
|
+
|
|
194
|
+
const service = createService({ amplitudeApiKey: 'not-a-valid-key' });
|
|
195
|
+
|
|
196
|
+
expect(service.isReady).toBe(false);
|
|
197
|
+
expect(service.isMockMode).toBe(true);
|
|
198
|
+
expect(mockInit).not.toHaveBeenCalled();
|
|
199
|
+
|
|
200
|
+
consoleSpy.mockRestore();
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
it('should print a console.log message when a mock API key is detected', () => {
|
|
204
|
+
const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
|
|
205
|
+
|
|
206
|
+
createService({ amplitudeApiKey: 'not-a-valid-key' });
|
|
207
|
+
|
|
208
|
+
expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('Mock API key detected'));
|
|
209
|
+
|
|
210
|
+
consoleSpy.mockRestore();
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
it('should not set isMockMode when API key is empty — service is simply not ready', () => {
|
|
214
|
+
const service = createService({ amplitudeApiKey: '' });
|
|
215
|
+
|
|
216
|
+
expect(service.isReady).toBe(false);
|
|
217
|
+
expect(service.isMockMode).toBe(false);
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
it('should not call the Amplitude client when trackEvent is called in mock mode', async () => {
|
|
221
|
+
const service = createService({ amplitudeApiKey: 'not-a-valid-key' });
|
|
222
|
+
|
|
223
|
+
await service.trackEvent({
|
|
224
|
+
eventName: SpiffyMetricsEventName.ChatUserMessageInput,
|
|
225
|
+
eventProps: { message: 'test' },
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
expect(mockTrack).not.toHaveBeenCalled();
|
|
229
|
+
});
|
|
180
230
|
});
|
|
181
231
|
|
|
182
232
|
describe('trackEvent', () => {
|
|
@@ -269,7 +319,7 @@ describe('AmplitudeService', () => {
|
|
|
269
319
|
it('should not track if client is not initialized', async () => {
|
|
270
320
|
const service = new AmplitudeService({
|
|
271
321
|
userId: '',
|
|
272
|
-
amplitudeApiKey:
|
|
322
|
+
amplitudeApiKey: validApiKey,
|
|
273
323
|
dataResidency: 'US',
|
|
274
324
|
env: 'test',
|
|
275
325
|
orgId: 'test-org-id',
|
|
@@ -419,7 +469,7 @@ describe('AmplitudeService', () => {
|
|
|
419
469
|
it('should fall back to window.localStorage when getLocalStorageItem is not provided', async () => {
|
|
420
470
|
const service = new AmplitudeService({
|
|
421
471
|
userId: 'test-user-id',
|
|
422
|
-
amplitudeApiKey:
|
|
472
|
+
amplitudeApiKey: validApiKey,
|
|
423
473
|
dataResidency: 'US',
|
|
424
474
|
env: 'test',
|
|
425
475
|
orgId: 'test-org-id',
|
|
@@ -51,6 +51,8 @@ export class AmplitudeService {
|
|
|
51
51
|
|
|
52
52
|
private config: AmplitudeServiceConfig;
|
|
53
53
|
|
|
54
|
+
private _isMockMode: boolean = false;
|
|
55
|
+
|
|
54
56
|
constructor(config: AmplitudeServiceConfig) {
|
|
55
57
|
this.config = config;
|
|
56
58
|
this.initialize();
|
|
@@ -65,6 +67,10 @@ export class AmplitudeService {
|
|
|
65
67
|
);
|
|
66
68
|
}
|
|
67
69
|
|
|
70
|
+
get isMockMode(): boolean {
|
|
71
|
+
return this._isMockMode;
|
|
72
|
+
}
|
|
73
|
+
|
|
68
74
|
private getLocalStorageItem(key: string): string | null {
|
|
69
75
|
if (this.config.getLocalStorageItem) {
|
|
70
76
|
return this.config.getLocalStorageItem(key);
|
|
@@ -213,8 +219,8 @@ export class AmplitudeService {
|
|
|
213
219
|
return enrichment;
|
|
214
220
|
}
|
|
215
221
|
|
|
216
|
-
static
|
|
217
|
-
return
|
|
222
|
+
private static isValidAmplitudeApiKey(apiKey: string): boolean {
|
|
223
|
+
return /^[0-9a-f]{32}$/i.test(apiKey);
|
|
218
224
|
}
|
|
219
225
|
|
|
220
226
|
private initialize(): void {
|
|
@@ -232,8 +238,12 @@ export class AmplitudeService {
|
|
|
232
238
|
return;
|
|
233
239
|
}
|
|
234
240
|
|
|
235
|
-
if (AmplitudeService.
|
|
236
|
-
|
|
241
|
+
if (!AmplitudeService.isValidAmplitudeApiKey(this.config.amplitudeApiKey)) {
|
|
242
|
+
this._isMockMode = true;
|
|
243
|
+
// eslint-disable-next-line no-console
|
|
244
|
+
console.log(
|
|
245
|
+
'[AmplitudeService] Mock API key detected — running in mock mode, no events will be sent to Amplitude.',
|
|
246
|
+
);
|
|
237
247
|
return;
|
|
238
248
|
}
|
|
239
249
|
|