@webex/internal-plugin-metrics 3.0.0-beta.2 → 3.0.0-beta.200

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.
Files changed (76) hide show
  1. package/README.md +1 -3
  2. package/dist/batcher.js +3 -22
  3. package/dist/batcher.js.map +1 -1
  4. package/dist/call-diagnostic/call-diagnostic-metrics-batcher.js +56 -0
  5. package/dist/call-diagnostic/call-diagnostic-metrics-batcher.js.map +1 -0
  6. package/dist/call-diagnostic/call-diagnostic-metrics-latencies.js +451 -0
  7. package/dist/call-diagnostic/call-diagnostic-metrics-latencies.js.map +1 -0
  8. package/dist/call-diagnostic/call-diagnostic-metrics.js +584 -0
  9. package/dist/call-diagnostic/call-diagnostic-metrics.js.map +1 -0
  10. package/dist/call-diagnostic/call-diagnostic-metrics.util.js +225 -0
  11. package/dist/call-diagnostic/call-diagnostic-metrics.util.js.map +1 -0
  12. package/dist/call-diagnostic/config.js +461 -0
  13. package/dist/call-diagnostic/config.js.map +1 -0
  14. package/dist/call-diagnostic/generated-types-temp/ClientEvent.js +7 -0
  15. package/dist/call-diagnostic/generated-types-temp/ClientEvent.js.map +1 -0
  16. package/dist/call-diagnostic/generated-types-temp/Event.js +7 -0
  17. package/dist/call-diagnostic/generated-types-temp/Event.js.map +1 -0
  18. package/dist/call-diagnostic/generated-types-temp/MediaQualityEvent.js +7 -0
  19. package/dist/call-diagnostic/generated-types-temp/MediaQualityEvent.js.map +1 -0
  20. package/dist/client-metrics-batcher.js +1 -7
  21. package/dist/client-metrics-batcher.js.map +1 -1
  22. package/dist/config.js +21 -5
  23. package/dist/config.js.map +1 -1
  24. package/dist/index.js +26 -10
  25. package/dist/index.js.map +1 -1
  26. package/dist/metrics.js +43 -80
  27. package/dist/metrics.js.map +1 -1
  28. package/dist/metrics.types.js +7 -0
  29. package/dist/metrics.types.js.map +1 -0
  30. package/dist/new-metrics.js +249 -0
  31. package/dist/new-metrics.js.map +1 -0
  32. package/dist/types/batcher.d.ts +2 -0
  33. package/dist/types/call-diagnostic/call-diagnostic-metrics-batcher.d.ts +2 -0
  34. package/dist/types/call-diagnostic/call-diagnostic-metrics-latencies.d.ts +189 -0
  35. package/dist/types/call-diagnostic/call-diagnostic-metrics.d.ts +348 -0
  36. package/dist/types/call-diagnostic/call-diagnostic-metrics.util.d.ts +52 -0
  37. package/dist/types/call-diagnostic/config.d.ts +57 -0
  38. package/dist/types/call-diagnostic/generated-types-temp/ClientEvent.d.ts +1112 -0
  39. package/dist/types/call-diagnostic/generated-types-temp/Event.d.ts +4851 -0
  40. package/dist/types/call-diagnostic/generated-types-temp/MediaQualityEvent.d.ts +2121 -0
  41. package/dist/types/client-metrics-batcher.d.ts +2 -0
  42. package/dist/types/config.d.ts +35 -0
  43. package/dist/types/index.d.ts +11 -0
  44. package/dist/types/metrics.d.ts +3 -0
  45. package/dist/types/metrics.types.d.ts +92 -0
  46. package/dist/types/new-metrics.d.ts +119 -0
  47. package/package.json +12 -8
  48. package/src/batcher.js +33 -26
  49. package/src/call-diagnostic/call-diagnostic-metrics-batcher.ts +51 -0
  50. package/src/call-diagnostic/call-diagnostic-metrics-latencies.ts +408 -0
  51. package/src/call-diagnostic/call-diagnostic-metrics.ts +591 -0
  52. package/src/call-diagnostic/call-diagnostic-metrics.util.ts +233 -0
  53. package/src/call-diagnostic/config.ts +455 -0
  54. package/src/call-diagnostic/generated-types-temp/ClientEvent.ts +2395 -0
  55. package/src/call-diagnostic/generated-types-temp/Event.ts +7762 -0
  56. package/src/call-diagnostic/generated-types-temp/MediaQualityEvent.ts +2321 -0
  57. package/src/client-metrics-batcher.js +3 -4
  58. package/src/config.js +25 -5
  59. package/src/index.ts +39 -0
  60. package/src/metrics.js +44 -58
  61. package/src/metrics.types.ts +137 -0
  62. package/src/new-metrics.ts +223 -0
  63. package/test/unit/spec/batcher.js +26 -15
  64. package/test/unit/spec/call-diagnostic/call-diagnostic-metrics-batcher.ts +243 -0
  65. package/test/unit/spec/call-diagnostic/call-diagnostic-metrics-latencies.ts +474 -0
  66. package/test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts +820 -0
  67. package/test/unit/spec/call-diagnostic/call-diagnostic-metrics.util.ts +336 -0
  68. package/test/unit/spec/client-metrics-batcher.js +26 -15
  69. package/test/unit/spec/metrics.js +85 -116
  70. package/test/unit/spec/new-metrics.ts +153 -0
  71. package/tsconfig.json +6 -0
  72. package/dist/call-diagnostic-events-batcher.js +0 -70
  73. package/dist/call-diagnostic-events-batcher.js.map +0 -1
  74. package/src/call-diagnostic-events-batcher.js +0 -62
  75. package/src/index.js +0 -17
  76. package/test/unit/spec/call-diagnostic-events-batcher.js +0 -180
