@sneat/logging 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/eslint.config.js +7 -0
- package/ng-package.json +7 -0
- package/package.json +18 -0
- package/project.json +38 -0
- package/src/index.ts +3 -0
- package/src/lib/anaylytics/analytics.service.spec.ts +35 -0
- package/src/lib/anaylytics/fire-analytics.service.spec.ts +245 -0
- package/src/lib/anaylytics/fire-analytics.service.ts +92 -0
- package/src/lib/anaylytics/index.ts +1 -0
- package/src/lib/anaylytics/multi-analytics.service.spec.ts +112 -0
- package/src/lib/anaylytics/multi-analytics.service.ts +47 -0
- package/src/lib/anaylytics/posthog-analytics.service.spec.ts +123 -0
- package/src/lib/anaylytics/posthog-analytics.service.ts +46 -0
- package/src/lib/anaylytics/provide-sneat-analytics.ts +68 -0
- package/src/lib/error-logger.service.spec.ts +284 -0
- package/src/lib/error-logger.service.ts +131 -0
- package/src/lib/sentry-setup.ts +32 -0
- package/src/lib/sneat-logging.module.ts +16 -0
- package/src/test-setup.ts +3 -0
- package/tsconfig.json +13 -0
- package/tsconfig.lib.json +19 -0
- package/tsconfig.lib.prod.json +7 -0
- package/tsconfig.spec.json +31 -0
- package/vite.config.mts +10 -0
package/eslint.config.js
ADDED
package/ng-package.json
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@sneat/logging",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"publishConfig": {
|
|
5
|
+
"access": "public"
|
|
6
|
+
},
|
|
7
|
+
"peerDependencies": {
|
|
8
|
+
"@angular/common": ">=21.0.0",
|
|
9
|
+
"@angular/core": ">=21.0.0",
|
|
10
|
+
"firebase": "*",
|
|
11
|
+
"@angular/fire": ">=20",
|
|
12
|
+
"@sentry/angular": "*",
|
|
13
|
+
"posthog-js": "*"
|
|
14
|
+
},
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"tslib": "2.8.1"
|
|
17
|
+
}
|
|
18
|
+
}
|
package/project.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "logging",
|
|
3
|
+
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
|
4
|
+
"projectType": "library",
|
|
5
|
+
"sourceRoot": "libs/logging/src",
|
|
6
|
+
"prefix": "sneat",
|
|
7
|
+
"targets": {
|
|
8
|
+
"build": {
|
|
9
|
+
"executor": "@nx/angular:ng-packagr-lite",
|
|
10
|
+
"outputs": [
|
|
11
|
+
"{workspaceRoot}/dist/libs/logging"
|
|
12
|
+
],
|
|
13
|
+
"options": {
|
|
14
|
+
"project": "libs/logging/ng-package.json",
|
|
15
|
+
"tsConfig": "libs/logging/tsconfig.lib.json"
|
|
16
|
+
},
|
|
17
|
+
"configurations": {
|
|
18
|
+
"production": {
|
|
19
|
+
"tsConfig": "libs/logging/tsconfig.lib.prod.json"
|
|
20
|
+
},
|
|
21
|
+
"development": {}
|
|
22
|
+
},
|
|
23
|
+
"defaultConfiguration": "production"
|
|
24
|
+
},
|
|
25
|
+
"test": {
|
|
26
|
+
"executor": "@nx/vitest:test",
|
|
27
|
+
"outputs": [
|
|
28
|
+
"{workspaceRoot}/coverage/libs/logging"
|
|
29
|
+
],
|
|
30
|
+
"options": {
|
|
31
|
+
"tsConfig": "libs/logging/tsconfig.spec.json"
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"lint": {
|
|
35
|
+
"executor": "@nx/eslint:lint"
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { MultiAnalyticsService } from './multi-analytics.service';
|
|
2
|
+
import { IAnalyticsService } from '@sneat/core';
|
|
3
|
+
|
|
4
|
+
describe('MultiAnalyticsService', () => {
|
|
5
|
+
beforeEach(() => {
|
|
6
|
+
vi.useFakeTimers();
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
afterEach(() => {
|
|
10
|
+
vi.useRealTimers();
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('should be created', () => {
|
|
14
|
+
const service = new MultiAnalyticsService([]);
|
|
15
|
+
expect(service).toBeTruthy();
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('should call all analytics services on logEvent', () => {
|
|
19
|
+
const mockService: IAnalyticsService = {
|
|
20
|
+
logEvent: vi.fn(),
|
|
21
|
+
identify: vi.fn(),
|
|
22
|
+
loggedOut: vi.fn(),
|
|
23
|
+
setCurrentScreen: vi.fn(),
|
|
24
|
+
};
|
|
25
|
+
const service = new MultiAnalyticsService([mockService]);
|
|
26
|
+
service.logEvent('test_event');
|
|
27
|
+
// logEvent uses setTimeout in MultiAnalyticsService
|
|
28
|
+
vi.runAllTimers();
|
|
29
|
+
expect(mockService.logEvent).toHaveBeenCalledWith(
|
|
30
|
+
'test_event',
|
|
31
|
+
undefined,
|
|
32
|
+
undefined,
|
|
33
|
+
);
|
|
34
|
+
});
|
|
35
|
+
});
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import { TestBed } from '@angular/core/testing';
|
|
2
|
+
import { Analytics, logEvent, setUserId, setUserProperties } from '@angular/fire/analytics';
|
|
3
|
+
import { ErrorLogger, IErrorLogger } from '@sneat/core';
|
|
4
|
+
import { FireAnalyticsService } from './fire-analytics.service';
|
|
5
|
+
|
|
6
|
+
vi.mock('@angular/fire/analytics', () => ({
|
|
7
|
+
Analytics: class {},
|
|
8
|
+
logEvent: vi.fn(),
|
|
9
|
+
setUserId: vi.fn(),
|
|
10
|
+
setUserProperties: vi.fn(),
|
|
11
|
+
}));
|
|
12
|
+
|
|
13
|
+
describe('FireAnalyticsService', () => {
|
|
14
|
+
let service: FireAnalyticsService;
|
|
15
|
+
let errorLogger: IErrorLogger;
|
|
16
|
+
let analytics: Analytics;
|
|
17
|
+
|
|
18
|
+
beforeEach(() => {
|
|
19
|
+
errorLogger = {
|
|
20
|
+
logError: vi.fn(),
|
|
21
|
+
logErrorHandler: () => vi.fn()
|
|
22
|
+
};
|
|
23
|
+
analytics = {} as Analytics;
|
|
24
|
+
|
|
25
|
+
TestBed.configureTestingModule({
|
|
26
|
+
providers: [
|
|
27
|
+
FireAnalyticsService,
|
|
28
|
+
{
|
|
29
|
+
provide: ErrorLogger,
|
|
30
|
+
useValue: errorLogger,
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
provide: Analytics,
|
|
34
|
+
useValue: analytics,
|
|
35
|
+
},
|
|
36
|
+
],
|
|
37
|
+
});
|
|
38
|
+
service = TestBed.inject(FireAnalyticsService);
|
|
39
|
+
vi.clearAllMocks();
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('should be created', () => {
|
|
43
|
+
expect(service).toBeTruthy();
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
describe('constructor', () => {
|
|
47
|
+
it('should log error if errorLogger is not provided', () => {
|
|
48
|
+
const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => undefined);
|
|
49
|
+
TestBed.resetTestingModule();
|
|
50
|
+
TestBed.configureTestingModule({
|
|
51
|
+
providers: [
|
|
52
|
+
FireAnalyticsService,
|
|
53
|
+
{
|
|
54
|
+
provide: ErrorLogger,
|
|
55
|
+
useValue: null,
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
provide: Analytics,
|
|
59
|
+
useValue: analytics,
|
|
60
|
+
},
|
|
61
|
+
],
|
|
62
|
+
});
|
|
63
|
+
TestBed.inject(FireAnalyticsService);
|
|
64
|
+
expect(consoleSpy).toHaveBeenCalledWith(
|
|
65
|
+
expect.stringContaining('!errorLogger')
|
|
66
|
+
);
|
|
67
|
+
consoleSpy.mockRestore();
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('should log error if analytics is not provided', () => {
|
|
71
|
+
const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => undefined);
|
|
72
|
+
TestBed.resetTestingModule();
|
|
73
|
+
TestBed.configureTestingModule({
|
|
74
|
+
providers: [
|
|
75
|
+
FireAnalyticsService,
|
|
76
|
+
{
|
|
77
|
+
provide: ErrorLogger,
|
|
78
|
+
useValue: errorLogger,
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
provide: Analytics,
|
|
82
|
+
useValue: null,
|
|
83
|
+
},
|
|
84
|
+
],
|
|
85
|
+
});
|
|
86
|
+
TestBed.inject(FireAnalyticsService);
|
|
87
|
+
expect(consoleSpy).toHaveBeenCalledWith(
|
|
88
|
+
expect.stringContaining('!analytics')
|
|
89
|
+
);
|
|
90
|
+
consoleSpy.mockRestore();
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
describe('logEvent', () => {
|
|
95
|
+
it('should call Firebase logEvent with event name and params', () => {
|
|
96
|
+
const eventName = 'test_event';
|
|
97
|
+
const eventParams = { param1: 'value1' };
|
|
98
|
+
const options = { some: 'option' };
|
|
99
|
+
|
|
100
|
+
service.logEvent(eventName, eventParams, options);
|
|
101
|
+
|
|
102
|
+
expect(logEvent).toHaveBeenCalledWith(analytics, eventName, eventParams, options);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('should log error if logEvent throws', () => {
|
|
106
|
+
const error = new Error('logEvent failed');
|
|
107
|
+
vi.mocked(logEvent).mockImplementationOnce(() => {
|
|
108
|
+
throw error;
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
service.logEvent('test_event');
|
|
112
|
+
|
|
113
|
+
expect(errorLogger.logError).toHaveBeenCalledWith(
|
|
114
|
+
error,
|
|
115
|
+
'Failed to log event to Firebase analytics',
|
|
116
|
+
{ show: false, feedback: false }
|
|
117
|
+
);
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
describe('setCurrentScreen', () => {
|
|
122
|
+
it('should call Firebase logEvent with $screen_view', () => {
|
|
123
|
+
const screenName = 'HomeScreen';
|
|
124
|
+
const options = { some: 'option' };
|
|
125
|
+
|
|
126
|
+
service.setCurrentScreen(screenName, options);
|
|
127
|
+
|
|
128
|
+
expect(logEvent).toHaveBeenCalledWith(
|
|
129
|
+
analytics,
|
|
130
|
+
'$screen_view',
|
|
131
|
+
{ screenName: screenName },
|
|
132
|
+
options
|
|
133
|
+
);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it('should log error if logEvent throws', () => {
|
|
137
|
+
const error = new Error('logEvent failed');
|
|
138
|
+
vi.mocked(logEvent).mockImplementationOnce(() => {
|
|
139
|
+
throw error;
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
service.setCurrentScreen('HomeScreen');
|
|
143
|
+
|
|
144
|
+
expect(errorLogger.logError).toHaveBeenCalledWith(
|
|
145
|
+
error,
|
|
146
|
+
'Failed to log screen view to Firebase analytics',
|
|
147
|
+
{ show: false, feedback: false }
|
|
148
|
+
);
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
describe('identify', () => {
|
|
153
|
+
it('should call setUserId with userID', () => {
|
|
154
|
+
const userID = 'user123';
|
|
155
|
+
|
|
156
|
+
service.identify(userID);
|
|
157
|
+
|
|
158
|
+
expect(setUserId).toHaveBeenCalledWith(analytics, userID);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it('should log error if setUserId throws', () => {
|
|
162
|
+
const error = new Error('setUserId failed');
|
|
163
|
+
vi.mocked(setUserId).mockImplementationOnce(() => {
|
|
164
|
+
throw error;
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
service.identify('user123');
|
|
168
|
+
|
|
169
|
+
expect(errorLogger.logError).toHaveBeenCalledWith(
|
|
170
|
+
error,
|
|
171
|
+
'Failed to set user id in Firebase analytics',
|
|
172
|
+
{ show: false, feedback: false }
|
|
173
|
+
);
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
it('should call setUserProperties with userPropertiesToSet', () => {
|
|
177
|
+
const userID = 'user123';
|
|
178
|
+
const userPropsToSet = { name: 'John', age: 30 };
|
|
179
|
+
|
|
180
|
+
service.identify(userID, userPropsToSet);
|
|
181
|
+
|
|
182
|
+
expect(setUserProperties).toHaveBeenCalledWith(analytics, userPropsToSet);
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
it('should call setUserProperties with userPropertiesToSetOnce', () => {
|
|
186
|
+
const userID = 'user123';
|
|
187
|
+
const userPropsToSetOnce = { email: 'john@test.com' };
|
|
188
|
+
|
|
189
|
+
service.identify(userID, undefined, userPropsToSetOnce);
|
|
190
|
+
|
|
191
|
+
expect(setUserProperties).toHaveBeenCalledWith(analytics, userPropsToSetOnce);
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
it('should merge userPropertiesToSet and userPropertiesToSetOnce', () => {
|
|
195
|
+
const userID = 'user123';
|
|
196
|
+
const userPropsToSet = { name: 'John' };
|
|
197
|
+
const userPropsToSetOnce = { email: 'john@test.com' };
|
|
198
|
+
|
|
199
|
+
service.identify(userID, userPropsToSet, userPropsToSetOnce);
|
|
200
|
+
|
|
201
|
+
expect(setUserProperties).toHaveBeenCalledWith(analytics, {
|
|
202
|
+
name: 'John',
|
|
203
|
+
email: 'john@test.com',
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
it('should log error if setUserProperties throws', () => {
|
|
208
|
+
const error = new Error('setUserProperties failed');
|
|
209
|
+
vi.mocked(setUserProperties).mockImplementationOnce(() => {
|
|
210
|
+
throw error;
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
service.identify('user123', { name: 'John' });
|
|
214
|
+
|
|
215
|
+
expect(errorLogger.logError).toHaveBeenCalledWith(
|
|
216
|
+
error,
|
|
217
|
+
'Failed to set user props in Firebase analytics',
|
|
218
|
+
{ show: false, feedback: false }
|
|
219
|
+
);
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
describe('loggedOut', () => {
|
|
224
|
+
it('should call setUserId with null', () => {
|
|
225
|
+
service.loggedOut();
|
|
226
|
+
|
|
227
|
+
expect(setUserId).toHaveBeenCalledWith(analytics, null);
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
it('should log error if setUserId throws', () => {
|
|
231
|
+
const error = new Error('setUserId failed');
|
|
232
|
+
vi.mocked(setUserId).mockImplementationOnce(() => {
|
|
233
|
+
throw error;
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
service.loggedOut();
|
|
237
|
+
|
|
238
|
+
expect(errorLogger.logError).toHaveBeenCalledWith(
|
|
239
|
+
error,
|
|
240
|
+
'Failed to logout user from Firebase analytics',
|
|
241
|
+
{ show: false, feedback: false }
|
|
242
|
+
);
|
|
243
|
+
});
|
|
244
|
+
});
|
|
245
|
+
});
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { inject, Injectable } from '@angular/core';
|
|
2
|
+
import {
|
|
3
|
+
Analytics,
|
|
4
|
+
logEvent,
|
|
5
|
+
setUserId,
|
|
6
|
+
setUserProperties,
|
|
7
|
+
} from '@angular/fire/analytics';
|
|
8
|
+
import { ErrorLogger, IErrorLogger, ILogErrorOptions } from '@sneat/core';
|
|
9
|
+
import {
|
|
10
|
+
IAnalyticsCallOptions,
|
|
11
|
+
IAnalyticsService,
|
|
12
|
+
UserProperties,
|
|
13
|
+
} from '@sneat/core';
|
|
14
|
+
|
|
15
|
+
const logErrOptions: ILogErrorOptions = { show: false, feedback: false };
|
|
16
|
+
|
|
17
|
+
@Injectable()
|
|
18
|
+
export class FireAnalyticsService implements IAnalyticsService {
|
|
19
|
+
private readonly errorLogger = inject<IErrorLogger>(ErrorLogger);
|
|
20
|
+
private readonly analytics = inject(Analytics);
|
|
21
|
+
|
|
22
|
+
constructor() {
|
|
23
|
+
if (!this.errorLogger) {
|
|
24
|
+
console.error(`FireAnalyticsService() - !errorLogger`);
|
|
25
|
+
}
|
|
26
|
+
if (!this.analytics) {
|
|
27
|
+
console.error(`FireAnalyticsService() - !analytics`);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
private readonly logError = (e: unknown, m: string) =>
|
|
32
|
+
this.errorLogger.logError(e, m, logErrOptions);
|
|
33
|
+
|
|
34
|
+
public logEvent(
|
|
35
|
+
eventName: string,
|
|
36
|
+
eventParams?: Record<string, unknown>,
|
|
37
|
+
options?: IAnalyticsCallOptions,
|
|
38
|
+
): void {
|
|
39
|
+
try {
|
|
40
|
+
logEvent(this.analytics, eventName, eventParams, options);
|
|
41
|
+
} catch (e) {
|
|
42
|
+
this.logError(e, 'Failed to log event to Firebase analytics');
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
public setCurrentScreen(
|
|
47
|
+
screenName: string,
|
|
48
|
+
options?: IAnalyticsCallOptions,
|
|
49
|
+
): void {
|
|
50
|
+
try {
|
|
51
|
+
const args = { screenName: screenName };
|
|
52
|
+
logEvent(this.analytics, '$screen_view', args, options);
|
|
53
|
+
} catch (e) {
|
|
54
|
+
this.logError(e, 'Failed to log screen view to Firebase analytics');
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
public identify(
|
|
59
|
+
userID: string,
|
|
60
|
+
userPropertiesToSet?: UserProperties,
|
|
61
|
+
userPropertiesToSetOnce?: UserProperties,
|
|
62
|
+
): void {
|
|
63
|
+
try {
|
|
64
|
+
setUserId(this.analytics, userID);
|
|
65
|
+
} catch (e) {
|
|
66
|
+
this.logError(e, 'Failed to set user id in Firebase analytics');
|
|
67
|
+
}
|
|
68
|
+
const customProperties: { [id: string]: unknown } = {};
|
|
69
|
+
|
|
70
|
+
if (userPropertiesToSet)
|
|
71
|
+
Object.keys(userPropertiesToSet).forEach(
|
|
72
|
+
(k) => (customProperties[k] = userPropertiesToSet[k]),
|
|
73
|
+
);
|
|
74
|
+
if (userPropertiesToSetOnce)
|
|
75
|
+
Object.keys(userPropertiesToSetOnce).forEach(
|
|
76
|
+
(k) => (customProperties[k] = userPropertiesToSetOnce[k]),
|
|
77
|
+
);
|
|
78
|
+
try {
|
|
79
|
+
setUserProperties(this.analytics, customProperties);
|
|
80
|
+
} catch (e) {
|
|
81
|
+
this.logError(e, 'Failed to set user props in Firebase analytics');
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
public loggedOut(): void {
|
|
86
|
+
try {
|
|
87
|
+
setUserId(this.analytics, null);
|
|
88
|
+
} catch (e) {
|
|
89
|
+
this.logError(e, 'Failed to logout user from Firebase analytics');
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './provide-sneat-analytics';
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { MultiAnalyticsService } from './multi-analytics.service';
|
|
2
|
+
import { IAnalyticsService } from '@sneat/core';
|
|
3
|
+
|
|
4
|
+
describe('MultiAnalyticsService', () => {
|
|
5
|
+
let mockAnalyticsService1: IAnalyticsService;
|
|
6
|
+
let mockAnalyticsService2: IAnalyticsService;
|
|
7
|
+
let service: MultiAnalyticsService;
|
|
8
|
+
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
mockAnalyticsService1 = {
|
|
11
|
+
identify: vi.fn(),
|
|
12
|
+
logEvent: vi.fn(),
|
|
13
|
+
setCurrentScreen: vi.fn(),
|
|
14
|
+
loggedOut: vi.fn(),
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
mockAnalyticsService2 = {
|
|
18
|
+
identify: vi.fn(),
|
|
19
|
+
logEvent: vi.fn(),
|
|
20
|
+
setCurrentScreen: vi.fn(),
|
|
21
|
+
loggedOut: vi.fn(),
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
service = new MultiAnalyticsService([
|
|
25
|
+
mockAnalyticsService1,
|
|
26
|
+
mockAnalyticsService2,
|
|
27
|
+
]);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('should create', () => {
|
|
31
|
+
expect(new MultiAnalyticsService([])).toBeTruthy();
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
describe('identify', () => {
|
|
35
|
+
it('should call identify on all analytics services', () => {
|
|
36
|
+
vi.useFakeTimers();
|
|
37
|
+
const userID = 'user123';
|
|
38
|
+
const userProps = { name: 'John' };
|
|
39
|
+
const userPropsOnce = { email: 'john@test.com' };
|
|
40
|
+
|
|
41
|
+
service.identify(userID, userProps, userPropsOnce);
|
|
42
|
+
vi.runAllTimers();
|
|
43
|
+
|
|
44
|
+
expect(mockAnalyticsService1.identify).toHaveBeenCalledWith(
|
|
45
|
+
userID,
|
|
46
|
+
userProps,
|
|
47
|
+
userPropsOnce,
|
|
48
|
+
);
|
|
49
|
+
expect(mockAnalyticsService2.identify).toHaveBeenCalledWith(
|
|
50
|
+
userID,
|
|
51
|
+
userProps,
|
|
52
|
+
userPropsOnce,
|
|
53
|
+
);
|
|
54
|
+
vi.useRealTimers();
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
describe('logEvent', () => {
|
|
59
|
+
it('should call logEvent on all analytics services', () => {
|
|
60
|
+
vi.useFakeTimers();
|
|
61
|
+
const eventName = 'test_event';
|
|
62
|
+
const eventParams = { param1: 'value1' };
|
|
63
|
+
const options = { some: 'option' };
|
|
64
|
+
|
|
65
|
+
service.logEvent(eventName, eventParams, options);
|
|
66
|
+
vi.runAllTimers();
|
|
67
|
+
|
|
68
|
+
expect(mockAnalyticsService1.logEvent).toHaveBeenCalledWith(
|
|
69
|
+
eventName,
|
|
70
|
+
eventParams,
|
|
71
|
+
options,
|
|
72
|
+
);
|
|
73
|
+
expect(mockAnalyticsService2.logEvent).toHaveBeenCalledWith(
|
|
74
|
+
eventName,
|
|
75
|
+
eventParams,
|
|
76
|
+
options,
|
|
77
|
+
);
|
|
78
|
+
vi.useRealTimers();
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
describe('setCurrentScreen', () => {
|
|
83
|
+
it('should call setCurrentScreen on all analytics services', () => {
|
|
84
|
+
const screenName = 'HomeScreen';
|
|
85
|
+
const options = { some: 'option' };
|
|
86
|
+
|
|
87
|
+
service.setCurrentScreen(screenName, options);
|
|
88
|
+
|
|
89
|
+
expect(mockAnalyticsService1.setCurrentScreen).toHaveBeenCalledWith(
|
|
90
|
+
screenName,
|
|
91
|
+
options,
|
|
92
|
+
);
|
|
93
|
+
expect(mockAnalyticsService2.setCurrentScreen).toHaveBeenCalledWith(
|
|
94
|
+
screenName,
|
|
95
|
+
options,
|
|
96
|
+
);
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
describe('loggedOut', () => {
|
|
101
|
+
it('should call loggedOut on all analytics services', () => {
|
|
102
|
+
vi.useFakeTimers();
|
|
103
|
+
|
|
104
|
+
service.loggedOut();
|
|
105
|
+
vi.runAllTimers();
|
|
106
|
+
|
|
107
|
+
expect(mockAnalyticsService1.loggedOut).toHaveBeenCalled();
|
|
108
|
+
expect(mockAnalyticsService2.loggedOut).toHaveBeenCalled();
|
|
109
|
+
vi.useRealTimers();
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
});
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import {
|
|
2
|
+
IAnalyticsCallOptions,
|
|
3
|
+
IAnalyticsService,
|
|
4
|
+
UserProperties,
|
|
5
|
+
} from '@sneat/core';
|
|
6
|
+
|
|
7
|
+
const prefix = 'MultiAnalyticsService.';
|
|
8
|
+
|
|
9
|
+
export class MultiAnalyticsService implements IAnalyticsService {
|
|
10
|
+
constructor(private readonly as: readonly IAnalyticsService[]) {
|
|
11
|
+
console.log(
|
|
12
|
+
prefix +
|
|
13
|
+
`.constructor() as=[${as.map((a) => a.constructor.name).join(',')}]`,
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
public identify(
|
|
18
|
+
userID: string,
|
|
19
|
+
userPropsToSet?: UserProperties,
|
|
20
|
+
userPropsToSetOnce?: UserProperties,
|
|
21
|
+
): void {
|
|
22
|
+
this.as.forEach((as) =>
|
|
23
|
+
setTimeout(() => as.identify(userID, userPropsToSet, userPropsToSetOnce)),
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
public logEvent(
|
|
28
|
+
eventName: string,
|
|
29
|
+
eventParams?: Readonly<Record<string, unknown>>,
|
|
30
|
+
options?: IAnalyticsCallOptions,
|
|
31
|
+
): void {
|
|
32
|
+
this.as.forEach((as) =>
|
|
33
|
+
setTimeout(() => as.logEvent(eventName, eventParams, options)),
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
public setCurrentScreen(
|
|
38
|
+
screenName: string,
|
|
39
|
+
options?: IAnalyticsCallOptions,
|
|
40
|
+
): void {
|
|
41
|
+
this.as.forEach((as) => as.setCurrentScreen(screenName, options));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
public loggedOut(): void {
|
|
45
|
+
this.as.forEach((as) => setTimeout(() => as.loggedOut()));
|
|
46
|
+
}
|
|
47
|
+
}
|