@webex/internal-plugin-metrics 3.12.0-next.4 → 3.12.0-next.40

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 (48) hide show
  1. package/dist/automated-user.js +19 -0
  2. package/dist/automated-user.js.map +1 -0
  3. package/dist/batcher.js +3 -0
  4. package/dist/batcher.js.map +1 -1
  5. package/dist/call-diagnostic/call-diagnostic-metrics-latencies.js +638 -65
  6. package/dist/call-diagnostic/call-diagnostic-metrics-latencies.js.map +1 -1
  7. package/dist/call-diagnostic/call-diagnostic-metrics.js +167 -76
  8. package/dist/call-diagnostic/call-diagnostic-metrics.js.map +1 -1
  9. package/dist/call-diagnostic/call-diagnostic-metrics.util.js +5 -0
  10. package/dist/call-diagnostic/call-diagnostic-metrics.util.js.map +1 -1
  11. package/dist/call-diagnostic/config.js +16 -3
  12. package/dist/call-diagnostic/config.js.map +1 -1
  13. package/dist/config.js +1 -0
  14. package/dist/config.js.map +1 -1
  15. package/dist/index.js +15 -0
  16. package/dist/index.js.map +1 -1
  17. package/dist/metrics.js +1 -1
  18. package/dist/metrics.types.js +4 -0
  19. package/dist/metrics.types.js.map +1 -1
  20. package/dist/new-metrics.js +16 -6
  21. package/dist/new-metrics.js.map +1 -1
  22. package/dist/types/automated-user.d.ts +2 -0
  23. package/dist/types/call-diagnostic/call-diagnostic-metrics-latencies.d.ts +208 -5
  24. package/dist/types/call-diagnostic/call-diagnostic-metrics.d.ts +67 -14
  25. package/dist/types/call-diagnostic/config.d.ts +4 -0
  26. package/dist/types/config.d.ts +1 -0
  27. package/dist/types/index.d.ts +5 -3
  28. package/dist/types/metrics.types.d.ts +4 -2
  29. package/dist/types/new-metrics.d.ts +4 -0
  30. package/package.json +12 -11
  31. package/src/automated-user.ts +16 -0
  32. package/src/batcher.js +4 -0
  33. package/src/call-diagnostic/call-diagnostic-metrics-latencies.ts +800 -73
  34. package/src/call-diagnostic/call-diagnostic-metrics.ts +101 -15
  35. package/src/call-diagnostic/call-diagnostic-metrics.util.ts +5 -0
  36. package/src/call-diagnostic/config.ts +14 -0
  37. package/src/config.js +1 -0
  38. package/src/index.ts +5 -0
  39. package/src/metrics.types.ts +16 -3
  40. package/src/new-metrics.ts +12 -4
  41. package/test/unit/spec/automated-user.ts +43 -0
  42. package/test/unit/spec/batcher.js +43 -0
  43. package/test/unit/spec/call-diagnostic/call-diagnostic-metrics-batcher.ts +14 -2
  44. package/test/unit/spec/call-diagnostic/call-diagnostic-metrics-latencies.ts +1434 -303
  45. package/test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts +852 -159
  46. package/test/unit/spec/call-diagnostic/call-diagnostic-metrics.util.ts +27 -0
  47. package/test/unit/spec/new-metrics.ts +39 -2
  48. package/test/unit/spec/prelogin-metrics-batcher.ts +71 -36
@@ -9,6 +9,7 @@ import {
9
9
  CallDiagnosticMetrics,
10
10
  getOSNameInternal,
11
11
  CallDiagnosticUtils,
12
+ AutomatedUserUtils,
12
13
  config,
13
14
  } from '@webex/internal-plugin-metrics';
14
15
  import uuid from 'uuid';
