@crimson-education/browser-logger 5.0.1-beta.3 → 5.0.1
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/README.md +127 -73
- package/lib/index.d.ts +1 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +47 -19
- package/lib/index.js.map +1 -1
- package/lib/logger/consoleTransport.d.ts +1 -1
- package/lib/logger/consoleTransport.d.ts.map +1 -1
- package/lib/logger/consoleTransport.js +22 -16
- package/lib/logger/consoleTransport.js.map +1 -1
- package/lib/logger/datadogTransport.d.ts +1 -1
- package/lib/logger/datadogTransport.d.ts.map +1 -1
- package/lib/logger/datadogTransport.js +8 -4
- package/lib/logger/datadogTransport.js.map +1 -1
- package/lib/logger/index.js +49 -29
- package/lib/logger/index.js.map +1 -1
- package/lib/logger/index.test.js +18 -16
- package/lib/logger/index.test.js.map +1 -1
- package/lib/logger/utils.js +9 -4
- package/lib/logger/utils.js.map +1 -1
- package/lib/reporters/amplifyReporter.d.ts +86 -0
- package/lib/reporters/amplifyReporter.d.ts.map +1 -0
- package/lib/reporters/amplifyReporter.js +225 -0
- package/lib/reporters/amplifyReporter.js.map +1 -0
- package/lib/reporters/amplifyReporter.test.d.ts +2 -0
- package/lib/reporters/amplifyReporter.test.d.ts.map +1 -0
- package/lib/reporters/amplifyReporter.test.js +51 -0
- package/lib/reporters/amplifyReporter.test.js.map +1 -0
- package/lib/reporters/datadogReporter.d.ts +1 -13
- package/lib/reporters/datadogReporter.d.ts.map +1 -1
- package/lib/reporters/datadogReporter.js +49 -122
- package/lib/reporters/datadogReporter.js.map +1 -1
- package/lib/reporters/gtmReporter.d.ts +1 -1
- package/lib/reporters/gtmReporter.d.ts.map +1 -1
- package/lib/reporters/gtmReporter.js +5 -1
- package/lib/reporters/gtmReporter.js.map +1 -1
- package/lib/reporters/index.js +71 -46
- package/lib/reporters/index.js.map +1 -1
- package/lib/reporters/logReporter.js +34 -24
- package/lib/reporters/logReporter.js.map +1 -1
- package/lib/reporters/posthogReporter.d.ts +51 -0
- package/lib/reporters/posthogReporter.d.ts.map +1 -0
- package/lib/reporters/posthogReporter.js +254 -0
- package/lib/reporters/posthogReporter.js.map +1 -0
- package/lib/reporters/posthogReporter.test.d.ts +2 -0
- package/lib/reporters/posthogReporter.test.d.ts.map +1 -0
- package/lib/reporters/posthogReporter.test.js +197 -0
- package/lib/reporters/posthogReporter.test.js.map +1 -0
- package/lib/types/index.js +18 -2
- package/lib/types/index.js.map +1 -1
- package/lib/types/logger.d.ts +3 -3
- package/lib/types/logger.d.ts.map +1 -1
- package/lib/types/logger.js +5 -2
- package/lib/types/logger.js.map +1 -1
- package/lib/types/reporter.d.ts +20 -6
- package/lib/types/reporter.d.ts.map +1 -1
- package/lib/types/reporter.js +2 -1
- package/lib/utils.js +5 -1
- package/lib/utils.js.map +1 -1
- package/lib/utils.test.js +4 -2
- package/lib/utils.test.js.map +1 -1
- package/package.json +17 -11
- package/src/index.ts +11 -0
- package/src/reporters/amplifyReporter.test.ts +61 -0
- package/src/reporters/amplifyReporter.ts +344 -0
- package/src/reporters/datadogReporter.ts +22 -133
- package/src/reporters/posthogReporter.test.ts +244 -0
- package/src/reporters/posthogReporter.ts +374 -0
- package/src/types/reporter.ts +16 -0
|
@@ -15,95 +15,6 @@ import { datadogRum, DefaultPrivacyLevel, RumInitConfiguration } from '@datadog/
|
|
|
15
15
|
import { logTransports } from '../logger';
|
|
16
16
|
import { DatadogLogTransportConfig, datadogTransport } from '../logger/datadogTransport';
|
|
17
17
|
|
|
18
|
-
// User frustration detection for Datadog Gen2
|
|
19
|
-
class DatadogFrustrationDetector {
|
|
20
|
-
private config: DatadogReporterConfig;
|
|
21
|
-
private frustrationEvents: Array<{ type: string; timestamp: number }> = [];
|
|
22
|
-
private readonly FRUSTRATION_THRESHOLD = 3; // Number of events to trigger frustration
|
|
23
|
-
private readonly FRUSTRATION_WINDOW = 5000; // Time window in ms
|
|
24
|
-
|
|
25
|
-
constructor(config: DatadogReporterConfig) {
|
|
26
|
-
this.config = config;
|
|
27
|
-
if (config.trackFrustrations) {
|
|
28
|
-
this.setupFrustrationDetection();
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
private setupFrustrationDetection() {
|
|
33
|
-
// Track rapid clicks (potential frustration)
|
|
34
|
-
let clickCount = 0;
|
|
35
|
-
let lastClickTime = 0;
|
|
36
|
-
|
|
37
|
-
document.addEventListener('click', () => {
|
|
38
|
-
const now = Date.now();
|
|
39
|
-
if (now - lastClickTime < 1000) {
|
|
40
|
-
// Rapid clicks within 1 second
|
|
41
|
-
clickCount++;
|
|
42
|
-
if (clickCount >= 3) {
|
|
43
|
-
this.recordFrustrationEvent('rapid_clicks');
|
|
44
|
-
clickCount = 0;
|
|
45
|
-
}
|
|
46
|
-
} else {
|
|
47
|
-
clickCount = 1;
|
|
48
|
-
}
|
|
49
|
-
lastClickTime = now;
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
// Track form errors (potential frustration)
|
|
53
|
-
document.addEventListener('invalid', () => {
|
|
54
|
-
this.recordFrustrationEvent('form_validation_error');
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
// Track 404 errors (potential frustration)
|
|
58
|
-
window.addEventListener('error', (event) => {
|
|
59
|
-
if (event.message.includes('404') || event.message.includes('Not Found')) {
|
|
60
|
-
this.recordFrustrationEvent('page_not_found');
|
|
61
|
-
}
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
// Track network errors (potential frustration)
|
|
65
|
-
window.addEventListener('unhandledrejection', (event) => {
|
|
66
|
-
if (event.reason && typeof event.reason === 'object' && 'status' in event.reason) {
|
|
67
|
-
const status = (event.reason as any).status;
|
|
68
|
-
if (status >= 400) {
|
|
69
|
-
this.recordFrustrationEvent('network_error');
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
private recordFrustrationEvent(type: string) {
|
|
76
|
-
const now = Date.now();
|
|
77
|
-
this.frustrationEvents.push({ type, timestamp: now });
|
|
78
|
-
|
|
79
|
-
// Clean old events outside the window
|
|
80
|
-
this.frustrationEvents = this.frustrationEvents.filter((event) => now - event.timestamp < this.FRUSTRATION_WINDOW);
|
|
81
|
-
|
|
82
|
-
// Check if we have enough events to trigger frustration
|
|
83
|
-
if (this.frustrationEvents.length >= this.FRUSTRATION_THRESHOLD) {
|
|
84
|
-
this.triggerFrustrationDetection();
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
private triggerFrustrationDetection() {
|
|
89
|
-
const frustrationData = {
|
|
90
|
-
frustrationType: 'user_frustration_detected',
|
|
91
|
-
frustrationEvents: this.frustrationEvents.map((e) => e.type),
|
|
92
|
-
frustrationCount: this.frustrationEvents.length,
|
|
93
|
-
pageUrl: window.location.href,
|
|
94
|
-
timestamp: new Date().toISOString(),
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
// Send frustration event to Datadog
|
|
98
|
-
if (typeof datadogRum !== 'undefined') {
|
|
99
|
-
datadogRum.addAction('User Frustration Detected', frustrationData);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// Clear events after reporting
|
|
103
|
-
this.frustrationEvents = [];
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
18
|
export interface DatadogReporterConfig extends ReporterConfigBase {
|
|
108
19
|
/** The RUM application ID. */
|
|
109
20
|
applicationId: string;
|
|
@@ -125,17 +36,11 @@ export interface DatadogReporterConfig extends ReporterConfigBase {
|
|
|
125
36
|
* @deprecated Prefer using the dedicated `logSampleRate`, `rumSampleRate`, and `replaySampleRate` options.
|
|
126
37
|
*/
|
|
127
38
|
sampleRate?: number;
|
|
128
|
-
|
|
129
|
-
sessionSampleRate?: number;
|
|
130
|
-
sessionReplaySampleRate?: number;
|
|
131
|
-
|
|
132
39
|
/**
|
|
133
40
|
* Sampling rate for browser log collection. Defaults to 100, meaning every log message is sent to Datadog.
|
|
134
41
|
*/
|
|
135
42
|
logSampleRate?: number;
|
|
136
|
-
|
|
137
43
|
/**
|
|
138
|
-
* @deprecated Prefer using `sessionSampleRate`.
|
|
139
44
|
* Sampling rate for RUM session collection. Defaults to 100, meaning every browser session is tracked.
|
|
140
45
|
*
|
|
141
46
|
* RUM can be used to track things like core web vitals.
|
|
@@ -143,9 +48,7 @@ export interface DatadogReporterConfig extends ReporterConfigBase {
|
|
|
143
48
|
* @see https://www.datadoghq.com/product/real-user-monitoring/
|
|
144
49
|
*/
|
|
145
50
|
rumSampleRate?: number;
|
|
146
|
-
|
|
147
51
|
/**
|
|
148
|
-
* @deprecated Prefer using `sessionReplaySampleRate`.
|
|
149
52
|
* Applied after the RUM sample rate, and controls the percentage of sessions tracked as Browser RUM & Session Replay.
|
|
150
53
|
* It defaults to 100, so every session is tracked as Browser RUM & Session Replay by default.
|
|
151
54
|
*
|
|
@@ -157,21 +60,11 @@ export interface DatadogReporterConfig extends ReporterConfigBase {
|
|
|
157
60
|
* Use a secure session cookie. This disables RUM events sent on insecure (non-HTTPS) connections.
|
|
158
61
|
*/
|
|
159
62
|
useSecureSessionCookie?: boolean;
|
|
160
|
-
|
|
161
63
|
/**
|
|
162
|
-
* @deprecated Prefer using `usePartitionedCrossSiteSessionCookie`.
|
|
163
64
|
* Use a secure cross-site session cookie.
|
|
164
65
|
* This allows the RUM Browser SDK to run when the site is loaded from another one (iframe). Implies `useSecureSessionCookie`
|
|
165
66
|
*/
|
|
166
67
|
useCrossSiteSessionCookie?: boolean;
|
|
167
|
-
|
|
168
|
-
/**
|
|
169
|
-
* Use a partitioned secure cross-site session cookie. This allows the RUM Browser SDK to run when the site is loaded from another one (iframe). Implies `useSecureSessionCookie`.
|
|
170
|
-
* Important: If you are using the RUM and Logs Browser SDKs, this option must be configured with identical values
|
|
171
|
-
* @default false
|
|
172
|
-
*/
|
|
173
|
-
usePartitionedCrossSiteSessionCookie?: boolean;
|
|
174
|
-
|
|
175
68
|
/**
|
|
176
69
|
* Preserve the session across subdomains for the same site.
|
|
177
70
|
*/
|
|
@@ -224,8 +117,7 @@ export interface DatadogReporterConfig extends ReporterConfigBase {
|
|
|
224
117
|
/**
|
|
225
118
|
* A list of request origins used to inject tracing headers, to be able to connect RUM and backend tracing.
|
|
226
119
|
*/
|
|
227
|
-
|
|
228
|
-
allowedTracingUrls?: (string | RegExp)[];
|
|
120
|
+
allowedTracingOrigins?: (string | RegExp)[];
|
|
229
121
|
/**
|
|
230
122
|
* Enables action tracking for user interactions. This enables the Heatmap tab within Datadog
|
|
231
123
|
*/
|
|
@@ -248,23 +140,23 @@ export function datadogReporter(info: ServiceInfo, config: DatadogReporterConfig
|
|
|
248
140
|
const enableLogTransport = config.logTransport !== false;
|
|
249
141
|
|
|
250
142
|
// Only init datadog logs if something is using it.
|
|
251
|
-
if (forwardErrorsToLogs
|
|
143
|
+
if (forwardErrorsToLogs !== true && enableLogTransport !== true) {
|
|
252
144
|
datadogLogs.init({
|
|
253
|
-
site: config.site
|
|
145
|
+
site: config.site,
|
|
146
|
+
proxyUrl: config.proxyUrl,
|
|
254
147
|
clientToken: config.clientToken,
|
|
255
148
|
service: info.service,
|
|
256
149
|
env: info.environment,
|
|
257
150
|
version: config.version ?? info.version,
|
|
258
151
|
|
|
152
|
+
sampleRate: config.logSampleRate ?? config.sampleRate ?? 100,
|
|
259
153
|
beforeSend: config.beforeLogsSend,
|
|
260
154
|
|
|
261
|
-
usePartitionedCrossSiteSessionCookie: config.usePartitionedCrossSiteSessionCookie,
|
|
262
155
|
useSecureSessionCookie: config.useSecureSessionCookie ?? !isLocalhost,
|
|
263
|
-
|
|
156
|
+
useCrossSiteSessionCookie: config.useCrossSiteSessionCookie ?? false,
|
|
264
157
|
trackSessionAcrossSubdomains: config.trackSessionAcrossSubdomains,
|
|
265
158
|
|
|
266
159
|
forwardErrorsToLogs,
|
|
267
|
-
allowedTrackingOrigins: config.allowedTrackingOrigins,
|
|
268
160
|
});
|
|
269
161
|
datadogLogs.logger.setHandler(HandlerType.http);
|
|
270
162
|
}
|
|
@@ -276,37 +168,34 @@ export function datadogReporter(info: ServiceInfo, config: DatadogReporterConfig
|
|
|
276
168
|
|
|
277
169
|
datadogRum.init({
|
|
278
170
|
enableExperimentalFeatures: ['feature_flags'],
|
|
279
|
-
site: config.site
|
|
171
|
+
site: config.site,
|
|
172
|
+
proxyUrl: config.proxyUrl,
|
|
280
173
|
clientToken: config.clientToken,
|
|
281
174
|
applicationId: config.applicationId,
|
|
282
175
|
service: info.service,
|
|
283
176
|
env: info.environment,
|
|
284
177
|
version: config.version ?? info.version,
|
|
285
178
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
sessionSampleRate: config.sessionSampleRate ?? config.rumSampleRate ?? config.sampleRate ?? 100,
|
|
289
|
-
sessionReplaySampleRate: config.sessionReplaySampleRate?? config.replaySampleRate ?? 100,
|
|
290
|
-
|
|
291
|
-
// Track interactions (Note: trackFrustrations is not available in Datadog v6)
|
|
292
|
-
trackUserInteractions: config.trackUserInteractions ?? config.trackInteractions ?? false,
|
|
179
|
+
sampleRate: config.rumSampleRate ?? config.sampleRate ?? 100,
|
|
180
|
+
replaySampleRate: config.replaySampleRate ?? 100,
|
|
293
181
|
|
|
294
182
|
useSecureSessionCookie: config.useSecureSessionCookie ?? !isLocalhost,
|
|
295
|
-
|
|
183
|
+
useCrossSiteSessionCookie: config.useCrossSiteSessionCookie ?? false,
|
|
296
184
|
trackSessionAcrossSubdomains: config.trackSessionAcrossSubdomains,
|
|
185
|
+
|
|
186
|
+
trackInteractions: config.trackInteractions ?? false,
|
|
187
|
+
trackFrustrations: config.trackFrustrations ?? false,
|
|
297
188
|
trackViewsManually: config.trackViewsManually ?? false,
|
|
298
189
|
actionNameAttribute: config.actionNameAttribute ?? 'data-analytics-name',
|
|
299
190
|
beforeSend: config.beforeSend,
|
|
300
191
|
|
|
301
192
|
defaultPrivacyLevel: config.defaultPrivacyLevel ?? 'mask-user-input',
|
|
302
|
-
|
|
303
|
-
|
|
193
|
+
allowedTracingOrigins: config.allowedTracingOrigins,
|
|
194
|
+
trackUserInteractions: config.trackUserInteractions ?? false,
|
|
195
|
+
|
|
304
196
|
excludedActivityUrls: config.excludedActivityUrls,
|
|
305
197
|
});
|
|
306
198
|
|
|
307
|
-
// Initialize frustration detector
|
|
308
|
-
new DatadogFrustrationDetector(config);
|
|
309
|
-
|
|
310
199
|
const reporter: IReporter = {
|
|
311
200
|
trackEvent: function (event: ReporterEvent): void {
|
|
312
201
|
datadogRum.addAction(event.message, {
|
|
@@ -325,16 +214,16 @@ export function datadogReporter(info: ServiceInfo, config: DatadogReporterConfig
|
|
|
325
214
|
addMetadata: function (metadata: Metadata): void {
|
|
326
215
|
for (const [key, value] of Object.entries(metadata)) {
|
|
327
216
|
if (value !== null) {
|
|
328
|
-
datadogRum.
|
|
217
|
+
datadogRum.addRumGlobalContext(key, value);
|
|
329
218
|
|
|
330
219
|
// Note, this will add duplicate context data in logs.
|
|
331
220
|
// But this is valuable for logs ingested outside of the browser logger.
|
|
332
|
-
datadogLogs.
|
|
221
|
+
datadogLogs.addLoggerGlobalContext(key, value);
|
|
333
222
|
} else {
|
|
334
|
-
datadogRum.
|
|
223
|
+
datadogRum.removeRumGlobalContext(key);
|
|
335
224
|
|
|
336
225
|
// But this is valuable for logs ingested outside of the browser logger.
|
|
337
|
-
datadogLogs.
|
|
226
|
+
datadogLogs.removeLoggerGlobalContext(key);
|
|
338
227
|
}
|
|
339
228
|
}
|
|
340
229
|
},
|
|
@@ -347,7 +236,7 @@ export function datadogReporter(info: ServiceInfo, config: DatadogReporterConfig
|
|
|
347
236
|
name: user.name ?? user.email,
|
|
348
237
|
});
|
|
349
238
|
} else {
|
|
350
|
-
datadogRum.
|
|
239
|
+
datadogRum.removeUser();
|
|
351
240
|
}
|
|
352
241
|
},
|
|
353
242
|
setRouteName: function (routeName: string): void {
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
jest.mock('posthog-js', () => ({
|
|
2
|
+
__esModule: true,
|
|
3
|
+
default: {
|
|
4
|
+
init: jest.fn(),
|
|
5
|
+
capture: jest.fn(),
|
|
6
|
+
captureException: jest.fn(),
|
|
7
|
+
getPageViewId: jest.fn(() => 'view-123'),
|
|
8
|
+
get_session_id: jest.fn(() => 'session-123'),
|
|
9
|
+
identify: jest.fn(),
|
|
10
|
+
register: jest.fn(),
|
|
11
|
+
reset: jest.fn(),
|
|
12
|
+
startSessionRecording: jest.fn(),
|
|
13
|
+
stopSessionRecording: jest.fn(),
|
|
14
|
+
unregister: jest.fn(),
|
|
15
|
+
},
|
|
16
|
+
}));
|
|
17
|
+
|
|
18
|
+
import posthog from 'posthog-js';
|
|
19
|
+
import { LogLevel } from '../types';
|
|
20
|
+
import { normalizeProperties, posthogReporter } from './posthogReporter';
|
|
21
|
+
|
|
22
|
+
const posthogMock = posthog as unknown as {
|
|
23
|
+
init: jest.Mock;
|
|
24
|
+
capture: jest.Mock;
|
|
25
|
+
captureException: jest.Mock;
|
|
26
|
+
getPageViewId: jest.Mock;
|
|
27
|
+
get_session_id: jest.Mock;
|
|
28
|
+
identify: jest.Mock;
|
|
29
|
+
register: jest.Mock;
|
|
30
|
+
reset: jest.Mock;
|
|
31
|
+
startSessionRecording: jest.Mock;
|
|
32
|
+
stopSessionRecording: jest.Mock;
|
|
33
|
+
unregister: jest.Mock;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
describe('posthogReporter', () => {
|
|
37
|
+
beforeEach(() => {
|
|
38
|
+
jest.clearAllMocks();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('normalizes metadata into PostHog-safe properties', () => {
|
|
42
|
+
const normalized = normalizeProperties({
|
|
43
|
+
a: 1,
|
|
44
|
+
b: true,
|
|
45
|
+
c: 'value',
|
|
46
|
+
d: undefined,
|
|
47
|
+
e: null,
|
|
48
|
+
f: new Date('2026-07-03T00:00:00.000Z'),
|
|
49
|
+
g: new Error('boom'),
|
|
50
|
+
h: {
|
|
51
|
+
nested: 'yes',
|
|
52
|
+
},
|
|
53
|
+
i: [1, undefined, 'x'],
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
expect(normalized).toEqual({
|
|
57
|
+
a: 1,
|
|
58
|
+
b: true,
|
|
59
|
+
c: 'value',
|
|
60
|
+
f: '2026-07-03T00:00:00.000Z',
|
|
61
|
+
g: {
|
|
62
|
+
message: 'boom',
|
|
63
|
+
name: 'Error',
|
|
64
|
+
stack: expect.any(String),
|
|
65
|
+
},
|
|
66
|
+
h: {
|
|
67
|
+
nested: 'yes',
|
|
68
|
+
},
|
|
69
|
+
i: [1, 'x'],
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('maps browser-logger APIs onto PostHog', () => {
|
|
74
|
+
const reporter = posthogReporter(
|
|
75
|
+
{
|
|
76
|
+
service: 'crimson-app-frontend',
|
|
77
|
+
application: 'crimson-app',
|
|
78
|
+
environment: 'production',
|
|
79
|
+
version: '1.2.3',
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
apiKey: 'ph_key',
|
|
83
|
+
apiHost: 'https://eu.i.posthog.com',
|
|
84
|
+
replaySampleRate: 0,
|
|
85
|
+
},
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
expect(posthogMock.init).toHaveBeenCalledWith(
|
|
89
|
+
'ph_key',
|
|
90
|
+
expect.objectContaining({
|
|
91
|
+
api_host: 'https://eu.i.posthog.com',
|
|
92
|
+
before_send: expect.any(Function),
|
|
93
|
+
capture_pageview: 'history_change',
|
|
94
|
+
disable_session_recording: true,
|
|
95
|
+
person_profiles: 'identified_only',
|
|
96
|
+
}),
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
reporter.addMetadata({
|
|
100
|
+
namespace: 'production',
|
|
101
|
+
clearMe: null,
|
|
102
|
+
});
|
|
103
|
+
expect(posthogMock.unregister).toHaveBeenCalledWith('clearMe');
|
|
104
|
+
expect(posthogMock.register).toHaveBeenCalledWith({
|
|
105
|
+
namespace: 'production',
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
reporter.trackEvent({
|
|
109
|
+
message: 'message-sent',
|
|
110
|
+
level: LogLevel.Info,
|
|
111
|
+
metadata: {
|
|
112
|
+
userId: '123',
|
|
113
|
+
},
|
|
114
|
+
metrics: {
|
|
115
|
+
durationMs: 42,
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
expect(posthogMock.capture).toHaveBeenCalledWith('message-sent', {
|
|
119
|
+
message: 'message-sent',
|
|
120
|
+
level: LogLevel.Info,
|
|
121
|
+
userId: '123',
|
|
122
|
+
durationMs: 42,
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
reporter.setUser({
|
|
126
|
+
id: '123',
|
|
127
|
+
email: 'user@crimsoneducation.org',
|
|
128
|
+
name: 'Crimson User',
|
|
129
|
+
});
|
|
130
|
+
expect(posthogMock.identify).toHaveBeenCalledWith('123', {
|
|
131
|
+
email: 'user@crimsoneducation.org',
|
|
132
|
+
name: 'Crimson User',
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
reporter.reportError?.(new Error('broken'), {
|
|
136
|
+
routeName: 'messages',
|
|
137
|
+
});
|
|
138
|
+
expect(posthogMock.captureException).toHaveBeenCalledWith(
|
|
139
|
+
expect.any(Error),
|
|
140
|
+
expect.objectContaining({
|
|
141
|
+
message: 'broken',
|
|
142
|
+
routeName: 'messages',
|
|
143
|
+
}),
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
reporter.recordSession?.();
|
|
147
|
+
reporter.recordSessionStop?.();
|
|
148
|
+
expect(posthogMock.startSessionRecording).toHaveBeenCalledWith(true);
|
|
149
|
+
expect(posthogMock.stopSessionRecording).toHaveBeenCalled();
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it('adds compatibility properties in before_send', () => {
|
|
153
|
+
const beforeSend = jest.fn((captureResult) => captureResult);
|
|
154
|
+
|
|
155
|
+
posthogReporter(
|
|
156
|
+
{
|
|
157
|
+
service: 'crimson-app-frontend',
|
|
158
|
+
application: 'crimson-app',
|
|
159
|
+
environment: 'production',
|
|
160
|
+
version: '1.2.3',
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
apiKey: 'ph_key',
|
|
164
|
+
apiHost: 'https://eu.i.posthog.com',
|
|
165
|
+
beforeSend,
|
|
166
|
+
},
|
|
167
|
+
);
|
|
168
|
+
|
|
169
|
+
const initConfig = posthogMock.init.mock.calls[0]?.[1];
|
|
170
|
+
const captureResult = initConfig.before_send({
|
|
171
|
+
uuid: 'event-uuid',
|
|
172
|
+
event: 'message-sent',
|
|
173
|
+
properties: {
|
|
174
|
+
$current_url: 'https://app.crimsoneducation.org/messages/1',
|
|
175
|
+
$browser: 'Chrome',
|
|
176
|
+
$device_type: 'Desktop',
|
|
177
|
+
$geoip_country_name: 'New Zealand',
|
|
178
|
+
},
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
expect(captureResult.properties).toEqual(
|
|
182
|
+
expect.objectContaining({
|
|
183
|
+
app_name: 'crimson-app',
|
|
184
|
+
domain: 'app.crimsoneducation.org',
|
|
185
|
+
event_type: 'message-sent',
|
|
186
|
+
url: 'https://app.crimsoneducation.org/messages/1',
|
|
187
|
+
url_path: '/messages/1',
|
|
188
|
+
browser: 'Chrome',
|
|
189
|
+
device: 'Desktop',
|
|
190
|
+
geo: 'New Zealand',
|
|
191
|
+
}),
|
|
192
|
+
);
|
|
193
|
+
expect(beforeSend).toHaveBeenCalledWith(captureResult);
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it('preserves registered metadata when clearing the user', () => {
|
|
197
|
+
const reporter = posthogReporter(
|
|
198
|
+
{
|
|
199
|
+
service: 'student-center-frontend',
|
|
200
|
+
application: 'core-student-center',
|
|
201
|
+
environment: 'production',
|
|
202
|
+
version: '2.0.0',
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
apiKey: 'ph_key',
|
|
206
|
+
apiHost: 'https://eu.i.posthog.com',
|
|
207
|
+
},
|
|
208
|
+
);
|
|
209
|
+
|
|
210
|
+
reporter.addMetadata({
|
|
211
|
+
application: 'core-student-center',
|
|
212
|
+
namespace: 'production',
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
reporter.setUser(null);
|
|
216
|
+
|
|
217
|
+
expect(posthogMock.reset).toHaveBeenCalled();
|
|
218
|
+
expect(posthogMock.register).toHaveBeenLastCalledWith({
|
|
219
|
+
application: 'core-student-center',
|
|
220
|
+
namespace: 'production',
|
|
221
|
+
});
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
it('treats an empty user id as a cleared user', () => {
|
|
225
|
+
const reporter = posthogReporter(
|
|
226
|
+
{
|
|
227
|
+
service: 'new-roadmap',
|
|
228
|
+
environment: 'staging',
|
|
229
|
+
version: '1.0.0',
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
apiKey: 'ph_key',
|
|
233
|
+
apiHost: 'https://eu.i.posthog.com',
|
|
234
|
+
},
|
|
235
|
+
);
|
|
236
|
+
|
|
237
|
+
reporter.setUser({
|
|
238
|
+
id: '',
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
expect(posthogMock.reset).toHaveBeenCalled();
|
|
242
|
+
expect(posthogMock.identify).not.toHaveBeenCalled();
|
|
243
|
+
});
|
|
244
|
+
});
|