@webex/contact-center 3.12.0-llmrefactor.7 → 3.12.0-llmrefactor.9
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/.sdd/manifest.json +3 -2
- package/ai-docs/CONTRACTS.md +3 -2
- package/dist/cc.js +108 -6
- package/dist/cc.js.map +1 -1
- package/dist/metrics/MetricsManager.js +16 -0
- package/dist/metrics/MetricsManager.js.map +1 -1
- package/dist/metrics/behavioral-events.js +91 -0
- package/dist/metrics/behavioral-events.js.map +1 -1
- package/dist/metrics/constants.js +16 -0
- package/dist/metrics/constants.js.map +1 -1
- package/dist/services/AnswerCallOnWebexService.js +34 -6
- package/dist/services/AnswerCallOnWebexService.js.map +1 -1
- package/dist/services/WebexCrossClientService.js +54 -3
- package/dist/services/WebexCrossClientService.js.map +1 -1
- package/dist/services/WxAppTelephonyMercurySync.js +21 -6
- package/dist/services/WxAppTelephonyMercurySync.js.map +1 -1
- package/dist/services/task/state-machine/TaskStateMachine.js +0 -17
- package/dist/services/task/state-machine/TaskStateMachine.js.map +1 -1
- package/dist/services/task/state-machine/guards.js +7 -37
- package/dist/services/task/state-machine/guards.js.map +1 -1
- package/dist/services/task/voice/Voice.js +81 -14
- package/dist/services/task/voice/Voice.js.map +1 -1
- package/dist/services/task/voice/wxAppVoiceMethods.js +154 -1
- package/dist/services/task/voice/wxAppVoiceMethods.js.map +1 -1
- package/dist/services/wxAppDiagnosticLogging.js +122 -0
- package/dist/services/wxAppDiagnosticLogging.js.map +1 -0
- package/dist/types/cc.d.ts +1 -0
- package/dist/types/metrics/MetricsManager.d.ts +6 -0
- package/dist/types/metrics/constants.d.ts +15 -0
- package/dist/types/services/AnswerCallOnWebexService.d.ts +1 -0
- package/dist/types/services/WebexCrossClientService.d.ts +4 -0
- package/dist/types/services/task/state-machine/TaskStateMachine.d.ts +0 -42
- package/dist/types/services/task/state-machine/guards.d.ts +4 -4
- package/dist/types/services/task/voice/Voice.d.ts +5 -1
- package/dist/types/services/task/voice/wxAppVoiceMethods.d.ts +3 -0
- package/dist/types/services/wxAppDiagnosticLogging.d.ts +51 -0
- package/dist/webex.js +1 -1
- package/package.json +8 -8
- package/src/cc.ts +155 -7
- package/src/metrics/MetricsManager.ts +16 -0
- package/src/metrics/ai-docs/metrics-spec.md +71 -9
- package/src/metrics/behavioral-events.ts +92 -0
- package/src/metrics/constants.ts +18 -0
- package/src/services/AnswerCallOnWebexService.ts +41 -8
- package/src/services/WebexCrossClientService.ts +85 -4
- package/src/services/WxAppTelephonyMercurySync.ts +31 -4
- package/src/services/agent/ai-docs/agent-spec.md +1 -0
- package/src/services/task/ai-docs/task-spec.md +17 -16
- package/src/services/task/state-machine/TaskStateMachine.ts +0 -20
- package/src/services/task/state-machine/ai-docs/AGENTS.md +2 -4
- package/src/services/task/state-machine/ai-docs/ARCHITECTURE.md +8 -9
- package/src/services/task/state-machine/ai-docs/task-state-machine-spec.md +5 -7
- package/src/services/task/state-machine/guards.ts +7 -63
- package/src/services/task/voice/Voice.ts +115 -26
- package/src/services/task/voice/wxAppVoiceMethods.ts +211 -2
- package/src/services/wxAppDiagnosticLogging.ts +174 -0
- package/test/unit/spec/cc.ts +251 -22
- package/test/unit/spec/metrics/MetricsManager.ts +25 -0
- package/test/unit/spec/metrics/behavioral-events.ts +42 -0
- package/test/unit/spec/services/AnswerCallOnWebexService.ts +41 -0
- package/test/unit/spec/services/WebexCrossClientService.ts +207 -1
- package/test/unit/spec/services/WxAppTelephonyMercurySync.ts +23 -0
- package/test/unit/spec/services/task/state-machine/TaskStateMachine.ts +72 -46
- package/test/unit/spec/services/task/state-machine/guards.ts +6 -6
- package/test/unit/spec/services/task/voice/Voice.ts +380 -0
- package/test/unit/spec/services/task/voice/wxAppVoiceMethods.ts +145 -1
- package/test/unit/spec/services/wxAppDiagnosticLogging.ts +143 -0
- package/umd/contact-center.min.js +2 -2
- package/umd/contact-center.min.js.map +1 -1
package/test/unit/spec/cc.ts
CHANGED
|
@@ -729,7 +729,7 @@ describe('webex.cc', () => {
|
|
|
729
729
|
expect(registerWebCallingLineSpy).toHaveBeenCalled();
|
|
730
730
|
expect(stationLoginMock).toHaveBeenCalledWith({
|
|
731
731
|
data: {
|
|
732
|
-
dialNumber:
|
|
732
|
+
dialNumber: `${WEB_RTC_PREFIX}agentId`,
|
|
733
733
|
teamId: 'teamId',
|
|
734
734
|
deviceType: LoginOption.BROWSER,
|
|
735
735
|
isExtension: false,
|
|
@@ -889,7 +889,7 @@ describe('webex.cc', () => {
|
|
|
889
889
|
expect(registerWebCallingLineSpy).not.toHaveBeenCalled();
|
|
890
890
|
expect(stationLoginSpy).toHaveBeenCalledWith({
|
|
891
891
|
data: {
|
|
892
|
-
dialNumber:
|
|
892
|
+
dialNumber: `${WEB_RTC_PREFIX}agentId`,
|
|
893
893
|
teamId: 'teamId',
|
|
894
894
|
deviceType: LoginOption.BROWSER,
|
|
895
895
|
isExtension: false,
|
|
@@ -1057,6 +1057,7 @@ describe('webex.cc', () => {
|
|
|
1057
1057
|
const subscribeSpy = jest
|
|
1058
1058
|
.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'subscribe')
|
|
1059
1059
|
.mockImplementation(() => {});
|
|
1060
|
+
jest.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'isSubscribed').mockReturnValue(true);
|
|
1060
1061
|
|
|
1061
1062
|
jest.spyOn(webex.cc.services.agent, 'stationLogin').mockResolvedValue({
|
|
1062
1063
|
data: {
|
|
@@ -1073,7 +1074,7 @@ describe('webex.cc', () => {
|
|
|
1073
1074
|
dialNumber: '1001',
|
|
1074
1075
|
});
|
|
1075
1076
|
|
|
1076
|
-
expect(publishSpy).toHaveBeenCalledWith(true, {userId: 'user-123'});
|
|
1077
|
+
expect(publishSpy).toHaveBeenCalledWith(true, {userId: 'user-123', trackPublishMetrics: true});
|
|
1077
1078
|
expect(subscribeSpy).toHaveBeenCalledWith('agentId', expect.any(Function));
|
|
1078
1079
|
});
|
|
1079
1080
|
|
|
@@ -1135,6 +1136,7 @@ describe('webex.cc', () => {
|
|
|
1135
1136
|
jest
|
|
1136
1137
|
.spyOn(webex.cc as any, 'releaseWxAppMercuryResources')
|
|
1137
1138
|
.mockResolvedValue(undefined);
|
|
1139
|
+
const metricSpy = jest.spyOn(mockMetricsManager, 'trackEvent');
|
|
1138
1140
|
|
|
1139
1141
|
jest.spyOn(webex.cc.services.agent, 'stationLogin').mockResolvedValue({
|
|
1140
1142
|
data: {
|
|
@@ -1154,10 +1156,62 @@ describe('webex.cc', () => {
|
|
|
1154
1156
|
expect(publishSpy).not.toHaveBeenCalled();
|
|
1155
1157
|
expect(teardownSpy).not.toHaveBeenCalled();
|
|
1156
1158
|
expect(unsubscribeSpy).toHaveBeenCalled();
|
|
1159
|
+
expect(metricSpy).toHaveBeenCalledWith(
|
|
1160
|
+
METRIC_EVENT_NAMES.WXAPP_SESSION_INIT_FAILED,
|
|
1161
|
+
expect.objectContaining({skipReason: 'mercury_subscribe_failed'}),
|
|
1162
|
+
['operational', 'behavioral']
|
|
1163
|
+
);
|
|
1157
1164
|
expect(webex.cc.isWxBetterTogetherEnabled()).toBe(false);
|
|
1158
1165
|
expect(mockTaskManager.applyEnableWxBetterTogether).toHaveBeenCalledWith(false);
|
|
1159
1166
|
});
|
|
1160
1167
|
|
|
1168
|
+
it('should record publish_failed when usersub publish fails after Mercury init', async () => {
|
|
1169
|
+
webex.cc.$config = {...webex.cc.$config, enableWxBetterTogether: true};
|
|
1170
|
+
webex.internal.device = {userId: 'user-123', url: 'https://wdm.example.com/devices/dev-1'};
|
|
1171
|
+
webex.cc.agentConfig = {
|
|
1172
|
+
agentId: 'agentId',
|
|
1173
|
+
webRtcEnabled: false,
|
|
1174
|
+
loginVoiceOptions: ['EXTENSION'],
|
|
1175
|
+
};
|
|
1176
|
+
webex.cc.webCallingService.loginOption = LoginOption.EXTENSION;
|
|
1177
|
+
mockTaskManager.applyEnableWxBetterTogether(true);
|
|
1178
|
+
|
|
1179
|
+
jest.spyOn(webex.cc as any, 'ensureWxAppMercuryConnected').mockResolvedValue(undefined);
|
|
1180
|
+
jest
|
|
1181
|
+
.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'subscribe')
|
|
1182
|
+
.mockImplementation(() => {});
|
|
1183
|
+
jest.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'isSubscribed').mockReturnValue(true);
|
|
1184
|
+
jest
|
|
1185
|
+
.spyOn(webex.cc['webexCrossClientService'], 'setManageWebexCallingInWxcc')
|
|
1186
|
+
.mockRejectedValue(new Error('publish failed'));
|
|
1187
|
+
jest
|
|
1188
|
+
.spyOn(webex.cc as any, 'releaseWxAppMercuryResources')
|
|
1189
|
+
.mockResolvedValue(undefined);
|
|
1190
|
+
const metricSpy = jest.spyOn(mockMetricsManager, 'trackEvent');
|
|
1191
|
+
|
|
1192
|
+
jest.spyOn(webex.cc.services.agent, 'stationLogin').mockResolvedValue({
|
|
1193
|
+
data: {
|
|
1194
|
+
agentId: 'agentId',
|
|
1195
|
+
teamId: 'teamId',
|
|
1196
|
+
channelsMap: {chat: [], email: [], social: [], telephony: []},
|
|
1197
|
+
},
|
|
1198
|
+
trackingId: 'track-1',
|
|
1199
|
+
} as StationLoginSuccess);
|
|
1200
|
+
|
|
1201
|
+
await webex.cc.stationLogin({
|
|
1202
|
+
teamId: 'teamId',
|
|
1203
|
+
loginOption: LoginOption.EXTENSION,
|
|
1204
|
+
dialNumber: '1001',
|
|
1205
|
+
});
|
|
1206
|
+
|
|
1207
|
+
expect(metricSpy).toHaveBeenCalledWith(
|
|
1208
|
+
METRIC_EVENT_NAMES.WXAPP_SESSION_INIT_FAILED,
|
|
1209
|
+
expect.objectContaining({skipReason: 'publish_failed'}),
|
|
1210
|
+
['operational', 'behavioral']
|
|
1211
|
+
);
|
|
1212
|
+
expect(webex.cc.isWxBetterTogetherEnabled()).toBe(false);
|
|
1213
|
+
});
|
|
1214
|
+
|
|
1161
1215
|
it('should register device before publishing usersub true on extension station login', async () => {
|
|
1162
1216
|
webex.cc.$config = {...webex.cc.$config, enableWxBetterTogether: true};
|
|
1163
1217
|
webex.internal.device = {
|
|
@@ -1182,6 +1236,7 @@ describe('webex.cc', () => {
|
|
|
1182
1236
|
};
|
|
1183
1237
|
webex.cc.webCallingService.loginOption = LoginOption.EXTENSION;
|
|
1184
1238
|
jest.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'subscribe').mockImplementation(() => {});
|
|
1239
|
+
jest.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'isSubscribed').mockReturnValue(true);
|
|
1185
1240
|
|
|
1186
1241
|
const callOrder: string[] = [];
|
|
1187
1242
|
jest.spyOn(webex.internal.device, 'register').mockImplementation(async function (this: {
|
|
@@ -1330,7 +1385,7 @@ describe('webex.cc', () => {
|
|
|
1330
1385
|
await publishSpy.mock.results[1]?.value;
|
|
1331
1386
|
|
|
1332
1387
|
expect(publishSpy).toHaveBeenCalledTimes(2);
|
|
1333
|
-
expect(publishSpy).toHaveBeenLastCalledWith(false, {userId: 'user-123'});
|
|
1388
|
+
expect(publishSpy).toHaveBeenLastCalledWith(false, {userId: 'user-123', trackPublishMetrics: true});
|
|
1334
1389
|
} finally {
|
|
1335
1390
|
jest.useRealTimers();
|
|
1336
1391
|
}
|
|
@@ -1399,9 +1454,174 @@ describe('webex.cc', () => {
|
|
|
1399
1454
|
dialNumber: '1001',
|
|
1400
1455
|
});
|
|
1401
1456
|
|
|
1402
|
-
expect(publishSpy).toHaveBeenCalledWith(false, {userId: 'user-123'});
|
|
1457
|
+
expect(publishSpy).toHaveBeenCalledWith(false, {userId: 'user-123', trackPublishMetrics: true});
|
|
1403
1458
|
expect(teardownSpy).toHaveBeenCalled();
|
|
1404
1459
|
expect(unsubscribeSpy).toHaveBeenCalled();
|
|
1460
|
+
expect(mockMetricsManager.trackEvent).toHaveBeenCalledWith(
|
|
1461
|
+
METRIC_EVENT_NAMES.WXAPP_SESSION_SKIPPED,
|
|
1462
|
+
expect.objectContaining({
|
|
1463
|
+
enableWxBetterTogether: false,
|
|
1464
|
+
skipReason: 'flag_disabled',
|
|
1465
|
+
}),
|
|
1466
|
+
['operational', 'behavioral']
|
|
1467
|
+
);
|
|
1468
|
+
});
|
|
1469
|
+
|
|
1470
|
+
it('should not emit WXAPP_SESSION_INIT_FAILED when flag-disabled cleanup fails', async () => {
|
|
1471
|
+
webex.cc.$config = {...webex.cc.$config, enableWxBetterTogether: false};
|
|
1472
|
+
webex.internal.device = {userId: 'user-123', url: 'https://wdm.example.com/devices/dev-1'};
|
|
1473
|
+
webex.cc.agentConfig = {
|
|
1474
|
+
agentId: 'agentId',
|
|
1475
|
+
webRtcEnabled: false,
|
|
1476
|
+
loginVoiceOptions: ['EXTENSION'],
|
|
1477
|
+
};
|
|
1478
|
+
webex.cc.webCallingService.loginOption = LoginOption.EXTENSION;
|
|
1479
|
+
|
|
1480
|
+
jest
|
|
1481
|
+
.spyOn(webex.cc['webexCrossClientService'], 'setManageWebexCallingInWxcc')
|
|
1482
|
+
.mockRejectedValue(new Error('forced false publish failed'));
|
|
1483
|
+
const metricSpy = jest.spyOn(mockMetricsManager, 'trackEvent');
|
|
1484
|
+
|
|
1485
|
+
jest.spyOn(webex.cc.services.agent, 'stationLogin').mockResolvedValue({
|
|
1486
|
+
data: {
|
|
1487
|
+
agentId: 'agentId',
|
|
1488
|
+
teamId: 'teamId',
|
|
1489
|
+
channelsMap: {chat: [], email: [], social: [], telephony: []},
|
|
1490
|
+
},
|
|
1491
|
+
trackingId: 'track-1',
|
|
1492
|
+
} as StationLoginSuccess);
|
|
1493
|
+
|
|
1494
|
+
await webex.cc.stationLogin({
|
|
1495
|
+
teamId: 'teamId',
|
|
1496
|
+
loginOption: LoginOption.EXTENSION,
|
|
1497
|
+
dialNumber: '1001',
|
|
1498
|
+
});
|
|
1499
|
+
|
|
1500
|
+
expect(metricSpy).not.toHaveBeenCalledWith(
|
|
1501
|
+
METRIC_EVENT_NAMES.WXAPP_SESSION_INIT_FAILED,
|
|
1502
|
+
expect.anything(),
|
|
1503
|
+
expect.anything()
|
|
1504
|
+
);
|
|
1505
|
+
});
|
|
1506
|
+
|
|
1507
|
+
it('should still publish usersub but record session init failed when Mercury is unavailable', async () => {
|
|
1508
|
+
webex.cc.$config = {...webex.cc.$config, enableWxBetterTogether: true};
|
|
1509
|
+
webex.internal.device = {
|
|
1510
|
+
userId: 'user-123',
|
|
1511
|
+
url: 'https://wdm.example.com/devices/dev-1',
|
|
1512
|
+
registered: true,
|
|
1513
|
+
register: jest.fn().mockResolvedValue(undefined),
|
|
1514
|
+
};
|
|
1515
|
+
delete webex.internal.mercury;
|
|
1516
|
+
webex.cc.agentConfig = {
|
|
1517
|
+
agentId: 'agentId',
|
|
1518
|
+
webRtcEnabled: false,
|
|
1519
|
+
loginVoiceOptions: ['EXTENSION'],
|
|
1520
|
+
};
|
|
1521
|
+
webex.cc.webCallingService.loginOption = LoginOption.EXTENSION;
|
|
1522
|
+
mockTaskManager.applyEnableWxBetterTogether(true);
|
|
1523
|
+
|
|
1524
|
+
const publishSpy = jest
|
|
1525
|
+
.spyOn(webex.cc['webexCrossClientService'], 'setManageWebexCallingInWxcc')
|
|
1526
|
+
.mockResolvedValue(undefined);
|
|
1527
|
+
jest
|
|
1528
|
+
.spyOn(webex.cc['webexCrossClientService'], 'isAnswerCallsStateActive')
|
|
1529
|
+
.mockReturnValue(true);
|
|
1530
|
+
const metricSpy = jest.spyOn(mockMetricsManager, 'trackEvent');
|
|
1531
|
+
|
|
1532
|
+
jest.spyOn(webex.cc.services.agent, 'stationLogin').mockResolvedValue({
|
|
1533
|
+
data: {
|
|
1534
|
+
agentId: 'agentId',
|
|
1535
|
+
teamId: 'teamId',
|
|
1536
|
+
channelsMap: {chat: [], email: [], social: [], telephony: []},
|
|
1537
|
+
},
|
|
1538
|
+
trackingId: 'track-1',
|
|
1539
|
+
} as StationLoginSuccess);
|
|
1540
|
+
|
|
1541
|
+
await webex.cc.stationLogin({
|
|
1542
|
+
teamId: 'teamId',
|
|
1543
|
+
loginOption: LoginOption.EXTENSION,
|
|
1544
|
+
dialNumber: '1001',
|
|
1545
|
+
});
|
|
1546
|
+
|
|
1547
|
+
expect(publishSpy).toHaveBeenCalledWith(true, {userId: 'user-123', trackPublishMetrics: true});
|
|
1548
|
+
expect(metricSpy).toHaveBeenCalledWith(
|
|
1549
|
+
METRIC_EVENT_NAMES.WXAPP_MERCURY_SUBSCRIBE_FAILED,
|
|
1550
|
+
expect.objectContaining({error: 'Mercury is unavailable for wxApp mute sync'}),
|
|
1551
|
+
['operational', 'behavioral']
|
|
1552
|
+
);
|
|
1553
|
+
expect(metricSpy).not.toHaveBeenCalledWith(
|
|
1554
|
+
METRIC_EVENT_NAMES.WXAPP_SESSION_INIT_SUCCESS,
|
|
1555
|
+
expect.anything(),
|
|
1556
|
+
expect.anything()
|
|
1557
|
+
);
|
|
1558
|
+
expect(metricSpy).toHaveBeenCalledWith(
|
|
1559
|
+
METRIC_EVENT_NAMES.WXAPP_SESSION_INIT_FAILED,
|
|
1560
|
+
expect.objectContaining({skipReason: 'mercury_not_subscribed'}),
|
|
1561
|
+
['operational', 'behavioral']
|
|
1562
|
+
);
|
|
1563
|
+
expect(webex.cc.isWxBetterTogetherEnabled()).toBe(true);
|
|
1564
|
+
});
|
|
1565
|
+
|
|
1566
|
+
it('should record session init failed when userId is missing but not emit success', async () => {
|
|
1567
|
+
webex.cc.$config = {...webex.cc.$config, enableWxBetterTogether: true};
|
|
1568
|
+
webex.internal.device = {
|
|
1569
|
+
url: 'https://wdm.example.com/devices/dev-1',
|
|
1570
|
+
registered: true,
|
|
1571
|
+
register: jest.fn().mockResolvedValue(undefined),
|
|
1572
|
+
};
|
|
1573
|
+
webex.internal.mercury = {
|
|
1574
|
+
connected: true,
|
|
1575
|
+
connect: jest.fn().mockResolvedValue(undefined),
|
|
1576
|
+
on: jest.fn(),
|
|
1577
|
+
off: jest.fn(),
|
|
1578
|
+
};
|
|
1579
|
+
webex.cc.agentConfig = {
|
|
1580
|
+
agentId: 'agentId',
|
|
1581
|
+
webRtcEnabled: false,
|
|
1582
|
+
loginVoiceOptions: ['EXTENSION'],
|
|
1583
|
+
};
|
|
1584
|
+
webex.cc.webCallingService.loginOption = LoginOption.EXTENSION;
|
|
1585
|
+
mockTaskManager.applyEnableWxBetterTogether(true);
|
|
1586
|
+
|
|
1587
|
+
const publishSpy = jest
|
|
1588
|
+
.spyOn(webex.cc['webexCrossClientService'], 'setManageWebexCallingInWxcc')
|
|
1589
|
+
.mockResolvedValue(undefined);
|
|
1590
|
+
jest.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'isSubscribed').mockReturnValue(true);
|
|
1591
|
+
const metricSpy = jest.spyOn(mockMetricsManager, 'trackEvent');
|
|
1592
|
+
|
|
1593
|
+
jest.spyOn(webex.cc.services.agent, 'stationLogin').mockResolvedValue({
|
|
1594
|
+
data: {
|
|
1595
|
+
agentId: 'agentId',
|
|
1596
|
+
teamId: 'teamId',
|
|
1597
|
+
channelsMap: {chat: [], email: [], social: [], telephony: []},
|
|
1598
|
+
},
|
|
1599
|
+
trackingId: 'track-1',
|
|
1600
|
+
} as StationLoginSuccess);
|
|
1601
|
+
|
|
1602
|
+
await webex.cc.stationLogin({
|
|
1603
|
+
teamId: 'teamId',
|
|
1604
|
+
loginOption: LoginOption.EXTENSION,
|
|
1605
|
+
dialNumber: '1001',
|
|
1606
|
+
});
|
|
1607
|
+
|
|
1608
|
+
expect(publishSpy).not.toHaveBeenCalledWith(true, expect.anything());
|
|
1609
|
+
expect(metricSpy).not.toHaveBeenCalledWith(
|
|
1610
|
+
METRIC_EVENT_NAMES.WXAPP_SESSION_INIT_SUCCESS,
|
|
1611
|
+
expect.anything(),
|
|
1612
|
+
expect.anything()
|
|
1613
|
+
);
|
|
1614
|
+
expect(metricSpy).toHaveBeenCalledWith(
|
|
1615
|
+
METRIC_EVENT_NAMES.WXAPP_SESSION_INIT_FAILED,
|
|
1616
|
+
expect.objectContaining({skipReason: 'usersub_not_published'}),
|
|
1617
|
+
['operational', 'behavioral']
|
|
1618
|
+
);
|
|
1619
|
+
expect(metricSpy).toHaveBeenCalledWith(
|
|
1620
|
+
METRIC_EVENT_NAMES.WXAPP_USERSUB_PUBLISH_FAILED,
|
|
1621
|
+
expect.objectContaining({skipReason: 'user_id_unavailable'}),
|
|
1622
|
+
['operational', 'behavioral']
|
|
1623
|
+
);
|
|
1624
|
+
expect(webex.cc.isWxBetterTogetherEnabled()).toBe(true);
|
|
1405
1625
|
});
|
|
1406
1626
|
|
|
1407
1627
|
it('should clear stale usersub on login after prior session enabled wxApp then hard refresh', async () => {
|
|
@@ -1444,7 +1664,7 @@ describe('webex.cc', () => {
|
|
|
1444
1664
|
dialNumber: '1001',
|
|
1445
1665
|
});
|
|
1446
1666
|
|
|
1447
|
-
expect(publishSpy).toHaveBeenCalledWith(false, {userId: 'user-123'});
|
|
1667
|
+
expect(publishSpy).toHaveBeenCalledWith(false, {userId: 'user-123', trackPublishMetrics: true});
|
|
1448
1668
|
});
|
|
1449
1669
|
});
|
|
1450
1670
|
|
|
@@ -1521,6 +1741,7 @@ describe('webex.cc', () => {
|
|
|
1521
1741
|
disconnect: jest.fn().mockResolvedValue(undefined),
|
|
1522
1742
|
};
|
|
1523
1743
|
jest.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'subscribe').mockImplementation(() => {});
|
|
1744
|
+
jest.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'isSubscribed').mockReturnValue(true);
|
|
1524
1745
|
webex.request = jest.fn().mockResolvedValue({body: {}});
|
|
1525
1746
|
webex.cc['agentConfig'] = {
|
|
1526
1747
|
agentId: 'agent-123',
|
|
@@ -1542,7 +1763,7 @@ describe('webex.cc', () => {
|
|
|
1542
1763
|
|
|
1543
1764
|
await webex.cc.stationLogout({logoutReason: 'Logout reason'});
|
|
1544
1765
|
|
|
1545
|
-
expect(publishSpy).toHaveBeenCalledWith(false, {userId: 'user-123'});
|
|
1766
|
+
expect(publishSpy).toHaveBeenCalledWith(false, {userId: 'user-123', trackPublishMetrics: true});
|
|
1546
1767
|
expect(teardownSpy).toHaveBeenCalled();
|
|
1547
1768
|
expect(unsubscribeSpy).toHaveBeenCalled();
|
|
1548
1769
|
});
|
|
@@ -1613,7 +1834,7 @@ describe('webex.cc', () => {
|
|
|
1613
1834
|
|
|
1614
1835
|
expect(result).toEqual({trackingId: 'track-1'});
|
|
1615
1836
|
expect(publishSpy).toHaveBeenCalledTimes(1);
|
|
1616
|
-
expect(publishSpy).toHaveBeenCalledWith(false, {userId: 'user-123'});
|
|
1837
|
+
expect(publishSpy).toHaveBeenCalledWith(false, {userId: 'user-123', trackPublishMetrics: true});
|
|
1617
1838
|
expect(teardownSpy).toHaveBeenCalled();
|
|
1618
1839
|
expect(unsubscribeSpy).toHaveBeenCalled();
|
|
1619
1840
|
|
|
@@ -1621,7 +1842,7 @@ describe('webex.cc', () => {
|
|
|
1621
1842
|
await publishSpy.mock.results[1]?.value;
|
|
1622
1843
|
|
|
1623
1844
|
expect(publishSpy).toHaveBeenCalledTimes(2);
|
|
1624
|
-
expect(publishSpy).toHaveBeenLastCalledWith(false, {userId: 'user-123'});
|
|
1845
|
+
expect(publishSpy).toHaveBeenLastCalledWith(false, {userId: 'user-123', trackPublishMetrics: true});
|
|
1625
1846
|
} finally {
|
|
1626
1847
|
jest.useRealTimers();
|
|
1627
1848
|
}
|
|
@@ -1643,6 +1864,7 @@ describe('webex.cc', () => {
|
|
|
1643
1864
|
disconnect: jest.fn().mockResolvedValue(undefined),
|
|
1644
1865
|
};
|
|
1645
1866
|
jest.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'subscribe').mockImplementation(() => {});
|
|
1867
|
+
jest.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'isSubscribed').mockReturnValue(true);
|
|
1646
1868
|
webex.cc['agentConfig'] = {
|
|
1647
1869
|
agentId: 'agent-123',
|
|
1648
1870
|
deviceType: LoginOption.EXTENSION,
|
|
@@ -1683,8 +1905,8 @@ describe('webex.cc', () => {
|
|
|
1683
1905
|
});
|
|
1684
1906
|
|
|
1685
1907
|
expect(publishSpy).toHaveBeenCalledTimes(2);
|
|
1686
|
-
expect(publishSpy).toHaveBeenNthCalledWith(1, false, {userId: 'user-123'});
|
|
1687
|
-
expect(publishSpy).toHaveBeenNthCalledWith(2, true, {userId: 'user-123'});
|
|
1908
|
+
expect(publishSpy).toHaveBeenNthCalledWith(1, false, {userId: 'user-123', trackPublishMetrics: true});
|
|
1909
|
+
expect(publishSpy).toHaveBeenNthCalledWith(2, true, {userId: 'user-123', trackPublishMetrics: true});
|
|
1688
1910
|
expect(teardownSpy).toHaveBeenCalledTimes(1);
|
|
1689
1911
|
|
|
1690
1912
|
jest.advanceTimersByTime(30_000);
|
|
@@ -2208,10 +2430,11 @@ describe('webex.cc', () => {
|
|
|
2208
2430
|
const subscribeSpy = jest
|
|
2209
2431
|
.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'subscribe')
|
|
2210
2432
|
.mockImplementation(() => {});
|
|
2433
|
+
jest.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'isSubscribed').mockReturnValue(true);
|
|
2211
2434
|
|
|
2212
2435
|
await webex.cc['silentRelogin']();
|
|
2213
2436
|
|
|
2214
|
-
expect(publishSpy).toHaveBeenCalledWith(true, {userId: 'user-123'});
|
|
2437
|
+
expect(publishSpy).toHaveBeenCalledWith(true, {userId: 'user-123', trackPublishMetrics: true});
|
|
2215
2438
|
expect(subscribeSpy).toHaveBeenCalledWith('agentId', expect.any(Function));
|
|
2216
2439
|
});
|
|
2217
2440
|
|
|
@@ -2244,7 +2467,7 @@ describe('webex.cc', () => {
|
|
|
2244
2467
|
|
|
2245
2468
|
await webex.cc['silentRelogin']();
|
|
2246
2469
|
|
|
2247
|
-
expect(publishSpy).toHaveBeenCalledWith(false, {userId: 'user-123'});
|
|
2470
|
+
expect(publishSpy).toHaveBeenCalledWith(false, {userId: 'user-123', trackPublishMetrics: true});
|
|
2248
2471
|
expect(teardownSpy).toHaveBeenCalled();
|
|
2249
2472
|
expect(unsubscribeSpy).toHaveBeenCalled();
|
|
2250
2473
|
});
|
|
@@ -2725,7 +2948,7 @@ describe('webex.cc', () => {
|
|
|
2725
2948
|
|
|
2726
2949
|
await (webex.cc as any).teardownWxAppLocalState();
|
|
2727
2950
|
|
|
2728
|
-
expect(publishSpy).toHaveBeenCalledWith(false, {userId: 'user-123'});
|
|
2951
|
+
expect(publishSpy).toHaveBeenCalledWith(false, {userId: 'user-123', trackPublishMetrics: true});
|
|
2729
2952
|
|
|
2730
2953
|
resolveEnable();
|
|
2731
2954
|
await enableInFlight;
|
|
@@ -3068,6 +3291,7 @@ describe('webex.cc', () => {
|
|
|
3068
3291
|
jest
|
|
3069
3292
|
.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'subscribe')
|
|
3070
3293
|
.mockImplementation(() => {});
|
|
3294
|
+
jest.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'isSubscribed').mockReturnValue(true);
|
|
3071
3295
|
jest.spyOn(webex.cc as any, 'releaseWxAppMercuryResources').mockResolvedValue(undefined);
|
|
3072
3296
|
|
|
3073
3297
|
jest.spyOn(webex.cc.services.agent, 'logout').mockResolvedValue({
|
|
@@ -3091,8 +3315,8 @@ describe('webex.cc', () => {
|
|
|
3091
3315
|
});
|
|
3092
3316
|
|
|
3093
3317
|
expect(webex.cc.isWxBetterTogetherEnabled()).toBe(true);
|
|
3094
|
-
expect(publishSpy).toHaveBeenCalledWith(false, {userId: 'user-123'});
|
|
3095
|
-
expect(publishSpy).toHaveBeenCalledWith(true, {userId: 'user-123'});
|
|
3318
|
+
expect(publishSpy).toHaveBeenCalledWith(false, {userId: 'user-123', trackPublishMetrics: true});
|
|
3319
|
+
expect(publishSpy).toHaveBeenCalledWith(true, {userId: 'user-123', trackPublishMetrics: true});
|
|
3096
3320
|
});
|
|
3097
3321
|
|
|
3098
3322
|
it('should use provided teamId if passed in payload', async () => {
|
|
@@ -3622,6 +3846,7 @@ describe('webex.cc', () => {
|
|
|
3622
3846
|
disconnect: jest.fn().mockResolvedValue(undefined),
|
|
3623
3847
|
};
|
|
3624
3848
|
jest.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'subscribe').mockImplementation(() => {});
|
|
3849
|
+
jest.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'isSubscribed').mockReturnValue(true);
|
|
3625
3850
|
webex.cc.$config = {...webex.cc.$config, enableWxBetterTogether: false};
|
|
3626
3851
|
webex.cc['agentConfig'] = {
|
|
3627
3852
|
agentId: 'agent-123',
|
|
@@ -3646,7 +3871,7 @@ describe('webex.cc', () => {
|
|
|
3646
3871
|
|
|
3647
3872
|
expect(webex.cc.isWxBetterTogetherEnabled()).toBe(true);
|
|
3648
3873
|
expect(mockTaskManager.applyEnableWxBetterTogether).toHaveBeenCalledWith(true);
|
|
3649
|
-
expect(publishSpy).toHaveBeenCalledWith(true, {userId: 'user-123'});
|
|
3874
|
+
expect(publishSpy).toHaveBeenCalledWith(true, {userId: 'user-123', trackPublishMetrics: true});
|
|
3650
3875
|
});
|
|
3651
3876
|
|
|
3652
3877
|
it('should publish false and update config when disabled after wxApp was enabled', async () => {
|
|
@@ -3666,7 +3891,7 @@ describe('webex.cc', () => {
|
|
|
3666
3891
|
|
|
3667
3892
|
expect(webex.cc.isWxBetterTogetherEnabled()).toBe(false);
|
|
3668
3893
|
expect(mockTaskManager.applyEnableWxBetterTogether).toHaveBeenCalledWith(false);
|
|
3669
|
-
expect(publishSpy).toHaveBeenCalledWith(false, {userId: 'user-123'});
|
|
3894
|
+
expect(publishSpy).toHaveBeenCalledWith(false, {userId: 'user-123', trackPublishMetrics: true});
|
|
3670
3895
|
});
|
|
3671
3896
|
|
|
3672
3897
|
it('should skip usersub publish when disabling without prior wxApp publish', async () => {
|
|
@@ -3747,7 +3972,7 @@ describe('webex.cc', () => {
|
|
|
3747
3972
|
await (webex.cc as any).setManageWebexCallingInWxcc(true);
|
|
3748
3973
|
|
|
3749
3974
|
expect(webex.cc.isWxBetterTogetherEnabled()).toBe(true);
|
|
3750
|
-
expect(publishSpy).toHaveBeenCalledWith(true, {userId: 'user-123'});
|
|
3975
|
+
expect(publishSpy).toHaveBeenCalledWith(true, {userId: 'user-123', trackPublishMetrics: true});
|
|
3751
3976
|
});
|
|
3752
3977
|
|
|
3753
3978
|
it('should no-op usersub publish when disabling before login', async () => {
|
|
@@ -3778,6 +4003,7 @@ describe('webex.cc', () => {
|
|
|
3778
4003
|
const subscribeSpy = jest
|
|
3779
4004
|
.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'subscribe')
|
|
3780
4005
|
.mockImplementation(() => {});
|
|
4006
|
+
jest.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'isSubscribed').mockReturnValue(true);
|
|
3781
4007
|
|
|
3782
4008
|
await (webex.cc as any).setManageWebexCallingInWxcc(true);
|
|
3783
4009
|
|
|
@@ -3792,6 +4018,7 @@ describe('webex.cc', () => {
|
|
|
3792
4018
|
const unsubscribeSpy = jest
|
|
3793
4019
|
.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'unsubscribe')
|
|
3794
4020
|
.mockImplementation(() => {});
|
|
4021
|
+
jest.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'isSubscribed').mockReturnValue(true);
|
|
3795
4022
|
|
|
3796
4023
|
await (webex.cc as any).setManageWebexCallingInWxcc(false);
|
|
3797
4024
|
|
|
@@ -3815,6 +4042,7 @@ describe('webex.cc', () => {
|
|
|
3815
4042
|
const subscribeSpy = jest
|
|
3816
4043
|
.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'subscribe')
|
|
3817
4044
|
.mockImplementation(() => {});
|
|
4045
|
+
jest.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'isSubscribed').mockReturnValue(true);
|
|
3818
4046
|
|
|
3819
4047
|
await (webex.cc as any).setManageWebexCallingInWxcc(true);
|
|
3820
4048
|
|
|
@@ -3879,6 +4107,7 @@ describe('webex.cc', () => {
|
|
|
3879
4107
|
.mockRejectedValueOnce(new Error('compensating false failed'))
|
|
3880
4108
|
.mockResolvedValue(undefined);
|
|
3881
4109
|
jest.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'subscribe').mockImplementation(() => {});
|
|
4110
|
+
jest.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'isSubscribed').mockReturnValue(true);
|
|
3882
4111
|
jest
|
|
3883
4112
|
.spyOn(webex.cc as any, 'ensureWxAppMercuryConnected')
|
|
3884
4113
|
.mockResolvedValue(undefined);
|
|
@@ -3901,14 +4130,14 @@ describe('webex.cc', () => {
|
|
|
3901
4130
|
|
|
3902
4131
|
expect(scheduleRetrySpy).toHaveBeenCalled();
|
|
3903
4132
|
expect(publishSpy).toHaveBeenCalledTimes(2);
|
|
3904
|
-
expect(publishSpy).toHaveBeenNthCalledWith(1, true, {userId: 'user-123'});
|
|
3905
|
-
expect(publishSpy).toHaveBeenNthCalledWith(2, false, {userId: 'user-123'});
|
|
4133
|
+
expect(publishSpy).toHaveBeenNthCalledWith(1, true, {userId: 'user-123', trackPublishMetrics: true});
|
|
4134
|
+
expect(publishSpy).toHaveBeenNthCalledWith(2, false, {userId: 'user-123', trackPublishMetrics: true});
|
|
3906
4135
|
|
|
3907
4136
|
jest.advanceTimersByTime(30_000);
|
|
3908
4137
|
await publishSpy.mock.results[2]?.value;
|
|
3909
4138
|
|
|
3910
4139
|
expect(publishSpy).toHaveBeenCalledTimes(3);
|
|
3911
|
-
expect(publishSpy).toHaveBeenLastCalledWith(false, {userId: 'user-123'});
|
|
4140
|
+
expect(publishSpy).toHaveBeenLastCalledWith(false, {userId: 'user-123', trackPublishMetrics: true});
|
|
3912
4141
|
} finally {
|
|
3913
4142
|
jest.useRealTimers();
|
|
3914
4143
|
}
|
|
@@ -252,6 +252,31 @@ describe('MetricsManagerImplementation', () => {
|
|
|
252
252
|
});
|
|
253
253
|
});
|
|
254
254
|
|
|
255
|
+
describe('cancelTimedEvent', () => {
|
|
256
|
+
it('should clear a running timer without attaching duration to a later unrelated event', () => {
|
|
257
|
+
jest.spyOn(MetricsManager.prototype as any, 'isMetricsDisabled').mockReturnValue(false);
|
|
258
|
+
metricsManager.timeEvent(['timedSuccess', 'timedFailed']);
|
|
259
|
+
metricsManager.cancelTimedEvent(['timedSuccess', 'timedFailed']);
|
|
260
|
+
|
|
261
|
+
const result = metricsManager['addDurationIfTimed']('timedFailed');
|
|
262
|
+
|
|
263
|
+
expect(result).toEqual({});
|
|
264
|
+
expect(metricsManager['runningEvents']['timedSuccess']).toBeUndefined();
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
it('should not clear timers when metrics are disabled', () => {
|
|
268
|
+
jest.spyOn(MetricsManager.prototype as any, 'isMetricsDisabled').mockReturnValue(true);
|
|
269
|
+
metricsManager['runningEvents']['event1'] = {
|
|
270
|
+
startTime: 100,
|
|
271
|
+
keys: new Set(['event1']),
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
metricsManager.cancelTimedEvent('event1');
|
|
275
|
+
|
|
276
|
+
expect(metricsManager['runningEvents']['event1']).toBeDefined();
|
|
277
|
+
});
|
|
278
|
+
});
|
|
279
|
+
|
|
255
280
|
describe('addDurationIfTimed', () => {
|
|
256
281
|
it('should return options when event name is not in runningEvents', () => {
|
|
257
282
|
const options: EventPayload = {key: 'value'};
|
|
@@ -184,6 +184,48 @@ describe('metrics/behavioral-events', () => {
|
|
|
184
184
|
verb: 'fail',
|
|
185
185
|
});
|
|
186
186
|
|
|
187
|
+
expect(getEventTaxonomy(METRIC_EVENT_NAMES.WXAPP_TASK_ACCEPT_SUCCESS)).toEqual({
|
|
188
|
+
product,
|
|
189
|
+
agent: 'user',
|
|
190
|
+
target: 'task_accept_webex_together',
|
|
191
|
+
verb: 'complete',
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
expect(getEventTaxonomy(METRIC_EVENT_NAMES.WXAPP_SESSION_SKIPPED)).toEqual({
|
|
195
|
+
product,
|
|
196
|
+
agent: 'user',
|
|
197
|
+
target: 'webex_together_session_init',
|
|
198
|
+
verb: 'ignore',
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
expect(getEventTaxonomy(METRIC_EVENT_NAMES.WXAPP_USERSUB_PUBLISH_SUCCESS)).toEqual({
|
|
202
|
+
product,
|
|
203
|
+
agent: 'user',
|
|
204
|
+
target: 'webex_together_usersub_publish',
|
|
205
|
+
verb: 'complete',
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
expect(getEventTaxonomy(METRIC_EVENT_NAMES.WXAPP_MERCURY_SUBSCRIBE_FAILED)).toEqual({
|
|
209
|
+
product,
|
|
210
|
+
agent: 'user',
|
|
211
|
+
target: 'webex_together_mercury_subscribe',
|
|
212
|
+
verb: 'fail',
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
expect(getEventTaxonomy(METRIC_EVENT_NAMES.WXAPP_TASK_MUTE_SUCCESS)).toEqual({
|
|
216
|
+
product,
|
|
217
|
+
agent: 'user',
|
|
218
|
+
target: 'task_mute_webex_together',
|
|
219
|
+
verb: 'complete',
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
expect(getEventTaxonomy(METRIC_EVENT_NAMES.WXAPP_TASK_DTMF_FAILED)).toEqual({
|
|
223
|
+
product,
|
|
224
|
+
agent: 'user',
|
|
225
|
+
target: 'task_dtmf_webex_together',
|
|
226
|
+
verb: 'fail',
|
|
227
|
+
});
|
|
228
|
+
|
|
187
229
|
expect(getEventTaxonomy('' as METRIC_EVENT_NAMES)).toEqual(undefined);
|
|
188
230
|
});
|
|
189
231
|
});
|
|
@@ -128,6 +128,27 @@ describe('AnswerCallOnWebexService', () => {
|
|
|
128
128
|
});
|
|
129
129
|
expect(LoggerProxy.error).toHaveBeenCalled();
|
|
130
130
|
});
|
|
131
|
+
|
|
132
|
+
it('preserves top-level trackingId on request failure', async () => {
|
|
133
|
+
const err = {
|
|
134
|
+
statusCode: 503,
|
|
135
|
+
trackingId: 'track-top-level-1',
|
|
136
|
+
message: 'Service unavailable',
|
|
137
|
+
};
|
|
138
|
+
(webex.request as jest.Mock).mockRejectedValue(err);
|
|
139
|
+
|
|
140
|
+
await expect(service.answerCall({callId: 'c1', endpointId: 'e1'})).rejects.toMatchObject({
|
|
141
|
+
isWxAppTelephonyError: true,
|
|
142
|
+
trackingId: 'track-top-level-1',
|
|
143
|
+
status: 503,
|
|
144
|
+
});
|
|
145
|
+
expect(LoggerProxy.error).toHaveBeenCalledWith(
|
|
146
|
+
expect.stringContaining('failed'),
|
|
147
|
+
expect.objectContaining({
|
|
148
|
+
data: expect.objectContaining({trackingId: 'track-top-level-1'}),
|
|
149
|
+
})
|
|
150
|
+
);
|
|
151
|
+
});
|
|
131
152
|
});
|
|
132
153
|
|
|
133
154
|
describe('rejectCall', () => {
|
|
@@ -213,6 +234,26 @@ describe('AnswerCallOnWebexService', () => {
|
|
|
213
234
|
expect(LoggerProxy.error).toHaveBeenCalled();
|
|
214
235
|
});
|
|
215
236
|
|
|
237
|
+
it('logs HTTP status on non-404 getCallDetails failure', async () => {
|
|
238
|
+
const err = {
|
|
239
|
+
statusCode: 503,
|
|
240
|
+
trackingId: 'track-top-level-1',
|
|
241
|
+
message: 'Service unavailable',
|
|
242
|
+
};
|
|
243
|
+
(webex.request as jest.Mock).mockRejectedValue(err);
|
|
244
|
+
|
|
245
|
+
await expect(service.getCallDetails({callId: 'c1'})).rejects.toBeDefined();
|
|
246
|
+
expect(LoggerProxy.error).toHaveBeenCalledWith(
|
|
247
|
+
expect.stringContaining('getCallDetails failed'),
|
|
248
|
+
expect.objectContaining({
|
|
249
|
+
data: expect.objectContaining({
|
|
250
|
+
trackingId: 'track-top-level-1',
|
|
251
|
+
status: 503,
|
|
252
|
+
}),
|
|
253
|
+
})
|
|
254
|
+
);
|
|
255
|
+
});
|
|
256
|
+
|
|
216
257
|
it('rejects without error log for expected call-not-found during mute backfill race', async () => {
|
|
217
258
|
(webex.request as jest.Mock).mockRejectedValue({status: 400, message: 'Call not found'});
|
|
218
259
|
|