@webex/contact-center 3.11.0-next.21 → 3.11.0-next.23
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/dist/cc.js +71 -0
- package/dist/cc.js.map +1 -1
- package/dist/constants.js +2 -1
- package/dist/constants.js.map +1 -1
- package/dist/metrics/behavioral-events.js +13 -0
- package/dist/metrics/behavioral-events.js.map +1 -1
- package/dist/metrics/constants.js +4 -1
- package/dist/metrics/constants.js.map +1 -1
- package/dist/services/config/types.js +7 -1
- package/dist/services/config/types.js.map +1 -1
- package/dist/services/core/Err.js.map +1 -1
- package/dist/services/core/aqm-reqs.js +92 -17
- package/dist/services/core/aqm-reqs.js.map +1 -1
- package/dist/services/task/TaskManager.js +60 -3
- package/dist/services/task/TaskManager.js.map +1 -1
- package/dist/services/task/TaskUtils.js +16 -3
- package/dist/services/task/TaskUtils.js.map +1 -1
- package/dist/services/task/constants.js +5 -1
- package/dist/services/task/constants.js.map +1 -1
- package/dist/services/task/dialer.js +51 -0
- package/dist/services/task/dialer.js.map +1 -1
- package/dist/services/task/types.js +15 -0
- package/dist/services/task/types.js.map +1 -1
- package/dist/types/cc.d.ts +31 -1
- package/dist/types/constants.d.ts +1 -0
- package/dist/types/metrics/constants.d.ts +2 -0
- package/dist/types/services/config/types.d.ts +12 -0
- package/dist/types/services/core/Err.d.ts +2 -0
- package/dist/types/services/core/aqm-reqs.d.ts +49 -0
- package/dist/types/services/task/TaskUtils.d.ts +8 -0
- package/dist/types/services/task/constants.d.ts +4 -0
- package/dist/types/services/task/dialer.d.ts +15 -0
- package/dist/types/services/task/types.d.ts +23 -1
- package/dist/types.js.map +1 -1
- package/dist/webex.js +1 -1
- package/package.json +2 -2
- package/src/cc.ts +99 -1
- package/src/constants.ts +1 -0
- package/src/metrics/behavioral-events.ts +14 -0
- package/src/metrics/constants.ts +4 -0
- package/src/services/config/types.ts +6 -0
- package/src/services/core/Err.ts +1 -0
- package/src/services/core/aqm-reqs.ts +100 -22
- package/src/services/task/TaskManager.ts +75 -3
- package/src/services/task/TaskUtils.ts +12 -0
- package/src/services/task/constants.ts +4 -0
- package/src/services/task/dialer.ts +56 -1
- package/src/services/task/types.ts +24 -0
- package/src/types.ts +2 -1
- package/test/unit/spec/cc.ts +65 -0
- package/test/unit/spec/services/task/TaskManager.ts +281 -105
- package/test/unit/spec/services/task/dialer.ts +198 -112
- package/umd/contact-center.min.js +2 -2
- package/umd/contact-center.min.js.map +1 -1
|
@@ -102,14 +102,15 @@ describe('TaskManager', () => {
|
|
|
102
102
|
|
|
103
103
|
incomingCallCb(mockCall);
|
|
104
104
|
|
|
105
|
-
expect(taskEmitSpy).toHaveBeenCalledWith(
|
|
105
|
+
expect(taskEmitSpy).toHaveBeenCalledWith(
|
|
106
|
+
TASK_EVENTS.TASK_INCOMING,
|
|
107
|
+
taskManager.getTask(taskId)
|
|
108
|
+
);
|
|
106
109
|
});
|
|
107
110
|
|
|
108
111
|
it('should re-emit task related events', () => {
|
|
109
112
|
const dummyPayload = {
|
|
110
|
-
data: {...taskDataMock,
|
|
111
|
-
type: CC_TASK_EVENTS.AGENT_CONSULTING,
|
|
112
|
-
},
|
|
113
|
+
data: {...taskDataMock, type: CC_TASK_EVENTS.AGENT_CONSULTING},
|
|
113
114
|
};
|
|
114
115
|
webSocketManagerMock.emit('message', JSON.stringify({data: taskDataMock}));
|
|
115
116
|
const taskEmitSpy = jest.spyOn(taskManager.getTask(taskId), 'emit');
|
|
@@ -187,7 +188,10 @@ describe('TaskManager', () => {
|
|
|
187
188
|
},
|
|
188
189
|
};
|
|
189
190
|
|
|
190
|
-
const currentTaskAssignedSpy = jest.spyOn(
|
|
191
|
+
const currentTaskAssignedSpy = jest.spyOn(
|
|
192
|
+
taskManager.getTask(payload.data.interactionId),
|
|
193
|
+
'emit'
|
|
194
|
+
);
|
|
191
195
|
|
|
192
196
|
webSocketManagerMock.emit('message', JSON.stringify(assignedPayload));
|
|
193
197
|
|
|
@@ -311,7 +315,10 @@ describe('TaskManager', () => {
|
|
|
311
315
|
webSocketManagerMock.emit('message', JSON.stringify(initalPayload));
|
|
312
316
|
|
|
313
317
|
const taskEmitSpy = jest.spyOn(taskManager.getTask(taskId), 'emit');
|
|
314
|
-
const webCallListenerSpy = jest.spyOn(
|
|
318
|
+
const webCallListenerSpy = jest.spyOn(
|
|
319
|
+
taskManager.getTask(taskId),
|
|
320
|
+
'unregisterWebCallListeners'
|
|
321
|
+
);
|
|
315
322
|
const callOffSpy = jest.spyOn(mockCall, 'off');
|
|
316
323
|
const payload = {
|
|
317
324
|
data: {
|
|
@@ -331,11 +338,9 @@ describe('TaskManager', () => {
|
|
|
331
338
|
};
|
|
332
339
|
|
|
333
340
|
taskManager.getTask(taskId).data = payload.data;
|
|
334
|
-
const task = taskManager.getTask(taskId)
|
|
341
|
+
const task = taskManager.getTask(taskId);
|
|
335
342
|
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
336
|
-
expect(taskEmitSpy).toHaveBeenCalledWith(
|
|
337
|
-
TASK_EVENTS.TASK_END, task
|
|
338
|
-
);
|
|
343
|
+
expect(taskEmitSpy).toHaveBeenCalledWith(TASK_EVENTS.TASK_END, task);
|
|
339
344
|
expect(webCallListenerSpy).toHaveBeenCalledWith();
|
|
340
345
|
expect(callOffSpy).toHaveBeenCalledWith(
|
|
341
346
|
CALL_EVENT_KEYS.REMOTE_MEDIA,
|
|
@@ -371,55 +376,42 @@ describe('TaskManager', () => {
|
|
|
371
376
|
|
|
372
377
|
taskManager.getTask(taskId).updateTaskData(payload.data);
|
|
373
378
|
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
374
|
-
expect(taskEmitSpy).toHaveBeenCalledWith(
|
|
375
|
-
|
|
376
|
-
{ ...payload.data}
|
|
377
|
-
);
|
|
378
|
-
expect(taskEmitSpy).toHaveBeenCalledWith(
|
|
379
|
-
TASK_EVENTS.TASK_END,
|
|
380
|
-
taskManager.getTask(taskId)
|
|
381
|
-
);
|
|
379
|
+
expect(taskEmitSpy).toHaveBeenCalledWith(CC_EVENTS.CONTACT_ENDED, {...payload.data});
|
|
380
|
+
expect(taskEmitSpy).toHaveBeenCalledWith(TASK_EVENTS.TASK_END, taskManager.getTask(taskId));
|
|
382
381
|
});
|
|
383
382
|
|
|
384
383
|
it('should emit TASK_REJECT event on AGENT_INVITE_FAILED event', () => {
|
|
385
384
|
webSocketManagerMock.emit('message', JSON.stringify(initalPayload));
|
|
386
385
|
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
386
|
+
const taskEmitSpy = jest.spyOn(taskManager.getTask(taskId), 'emit');
|
|
387
|
+
const metricsTrackSpy = jest.spyOn(taskManager.metricsManager, 'trackEvent');
|
|
388
|
+
const payload = {
|
|
389
|
+
data: {
|
|
390
|
+
type: CC_EVENTS.AGENT_INVITE_FAILED,
|
|
391
|
+
agentId: '723a8ffb-a26e-496d-b14a-ff44fb83b64f',
|
|
392
|
+
eventTime: 1733211616959,
|
|
393
|
+
eventType: 'RoutingMessage',
|
|
394
|
+
interaction: {state: 'connected'},
|
|
395
|
+
interactionId: taskId,
|
|
396
|
+
orgId: '6ecef209-9a34-4ed1-a07a-7ddd1dbe925a',
|
|
397
|
+
trackingId: '575c0ec2-618c-42af-a61c-53aeb0a221ee',
|
|
398
|
+
mediaResourceId: '0ae913a4-c857-4705-8d49-76dd3dde75e4',
|
|
399
|
+
destAgentId: 'ebeb893b-ba67-4f36-8418-95c7492b28c2',
|
|
400
|
+
owner: '723a8ffb-a26e-496d-b14a-ff44fb83b64f',
|
|
401
|
+
queueMgr: 'aqm',
|
|
402
|
+
reason: 'INVITE_FAILED',
|
|
403
|
+
},
|
|
404
|
+
};
|
|
406
405
|
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
TASK_EVENTS.TASK_REJECT,
|
|
415
|
-
payload.data.reason
|
|
416
|
-
);
|
|
417
|
-
// Verify the correct metric event name is used for AGENT_INVITE_FAILED
|
|
418
|
-
expect(metricsTrackSpy).toHaveBeenCalled();
|
|
419
|
-
expect(metricsTrackSpy.mock.calls[0][0]).toBe('Agent Invite Failed');
|
|
406
|
+
taskManager.getTask(taskId).updateTaskData(payload.data);
|
|
407
|
+
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
408
|
+
expect(taskEmitSpy).toHaveBeenCalledWith(CC_EVENTS.AGENT_INVITE_FAILED, {...payload.data});
|
|
409
|
+
expect(taskEmitSpy).toHaveBeenCalledWith(TASK_EVENTS.TASK_REJECT, payload.data.reason);
|
|
410
|
+
// Verify the correct metric event name is used for AGENT_INVITE_FAILED
|
|
411
|
+
expect(metricsTrackSpy).toHaveBeenCalled();
|
|
412
|
+
expect(metricsTrackSpy.mock.calls[0][0]).toBe('Agent Invite Failed');
|
|
420
413
|
});
|
|
421
414
|
|
|
422
|
-
|
|
423
415
|
it('should not emit TASK_HYDRATE if task is already present in taskManager', () => {
|
|
424
416
|
const payload = {
|
|
425
417
|
data: {
|
|
@@ -492,9 +484,9 @@ describe('TaskManager', () => {
|
|
|
492
484
|
mediaType: 'telephony',
|
|
493
485
|
state: 'conference',
|
|
494
486
|
participants: {
|
|
495
|
-
[testAgentId]: {
|
|
496
|
-
'agent-2': {
|
|
497
|
-
'customer-1': {
|
|
487
|
+
[testAgentId]: {pType: 'Agent', hasLeft: false},
|
|
488
|
+
'agent-2': {pType: 'Agent', hasLeft: false},
|
|
489
|
+
'customer-1': {pType: 'Customer', hasLeft: false},
|
|
498
490
|
},
|
|
499
491
|
media: {
|
|
500
492
|
[taskId]: {
|
|
@@ -526,8 +518,8 @@ describe('TaskManager', () => {
|
|
|
526
518
|
mediaType: 'telephony',
|
|
527
519
|
state: 'connected',
|
|
528
520
|
participants: {
|
|
529
|
-
[testAgentId]: {
|
|
530
|
-
'customer-1': {
|
|
521
|
+
[testAgentId]: {pType: 'Agent', hasLeft: false},
|
|
522
|
+
'customer-1': {pType: 'Customer', hasLeft: false},
|
|
531
523
|
},
|
|
532
524
|
media: {
|
|
533
525
|
[taskId]: {
|
|
@@ -563,7 +555,7 @@ describe('TaskManager', () => {
|
|
|
563
555
|
destAgentId: 'ebeb893b-ba67-4f36-8418-95c7492b28c2',
|
|
564
556
|
owner: '723a8ffb-a26e-496d-b14a-ff44fb83b64f',
|
|
565
557
|
queueMgr: 'aqm',
|
|
566
|
-
wrapUpRequired: true
|
|
558
|
+
wrapUpRequired: true,
|
|
567
559
|
},
|
|
568
560
|
};
|
|
569
561
|
|
|
@@ -716,7 +708,9 @@ describe('TaskManager', () => {
|
|
|
716
708
|
|
|
717
709
|
const task = taskManager.getTask(taskId);
|
|
718
710
|
const taskEmitSpy = jest.spyOn(task, 'emit');
|
|
719
|
-
const taskAcceptSpy = jest
|
|
711
|
+
const taskAcceptSpy = jest
|
|
712
|
+
.spyOn(task, 'accept')
|
|
713
|
+
.mockRejectedValue(new Error('Accept failed'));
|
|
720
714
|
|
|
721
715
|
// Step 2: Trigger AGENT_OFFER_CONTACT with auto-answer (will fail)
|
|
722
716
|
const autoAnswerPayload = {
|
|
@@ -814,7 +808,10 @@ describe('TaskManager', () => {
|
|
|
814
808
|
expect(taskAcceptSpy).not.toHaveBeenCalled();
|
|
815
809
|
|
|
816
810
|
// Verify TASK_AUTO_ANSWERED event was NOT emitted
|
|
817
|
-
expect(taskEmitSpy).not.toHaveBeenCalledWith(
|
|
811
|
+
expect(taskEmitSpy).not.toHaveBeenCalledWith(
|
|
812
|
+
TASK_EVENTS.TASK_AUTO_ANSWERED,
|
|
813
|
+
expect.anything()
|
|
814
|
+
);
|
|
818
815
|
});
|
|
819
816
|
});
|
|
820
817
|
|
|
@@ -1257,7 +1254,8 @@ describe('TaskManager', () => {
|
|
|
1257
1254
|
webSocketManagerMock.emit('message', JSON.stringify(payloadConnected));
|
|
1258
1255
|
|
|
1259
1256
|
// First call should set wrapUpRequired to true
|
|
1260
|
-
expect(task.updateTaskData).toHaveBeenNthCalledWith(
|
|
1257
|
+
expect(task.updateTaskData).toHaveBeenNthCalledWith(
|
|
1258
|
+
1,
|
|
1261
1259
|
expect.objectContaining({
|
|
1262
1260
|
wrapUpRequired: true,
|
|
1263
1261
|
})
|
|
@@ -1279,13 +1277,13 @@ describe('TaskManager', () => {
|
|
|
1279
1277
|
webSocketManagerMock.emit('message', JSON.stringify(payloadHeld));
|
|
1280
1278
|
|
|
1281
1279
|
// Second call should also set wrapUpRequired to true
|
|
1282
|
-
expect(task.updateTaskData).toHaveBeenNthCalledWith(
|
|
1280
|
+
expect(task.updateTaskData).toHaveBeenNthCalledWith(
|
|
1281
|
+
2,
|
|
1283
1282
|
expect.objectContaining({
|
|
1284
1283
|
wrapUpRequired: true,
|
|
1285
1284
|
})
|
|
1286
1285
|
);
|
|
1287
1286
|
});
|
|
1288
|
-
|
|
1289
1287
|
});
|
|
1290
1288
|
|
|
1291
1289
|
it('should remove OUTDIAL task from taskCollection on AGENT_CONTACT_ASSIGN_FAILED when NOT terminated (user-declined)', () => {
|
|
@@ -1406,7 +1404,10 @@ describe('TaskManager', () => {
|
|
|
1406
1404
|
const taskUpdateTaskDataSpy = jest.spyOn(taskManager.getTask(taskId), 'updateTaskData');
|
|
1407
1405
|
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
1408
1406
|
expect(taskUpdateTaskDataSpy).toHaveBeenCalledWith(payload.data);
|
|
1409
|
-
expect(taskEmitSpy).toHaveBeenCalledWith(
|
|
1407
|
+
expect(taskEmitSpy).toHaveBeenCalledWith(
|
|
1408
|
+
TASK_EVENTS.TASK_CONSULT_END,
|
|
1409
|
+
taskManager.getTask(taskId)
|
|
1410
|
+
);
|
|
1410
1411
|
});
|
|
1411
1412
|
|
|
1412
1413
|
it('should emit TASK_CONSULT_ENDED event and remove currentTask when on AGENT_CONSULT_ENDED event when requested for a consult', () => {
|
|
@@ -1587,7 +1588,10 @@ describe('TaskManager', () => {
|
|
|
1587
1588
|
webSocketManagerMock.emit('message', JSON.stringify(assignFailedPayload));
|
|
1588
1589
|
|
|
1589
1590
|
expect(taskUpdateDataSpy).toHaveBeenCalledWith(assignFailedPayload.data);
|
|
1590
|
-
expect(taskEmitSpy).toHaveBeenCalledWith(
|
|
1591
|
+
expect(taskEmitSpy).toHaveBeenCalledWith(
|
|
1592
|
+
TASK_EVENTS.TASK_REJECT,
|
|
1593
|
+
assignFailedPayload.data.reason
|
|
1594
|
+
);
|
|
1591
1595
|
// Verify the correct metric event name is used for AGENT_CONTACT_ASSIGN_FAILED
|
|
1592
1596
|
expect(metricsTrackSpy).toHaveBeenCalled();
|
|
1593
1597
|
expect(metricsTrackSpy.mock.calls[0][0]).toBe('Agent Contact Assign Failed');
|
|
@@ -1662,7 +1666,10 @@ describe('TaskManager', () => {
|
|
|
1662
1666
|
};
|
|
1663
1667
|
webSocketManagerMock.emit('message', JSON.stringify(consultingPayload));
|
|
1664
1668
|
expect(taskEmitSpy).toHaveBeenCalledWith(CC_EVENTS.AGENT_CONSULTING, consultingPayload.data);
|
|
1665
|
-
expect(taskEmitSpy).toHaveBeenCalledWith(
|
|
1669
|
+
expect(taskEmitSpy).toHaveBeenCalledWith(
|
|
1670
|
+
TASK_EVENTS.TASK_CONSULTING,
|
|
1671
|
+
taskManager.getTask(taskId)
|
|
1672
|
+
);
|
|
1666
1673
|
});
|
|
1667
1674
|
|
|
1668
1675
|
it('should emit TASK_END event on AGENT_CONTACT_UNASSIGNED', () => {
|
|
@@ -1685,7 +1692,10 @@ describe('TaskManager', () => {
|
|
|
1685
1692
|
},
|
|
1686
1693
|
};
|
|
1687
1694
|
webSocketManagerMock.emit('message', JSON.stringify(unassignedPayload));
|
|
1688
|
-
expect(taskEmitSpy).toHaveBeenCalledWith(
|
|
1695
|
+
expect(taskEmitSpy).toHaveBeenCalledWith(
|
|
1696
|
+
CC_EVENTS.AGENT_CONTACT_UNASSIGNED,
|
|
1697
|
+
unassignedPayload.data
|
|
1698
|
+
);
|
|
1689
1699
|
expect(taskEmitSpy).toHaveBeenCalledWith(TASK_EVENTS.TASK_END, taskManager.getTask(taskId));
|
|
1690
1700
|
});
|
|
1691
1701
|
|
|
@@ -1694,7 +1704,7 @@ describe('TaskManager', () => {
|
|
|
1694
1704
|
const chatPayload = {
|
|
1695
1705
|
data: {
|
|
1696
1706
|
...initalPayload.data,
|
|
1697
|
-
interaction: {
|
|
1707
|
+
interaction: {mediaType: 'chat'},
|
|
1698
1708
|
},
|
|
1699
1709
|
};
|
|
1700
1710
|
|
|
@@ -1716,7 +1726,7 @@ describe('TaskManager', () => {
|
|
|
1716
1726
|
const emailPayload = {
|
|
1717
1727
|
data: {
|
|
1718
1728
|
...initalPayload.data,
|
|
1719
|
-
interaction: {
|
|
1729
|
+
interaction: {mediaType: 'email'},
|
|
1720
1730
|
},
|
|
1721
1731
|
};
|
|
1722
1732
|
|
|
@@ -1739,7 +1749,7 @@ describe('TaskManager', () => {
|
|
|
1739
1749
|
data: {
|
|
1740
1750
|
...initalPayload.data,
|
|
1741
1751
|
type: CC_EVENTS.AGENT_CONTACT_RESERVED,
|
|
1742
|
-
interaction: {
|
|
1752
|
+
interaction: {mediaType: 'chat'},
|
|
1743
1753
|
},
|
|
1744
1754
|
};
|
|
1745
1755
|
|
|
@@ -1771,7 +1781,7 @@ describe('TaskManager', () => {
|
|
|
1771
1781
|
data: {
|
|
1772
1782
|
...chatReservedPayload.data,
|
|
1773
1783
|
type: CC_EVENTS.CONTACT_ENDED,
|
|
1774
|
-
interaction: {
|
|
1784
|
+
interaction: {mediaType: 'chat', state: 'new'}, // Change to 'new' state
|
|
1775
1785
|
},
|
|
1776
1786
|
};
|
|
1777
1787
|
|
|
@@ -1788,7 +1798,7 @@ describe('TaskManager', () => {
|
|
|
1788
1798
|
data: {
|
|
1789
1799
|
...initalPayload.data,
|
|
1790
1800
|
interactionId: 'telephony-task-id',
|
|
1791
|
-
interaction: {
|
|
1801
|
+
interaction: {mediaType: 'telephony'},
|
|
1792
1802
|
},
|
|
1793
1803
|
};
|
|
1794
1804
|
|
|
@@ -1796,7 +1806,7 @@ describe('TaskManager', () => {
|
|
|
1796
1806
|
data: {
|
|
1797
1807
|
...initalPayload.data,
|
|
1798
1808
|
interactionId: 'chat-task-id',
|
|
1799
|
-
interaction: {
|
|
1809
|
+
interaction: {mediaType: 'chat'},
|
|
1800
1810
|
},
|
|
1801
1811
|
};
|
|
1802
1812
|
|
|
@@ -1804,7 +1814,7 @@ describe('TaskManager', () => {
|
|
|
1804
1814
|
data: {
|
|
1805
1815
|
...initalPayload.data,
|
|
1806
1816
|
interactionId: 'email-task-id',
|
|
1807
|
-
interaction: {
|
|
1817
|
+
interaction: {mediaType: 'email'},
|
|
1808
1818
|
},
|
|
1809
1819
|
};
|
|
1810
1820
|
|
|
@@ -1819,9 +1829,15 @@ describe('TaskManager', () => {
|
|
|
1819
1829
|
expect(taskManager.getAllTasks()).toHaveProperty(emailPayload.data.interactionId);
|
|
1820
1830
|
|
|
1821
1831
|
// Verify the task media types are correctly set
|
|
1822
|
-
expect(
|
|
1823
|
-
|
|
1824
|
-
|
|
1832
|
+
expect(
|
|
1833
|
+
taskManager.getTask(telephonyPayload.data.interactionId).data.interaction.mediaType
|
|
1834
|
+
).toBe('telephony');
|
|
1835
|
+
expect(taskManager.getTask(chatPayload.data.interactionId).data.interaction.mediaType).toBe(
|
|
1836
|
+
'chat'
|
|
1837
|
+
);
|
|
1838
|
+
expect(taskManager.getTask(emailPayload.data.interactionId).data.interaction.mediaType).toBe(
|
|
1839
|
+
'email'
|
|
1840
|
+
);
|
|
1825
1841
|
});
|
|
1826
1842
|
|
|
1827
1843
|
it('should properly handle one task ending when multiple tasks are active', () => {
|
|
@@ -1830,7 +1846,7 @@ describe('TaskManager', () => {
|
|
|
1830
1846
|
data: {
|
|
1831
1847
|
...initalPayload.data,
|
|
1832
1848
|
interactionId: 'task-id-1',
|
|
1833
|
-
interaction: {
|
|
1849
|
+
interaction: {mediaType: 'telephony'},
|
|
1834
1850
|
},
|
|
1835
1851
|
};
|
|
1836
1852
|
|
|
@@ -1838,7 +1854,7 @@ describe('TaskManager', () => {
|
|
|
1838
1854
|
data: {
|
|
1839
1855
|
...initalPayload.data,
|
|
1840
1856
|
interactionId: 'task-id-2',
|
|
1841
|
-
interaction: {
|
|
1857
|
+
interaction: {mediaType: 'chat'},
|
|
1842
1858
|
},
|
|
1843
1859
|
};
|
|
1844
1860
|
|
|
@@ -1846,7 +1862,7 @@ describe('TaskManager', () => {
|
|
|
1846
1862
|
data: {
|
|
1847
1863
|
...initalPayload.data,
|
|
1848
1864
|
interactionId: 'task-id-3',
|
|
1849
|
-
interaction: {
|
|
1865
|
+
interaction: {mediaType: 'email'},
|
|
1850
1866
|
},
|
|
1851
1867
|
};
|
|
1852
1868
|
|
|
@@ -1873,7 +1889,7 @@ describe('TaskManager', () => {
|
|
|
1873
1889
|
data: {
|
|
1874
1890
|
...task2Payload.data,
|
|
1875
1891
|
type: CC_EVENTS.CONTACT_ENDED,
|
|
1876
|
-
interaction: {
|
|
1892
|
+
interaction: {mediaType: 'chat', state: 'new'}, // Using 'new' to trigger cleanup
|
|
1877
1893
|
},
|
|
1878
1894
|
};
|
|
1879
1895
|
|
|
@@ -1899,7 +1915,7 @@ describe('TaskManager', () => {
|
|
|
1899
1915
|
data: {
|
|
1900
1916
|
...task3Payload.data,
|
|
1901
1917
|
type: CC_EVENTS.CONTACT_ENDED,
|
|
1902
|
-
interaction: {
|
|
1918
|
+
interaction: {mediaType: 'email', state: 'connected'}, // Using 'connected' to NOT trigger cleanup
|
|
1903
1919
|
},
|
|
1904
1920
|
};
|
|
1905
1921
|
|
|
@@ -1957,16 +1973,16 @@ describe('TaskManager', () => {
|
|
|
1957
1973
|
|
|
1958
1974
|
it('should update task data on AGENT_WRAPUP event', () => {
|
|
1959
1975
|
const payload = {
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1976
|
+
data: {
|
|
1977
|
+
type: CC_EVENTS.AGENT_WRAPUP,
|
|
1978
|
+
interactionId: taskId,
|
|
1979
|
+
wrapUpRequired: true,
|
|
1980
|
+
},
|
|
1965
1981
|
};
|
|
1966
1982
|
const task = taskManager.getTask(taskId);
|
|
1967
1983
|
const updateSpy = jest.spyOn(task, 'updateTaskData').mockImplementation((data) => {
|
|
1968
|
-
|
|
1969
|
-
|
|
1984
|
+
task.data = {...(task.data || {}), ...(data || {})};
|
|
1985
|
+
return task;
|
|
1970
1986
|
});
|
|
1971
1987
|
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
1972
1988
|
expect(updateSpy).toHaveBeenCalledWith(payload.data);
|
|
@@ -1981,7 +1997,7 @@ describe('TaskManager', () => {
|
|
|
1981
1997
|
data: {
|
|
1982
1998
|
type: CC_EVENTS.AGENT_CONTACT_UNASSIGNED,
|
|
1983
1999
|
agentId: initalPayload.data.agentId,
|
|
1984
|
-
interaction: {
|
|
2000
|
+
interaction: {mediaType: 'telephony'},
|
|
1985
2001
|
interactionId: initalPayload.data.interactionId,
|
|
1986
2002
|
orgId: initalPayload.data.orgId,
|
|
1987
2003
|
trackingId: initalPayload.data.trackingId,
|
|
@@ -2000,7 +2016,7 @@ describe('TaskManager', () => {
|
|
|
2000
2016
|
data: {
|
|
2001
2017
|
type: CC_EVENTS.AGENT_WRAPUP,
|
|
2002
2018
|
interactionId: taskId,
|
|
2003
|
-
interaction: {
|
|
2019
|
+
interaction: {mediaType: 'telephony'},
|
|
2004
2020
|
},
|
|
2005
2021
|
};
|
|
2006
2022
|
webSocketManagerMock.emit('message', JSON.stringify(wrapupPayload));
|
|
@@ -2017,7 +2033,7 @@ describe('TaskManager', () => {
|
|
|
2017
2033
|
data: {
|
|
2018
2034
|
type: CC_EVENTS.AGENT_VTEAM_TRANSFERRED,
|
|
2019
2035
|
agentId: initalPayload.data.agentId,
|
|
2020
|
-
interaction: {
|
|
2036
|
+
interaction: {mediaType: 'telephony'},
|
|
2021
2037
|
interactionId: initalPayload.data.interactionId,
|
|
2022
2038
|
orgId: initalPayload.data.orgId,
|
|
2023
2039
|
trackingId: initalPayload.data.trackingId,
|
|
@@ -2036,7 +2052,7 @@ describe('TaskManager', () => {
|
|
|
2036
2052
|
data: {
|
|
2037
2053
|
type: CC_EVENTS.AGENT_WRAPUP,
|
|
2038
2054
|
interactionId: taskId,
|
|
2039
|
-
interaction: {
|
|
2055
|
+
interaction: {mediaType: 'telephony'},
|
|
2040
2056
|
},
|
|
2041
2057
|
};
|
|
2042
2058
|
webSocketManagerMock.emit('message', JSON.stringify(wrapupPayload));
|
|
@@ -2069,11 +2085,11 @@ describe('TaskManager', () => {
|
|
|
2069
2085
|
taskManager.setAgentId(agentId);
|
|
2070
2086
|
|
|
2071
2087
|
task = {
|
|
2072
|
-
data: {
|
|
2088
|
+
data: {interactionId: taskId},
|
|
2073
2089
|
emit: jest.fn(),
|
|
2074
2090
|
updateTaskData: jest.fn().mockImplementation((updatedData) => {
|
|
2075
2091
|
// Mock the updateTaskData method to actually update task.data
|
|
2076
|
-
task.data = {
|
|
2092
|
+
task.data = {...task.data, ...updatedData};
|
|
2077
2093
|
return task;
|
|
2078
2094
|
}),
|
|
2079
2095
|
};
|
|
@@ -2135,8 +2151,8 @@ describe('TaskManager', () => {
|
|
|
2135
2151
|
participantType: 'agent',
|
|
2136
2152
|
interaction: {
|
|
2137
2153
|
participants: {
|
|
2138
|
-
[agentId]: {
|
|
2139
|
-
'new-participant-123': {
|
|
2154
|
+
[agentId]: {pType: 'Agent', hasLeft: false},
|
|
2155
|
+
'new-participant-123': {pType: 'Agent', hasLeft: false},
|
|
2140
2156
|
},
|
|
2141
2157
|
media: {
|
|
2142
2158
|
[taskId]: {
|
|
@@ -2163,10 +2179,10 @@ describe('TaskManager', () => {
|
|
|
2163
2179
|
participantId: 'new-agent-789',
|
|
2164
2180
|
interaction: {
|
|
2165
2181
|
participants: {
|
|
2166
|
-
[agentId]: {
|
|
2167
|
-
'agent-2': {
|
|
2168
|
-
'new-agent-789': {
|
|
2169
|
-
'customer-1': {
|
|
2182
|
+
[agentId]: {pType: 'Agent', hasLeft: false},
|
|
2183
|
+
'agent-2': {pType: 'Agent', hasLeft: false},
|
|
2184
|
+
'new-agent-789': {pType: 'Agent', hasLeft: false},
|
|
2185
|
+
'customer-1': {pType: 'Customer', hasLeft: false},
|
|
2170
2186
|
},
|
|
2171
2187
|
media: {
|
|
2172
2188
|
[taskId]: {
|
|
@@ -2204,9 +2220,9 @@ describe('TaskManager', () => {
|
|
|
2204
2220
|
interactionId: taskId,
|
|
2205
2221
|
interaction: {
|
|
2206
2222
|
participants: {
|
|
2207
|
-
[agentId]: {
|
|
2208
|
-
'agent-2': {
|
|
2209
|
-
'customer-1': {
|
|
2223
|
+
[agentId]: {pType: 'Agent', hasLeft: false},
|
|
2224
|
+
'agent-2': {pType: 'Agent', hasLeft: true}, // This agent left
|
|
2225
|
+
'customer-1': {pType: 'Customer', hasLeft: false},
|
|
2210
2226
|
},
|
|
2211
2227
|
media: {
|
|
2212
2228
|
[taskId]: {
|
|
@@ -2508,7 +2524,7 @@ describe('TaskManager', () => {
|
|
|
2508
2524
|
it('should only update task for matching interactionId', () => {
|
|
2509
2525
|
const otherTaskId = 'other-task-id';
|
|
2510
2526
|
const otherTask = {
|
|
2511
|
-
data: {
|
|
2527
|
+
data: {interactionId: otherTaskId},
|
|
2512
2528
|
emit: jest.fn(),
|
|
2513
2529
|
};
|
|
2514
2530
|
taskManager.taskCollection[otherTaskId] = otherTask;
|
|
@@ -3056,5 +3072,165 @@ describe('TaskManager', () => {
|
|
|
3056
3072
|
});
|
|
3057
3073
|
});
|
|
3058
3074
|
|
|
3059
|
-
|
|
3075
|
+
describe('Campaign Preview Reservation', () => {
|
|
3076
|
+
it('should create a task and emit TASK_CAMPAIGN_PREVIEW_RESERVATION when AgentOfferCampaignReservation is received', () => {
|
|
3077
|
+
const campaignPayload = {
|
|
3078
|
+
data: {
|
|
3079
|
+
type: CC_EVENTS.AGENT_OFFER_CAMPAIGN_RESERVATION,
|
|
3080
|
+
interactionId: 'campaign-interaction-123',
|
|
3081
|
+
agentId: taskDataMock.agentId,
|
|
3082
|
+
orgId: taskDataMock.orgId,
|
|
3083
|
+
trackingId: 'campaign-tracking-456',
|
|
3084
|
+
interaction: {
|
|
3085
|
+
mediaType: 'telephony',
|
|
3086
|
+
callProcessingDetails: {
|
|
3087
|
+
campaignId: 'campaign-789',
|
|
3088
|
+
},
|
|
3089
|
+
},
|
|
3090
|
+
},
|
|
3091
|
+
};
|
|
3092
|
+
|
|
3093
|
+
const managerEmitSpy = jest.spyOn(taskManager, 'emit');
|
|
3094
|
+
|
|
3095
|
+
webSocketManagerMock.emit('message', JSON.stringify(campaignPayload));
|
|
3096
|
+
|
|
3097
|
+
// Should emit with a task object (not raw data)
|
|
3098
|
+
expect(managerEmitSpy).toHaveBeenCalledWith(
|
|
3099
|
+
TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_RESERVATION,
|
|
3100
|
+
expect.objectContaining({
|
|
3101
|
+
data: expect.objectContaining({
|
|
3102
|
+
interactionId: 'campaign-interaction-123',
|
|
3103
|
+
type: CC_EVENTS.AGENT_OFFER_CAMPAIGN_RESERVATION,
|
|
3104
|
+
wrapUpRequired: false,
|
|
3105
|
+
isAutoAnswering: false,
|
|
3106
|
+
}),
|
|
3107
|
+
})
|
|
3108
|
+
);
|
|
3109
|
+
|
|
3110
|
+
// Task should be in the collection so subsequent events (e.g. AGENT_CONTACT_ASSIGNED) can find it
|
|
3111
|
+
expect(taskManager['taskCollection']['campaign-interaction-123']).toBeDefined();
|
|
3112
|
+
});
|
|
3113
|
+
|
|
3114
|
+
it('should not emit TASK_INCOMING for campaign preview reservation when incoming WebRTC call arrives', () => {
|
|
3115
|
+
const campaignPayload = {
|
|
3116
|
+
data: {
|
|
3117
|
+
type: CC_EVENTS.AGENT_OFFER_CAMPAIGN_RESERVATION,
|
|
3118
|
+
interactionId: 'campaign-interaction-123',
|
|
3119
|
+
agentId: taskDataMock.agentId,
|
|
3120
|
+
orgId: taskDataMock.orgId,
|
|
3121
|
+
trackingId: 'campaign-tracking-456',
|
|
3122
|
+
interaction: {
|
|
3123
|
+
mediaType: 'telephony',
|
|
3124
|
+
callProcessingDetails: {
|
|
3125
|
+
campaignId: 'campaign-789',
|
|
3126
|
+
},
|
|
3127
|
+
},
|
|
3128
|
+
},
|
|
3129
|
+
};
|
|
3130
|
+
|
|
3131
|
+
// Remove the default task so only the campaign preview task is in the collection
|
|
3132
|
+
delete taskManager['taskCollection'][taskId];
|
|
3133
|
+
|
|
3134
|
+
// Create campaign preview task via the reservation event
|
|
3135
|
+
webSocketManagerMock.emit('message', JSON.stringify(campaignPayload));
|
|
3136
|
+
|
|
3137
|
+
const managerEmitSpy = jest.spyOn(taskManager, 'emit');
|
|
3138
|
+
|
|
3139
|
+
// Simulate an incoming WebRTC call
|
|
3140
|
+
const incomingCallCb = onSpy.mock.calls[0][1];
|
|
3141
|
+
incomingCallCb(mockCall);
|
|
3142
|
+
|
|
3143
|
+
// TASK_INCOMING should NOT be emitted because the only telephony task is a campaign preview
|
|
3144
|
+
expect(managerEmitSpy).not.toHaveBeenCalledWith(TASK_EVENTS.TASK_INCOMING, expect.anything());
|
|
3145
|
+
});
|
|
3146
|
+
|
|
3147
|
+
it('should update existing task when AgentOfferCampaignReservation is received for known interactionId', () => {
|
|
3148
|
+
const campaignPayload = {
|
|
3149
|
+
data: {
|
|
3150
|
+
type: CC_EVENTS.AGENT_OFFER_CAMPAIGN_RESERVATION,
|
|
3151
|
+
interactionId: 'campaign-interaction-123',
|
|
3152
|
+
agentId: taskDataMock.agentId,
|
|
3153
|
+
orgId: taskDataMock.orgId,
|
|
3154
|
+
trackingId: 'campaign-tracking-456',
|
|
3155
|
+
interaction: {
|
|
3156
|
+
mediaType: 'telephony',
|
|
3157
|
+
callProcessingDetails: {
|
|
3158
|
+
campaignId: 'campaign-789',
|
|
3159
|
+
},
|
|
3160
|
+
},
|
|
3161
|
+
},
|
|
3162
|
+
};
|
|
3163
|
+
|
|
3164
|
+
// Send the first reservation to create the task
|
|
3165
|
+
webSocketManagerMock.emit('message', JSON.stringify(campaignPayload));
|
|
3060
3166
|
|
|
3167
|
+
const managerEmitSpy = jest.spyOn(taskManager, 'emit');
|
|
3168
|
+
|
|
3169
|
+
// Send a second reservation for the same interactionId
|
|
3170
|
+
webSocketManagerMock.emit('message', JSON.stringify(campaignPayload));
|
|
3171
|
+
|
|
3172
|
+
expect(managerEmitSpy).toHaveBeenCalledWith(
|
|
3173
|
+
TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_RESERVATION,
|
|
3174
|
+
expect.objectContaining({
|
|
3175
|
+
data: expect.objectContaining({
|
|
3176
|
+
interactionId: 'campaign-interaction-123',
|
|
3177
|
+
}),
|
|
3178
|
+
})
|
|
3179
|
+
);
|
|
3180
|
+
});
|
|
3181
|
+
|
|
3182
|
+
it('should update task data but NOT remove task when CampaignContactUpdated is received', () => {
|
|
3183
|
+
const campaignInteractionId = 'campaign-interaction-123';
|
|
3184
|
+
|
|
3185
|
+
// First create a campaign preview task
|
|
3186
|
+
const reservationPayload = {
|
|
3187
|
+
data: {
|
|
3188
|
+
type: CC_EVENTS.AGENT_OFFER_CAMPAIGN_RESERVATION,
|
|
3189
|
+
interactionId: campaignInteractionId,
|
|
3190
|
+
agentId: taskDataMock.agentId,
|
|
3191
|
+
orgId: taskDataMock.orgId,
|
|
3192
|
+
trackingId: 'campaign-tracking-456',
|
|
3193
|
+
interaction: {
|
|
3194
|
+
mediaType: 'telephony',
|
|
3195
|
+
callProcessingDetails: {
|
|
3196
|
+
campaignId: 'campaign-789',
|
|
3197
|
+
},
|
|
3198
|
+
},
|
|
3199
|
+
},
|
|
3200
|
+
};
|
|
3201
|
+
|
|
3202
|
+
webSocketManagerMock.emit('message', JSON.stringify(reservationPayload));
|
|
3203
|
+
|
|
3204
|
+
// Verify task exists in collection
|
|
3205
|
+
const task = taskManager['taskCollection'][campaignInteractionId];
|
|
3206
|
+
expect(task).toBeDefined();
|
|
3207
|
+
|
|
3208
|
+
const taskEmitSpy = jest.spyOn(task, 'emit');
|
|
3209
|
+
|
|
3210
|
+
// Now send CampaignContactUpdated
|
|
3211
|
+
const campaignContactUpdatedPayload = {
|
|
3212
|
+
data: {
|
|
3213
|
+
type: CC_EVENTS.CAMPAIGN_CONTACT_UPDATED,
|
|
3214
|
+
interactionId: campaignInteractionId,
|
|
3215
|
+
agentId: taskDataMock.agentId,
|
|
3216
|
+
orgId: taskDataMock.orgId,
|
|
3217
|
+
interaction: {
|
|
3218
|
+
mediaType: 'telephony',
|
|
3219
|
+
state: 'new',
|
|
3220
|
+
callProcessingDetails: {
|
|
3221
|
+
campaignId: 'campaign-789',
|
|
3222
|
+
},
|
|
3223
|
+
},
|
|
3224
|
+
},
|
|
3225
|
+
};
|
|
3226
|
+
|
|
3227
|
+
webSocketManagerMock.emit('message', JSON.stringify(campaignContactUpdatedPayload));
|
|
3228
|
+
|
|
3229
|
+
// Task should still exist in collection (not removed — non-terminal event)
|
|
3230
|
+
expect(taskManager['taskCollection'][campaignInteractionId]).toBeDefined();
|
|
3231
|
+
|
|
3232
|
+
// TASK_END should NOT have been emitted
|
|
3233
|
+
expect(taskEmitSpy).not.toHaveBeenCalledWith(TASK_EVENTS.TASK_END, expect.anything());
|
|
3234
|
+
});
|
|
3235
|
+
});
|
|
3236
|
+
});
|