@@ -34,6 +35,7 @@ describe('internal-plugin-metrics', () => {
34
35
  var now = new Date();
35
36
 
36
37
  let cd: CallDiagnosticMetrics;
38
+ let isAutomatedUserStub: sinon.SinonStub;
37
39
 
38
40
  const fakeMeeting = {
39
41
  id: '1',
@@ -155,6 +157,7 @@ describe('internal-plugin-metrics', () => {
155
157
  sinon.useFakeTimers(now.getTime());
156
158
  cd = new CallDiagnosticMetrics({}, {parent: webex});
157
159
  sinon.stub(uuid, 'v4').returns('my-fake-id');
160
+ isAutomatedUserStub = sinon.stub(AutomatedUserUtils, 'isAutomatedUser').returns(false);
158
161
  cd.setDeviceInfo(webex.internal.device);
159
162
  });
160
163
 
@@ -361,6 +364,23 @@ describe('internal-plugin-metrics', () => {
361
364
  });
362
365
  });
363
366
 
367
+ it('should build origin correctly when the meetings plugin is not available (before webex is ready)', () => {
368
+ // meetings plugin, geoHintInfo and meetingCollection are all absent before webex.ready
369
+ webex.meetings = undefined;
370
+
371
+ //@ts-ignore
372
+ const res = cd.getOrigin(
373
+ {subClientType: 'WEB_APP', clientType: 'TEAMS_CLIENT'},
374
+ fakeMeeting.id
375
+ );
376
+
377
+ assert.equal(res.clientInfo.clientType, 'TEAMS_CLIENT');
378
+ assert.equal(res.clientInfo.subClientType, 'WEB_APP');
379
+ assert.isUndefined(res.clientInfo.publicNetworkPrefix);
380
+ assert.isUndefined(res.clientInfo.localNetworkPrefix);
381
+ assert.equal(res.name, 'endpoint');
382
+ });
383
+
364
384
  it('builds origin correctly, when overriding clientVersion', () => {
365
385
  webex.meetings.config.metrics.clientVersion = '43.9.0.1234';
366
386
 
@@ -391,6 +411,64 @@ describe('internal-plugin-metrics', () => {
391
411
  });
392
412
  });
393
413
 
414
+ it('builds origin correctly with browser details provided by the host client', () => {
415
+ // host resolves wrapper browsers (WebOS, Electron) to the engine that determines capability
416
+ webex.meetings.config.metrics.browser = 'WebOS Browser';
417
+ webex.meetings.config.metrics.browserVersion = '108.0.0.0';
418
+
419
+ //@ts-ignore
420
+ const res = cd.getOrigin(
421
+ {subClientType: 'WEB_APP', clientType: 'TEAMS_CLIENT'},
422
+ fakeMeeting.id
423
+ );
424
+
425
+ assert.equal(res.clientInfo.browser, 'WebOS Browser');
426
+ assert.equal(res.clientInfo.browserVersion, '108.0.0.0');
427
+ });
428
+
429
+ it('falls back to SDK browser detection when the host provides nothing', () => {
430
+ //@ts-ignore
431
+ const res = cd.getOrigin(
432
+ {subClientType: 'WEB_APP', clientType: 'TEAMS_CLIENT'},
433
+ fakeMeeting.id
434
+ );
435
+
436
+ assert.equal(res.clientInfo.browser, getBrowserName());
437
+ assert.equal(res.clientInfo.browserVersion, getBrowserVersion());
438
+ });
439
+
440
+ it('builds origin correctly with the browser support flags from config', () => {
441
+ // `false` is the meaningful "unsupported family" signal, so it must survive a truthy check
442
+ webex.meetings.config.metrics.isSupportedBrowserFamily = false;
443
+ webex.meetings.config.metrics.isOutdatedBrowserVersion = true;
444
+
445
+ //@ts-ignore
446
+ const res = cd.getOrigin(
447
+ {subClientType: 'WEB_APP', clientType: 'TEAMS_CLIENT'},
448
+ fakeMeeting.id
449
+ );
450
+
451
+ assert.deepEqual(res, {
452
+ clientInfo: {
453
+ browser: getBrowserName(),
454
+ browserVersion: getBrowserVersion(),
455
+ clientType: 'TEAMS_CLIENT',
456
+ clientVersion: 'webex-js-sdk/webex-version',
457
+ publicNetworkPrefix: '1.3.4.0',
458
+ localNetworkPrefix: '192.168.1.80',
459
+ os: getOSNameInternal(),
460
+ osVersion: getOSVersion() || 'unknown',
461
+ subClientType: 'WEB_APP',
462
+ isSupportedBrowserFamily: false,
463
+ isOutdatedBrowserVersion: true,
464
+ },
465
+ environment: 'meeting_evn',
466
+ name: 'endpoint',
467
+ networkType: 'unknown',
468
+ userAgent,
469
+ });
470
+ });
471
+
394
472
  it('should build origin correctly with no meeting or stats analyzer', () => {
395
473
  //@ts-ignore
396
474
  const res = cd.getOrigin();
@@ -857,6 +935,103 @@ describe('internal-plugin-metrics', () => {
857
935
  });
858
936
  });
859
937
 
938
+ describe('#prepareDiagnosticEvent isAutomatedUser field', () => {
939
+ it('should set isAutomatedUser to false when window is not defined', () => {
940
+ const options = {meetingId: fakeMeeting.id};
941
+ sinon.stub(cd, 'getOrigin').returns({origin: 'fake-origin'});
942
+
943
+ const res = cd.prepareDiagnosticEvent(
944
+ {
945
+ canProceed: true,
946
+ identifiers: {correlationId: 'test-id'},
947
+ name: 'client.alert.displayed',
948
+ isAutomatedUser: false,
949
+ userActivation: undefined,
950
+ },
951
+ options
952
+ );
953
+
954
+ // In the test environment, isAutomatedUser should be false since we're not in a webdriver environment
955
+ assert.isFalse(
956
+ res.event.isAutomatedUser,
957
+ 'isAutomatedUser should be false in non-webdriver test environment'
958
+ );
959
+ });
960
+
961
+ it('should include isAutomatedUser field in the returned event', () => {
962
+ const options = {meetingId: fakeMeeting.id};
963
+ sinon.stub(cd, 'getOrigin').returns({origin: 'fake-origin'});
964
+
965
+ const res = cd.prepareDiagnosticEvent(
966
+ {
967
+ canProceed: true,
968
+ identifiers: {correlationId: 'test-id'},
969
+ name: 'client.alert.displayed',
970
+ isAutomatedUser: false,
971
+ userActivation: undefined,
972
+ },
973
+ options
974
+ );
975
+
976
+ // Verify the isAutomatedUser field is present in the event
977
+ assert.isDefined(res.event.isAutomatedUser, 'isAutomatedUser field should be defined');
978
+ assert.isBoolean(res.event.isAutomatedUser, 'isAutomatedUser should be a boolean');
979
+ });
980
+
981
+ it('should set isAutomatedUser to true when the user is automated', () => {
982
+ isAutomatedUserStub.returns(true);
983
+ const prepareDiagnosticEventSpy = sinon.spy(cd, 'prepareDiagnosticEvent');
984
+ sinon.stub(cd, 'getOrigin').returns({origin: 'fake-origin'});
985
+ cd.setMercuryConnectedStatus(true);
986
+
987
+ cd.submitClientEvent({
988
+ name: 'client.alert.displayed',
989
+ options: {correlationId: 'correlationId'},
990
+ });
991
+
992
+ assert.isTrue(
993
+ prepareDiagnosticEventSpy.firstCall.args[0].isAutomatedUser,
994
+ 'isAutomatedUser should be true for an automated user'
995
+ );
996
+ });
997
+ });
998
+
999
+ describe('#getUserActivation', () => {
1000
+ let originalDescriptor;
1001
+
1002
+ beforeEach(() => {
1003
+ originalDescriptor = Object.getOwnPropertyDescriptor(global, 'navigator');
1004
+ });
1005
+
1006
+ afterEach(() => {
1007
+ if (originalDescriptor) {
1008
+ Object.defineProperty(global, 'navigator', originalDescriptor);
1009
+ } else {
1010
+ delete (global as any).navigator;
1011
+ }
1012
+ });
1013
+
1014
+ it('should return the userActivation state when navigator.userActivation is available', () => {
1015
+ Object.defineProperty(global, 'navigator', {
1016
+ value: {userActivation: {hasBeenActive: true, isActive: false}},
1017
+ configurable: true,
1018
+ writable: true,
1019
+ });
1020
+
1021
+ assert.deepEqual(cd.getUserActivation(), {hasBeenActive: true, isActive: false});
1022
+ });
1023
+
1024
+ it('should return undefined when navigator.userActivation is not available', () => {
1025
+ Object.defineProperty(global, 'navigator', {
1026
+ value: {},
1027
+ configurable: true,
1028
+ writable: true,
1029
+ });
1030
+
1031
+ assert.isUndefined(cd.getUserActivation());
1032
+ });
1033
+ });
1034
+
860
1035
  describe('#submitClientEvent', () => {
861
1036
  it('should submit client event successfully with meetingId', () => {
862
1037
  const prepareDiagnosticEventSpy = sinon.spy(cd, 'prepareDiagnosticEvent');
@@ -907,11 +1082,15 @@ describe('internal-plugin-metrics', () => {
907
1082
  userId: 'userId',
908
1083
  },
909
1084
  loginType: 'login-ci',
1085
+ telemetryOptOut: undefined,
910
1086
  name: 'client.alert.displayed',
911
1087
  userType: 'host',
912
1088
  isConvergedArchitectureEnabled: undefined,
913
1089
  webexSubServiceType: undefined,
914
1090
  webClientPreload: undefined,
1091
+ isVipMeeting: false,
1092
+ isAutomatedUser: false,
1093
+ userActivation: undefined,
915
1094
  },
916
1095
  options
917
1096
  );
@@ -935,11 +1114,15 @@ describe('internal-plugin-metrics', () => {
935
1114
  userId: 'userId',
936
1115
  },
937
1116
  loginType: 'login-ci',
1117
+ telemetryOptOut: undefined,
938
1118
  name: 'client.alert.displayed',
939
1119
  userType: 'host',
940
1120
  isConvergedArchitectureEnabled: undefined,
941
1121
  webexSubServiceType: undefined,
942
1122
  webClientPreload: undefined,
1123
+ isVipMeeting: false,
1124
+ isAutomatedUser: false,
1125
+ userActivation: undefined,
943
1126
  },
944
1127
  eventId: 'my-fake-id',
945
1128
  origin: {
@@ -974,11 +1157,15 @@ describe('internal-plugin-metrics', () => {
974
1157
  userId: 'userId',
975
1158
  },
976
1159
  loginType: 'login-ci',
1160
+ telemetryOptOut: undefined,
977
1161
  name: 'client.alert.displayed',
978
1162
  userType: 'host',
979
1163
  isConvergedArchitectureEnabled: undefined,
980
1164
  webexSubServiceType: undefined,
981
1165
  webClientPreload: undefined,
1166
+ isVipMeeting: false,
1167
+ isAutomatedUser: false,
1168
+ userActivation: undefined,
982
1169
  },
983
1170
  eventId: 'my-fake-id',
984
1171
  origin: {
@@ -1049,11 +1236,15 @@ describe('internal-plugin-metrics', () => {
1049
1236
  userId: 'userId',
1050
1237
  },
1051
1238
  loginType: 'login-ci',
1239
+ telemetryOptOut: undefined,
1052
1240
  name: 'client.alert.displayed',
1053
1241
  userType: 'host',
1054
1242
  isConvergedArchitectureEnabled: undefined,
1055
1243
  webexSubServiceType: undefined,
1056
1244
  webClientPreload: undefined,
1245
+ isVipMeeting: false,
1246
+ isAutomatedUser: false,
1247
+ userActivation: undefined,
1057
1248
  },
1058
1249
  options
1059
1250
  );
@@ -1077,11 +1268,15 @@ describe('internal-plugin-metrics', () => {
1077
1268
  userId: 'userId',
1078
1269
  },
1079
1270
  loginType: 'login-ci',
1271
+ telemetryOptOut: undefined,
1080
1272
  name: 'client.alert.displayed',
1081
1273
  userType: 'host',
1082
1274
  isConvergedArchitectureEnabled: undefined,
1083
1275
  webexSubServiceType: undefined,
1084
1276
  webClientPreload: undefined,
1277
+ isVipMeeting: false,
1278
+ isAutomatedUser: false,
1279
+ userActivation: undefined,
1085
1280
  },
1086
1281
  eventId: 'my-fake-id',
1087
1282
  origin: {
@@ -1116,11 +1311,15 @@ describe('internal-plugin-metrics', () => {
1116
1311
  userId: 'userId',
1117
1312
  },
1118
1313
  loginType: 'login-ci',
1314
+ telemetryOptOut: undefined,
1119
1315
  name: 'client.alert.displayed',
1120
1316
  userType: 'host',
1121
1317
  isConvergedArchitectureEnabled: undefined,
1122
1318
  webexSubServiceType: undefined,
1123
1319
  webClientPreload: undefined,
1320
+ isVipMeeting: false,
1321
+ isAutomatedUser: false,
1322
+ userActivation: undefined,
1124
1323
  },
1125
1324
  eventId: 'my-fake-id',
1126
1325
  origin: {
@@ -1192,11 +1391,15 @@ describe('internal-plugin-metrics', () => {
1192
1391
  userId: 'userId',
1193
1392
  },
1194
1393
  loginType: 'login-ci',
1394
+ telemetryOptOut: undefined,
1195
1395
  name: 'client.alert.displayed',
1196
1396
  userType: 'host',
1197
1397
  isConvergedArchitectureEnabled: undefined,
1198
1398
  webexSubServiceType: undefined,
1199
1399
  webClientPreload: undefined,
1400
+ isVipMeeting: false,
1401
+ isAutomatedUser: false,
1402
+ userActivation: undefined,
1200
1403
  },
1201
1404
  options
1202
1405
  );
@@ -1221,11 +1424,15 @@ describe('internal-plugin-metrics', () => {
1221
1424
  userId: 'userId',
1222
1425
  },
1223
1426
  loginType: 'login-ci',
1427
+ telemetryOptOut: undefined,
1224
1428
  name: 'client.alert.displayed',
1225
1429
  userType: 'host',
1226
1430
  isConvergedArchitectureEnabled: undefined,
1227
1431
  webexSubServiceType: undefined,
1228
1432
  webClientPreload: undefined,
1433
+ isVipMeeting: false,
1434
+ isAutomatedUser: false,
1435
+ userActivation: undefined,
1229
1436
  },
1230
1437
  eventId: 'my-fake-id',
1231
1438
  origin: {
@@ -1261,11 +1468,15 @@ describe('internal-plugin-metrics', () => {
1261
1468
  userId: 'userId',
1262
1469
  },
1263
1470
  loginType: 'login-ci',
1471
+ telemetryOptOut: undefined,
1264
1472
  name: 'client.alert.displayed',
1265
1473
  userType: 'host',
1266
1474
  isConvergedArchitectureEnabled: undefined,
1267
1475
  webexSubServiceType: undefined,
1268
1476
  webClientPreload: undefined,
1477
+ isVipMeeting: false,
1478
+ isAutomatedUser: false,
1479
+ userActivation: undefined,
1269
1480
  },
1270
1481
  eventId: 'my-fake-id',
1271
1482
  origin: {
@@ -1336,11 +1547,15 @@ describe('internal-plugin-metrics', () => {
1336
1547
  userId: 'userId',
1337
1548
  },
1338
1549
  loginType: 'login-ci',
1550
+ telemetryOptOut: undefined,
1339
1551
  webClientPreload: undefined,
1340
1552
  name: 'client.alert.displayed',
1341
1553
  userType: 'host',
1342
1554
  isConvergedArchitectureEnabled: undefined,
1343
1555
  webexSubServiceType: undefined,
1556
+ isVipMeeting: false,
1557
+ isAutomatedUser: false,
1558
+ userActivation: undefined,
1344
1559
  },
1345
1560
  options
1346
1561
  );
@@ -1365,11 +1580,15 @@ describe('internal-plugin-metrics', () => {
1365
1580
  userId: 'userId',
1366
1581
  },
1367
1582
  loginType: 'login-ci',
1583
+ telemetryOptOut: undefined,
1368
1584
  webClientPreload: undefined,
1369
1585
  name: 'client.alert.displayed',
1370
1586
  userType: 'host',
1371
1587
  isConvergedArchitectureEnabled: undefined,
1372
1588
  webexSubServiceType: undefined,
1589
+ isVipMeeting: false,
1590
+ isAutomatedUser: false,
1591
+ userActivation: undefined,
1373
1592
  },
1374
1593
  eventId: 'my-fake-id',
1375
1594
  origin: {
@@ -1405,11 +1624,15 @@ describe('internal-plugin-metrics', () => {
1405
1624
  userId: 'userId',
1406
1625
  },
1407
1626
  loginType: 'login-ci',
1627
+ telemetryOptOut: undefined,
1408
1628
  webClientPreload: undefined,
1409
1629
  name: 'client.alert.displayed',
1410
1630
  userType: 'host',
1411
1631
  isConvergedArchitectureEnabled: undefined,
1412
1632
  webexSubServiceType: undefined,
1633
+ isVipMeeting: false,
1634
+ isAutomatedUser: false,
1635
+ userActivation: undefined,
1413
1636
  },
1414
1637
  eventId: 'my-fake-id',
1415
1638
  origin: {
@@ -1480,6 +1703,7 @@ describe('internal-plugin-metrics', () => {
1480
1703
  userId: 'userId',
1481
1704
  },
1482
1705
  loginType: 'login-ci',
1706
+ telemetryOptOut: undefined,
1483
1707
  name: 'client.alert.displayed',
1484
1708
  userType: 'host',
1485
1709
  userNameInput: 'test',
@@ -1487,6 +1711,9 @@ describe('internal-plugin-metrics', () => {
1487
1711
  isConvergedArchitectureEnabled: undefined,
1488
1712
  webexSubServiceType: undefined,
1489
1713
  webClientPreload: undefined,
1714
+ isVipMeeting: false,
1715
+ isAutomatedUser: false,
1716
+ userActivation: undefined,
1490
1717
  },
1491
1718
  options
1492
1719
  );
@@ -1511,6 +1738,7 @@ describe('internal-plugin-metrics', () => {
1511
1738
  userId: 'userId',
1512
1739
  },
1513
1740
  loginType: 'login-ci',
1741
+ telemetryOptOut: undefined,
1514
1742
  name: 'client.alert.displayed',
1515
1743
  userType: 'host',
1516
1744
  userNameInput: 'test',
@@ -1518,6 +1746,9 @@ describe('internal-plugin-metrics', () => {
1518
1746
  isConvergedArchitectureEnabled: undefined,
1519
1747
  webexSubServiceType: undefined,
1520
1748
  webClientPreload: undefined,
1749
+ isVipMeeting: false,
1750
+ isAutomatedUser: false,
1751
+ userActivation: undefined,
1521
1752
  },
1522
1753
  eventId: 'my-fake-id',
1523
1754
  origin: {
@@ -1553,6 +1784,7 @@ describe('internal-plugin-metrics', () => {
1553
1784
  userId: 'userId',
1554
1785
  },
1555
1786
  loginType: 'login-ci',
1787
+ telemetryOptOut: undefined,
1556
1788
  name: 'client.alert.displayed',
1557
1789
  userType: 'host',
1558
1790
  userNameInput: 'test',
@@ -1560,6 +1792,9 @@ describe('internal-plugin-metrics', () => {
1560
1792
  isConvergedArchitectureEnabled: undefined,
1561
1793
  webexSubServiceType: undefined,
1562
1794
  webClientPreload: undefined,
1795
+ isVipMeeting: false,
1796
+ isAutomatedUser: false,
1797
+ userActivation: undefined,
1563
1798
  },
1564
1799
  eventId: 'my-fake-id',
1565
1800
  origin: {
@@ -1684,8 +1919,11 @@ describe('internal-plugin-metrics', () => {
1684
1919
  userId: 'userId',
1685
1920
  },
1686
1921
  loginType: 'login-ci',
1922
+ telemetryOptOut: undefined,
1687
1923
  name: 'client.alert.displayed',
1688
1924
  webClientPreload: undefined,
1925
+ isAutomatedUser: false,
1926
+ userActivation: undefined,
1689
1927
  },
1690
1928
  options
1691
1929
  );
@@ -1707,8 +1945,11 @@ describe('internal-plugin-metrics', () => {
1707
1945
  userId: 'userId',
1708
1946
  },
1709
1947
  loginType: 'login-ci',
1948
+ telemetryOptOut: undefined,
1710
1949
  name: 'client.alert.displayed',
1711
1950
  webClientPreload: undefined,
1951
+ isAutomatedUser: false,
1952
+ userActivation: undefined,
1712
1953
  },
1713
1954
  eventId: 'my-fake-id',
1714
1955
  origin: {
@@ -1782,8 +2023,11 @@ describe('internal-plugin-metrics', () => {
1782
2023
  userId: 'myPreLoginId',
1783
2024
  },
1784
2025
  loginType: 'login-ci',
2026
+ telemetryOptOut: undefined,
1785
2027
  name: 'client.alert.displayed',
1786
2028
  webClientPreload: undefined,
2029
+ isAutomatedUser: false,
2030
+ userActivation: undefined,
1787
2031
  },
1788
2032
  options
1789
2033
  );
@@ -1811,7 +2055,10 @@ describe('internal-plugin-metrics', () => {
1811
2055
  },
1812
2056
  eventData: {webClientDomain: 'whatever', isMercuryConnected: true},
1813
2057
  loginType: 'login-ci',
2058
+ telemetryOptOut: undefined,
1814
2059
  webClientPreload: undefined,
2060
+ isAutomatedUser: false,
2061
+ userActivation: undefined,
1815
2062
  },
1816
2063
  },
1817
2064
  options.preLoginId
@@ -1873,8 +2120,11 @@ describe('internal-plugin-metrics', () => {
1873
2120
  userNameInput: 'current',
1874
2121
  emailInput: 'current',
1875
2122
  loginType: 'login-ci',
2123
+ telemetryOptOut: undefined,
1876
2124
  name: 'client.alert.displayed',
1877
2125
  webClientPreload: undefined,
2126
+ isAutomatedUser: false,
2127
+ userActivation: undefined,
1878
2128
  },
1879
2129
  options
1880
2130
  );
@@ -1902,9 +2152,12 @@ describe('internal-plugin-metrics', () => {
1902
2152
  },
1903
2153
  eventData: {webClientDomain: 'whatever', isMercuryConnected: true},
1904
2154
  loginType: 'login-ci',
2155
+ telemetryOptOut: undefined,
1905
2156
  userNameInput: 'current',
1906
2157
  emailInput: 'current',
1907
2158
  webClientPreload: undefined,
2159
+ isAutomatedUser: false,
2160
+ userActivation: undefined,
1908
2161
  },
1909
2162
  },
1910
2163
  options.preLoginId
@@ -1944,12 +2197,16 @@ describe('internal-plugin-metrics', () => {
1944
2197
  userId: 'userId',
1945
2198
  },
1946
2199
  loginType: 'fakeLoginType',
2200
+ telemetryOptOut: undefined,
1947
2201
  name: 'client.alert.displayed',
1948
2202
  userType: 'host',
1949
2203
  joinFlowVersion: 'Other',
1950
2204
  isConvergedArchitectureEnabled: undefined,
1951
2205
  webexSubServiceType: undefined,
1952
2206
  webClientPreload: undefined,
2207
+ isVipMeeting: false,
2208
+ isAutomatedUser: false,
2209
+ userActivation: undefined,
1953
2210
  },
1954
2211
  eventId: 'my-fake-id',
1955
2212
  origin: {
@@ -1999,12 +2256,16 @@ describe('internal-plugin-metrics', () => {
1999
2256
  userId: 'userId',
2000
2257
  },
2001
2258
  loginType: 'fakeLoginType',
2259
+ telemetryOptOut: undefined,
2002
2260
  name: 'client.alert.displayed',
2003
2261
  userType: 'host',
2004
2262
  joinFlowVersion: 'Other',
2005
2263
  isConvergedArchitectureEnabled: undefined,
2006
2264
  webexSubServiceType: undefined,
2007
2265
  webClientPreload: undefined,
2266
+ isVipMeeting: false,
2267
+ isAutomatedUser: false,
2268
+ userActivation: undefined,
2008
2269
  },
2009
2270
  eventId: 'my-fake-id',
2010
2271
  origin: {
@@ -2059,8 +2320,11 @@ describe('internal-plugin-metrics', () => {
2059
2320
  userId: 'userId',
2060
2321
  },
2061
2322
  loginType: 'login-ci',
2323
+ telemetryOptOut: undefined,
2062
2324
  name: 'client.alert.displayed',
2063
2325
  webClientPreload: true,
2326
+ isAutomatedUser: false,
2327
+ userActivation: undefined,
2064
2328
  },
2065
2329
  options
2066
2330
  );
@@ -2082,8 +2346,221 @@ describe('internal-plugin-metrics', () => {
2082
2346
  userId: 'userId',
2083
2347
  },
2084
2348
  loginType: 'login-ci',
2349
+ telemetryOptOut: undefined,
2085
2350
  name: 'client.alert.displayed',
2086
2351
  webClientPreload: true,
2352
+ isAutomatedUser: false,
2353
+ userActivation: undefined,
2354
+ },
2355
+ eventId: 'my-fake-id',
2356
+ origin: {
2357
+ origin: 'fake-origin',
2358
+ },
2359
+ originTime: {
2360
+ sent: 'not_defined_yet',
2361
+ triggered: now.toISOString(),
2362
+ },
2363
+ senderCountryCode: 'UK',
2364
+ version: 1,
2365
+ });
2366
+ });
2367
+
2368
+ it('should submit client event with isVipMeeting: false when vipMeeting is false', () => {
2369
+ const prepareDiagnosticEventSpy = sinon.spy(cd, 'prepareDiagnosticEvent');
2370
+ const submitToCallDiagnosticsSpy = sinon.spy(cd, 'submitToCallDiagnostics');
2371
+ const generateClientEventErrorPayloadSpy = sinon.spy(cd, 'generateClientEventErrorPayload');
2372
+ sinon.stub(cd, 'getOrigin').returns({origin: 'fake-origin'});
2373
+
2374
+ webex.meetings.getBasicMeetingInformation = sinon.stub().returns({
2375
+ ...fakeMeeting,
2376
+ meetingInfo: {
2377
+ vipmeeting: false,
2378
+ },
2379
+ });
2380
+
2381
+ const options = {
2382
+ correlationId: 'correlationId',
2383
+ webexConferenceIdStr: 'webexConferenceIdStr1',
2384
+ globalMeetingId: 'globalMeetingId1',
2385
+ sessionCorrelationId: 'sessionCorrelationId1',
2386
+ meetingId: fakeMeeting.id,
2387
+ };
2388
+ cd.setMercuryConnectedStatus(true);
2389
+ cd.submitClientEvent({
2390
+ name: 'client.alert.displayed',
2391
+ options,
2392
+ });
2393
+
2394
+ assert.notCalled(generateClientEventErrorPayloadSpy);
2395
+ assert.calledWith(
2396
+ prepareDiagnosticEventSpy,
2397
+ {
2398
+ canProceed: true,
2399
+ eventData: {
2400
+ webClientDomain: 'whatever',
2401
+ isMercuryConnected: true,
2402
+ },
2403
+ identifiers: {
2404
+ correlationId: 'correlationId',
2405
+ webexConferenceIdStr: 'webexConferenceIdStr1',
2406
+ globalMeetingId: 'globalMeetingId1',
2407
+ sessionCorrelationId: 'sessionCorrelationId1',
2408
+ deviceId: 'deviceUrl',
2409
+ locusId: 'url',
2410
+ locusSessionId: 'locusSessionId',
2411
+ locusStartTime: 'lastActive',
2412
+ locusUrl: 'locus/url',
2413
+ orgId: 'orgId',
2414
+ userId: 'userId',
2415
+ },
2416
+ loginType: 'login-ci',
2417
+ telemetryOptOut: undefined,
2418
+ name: 'client.alert.displayed',
2419
+ userType: 'host',
2420
+ isConvergedArchitectureEnabled: undefined,
2421
+ webexSubServiceType: undefined,
2422
+ webClientPreload: undefined,
2423
+ isVipMeeting: false,
2424
+ isAutomatedUser: false,
2425
+ userActivation: undefined,
2426
+ },
2427
+ options
2428
+ );
2429
+ assert.calledWith(submitToCallDiagnosticsSpy, {
2430
+ event: {
2431
+ canProceed: true,
2432
+ eventData: {
2433
+ webClientDomain: 'whatever',
2434
+ isMercuryConnected: true,
2435
+ },
2436
+ identifiers: {
2437
+ correlationId: 'correlationId',
2438
+ webexConferenceIdStr: 'webexConferenceIdStr1',
2439
+ globalMeetingId: 'globalMeetingId1',
2440
+ sessionCorrelationId: 'sessionCorrelationId1',
2441
+ deviceId: 'deviceUrl',
2442
+ locusId: 'url',
2443
+ locusSessionId: 'locusSessionId',
2444
+ locusStartTime: 'lastActive',
2445
+ locusUrl: 'locus/url',
2446
+ orgId: 'orgId',
2447
+ userId: 'userId',
2448
+ },
2449
+ loginType: 'login-ci',
2450
+ telemetryOptOut: undefined,
2451
+ name: 'client.alert.displayed',
2452
+ userType: 'host',
2453
+ isConvergedArchitectureEnabled: undefined,
2454
+ webexSubServiceType: undefined,
2455
+ webClientPreload: undefined,
2456
+ isVipMeeting: false,
2457
+ isAutomatedUser: false,
2458
+ userActivation: undefined,
2459
+ },
2460
+ eventId: 'my-fake-id',
2461
+ origin: {
2462
+ origin: 'fake-origin',
2463
+ },
2464
+ originTime: {
2465
+ sent: 'not_defined_yet',
2466
+ triggered: now.toISOString(),
2467
+ },
2468
+ senderCountryCode: 'UK',
2469
+ version: 1,
2470
+ });
2471
+ });
2472
+
2473
+ it('should submit client event with isVipMeeting: true when vipMeeting is true', () => {
2474
+ const prepareDiagnosticEventSpy = sinon.spy(cd, 'prepareDiagnosticEvent');
2475
+ const submitToCallDiagnosticsSpy = sinon.spy(cd, 'submitToCallDiagnostics');
2476
+ const generateClientEventErrorPayloadSpy = sinon.spy(cd, 'generateClientEventErrorPayload');
2477
+ sinon.stub(cd, 'getOrigin').returns({origin: 'fake-origin'});
2478
+
2479
+ webex.meetings.getBasicMeetingInformation = sinon.stub().returns({
2480
+ ...fakeMeeting,
2481
+ meetingInfo: {
2482
+ vipmeeting: true,
2483
+ },
2484
+ });
2485
+
2486
+ const options = {
2487
+ correlationId: 'correlationId',
2488
+ webexConferenceIdStr: 'webexConferenceIdStr1',
2489
+ globalMeetingId: 'globalMeetingId1',
2490
+ sessionCorrelationId: 'sessionCorrelationId1',
2491
+ meetingId: fakeMeeting.id,
2492
+ };
2493
+ cd.setMercuryConnectedStatus(true);
2494
+ cd.submitClientEvent({
2495
+ name: 'client.alert.displayed',
2496
+ options,
2497
+ });
2498
+
2499
+ assert.notCalled(generateClientEventErrorPayloadSpy);
2500
+ assert.calledWith(
2501
+ prepareDiagnosticEventSpy,
2502
+ {
2503
+ canProceed: true,
2504
+ eventData: {
2505
+ webClientDomain: 'whatever',
2506
+ isMercuryConnected: true,
2507
+ },
2508
+ identifiers: {
2509
+ correlationId: 'correlationId',
2510
+ webexConferenceIdStr: 'webexConferenceIdStr1',
2511
+ globalMeetingId: 'globalMeetingId1',
2512
+ sessionCorrelationId: 'sessionCorrelationId1',
2513
+ deviceId: 'deviceUrl',
2514
+ locusId: 'url',
2515
+ locusSessionId: 'locusSessionId',
2516
+ locusStartTime: 'lastActive',
2517
+ locusUrl: 'locus/url',
2518
+ orgId: 'orgId',
2519
+ userId: 'userId',
2520
+ },
2521
+ loginType: 'login-ci',
2522
+ telemetryOptOut: undefined,
2523
+ name: 'client.alert.displayed',
2524
+ userType: 'host',
2525
+ isConvergedArchitectureEnabled: undefined,
2526
+ webexSubServiceType: undefined,
2527
+ webClientPreload: undefined,
2528
+ isVipMeeting: true,
2529
+ isAutomatedUser: false,
2530
+ userActivation: undefined,
2531
+ },
2532
+ options
2533
+ );
2534
+ assert.calledWith(submitToCallDiagnosticsSpy, {
2535
+ event: {
2536
+ canProceed: true,
2537
+ eventData: {
2538
+ webClientDomain: 'whatever',
2539
+ isMercuryConnected: true,
2540
+ },
2541
+ identifiers: {
2542
+ correlationId: 'correlationId',
2543
+ webexConferenceIdStr: 'webexConferenceIdStr1',
2544
+ globalMeetingId: 'globalMeetingId1',
2545
+ sessionCorrelationId: 'sessionCorrelationId1',
2546
+ deviceId: 'deviceUrl',
2547
+ locusId: 'url',
2548
+ locusSessionId: 'locusSessionId',
2549
+ locusStartTime: 'lastActive',
2550
+ locusUrl: 'locus/url',
2551
+ orgId: 'orgId',
2552
+ userId: 'userId',
2553
+ },
2554
+ loginType: 'login-ci',
2555
+ telemetryOptOut: undefined,
2556
+ name: 'client.alert.displayed',
2557
+ userType: 'host',
2558
+ isConvergedArchitectureEnabled: undefined,
2559
+ webexSubServiceType: undefined,
2560
+ webClientPreload: undefined,
2561
+ isVipMeeting: true,
2562
+ isAutomatedUser: false,
2563
+ userActivation: undefined,
2087
2564
  },
2088
2565
  eventId: 'my-fake-id',
2089
2566
  origin: {
@@ -2153,11 +2630,15 @@ describe('internal-plugin-metrics', () => {
2153
2630
  },
2154
2631
  ],
2155
2632
  loginType: 'login-ci',
2633
+ telemetryOptOut: undefined,
2156
2634
  name: 'client.alert.displayed',
2157
2635
  userType: 'host',
2158
2636
  isConvergedArchitectureEnabled: undefined,
2159
2637
  webexSubServiceType: undefined,
2160
2638
  webClientPreload: undefined,
2639
+ isVipMeeting: false,
2640
+ isAutomatedUser: false,
2641
+ userActivation: undefined,
2161
2642
  },
2162
2643
  eventId: 'my-fake-id',
2163
2644
  origin: {
@@ -2235,11 +2716,15 @@ describe('internal-plugin-metrics', () => {
2235
2716
  },
2236
2717
  ],
2237
2718
  loginType: 'login-ci',
2719
+ telemetryOptOut: undefined,
2238
2720
  name: 'client.alert.displayed',
2239
2721
  userType: 'host',
2240
2722
  isConvergedArchitectureEnabled: undefined,
2241
2723
  webexSubServiceType: undefined,
2242
2724
  webClientPreload: undefined,
2725
+ isVipMeeting: false,
2726
+ isAutomatedUser: false,
2727
+ userActivation: undefined,
2243
2728
  },
2244
2729
  eventId: 'my-fake-id',
2245
2730
  origin: {
@@ -2311,8 +2796,11 @@ describe('internal-plugin-metrics', () => {
2311
2796
  },
2312
2797
  ],
2313
2798
  loginType: 'login-ci',
2799
+ telemetryOptOut: undefined,
2314
2800
  name: 'client.alert.displayed',
2315
2801
  webClientPreload: undefined,
2802
+ isAutomatedUser: false,
2803
+ userActivation: undefined,
2316
2804
  },
2317
2805
  eventId: 'my-fake-id',
2318
2806
  origin: {
@@ -2386,8 +2874,11 @@ describe('internal-plugin-metrics', () => {
2386
2874
  },
2387
2875
  ],
2388
2876
  loginType: 'login-ci',
2877
+ telemetryOptOut: undefined,
2389
2878
  name: 'client.alert.displayed',
2390
2879
  webClientPreload: undefined,
2880
+ isAutomatedUser: false,
2881
+ userActivation: undefined,
2391
2882
  },
2392
2883
  eventId: 'my-fake-id',
2393
2884
  origin: {
@@ -2468,11 +2959,15 @@ describe('internal-plugin-metrics', () => {
2468
2959
  },
2469
2960
  ],
2470
2961
  loginType: 'login-ci',
2962
+ telemetryOptOut: undefined,
2471
2963
  name: 'client.alert.displayed',
2472
2964
  userType: 'host',
2473
2965
  isConvergedArchitectureEnabled: undefined,
2474
2966
  webexSubServiceType: undefined,
2475
2967
  webClientPreload: undefined,
2968
+ isVipMeeting: false,
2969
+ isAutomatedUser: false,
2970
+ userActivation: undefined,
2476
2971
  },
2477
2972
  eventId: 'my-fake-id',
2478
2973
  origin: {
@@ -2563,16 +3058,18 @@ describe('internal-plugin-metrics', () => {
2563
3058
  });
2564
3059
 
2565
3060
  assert.calledThrice(submitToCallDiagnosticsStub);
2566
- });
3061
+ });
2567
3062
 
2568
- ([
2569
- ['client.media.render.start'],
2570
- ['client.media.render.stop'],
2571
- ['client.media.rx.start'],
2572
- ['client.media.rx.stop'],
2573
- ['client.media.tx.start'],
2574
- ['client.media.tx.stop']
2575
- ] as const).forEach(([name]) => {
3063
+ (
3064
+ [
3065
+ ['client.media.render.start'],
3066
+ ['client.media.render.stop'],
3067
+ ['client.media.rx.start'],
3068
+ ['client.media.rx.stop'],
3069
+ ['client.media.tx.start'],
3070
+ ['client.media.tx.stop'],
3071
+ ] as const
3072
+ ).forEach(([name]) => {
2576
3073
  it(`should only send ${name} once per mediaType`, () => {
2577
3074
  const options = {
2578
3075
  meetingId: fakeMeeting.id,
@@ -2616,7 +3113,8 @@ describe('internal-plugin-metrics', () => {
2616
3113
  });
2617
3114
 
2618
3115
  assert.notCalled(submitToCallDiagnosticsStub);
2619
- assert.neverCalledWithMatch(webex.logger.log,
3116
+ assert.neverCalledWithMatch(
3117
+ webex.logger.log,
2620
3118
  'call-diagnostic-events -> ',
2621
3119
  sinon.match(createEventLimitRegex(name, 'mediaType video'))
2622
3120
  );
@@ -2659,7 +3157,7 @@ describe('internal-plugin-metrics', () => {
2659
3157
  // Send event with different shareInstanceId
2660
3158
  cd.submitClientEvent({
2661
3159
  name,
2662
- payload: { ...payload, shareInstanceId: 'instance-2' },
3160
+ payload: {...payload, shareInstanceId: 'instance-2'},
2663
3161
  options,
2664
3162
  });
2665
3163
 
@@ -2667,89 +3165,88 @@ describe('internal-plugin-metrics', () => {
2667
3165
  });
2668
3166
  });
2669
3167
 
2670
- ([
2671
- ['client.roap-message.received'],
2672
- ['client.roap-message.sent']
2673
- ] as const).forEach(([name]) => {
2674
- it(`should not send third event of same type and not log warning again for ${name}`, () => {
2675
- const options = {
2676
- meetingId: fakeMeeting.id,
2677
- };
2678
- const payload = {
2679
- roap: {
2680
- messageType: 'OFFER' as const,
2681
- },
2682
- };
2683
- const submitToCallDiagnosticsStub = sinon.stub(cd, 'submitToCallDiagnostics');
2684
-
2685
- // Clear any existing call history to get accurate counts
2686
- webex.logger.log.resetHistory();
2687
-
2688
- // Send first event
2689
- cd.submitClientEvent({
2690
- name,
2691
- payload,
2692
- options,
3168
+ ([['client.roap-message.received'], ['client.roap-message.sent']] as const).forEach(
3169
+ ([name]) => {
3170
+ it(`should not send third event of same type and not log warning again for ${name}`, () => {
3171
+ const options = {
3172
+ meetingId: fakeMeeting.id,
3173
+ };
3174
+ const payload = {
3175
+ roap: {
3176
+ messageType: 'OFFER' as const,
3177
+ },
3178
+ };
3179
+ const submitToCallDiagnosticsStub = sinon.stub(cd, 'submitToCallDiagnostics');
3180
+
3181
+ // Clear any existing call history to get accurate counts
3182
+ webex.logger.log.resetHistory();
3183
+
3184
+ // Send first event
3185
+ cd.submitClientEvent({
3186
+ name,
3187
+ payload,
3188
+ options,
3189
+ });
3190
+
3191
+ assert.calledOnce(submitToCallDiagnosticsStub);
3192
+ submitToCallDiagnosticsStub.resetHistory();
3193
+
3194
+ // Send second event (should trigger warning)
3195
+ cd.submitClientEvent({
3196
+ name,
3197
+ payload,
3198
+ options,
3199
+ });
3200
+
3201
+ assert.notCalled(submitToCallDiagnosticsStub);
3202
+ assert.calledWith(
3203
+ webex.logger.log,
3204
+ 'call-diagnostic-events -> ',
3205
+ sinon.match(createEventLimitRegex(name, 'ROAP type OFFER'))
3206
+ );
3207
+ webex.logger.log.resetHistory();
3208
+
3209
+ cd.submitClientEvent({
3210
+ name,
3211
+ payload,
3212
+ options,
3213
+ });
3214
+
3215
+ assert.notCalled(submitToCallDiagnosticsStub);
3216
+ assert.neverCalledWithMatch(
3217
+ webex.logger.log,
3218
+ 'call-diagnostic-events -> ',
3219
+ sinon.match(createEventLimitRegex(name, 'ROAP type OFFER'))
3220
+ );
2693
3221
  });
2694
3222
 
2695
- assert.calledOnce(submitToCallDiagnosticsStub);
2696
- submitToCallDiagnosticsStub.resetHistory();
2697
-
2698
- // Send second event (should trigger warning)
2699
- cd.submitClientEvent({
2700
- name,
2701
- payload,
2702
- options,
2703
- });
3223
+ it(`should handle roap.type instead of roap.messageType for ${name}`, () => {
3224
+ const options = {
3225
+ meetingId: fakeMeeting.id,
3226
+ };
3227
+ const payload = {
3228
+ roap: {
3229
+ type: 'ANSWER' as const,
3230
+ },
3231
+ };
3232
+ const submitToCallDiagnosticsStub = sinon.stub(cd, 'submitToCallDiagnostics');
2704
3233
 
2705
- assert.notCalled(submitToCallDiagnosticsStub);
2706
- assert.calledWith(
2707
- webex.logger.log,
2708
- 'call-diagnostic-events -> ',
2709
- sinon.match(createEventLimitRegex(name, 'ROAP type OFFER'))
2710
- );
2711
- webex.logger.log.resetHistory();
2712
-
2713
- cd.submitClientEvent({
2714
- name,
2715
- payload,
2716
- options,
2717
- });
3234
+ cd.submitClientEvent({
3235
+ name,
3236
+ payload,
3237
+ options,
3238
+ });
2718
3239
 
2719
- assert.notCalled(submitToCallDiagnosticsStub);
2720
- assert.neverCalledWithMatch(
2721
- webex.logger.log,
2722
- 'call-diagnostic-events -> ',
2723
- sinon.match(createEventLimitRegex(name, 'ROAP type OFFER'))
2724
- );
2725
- });
3240
+ cd.submitClientEvent({
3241
+ name,
3242
+ payload,
3243
+ options,
3244
+ });
2726
3245
 
2727
- it(`should handle roap.type instead of roap.messageType for ${name}`, () => {
2728
- const options = {
2729
- meetingId: fakeMeeting.id,
2730
- };
2731
- const payload = {
2732
- roap: {
2733
- type: 'ANSWER' as const,
2734
- },
2735
- };
2736
- const submitToCallDiagnosticsStub = sinon.stub(cd, 'submitToCallDiagnostics');
2737
-
2738
- cd.submitClientEvent({
2739
- name,
2740
- payload,
2741
- options,
3246
+ assert.calledOnce(submitToCallDiagnosticsStub);
2742
3247
  });
2743
-
2744
- cd.submitClientEvent({
2745
- name,
2746
- payload,
2747
- options,
2748
- });
2749
-
2750
- assert.calledOnce(submitToCallDiagnosticsStub);
2751
- });
2752
- });
3248
+ }
3249
+ );
2753
3250
  });
2754
3251
  });
2755
3252
 
@@ -2760,7 +3257,10 @@ describe('internal-plugin-metrics', () => {
2760
3257
  cd.callDiagnosticEventsBatcher = {request: requestStub};
2761
3258
  //@ts-ignore
2762
3259
  cd.submitToCallDiagnostics({event: 'test'});
2763
- assert.calledWith(requestStub, {eventPayload: {event: 'test'}, type: ['diagnostic-event']});
3260
+ assert.calledWith(requestStub, {
3261
+ eventPayload: {event: 'test'},
3262
+ type: ['diagnostic-event'],
3263
+ });
2764
3264
  });
2765
3265
  });
2766
3266
 
@@ -2829,6 +3329,7 @@ describe('internal-plugin-metrics', () => {
2829
3329
  mediaEngineSoftwareVersion: getOSVersion() || 'unknown',
2830
3330
  startTime: now.toISOString(),
2831
3331
  },
3332
+ webexSubServiceType: undefined,
2832
3333
  },
2833
3334
  options
2834
3335
  );
@@ -2869,6 +3370,7 @@ describe('internal-plugin-metrics', () => {
2869
3370
  mediaEngineSoftwareVersion: getOSVersion() || 'unknown',
2870
3371
  startTime: now.toISOString(),
2871
3372
  },
3373
+ webexSubServiceType: undefined,
2872
3374
  },
2873
3375
  },
2874
3376
  });
@@ -2907,10 +3409,48 @@ describe('internal-plugin-metrics', () => {
2907
3409
  mediaEngineSoftwareVersion: getOSVersion() || 'unknown',
2908
3410
  startTime: now.toISOString(),
2909
3411
  },
3412
+ webexSubServiceType: undefined,
2910
3413
  },
2911
3414
  });
2912
3415
  });
2913
3416
 
3417
+ it('includes webexSubServiceType in the media quality event payload', () => {
3418
+ const meeting = {
3419
+ ...fakeMeeting,
3420
+ meetingInfo: {
3421
+ enableConvergedArchitecture: true,
3422
+ enableEvent: true,
3423
+ enableConvergedWebinarLargeScale: true,
3424
+ },
3425
+ };
3426
+ const prepareDiagnosticEventSpy = sinon.spy(cd, 'prepareDiagnosticEvent');
3427
+ sinon.stub(cd, 'getOrigin').returns({origin: 'fake-origin'});
3428
+ webex.meetings.getBasicMeetingInformation = sinon.stub().returns(meeting);
3429
+
3430
+ const options = {
3431
+ networkType: 'wifi' as const,
3432
+ meetingId: fakeMeeting.id,
3433
+ };
3434
+
3435
+ cd.submitMQE({
3436
+ name: 'client.mediaquality.event',
3437
+ payload: {
3438
+ //@ts-ignore
3439
+ intervals: [{}],
3440
+ },
3441
+ options,
3442
+ });
3443
+
3444
+ assert.calledOnceWithExactly(
3445
+ prepareDiagnosticEventSpy,
3446
+ sinon.match({
3447
+ name: 'client.mediaquality.event',
3448
+ webexSubServiceType: 'LargeScaleWebinar',
3449
+ }),
3450
+ options
3451
+ );
3452
+ });
3453
+
2914
3454
  it('throws if meeting id not provided', () => {
2915
3455
  assert.throws(() =>
2916
3456
  cd.submitMQE({
@@ -3631,6 +4171,97 @@ describe('internal-plugin-metrics', () => {
3631
4171
  });
3632
4172
  });
3633
4173
 
4174
+ describe('#getTelemetryOptOut', () => {
4175
+ it('returns "manual" when manual telemetry opt-out is enabled', () => {
4176
+ cd.setIsTelemetryOptOutManual(true);
4177
+ assert.equal(cd.getTelemetryOptOut(), 'manual');
4178
+ });
4179
+
4180
+ it('returns "automatic" when automatic telemetry opt-out is enabled', () => {
4181
+ cd.setIsTelemetryOptOutAutomatic(true);
4182
+ assert.equal(cd.getTelemetryOptOut(), 'automatic');
4183
+ });
4184
+
4185
+ it('returns "manual" when manual opt-out takes precedence over automatic', () => {
4186
+ cd.setIsTelemetryOptOutManual(true);
4187
+ cd.setIsTelemetryOptOutAutomatic(true);
4188
+ assert.equal(cd.getTelemetryOptOut(), 'manual');
4189
+ });
4190
+
4191
+ it('returns undefined when neither manual nor automatic opt-out is set', () => {
4192
+ assert.isUndefined(cd.getTelemetryOptOut());
4193
+ });
4194
+
4195
+ it('returns undefined after disabling manual opt-out', () => {
4196
+ cd.setIsTelemetryOptOutManual(true);
4197
+ cd.setIsTelemetryOptOutManual(false);
4198
+ assert.isUndefined(cd.getTelemetryOptOut());
4199
+ });
4200
+ });
4201
+
4202
+ describe('#setIsTelemetryOptOutManual', () => {
4203
+ it('sets manual telemetry opt-out to true', () => {
4204
+ cd.setIsTelemetryOptOutManual(true);
4205
+ assert.equal(cd.getTelemetryOptOut(), 'manual');
4206
+ });
4207
+
4208
+ it('sets manual telemetry opt-out to false', () => {
4209
+ cd.setIsTelemetryOptOutManual(true);
4210
+ cd.setIsTelemetryOptOutManual(false);
4211
+ assert.isUndefined(cd.getTelemetryOptOut());
4212
+ });
4213
+
4214
+ it('can toggle manual telemetry opt-out multiple times', () => {
4215
+ cd.setIsTelemetryOptOutManual(true);
4216
+ assert.equal(cd.getTelemetryOptOut(), 'manual');
4217
+
4218
+ cd.setIsTelemetryOptOutManual(false);
4219
+ assert.isUndefined(cd.getTelemetryOptOut());
4220
+
4221
+ cd.setIsTelemetryOptOutManual(true);
4222
+ assert.equal(cd.getTelemetryOptOut(), 'manual');
4223
+ });
4224
+
4225
+ it('manual opt-out takes precedence when automatic is also set', () => {
4226
+ cd.setIsTelemetryOptOutAutomatic(true);
4227
+ cd.setIsTelemetryOptOutManual(true);
4228
+ assert.equal(cd.getTelemetryOptOut(), 'manual');
4229
+ });
4230
+ });
4231
+
4232
+ describe('#setIsTelemetryOptOutAutomatic', () => {
4233
+ it('sets automatic telemetry opt-out to true', () => {
4234
+ cd.setIsTelemetryOptOutAutomatic(true);
4235
+ assert.equal(cd.getTelemetryOptOut(), 'automatic');
4236
+ });
4237
+
4238
+ it('sets automatic telemetry opt-out to false', () => {
4239
+ cd.setIsTelemetryOptOutAutomatic(true);
4240
+ cd.setIsTelemetryOptOutAutomatic(false);
4241
+ assert.isUndefined(cd.getTelemetryOptOut());
4242
+ });
4243
+
4244
+ it('can toggle automatic telemetry opt-out multiple times', () => {
4245
+ cd.setIsTelemetryOptOutAutomatic(true);
4246
+ assert.equal(cd.getTelemetryOptOut(), 'automatic');
4247
+
4248
+ cd.setIsTelemetryOptOutAutomatic(false);
4249
+ assert.isUndefined(cd.getTelemetryOptOut());
4250
+
4251
+ cd.setIsTelemetryOptOutAutomatic(true);
4252
+ assert.equal(cd.getTelemetryOptOut(), 'automatic');
4253
+ });
4254
+
4255
+ it('does not override manual opt-out', () => {
4256
+ cd.setIsTelemetryOptOutManual(true);
4257
+ cd.setIsTelemetryOptOutAutomatic(true);
4258
+ assert.equal(cd.getTelemetryOptOut(), 'manual');
4259
+
4260
+ cd.setIsTelemetryOptOutAutomatic(false);
4261
+ assert.equal(cd.getTelemetryOptOut(), 'manual');
4262
+ });
4263
+ });
4264
+
3634
4265
  describe('#getSubServiceType', () => {
3635
4266
  it('returns subServicetype as PMR when PMR meeting', () => {
3636
4267
  fakeMeeting.meetingInfo = {
@@ -3680,6 +4311,15 @@ describe('internal-plugin-metrics', () => {
3680
4311
  assert.deepEqual(cd.getSubServiceType(fakeMeeting), 'Webcast');
3681
4312
  });
3682
4313
 
4314
+ it('returns subServicetype as LargeScaleWebinar when meeting is converged Webinar and enable large scale', () => {
4315
+ fakeMeeting.meetingInfo = {
4316
+ enableEvent: true,
4317
+ enableConvergedArchitecture: true,
4318
+ enableConvergedWebinarLargeScale: true,
4319
+ };
4320
+ assert.deepEqual(cd.getSubServiceType(fakeMeeting), 'LargeScaleWebinar');
4321
+ });
4322
+
3683
4323
  it('returns subServicetype as undefined when correct parameters are not found', () => {
3684
4324
  fakeMeeting.meetingInfo = {};
3685
4325
  assert.deepEqual(cd.getSubServiceType(fakeMeeting), undefined);
@@ -3740,12 +4380,16 @@ describe('internal-plugin-metrics', () => {
3740
4380
  userId: 'userId',
3741
4381
  },
3742
4382
  loginType: 'login-ci',
4383
+ telemetryOptOut: undefined,
3743
4384
  name: 'client.exit.app',
3744
4385
  trigger: 'user-interaction',
3745
4386
  userType: 'host',
3746
4387
  isConvergedArchitectureEnabled: undefined,
3747
4388
  webexSubServiceType: undefined,
3748
4389
  webClientPreload: undefined,
4390
+ isVipMeeting: false,
4391
+ isAutomatedUser: false,
4392
+ userActivation: undefined,
3749
4393
  },
3750
4394
  eventId: 'my-fake-id',
3751
4395
  origin: {
@@ -3900,6 +4544,30 @@ describe('internal-plugin-metrics', () => {
3900
4544
  options.meetingJoinPhase
3901
4545
  );
3902
4546
  });
4547
+
4548
+ it('builds request options before webex is ready (no meetings plugin or internal metrics config)', async () => {
4549
+ // before webex.ready the meetings plugin and internal metrics config are not available
4550
+ webex.meetings = undefined;
4551
+ webex.internal.metrics = undefined;
4552
+
4553
+ const options = {
4554
+ correlationId: 'myCorrelationId',
4555
+ clientType: 'TEAMS_CLIENT',
4556
+ subClientType: 'WEB_APP',
4557
+ };
4558
+
4559
+ const fetchOptions = await cd.buildClientEventFetchRequestOptions({
4560
+ name: 'client.exit.app',
4561
+ payload: {trigger: 'user-interaction', canProceed: false},
4562
+ options,
4563
+ });
4564
+
4565
+ const eventPayload = fetchOptions.body.metrics[0].eventPayload;
4566
+ assert.equal(eventPayload.event.name, 'client.exit.app');
4567
+ assert.isUndefined(eventPayload.senderCountryCode);
4568
+ assert.isUndefined(fetchOptions.waitForServiceTimeout);
4569
+ assert.equal(fetchOptions.resource, 'clientmetrics');
4570
+ });
3903
4571
  });
3904
4572
 
3905
4573
  describe('#submitToCallDiagnosticsPreLogin', () => {
@@ -3913,7 +4581,10 @@ describe('internal-plugin-metrics', () => {
3913
4581
  cd.submitToCallDiagnosticsPreLogin({event: 'test'}, preLoginId);
3914
4582
  //@ts-ignore
3915
4583
  assert.calledWith(cd.preLoginMetricsBatcher.savePreLoginId, preLoginId);
3916
- assert.calledWith(requestStub, {eventPayload: {event: 'test'}, type: ['diagnostic-event']});
4584
+ assert.calledWith(requestStub, {
4585
+ eventPayload: {event: 'test'},
4586
+ type: ['diagnostic-event'],
4587
+ });
3917
4588
  });
3918
4589
  });
3919
4590
 
@@ -4114,12 +4785,14 @@ describe('internal-plugin-metrics', () => {
4114
4785
  payload: {
4115
4786
  meetingSummaryInfo: {
4116
4787
  featureName: 'syncSystemMuteStatus',
4117
- featureActions: [{
4118
- actionName: 'syncMeetingMicUnmuteStatusToSystem',
4119
- actionId: '14200',
4120
- isInitialValue: false,
4121
- clickCount: '1'
4122
- }]
4788
+ featureActions: [
4789
+ {
4790
+ actionName: 'syncMeetingMicUnmuteStatusToSystem',
4791
+ actionId: '14200',
4792
+ isInitialValue: false,
4793
+ clickCount: '1',
4794
+ },
4795
+ ],
4123
4796
  },
4124
4797
  },
4125
4798
  options,
@@ -4140,22 +4813,28 @@ describe('internal-plugin-metrics', () => {
4140
4813
  locusSessionId: 'locusSessionId',
4141
4814
  locusStartTime: 'lastActive',
4142
4815
  },
4143
- eventData: { webClientDomain: 'whatever'},
4816
+ eventData: {webClientDomain: 'whatever'},
4144
4817
  userType: 'host',
4145
4818
  loginType: 'login-ci',
4819
+ telemetryOptOut: undefined,
4146
4820
  isConvergedArchitectureEnabled: undefined,
4147
4821
  webexSubServiceType: undefined,
4148
4822
  webClientPreload: undefined,
4823
+ isVipMeeting: false,
4824
+ isAutomatedUser: false,
4825
+ userActivation: undefined,
4149
4826
  meetingSummaryInfo: {
4150
4827
  featureName: 'syncSystemMuteStatus',
4151
- featureActions: [{
4152
- actionName: 'syncMeetingMicUnmuteStatusToSystem',
4153
- actionId: '14200',
4154
- isInitialValue: false,
4155
- clickCount: '1'
4156
- }]
4828
+ featureActions: [
4829
+ {
4830
+ actionName: 'syncMeetingMicUnmuteStatusToSystem',
4831
+ actionId: '14200',
4832
+ isInitialValue: false,
4833
+ clickCount: '1',
4834
+ },
4835
+ ],
4157
4836
  },
4158
- key: "UcfFeatureUsage",
4837
+ key: 'UcfFeatureUsage',
4159
4838
  },
4160
4839
  options
4161
4840
  );
@@ -4168,7 +4847,7 @@ describe('internal-plugin-metrics', () => {
4168
4847
  },
4169
4848
  event: {
4170
4849
  canProceed: true,
4171
- eventData: { webClientDomain: 'whatever'},
4850
+ eventData: {webClientDomain: 'whatever'},
4172
4851
  identifiers: {
4173
4852
  correlationId: 'correlationId',
4174
4853
  deviceId: 'deviceUrl',
@@ -4180,21 +4859,27 @@ describe('internal-plugin-metrics', () => {
4180
4859
  userId: 'userId',
4181
4860
  },
4182
4861
  loginType: 'login-ci',
4862
+ telemetryOptOut: undefined,
4183
4863
  name: 'client.feature.meeting.summary',
4184
4864
  userType: 'host',
4185
4865
  isConvergedArchitectureEnabled: undefined,
4186
4866
  webexSubServiceType: undefined,
4187
4867
  webClientPreload: undefined,
4868
+ isVipMeeting: false,
4869
+ isAutomatedUser: false,
4870
+ userActivation: undefined,
4188
4871
  meetingSummaryInfo: {
4189
4872
  featureName: 'syncSystemMuteStatus',
4190
- featureActions: [{
4191
- actionName: 'syncMeetingMicUnmuteStatusToSystem',
4192
- actionId: '14200',
4193
- isInitialValue: false,
4194
- clickCount: '1'
4195
- }]
4873
+ featureActions: [
4874
+ {
4875
+ actionName: 'syncMeetingMicUnmuteStatusToSystem',
4876
+ actionId: '14200',
4877
+ isInitialValue: false,
4878
+ clickCount: '1',
4879
+ },
4880
+ ],
4196
4881
  },
4197
- key: "UcfFeatureUsage",
4882
+ key: 'UcfFeatureUsage',
4198
4883
  },
4199
4884
  originTime: {
4200
4885
  sent: 'not_defined_yet',
@@ -4235,12 +4920,14 @@ describe('internal-plugin-metrics', () => {
4235
4920
  payload: {
4236
4921
  meetingSummaryInfo: {
4237
4922
  featureName: 'syncSystemMuteStatus',
4238
- featureActions: [{
4239
- actionName: 'syncMeetingMicUnmuteStatusToSystem',
4240
- actionId: '14200',
4241
- isInitialValue: false,
4242
- clickCount: '1'
4243
- }]
4923
+ featureActions: [
4924
+ {
4925
+ actionName: 'syncMeetingMicUnmuteStatusToSystem',
4926
+ actionId: '14200',
4927
+ isInitialValue: false,
4928
+ clickCount: '1',
4929
+ },
4930
+ ],
4244
4931
  },
4245
4932
  },
4246
4933
  delaySubmitEvent: true,
@@ -4252,12 +4939,14 @@ describe('internal-plugin-metrics', () => {
4252
4939
  payload: {
4253
4940
  meetingSummaryInfo: {
4254
4941
  featureName: 'syncSystemVideoStatus',
4255
- featureActions: [{
4256
- actionName: 'syncMeetingVideoUnmuteStatusToSystem',
4257
- actionId: '13400',
4258
- isInitialValue: false,
4259
- clickCount: '1'
4260
- }]
4942
+ featureActions: [
4943
+ {
4944
+ actionName: 'syncMeetingVideoUnmuteStatusToSystem',
4945
+ actionId: '13400',
4946
+ isInitialValue: false,
4947
+ clickCount: '1',
4948
+ },
4949
+ ],
4261
4950
  },
4262
4951
  },
4263
4952
  delaySubmitEvent: true,
@@ -4275,12 +4964,14 @@ describe('internal-plugin-metrics', () => {
4275
4964
  payload: {
4276
4965
  meetingSummaryInfo: {
4277
4966
  featureName: 'syncSystemMuteStatus',
4278
- featureActions: [{
4279
- actionName: 'syncMeetingMicUnmuteStatusToSystem',
4280
- actionId: '14200',
4281
- isInitialValue: false,
4282
- clickCount: '1'
4283
- }]
4967
+ featureActions: [
4968
+ {
4969
+ actionName: 'syncMeetingMicUnmuteStatusToSystem',
4970
+ actionId: '14200',
4971
+ isInitialValue: false,
4972
+ clickCount: '1',
4973
+ },
4974
+ ],
4284
4975
  },
4285
4976
  },
4286
4977
  options: {
@@ -4293,12 +4984,14 @@ describe('internal-plugin-metrics', () => {
4293
4984
  payload: {
4294
4985
  meetingSummaryInfo: {
4295
4986
  featureName: 'syncSystemVideoStatus',
4296
- featureActions: [{
4297
- actionName: 'syncMeetingVideoUnmuteStatusToSystem',
4298
- actionId: '13400',
4299
- isInitialValue: false,
4300
- clickCount: '1'
4301
- }]
4987
+ featureActions: [
4988
+ {
4989
+ actionName: 'syncMeetingVideoUnmuteStatusToSystem',
4990
+ actionId: '13400',
4991
+ isInitialValue: false,
4992
+ clickCount: '1',
4993
+ },
4994
+ ],
4302
4995
  },
4303
4996
  },
4304
4997
  options: {
@@ -4322,17 +5015,17 @@ describe('internal-plugin-metrics', () => {
4322
5015
 
4323
5016
  it('should clear event limits for specific correlationId only', () => {
4324
5017
  // Use the actual correlationIds from our fakeMeeting fixtures
4325
- const correlationId1 = fakeMeeting.correlationId; // e.g. 'correlationId1'
4326
- const correlationId2 = fakeMeeting2.correlationId; // e.g. 'correlationId2'
4327
- const options1 = { meetingId: fakeMeeting.id };
4328
- const options2 = { meetingId: fakeMeeting2.id };
4329
- const payload = { mediaType: 'video' as const };
5018
+ const correlationId1 = fakeMeeting.correlationId; // e.g. 'correlationId1'
5019
+ const correlationId2 = fakeMeeting2.correlationId; // e.g. 'correlationId2'
5020
+ const options1 = {meetingId: fakeMeeting.id};
5021
+ const options2 = {meetingId: fakeMeeting2.id};
5022
+ const payload = {mediaType: 'video' as const};
4330
5023
 
4331
5024
  // Set up events for both correlations to trigger limits
4332
- cd.submitClientEvent({ name: 'client.media.render.start', payload, options: options1 });
4333
- cd.submitClientEvent({ name: 'client.media.render.start', payload, options: options2 });
4334
- cd.submitClientEvent({ name: 'client.media.render.start', payload, options: options1 });
4335
- cd.submitClientEvent({ name: 'client.media.render.start', payload, options: options2 });
5025
+ cd.submitClientEvent({name: 'client.media.render.start', payload, options: options1});
5026
+ cd.submitClientEvent({name: 'client.media.render.start', payload, options: options2});
5027
+ cd.submitClientEvent({name: 'client.media.render.start', payload, options: options1});
5028
+ cd.submitClientEvent({name: 'client.media.render.start', payload, options: options2});
4336
5029
  assert.isTrue(cd.eventLimitTracker.size > 0);
4337
5030
  assert.isTrue(cd.eventLimitWarningsLogged.size > 0);
4338
5031
 
@@ -4343,17 +5036,17 @@ describe('internal-plugin-metrics', () => {
4343
5036
  const remainingWarningKeys = Array.from(cd.eventLimitWarningsLogged.keys());
4344
5037
 
4345
5038
  // Should have no keys with correlationId1
4346
- assert.isFalse(remainingTrackerKeys.some(key => key.split(':')[1] === correlationId1));
4347
- assert.isFalse(remainingWarningKeys.some(key => key.split(':')[1] === correlationId1));
5039
+ assert.isFalse(remainingTrackerKeys.some((key) => key.split(':')[1] === correlationId1));
5040
+ assert.isFalse(remainingWarningKeys.some((key) => key.split(':')[1] === correlationId1));
4348
5041
 
4349
5042
  // Should still have keys with correlationId2
4350
- assert.isTrue(remainingTrackerKeys.some(key => key.split(':')[1] === correlationId2));
4351
- assert.isTrue(remainingWarningKeys.some(key => key.split(':')[1] === correlationId2));
5043
+ assert.isTrue(remainingTrackerKeys.some((key) => key.split(':')[1] === correlationId2));
5044
+ assert.isTrue(remainingWarningKeys.some((key) => key.split(':')[1] === correlationId2));
4352
5045
  });
4353
5046
 
4354
5047
  it('should handle empty correlationId gracefully', () => {
4355
- const options = { meetingId: fakeMeeting.id };
4356
- const payload = { mediaType: 'video' as const };
5048
+ const options = {meetingId: fakeMeeting.id};
5049
+ const payload = {mediaType: 'video' as const};
4357
5050
 
4358
5051
  // Set up some tracking data
4359
5052
  cd.submitClientEvent({
@@ -4381,8 +5074,8 @@ describe('internal-plugin-metrics', () => {
4381
5074
  });
4382
5075
 
4383
5076
  it('should handle non-existent correlationId gracefully', () => {
4384
- const options = { meetingId: fakeMeeting.id };
4385
- const payload = { mediaType: 'video' as const };
5077
+ const options = {meetingId: fakeMeeting.id};
5078
+ const payload = {mediaType: 'video' as const};
4386
5079
 
4387
5080
  // Set up some tracking data
4388
5081
  cd.submitClientEvent({
@@ -4403,10 +5096,10 @@ describe('internal-plugin-metrics', () => {
4403
5096
 
4404
5097
  it('should clear multiple event types for the same correlationId', () => {
4405
5098
  const correlationId = fakeMeeting.correlationId;
4406
- const options = { meetingId: fakeMeeting.id };
4407
- const videoPayload = { mediaType: 'video' as const };
4408
- const audioPayload = { mediaType: 'audio' as const };
4409
- const roapPayload = { roap: { messageType: 'OFFER' as const } };
5099
+ const options = {meetingId: fakeMeeting.id};
5100
+ const videoPayload = {mediaType: 'video' as const};
5101
+ const audioPayload = {mediaType: 'audio' as const};
5102
+ const roapPayload = {roap: {messageType: 'OFFER' as const}};
4410
5103
 
4411
5104
  // Set up multiple event types for the same correlation
4412
5105
  cd.submitClientEvent({
@@ -4459,8 +5152,8 @@ describe('internal-plugin-metrics', () => {
4459
5152
 
4460
5153
  it('should allow events to be sent again after clearing limits for correlationId', () => {
4461
5154
  const correlationId = fakeMeeting.correlationId;
4462
- const options = { meetingId: fakeMeeting.id };
4463
- const payload = { mediaType: 'video' as const };
5155
+ const options = {meetingId: fakeMeeting.id};
5156
+ const payload = {mediaType: 'video' as const};
4464
5157
  const submitToCallDiagnosticsStub = sinon.stub(cd, 'submitToCallDiagnostics');
4465
5158
 
4466
5159
  // Send first event (should succeed)