@@ -0,0 +1,820 @@
1
+ import sinon from 'sinon';
2
+ import {assert} from '@webex/test-helper-chai';
3
+
4
+ import CallDiagnosticMetrics from '../../../../src/call-diagnostic/call-diagnostic-metrics';
5
+ import CallDiagnosticLatencies from '../../../../src/call-diagnostic/call-diagnostic-metrics-latencies';
6
+ import * as Utils from '../../../../src/call-diagnostic/call-diagnostic-metrics.util';
7
+ import {BrowserDetection} from '@webex/common';
8
+ import {getOSNameInternal} from '@webex/internal-plugin-metrics';
9
+ import uuid from 'uuid';
10
+
11
+ //@ts-ignore
12
+ global.window = {location: {hostname: 'whatever'}};
13
+
14
+ const {getOSName, getOSVersion, getBrowserName, getBrowserVersion} = BrowserDetection();
15
+ const userAgent = `webex-js-sdk/test-webex-version client=Cantina; (os=${getOSName()}/${
16
+ getOSVersion().split('.')[0]
17
+ })`;
18
+
19
+ describe('internal-plugin-metrics', () => {
20
+ describe('CallDiagnosticMetrics', () => {
21
+ var now = new Date();
22
+
23
+ let cd: CallDiagnosticMetrics;
24
+
25
+ const fakeMeeting = {
26
+ id: '1',
27
+ correlationId: 'correlationId',
28
+ environment: 'meeting_evn',
29
+ locusUrl: 'locus/url',
30
+ locusInfo: {
31
+ fullState: {
32
+ lastActive: 'lastActive',
33
+ },
34
+ },
35
+ getCurUserType: () => 'host',
36
+ };
37
+
38
+ let webex;
39
+
40
+ beforeEach(() => {
41
+ webex = {
42
+ canAuthorize: true,
43
+ version: 'webex-version',
44
+ internal: {
45
+ services: {
46
+ get: () => 'locus-url',
47
+ },
48
+ metrics: {
49
+ submitClientMetrics: sinon.stub(),
50
+ },
51
+ newMetrics: {},
52
+ device: {
53
+ userId: 'userId',
54
+ url: 'deviceUrl',
55
+ orgId: 'orgId',
56
+ },
57
+ },
58
+ meetings: {
59
+ config: {
60
+ metrics: {
61
+ clientType: 'TEAMS_CLIENT',
62
+ subClientType: 'WEB_APP',
63
+ },
64
+ },
65
+ metrics: {
66
+ clientName: 'Cantina',
67
+ },
68
+ meetingCollection: {
69
+ get: () => fakeMeeting,
70
+ },
71
+ geoHintInfo: {
72
+ clientAddress: '1.3.4.5',
73
+ countryCode: 'UK',
74
+ },
75
+ },
76
+ credentials: {
77
+ isUnverifiedGuest: false,
78
+ },
79
+ prepareFetchOptions: sinon.stub().callsFake((opts: any) => ({...opts, foo: 'bar'})),
80
+ };
81
+
82
+ webex.internal.newMetrics.callDiagnosticLatencies = new CallDiagnosticLatencies(
83
+ {},
84
+ {parent: webex}
85
+ );
86
+
87
+ sinon.createSandbox();
88
+ sinon.useFakeTimers(now.getTime());
89
+ cd = new CallDiagnosticMetrics({}, {parent: webex});
90
+ sinon.stub(uuid, 'v4').returns('my-fake-id');
91
+ });
92
+
93
+ afterEach(() => {
94
+ sinon.restore();
95
+ });
96
+
97
+ describe('#getOrigin', () => {
98
+ it('should build origin correctly', () => {
99
+ sinon.stub(Utils, 'anonymizeIPAddress').returns('1.1.1.1');
100
+
101
+ //@ts-ignore
102
+ const res = cd.getOrigin(
103
+ {subClientType: 'WEB_APP', clientType: 'TEAMS_CLIENT'},
104
+ fakeMeeting.id
105
+ );
106
+
107
+ assert.deepEqual(res, {
108
+ clientInfo: {
109
+ browser: getBrowserName(),
110
+ browserVersion: getBrowserVersion(),
111
+ clientType: 'TEAMS_CLIENT',
112
+ clientVersion: 'webex-js-sdk/webex-version',
113
+ localNetworkPrefix: '1.1.1.1',
114
+ os: getOSNameInternal(),
115
+ osVersion: getOSVersion(),
116
+ subClientType: 'WEB_APP',
117
+ },
118
+ environment: 'meeting_evn',
119
+ name: 'endpoint',
120
+ networkType: 'unknown',
121
+ userAgent,
122
+ });
123
+ });
124
+
125
+ it('should build origin correctly with no meeting', () => {
126
+ sinon.stub(Utils, 'anonymizeIPAddress').returns('1.1.1.1');
127
+
128
+ //@ts-ignore
129
+ const res = cd.getOrigin();
130
+
131
+ assert.deepEqual(res, {
132
+ clientInfo: {
133
+ browser: getBrowserName(),
134
+ browserVersion: getBrowserVersion(),
135
+ clientType: 'TEAMS_CLIENT',
136
+ clientVersion: 'webex-js-sdk/webex-version',
137
+ localNetworkPrefix: '1.1.1.1',
138
+ os: getOSNameInternal(),
139
+ osVersion: getOSVersion(),
140
+ subClientType: 'WEB_APP',
141
+ },
142
+ name: 'endpoint',
143
+ networkType: 'unknown',
144
+ userAgent,
145
+ });
146
+ });
147
+ });
148
+
149
+ describe('#getIdentifiers', () => {
150
+ it('should build identifiers correctly', () => {
151
+ const res = cd.getIdentifiers({
152
+ mediaConnections: [
153
+ {mediaAgentAlias: 'mediaAgentAlias', mediaAgentGroupId: 'mediaAgentGroupId'},
154
+ ],
155
+ meeting: fakeMeeting,
156
+ });
157
+
158
+ assert.deepEqual(res, {
159
+ correlationId: 'correlationId',
160
+ deviceId: 'deviceUrl',
161
+ locusId: 'url',
162
+ locusStartTime: 'lastActive',
163
+ locusUrl: 'locus/url',
164
+ mediaAgentAlias: 'mediaAgentAlias',
165
+ mediaAgentGroupId: 'mediaAgentGroupId',
166
+ orgId: 'orgId',
167
+ userId: 'userId',
168
+ });
169
+ });
170
+
171
+ it('should build identifiers correctly given correlationId', () => {
172
+ const res = cd.getIdentifiers({
173
+ correlationId: 'correlationId',
174
+ });
175
+
176
+ assert.deepEqual(res, {
177
+ correlationId: 'correlationId',
178
+ deviceId: 'deviceUrl',
179
+ locusUrl: 'locus-url',
180
+ orgId: 'orgId',
181
+ userId: 'userId',
182
+ });
183
+ });
184
+
185
+ it('should throw Error if correlationId is missing', () => {
186
+ assert.throws(() =>
187
+ cd.getIdentifiers({
188
+ mediaConnections: [
189
+ {mediaAgentAlias: 'mediaAgentAlias', mediaAgentGroupId: 'mediaAgentGroupId'},
190
+ ],
191
+ meeting: {...fakeMeeting, correlationId: undefined},
192
+ })
193
+ );
194
+ });
195
+ });
196
+
197
+ it('should prepare diagnostic event successfully', () => {
198
+ const options = {meetingId: fakeMeeting.id};
199
+ const getOriginStub = sinon.stub(cd, 'getOrigin').returns({origin: 'fake-origin'});
200
+ const clearEmptyKeysRecursivelyStub = sinon.stub(Utils, 'clearEmptyKeysRecursively');
201
+
202
+ const res = cd.prepareDiagnosticEvent(
203
+ {
204
+ canProceed: false,
205
+ identifiers: {correlationId: 'id'},
206
+ name: 'client.alert.displayed',
207
+ },
208
+ options
209
+ );
210
+
211
+ assert.calledWith(getOriginStub, options, options.meetingId);
212
+ assert.calledOnce(clearEmptyKeysRecursivelyStub);
213
+ assert.deepEqual(res, {
214
+ event: {
215
+ canProceed: false,
216
+ identifiers: {
217
+ correlationId: 'id',
218
+ },
219
+ name: 'client.alert.displayed',
220
+ },
221
+ eventId: 'my-fake-id',
222
+ origin: {
223
+ origin: 'fake-origin',
224
+ },
225
+ originTime: {
226
+ sent: 'not_defined_yet',
227
+ triggered: now.toISOString(),
228
+ },
229
+ senderCountryCode: 'UK',
230
+ version: 1,
231
+ });
232
+ });
233
+
234
+ describe('#submitClientEvent', () => {
235
+ it('should submit client event successfully with meetingId', () => {
236
+ const prepareDiagnosticEventSpy = sinon.spy(cd, 'prepareDiagnosticEvent');
237
+ const submitToCallDiagnosticsSpy = sinon.spy(cd, 'submitToCallDiagnostics');
238
+ const generateClientEventErrorPayloadSpy = sinon.spy(cd, 'generateClientEventErrorPayload');
239
+ const getIdentifiersSpy = sinon.spy(cd, 'getIdentifiers');
240
+ sinon.stub(cd, 'getOrigin').returns({origin: 'fake-origin'});
241
+ const options = {
242
+ meetingId: fakeMeeting.id,
243
+ mediaConnections: [{mediaAgentAlias: 'alias', mediaAgentGroupId: '1'}],
244
+ };
245
+
246
+ cd.submitClientEvent({
247
+ name: 'client.alert.displayed',
248
+ options,
249
+ });
250
+
251
+ assert.calledWith(getIdentifiersSpy, {
252
+ meeting: fakeMeeting,
253
+ mediaConnections: [{mediaAgentAlias: 'alias', mediaAgentGroupId: '1'}],
254
+ });
255
+ assert.notCalled(generateClientEventErrorPayloadSpy);
256
+ assert.calledWith(
257
+ prepareDiagnosticEventSpy,
258
+ {
259
+ canProceed: true,
260
+ eventData: {
261
+ webClientDomain: 'whatever',
262
+ },
263
+ identifiers: {
264
+ correlationId: 'correlationId',
265
+ deviceId: 'deviceUrl',
266
+ locusId: 'url',
267
+ locusStartTime: 'lastActive',
268
+ locusUrl: 'locus/url',
269
+ mediaAgentAlias: 'alias',
270
+ mediaAgentGroupId: '1',
271
+ orgId: 'orgId',
272
+ userId: 'userId',
273
+ },
274
+ loginType: 'login-ci',
275
+ name: 'client.alert.displayed',
276
+ userType: 'host',
277
+ },
278
+ options
279
+ );
280
+ assert.calledWith(submitToCallDiagnosticsSpy, {
281
+ event: {
282
+ canProceed: true,
283
+ eventData: {
284
+ webClientDomain: 'whatever',
285
+ },
286
+ identifiers: {
287
+ correlationId: 'correlationId',
288
+ deviceId: 'deviceUrl',
289
+ locusId: 'url',
290
+ locusStartTime: 'lastActive',
291
+ locusUrl: 'locus/url',
292
+ mediaAgentAlias: 'alias',
293
+ mediaAgentGroupId: '1',
294
+ orgId: 'orgId',
295
+ userId: 'userId',
296
+ },
297
+ loginType: 'login-ci',
298
+ name: 'client.alert.displayed',
299
+ userType: 'host',
300
+ },
301
+ eventId: 'my-fake-id',
302
+ origin: {
303
+ origin: 'fake-origin',
304
+ },
305
+ originTime: {
306
+ sent: 'not_defined_yet',
307
+ triggered: now.toISOString(),
308
+ },
309
+ senderCountryCode: 'UK',
310
+ version: 1,
311
+ });
312
+ });
313
+
314
+ it('should submit client event successfully with correlationId', () => {
315
+ const prepareDiagnosticEventSpy = sinon.spy(cd, 'prepareDiagnosticEvent');
316
+ const submitToCallDiagnosticsSpy = sinon.spy(cd, 'submitToCallDiagnostics');
317
+ const generateClientEventErrorPayloadSpy = sinon.spy(cd, 'generateClientEventErrorPayload');
318
+ const getIdentifiersSpy = sinon.spy(cd, 'getIdentifiers');
319
+ sinon.stub(cd, 'getOrigin').returns({origin: 'fake-origin'});
320
+
321
+ const options = {
322
+ correlationId: 'correlationId',
323
+ };
324
+
325
+ cd.submitClientEvent({
326
+ name: 'client.alert.displayed',
327
+ options,
328
+ });
329
+
330
+ assert.calledWith(getIdentifiersSpy, {
331
+ correlationId: 'correlationId',
332
+ });
333
+
334
+ assert.notCalled(generateClientEventErrorPayloadSpy);
335
+ assert.calledWith(
336
+ prepareDiagnosticEventSpy,
337
+ {
338
+ canProceed: true,
339
+ eventData: {
340
+ webClientDomain: 'whatever',
341
+ },
342
+ identifiers: {
343
+ correlationId: 'correlationId',
344
+ deviceId: 'deviceUrl',
345
+ locusUrl: 'locus-url',
346
+ orgId: 'orgId',
347
+ userId: 'userId',
348
+ },
349
+ loginType: 'login-ci',
350
+ name: 'client.alert.displayed',
351
+ },
352
+ options
353
+ );
354
+ assert.calledWith(submitToCallDiagnosticsSpy, {
355
+ event: {
356
+ canProceed: true,
357
+ eventData: {
358
+ webClientDomain: 'whatever',
359
+ },
360
+ identifiers: {
361
+ correlationId: 'correlationId',
362
+ deviceId: 'deviceUrl',
363
+ locusUrl: 'locus-url',
364
+ orgId: 'orgId',
365
+ userId: 'userId',
366
+ },
367
+ loginType: 'login-ci',
368
+ name: 'client.alert.displayed',
369
+ },
370
+ eventId: 'my-fake-id',
371
+ origin: {
372
+ origin: 'fake-origin',
373
+ },
374
+ originTime: {
375
+ sent: 'not_defined_yet',
376
+ triggered: now.toISOString(),
377
+ },
378
+ senderCountryCode: 'UK',
379
+ version: 1,
380
+ });
381
+ });
382
+
383
+ it('it should include errors if provided', () => {
384
+ sinon.stub(cd, 'getOrigin').returns({origin: 'fake-origin'});
385
+ const submitToCallDiagnosticsSpy = sinon.spy(cd, 'submitToCallDiagnostics');
386
+
387
+ const options = {
388
+ meetingId: fakeMeeting.id,
389
+ mediaConnections: [{mediaAgentAlias: 'alias', mediaAgentGroupId: '1'}],
390
+ rawError: {
391
+ body: {
392
+ errorCode: 2409005,
393
+ },
394
+ },
395
+ };
396
+
397
+ cd.submitClientEvent({
398
+ name: 'client.alert.displayed',
399
+ options,
400
+ });
401
+
402
+ assert.calledWith(submitToCallDiagnosticsSpy, {
403
+ event: {
404
+ canProceed: true,
405
+ eventData: {
406
+ webClientDomain: 'whatever',
407
+ },
408
+ identifiers: {
409
+ correlationId: 'correlationId',
410
+ deviceId: 'deviceUrl',
411
+ locusId: 'url',
412
+ locusStartTime: 'lastActive',
413
+ locusUrl: 'locus/url',
414
+ mediaAgentAlias: 'alias',
415
+ mediaAgentGroupId: '1',
416
+ orgId: 'orgId',
417
+ userId: 'userId',
418
+ },
419
+ errors: [
420
+ {
421
+ category: 'expected',
422
+ errorDescription: 'StartRecordingFailed',
423
+ fatal: true,
424
+ name: 'other',
425
+ shownToUser: false,
426
+ serviceErrorCode: 2409005,
427
+ errorCode: 4029,
428
+ },
429
+ ],
430
+ loginType: 'login-ci',
431
+ name: 'client.alert.displayed',
432
+ userType: 'host',
433
+ },
434
+ eventId: 'my-fake-id',
435
+ origin: {
436
+ origin: 'fake-origin',
437
+ },
438
+ originTime: {
439
+ sent: 'not_defined_yet',
440
+ triggered: now.toISOString(),
441
+ },
442
+ senderCountryCode: 'UK',
443
+ version: 1,
444
+ });
445
+ });
446
+
447
+ it('should include errors in payload if provided via payload', () => {
448
+ sinon.stub(cd, 'getOrigin').returns({origin: 'fake-origin'});
449
+ const submitToCallDiagnosticsSpy = sinon.spy(cd, 'submitToCallDiagnostics');
450
+
451
+ const options = {
452
+ meetingId: fakeMeeting.id,
453
+ mediaConnections: [{mediaAgentAlias: 'alias', mediaAgentGroupId: '1'}],
454
+ };
455
+
456
+ cd.submitClientEvent({
457
+ name: 'client.alert.displayed',
458
+ payload: {
459
+ errors: [
460
+ {
461
+ name: 'locus.response',
462
+ fatal: true,
463
+ category: 'signaling',
464
+ shownToUser: false,
465
+ },
466
+ ],
467
+ },
468
+ options,
469
+ });
470
+
471
+ assert.calledWith(submitToCallDiagnosticsSpy, {
472
+ event: {
473
+ canProceed: true,
474
+ eventData: {
475
+ webClientDomain: 'whatever',
476
+ },
477
+ identifiers: {
478
+ correlationId: 'correlationId',
479
+ deviceId: 'deviceUrl',
480
+ locusId: 'url',
481
+ locusStartTime: 'lastActive',
482
+ locusUrl: 'locus/url',
483
+ mediaAgentAlias: 'alias',
484
+ mediaAgentGroupId: '1',
485
+ orgId: 'orgId',
486
+ userId: 'userId',
487
+ },
488
+ errors: [
489
+ {
490
+ name: 'locus.response',
491
+ fatal: true,
492
+ category: 'signaling',
493
+ shownToUser: false,
494
+ },
495
+ ],
496
+ loginType: 'login-ci',
497
+ name: 'client.alert.displayed',
498
+ userType: 'host',
499
+ },
500
+ eventId: 'my-fake-id',
501
+ origin: {
502
+ origin: 'fake-origin',
503
+ },
504
+ originTime: {
505
+ sent: 'not_defined_yet',
506
+ triggered: now.toISOString(),
507
+ },
508
+ senderCountryCode: 'UK',
509
+ version: 1,
510
+ });
511
+ });
512
+
513
+ it('should throw if meetingId nor correlationId not provided', () => {
514
+ assert.throws(() =>
515
+ cd.submitClientEvent({
516
+ name: 'client.alert.displayed',
517
+ })
518
+ );
519
+ });
520
+
521
+ it('should send behavioral event if meetingId provided but meeting is undefined', () => {
522
+ webex.meetings.meetingCollection.get = sinon.stub().returns(undefined);
523
+ cd.submitClientEvent({name: 'client.alert.displayed', options: {meetingId: 'meetingId'}});
524
+ assert.calledWith(
525
+ webex.internal.metrics.submitClientMetrics,
526
+ 'js_sdk_call_diagnostic_event_failed_to_send',
527
+ {
528
+ fields: {meetingId: 'meetingId', name: 'client.alert.displayed'},
529
+ }
530
+ );
531
+ });
532
+ });
533
+
534
+ it('should send request to call diagnostic batcher', () => {
535
+ const requestStub = sinon.stub();
536
+ //@ts-ignore
537
+ cd.callDiagnosticEventsBatcher = {request: requestStub};
538
+ //@ts-ignore
539
+ cd.submitToCallDiagnostics({event: 'test'});
540
+ assert.calledWith(requestStub, {eventPayload: {event: 'test'}, type: ['diagnostic-event']});
541
+ });
542
+
543
+ describe('#submitMQE', () => {
544
+ it('submits the event correctly', () => {
545
+ const prepareDiagnosticEventSpy = sinon.spy(cd, 'prepareDiagnosticEvent');
546
+ const submitToCallDiagnosticsSpy = sinon.spy(cd, 'submitToCallDiagnostics');
547
+ const getErrorPayloadForClientErrorCodeSpy = sinon.spy(
548
+ cd,
549
+ 'getErrorPayloadForClientErrorCode'
550
+ );
551
+ const getIdentifiersSpy = sinon.spy(cd, 'getIdentifiers');
552
+ sinon.stub(cd, 'getOrigin').returns({origin: 'fake-origin'});
553
+ const options = {
554
+ networkType: 'wifi' as const,
555
+ meetingId: fakeMeeting.id,
556
+ };
557
+
558
+ cd.submitMQE({
559
+ name: 'client.mediaquality.event',
560
+ payload: {
561
+ //@ts-ignore
562
+ intervals: [{}],
563
+ },
564
+ options,
565
+ });
566
+
567
+ assert.calledWith(getIdentifiersSpy, {
568
+ meeting: fakeMeeting,
569
+ mediaConnections: undefined,
570
+ });
571
+ assert.notCalled(getErrorPayloadForClientErrorCodeSpy);
572
+ assert.calledWith(
573
+ prepareDiagnosticEventSpy,
574
+ {
575
+ name: 'client.mediaquality.event',
576
+ canProceed: true,
577
+ identifiers: {
578
+ correlationId: 'correlationId',
579
+ userId: 'userId',
580
+ deviceId: 'deviceUrl',
581
+ orgId: 'orgId',
582
+ locusUrl: 'locus/url',
583
+ locusId: 'url',
584
+ locusStartTime: 'lastActive',
585
+ },
586
+ eventData: {webClientDomain: 'whatever'},
587
+ intervals: [{}],
588
+ sourceMetadata: {
589
+ applicationSoftwareType: 'webex-js-sdk',
590
+ applicationSoftwareVersion: 'webex-version',
591
+ mediaEngineSoftwareType: 'browser',
592
+ mediaEngineSoftwareVersion: getOSVersion(),
593
+ startTime: now.toISOString(),
594
+ },
595
+ },
596
+ options
597
+ );
598
+
599
+ assert.calledWith(submitToCallDiagnosticsSpy, {
600
+ eventId: 'my-fake-id',
601
+ version: 1,
602
+ origin: {origin: 'fake-origin'},
603
+ originTime: {triggered: now.toISOString(), sent: 'not_defined_yet'},
604
+ senderCountryCode: 'UK',
605
+ event: {
606
+ name: 'client.mediaquality.event',
607
+ canProceed: true,
608
+ identifiers: {
609
+ correlationId: 'correlationId',
610
+ userId: 'userId',
611
+ deviceId: 'deviceUrl',
612
+ orgId: 'orgId',
613
+ locusUrl: 'locus/url',
614
+ locusId: 'url',
615
+ locusStartTime: 'lastActive',
616
+ },
617
+ eventData: {webClientDomain: 'whatever'},
618
+ intervals: [{}],
619
+ sourceMetadata: {
620
+ applicationSoftwareType: 'webex-js-sdk',
621
+ applicationSoftwareVersion: 'webex-version',
622
+ mediaEngineSoftwareType: 'browser',
623
+ mediaEngineSoftwareVersion: getOSVersion(),
624
+ startTime: now.toISOString(),
625
+ },
626
+ },
627
+ });
628
+ });
629
+
630
+ it('throws if meeting id not provided', () => {
631
+ assert.throws(() =>
632
+ cd.submitMQE({
633
+ name: 'client.mediaquality.event',
634
+ payload: {
635
+ //@ts-ignore
636
+ intervals: [{}],
637
+ },
638
+ //@ts-ignore
639
+ options: {meetingId: undefined, networkType: 'wifi'},
640
+ })
641
+ );
642
+ });
643
+
644
+ it('should send behavioral event if meeting is undefined', () => {
645
+ webex.meetings.meetingCollection.get = sinon.stub().returns(undefined);
646
+ cd.submitMQE({
647
+ name: 'client.mediaquality.event',
648
+ payload: {
649
+ //@ts-ignore
650
+ intervals: [{}],
651
+ },
652
+ options: {meetingId: 'meetingId'},
653
+ });
654
+ assert.calledWith(
655
+ webex.internal.metrics.submitClientMetrics,
656
+ 'js_sdk_call_diagnostic_event_failed_to_send',
657
+ {
658
+ fields: {meetingId: 'meetingId', name: 'client.mediaquality.event'},
659
+ }
660
+ );
661
+ });
662
+ });
663
+ describe('#getErrorPayloadForClientErrorCode', () => {
664
+ it('it should grab the payload for client error code correctly', () => {
665
+ const res = cd.getErrorPayloadForClientErrorCode({
666
+ clientErrorCode: 4008,
667
+ serviceErrorCode: 10000,
668
+ });
669
+ assert.deepEqual(res, {
670
+ category: 'signaling',
671
+ errorDescription: 'NewLocusError',
672
+ fatal: true,
673
+ name: 'other',
674
+ shownToUser: false,
675
+ errorCode: 4008,
676
+ serviceErrorCode: 10000,
677
+ });
678
+ });
679
+
680
+ it('it should return undefined if trying to get payload for client error code that doesnt exist', () => {
681
+ const res = cd.getErrorPayloadForClientErrorCode({
682
+ clientErrorCode: 123456,
683
+ serviceErrorCode: 100000,
684
+ });
685
+ assert.deepEqual(res, undefined);
686
+ });
687
+ });
688
+
689
+ describe('#generateClientEventErrorPayload', () => {
690
+ it('should generate event error payload correctly', () => {
691
+ const res = cd.generateClientEventErrorPayload({body: {errorCode: 2409005}});
692
+ assert.deepEqual(res, {
693
+ category: 'expected',
694
+ errorDescription: 'StartRecordingFailed',
695
+ fatal: true,
696
+ name: 'other',
697
+ shownToUser: false,
698
+ errorCode: 4029,
699
+ serviceErrorCode: 2409005,
700
+ });
701
+ });
702
+
703
+ it('should return default new locus event error payload correctly if locus error', () => {
704
+ const res = cd.generateClientEventErrorPayload({body: {errorCode: 2400000}});
705
+ assert.deepEqual(res, {
706
+ category: 'signaling',
707
+ errorDescription: 'NewLocusError',
708
+ fatal: true,
709
+ name: 'other',
710
+ shownToUser: false,
711
+ serviceErrorCode: 2400000,
712
+ errorCode: 4008,
713
+ });
714
+ });
715
+
716
+ it('should return default meeting info lookup error payload correctly if not locus error', () => {
717
+ const res = cd.generateClientEventErrorPayload({body: {errorCode: 9400000}});
718
+ assert.deepEqual(res, {
719
+ category: 'signaling',
720
+ errorDescription: 'MeetingInfoLookupError',
721
+ fatal: true,
722
+ name: 'other',
723
+ shownToUser: false,
724
+ serviceErrorCode: 9400000,
725
+ errorCode: 4100,
726
+ });
727
+ });
728
+
729
+ it('should return undefined if no error code provided', () => {
730
+ const res = cd.generateClientEventErrorPayload({body: {errorCode: undefined}});
731
+ assert.deepEqual(res, undefined);
732
+ });
733
+ });
734
+
735
+ describe('#getCurLoginType', () => {
736
+ it('returns login-ci if not unverified guest', () => {
737
+ webex.credentials.isUnverifiedGuest = false;
738
+ assert.deepEqual(cd.getCurLoginType(), 'login-ci');
739
+ });
740
+ it('returns unverified guest', () => {
741
+ webex.credentials.isUnverifiedGuest = true;
742
+ assert.deepEqual(cd.getCurLoginType(), 'unverified-guest');
743
+ });
744
+ });
745
+
746
+ describe('#buildClientEventFetchRequestOptions', () => {
747
+ it('returns expected options', async () => {
748
+ const options = {
749
+ meetingId: fakeMeeting.id,
750
+ };
751
+
752
+ const triggered = new Date();
753
+ const fetchOptions = await cd.buildClientEventFetchRequestOptions({
754
+ name: 'client.exit.app',
755
+ payload: {trigger: 'user-interaction', canProceed: false},
756
+ options,
757
+ });
758
+
759
+ assert.deepEqual(fetchOptions, {
760
+ body: {
761
+ metrics: [
762
+ {
763
+ eventPayload: {
764
+ event: {
765
+ canProceed: false,
766
+ eventData: {
767
+ webClientDomain: 'whatever',
768
+ },
769
+ identifiers: {
770
+ correlationId: 'correlationId',
771
+ deviceId: 'deviceUrl',
772
+ locusId: 'url',
773
+ locusStartTime: 'lastActive',
774
+ locusUrl: 'locus/url',
775
+ orgId: 'orgId',
776
+ userId: 'userId',
777
+ },
778
+ loginType: 'login-ci',
779
+ name: 'client.exit.app',
780
+ trigger: 'user-interaction',
781
+ userType: 'host',
782
+ },
783
+ eventId: 'my-fake-id',
784
+ origin: {
785
+ buildType: 'test',
786
+ clientInfo: {
787
+ clientType: 'TEAMS_CLIENT',
788
+ clientVersion: 'webex-js-sdk/webex-version',
789
+ localNetworkPrefix:
790
+ Utils.anonymizeIPAddress(webex.meetings.geoHintInfo?.clientAddress) ||
791
+ undefined,
792
+ os: getOSNameInternal() || 'unknown',
793
+ osVersion: getOSVersion(),
794
+ subClientType: 'WEB_APP',
795
+ },
796
+ environment: 'meeting_evn',
797
+ name: 'endpoint',
798
+ networkType: 'unknown',
799
+ userAgent,
800
+ },
801
+ originTime: {
802
+ sent: 'not_defined_yet',
803
+ triggered: triggered.toISOString(),
804
+ },
805
+ senderCountryCode: webex.meetings.geoHintInfo?.countryCode,
806
+ version: 1,
807
+ },
808
+ type: ['diagnostic-event'],
809
+ },
810
+ ],
811
+ },
812
+ foo: 'bar',
813
+ method: 'POST',
814
+ resource: 'clientmetrics',
815
+ service: 'metrics',
816
+ });
817
+ });
818
+ });
819
+ });
820
+ });