@webex/contact-center 3.12.0-task-refactor.10 → 3.12.0-task-refactor.12
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 +345 -82
- package/dist/cc.js.map +1 -1
- package/dist/config.js +6 -0
- package/dist/config.js.map +1 -1
- package/dist/constants.js +3 -0
- package/dist/constants.js.map +1 -1
- package/dist/metrics/behavioral-events.js +39 -0
- package/dist/metrics/behavioral-events.js.map +1 -1
- package/dist/metrics/constants.js +8 -1
- package/dist/metrics/constants.js.map +1 -1
- package/dist/services/ApiAiAssistant.js +3 -0
- package/dist/services/ApiAiAssistant.js.map +1 -1
- package/dist/services/config/Util.js +1 -1
- package/dist/services/config/Util.js.map +1 -1
- package/dist/services/config/types.js +10 -0
- package/dist/services/config/types.js.map +1 -1
- package/dist/services/constants.js +1 -0
- package/dist/services/constants.js.map +1 -1
- package/dist/services/core/Err.js.map +1 -1
- package/dist/services/core/Utils.js +57 -6
- package/dist/services/core/Utils.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/core/websocket/WebSocketManager.js +24 -3
- package/dist/services/core/websocket/WebSocketManager.js.map +1 -1
- package/dist/services/index.js +1 -0
- package/dist/services/index.js.map +1 -1
- package/dist/services/task/Task.js +16 -4
- package/dist/services/task/Task.js.map +1 -1
- package/dist/services/task/TaskManager.js +158 -6
- package/dist/services/task/TaskManager.js.map +1 -1
- package/dist/services/task/TaskUtils.js +39 -11
- package/dist/services/task/TaskUtils.js.map +1 -1
- package/dist/services/task/constants.js +7 -1
- package/dist/services/task/constants.js.map +1 -1
- package/dist/services/task/dialer.js +129 -0
- package/dist/services/task/dialer.js.map +1 -1
- package/dist/services/task/state-machine/TaskStateMachine.js +30 -3
- package/dist/services/task/state-machine/TaskStateMachine.js.map +1 -1
- package/dist/services/task/state-machine/actions.js +4 -0
- package/dist/services/task/state-machine/actions.js.map +1 -1
- package/dist/services/task/state-machine/constants.js +3 -0
- package/dist/services/task/state-machine/constants.js.map +1 -1
- package/dist/services/task/state-machine/guards.js +23 -0
- package/dist/services/task/state-machine/guards.js.map +1 -1
- package/dist/services/task/state-machine/types.js.map +1 -1
- package/dist/services/task/state-machine/uiControlsComputer.js +1 -1
- package/dist/services/task/state-machine/uiControlsComputer.js.map +1 -1
- package/dist/services/task/types.js +69 -0
- package/dist/services/task/types.js.map +1 -1
- package/dist/types/cc.d.ts +93 -2
- package/dist/types/config.d.ts +6 -0
- package/dist/types/constants.d.ts +3 -0
- package/dist/types/metrics/constants.d.ts +6 -0
- package/dist/types/services/config/types.d.ts +22 -1
- package/dist/types/services/core/Err.d.ts +6 -0
- package/dist/types/services/core/Utils.d.ts +20 -2
- package/dist/types/services/core/aqm-reqs.d.ts +49 -0
- package/dist/types/services/core/websocket/WebSocketManager.d.ts +1 -0
- package/dist/types/services/core/websocket/connection-service.d.ts +1 -0
- package/dist/types/services/task/Task.d.ts +1 -0
- package/dist/types/services/task/TaskUtils.d.ts +13 -0
- package/dist/types/services/task/constants.d.ts +6 -0
- package/dist/types/services/task/dialer.d.ts +45 -0
- package/dist/types/services/task/state-machine/TaskStateMachine.d.ts +36 -8
- package/dist/types/services/task/state-machine/constants.d.ts +3 -0
- package/dist/types/services/task/state-machine/guards.d.ts +2 -0
- package/dist/types/services/task/state-machine/types.d.ts +11 -0
- package/dist/types/services/task/types.d.ts +85 -1
- package/dist/types/types.d.ts +2 -0
- package/dist/types.js.map +1 -1
- package/dist/webex.js +1 -1
- package/package.json +2 -2
- package/src/cc.ts +452 -102
- package/src/config.ts +6 -0
- package/src/constants.ts +3 -0
- package/src/metrics/behavioral-events.ts +42 -0
- package/src/metrics/constants.ts +9 -1
- package/src/services/ApiAiAssistant.ts +3 -0
- package/src/services/config/Util.ts +1 -1
- package/src/services/config/types.ts +12 -1
- package/src/services/constants.ts +1 -0
- package/src/services/core/Err.ts +3 -0
- package/src/services/core/Utils.ts +68 -5
- package/src/services/core/aqm-reqs.ts +100 -22
- package/src/services/core/websocket/WebSocketManager.ts +23 -2
- package/src/services/index.ts +1 -0
- package/src/services/task/Task.ts +22 -4
- package/src/services/task/TaskManager.ts +175 -7
- package/src/services/task/TaskUtils.ts +33 -1
- package/src/services/task/constants.ts +6 -0
- package/src/services/task/dialer.ts +136 -1
- package/src/services/task/state-machine/TaskStateMachine.ts +37 -5
- package/src/services/task/state-machine/actions.ts +4 -0
- package/src/services/task/state-machine/constants.ts +3 -0
- package/src/services/task/state-machine/guards.ts +30 -0
- package/src/services/task/state-machine/types.ts +14 -1
- package/src/services/task/state-machine/uiControlsComputer.ts +1 -1
- package/src/services/task/types.ts +91 -0
- package/src/types.ts +6 -1
- package/test/unit/spec/cc.ts +432 -26
- package/test/unit/spec/services/config/index.ts +30 -2
- package/test/unit/spec/services/core/Utils.ts +367 -1
- package/test/unit/spec/services/core/websocket/WebSocketManager.ts +90 -1
- package/test/unit/spec/services/core/websocket/connection-service.ts +4 -5
- package/test/unit/spec/services/task/TaskManager.ts +2334 -145
- package/test/unit/spec/services/task/dialer.ts +372 -96
- package/test/unit/spec/services/task/state-machine/uiControlsComputer.ts +47 -0
- package/umd/contact-center.min.js +2 -2
- package/umd/contact-center.min.js.map +1 -1
|
@@ -92,6 +92,10 @@ describe('TaskManager', () => {
|
|
|
92
92
|
}
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
+
if ([TaskEvent.ASSIGN, TaskEvent.CONSULTING_ACTIVE].includes(event.type as TaskEvent)) {
|
|
96
|
+
task.emit(TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE, task);
|
|
97
|
+
}
|
|
98
|
+
|
|
95
99
|
// Auto-answer is now handled at the Task layer (triggered by state machine actions)
|
|
96
100
|
if (
|
|
97
101
|
[TaskEvent.TASK_OFFERED, TaskEvent.OFFER_CONSULT].includes(event.type as TaskEvent) &&
|
|
@@ -160,7 +164,19 @@ describe('TaskManager', () => {
|
|
|
160
164
|
beforeEach(() => {
|
|
161
165
|
contactMock = contact;
|
|
162
166
|
webSocketManagerMock = new EventEmitter();
|
|
167
|
+
<<<<<<< HEAD
|
|
163
168
|
rtdWebSocketManagerMock = new EventEmitter();
|
|
169
|
+
=======
|
|
170
|
+
mockApiAIAssistant = {
|
|
171
|
+
sendEvent: jest.fn().mockResolvedValue({}),
|
|
172
|
+
setAIFeatureFlags: jest.fn(),
|
|
173
|
+
aiFeature: {
|
|
174
|
+
realtimeTranscripts: {
|
|
175
|
+
enable: true,
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
};
|
|
179
|
+
>>>>>>> 81672300901738f14289fd2a7414c6c4b1389aa6
|
|
164
180
|
|
|
165
181
|
webex = {
|
|
166
182
|
logger: {
|
|
@@ -190,6 +206,7 @@ describe('TaskManager', () => {
|
|
|
190
206
|
onSpy = jest.spyOn(webCallingService, 'on');
|
|
191
207
|
offSpy = jest.spyOn(webCallingService, 'off');
|
|
192
208
|
|
|
209
|
+
<<<<<<< HEAD
|
|
193
210
|
mockApiAIAssistant = {
|
|
194
211
|
sendEvent: jest.fn().mockResolvedValue({}),
|
|
195
212
|
};
|
|
@@ -203,6 +220,26 @@ describe('TaskManager', () => {
|
|
|
203
220
|
);
|
|
204
221
|
taskManager.taskCollection[taskId] = createMockTask(taskDataMock);
|
|
205
222
|
(taskManager as any).setupTaskListeners?.(taskManager.taskCollection[taskId]);
|
|
223
|
+
=======
|
|
224
|
+
taskManager = new TaskManager(
|
|
225
|
+
mockApiAIAssistant,
|
|
226
|
+
contactMock,
|
|
227
|
+
webCallingService,
|
|
228
|
+
webSocketManagerMock
|
|
229
|
+
);
|
|
230
|
+
const taskMock = {
|
|
231
|
+
emit: jest.fn(),
|
|
232
|
+
accept: jest.fn(),
|
|
233
|
+
decline: jest.fn(),
|
|
234
|
+
updateTaskData: jest.fn().mockImplementation((updatedData) => {
|
|
235
|
+
taskMock.data = {...taskMock.data, ...updatedData};
|
|
236
|
+
return taskMock;
|
|
237
|
+
}),
|
|
238
|
+
data: taskDataMock,
|
|
239
|
+
};
|
|
240
|
+
taskManager.taskCollection[taskId] = taskMock;
|
|
241
|
+
taskManager.agentId = 'test-agent-id';
|
|
242
|
+
>>>>>>> 81672300901738f14289fd2a7414c6c4b1389aa6
|
|
206
243
|
taskManager.call = mockCall;
|
|
207
244
|
taskManager.setAgentId('test-agent-id');
|
|
208
245
|
|
|
@@ -229,8 +266,15 @@ describe('TaskManager', () => {
|
|
|
229
266
|
|
|
230
267
|
incomingCallCb(mockCall);
|
|
231
268
|
|
|
269
|
+
<<<<<<< HEAD
|
|
232
270
|
expect(incomingHandler).toHaveBeenCalledWith(taskManager.getTask(taskId));
|
|
233
271
|
taskManager.off(TASK_EVENTS.TASK_INCOMING, incomingHandler);
|
|
272
|
+
=======
|
|
273
|
+
expect(taskEmitSpy).toHaveBeenCalledWith(
|
|
274
|
+
TASK_EVENTS.TASK_INCOMING,
|
|
275
|
+
taskManager.getTask(taskId)
|
|
276
|
+
);
|
|
277
|
+
>>>>>>> 81672300901738f14289fd2a7414c6c4b1389aa6
|
|
234
278
|
});
|
|
235
279
|
|
|
236
280
|
it('should re-emit task related events', () => {
|
|
@@ -428,6 +472,106 @@ describe('TaskManager', () => {
|
|
|
428
472
|
expect(existingTaskEmitSpy).not.toHaveBeenCalled();
|
|
429
473
|
});
|
|
430
474
|
|
|
475
|
+
it('should invoke sendEvent for configured start/stop backend events', () => {
|
|
476
|
+
const message = (type: CC_EVENTS) =>
|
|
477
|
+
JSON.stringify({
|
|
478
|
+
data: {
|
|
479
|
+
...taskDataMock,
|
|
480
|
+
taskId,
|
|
481
|
+
type,
|
|
482
|
+
},
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
webSocketManagerMock.emit('message', message(CC_EVENTS.AGENT_CONTACT_ASSIGNED));
|
|
486
|
+
webSocketManagerMock.emit('message', message(CC_EVENTS.AGENT_CONSULTING));
|
|
487
|
+
webSocketManagerMock.emit('message', message(CC_EVENTS.AGENT_CONSULT_CONFERENCED));
|
|
488
|
+
webSocketManagerMock.emit('message', message(CC_EVENTS.AGENT_CONSULT_ENDED));
|
|
489
|
+
webSocketManagerMock.emit('message', message(CC_EVENTS.AGENT_WRAPUP));
|
|
490
|
+
webSocketManagerMock.emit('message', message(CC_EVENTS.PARTICIPANT_LEFT_CONFERENCE));
|
|
491
|
+
|
|
492
|
+
expect(mockApiAIAssistant.sendEvent).toHaveBeenCalledTimes(6);
|
|
493
|
+
expect(mockApiAIAssistant.sendEvent).toHaveBeenCalledWith(
|
|
494
|
+
'test-agent-id',
|
|
495
|
+
taskId,
|
|
496
|
+
'CUSTOM_EVENT',
|
|
497
|
+
'GET_TRANSCRIPTS',
|
|
498
|
+
'START'
|
|
499
|
+
);
|
|
500
|
+
expect(mockApiAIAssistant.sendEvent).toHaveBeenCalledWith(
|
|
501
|
+
'test-agent-id',
|
|
502
|
+
taskId,
|
|
503
|
+
'CUSTOM_EVENT',
|
|
504
|
+
'GET_TRANSCRIPTS',
|
|
505
|
+
'STOP'
|
|
506
|
+
);
|
|
507
|
+
});
|
|
508
|
+
|
|
509
|
+
it('should not invoke sendEvent for transcript events when realtime transcript feature is disabled', () => {
|
|
510
|
+
mockApiAIAssistant.aiFeature = {
|
|
511
|
+
realtimeTranscripts: {
|
|
512
|
+
enable: false,
|
|
513
|
+
},
|
|
514
|
+
};
|
|
515
|
+
mockApiAIAssistant.setAIFeatureFlags(mockApiAIAssistant.aiFeature);
|
|
516
|
+
|
|
517
|
+
const message = (type: CC_EVENTS) =>
|
|
518
|
+
JSON.stringify({
|
|
519
|
+
data: {
|
|
520
|
+
...taskDataMock,
|
|
521
|
+
taskId,
|
|
522
|
+
type,
|
|
523
|
+
},
|
|
524
|
+
});
|
|
525
|
+
|
|
526
|
+
webSocketManagerMock.emit('message', message(CC_EVENTS.AGENT_CONTACT_ASSIGNED));
|
|
527
|
+
webSocketManagerMock.emit('message', message(CC_EVENTS.AGENT_CONSULTING));
|
|
528
|
+
webSocketManagerMock.emit('message', message(CC_EVENTS.AGENT_CONSULT_CONFERENCED));
|
|
529
|
+
webSocketManagerMock.emit('message', message(CC_EVENTS.AGENT_CONSULT_ENDED));
|
|
530
|
+
webSocketManagerMock.emit('message', message(CC_EVENTS.AGENT_WRAPUP));
|
|
531
|
+
webSocketManagerMock.emit('message', message(CC_EVENTS.PARTICIPANT_LEFT_CONFERENCE));
|
|
532
|
+
|
|
533
|
+
expect(mockApiAIAssistant.sendEvent).not.toHaveBeenCalled();
|
|
534
|
+
});
|
|
535
|
+
|
|
536
|
+
it('should emit REAL_TIME_TRANSCRIPTION from task object', () => {
|
|
537
|
+
const task = taskManager.getTask(taskId);
|
|
538
|
+
const taskEmitSpy = jest.spyOn(task, 'emit');
|
|
539
|
+
const realtimePayload = {
|
|
540
|
+
data: {
|
|
541
|
+
...taskDataMock,
|
|
542
|
+
type: CC_EVENTS.REAL_TIME_TRANSCRIPTION,
|
|
543
|
+
data: {
|
|
544
|
+
content: 'hello from transcript',
|
|
545
|
+
},
|
|
546
|
+
},
|
|
547
|
+
};
|
|
548
|
+
|
|
549
|
+
webSocketManagerMock.emit('message', JSON.stringify(realtimePayload));
|
|
550
|
+
|
|
551
|
+
expect(taskEmitSpy).toHaveBeenCalledWith(
|
|
552
|
+
CC_EVENTS.REAL_TIME_TRANSCRIPTION,
|
|
553
|
+
realtimePayload.data
|
|
554
|
+
);
|
|
555
|
+
});
|
|
556
|
+
|
|
557
|
+
it('should emit REAL_TIME_TRANSCRIPTION from RTD websocket payload on task object', () => {
|
|
558
|
+
const task = taskManager.getTask(taskId);
|
|
559
|
+
const taskEmitSpy = jest.spyOn(task, 'emit');
|
|
560
|
+
const realtimePayload = {
|
|
561
|
+
data: {
|
|
562
|
+
notifType: CC_EVENTS.REAL_TIME_TRANSCRIPTION,
|
|
563
|
+
data: {
|
|
564
|
+
conversationId: taskId,
|
|
565
|
+
content: 'hello from rtd websocket',
|
|
566
|
+
},
|
|
567
|
+
},
|
|
568
|
+
};
|
|
569
|
+
|
|
570
|
+
taskManager.handleRealtimeWebsocketEvent(JSON.stringify(realtimePayload));
|
|
571
|
+
|
|
572
|
+
expect(taskEmitSpy).toHaveBeenCalledWith(CC_EVENTS.REAL_TIME_TRANSCRIPTION, realtimePayload.data);
|
|
573
|
+
});
|
|
574
|
+
|
|
431
575
|
it('should not re-emit agent related events', () => {
|
|
432
576
|
const dummyPayload = {
|
|
433
577
|
data: {
|
|
@@ -494,6 +638,7 @@ describe('TaskManager', () => {
|
|
|
494
638
|
taskManager.getTask(payload.data.interactionId),
|
|
495
639
|
'emit'
|
|
496
640
|
);
|
|
641
|
+
const taskManagerEmitSpy = jest.spyOn(taskManager, 'emit');
|
|
497
642
|
|
|
498
643
|
webSocketManagerMock.emit('message', JSON.stringify(assignedPayload));
|
|
499
644
|
|
|
@@ -501,6 +646,10 @@ describe('TaskManager', () => {
|
|
|
501
646
|
TASK_EVENTS.TASK_ASSIGNED,
|
|
502
647
|
taskManager.getTask(taskId)
|
|
503
648
|
);
|
|
649
|
+
expect(taskManagerEmitSpy).toHaveBeenCalledWith(
|
|
650
|
+
TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE,
|
|
651
|
+
taskManager.getTask(taskId)
|
|
652
|
+
);
|
|
504
653
|
});
|
|
505
654
|
|
|
506
655
|
it('should handle WebSocket message for AGENT_CONTACT_RESERVED and emit task:incoming for extension case', () => {
|
|
@@ -664,6 +813,7 @@ describe('TaskManager', () => {
|
|
|
664
813
|
it('test call listeners being switched off on call end', () => {
|
|
665
814
|
webSocketManagerMock.emit('message', JSON.stringify(initalPayload));
|
|
666
815
|
|
|
816
|
+
<<<<<<< HEAD
|
|
667
817
|
const webrtcTask = new WebRTC(contactMock, webCallingService, taskDataMock, {
|
|
668
818
|
isEndTaskEnabled: true,
|
|
669
819
|
isEndConsultEnabled: true,
|
|
@@ -686,6 +836,13 @@ describe('TaskManager', () => {
|
|
|
686
836
|
});
|
|
687
837
|
|
|
688
838
|
const webCallListenerSpy = jest.spyOn(task, 'unregisterWebCallListeners');
|
|
839
|
+
=======
|
|
840
|
+
const taskEmitSpy = jest.spyOn(taskManager.getTask(taskId), 'emit');
|
|
841
|
+
const webCallListenerSpy = jest.spyOn(
|
|
842
|
+
taskManager.getTask(taskId),
|
|
843
|
+
'unregisterWebCallListeners'
|
|
844
|
+
);
|
|
845
|
+
>>>>>>> 81672300901738f14289fd2a7414c6c4b1389aa6
|
|
689
846
|
const callOffSpy = jest.spyOn(mockCall, 'off');
|
|
690
847
|
const sendStateMachineEventSpy = jest.spyOn(task, 'sendStateMachineEvent');
|
|
691
848
|
const payload = {
|
|
@@ -718,12 +875,18 @@ describe('TaskManager', () => {
|
|
|
718
875
|
webSocketManagerMock.emit('message', JSON.stringify(hydratePayload));
|
|
719
876
|
|
|
720
877
|
taskManager.getTask(taskId).data = payload.data;
|
|
878
|
+
<<<<<<< HEAD
|
|
721
879
|
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
722
880
|
const stateMachineEvent = expectLastStateMachineEvent(
|
|
723
881
|
sendStateMachineEventSpy,
|
|
724
882
|
TaskEvent.CONTACT_ENDED
|
|
725
883
|
);
|
|
726
884
|
expect(stateMachineEvent?.taskData.wrapUpRequired).toBe(false);
|
|
885
|
+
=======
|
|
886
|
+
const task = taskManager.getTask(taskId);
|
|
887
|
+
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
888
|
+
expect(taskEmitSpy).toHaveBeenCalledWith(TASK_EVENTS.TASK_END, task);
|
|
889
|
+
>>>>>>> 81672300901738f14289fd2a7414c6c4b1389aa6
|
|
727
890
|
expect(webCallListenerSpy).toHaveBeenCalledWith();
|
|
728
891
|
expect(callOffSpy).toHaveBeenCalledWith(
|
|
729
892
|
CALL_EVENT_KEYS.REMOTE_MEDIA,
|
|
@@ -760,19 +923,29 @@ describe('TaskManager', () => {
|
|
|
760
923
|
|
|
761
924
|
task.updateTaskData(payload.data);
|
|
762
925
|
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
926
|
+
<<<<<<< HEAD
|
|
763
927
|
const stateMachineEvent = expectLastStateMachineEvent(
|
|
764
928
|
sendStateMachineEventSpy,
|
|
765
929
|
TaskEvent.CONTACT_ENDED
|
|
766
930
|
);
|
|
767
931
|
expect(stateMachineEvent?.taskData.wrapUpRequired).toBe(true);
|
|
768
932
|
sendStateMachineEventSpy.mockRestore();
|
|
933
|
+
=======
|
|
934
|
+
expect(taskEmitSpy).toHaveBeenCalledWith(CC_EVENTS.CONTACT_ENDED, {...payload.data});
|
|
935
|
+
expect(taskEmitSpy).toHaveBeenCalledWith(TASK_EVENTS.TASK_END, taskManager.getTask(taskId));
|
|
936
|
+
>>>>>>> 81672300901738f14289fd2a7414c6c4b1389aa6
|
|
769
937
|
});
|
|
770
938
|
|
|
771
939
|
it('should emit TASK_REJECT event on AGENT_INVITE_FAILED event', () => {
|
|
772
940
|
webSocketManagerMock.emit('message', JSON.stringify(initalPayload));
|
|
773
941
|
|
|
942
|
+
<<<<<<< HEAD
|
|
774
943
|
const task = taskManager.getTask(taskId);
|
|
775
944
|
const sendStateMachineEventSpy = jest.spyOn(task, 'sendStateMachineEvent');
|
|
945
|
+
=======
|
|
946
|
+
const taskEmitSpy = jest.spyOn(taskManager.getTask(taskId), 'emit');
|
|
947
|
+
const metricsTrackSpy = jest.spyOn(taskManager.metricsManager, 'trackEvent');
|
|
948
|
+
>>>>>>> 81672300901738f14289fd2a7414c6c4b1389aa6
|
|
776
949
|
const payload = {
|
|
777
950
|
data: {
|
|
778
951
|
type: CC_EVENTS.AGENT_INVITE_FAILED,
|
|
@@ -791,6 +964,7 @@ describe('TaskManager', () => {
|
|
|
791
964
|
},
|
|
792
965
|
};
|
|
793
966
|
|
|
967
|
+
<<<<<<< HEAD
|
|
794
968
|
task.updateTaskData(payload.data);
|
|
795
969
|
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
796
970
|
const stateMachineEvent = expectLastStateMachineEvent(
|
|
@@ -802,6 +976,18 @@ describe('TaskManager', () => {
|
|
|
802
976
|
});
|
|
803
977
|
|
|
804
978
|
it('should emit TASK_HYDRATE even if task is already present in taskManager', () => {
|
|
979
|
+
=======
|
|
980
|
+
taskManager.getTask(taskId).updateTaskData(payload.data);
|
|
981
|
+
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
982
|
+
expect(taskEmitSpy).toHaveBeenCalledWith(CC_EVENTS.AGENT_INVITE_FAILED, {...payload.data});
|
|
983
|
+
expect(taskEmitSpy).toHaveBeenCalledWith(TASK_EVENTS.TASK_REJECT, payload.data.reason);
|
|
984
|
+
// Verify the correct metric event name is used for AGENT_INVITE_FAILED
|
|
985
|
+
expect(metricsTrackSpy).toHaveBeenCalled();
|
|
986
|
+
expect(metricsTrackSpy.mock.calls[0][0]).toBe('Agent Invite Failed');
|
|
987
|
+
});
|
|
988
|
+
|
|
989
|
+
it('should not emit TASK_HYDRATE if task is already present in taskManager', () => {
|
|
990
|
+
>>>>>>> 81672300901738f14289fd2a7414c6c4b1389aa6
|
|
805
991
|
const payload = {
|
|
806
992
|
data: {
|
|
807
993
|
...initalPayload.data,
|
|
@@ -938,7 +1124,11 @@ describe('TaskManager', () => {
|
|
|
938
1124
|
expect(createdTask.data.isConferenceInProgress).toBe(false);
|
|
939
1125
|
});
|
|
940
1126
|
|
|
1127
|
+
<<<<<<< HEAD
|
|
941
1128
|
it('should emit TASK_WRAPUP event on AGENT_WRAPUP event', () => {
|
|
1129
|
+
=======
|
|
1130
|
+
it('should emit TASK_END event on AGENT_WRAPUP event', () => {
|
|
1131
|
+
>>>>>>> 81672300901738f14289fd2a7414c6c4b1389aa6
|
|
942
1132
|
webSocketManagerMock.emit('message', JSON.stringify(initalPayload));
|
|
943
1133
|
|
|
944
1134
|
const wrapupPayload = {
|
|
@@ -1093,8 +1283,13 @@ describe('TaskManager', () => {
|
|
|
1093
1283
|
|
|
1094
1284
|
const task = taskManager.getTask(taskId);
|
|
1095
1285
|
const taskEmitSpy = jest.spyOn(task, 'emit');
|
|
1286
|
+
<<<<<<< HEAD
|
|
1096
1287
|
const taskAcceptSpy = jest.spyOn(task, 'accept').mockResolvedValue(undefined);
|
|
1097
1288
|
const sendStateMachineEventSpy = jest.spyOn(task, 'sendStateMachineEvent');
|
|
1289
|
+
=======
|
|
1290
|
+
const taskManagerEmitSpy = jest.spyOn(taskManager, 'emit');
|
|
1291
|
+
const taskAcceptSpy = jest.spyOn(task, 'accept').mockResolvedValue(undefined);
|
|
1292
|
+
>>>>>>> 81672300901738f14289fd2a7414c6c4b1389aa6
|
|
1098
1293
|
|
|
1099
1294
|
// Step 2: Trigger AGENT_OFFER_CONTACT with auto-answer
|
|
1100
1295
|
const autoAnswerPayload = {
|
|
@@ -1118,6 +1313,7 @@ describe('TaskManager', () => {
|
|
|
1118
1313
|
// Verify accept was called
|
|
1119
1314
|
expect(taskAcceptSpy).toHaveBeenCalledTimes(1);
|
|
1120
1315
|
|
|
1316
|
+
<<<<<<< HEAD
|
|
1121
1317
|
const stateMachineEvent = expectLastStateMachineEvent(
|
|
1122
1318
|
sendStateMachineEventSpy,
|
|
1123
1319
|
TaskEvent.TASK_OFFERED
|
|
@@ -1126,6 +1322,11 @@ describe('TaskManager', () => {
|
|
|
1126
1322
|
// Verify task auto-answer event was emitted
|
|
1127
1323
|
expect(taskEmitSpy).toHaveBeenCalledWith(TASK_EVENTS.TASK_AUTO_ANSWERED, task);
|
|
1128
1324
|
sendStateMachineEventSpy.mockRestore();
|
|
1325
|
+
=======
|
|
1326
|
+
// Verify BOTH events were emitted
|
|
1327
|
+
expect(taskManagerEmitSpy).toHaveBeenCalledWith(TASK_EVENTS.TASK_OFFER_CONTACT, task);
|
|
1328
|
+
expect(taskEmitSpy).toHaveBeenCalledWith(TASK_EVENTS.TASK_AUTO_ANSWERED, task);
|
|
1329
|
+
>>>>>>> 81672300901738f14289fd2a7414c6c4b1389aa6
|
|
1129
1330
|
});
|
|
1130
1331
|
|
|
1131
1332
|
it('should NOT emit TASK_AUTO_ANSWERED event when auto-answer fails', async () => {
|
|
@@ -1171,7 +1372,10 @@ describe('TaskManager', () => {
|
|
|
1171
1372
|
const task = taskManager.getTask(taskId);
|
|
1172
1373
|
const taskEmitSpy = jest.spyOn(task, 'emit');
|
|
1173
1374
|
const taskAcceptSpy = jest.spyOn(task, 'accept').mockResolvedValue(undefined);
|
|
1375
|
+
<<<<<<< HEAD
|
|
1174
1376
|
const sendStateMachineEventSpy = jest.spyOn(task, 'sendStateMachineEvent');
|
|
1377
|
+
=======
|
|
1378
|
+
>>>>>>> 81672300901738f14289fd2a7414c6c4b1389aa6
|
|
1175
1379
|
|
|
1176
1380
|
// Step 2: Trigger AGENT_OFFER_CONSULT with auto-answer
|
|
1177
1381
|
const consultAutoAnswerPayload = {
|
|
@@ -1196,6 +1400,7 @@ describe('TaskManager', () => {
|
|
|
1196
1400
|
// Verify accept was called
|
|
1197
1401
|
expect(taskAcceptSpy).toHaveBeenCalledTimes(1);
|
|
1198
1402
|
|
|
1403
|
+
<<<<<<< HEAD
|
|
1199
1404
|
const stateMachineEvent = expectLastStateMachineEvent(
|
|
1200
1405
|
sendStateMachineEventSpy,
|
|
1201
1406
|
TaskEvent.OFFER_CONSULT
|
|
@@ -1205,11 +1410,18 @@ describe('TaskManager', () => {
|
|
|
1205
1410
|
isConsulted: true,
|
|
1206
1411
|
});
|
|
1207
1412
|
// Verify task auto-answer event was emitted
|
|
1413
|
+
=======
|
|
1414
|
+
// Verify BOTH events were emitted
|
|
1415
|
+
expect(taskEmitSpy).toHaveBeenCalledWith(TASK_EVENTS.TASK_OFFER_CONSULT, task);
|
|
1416
|
+
>>>>>>> 81672300901738f14289fd2a7414c6c4b1389aa6
|
|
1208
1417
|
expect(taskEmitSpy).toHaveBeenCalledWith(TASK_EVENTS.TASK_AUTO_ANSWERED, task);
|
|
1209
1418
|
|
|
1210
1419
|
// Verify isConsulted flag is set correctly
|
|
1211
1420
|
expect(task.data.isConsulted).toBe(true);
|
|
1421
|
+
<<<<<<< HEAD
|
|
1212
1422
|
sendStateMachineEventSpy.mockRestore();
|
|
1423
|
+
=======
|
|
1424
|
+
>>>>>>> 81672300901738f14289fd2a7414c6c4b1389aa6
|
|
1213
1425
|
});
|
|
1214
1426
|
|
|
1215
1427
|
it('should NOT emit TASK_AUTO_ANSWERED when isAutoAnswering is false', async () => {
|
|
@@ -1250,6 +1462,7 @@ describe('TaskManager', () => {
|
|
|
1250
1462
|
});
|
|
1251
1463
|
});
|
|
1252
1464
|
|
|
1465
|
+
<<<<<<< HEAD
|
|
1253
1466
|
it('should remove OUTDIAL task from taskCollection on AGENT_OUTBOUND_FAILED when terminated', () => {
|
|
1254
1467
|
const task = taskManager.getTask(taskId);
|
|
1255
1468
|
Object.assign(task.data, {
|
|
@@ -1264,6 +1477,26 @@ describe('TaskManager', () => {
|
|
|
1264
1477
|
task.unregisterWebCallListeners = jest.fn();
|
|
1265
1478
|
const removeTaskSpy = jest.spyOn(taskManager, 'removeTaskFromCollection');
|
|
1266
1479
|
const sendStateMachineEventSpy = jest.spyOn(task, 'sendStateMachineEvent');
|
|
1480
|
+
=======
|
|
1481
|
+
it('should NOT remove OUTDIAL task from taskCollection on AGENT_OUTBOUND_FAILED when terminated (wrap-up flow)', () => {
|
|
1482
|
+
const task = taskManager.getTask(taskId);
|
|
1483
|
+
task.updateTaskData = jest.fn().mockImplementation((newData) => {
|
|
1484
|
+
task.data = {
|
|
1485
|
+
...task.data,
|
|
1486
|
+
...newData,
|
|
1487
|
+
interaction: {
|
|
1488
|
+
...task.data.interaction,
|
|
1489
|
+
...newData.interaction,
|
|
1490
|
+
outboundType: 'OUTDIAL',
|
|
1491
|
+
state: 'new',
|
|
1492
|
+
isTerminated: true,
|
|
1493
|
+
},
|
|
1494
|
+
};
|
|
1495
|
+
return task;
|
|
1496
|
+
});
|
|
1497
|
+
task.unregisterWebCallListeners = jest.fn();
|
|
1498
|
+
const removeTaskSpy = jest.spyOn(taskManager, 'removeTaskFromCollection');
|
|
1499
|
+
>>>>>>> 81672300901738f14289fd2a7414c6c4b1389aa6
|
|
1267
1500
|
|
|
1268
1501
|
const payload = {
|
|
1269
1502
|
data: {
|
|
@@ -1276,7 +1509,10 @@ describe('TaskManager', () => {
|
|
|
1276
1509
|
state: 'new',
|
|
1277
1510
|
isTerminated: true,
|
|
1278
1511
|
},
|
|
1512
|
+
<<<<<<< HEAD
|
|
1279
1513
|
agentsPendingWrapUp: ['agent-123'],
|
|
1514
|
+
=======
|
|
1515
|
+
>>>>>>> 81672300901738f14289fd2a7414c6c4b1389aa6
|
|
1280
1516
|
interactionId: taskId,
|
|
1281
1517
|
orgId: '6ecef209-9a34-4ed1-a07a-7ddd1dbe925a',
|
|
1282
1518
|
trackingId: '575c0ec2-618c-42af-a61c-53aeb0a221ee',
|
|
@@ -1290,62 +1526,26 @@ describe('TaskManager', () => {
|
|
|
1290
1526
|
};
|
|
1291
1527
|
|
|
1292
1528
|
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
1529
|
+
<<<<<<< HEAD
|
|
1530
|
+
=======
|
|
1293
1531
|
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
expect(taskManager.getTask(taskId)).toBeUndefined();
|
|
1297
|
-
expect(removeTaskSpy).toHaveBeenCalled();
|
|
1298
|
-
const stateMachineEvent = expectLastStateMachineEvent(
|
|
1299
|
-
sendStateMachineEventSpy,
|
|
1300
|
-
TaskEvent.OUTBOUND_FAILED
|
|
1301
|
-
);
|
|
1302
|
-
expect(stateMachineEvent?.reason).toBe('CUSTOMER_BUSY');
|
|
1303
|
-
sendStateMachineEventSpy.mockRestore();
|
|
1532
|
+
expect(taskManager.getTask(taskId)).toBeDefined();
|
|
1533
|
+
expect(removeTaskSpy).not.toHaveBeenCalled();
|
|
1304
1534
|
});
|
|
1305
1535
|
|
|
1306
1536
|
it('should emit TASK_OUTDIAL_FAILED event on AGENT_OUTBOUND_FAILED', () => {
|
|
1307
1537
|
const task = taskManager.getTask(taskId);
|
|
1308
|
-
|
|
1309
|
-
const
|
|
1310
|
-
data: {
|
|
1311
|
-
type: CC_EVENTS.AGENT_OUTBOUND_FAILED,
|
|
1312
|
-
interactionId: taskId,
|
|
1313
|
-
reason: 'CUSTOMER_BUSY',
|
|
1314
|
-
},
|
|
1315
|
-
};
|
|
1316
|
-
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
1317
|
-
const stateMachineEvent = expectLastStateMachineEvent(
|
|
1318
|
-
sendStateMachineEventSpy,
|
|
1319
|
-
TaskEvent.OUTBOUND_FAILED
|
|
1320
|
-
);
|
|
1321
|
-
expect(stateMachineEvent?.reason).toBe('CUSTOMER_BUSY');
|
|
1322
|
-
sendStateMachineEventSpy.mockRestore();
|
|
1323
|
-
});
|
|
1324
|
-
|
|
1325
|
-
it('should pass taskData in OUTBOUND_FAILED event for shouldWrapUp guard evaluation', () => {
|
|
1326
|
-
const task = taskManager.getTask(taskId);
|
|
1327
|
-
const sendStateMachineEventSpy = jest.spyOn(task, 'sendStateMachineEvent');
|
|
1538
|
+
task.updateTaskData = jest.fn().mockReturnValue(task);
|
|
1539
|
+
const taskEmitSpy = jest.spyOn(task, 'emit');
|
|
1328
1540
|
const payload = {
|
|
1329
1541
|
data: {
|
|
1330
1542
|
type: CC_EVENTS.AGENT_OUTBOUND_FAILED,
|
|
1331
1543
|
interactionId: taskId,
|
|
1332
1544
|
reason: 'CUSTOMER_BUSY',
|
|
1333
|
-
agentsPendingWrapUp: ['agent-123'],
|
|
1334
|
-
interaction: {
|
|
1335
|
-
outboundType: 'OUTDIAL',
|
|
1336
|
-
isTerminated: true,
|
|
1337
|
-
},
|
|
1338
1545
|
},
|
|
1339
1546
|
};
|
|
1340
1547
|
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
1341
|
-
|
|
1342
|
-
sendStateMachineEventSpy,
|
|
1343
|
-
TaskEvent.OUTBOUND_FAILED
|
|
1344
|
-
);
|
|
1345
|
-
expect(stateMachineEvent?.taskData).toBeDefined();
|
|
1346
|
-
expect(stateMachineEvent?.taskData?.agentsPendingWrapUp).toEqual(['agent-123']);
|
|
1347
|
-
expect(stateMachineEvent?.taskData?.interaction?.outboundType).toBe('OUTDIAL');
|
|
1348
|
-
sendStateMachineEventSpy.mockRestore();
|
|
1548
|
+
expect(taskEmitSpy).toHaveBeenCalledWith(TASK_EVENTS.TASK_OUTDIAL_FAILED, 'CUSTOMER_BUSY');
|
|
1349
1549
|
});
|
|
1350
1550
|
|
|
1351
1551
|
it('should handle AGENT_OUTBOUND_FAILED gracefully when task is undefined', () => {
|
|
@@ -1363,15 +1563,23 @@ describe('TaskManager', () => {
|
|
|
1363
1563
|
});
|
|
1364
1564
|
|
|
1365
1565
|
it('should NOT remove OUTDIAL task on CONTACT_ENDED when agentsPendingWrapUp exists', () => {
|
|
1566
|
+
const agentId = '723a8ffb-a26e-496d-b14a-ff44fb83b64f';
|
|
1567
|
+
taskManager.setAgentId(agentId);
|
|
1568
|
+
|
|
1366
1569
|
const task = taskManager.getTask(taskId);
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
...task.data
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1570
|
+
task.updateTaskData = jest.fn().mockImplementation((newData) => {
|
|
1571
|
+
task.data = {
|
|
1572
|
+
...task.data,
|
|
1573
|
+
...newData,
|
|
1574
|
+
interaction: {
|
|
1575
|
+
...task.data.interaction,
|
|
1576
|
+
outboundType: 'OUTDIAL',
|
|
1577
|
+
state: 'new',
|
|
1578
|
+
mediaType: 'telephony',
|
|
1579
|
+
},
|
|
1580
|
+
agentsPendingWrapUp: [agentId],
|
|
1581
|
+
};
|
|
1582
|
+
return task;
|
|
1375
1583
|
});
|
|
1376
1584
|
task.unregisterWebCallListeners = jest.fn();
|
|
1377
1585
|
const removeTaskSpy = jest.spyOn(taskManager, 'removeTaskFromCollection');
|
|
@@ -1385,7 +1593,7 @@ describe('TaskManager', () => {
|
|
|
1385
1593
|
state: 'new',
|
|
1386
1594
|
mediaType: 'telephony',
|
|
1387
1595
|
},
|
|
1388
|
-
agentsPendingWrapUp: [
|
|
1596
|
+
agentsPendingWrapUp: [agentId],
|
|
1389
1597
|
},
|
|
1390
1598
|
};
|
|
1391
1599
|
|
|
@@ -1397,14 +1605,19 @@ describe('TaskManager', () => {
|
|
|
1397
1605
|
|
|
1398
1606
|
it('should remove OUTDIAL task on CONTACT_ENDED when agentsPendingWrapUp is empty', () => {
|
|
1399
1607
|
const task = taskManager.getTask(taskId);
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
...task.data
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1608
|
+
task.updateTaskData = jest.fn().mockImplementation((newData) => {
|
|
1609
|
+
task.data = {
|
|
1610
|
+
...task.data,
|
|
1611
|
+
...newData,
|
|
1612
|
+
interaction: {
|
|
1613
|
+
...task.data.interaction,
|
|
1614
|
+
outboundType: 'OUTDIAL',
|
|
1615
|
+
state: 'new',
|
|
1616
|
+
mediaType: 'telephony',
|
|
1617
|
+
},
|
|
1618
|
+
agentsPendingWrapUp: [],
|
|
1619
|
+
};
|
|
1620
|
+
return task;
|
|
1408
1621
|
});
|
|
1409
1622
|
task.unregisterWebCallListeners = jest.fn();
|
|
1410
1623
|
const removeTaskSpy = jest.spyOn(taskManager, 'removeTaskFromCollection');
|
|
@@ -1429,13 +1642,19 @@ describe('TaskManager', () => {
|
|
|
1429
1642
|
|
|
1430
1643
|
it('should remove OUTDIAL task on CONTACT_ENDED when agentsPendingWrapUp is undefined', () => {
|
|
1431
1644
|
const task = taskManager.getTask(taskId);
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
...task.data
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1645
|
+
task.updateTaskData = jest.fn().mockImplementation((newData) => {
|
|
1646
|
+
task.data = {
|
|
1647
|
+
...task.data,
|
|
1648
|
+
...newData,
|
|
1649
|
+
interaction: {
|
|
1650
|
+
...task.data.interaction,
|
|
1651
|
+
outboundType: 'OUTDIAL',
|
|
1652
|
+
state: 'new',
|
|
1653
|
+
mediaType: 'telephony',
|
|
1654
|
+
},
|
|
1655
|
+
// agentsPendingWrapUp is undefined
|
|
1656
|
+
};
|
|
1657
|
+
return task;
|
|
1439
1658
|
});
|
|
1440
1659
|
task.unregisterWebCallListeners = jest.fn();
|
|
1441
1660
|
const removeTaskSpy = jest.spyOn(taskManager, 'removeTaskFromCollection');
|
|
@@ -1474,34 +1693,338 @@ describe('TaskManager', () => {
|
|
|
1474
1693
|
}).not.toThrow();
|
|
1475
1694
|
});
|
|
1476
1695
|
|
|
1477
|
-
|
|
1478
|
-
const
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
state: 'new',
|
|
1484
|
-
isTerminated: false,
|
|
1485
|
-
},
|
|
1696
|
+
describe('wrapUpRequired logic in CONTACT_ENDED event', () => {
|
|
1697
|
+
const agentId = '723a8ffb-a26e-496d-b14a-ff44fb83b64f';
|
|
1698
|
+
|
|
1699
|
+
beforeEach(() => {
|
|
1700
|
+
// Set the agent ID on taskManager
|
|
1701
|
+
taskManager.setAgentId(agentId);
|
|
1486
1702
|
});
|
|
1487
|
-
task.unregisterWebCallListeners = jest.fn();
|
|
1488
|
-
const removeTaskSpy = jest.spyOn(taskManager, 'removeTaskFromCollection');
|
|
1489
1703
|
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1704
|
+
it('should set wrapUpRequired to true when agent is in agentsPendingWrapUp array for non-campaign tasks', () => {
|
|
1705
|
+
const task = taskManager.getTask(taskId);
|
|
1706
|
+
task.updateTaskData = jest.fn().mockImplementation((newData) => {
|
|
1707
|
+
task.data = {
|
|
1708
|
+
...task.data,
|
|
1709
|
+
...newData,
|
|
1710
|
+
};
|
|
1711
|
+
return task;
|
|
1712
|
+
});
|
|
1713
|
+
task.unregisterWebCallListeners = jest.fn();
|
|
1714
|
+
|
|
1715
|
+
const payload = {
|
|
1716
|
+
data: {
|
|
1717
|
+
type: CC_EVENTS.CONTACT_ENDED,
|
|
1718
|
+
interactionId: taskId,
|
|
1719
|
+
interaction: {
|
|
1720
|
+
state: 'connected',
|
|
1721
|
+
mediaType: 'telephony',
|
|
1722
|
+
},
|
|
1723
|
+
agentsPendingWrapUp: [agentId, 'other-agent-id'],
|
|
1500
1724
|
},
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1725
|
+
};
|
|
1726
|
+
|
|
1727
|
+
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
1728
|
+
|
|
1729
|
+
expect(task.updateTaskData).toHaveBeenCalledWith(
|
|
1730
|
+
expect.objectContaining({
|
|
1731
|
+
wrapUpRequired: true,
|
|
1732
|
+
})
|
|
1733
|
+
);
|
|
1734
|
+
});
|
|
1735
|
+
|
|
1736
|
+
it('should set wrapUpRequired to false when agent is not in agentsPendingWrapUp array', () => {
|
|
1737
|
+
const task = taskManager.getTask(taskId);
|
|
1738
|
+
task.updateTaskData = jest.fn().mockImplementation((newData) => {
|
|
1739
|
+
task.data = {
|
|
1740
|
+
...task.data,
|
|
1741
|
+
...newData,
|
|
1742
|
+
};
|
|
1743
|
+
return task;
|
|
1744
|
+
});
|
|
1745
|
+
task.unregisterWebCallListeners = jest.fn();
|
|
1746
|
+
|
|
1747
|
+
const payload = {
|
|
1748
|
+
data: {
|
|
1749
|
+
type: CC_EVENTS.CONTACT_ENDED,
|
|
1750
|
+
interactionId: taskId,
|
|
1751
|
+
interaction: {
|
|
1752
|
+
state: 'connected',
|
|
1753
|
+
mediaType: 'telephony',
|
|
1754
|
+
},
|
|
1755
|
+
agentsPendingWrapUp: ['other-agent-id', 'another-agent-id'],
|
|
1756
|
+
},
|
|
1757
|
+
};
|
|
1758
|
+
|
|
1759
|
+
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
1760
|
+
|
|
1761
|
+
expect(task.updateTaskData).toHaveBeenCalledWith(
|
|
1762
|
+
expect.objectContaining({
|
|
1763
|
+
wrapUpRequired: false,
|
|
1764
|
+
})
|
|
1765
|
+
);
|
|
1766
|
+
});
|
|
1767
|
+
|
|
1768
|
+
it('should set wrapUpRequired to false when agentsPendingWrapUp is an empty array', () => {
|
|
1769
|
+
const task = taskManager.getTask(taskId);
|
|
1770
|
+
task.updateTaskData = jest.fn().mockImplementation((newData) => {
|
|
1771
|
+
task.data = {
|
|
1772
|
+
...task.data,
|
|
1773
|
+
...newData,
|
|
1774
|
+
};
|
|
1775
|
+
return task;
|
|
1776
|
+
});
|
|
1777
|
+
task.unregisterWebCallListeners = jest.fn();
|
|
1778
|
+
|
|
1779
|
+
const payload = {
|
|
1780
|
+
data: {
|
|
1781
|
+
type: CC_EVENTS.CONTACT_ENDED,
|
|
1782
|
+
interactionId: taskId,
|
|
1783
|
+
interaction: {
|
|
1784
|
+
state: 'connected',
|
|
1785
|
+
mediaType: 'telephony',
|
|
1786
|
+
},
|
|
1787
|
+
agentsPendingWrapUp: [],
|
|
1788
|
+
},
|
|
1789
|
+
};
|
|
1790
|
+
|
|
1791
|
+
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
1792
|
+
|
|
1793
|
+
expect(task.updateTaskData).toHaveBeenCalledWith(
|
|
1794
|
+
expect.objectContaining({
|
|
1795
|
+
wrapUpRequired: false,
|
|
1796
|
+
})
|
|
1797
|
+
);
|
|
1798
|
+
});
|
|
1799
|
+
|
|
1800
|
+
it('should set wrapUpRequired to false when agentsPendingWrapUp is undefined', () => {
|
|
1801
|
+
const task = taskManager.getTask(taskId);
|
|
1802
|
+
task.updateTaskData = jest.fn().mockImplementation((newData) => {
|
|
1803
|
+
task.data = {
|
|
1804
|
+
...task.data,
|
|
1805
|
+
...newData,
|
|
1806
|
+
};
|
|
1807
|
+
return task;
|
|
1808
|
+
});
|
|
1809
|
+
task.unregisterWebCallListeners = jest.fn();
|
|
1810
|
+
|
|
1811
|
+
const payload = {
|
|
1812
|
+
data: {
|
|
1813
|
+
type: CC_EVENTS.CONTACT_ENDED,
|
|
1814
|
+
interactionId: taskId,
|
|
1815
|
+
interaction: {
|
|
1816
|
+
state: 'connected',
|
|
1817
|
+
mediaType: 'telephony',
|
|
1818
|
+
},
|
|
1819
|
+
// agentsPendingWrapUp is not defined
|
|
1820
|
+
},
|
|
1821
|
+
};
|
|
1822
|
+
|
|
1823
|
+
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
1824
|
+
|
|
1825
|
+
expect(task.updateTaskData).toHaveBeenCalledWith(
|
|
1826
|
+
expect.objectContaining({
|
|
1827
|
+
wrapUpRequired: false,
|
|
1828
|
+
})
|
|
1829
|
+
);
|
|
1830
|
+
});
|
|
1831
|
+
|
|
1832
|
+
it('should set wrapUpRequired to false when agentsPendingWrapUp is null', () => {
|
|
1833
|
+
const task = taskManager.getTask(taskId);
|
|
1834
|
+
task.updateTaskData = jest.fn().mockImplementation((newData) => {
|
|
1835
|
+
task.data = {
|
|
1836
|
+
...task.data,
|
|
1837
|
+
...newData,
|
|
1838
|
+
};
|
|
1839
|
+
return task;
|
|
1840
|
+
});
|
|
1841
|
+
task.unregisterWebCallListeners = jest.fn();
|
|
1842
|
+
|
|
1843
|
+
const payload = {
|
|
1844
|
+
data: {
|
|
1845
|
+
type: CC_EVENTS.CONTACT_ENDED,
|
|
1846
|
+
interactionId: taskId,
|
|
1847
|
+
interaction: {
|
|
1848
|
+
state: 'connected',
|
|
1849
|
+
mediaType: 'telephony',
|
|
1850
|
+
},
|
|
1851
|
+
agentsPendingWrapUp: null,
|
|
1852
|
+
},
|
|
1853
|
+
};
|
|
1854
|
+
|
|
1855
|
+
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
1856
|
+
|
|
1857
|
+
expect(task.updateTaskData).toHaveBeenCalledWith(
|
|
1858
|
+
expect.objectContaining({
|
|
1859
|
+
wrapUpRequired: false,
|
|
1860
|
+
})
|
|
1861
|
+
);
|
|
1862
|
+
});
|
|
1863
|
+
|
|
1864
|
+
it('should set wrapUpRequired to true when agent is the only one in agentsPendingWrapUp for non-campaign tasks', () => {
|
|
1865
|
+
const task = taskManager.getTask(taskId);
|
|
1866
|
+
task.updateTaskData = jest.fn().mockImplementation((newData) => {
|
|
1867
|
+
task.data = {
|
|
1868
|
+
...task.data,
|
|
1869
|
+
...newData,
|
|
1870
|
+
};
|
|
1871
|
+
return task;
|
|
1872
|
+
});
|
|
1873
|
+
task.unregisterWebCallListeners = jest.fn();
|
|
1874
|
+
|
|
1875
|
+
const payload = {
|
|
1876
|
+
data: {
|
|
1877
|
+
type: CC_EVENTS.CONTACT_ENDED,
|
|
1878
|
+
interactionId: taskId,
|
|
1879
|
+
interaction: {
|
|
1880
|
+
state: 'connected',
|
|
1881
|
+
mediaType: 'telephony',
|
|
1882
|
+
},
|
|
1883
|
+
agentsPendingWrapUp: [agentId],
|
|
1884
|
+
},
|
|
1885
|
+
};
|
|
1886
|
+
|
|
1887
|
+
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
1888
|
+
|
|
1889
|
+
expect(task.updateTaskData).toHaveBeenCalledWith(
|
|
1890
|
+
expect.objectContaining({
|
|
1891
|
+
wrapUpRequired: true,
|
|
1892
|
+
})
|
|
1893
|
+
);
|
|
1894
|
+
});
|
|
1895
|
+
|
|
1896
|
+
it('should set wrapUpRequired to true for different interaction states when agent is in agentsPendingWrapUp for non-campaign tasks', () => {
|
|
1897
|
+
const task = taskManager.getTask(taskId);
|
|
1898
|
+
task.updateTaskData = jest.fn().mockImplementation((newData) => {
|
|
1899
|
+
task.data = {
|
|
1900
|
+
...task.data,
|
|
1901
|
+
...newData,
|
|
1902
|
+
interaction: {
|
|
1903
|
+
...task.data.interaction,
|
|
1904
|
+
...newData.interaction,
|
|
1905
|
+
},
|
|
1906
|
+
};
|
|
1907
|
+
return task;
|
|
1908
|
+
});
|
|
1909
|
+
task.unregisterWebCallListeners = jest.fn();
|
|
1910
|
+
|
|
1911
|
+
// Test with 'connected' state
|
|
1912
|
+
const payloadConnected = {
|
|
1913
|
+
data: {
|
|
1914
|
+
type: CC_EVENTS.CONTACT_ENDED,
|
|
1915
|
+
interactionId: taskId,
|
|
1916
|
+
interaction: {
|
|
1917
|
+
state: 'connected',
|
|
1918
|
+
mediaType: 'telephony',
|
|
1919
|
+
},
|
|
1920
|
+
agentsPendingWrapUp: [agentId],
|
|
1921
|
+
},
|
|
1922
|
+
};
|
|
1923
|
+
|
|
1924
|
+
webSocketManagerMock.emit('message', JSON.stringify(payloadConnected));
|
|
1925
|
+
|
|
1926
|
+
expect(task.updateTaskData).toHaveBeenNthCalledWith(
|
|
1927
|
+
1,
|
|
1928
|
+
expect.objectContaining({
|
|
1929
|
+
wrapUpRequired: true,
|
|
1930
|
+
})
|
|
1931
|
+
);
|
|
1932
|
+
|
|
1933
|
+
// Test with 'held' state to verify it still works regardless of state
|
|
1934
|
+
const payloadHeld = {
|
|
1935
|
+
data: {
|
|
1936
|
+
type: CC_EVENTS.CONTACT_ENDED,
|
|
1937
|
+
interactionId: taskId,
|
|
1938
|
+
interaction: {
|
|
1939
|
+
state: 'held',
|
|
1940
|
+
mediaType: 'telephony',
|
|
1941
|
+
},
|
|
1942
|
+
agentsPendingWrapUp: [agentId],
|
|
1943
|
+
},
|
|
1944
|
+
};
|
|
1945
|
+
|
|
1946
|
+
webSocketManagerMock.emit('message', JSON.stringify(payloadHeld));
|
|
1947
|
+
|
|
1948
|
+
expect(task.updateTaskData).toHaveBeenNthCalledWith(
|
|
1949
|
+
2,
|
|
1950
|
+
expect.objectContaining({
|
|
1951
|
+
wrapUpRequired: true,
|
|
1952
|
+
})
|
|
1953
|
+
);
|
|
1954
|
+
});
|
|
1955
|
+
|
|
1956
|
+
it('should set wrapUpRequired to false for campaign preview tasks even when agent is in agentsPendingWrapUp', () => {
|
|
1957
|
+
const task = taskManager.getTask(taskId);
|
|
1958
|
+
// Set up task as a campaign preview task via outboundType
|
|
1959
|
+
task.data.interaction = {
|
|
1960
|
+
...task.data.interaction,
|
|
1961
|
+
outboundType: 'STANDARD_PREVIEW_CAMPAIGN',
|
|
1962
|
+
};
|
|
1963
|
+
task.updateTaskData = jest.fn().mockImplementation((newData) => {
|
|
1964
|
+
task.data = {
|
|
1965
|
+
...task.data,
|
|
1966
|
+
...newData,
|
|
1967
|
+
};
|
|
1968
|
+
return task;
|
|
1969
|
+
});
|
|
1970
|
+
task.unregisterWebCallListeners = jest.fn();
|
|
1971
|
+
|
|
1972
|
+
const payload = {
|
|
1973
|
+
data: {
|
|
1974
|
+
type: CC_EVENTS.CONTACT_ENDED,
|
|
1975
|
+
interactionId: taskId,
|
|
1976
|
+
interaction: {
|
|
1977
|
+
state: 'connected',
|
|
1978
|
+
mediaType: 'telephony',
|
|
1979
|
+
},
|
|
1980
|
+
agentsPendingWrapUp: [agentId],
|
|
1981
|
+
},
|
|
1982
|
+
};
|
|
1983
|
+
|
|
1984
|
+
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
1985
|
+
|
|
1986
|
+
expect(task.updateTaskData).toHaveBeenCalledWith(
|
|
1987
|
+
expect.objectContaining({
|
|
1988
|
+
wrapUpRequired: false,
|
|
1989
|
+
})
|
|
1990
|
+
);
|
|
1991
|
+
});
|
|
1992
|
+
});
|
|
1993
|
+
|
|
1994
|
+
it('should remove OUTDIAL task from taskCollection on AGENT_CONTACT_ASSIGN_FAILED when NOT terminated (user-declined)', () => {
|
|
1995
|
+
const task = taskManager.getTask(taskId);
|
|
1996
|
+
task.updateTaskData = jest.fn().mockImplementation((newData) => {
|
|
1997
|
+
task.data = {
|
|
1998
|
+
...task.data,
|
|
1999
|
+
...newData,
|
|
2000
|
+
interaction: {
|
|
2001
|
+
...task.data.interaction,
|
|
2002
|
+
...newData.interaction,
|
|
2003
|
+
outboundType: 'OUTDIAL',
|
|
2004
|
+
state: 'new',
|
|
2005
|
+
isTerminated: false,
|
|
2006
|
+
},
|
|
2007
|
+
};
|
|
2008
|
+
return task;
|
|
2009
|
+
});
|
|
2010
|
+
task.unregisterWebCallListeners = jest.fn();
|
|
2011
|
+
const removeTaskSpy = jest.spyOn(taskManager, 'removeTaskFromCollection');
|
|
2012
|
+
|
|
2013
|
+
const payload = {
|
|
2014
|
+
data: {
|
|
2015
|
+
type: CC_EVENTS.AGENT_CONTACT_ASSIGN_FAILED,
|
|
2016
|
+
agentId: '723a8ffb-a26e-496d-b14a-ff44fb83b64f',
|
|
2017
|
+
eventTime: 1733211616959,
|
|
2018
|
+
eventType: 'RoutingMessage',
|
|
2019
|
+
interaction: {
|
|
2020
|
+
outboundType: 'OUTDIAL',
|
|
2021
|
+
state: 'new',
|
|
2022
|
+
isTerminated: false,
|
|
2023
|
+
},
|
|
2024
|
+
interactionId: taskId,
|
|
2025
|
+
orgId: '6ecef209-9a34-4ed1-a07a-7ddd1dbe925a',
|
|
2026
|
+
trackingId: '575c0ec2-618c-42af-a61c-53aeb0a221ee',
|
|
2027
|
+
mediaResourceId: '0ae913a4-c857-4705-8d49-76dd3dde75e4',
|
|
1505
2028
|
destAgentId: 'ebeb893b-ba67-4f36-8418-95c7492b28c2',
|
|
1506
2029
|
owner: '723a8ffb-a26e-496d-b14a-ff44fb83b64f',
|
|
1507
2030
|
queueMgr: 'aqm',
|
|
@@ -1509,91 +2032,322 @@ describe('TaskManager', () => {
|
|
|
1509
2032
|
reasonCode: 156,
|
|
1510
2033
|
},
|
|
1511
2034
|
};
|
|
2035
|
+
>>>>>>> 81672300901738f14289fd2a7414c6c4b1389aa6
|
|
1512
2036
|
|
|
1513
2037
|
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
1514
2038
|
|
|
1515
2039
|
expect(taskManager.getTask(taskId)).toBeUndefined();
|
|
1516
2040
|
expect(removeTaskSpy).toHaveBeenCalled();
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
it('handle AGENT_OFFER_CONSULT event', () => {
|
|
1520
|
-
const payload = {
|
|
1521
|
-
data: {
|
|
1522
|
-
...initalPayload.data,
|
|
1523
|
-
type: CC_EVENTS.AGENT_OFFER_CONSULT,
|
|
1524
|
-
},
|
|
1525
|
-
};
|
|
1526
|
-
|
|
1527
|
-
webSocketManagerMock.emit('message', JSON.stringify(initalPayload));
|
|
1528
|
-
const task = taskManager.getTask(taskId);
|
|
1529
|
-
const sendStateMachineEventSpy = jest.spyOn(task, 'sendStateMachineEvent');
|
|
1530
|
-
|
|
1531
|
-
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
1532
|
-
|
|
2041
|
+
<<<<<<< HEAD
|
|
1533
2042
|
const stateMachineEvent = expectLastStateMachineEvent(
|
|
1534
2043
|
sendStateMachineEventSpy,
|
|
1535
|
-
TaskEvent.
|
|
2044
|
+
TaskEvent.OUTBOUND_FAILED
|
|
1536
2045
|
);
|
|
1537
|
-
expect(stateMachineEvent?.
|
|
2046
|
+
expect(stateMachineEvent?.reason).toBe('CUSTOMER_BUSY');
|
|
1538
2047
|
sendStateMachineEventSpy.mockRestore();
|
|
1539
2048
|
});
|
|
1540
2049
|
|
|
1541
|
-
it('should emit
|
|
1542
|
-
const
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
type: CC_EVENTS.AGENT_OFFER_CONSULT,
|
|
1546
|
-
},
|
|
1547
|
-
};
|
|
1548
|
-
|
|
1549
|
-
const consultingPayload = {
|
|
2050
|
+
it('should emit TASK_OUTDIAL_FAILED event on AGENT_OUTBOUND_FAILED', () => {
|
|
2051
|
+
const task = taskManager.getTask(taskId);
|
|
2052
|
+
const sendStateMachineEventSpy = jest.spyOn(task, 'sendStateMachineEvent');
|
|
2053
|
+
const payload = {
|
|
1550
2054
|
data: {
|
|
1551
|
-
|
|
1552
|
-
|
|
2055
|
+
type: CC_EVENTS.AGENT_OUTBOUND_FAILED,
|
|
2056
|
+
interactionId: taskId,
|
|
2057
|
+
reason: 'CUSTOMER_BUSY',
|
|
1553
2058
|
},
|
|
1554
2059
|
};
|
|
1555
|
-
|
|
1556
|
-
webSocketManagerMock.emit('message', JSON.stringify(initalPayload));
|
|
1557
|
-
|
|
1558
|
-
const task = taskManager.getTask(taskId);
|
|
1559
|
-
const sendStateMachineEventSpy = jest.spyOn(task, 'sendStateMachineEvent');
|
|
1560
|
-
|
|
1561
|
-
webSocketManagerMock.emit('message', JSON.stringify(initialConsultingPayload));
|
|
1562
|
-
webSocketManagerMock.emit('message', JSON.stringify(consultingPayload));
|
|
2060
|
+
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
1563
2061
|
const stateMachineEvent = expectLastStateMachineEvent(
|
|
1564
2062
|
sendStateMachineEventSpy,
|
|
1565
|
-
TaskEvent.
|
|
2063
|
+
TaskEvent.OUTBOUND_FAILED
|
|
1566
2064
|
);
|
|
1567
|
-
expect(stateMachineEvent?.
|
|
2065
|
+
expect(stateMachineEvent?.reason).toBe('CUSTOMER_BUSY');
|
|
1568
2066
|
sendStateMachineEventSpy.mockRestore();
|
|
1569
2067
|
});
|
|
1570
2068
|
|
|
1571
|
-
it('should
|
|
2069
|
+
it('should pass taskData in OUTBOUND_FAILED event for shouldWrapUp guard evaluation', () => {
|
|
2070
|
+
const task = taskManager.getTask(taskId);
|
|
2071
|
+
const sendStateMachineEventSpy = jest.spyOn(task, 'sendStateMachineEvent');
|
|
1572
2072
|
const payload = {
|
|
1573
2073
|
data: {
|
|
1574
|
-
|
|
1575
|
-
|
|
2074
|
+
type: CC_EVENTS.AGENT_OUTBOUND_FAILED,
|
|
2075
|
+
interactionId: taskId,
|
|
2076
|
+
reason: 'CUSTOMER_BUSY',
|
|
2077
|
+
agentsPendingWrapUp: ['agent-123'],
|
|
2078
|
+
interaction: {
|
|
2079
|
+
outboundType: 'OUTDIAL',
|
|
2080
|
+
isTerminated: true,
|
|
2081
|
+
},
|
|
1576
2082
|
},
|
|
1577
2083
|
};
|
|
1578
|
-
|
|
1579
|
-
webSocketManagerMock.emit('message', JSON.stringify(initalPayload));
|
|
1580
|
-
taskManager.getTask(taskId).data.isConsulted = true;
|
|
1581
|
-
const task = taskManager.getTask(taskId);
|
|
1582
|
-
const sendStateMachineEventSpy = jest.spyOn(task, 'sendStateMachineEvent');
|
|
1583
2084
|
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
1584
2085
|
const stateMachineEvent = expectLastStateMachineEvent(
|
|
1585
2086
|
sendStateMachineEventSpy,
|
|
1586
|
-
TaskEvent.
|
|
2087
|
+
TaskEvent.OUTBOUND_FAILED
|
|
1587
2088
|
);
|
|
1588
|
-
expect(stateMachineEvent?.taskData).
|
|
2089
|
+
expect(stateMachineEvent?.taskData).toBeDefined();
|
|
2090
|
+
expect(stateMachineEvent?.taskData?.agentsPendingWrapUp).toEqual(['agent-123']);
|
|
2091
|
+
expect(stateMachineEvent?.taskData?.interaction?.outboundType).toBe('OUTDIAL');
|
|
1589
2092
|
sendStateMachineEventSpy.mockRestore();
|
|
1590
2093
|
});
|
|
1591
2094
|
|
|
1592
|
-
it('should
|
|
2095
|
+
it('should handle AGENT_OUTBOUND_FAILED gracefully when task is undefined', () => {
|
|
1593
2096
|
const payload = {
|
|
1594
2097
|
data: {
|
|
1595
|
-
|
|
1596
|
-
|
|
2098
|
+
type: CC_EVENTS.AGENT_OUTBOUND_FAILED,
|
|
2099
|
+
interactionId: 'non-existent-task-id',
|
|
2100
|
+
reason: 'CUSTOMER_BUSY',
|
|
2101
|
+
},
|
|
2102
|
+
};
|
|
2103
|
+
// Should not throw error when task doesn't exist
|
|
2104
|
+
expect(() => {
|
|
2105
|
+
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
2106
|
+
}).not.toThrow();
|
|
2107
|
+
});
|
|
2108
|
+
|
|
2109
|
+
it('should NOT remove OUTDIAL task on CONTACT_ENDED when agentsPendingWrapUp exists', () => {
|
|
2110
|
+
const task = taskManager.getTask(taskId);
|
|
2111
|
+
Object.assign(task.data, {
|
|
2112
|
+
interaction: {
|
|
2113
|
+
...task.data.interaction,
|
|
2114
|
+
outboundType: 'OUTDIAL',
|
|
2115
|
+
state: 'new',
|
|
2116
|
+
mediaType: 'telephony',
|
|
2117
|
+
},
|
|
2118
|
+
agentsPendingWrapUp: ['agent-123'],
|
|
2119
|
+
});
|
|
2120
|
+
task.unregisterWebCallListeners = jest.fn();
|
|
2121
|
+
const removeTaskSpy = jest.spyOn(taskManager, 'removeTaskFromCollection');
|
|
2122
|
+
|
|
2123
|
+
const payload = {
|
|
2124
|
+
data: {
|
|
2125
|
+
type: CC_EVENTS.CONTACT_ENDED,
|
|
2126
|
+
interactionId: taskId,
|
|
2127
|
+
interaction: {
|
|
2128
|
+
outboundType: 'OUTDIAL',
|
|
2129
|
+
state: 'new',
|
|
2130
|
+
mediaType: 'telephony',
|
|
2131
|
+
},
|
|
2132
|
+
agentsPendingWrapUp: ['agent-123'],
|
|
2133
|
+
},
|
|
2134
|
+
};
|
|
2135
|
+
|
|
2136
|
+
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
2137
|
+
|
|
2138
|
+
expect(removeTaskSpy).not.toHaveBeenCalled();
|
|
2139
|
+
expect(taskManager.getTask(taskId)).toBeDefined();
|
|
2140
|
+
});
|
|
2141
|
+
|
|
2142
|
+
it('should remove OUTDIAL task on CONTACT_ENDED when agentsPendingWrapUp is empty', () => {
|
|
2143
|
+
const task = taskManager.getTask(taskId);
|
|
2144
|
+
Object.assign(task.data, {
|
|
2145
|
+
interaction: {
|
|
2146
|
+
...task.data.interaction,
|
|
2147
|
+
outboundType: 'OUTDIAL',
|
|
2148
|
+
state: 'new',
|
|
2149
|
+
mediaType: 'telephony',
|
|
2150
|
+
},
|
|
2151
|
+
agentsPendingWrapUp: [],
|
|
2152
|
+
});
|
|
2153
|
+
task.unregisterWebCallListeners = jest.fn();
|
|
2154
|
+
const removeTaskSpy = jest.spyOn(taskManager, 'removeTaskFromCollection');
|
|
2155
|
+
|
|
2156
|
+
const payload = {
|
|
2157
|
+
data: {
|
|
2158
|
+
type: CC_EVENTS.CONTACT_ENDED,
|
|
2159
|
+
interactionId: taskId,
|
|
2160
|
+
interaction: {
|
|
2161
|
+
outboundType: 'OUTDIAL',
|
|
2162
|
+
state: 'new',
|
|
2163
|
+
mediaType: 'telephony',
|
|
2164
|
+
},
|
|
2165
|
+
agentsPendingWrapUp: [],
|
|
2166
|
+
},
|
|
2167
|
+
};
|
|
2168
|
+
|
|
2169
|
+
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
2170
|
+
|
|
2171
|
+
expect(removeTaskSpy).toHaveBeenCalled();
|
|
2172
|
+
});
|
|
2173
|
+
|
|
2174
|
+
it('should remove OUTDIAL task on CONTACT_ENDED when agentsPendingWrapUp is undefined', () => {
|
|
2175
|
+
const task = taskManager.getTask(taskId);
|
|
2176
|
+
Object.assign(task.data, {
|
|
2177
|
+
interaction: {
|
|
2178
|
+
...task.data.interaction,
|
|
2179
|
+
outboundType: 'OUTDIAL',
|
|
2180
|
+
state: 'new',
|
|
2181
|
+
mediaType: 'telephony',
|
|
2182
|
+
},
|
|
2183
|
+
});
|
|
2184
|
+
task.unregisterWebCallListeners = jest.fn();
|
|
2185
|
+
const removeTaskSpy = jest.spyOn(taskManager, 'removeTaskFromCollection');
|
|
2186
|
+
|
|
2187
|
+
const payload = {
|
|
2188
|
+
data: {
|
|
2189
|
+
type: CC_EVENTS.CONTACT_ENDED,
|
|
2190
|
+
interactionId: taskId,
|
|
2191
|
+
interaction: {
|
|
2192
|
+
outboundType: 'OUTDIAL',
|
|
2193
|
+
state: 'new',
|
|
2194
|
+
mediaType: 'telephony',
|
|
2195
|
+
},
|
|
2196
|
+
// agentsPendingWrapUp not included
|
|
2197
|
+
},
|
|
2198
|
+
};
|
|
2199
|
+
|
|
2200
|
+
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
2201
|
+
|
|
2202
|
+
expect(removeTaskSpy).toHaveBeenCalled();
|
|
2203
|
+
});
|
|
2204
|
+
|
|
2205
|
+
it('should handle CONTACT_ENDED gracefully when task is undefined', () => {
|
|
2206
|
+
const payload = {
|
|
2207
|
+
data: {
|
|
2208
|
+
type: CC_EVENTS.CONTACT_ENDED,
|
|
2209
|
+
interactionId: 'non-existent-task-id',
|
|
2210
|
+
interaction: {
|
|
2211
|
+
state: 'new',
|
|
2212
|
+
},
|
|
2213
|
+
},
|
|
2214
|
+
};
|
|
2215
|
+
// Should not throw error when task doesn't exist
|
|
2216
|
+
expect(() => {
|
|
2217
|
+
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
2218
|
+
}).not.toThrow();
|
|
2219
|
+
});
|
|
2220
|
+
|
|
2221
|
+
it('should remove OUTDIAL task from taskCollection on AGENT_CONTACT_ASSIGN_FAILED when NOT terminated (user-declined)', () => {
|
|
2222
|
+
const task = taskManager.getTask(taskId);
|
|
2223
|
+
Object.assign(task.data, {
|
|
2224
|
+
interaction: {
|
|
2225
|
+
...task.data.interaction,
|
|
2226
|
+
outboundType: 'OUTDIAL',
|
|
2227
|
+
state: 'new',
|
|
2228
|
+
isTerminated: false,
|
|
2229
|
+
},
|
|
2230
|
+
});
|
|
2231
|
+
task.unregisterWebCallListeners = jest.fn();
|
|
2232
|
+
const removeTaskSpy = jest.spyOn(taskManager, 'removeTaskFromCollection');
|
|
2233
|
+
|
|
2234
|
+
const payload = {
|
|
2235
|
+
data: {
|
|
2236
|
+
type: CC_EVENTS.AGENT_CONTACT_ASSIGN_FAILED,
|
|
2237
|
+
agentId: '723a8ffb-a26e-496d-b14a-ff44fb83b64f',
|
|
2238
|
+
eventTime: 1733211616959,
|
|
2239
|
+
eventType: 'RoutingMessage',
|
|
2240
|
+
interaction: {
|
|
2241
|
+
outboundType: 'OUTDIAL',
|
|
2242
|
+
state: 'new',
|
|
2243
|
+
isTerminated: false,
|
|
2244
|
+
},
|
|
2245
|
+
interactionId: taskId,
|
|
2246
|
+
orgId: '6ecef209-9a34-4ed1-a07a-7ddd1dbe925a',
|
|
2247
|
+
trackingId: '575c0ec2-618c-42af-a61c-53aeb0a221ee',
|
|
2248
|
+
mediaResourceId: '0ae913a4-c857-4705-8d49-76dd3dde75e4',
|
|
2249
|
+
destAgentId: 'ebeb893b-ba67-4f36-8418-95c7492b28c2',
|
|
2250
|
+
owner: '723a8ffb-a26e-496d-b14a-ff44fb83b64f',
|
|
2251
|
+
queueMgr: 'aqm',
|
|
2252
|
+
reason: 'USER_DECLINED',
|
|
2253
|
+
reasonCode: 156,
|
|
2254
|
+
},
|
|
2255
|
+
};
|
|
2256
|
+
|
|
2257
|
+
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
2258
|
+
|
|
2259
|
+
expect(taskManager.getTask(taskId)).toBeUndefined();
|
|
2260
|
+
expect(removeTaskSpy).toHaveBeenCalled();
|
|
2261
|
+
=======
|
|
2262
|
+
>>>>>>> 81672300901738f14289fd2a7414c6c4b1389aa6
|
|
2263
|
+
});
|
|
2264
|
+
|
|
2265
|
+
it('handle AGENT_OFFER_CONSULT event', () => {
|
|
2266
|
+
const payload = {
|
|
2267
|
+
data: {
|
|
2268
|
+
...initalPayload.data,
|
|
2269
|
+
type: CC_EVENTS.AGENT_OFFER_CONSULT,
|
|
2270
|
+
},
|
|
2271
|
+
};
|
|
2272
|
+
|
|
2273
|
+
webSocketManagerMock.emit('message', JSON.stringify(initalPayload));
|
|
2274
|
+
const task = taskManager.getTask(taskId);
|
|
2275
|
+
const sendStateMachineEventSpy = jest.spyOn(task, 'sendStateMachineEvent');
|
|
2276
|
+
|
|
2277
|
+
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
2278
|
+
|
|
2279
|
+
const stateMachineEvent = expectLastStateMachineEvent(
|
|
2280
|
+
sendStateMachineEventSpy,
|
|
2281
|
+
TaskEvent.OFFER_CONSULT
|
|
2282
|
+
);
|
|
2283
|
+
expect(stateMachineEvent?.taskData).toEqual({...payload.data, isConsulted: true});
|
|
2284
|
+
sendStateMachineEventSpy.mockRestore();
|
|
2285
|
+
});
|
|
2286
|
+
|
|
2287
|
+
it('should emit TASK_CONSULT_ACCEPTED event on AGENT_CONSULTING event', () => {
|
|
2288
|
+
const initialConsultingPayload = {
|
|
2289
|
+
data: {
|
|
2290
|
+
...initalPayload.data,
|
|
2291
|
+
type: CC_EVENTS.AGENT_OFFER_CONSULT,
|
|
2292
|
+
},
|
|
2293
|
+
};
|
|
2294
|
+
|
|
2295
|
+
const consultingPayload = {
|
|
2296
|
+
data: {
|
|
2297
|
+
...initalPayload.data,
|
|
2298
|
+
type: CC_EVENTS.AGENT_CONSULTING,
|
|
2299
|
+
},
|
|
2300
|
+
};
|
|
2301
|
+
|
|
2302
|
+
webSocketManagerMock.emit('message', JSON.stringify(initalPayload));
|
|
2303
|
+
|
|
2304
|
+
const task = taskManager.getTask(taskId);
|
|
2305
|
+
const sendStateMachineEventSpy = jest.spyOn(task, 'sendStateMachineEvent');
|
|
2306
|
+
|
|
2307
|
+
webSocketManagerMock.emit('message', JSON.stringify(initialConsultingPayload));
|
|
2308
|
+
webSocketManagerMock.emit('message', JSON.stringify(consultingPayload));
|
|
2309
|
+
const stateMachineEvent = expectLastStateMachineEvent(
|
|
2310
|
+
sendStateMachineEventSpy,
|
|
2311
|
+
TaskEvent.CONSULTING_ACTIVE
|
|
2312
|
+
);
|
|
2313
|
+
expect(stateMachineEvent?.taskData).toEqual(consultingPayload.data);
|
|
2314
|
+
sendStateMachineEventSpy.mockRestore();
|
|
2315
|
+
});
|
|
2316
|
+
|
|
2317
|
+
it('should emit TASK_CONSULT_ENDED event on AGENT_CONSULT_ENDED event', () => {
|
|
2318
|
+
const payload = {
|
|
2319
|
+
data: {
|
|
2320
|
+
...initalPayload.data,
|
|
2321
|
+
type: CC_EVENTS.AGENT_CONSULT_ENDED,
|
|
2322
|
+
},
|
|
2323
|
+
};
|
|
2324
|
+
|
|
2325
|
+
webSocketManagerMock.emit('message', JSON.stringify(initalPayload));
|
|
2326
|
+
taskManager.getTask(taskId).data.isConsulted = true;
|
|
2327
|
+
const task = taskManager.getTask(taskId);
|
|
2328
|
+
const sendStateMachineEventSpy = jest.spyOn(task, 'sendStateMachineEvent');
|
|
2329
|
+
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
2330
|
+
<<<<<<< HEAD
|
|
2331
|
+
const stateMachineEvent = expectLastStateMachineEvent(
|
|
2332
|
+
sendStateMachineEventSpy,
|
|
2333
|
+
TaskEvent.CONSULT_END
|
|
2334
|
+
);
|
|
2335
|
+
expect(stateMachineEvent?.taskData).toEqual(payload.data);
|
|
2336
|
+
sendStateMachineEventSpy.mockRestore();
|
|
2337
|
+
=======
|
|
2338
|
+
expect(taskUpdateTaskDataSpy).toHaveBeenCalledWith(payload.data);
|
|
2339
|
+
expect(taskEmitSpy).toHaveBeenCalledWith(
|
|
2340
|
+
TASK_EVENTS.TASK_CONSULT_END,
|
|
2341
|
+
taskManager.getTask(taskId)
|
|
2342
|
+
);
|
|
2343
|
+
>>>>>>> 81672300901738f14289fd2a7414c6c4b1389aa6
|
|
2344
|
+
});
|
|
2345
|
+
|
|
2346
|
+
it('should emit TASK_CONSULT_ENDED event and remove currentTask when on AGENT_CONSULT_ENDED event when requested for a consult', () => {
|
|
2347
|
+
const payload = {
|
|
2348
|
+
data: {
|
|
2349
|
+
...initalPayload.data,
|
|
2350
|
+
type: CC_EVENTS.AGENT_CONSULT_ENDED,
|
|
1597
2351
|
},
|
|
1598
2352
|
};
|
|
1599
2353
|
|
|
@@ -1766,12 +2520,23 @@ describe('TaskManager', () => {
|
|
|
1766
2520
|
|
|
1767
2521
|
webSocketManagerMock.emit('message', JSON.stringify(assignFailedPayload));
|
|
1768
2522
|
|
|
2523
|
+
<<<<<<< HEAD
|
|
1769
2524
|
const stateMachineEvent = expectLastStateMachineEvent(
|
|
1770
2525
|
sendStateMachineEventSpy,
|
|
1771
2526
|
TaskEvent.ASSIGN_FAILED
|
|
1772
2527
|
);
|
|
1773
2528
|
expect(stateMachineEvent?.reason).toBe(assignFailedPayload.data.reason);
|
|
1774
2529
|
sendStateMachineEventSpy.mockRestore();
|
|
2530
|
+
=======
|
|
2531
|
+
expect(taskUpdateDataSpy).toHaveBeenCalledWith(assignFailedPayload.data);
|
|
2532
|
+
expect(taskEmitSpy).toHaveBeenCalledWith(
|
|
2533
|
+
TASK_EVENTS.TASK_REJECT,
|
|
2534
|
+
assignFailedPayload.data.reason
|
|
2535
|
+
);
|
|
2536
|
+
// Verify the correct metric event name is used for AGENT_CONTACT_ASSIGN_FAILED
|
|
2537
|
+
expect(metricsTrackSpy).toHaveBeenCalled();
|
|
2538
|
+
expect(metricsTrackSpy.mock.calls[0][0]).toBe('Agent Contact Assign Failed');
|
|
2539
|
+
>>>>>>> 81672300901738f14289fd2a7414c6c4b1389aa6
|
|
1775
2540
|
});
|
|
1776
2541
|
|
|
1777
2542
|
it('should remove currentTask from taskCollection on AGENT_WRAPPEDUP event', () => {
|
|
@@ -1837,6 +2602,7 @@ describe('TaskManager', () => {
|
|
|
1837
2602
|
taskManager.getTask(taskId).data.isConsulted = false;
|
|
1838
2603
|
const task = taskManager.getTask(taskId);
|
|
1839
2604
|
const sendStateMachineEventSpy = jest.spyOn(task, 'sendStateMachineEvent');
|
|
2605
|
+
const taskManagerEmitSpy = jest.spyOn(taskManager, 'emit');
|
|
1840
2606
|
const consultingPayload = {
|
|
1841
2607
|
data: {
|
|
1842
2608
|
...initalPayload.data,
|
|
@@ -1845,8 +2611,20 @@ describe('TaskManager', () => {
|
|
|
1845
2611
|
},
|
|
1846
2612
|
};
|
|
1847
2613
|
webSocketManagerMock.emit('message', JSON.stringify(consultingPayload));
|
|
2614
|
+
<<<<<<< HEAD
|
|
1848
2615
|
expectLastStateMachineEvent(sendStateMachineEventSpy, TaskEvent.CONSULTING_ACTIVE);
|
|
2616
|
+
expect(taskManagerEmitSpy).toHaveBeenCalledWith(
|
|
2617
|
+
TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE,
|
|
2618
|
+
taskManager.getTask(taskId)
|
|
2619
|
+
);
|
|
1849
2620
|
sendStateMachineEventSpy.mockRestore();
|
|
2621
|
+
=======
|
|
2622
|
+
expect(taskEmitSpy).toHaveBeenCalledWith(CC_EVENTS.AGENT_CONSULTING, consultingPayload.data);
|
|
2623
|
+
expect(taskEmitSpy).toHaveBeenCalledWith(
|
|
2624
|
+
TASK_EVENTS.TASK_CONSULTING,
|
|
2625
|
+
taskManager.getTask(taskId)
|
|
2626
|
+
);
|
|
2627
|
+
>>>>>>> 81672300901738f14289fd2a7414c6c4b1389aa6
|
|
1850
2628
|
});
|
|
1851
2629
|
|
|
1852
2630
|
it('should update task data on AGENT_CONTACT_UNASSIGNED', () => {
|
|
@@ -1873,6 +2651,7 @@ describe('TaskManager', () => {
|
|
|
1873
2651
|
},
|
|
1874
2652
|
};
|
|
1875
2653
|
webSocketManagerMock.emit('message', JSON.stringify(unassignedPayload));
|
|
2654
|
+
<<<<<<< HEAD
|
|
1876
2655
|
expect(sendStateMachineEventSpy).not.toHaveBeenCalled();
|
|
1877
2656
|
expect(updateTaskDataSpy).toHaveBeenCalledWith(unassignedPayload.data);
|
|
1878
2657
|
});
|
|
@@ -1943,6 +2722,13 @@ describe('TaskManager', () => {
|
|
|
1943
2722
|
destinationType: null,
|
|
1944
2723
|
})
|
|
1945
2724
|
);
|
|
2725
|
+
=======
|
|
2726
|
+
expect(taskEmitSpy).toHaveBeenCalledWith(
|
|
2727
|
+
CC_EVENTS.AGENT_CONTACT_UNASSIGNED,
|
|
2728
|
+
unassignedPayload.data
|
|
2729
|
+
);
|
|
2730
|
+
expect(taskEmitSpy).toHaveBeenCalledWith(TASK_EVENTS.TASK_END, taskManager.getTask(taskId));
|
|
2731
|
+
>>>>>>> 81672300901738f14289fd2a7414c6c4b1389aa6
|
|
1946
2732
|
});
|
|
1947
2733
|
|
|
1948
2734
|
it('should handle chat interaction and emit TASK_INCOMING immediately', () => {
|
|
@@ -1954,6 +2740,11 @@ describe('TaskManager', () => {
|
|
|
1954
2740
|
},
|
|
1955
2741
|
};
|
|
1956
2742
|
|
|
2743
|
+
<<<<<<< HEAD
|
|
2744
|
+
=======
|
|
2745
|
+
const taskIncomingSpy = jest.spyOn(taskManager, 'emit');
|
|
2746
|
+
|
|
2747
|
+
>>>>>>> 81672300901738f14289fd2a7414c6c4b1389aa6
|
|
1957
2748
|
// Simulate receiving a chat task
|
|
1958
2749
|
webSocketManagerMock.emit('message', JSON.stringify(chatPayload));
|
|
1959
2750
|
|
|
@@ -1976,6 +2767,11 @@ describe('TaskManager', () => {
|
|
|
1976
2767
|
},
|
|
1977
2768
|
};
|
|
1978
2769
|
|
|
2770
|
+
<<<<<<< HEAD
|
|
2771
|
+
=======
|
|
2772
|
+
const taskIncomingSpy = jest.spyOn(taskManager, 'emit');
|
|
2773
|
+
|
|
2774
|
+
>>>>>>> 81672300901738f14289fd2a7414c6c4b1389aa6
|
|
1979
2775
|
// Simulate receiving an email task
|
|
1980
2776
|
webSocketManagerMock.emit('message', JSON.stringify(emailPayload));
|
|
1981
2777
|
|
|
@@ -1999,11 +2795,21 @@ describe('TaskManager', () => {
|
|
|
1999
2795
|
},
|
|
2000
2796
|
};
|
|
2001
2797
|
|
|
2798
|
+
<<<<<<< HEAD
|
|
2002
2799
|
webSocketManagerMock.emit('message', JSON.stringify(chatReservedPayload));
|
|
2003
2800
|
const task = taskManager.getTask(chatReservedPayload.data.interactionId);
|
|
2004
2801
|
const sendStateMachineEventSpy = task.sendStateMachineEvent as jest.Mock;
|
|
2005
2802
|
|
|
2006
2803
|
expectLastStateMachineEvent(sendStateMachineEventSpy, TaskEvent.TASK_INCOMING);
|
|
2804
|
+
=======
|
|
2805
|
+
const taskIncomingSpy = jest.spyOn(taskManager, 'emit');
|
|
2806
|
+
webSocketManagerMock.emit('message', JSON.stringify(chatReservedPayload));
|
|
2807
|
+
|
|
2808
|
+
expect(taskIncomingSpy).toHaveBeenCalledWith(
|
|
2809
|
+
TASK_EVENTS.TASK_INCOMING,
|
|
2810
|
+
taskManager.getTask(chatReservedPayload.data.interactionId)
|
|
2811
|
+
);
|
|
2812
|
+
>>>>>>> 81672300901738f14289fd2a7414c6c4b1389aa6
|
|
2007
2813
|
|
|
2008
2814
|
// 2. Chat task is assigned
|
|
2009
2815
|
const chatAssignedPayload = {
|
|
@@ -2013,17 +2819,29 @@ describe('TaskManager', () => {
|
|
|
2013
2819
|
},
|
|
2014
2820
|
};
|
|
2015
2821
|
|
|
2822
|
+
<<<<<<< HEAD
|
|
2016
2823
|
webSocketManagerMock.emit('message', JSON.stringify(chatAssignedPayload));
|
|
2017
2824
|
|
|
2018
2825
|
expectLastStateMachineEvent(sendStateMachineEventSpy, TaskEvent.ASSIGN);
|
|
2826
|
+
=======
|
|
2827
|
+
const task = taskManager.getTask(chatReservedPayload.data.interactionId);
|
|
2828
|
+
const taskEmitSpy = jest.spyOn(task, 'emit');
|
|
2019
2829
|
|
|
2020
|
-
|
|
2830
|
+
webSocketManagerMock.emit('message', JSON.stringify(chatAssignedPayload));
|
|
2831
|
+
|
|
2832
|
+
expect(taskEmitSpy).toHaveBeenCalledWith(TASK_EVENTS.TASK_ASSIGNED, task);
|
|
2833
|
+
>>>>>>> 81672300901738f14289fd2a7414c6c4b1389aa6
|
|
2834
|
+
|
|
2835
|
+
// 3. Chat task is ended with state 'new' to trigger cleanup
|
|
2021
2836
|
const chatEndedPayload = {
|
|
2022
2837
|
data: {
|
|
2023
2838
|
...chatReservedPayload.data,
|
|
2024
2839
|
type: CC_EVENTS.CONTACT_ENDED,
|
|
2025
2840
|
interaction: {mediaType: 'chat', state: 'new'}, // Change to 'new' state
|
|
2841
|
+
<<<<<<< HEAD
|
|
2026
2842
|
wrapUpRequired: false,
|
|
2843
|
+
=======
|
|
2844
|
+
>>>>>>> 81672300901738f14289fd2a7414c6c4b1389aa6
|
|
2027
2845
|
},
|
|
2028
2846
|
};
|
|
2029
2847
|
|
|
@@ -2120,8 +2938,18 @@ describe('TaskManager', () => {
|
|
|
2120
2938
|
expect(taskManager.getAllTasks()).toHaveProperty(task2Payload.data.interactionId);
|
|
2121
2939
|
expect(taskManager.getAllTasks()).toHaveProperty(task3Payload.data.interactionId);
|
|
2122
2940
|
|
|
2941
|
+
<<<<<<< HEAD
|
|
2123
2942
|
const task2 = taskManager.getTask(task2Payload.data.interactionId);
|
|
2124
2943
|
const task2SendStateMachineEventSpy = task2.sendStateMachineEvent as jest.Mock;
|
|
2944
|
+
=======
|
|
2945
|
+
// Create spies for all tasks
|
|
2946
|
+
const task1EmitSpy = jest.spyOn(taskManager.getTask(task1Payload.data.interactionId), 'emit');
|
|
2947
|
+
const task2EmitSpy = jest.spyOn(taskManager.getTask(task2Payload.data.interactionId), 'emit');
|
|
2948
|
+
const task3EmitSpy = jest.spyOn(taskManager.getTask(task3Payload.data.interactionId), 'emit');
|
|
2949
|
+
|
|
2950
|
+
// Store reference to task2 before it gets removed
|
|
2951
|
+
const task2 = taskManager.getTask(task2Payload.data.interactionId);
|
|
2952
|
+
>>>>>>> 81672300901738f14289fd2a7414c6c4b1389aa6
|
|
2125
2953
|
|
|
2126
2954
|
// End only the second task (chat task)
|
|
2127
2955
|
const chatEndedPayload = {
|
|
@@ -2129,6 +2957,7 @@ describe('TaskManager', () => {
|
|
|
2129
2957
|
...task2Payload.data,
|
|
2130
2958
|
type: CC_EVENTS.CONTACT_ENDED,
|
|
2131
2959
|
interaction: {mediaType: 'chat', state: 'new'}, // Using 'new' to trigger cleanup
|
|
2960
|
+
<<<<<<< HEAD
|
|
2132
2961
|
wrapUpRequired: false,
|
|
2133
2962
|
},
|
|
2134
2963
|
};
|
|
@@ -2141,6 +2970,17 @@ describe('TaskManager', () => {
|
|
|
2141
2970
|
TaskEvent.CONTACT_ENDED
|
|
2142
2971
|
);
|
|
2143
2972
|
expect(firstEndEvent?.taskData).toEqual(chatEndedPayload.data);
|
|
2973
|
+
=======
|
|
2974
|
+
},
|
|
2975
|
+
};
|
|
2976
|
+
|
|
2977
|
+
webSocketManagerMock.emit('message', JSON.stringify(chatEndedPayload));
|
|
2978
|
+
|
|
2979
|
+
// Verify only task2 emitted TASK_END
|
|
2980
|
+
expect(task1EmitSpy).not.toHaveBeenCalledWith(TASK_EVENTS.TASK_END);
|
|
2981
|
+
expect(task2EmitSpy).toHaveBeenCalledWith(TASK_EVENTS.TASK_END, task2);
|
|
2982
|
+
expect(task3EmitSpy).not.toHaveBeenCalledWith(TASK_EVENTS.TASK_END);
|
|
2983
|
+
>>>>>>> 81672300901738f14289fd2a7414c6c4b1389aa6
|
|
2144
2984
|
|
|
2145
2985
|
// Verify task2 was removed from collection (since state was 'new')
|
|
2146
2986
|
expect(taskManager.getTask(task2Payload.data.interactionId)).toBeUndefined();
|
|
@@ -2151,7 +2991,10 @@ describe('TaskManager', () => {
|
|
|
2151
2991
|
|
|
2152
2992
|
// Store reference to task3 before we end it
|
|
2153
2993
|
const task3 = taskManager.getTask(task3Payload.data.interactionId);
|
|
2994
|
+
<<<<<<< HEAD
|
|
2154
2995
|
const task3SendStateMachineEventSpy = task3.sendStateMachineEvent as jest.Mock;
|
|
2996
|
+
=======
|
|
2997
|
+
>>>>>>> 81672300901738f14289fd2a7414c6c4b1389aa6
|
|
2155
2998
|
|
|
2156
2999
|
// Now end task3 with a state that doesn't trigger cleanup
|
|
2157
3000
|
const emailEndedPayload = {
|
|
@@ -2159,6 +3002,7 @@ describe('TaskManager', () => {
|
|
|
2159
3002
|
...task3Payload.data,
|
|
2160
3003
|
type: CC_EVENTS.CONTACT_ENDED,
|
|
2161
3004
|
interaction: {mediaType: 'email', state: 'connected'}, // Using 'connected' to NOT trigger cleanup
|
|
3005
|
+
<<<<<<< HEAD
|
|
2162
3006
|
wrapUpRequired: true,
|
|
2163
3007
|
},
|
|
2164
3008
|
};
|
|
@@ -2171,6 +3015,15 @@ describe('TaskManager', () => {
|
|
|
2171
3015
|
TaskEvent.CONTACT_ENDED
|
|
2172
3016
|
);
|
|
2173
3017
|
expect(secondEndEvent?.taskData).toEqual(emailEndedPayload.data);
|
|
3018
|
+
=======
|
|
3019
|
+
},
|
|
3020
|
+
};
|
|
3021
|
+
|
|
3022
|
+
webSocketManagerMock.emit('message', JSON.stringify(emailEndedPayload));
|
|
3023
|
+
|
|
3024
|
+
// Verify task3 emitted TASK_END
|
|
3025
|
+
expect(task3EmitSpy).toHaveBeenCalledWith(TASK_EVENTS.TASK_END, task3);
|
|
3026
|
+
>>>>>>> 81672300901738f14289fd2a7414c6c4b1389aa6
|
|
2174
3027
|
|
|
2175
3028
|
// Verify task3 is still in collection (since state was 'connected')
|
|
2176
3029
|
expect(taskManager.getTask(task3Payload.data.interactionId)).toBeDefined();
|
|
@@ -2182,8 +3035,17 @@ describe('TaskManager', () => {
|
|
|
2182
3035
|
it('should emit TRANSFER_SUCCESS event on AGENT_VTEAM_TRANSFERRED event', () => {
|
|
2183
3036
|
// First create a task by emitting the initial payload
|
|
2184
3037
|
webSocketManagerMock.emit('message', JSON.stringify(initalPayload));
|
|
3038
|
+
<<<<<<< HEAD
|
|
2185
3039
|
const task = taskManager.getTask(taskId);
|
|
2186
3040
|
const sendStateMachineEventSpy = jest.spyOn(task, 'sendStateMachineEvent');
|
|
3041
|
+
=======
|
|
3042
|
+
|
|
3043
|
+
// Get a reference to the task from taskCollection
|
|
3044
|
+
const task = taskManager.getTask(taskId);
|
|
3045
|
+
|
|
3046
|
+
// Now spy on the task's emit method
|
|
3047
|
+
const taskEmitSpy = jest.spyOn(task, 'emit');
|
|
3048
|
+
>>>>>>> 81672300901738f14289fd2a7414c6c4b1389aa6
|
|
2187
3049
|
|
|
2188
3050
|
const vteamTransferredPayload = {
|
|
2189
3051
|
data: {
|
|
@@ -2207,9 +3069,14 @@ describe('TaskManager', () => {
|
|
|
2207
3069
|
|
|
2208
3070
|
webSocketManagerMock.emit('message', JSON.stringify(vteamTransferredPayload));
|
|
2209
3071
|
|
|
3072
|
+
<<<<<<< HEAD
|
|
2210
3073
|
// Check that the state machine received the END event
|
|
2211
3074
|
expectLastStateMachineEvent(sendStateMachineEventSpy, TaskEvent.TRANSFER_SUCCESS);
|
|
2212
3075
|
sendStateMachineEventSpy.mockRestore();
|
|
3076
|
+
=======
|
|
3077
|
+
// Check that task.emit was called with TASK_END event
|
|
3078
|
+
expect(taskEmitSpy).toHaveBeenCalledWith(TASK_EVENTS.TASK_END, task);
|
|
3079
|
+
>>>>>>> 81672300901738f14289fd2a7414c6c4b1389aa6
|
|
2213
3080
|
|
|
2214
3081
|
// The task should still exist in the collection based on current implementation
|
|
2215
3082
|
expect(taskManager.getTask(taskId)).toBeDefined();
|
|
@@ -2224,7 +3091,14 @@ describe('TaskManager', () => {
|
|
|
2224
3091
|
},
|
|
2225
3092
|
};
|
|
2226
3093
|
const task = taskManager.getTask(taskId);
|
|
3094
|
+
<<<<<<< HEAD
|
|
2227
3095
|
const sendStateMachineEventSpy = jest.spyOn(task, 'sendStateMachineEvent');
|
|
3096
|
+
=======
|
|
3097
|
+
const updateSpy = jest.spyOn(task, 'updateTaskData').mockImplementation((data) => {
|
|
3098
|
+
task.data = {...(task.data || {}), ...(data || {})};
|
|
3099
|
+
return task;
|
|
3100
|
+
});
|
|
3101
|
+
>>>>>>> 81672300901738f14289fd2a7414c6c4b1389aa6
|
|
2228
3102
|
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
2229
3103
|
expect(sendStateMachineEventSpy).toHaveBeenCalled();
|
|
2230
3104
|
const stateMachineEvent = sendStateMachineEventSpy.mock.calls.at(-1)?.[0];
|
|
@@ -2345,6 +3219,7 @@ describe('TaskManager', () => {
|
|
|
2345
3219
|
|
|
2346
3220
|
describe('Conference event handling', () => {
|
|
2347
3221
|
let task;
|
|
3222
|
+
<<<<<<< HEAD
|
|
2348
3223
|
|
|
2349
3224
|
beforeEach(() => {
|
|
2350
3225
|
task = {
|
|
@@ -2352,6 +3227,22 @@ describe('TaskManager', () => {
|
|
|
2352
3227
|
emit: jest.fn(),
|
|
2353
3228
|
updateTaskData: jest.fn(),
|
|
2354
3229
|
sendStateMachineEvent: jest.fn(),
|
|
3230
|
+
=======
|
|
3231
|
+
const agentId = '723a8ffb-a26e-496d-b14a-ff44fb83b64f';
|
|
3232
|
+
|
|
3233
|
+
beforeEach(() => {
|
|
3234
|
+
// Set the agentId on taskManager before tests run
|
|
3235
|
+
taskManager.setAgentId(agentId);
|
|
3236
|
+
|
|
3237
|
+
task = {
|
|
3238
|
+
data: {interactionId: taskId},
|
|
3239
|
+
emit: jest.fn(),
|
|
3240
|
+
updateTaskData: jest.fn().mockImplementation((updatedData) => {
|
|
3241
|
+
// Mock the updateTaskData method to actually update task.data
|
|
3242
|
+
task.data = {...task.data, ...updatedData};
|
|
3243
|
+
return task;
|
|
3244
|
+
}),
|
|
3245
|
+
>>>>>>> 81672300901738f14289fd2a7414c6c4b1389aa6
|
|
2355
3246
|
};
|
|
2356
3247
|
taskManager.taskCollection[taskId] = task as any;
|
|
2357
3248
|
});
|
|
@@ -2388,7 +3279,28 @@ describe('TaskManager', () => {
|
|
|
2388
3279
|
|
|
2389
3280
|
it('sends PARTICIPANT_LEFT_CONFERENCE to state machine as PARTICIPANT_LEAVE', () => {
|
|
2390
3281
|
const payload = {
|
|
3282
|
+
<<<<<<< HEAD
|
|
2391
3283
|
data: {type: CC_EVENTS.PARTICIPANT_LEFT_CONFERENCE, interactionId: taskId},
|
|
3284
|
+
=======
|
|
3285
|
+
data: {
|
|
3286
|
+
type: CC_EVENTS.PARTICIPANT_JOINED_CONFERENCE,
|
|
3287
|
+
interactionId: taskId,
|
|
3288
|
+
participantId: 'new-participant-123',
|
|
3289
|
+
participantType: 'agent',
|
|
3290
|
+
interaction: {
|
|
3291
|
+
participants: {
|
|
3292
|
+
[agentId]: {pType: 'Agent', hasLeft: false},
|
|
3293
|
+
'new-participant-123': {pType: 'Agent', hasLeft: false},
|
|
3294
|
+
},
|
|
3295
|
+
media: {
|
|
3296
|
+
[taskId]: {
|
|
3297
|
+
mType: 'mainCall',
|
|
3298
|
+
participants: [agentId, 'new-participant-123'],
|
|
3299
|
+
},
|
|
3300
|
+
},
|
|
3301
|
+
},
|
|
3302
|
+
},
|
|
3303
|
+
>>>>>>> 81672300901738f14289fd2a7414c6c4b1389aa6
|
|
2392
3304
|
};
|
|
2393
3305
|
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
2394
3306
|
expect(task.sendStateMachineEvent).toHaveBeenCalled();
|
|
@@ -2396,7 +3308,346 @@ describe('TaskManager', () => {
|
|
|
2396
3308
|
expect(call.type).toBe(TaskEvent.PARTICIPANT_LEAVE);
|
|
2397
3309
|
});
|
|
2398
3310
|
|
|
3311
|
+
<<<<<<< HEAD
|
|
2399
3312
|
it('handles AGENT_CONSULT_CONFERENCING event without errors', () => {
|
|
3313
|
+
=======
|
|
3314
|
+
it('should call updateTaskData only once for PARTICIPANT_JOINED_CONFERENCE with pre-calculated isConferenceInProgress', () => {
|
|
3315
|
+
const payload = {
|
|
3316
|
+
data: {
|
|
3317
|
+
type: CC_EVENTS.PARTICIPANT_JOINED_CONFERENCE,
|
|
3318
|
+
interactionId: taskId,
|
|
3319
|
+
participantId: 'new-agent-789',
|
|
3320
|
+
interaction: {
|
|
3321
|
+
participants: {
|
|
3322
|
+
[agentId]: {pType: 'Agent', hasLeft: false},
|
|
3323
|
+
'agent-2': {pType: 'Agent', hasLeft: false},
|
|
3324
|
+
'new-agent-789': {pType: 'Agent', hasLeft: false},
|
|
3325
|
+
'customer-1': {pType: 'Customer', hasLeft: false},
|
|
3326
|
+
},
|
|
3327
|
+
media: {
|
|
3328
|
+
[taskId]: {
|
|
3329
|
+
mType: 'mainCall',
|
|
3330
|
+
participants: [agentId, 'agent-2', 'new-agent-789', 'customer-1'],
|
|
3331
|
+
},
|
|
3332
|
+
},
|
|
3333
|
+
},
|
|
3334
|
+
},
|
|
3335
|
+
};
|
|
3336
|
+
|
|
3337
|
+
const updateTaskDataSpy = jest.spyOn(task, 'updateTaskData');
|
|
3338
|
+
|
|
3339
|
+
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
3340
|
+
|
|
3341
|
+
// Verify updateTaskData was called exactly once
|
|
3342
|
+
expect(updateTaskDataSpy).toHaveBeenCalledTimes(1);
|
|
3343
|
+
|
|
3344
|
+
// Verify it was called with isConferenceInProgress already calculated
|
|
3345
|
+
expect(updateTaskDataSpy).toHaveBeenCalledWith(
|
|
3346
|
+
expect.objectContaining({
|
|
3347
|
+
participantId: 'new-agent-789',
|
|
3348
|
+
isConferenceInProgress: true, // 3 active agents
|
|
3349
|
+
})
|
|
3350
|
+
);
|
|
3351
|
+
|
|
3352
|
+
expect(task.emit).toHaveBeenCalledWith(TASK_EVENTS.TASK_PARTICIPANT_JOINED, task);
|
|
3353
|
+
});
|
|
3354
|
+
|
|
3355
|
+
describe('PARTICIPANT_LEFT_CONFERENCE event handling', () => {
|
|
3356
|
+
it('should call updateTaskData only once for PARTICIPANT_LEFT_CONFERENCE with pre-calculated isConferenceInProgress', () => {
|
|
3357
|
+
const payload = {
|
|
3358
|
+
data: {
|
|
3359
|
+
type: CC_EVENTS.PARTICIPANT_LEFT_CONFERENCE,
|
|
3360
|
+
interactionId: taskId,
|
|
3361
|
+
interaction: {
|
|
3362
|
+
participants: {
|
|
3363
|
+
[agentId]: {pType: 'Agent', hasLeft: false},
|
|
3364
|
+
'agent-2': {pType: 'Agent', hasLeft: true}, // This agent left
|
|
3365
|
+
'customer-1': {pType: 'Customer', hasLeft: false},
|
|
3366
|
+
},
|
|
3367
|
+
media: {
|
|
3368
|
+
[taskId]: {
|
|
3369
|
+
mType: 'mainCall',
|
|
3370
|
+
participants: [agentId, 'customer-1'], // agent-2 removed from participants
|
|
3371
|
+
},
|
|
3372
|
+
},
|
|
3373
|
+
},
|
|
3374
|
+
},
|
|
3375
|
+
};
|
|
3376
|
+
|
|
3377
|
+
const updateTaskDataSpy = jest.spyOn(task, 'updateTaskData');
|
|
3378
|
+
|
|
3379
|
+
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
3380
|
+
|
|
3381
|
+
// Verify updateTaskData was called exactly once
|
|
3382
|
+
expect(updateTaskDataSpy).toHaveBeenCalledTimes(1);
|
|
3383
|
+
|
|
3384
|
+
// Verify it was called with isConferenceInProgress already calculated
|
|
3385
|
+
expect(updateTaskDataSpy).toHaveBeenCalledWith(
|
|
3386
|
+
expect.objectContaining({
|
|
3387
|
+
isConferenceInProgress: false, // Only 1 active agent remains
|
|
3388
|
+
})
|
|
3389
|
+
);
|
|
3390
|
+
|
|
3391
|
+
expect(task.emit).toHaveBeenCalledWith(TASK_EVENTS.TASK_PARTICIPANT_LEFT, task);
|
|
3392
|
+
});
|
|
3393
|
+
|
|
3394
|
+
it('should emit TASK_PARTICIPANT_LEFT event when participant leaves conference', () => {
|
|
3395
|
+
const payload = {
|
|
3396
|
+
data: {
|
|
3397
|
+
type: CC_EVENTS.PARTICIPANT_LEFT_CONFERENCE,
|
|
3398
|
+
interactionId: taskId,
|
|
3399
|
+
interaction: {
|
|
3400
|
+
participants: {
|
|
3401
|
+
[agentId]: {
|
|
3402
|
+
hasLeft: false,
|
|
3403
|
+
},
|
|
3404
|
+
},
|
|
3405
|
+
},
|
|
3406
|
+
},
|
|
3407
|
+
};
|
|
3408
|
+
|
|
3409
|
+
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
3410
|
+
|
|
3411
|
+
expect(task.emit).toHaveBeenCalledWith(TASK_EVENTS.TASK_PARTICIPANT_LEFT, task);
|
|
3412
|
+
});
|
|
3413
|
+
|
|
3414
|
+
it('should NOT remove task when agent is still in interaction', () => {
|
|
3415
|
+
const payload = {
|
|
3416
|
+
data: {
|
|
3417
|
+
type: CC_EVENTS.PARTICIPANT_LEFT_CONFERENCE,
|
|
3418
|
+
interactionId: taskId,
|
|
3419
|
+
interaction: {
|
|
3420
|
+
participants: {
|
|
3421
|
+
[agentId]: {
|
|
3422
|
+
hasLeft: false,
|
|
3423
|
+
},
|
|
3424
|
+
},
|
|
3425
|
+
},
|
|
3426
|
+
},
|
|
3427
|
+
};
|
|
3428
|
+
|
|
3429
|
+
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
3430
|
+
|
|
3431
|
+
// Task should still exist in collection
|
|
3432
|
+
expect(taskManager.getTask(taskId)).toBeDefined();
|
|
3433
|
+
expect(task.emit).toHaveBeenCalledWith(TASK_EVENTS.TASK_PARTICIPANT_LEFT, task);
|
|
3434
|
+
});
|
|
3435
|
+
|
|
3436
|
+
it('should NOT remove task when agent left but is in main interaction', () => {
|
|
3437
|
+
const payload = {
|
|
3438
|
+
data: {
|
|
3439
|
+
type: CC_EVENTS.PARTICIPANT_LEFT_CONFERENCE,
|
|
3440
|
+
interactionId: taskId,
|
|
3441
|
+
interaction: {
|
|
3442
|
+
participants: {
|
|
3443
|
+
[agentId]: {
|
|
3444
|
+
hasLeft: true,
|
|
3445
|
+
},
|
|
3446
|
+
},
|
|
3447
|
+
media: {
|
|
3448
|
+
[taskId]: {
|
|
3449
|
+
mType: 'mainCall',
|
|
3450
|
+
participants: [agentId],
|
|
3451
|
+
},
|
|
3452
|
+
},
|
|
3453
|
+
},
|
|
3454
|
+
},
|
|
3455
|
+
};
|
|
3456
|
+
|
|
3457
|
+
const removeTaskSpy = jest.spyOn(taskManager, 'removeTaskFromCollection');
|
|
3458
|
+
|
|
3459
|
+
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
3460
|
+
|
|
3461
|
+
// Task should still exist - not removed
|
|
3462
|
+
expect(removeTaskSpy).not.toHaveBeenCalled();
|
|
3463
|
+
expect(taskManager.getTask(taskId)).toBeDefined();
|
|
3464
|
+
expect(task.emit).toHaveBeenCalledWith(TASK_EVENTS.TASK_PARTICIPANT_LEFT, task);
|
|
3465
|
+
});
|
|
3466
|
+
|
|
3467
|
+
it('should NOT remove task when agent left but is primary (owner)', () => {
|
|
3468
|
+
const payload = {
|
|
3469
|
+
data: {
|
|
3470
|
+
type: CC_EVENTS.PARTICIPANT_LEFT_CONFERENCE,
|
|
3471
|
+
interactionId: taskId,
|
|
3472
|
+
interaction: {
|
|
3473
|
+
participants: {
|
|
3474
|
+
[agentId]: {
|
|
3475
|
+
hasLeft: true,
|
|
3476
|
+
},
|
|
3477
|
+
},
|
|
3478
|
+
owner: agentId,
|
|
3479
|
+
media: {
|
|
3480
|
+
[taskId]: {
|
|
3481
|
+
mType: 'consultCall',
|
|
3482
|
+
participants: ['other-agent'],
|
|
3483
|
+
},
|
|
3484
|
+
},
|
|
3485
|
+
},
|
|
3486
|
+
},
|
|
3487
|
+
};
|
|
3488
|
+
|
|
3489
|
+
const removeTaskSpy = jest.spyOn(taskManager, 'removeTaskFromCollection');
|
|
3490
|
+
|
|
3491
|
+
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
3492
|
+
|
|
3493
|
+
// Task should still exist - not removed because agent is primary
|
|
3494
|
+
expect(removeTaskSpy).not.toHaveBeenCalled();
|
|
3495
|
+
expect(taskManager.getTask(taskId)).toBeDefined();
|
|
3496
|
+
expect(task.emit).toHaveBeenCalledWith(TASK_EVENTS.TASK_PARTICIPANT_LEFT, task);
|
|
3497
|
+
});
|
|
3498
|
+
|
|
3499
|
+
it('should remove task when agent left and is NOT in main interaction and is NOT primary', () => {
|
|
3500
|
+
const payload = {
|
|
3501
|
+
data: {
|
|
3502
|
+
type: CC_EVENTS.PARTICIPANT_LEFT_CONFERENCE,
|
|
3503
|
+
interactionId: taskId,
|
|
3504
|
+
interaction: {
|
|
3505
|
+
participants: {
|
|
3506
|
+
[agentId]: {
|
|
3507
|
+
hasLeft: true,
|
|
3508
|
+
},
|
|
3509
|
+
},
|
|
3510
|
+
owner: 'another-agent-id',
|
|
3511
|
+
media: {
|
|
3512
|
+
[taskId]: {
|
|
3513
|
+
mType: 'mainCall',
|
|
3514
|
+
participants: ['another-agent-id'],
|
|
3515
|
+
},
|
|
3516
|
+
},
|
|
3517
|
+
},
|
|
3518
|
+
},
|
|
3519
|
+
};
|
|
3520
|
+
|
|
3521
|
+
const removeTaskSpy = jest.spyOn(taskManager, 'removeTaskFromCollection');
|
|
3522
|
+
|
|
3523
|
+
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
3524
|
+
|
|
3525
|
+
// Task should be removed
|
|
3526
|
+
expect(removeTaskSpy).toHaveBeenCalled();
|
|
3527
|
+
expect(task.emit).toHaveBeenCalledWith(TASK_EVENTS.TASK_PARTICIPANT_LEFT, task);
|
|
3528
|
+
});
|
|
3529
|
+
|
|
3530
|
+
it('should remove task when agent is not in participants list', () => {
|
|
3531
|
+
const payload = {
|
|
3532
|
+
data: {
|
|
3533
|
+
type: CC_EVENTS.PARTICIPANT_LEFT_CONFERENCE,
|
|
3534
|
+
interactionId: taskId,
|
|
3535
|
+
interaction: {
|
|
3536
|
+
participants: {
|
|
3537
|
+
'other-agent-id': {
|
|
3538
|
+
hasLeft: false,
|
|
3539
|
+
},
|
|
3540
|
+
},
|
|
3541
|
+
owner: 'another-agent-id',
|
|
3542
|
+
},
|
|
3543
|
+
},
|
|
3544
|
+
};
|
|
3545
|
+
|
|
3546
|
+
const removeTaskSpy = jest.spyOn(taskManager, 'removeTaskFromCollection');
|
|
3547
|
+
|
|
3548
|
+
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
3549
|
+
|
|
3550
|
+
// Task should be removed because agent is not in participants
|
|
3551
|
+
expect(removeTaskSpy).toHaveBeenCalled();
|
|
3552
|
+
expect(task.emit).toHaveBeenCalledWith(TASK_EVENTS.TASK_PARTICIPANT_LEFT, task);
|
|
3553
|
+
});
|
|
3554
|
+
|
|
3555
|
+
it('should update isConferenceInProgress based on remaining active agents', () => {
|
|
3556
|
+
const payload = {
|
|
3557
|
+
data: {
|
|
3558
|
+
type: CC_EVENTS.PARTICIPANT_LEFT_CONFERENCE,
|
|
3559
|
+
interactionId: taskId,
|
|
3560
|
+
interaction: {
|
|
3561
|
+
participants: {
|
|
3562
|
+
[agentId]: {
|
|
3563
|
+
hasLeft: false,
|
|
3564
|
+
pType: 'Agent',
|
|
3565
|
+
},
|
|
3566
|
+
'agent-2': {
|
|
3567
|
+
hasLeft: false,
|
|
3568
|
+
pType: 'Agent',
|
|
3569
|
+
},
|
|
3570
|
+
'customer-1': {
|
|
3571
|
+
hasLeft: false,
|
|
3572
|
+
pType: 'Customer',
|
|
3573
|
+
},
|
|
3574
|
+
},
|
|
3575
|
+
media: {
|
|
3576
|
+
[taskId]: {
|
|
3577
|
+
mType: 'mainCall',
|
|
3578
|
+
participants: [agentId, 'agent-2', 'customer-1'],
|
|
3579
|
+
},
|
|
3580
|
+
},
|
|
3581
|
+
},
|
|
3582
|
+
},
|
|
3583
|
+
};
|
|
3584
|
+
|
|
3585
|
+
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
3586
|
+
|
|
3587
|
+
// isConferenceInProgress should be true (2 active agents)
|
|
3588
|
+
expect(task.data.isConferenceInProgress).toBe(true);
|
|
3589
|
+
expect(task.emit).toHaveBeenCalledWith(TASK_EVENTS.TASK_PARTICIPANT_LEFT, task);
|
|
3590
|
+
});
|
|
3591
|
+
|
|
3592
|
+
it('should set isConferenceInProgress to false when only one agent remains', () => {
|
|
3593
|
+
const payload = {
|
|
3594
|
+
data: {
|
|
3595
|
+
type: CC_EVENTS.PARTICIPANT_LEFT_CONFERENCE,
|
|
3596
|
+
interactionId: taskId,
|
|
3597
|
+
interaction: {
|
|
3598
|
+
participants: {
|
|
3599
|
+
[agentId]: {
|
|
3600
|
+
hasLeft: false,
|
|
3601
|
+
pType: 'Agent',
|
|
3602
|
+
},
|
|
3603
|
+
'agent-2': {
|
|
3604
|
+
hasLeft: true,
|
|
3605
|
+
pType: 'Agent',
|
|
3606
|
+
},
|
|
3607
|
+
'customer-1': {
|
|
3608
|
+
hasLeft: false,
|
|
3609
|
+
pType: 'Customer',
|
|
3610
|
+
},
|
|
3611
|
+
},
|
|
3612
|
+
media: {
|
|
3613
|
+
[taskId]: {
|
|
3614
|
+
mType: 'mainCall',
|
|
3615
|
+
participants: [agentId, 'customer-1'],
|
|
3616
|
+
},
|
|
3617
|
+
},
|
|
3618
|
+
},
|
|
3619
|
+
},
|
|
3620
|
+
};
|
|
3621
|
+
|
|
3622
|
+
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
3623
|
+
|
|
3624
|
+
// isConferenceInProgress should be false (only 1 active agent)
|
|
3625
|
+
expect(task.data.isConferenceInProgress).toBe(false);
|
|
3626
|
+
expect(task.emit).toHaveBeenCalledWith(TASK_EVENTS.TASK_PARTICIPANT_LEFT, task);
|
|
3627
|
+
});
|
|
3628
|
+
|
|
3629
|
+
it('should handle participant left when no participants data exists', () => {
|
|
3630
|
+
const payload = {
|
|
3631
|
+
data: {
|
|
3632
|
+
type: CC_EVENTS.PARTICIPANT_LEFT_CONFERENCE,
|
|
3633
|
+
interactionId: taskId,
|
|
3634
|
+
interaction: {},
|
|
3635
|
+
},
|
|
3636
|
+
};
|
|
3637
|
+
|
|
3638
|
+
const removeTaskSpy = jest.spyOn(taskManager, 'removeTaskFromCollection');
|
|
3639
|
+
|
|
3640
|
+
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
3641
|
+
|
|
3642
|
+
// When no participants data exists, checkParticipantNotInInteraction returns true
|
|
3643
|
+
// Since agent won't be in main interaction either, task should be removed
|
|
3644
|
+
expect(removeTaskSpy).toHaveBeenCalled();
|
|
3645
|
+
expect(task.emit).toHaveBeenCalledWith(TASK_EVENTS.TASK_PARTICIPANT_LEFT, task);
|
|
3646
|
+
});
|
|
3647
|
+
});
|
|
3648
|
+
|
|
3649
|
+
it('should handle PARTICIPANT_LEFT_CONFERENCE_FAILED event', () => {
|
|
3650
|
+
>>>>>>> 81672300901738f14289fd2a7414c6c4b1389aa6
|
|
2400
3651
|
const payload = {
|
|
2401
3652
|
data: {type: CC_EVENTS.AGENT_CONSULT_CONFERENCING, interactionId: taskId},
|
|
2402
3653
|
};
|
|
@@ -2416,7 +3667,11 @@ describe('TaskManager', () => {
|
|
|
2416
3667
|
|
|
2417
3668
|
it('only routes conference events to matching tasks', () => {
|
|
2418
3669
|
const otherTaskId = 'other-task-id';
|
|
3670
|
+
<<<<<<< HEAD
|
|
2419
3671
|
const otherTask: any = {
|
|
3672
|
+
=======
|
|
3673
|
+
const otherTask = {
|
|
3674
|
+
>>>>>>> 81672300901738f14289fd2a7414c6c4b1389aa6
|
|
2420
3675
|
data: {interactionId: otherTaskId},
|
|
2421
3676
|
emit: jest.fn(),
|
|
2422
3677
|
updateTaskData: jest.fn(),
|
|
@@ -2429,6 +3684,7 @@ describe('TaskManager', () => {
|
|
|
2429
3684
|
};
|
|
2430
3685
|
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
2431
3686
|
|
|
3687
|
+
<<<<<<< HEAD
|
|
2432
3688
|
expect(task.sendStateMachineEvent).toHaveBeenCalled();
|
|
2433
3689
|
expect(otherTask.sendStateMachineEvent).not.toHaveBeenCalled();
|
|
2434
3690
|
});
|
|
@@ -2460,6 +3716,939 @@ describe('TaskManager', () => {
|
|
|
2460
3716
|
});
|
|
2461
3717
|
|
|
2462
3718
|
sendStateMachineEventSpy.mockRestore();
|
|
3719
|
+
=======
|
|
3720
|
+
// Only the matching task should be updated
|
|
3721
|
+
expect(task.data.isConferencing).toBe(true);
|
|
3722
|
+
expect(task.emit).toHaveBeenCalledWith(TASK_EVENTS.TASK_CONFERENCE_STARTED, task);
|
|
3723
|
+
|
|
3724
|
+
// Other task should not be affected
|
|
3725
|
+
expect(otherTask.data.isConferencing).toBeUndefined();
|
|
3726
|
+
expect(otherTask.emit).not.toHaveBeenCalled();
|
|
3727
|
+
});
|
|
3728
|
+
});
|
|
3729
|
+
|
|
3730
|
+
describe('handleTaskCleanup - stage changes', () => {
|
|
3731
|
+
const agentId = '723a8ffb-a26e-496d-b14a-ff44fb83b64f';
|
|
3732
|
+
|
|
3733
|
+
beforeEach(() => {
|
|
3734
|
+
taskManager.setAgentId(agentId);
|
|
3735
|
+
});
|
|
3736
|
+
|
|
3737
|
+
it('should remove OUTDIAL task on CONTACT_ENDED when current agent is NOT in agentsPendingWrapUp', () => {
|
|
3738
|
+
const task = taskManager.getTask(taskId);
|
|
3739
|
+
task.updateTaskData = jest.fn().mockImplementation((newData) => {
|
|
3740
|
+
task.data = {
|
|
3741
|
+
...task.data,
|
|
3742
|
+
...newData,
|
|
3743
|
+
interaction: {
|
|
3744
|
+
...task.data.interaction,
|
|
3745
|
+
outboundType: 'OUTDIAL',
|
|
3746
|
+
state: 'new',
|
|
3747
|
+
mediaType: 'telephony',
|
|
3748
|
+
},
|
|
3749
|
+
agentsPendingWrapUp: ['different-agent-123'], // Current agent not in the list
|
|
3750
|
+
};
|
|
3751
|
+
return task;
|
|
3752
|
+
});
|
|
3753
|
+
task.unregisterWebCallListeners = jest.fn();
|
|
3754
|
+
const removeTaskSpy = jest.spyOn(taskManager, 'removeTaskFromCollection');
|
|
3755
|
+
|
|
3756
|
+
const payload = {
|
|
3757
|
+
data: {
|
|
3758
|
+
type: CC_EVENTS.CONTACT_ENDED,
|
|
3759
|
+
interactionId: taskId,
|
|
3760
|
+
interaction: {
|
|
3761
|
+
outboundType: 'OUTDIAL',
|
|
3762
|
+
state: 'new',
|
|
3763
|
+
mediaType: 'telephony',
|
|
3764
|
+
},
|
|
3765
|
+
agentsPendingWrapUp: ['different-agent-123'], // Current agent not in the list
|
|
3766
|
+
},
|
|
3767
|
+
};
|
|
3768
|
+
|
|
3769
|
+
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
3770
|
+
|
|
3771
|
+
expect(removeTaskSpy).toHaveBeenCalled();
|
|
3772
|
+
expect(taskManager.getTask(taskId)).toBeUndefined();
|
|
3773
|
+
});
|
|
3774
|
+
|
|
3775
|
+
it('should NOT remove OUTDIAL task on CONTACT_ENDED when current agent IS in agentsPendingWrapUp', () => {
|
|
3776
|
+
const task = taskManager.getTask(taskId);
|
|
3777
|
+
task.updateTaskData = jest.fn().mockImplementation((newData) => {
|
|
3778
|
+
task.data = {
|
|
3779
|
+
...task.data,
|
|
3780
|
+
...newData,
|
|
3781
|
+
interaction: {
|
|
3782
|
+
...task.data.interaction,
|
|
3783
|
+
outboundType: 'OUTDIAL',
|
|
3784
|
+
state: 'new',
|
|
3785
|
+
mediaType: 'telephony',
|
|
3786
|
+
},
|
|
3787
|
+
agentsPendingWrapUp: [agentId, 'other-agent-456'], // Current agent IS in the list
|
|
3788
|
+
};
|
|
3789
|
+
return task;
|
|
3790
|
+
});
|
|
3791
|
+
task.unregisterWebCallListeners = jest.fn();
|
|
3792
|
+
const removeTaskSpy = jest.spyOn(taskManager, 'removeTaskFromCollection');
|
|
3793
|
+
|
|
3794
|
+
const payload = {
|
|
3795
|
+
data: {
|
|
3796
|
+
type: CC_EVENTS.CONTACT_ENDED,
|
|
3797
|
+
interactionId: taskId,
|
|
3798
|
+
interaction: {
|
|
3799
|
+
outboundType: 'OUTDIAL',
|
|
3800
|
+
state: 'new',
|
|
3801
|
+
mediaType: 'telephony',
|
|
3802
|
+
},
|
|
3803
|
+
agentsPendingWrapUp: [agentId, 'other-agent-456'], // Current agent IS in the list
|
|
3804
|
+
},
|
|
3805
|
+
};
|
|
3806
|
+
|
|
3807
|
+
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
3808
|
+
|
|
3809
|
+
expect(removeTaskSpy).not.toHaveBeenCalled();
|
|
3810
|
+
expect(taskManager.getTask(taskId)).toBeDefined();
|
|
3811
|
+
});
|
|
3812
|
+
|
|
3813
|
+
it('should remove OUTDIAL task when needsWrapUp is false and task is outdial', () => {
|
|
3814
|
+
const task = taskManager.getTask(taskId);
|
|
3815
|
+
task.updateTaskData = jest.fn().mockImplementation((newData) => {
|
|
3816
|
+
task.data = {
|
|
3817
|
+
...task.data,
|
|
3818
|
+
...newData,
|
|
3819
|
+
interaction: {
|
|
3820
|
+
...task.data.interaction,
|
|
3821
|
+
outboundType: 'OUTDIAL',
|
|
3822
|
+
state: 'WRAPUP', // Not 'new' state
|
|
3823
|
+
mediaType: 'telephony',
|
|
3824
|
+
},
|
|
3825
|
+
agentsPendingWrapUp: [], // No agents pending wrap-up
|
|
3826
|
+
};
|
|
3827
|
+
return task;
|
|
3828
|
+
});
|
|
3829
|
+
task.unregisterWebCallListeners = jest.fn();
|
|
3830
|
+
const removeTaskSpy = jest.spyOn(taskManager, 'removeTaskFromCollection');
|
|
3831
|
+
|
|
3832
|
+
const payload = {
|
|
3833
|
+
data: {
|
|
3834
|
+
type: CC_EVENTS.CONTACT_ENDED,
|
|
3835
|
+
interactionId: taskId,
|
|
3836
|
+
interaction: {
|
|
3837
|
+
outboundType: 'OUTDIAL',
|
|
3838
|
+
state: 'WRAPUP', // Not 'new' state
|
|
3839
|
+
mediaType: 'telephony',
|
|
3840
|
+
},
|
|
3841
|
+
agentsPendingWrapUp: [], // No agents pending wrap-up
|
|
3842
|
+
},
|
|
3843
|
+
};
|
|
3844
|
+
|
|
3845
|
+
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
3846
|
+
|
|
3847
|
+
expect(removeTaskSpy).toHaveBeenCalled();
|
|
3848
|
+
expect(taskManager.getTask(taskId)).toBeUndefined();
|
|
3849
|
+
});
|
|
3850
|
+
|
|
3851
|
+
it('should remove OUTDIAL task when needsWrapUp is false (agentsPendingWrapUp is undefined)', () => {
|
|
3852
|
+
const task = taskManager.getTask(taskId);
|
|
3853
|
+
task.updateTaskData = jest.fn().mockImplementation((newData) => {
|
|
3854
|
+
task.data = {
|
|
3855
|
+
...task.data,
|
|
3856
|
+
...newData,
|
|
3857
|
+
interaction: {
|
|
3858
|
+
...task.data.interaction,
|
|
3859
|
+
outboundType: 'OUTDIAL',
|
|
3860
|
+
state: 'WRAPUP',
|
|
3861
|
+
mediaType: 'telephony',
|
|
3862
|
+
},
|
|
3863
|
+
agentsPendingWrapUp: undefined, // No agentsPendingWrapUp field
|
|
3864
|
+
};
|
|
3865
|
+
return task;
|
|
3866
|
+
});
|
|
3867
|
+
task.unregisterWebCallListeners = jest.fn();
|
|
3868
|
+
const removeTaskSpy = jest.spyOn(taskManager, 'removeTaskFromCollection');
|
|
3869
|
+
|
|
3870
|
+
const payload = {
|
|
3871
|
+
data: {
|
|
3872
|
+
type: CC_EVENTS.CONTACT_ENDED,
|
|
3873
|
+
interactionId: taskId,
|
|
3874
|
+
interaction: {
|
|
3875
|
+
outboundType: 'OUTDIAL',
|
|
3876
|
+
state: 'WRAPUP',
|
|
3877
|
+
mediaType: 'telephony',
|
|
3878
|
+
},
|
|
3879
|
+
// agentsPendingWrapUp not included
|
|
3880
|
+
},
|
|
3881
|
+
};
|
|
3882
|
+
|
|
3883
|
+
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
3884
|
+
|
|
3885
|
+
expect(removeTaskSpy).toHaveBeenCalled();
|
|
3886
|
+
expect(taskManager.getTask(taskId)).toBeUndefined();
|
|
3887
|
+
});
|
|
3888
|
+
|
|
3889
|
+
it('should NOT remove OUTDIAL task when needsWrapUp is true (current agent in agentsPendingWrapUp) even if state is WRAPUP', () => {
|
|
3890
|
+
const task = taskManager.getTask(taskId);
|
|
3891
|
+
task.updateTaskData = jest.fn().mockImplementation((newData) => {
|
|
3892
|
+
task.data = {
|
|
3893
|
+
...task.data,
|
|
3894
|
+
...newData,
|
|
3895
|
+
interaction: {
|
|
3896
|
+
...task.data.interaction,
|
|
3897
|
+
outboundType: 'OUTDIAL',
|
|
3898
|
+
state: 'WRAPUP',
|
|
3899
|
+
mediaType: 'telephony',
|
|
3900
|
+
},
|
|
3901
|
+
agentsPendingWrapUp: [agentId], // Current agent needs wrap-up
|
|
3902
|
+
};
|
|
3903
|
+
return task;
|
|
3904
|
+
});
|
|
3905
|
+
task.unregisterWebCallListeners = jest.fn();
|
|
3906
|
+
const removeTaskSpy = jest.spyOn(taskManager, 'removeTaskFromCollection');
|
|
3907
|
+
|
|
3908
|
+
const payload = {
|
|
3909
|
+
data: {
|
|
3910
|
+
type: CC_EVENTS.CONTACT_ENDED,
|
|
3911
|
+
interactionId: taskId,
|
|
3912
|
+
interaction: {
|
|
3913
|
+
outboundType: 'OUTDIAL',
|
|
3914
|
+
state: 'WRAPUP',
|
|
3915
|
+
mediaType: 'telephony',
|
|
3916
|
+
},
|
|
3917
|
+
agentsPendingWrapUp: [agentId], // Current agent needs wrap-up
|
|
3918
|
+
},
|
|
3919
|
+
};
|
|
3920
|
+
|
|
3921
|
+
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
3922
|
+
|
|
3923
|
+
expect(removeTaskSpy).not.toHaveBeenCalled();
|
|
3924
|
+
expect(taskManager.getTask(taskId)).toBeDefined();
|
|
3925
|
+
});
|
|
3926
|
+
|
|
3927
|
+
it('should remove non-OUTDIAL task when state is new regardless of agentsPendingWrapUp', () => {
|
|
3928
|
+
const task = taskManager.getTask(taskId);
|
|
3929
|
+
task.updateTaskData = jest.fn().mockImplementation((newData) => {
|
|
3930
|
+
task.data = {
|
|
3931
|
+
...task.data,
|
|
3932
|
+
...newData,
|
|
3933
|
+
interaction: {
|
|
3934
|
+
...task.data.interaction,
|
|
3935
|
+
outboundType: 'PREVIEW', // Not OUTDIAL
|
|
3936
|
+
state: 'new',
|
|
3937
|
+
mediaType: 'telephony',
|
|
3938
|
+
},
|
|
3939
|
+
agentsPendingWrapUp: [agentId],
|
|
3940
|
+
};
|
|
3941
|
+
return task;
|
|
3942
|
+
});
|
|
3943
|
+
task.unregisterWebCallListeners = jest.fn();
|
|
3944
|
+
const removeTaskSpy = jest.spyOn(taskManager, 'removeTaskFromCollection');
|
|
3945
|
+
|
|
3946
|
+
const payload = {
|
|
3947
|
+
data: {
|
|
3948
|
+
type: CC_EVENTS.CONTACT_ENDED,
|
|
3949
|
+
interactionId: taskId,
|
|
3950
|
+
interaction: {
|
|
3951
|
+
outboundType: 'PREVIEW',
|
|
3952
|
+
state: 'new',
|
|
3953
|
+
mediaType: 'telephony',
|
|
3954
|
+
},
|
|
3955
|
+
agentsPendingWrapUp: [agentId],
|
|
3956
|
+
},
|
|
3957
|
+
};
|
|
3958
|
+
|
|
3959
|
+
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
3960
|
+
|
|
3961
|
+
expect(removeTaskSpy).toHaveBeenCalled();
|
|
3962
|
+
expect(taskManager.getTask(taskId)).toBeUndefined();
|
|
3963
|
+
});
|
|
3964
|
+
|
|
3965
|
+
it('should handle agentsPendingWrapUp with multiple agents correctly - remove if current agent not in list', () => {
|
|
3966
|
+
const task = taskManager.getTask(taskId);
|
|
3967
|
+
task.updateTaskData = jest.fn().mockImplementation((newData) => {
|
|
3968
|
+
task.data = {
|
|
3969
|
+
...task.data,
|
|
3970
|
+
...newData,
|
|
3971
|
+
interaction: {
|
|
3972
|
+
...task.data.interaction,
|
|
3973
|
+
outboundType: 'OUTDIAL',
|
|
3974
|
+
state: 'new',
|
|
3975
|
+
mediaType: 'telephony',
|
|
3976
|
+
},
|
|
3977
|
+
agentsPendingWrapUp: ['agent-1', 'agent-2', 'agent-3'], // Current agent not in the list
|
|
3978
|
+
};
|
|
3979
|
+
return task;
|
|
3980
|
+
});
|
|
3981
|
+
task.unregisterWebCallListeners = jest.fn();
|
|
3982
|
+
const removeTaskSpy = jest.spyOn(taskManager, 'removeTaskFromCollection');
|
|
3983
|
+
|
|
3984
|
+
const payload = {
|
|
3985
|
+
data: {
|
|
3986
|
+
type: CC_EVENTS.CONTACT_ENDED,
|
|
3987
|
+
interactionId: taskId,
|
|
3988
|
+
interaction: {
|
|
3989
|
+
outboundType: 'OUTDIAL',
|
|
3990
|
+
state: 'new',
|
|
3991
|
+
mediaType: 'telephony',
|
|
3992
|
+
},
|
|
3993
|
+
agentsPendingWrapUp: ['agent-1', 'agent-2', 'agent-3'],
|
|
3994
|
+
},
|
|
3995
|
+
};
|
|
3996
|
+
|
|
3997
|
+
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
3998
|
+
|
|
3999
|
+
expect(removeTaskSpy).toHaveBeenCalled();
|
|
4000
|
+
expect(taskManager.getTask(taskId)).toBeUndefined();
|
|
4001
|
+
});
|
|
4002
|
+
|
|
4003
|
+
it('should handle agentsPendingWrapUp with multiple agents correctly - keep if current agent is in list', () => {
|
|
4004
|
+
const task = taskManager.getTask(taskId);
|
|
4005
|
+
task.updateTaskData = jest.fn().mockImplementation((newData) => {
|
|
4006
|
+
task.data = {
|
|
4007
|
+
...task.data,
|
|
4008
|
+
...newData,
|
|
4009
|
+
interaction: {
|
|
4010
|
+
...task.data.interaction,
|
|
4011
|
+
outboundType: 'OUTDIAL',
|
|
4012
|
+
state: 'new',
|
|
4013
|
+
mediaType: 'telephony',
|
|
4014
|
+
},
|
|
4015
|
+
agentsPendingWrapUp: ['agent-1', agentId, 'agent-3'], // Current agent IS in the list
|
|
4016
|
+
};
|
|
4017
|
+
return task;
|
|
4018
|
+
});
|
|
4019
|
+
task.unregisterWebCallListeners = jest.fn();
|
|
4020
|
+
const removeTaskSpy = jest.spyOn(taskManager, 'removeTaskFromCollection');
|
|
4021
|
+
|
|
4022
|
+
const payload = {
|
|
4023
|
+
data: {
|
|
4024
|
+
type: CC_EVENTS.CONTACT_ENDED,
|
|
4025
|
+
interactionId: taskId,
|
|
4026
|
+
interaction: {
|
|
4027
|
+
outboundType: 'OUTDIAL',
|
|
4028
|
+
state: 'new',
|
|
4029
|
+
mediaType: 'telephony',
|
|
4030
|
+
},
|
|
4031
|
+
agentsPendingWrapUp: ['agent-1', agentId, 'agent-3'],
|
|
4032
|
+
},
|
|
4033
|
+
};
|
|
4034
|
+
|
|
4035
|
+
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
4036
|
+
|
|
4037
|
+
expect(removeTaskSpy).not.toHaveBeenCalled();
|
|
4038
|
+
expect(taskManager.getTask(taskId)).toBeDefined();
|
|
4039
|
+
});
|
|
4040
|
+
});
|
|
4041
|
+
|
|
4042
|
+
describe('CONTACT_MERGED event handling', () => {
|
|
4043
|
+
let task;
|
|
4044
|
+
let taskEmitSpy;
|
|
4045
|
+
let managerEmitSpy;
|
|
4046
|
+
|
|
4047
|
+
beforeEach(() => {
|
|
4048
|
+
// Create initial task
|
|
4049
|
+
webSocketManagerMock.emit('message', JSON.stringify(initalPayload));
|
|
4050
|
+
task = taskManager.getTask(taskId);
|
|
4051
|
+
taskEmitSpy = jest.spyOn(task, 'emit');
|
|
4052
|
+
managerEmitSpy = jest.spyOn(taskManager, 'emit');
|
|
4053
|
+
});
|
|
4054
|
+
|
|
4055
|
+
it('should update existing task data and emit TASK_MERGED event when CONTACT_MERGED is received', () => {
|
|
4056
|
+
const mergedPayload = {
|
|
4057
|
+
data: {
|
|
4058
|
+
type: CC_EVENTS.CONTACT_MERGED,
|
|
4059
|
+
interactionId: taskId,
|
|
4060
|
+
agentId: taskDataMock.agentId,
|
|
4061
|
+
interaction: {
|
|
4062
|
+
...taskDataMock.interaction,
|
|
4063
|
+
state: 'merged',
|
|
4064
|
+
customField: 'updated-value',
|
|
4065
|
+
},
|
|
4066
|
+
},
|
|
4067
|
+
};
|
|
4068
|
+
|
|
4069
|
+
webSocketManagerMock.emit('message', JSON.stringify(mergedPayload));
|
|
4070
|
+
|
|
4071
|
+
const updatedTask = taskManager.getTask(taskId);
|
|
4072
|
+
expect(updatedTask).toBeDefined();
|
|
4073
|
+
expect(updatedTask.data.interaction.customField).toBe('updated-value');
|
|
4074
|
+
expect(updatedTask.data.interaction.state).toBe('merged');
|
|
4075
|
+
expect(managerEmitSpy).toHaveBeenCalledWith(TASK_EVENTS.TASK_MERGED, updatedTask);
|
|
4076
|
+
});
|
|
4077
|
+
|
|
4078
|
+
it('should create new task when CONTACT_MERGED is received for non-existing task', () => {
|
|
4079
|
+
const newMergedTaskId = 'new-merged-task-id';
|
|
4080
|
+
const mergedPayload = {
|
|
4081
|
+
data: {
|
|
4082
|
+
type: CC_EVENTS.CONTACT_MERGED,
|
|
4083
|
+
interactionId: newMergedTaskId,
|
|
4084
|
+
agentId: taskDataMock.agentId,
|
|
4085
|
+
interaction: {
|
|
4086
|
+
mediaType: 'telephony',
|
|
4087
|
+
state: 'merged',
|
|
4088
|
+
participants: {
|
|
4089
|
+
[taskDataMock.agentId]: {
|
|
4090
|
+
isWrapUp: false,
|
|
4091
|
+
hasJoined: true,
|
|
4092
|
+
},
|
|
4093
|
+
},
|
|
4094
|
+
},
|
|
4095
|
+
},
|
|
4096
|
+
};
|
|
4097
|
+
|
|
4098
|
+
// Verify task doesn't exist before
|
|
4099
|
+
expect(taskManager.getTask(newMergedTaskId)).toBeUndefined();
|
|
4100
|
+
|
|
4101
|
+
webSocketManagerMock.emit('message', JSON.stringify(mergedPayload));
|
|
4102
|
+
|
|
4103
|
+
// Verify task was created
|
|
4104
|
+
const newTask = taskManager.getTask(newMergedTaskId);
|
|
4105
|
+
expect(newTask).toBeDefined();
|
|
4106
|
+
expect(newTask.data.interactionId).toBe(newMergedTaskId);
|
|
4107
|
+
expect(managerEmitSpy).toHaveBeenCalledWith(TASK_EVENTS.TASK_MERGED, newTask);
|
|
4108
|
+
});
|
|
4109
|
+
|
|
4110
|
+
it('should remove child task when childInteractionId is present in CONTACT_MERGED', () => {
|
|
4111
|
+
const childTaskId = 'child-task-id';
|
|
4112
|
+
const parentTaskId = 'parent-task-id';
|
|
4113
|
+
|
|
4114
|
+
// Create child task
|
|
4115
|
+
const childPayload = {
|
|
4116
|
+
data: {
|
|
4117
|
+
type: CC_EVENTS.AGENT_CONTACT_RESERVED,
|
|
4118
|
+
interactionId: childTaskId,
|
|
4119
|
+
agentId: taskDataMock.agentId,
|
|
4120
|
+
interaction: {mediaType: 'telephony'},
|
|
4121
|
+
},
|
|
4122
|
+
};
|
|
4123
|
+
webSocketManagerMock.emit('message', JSON.stringify(childPayload));
|
|
4124
|
+
|
|
4125
|
+
// Verify child task exists
|
|
4126
|
+
expect(taskManager.getTask(childTaskId)).toBeDefined();
|
|
4127
|
+
|
|
4128
|
+
// Create parent task
|
|
4129
|
+
const parentPayload = {
|
|
4130
|
+
data: {
|
|
4131
|
+
type: CC_EVENTS.AGENT_CONTACT_RESERVED,
|
|
4132
|
+
interactionId: parentTaskId,
|
|
4133
|
+
agentId: taskDataMock.agentId,
|
|
4134
|
+
interaction: {mediaType: 'telephony'},
|
|
4135
|
+
},
|
|
4136
|
+
};
|
|
4137
|
+
webSocketManagerMock.emit('message', JSON.stringify(parentPayload));
|
|
4138
|
+
|
|
4139
|
+
// Send CONTACT_MERGED with childInteractionId
|
|
4140
|
+
const mergedPayload = {
|
|
4141
|
+
data: {
|
|
4142
|
+
type: CC_EVENTS.CONTACT_MERGED,
|
|
4143
|
+
interactionId: parentTaskId,
|
|
4144
|
+
childInteractionId: childTaskId,
|
|
4145
|
+
agentId: taskDataMock.agentId,
|
|
4146
|
+
interaction: {
|
|
4147
|
+
mediaType: 'telephony',
|
|
4148
|
+
state: 'merged',
|
|
4149
|
+
},
|
|
4150
|
+
},
|
|
4151
|
+
};
|
|
4152
|
+
|
|
4153
|
+
webSocketManagerMock.emit('message', JSON.stringify(mergedPayload));
|
|
4154
|
+
|
|
4155
|
+
// Verify child task was removed
|
|
4156
|
+
expect(taskManager.getTask(childTaskId)).toBeUndefined();
|
|
4157
|
+
|
|
4158
|
+
// Verify parent task still exists
|
|
4159
|
+
expect(taskManager.getTask(parentTaskId)).toBeDefined();
|
|
4160
|
+
|
|
4161
|
+
// Verify TASK_MERGED event was emitted
|
|
4162
|
+
expect(managerEmitSpy).toHaveBeenCalledWith(
|
|
4163
|
+
TASK_EVENTS.TASK_MERGED,
|
|
4164
|
+
expect.objectContaining({
|
|
4165
|
+
data: expect.objectContaining({
|
|
4166
|
+
interactionId: parentTaskId,
|
|
4167
|
+
}),
|
|
4168
|
+
})
|
|
4169
|
+
);
|
|
4170
|
+
});
|
|
4171
|
+
|
|
4172
|
+
it('should handle CONTACT_MERGED with EP-DN participant correctly', () => {
|
|
4173
|
+
const epdnTaskId = 'epdn-merged-task';
|
|
4174
|
+
const mergedPayload = {
|
|
4175
|
+
data: {
|
|
4176
|
+
type: CC_EVENTS.CONTACT_MERGED,
|
|
4177
|
+
interactionId: epdnTaskId,
|
|
4178
|
+
agentId: taskDataMock.agentId,
|
|
4179
|
+
interaction: {
|
|
4180
|
+
mediaType: 'telephony',
|
|
4181
|
+
state: 'merged',
|
|
4182
|
+
participants: {
|
|
4183
|
+
[taskDataMock.agentId]: {
|
|
4184
|
+
type: 'Agent',
|
|
4185
|
+
isWrapUp: false,
|
|
4186
|
+
hasJoined: true,
|
|
4187
|
+
},
|
|
4188
|
+
'epdn-participant': {
|
|
4189
|
+
type: 'EpDn',
|
|
4190
|
+
epId: 'entry-point-123',
|
|
4191
|
+
isWrapUp: false,
|
|
4192
|
+
},
|
|
4193
|
+
},
|
|
4194
|
+
},
|
|
4195
|
+
},
|
|
4196
|
+
};
|
|
4197
|
+
|
|
4198
|
+
webSocketManagerMock.emit('message', JSON.stringify(mergedPayload));
|
|
4199
|
+
|
|
4200
|
+
const mergedTask = taskManager.getTask(epdnTaskId);
|
|
4201
|
+
expect(mergedTask).toBeDefined();
|
|
4202
|
+
expect(mergedTask.data.interaction.participants['epdn-participant']).toBeDefined();
|
|
4203
|
+
expect(mergedTask.data.interaction.participants['epdn-participant'].type).toBe('EpDn');
|
|
4204
|
+
expect(managerEmitSpy).toHaveBeenCalledWith(TASK_EVENTS.TASK_MERGED, mergedTask);
|
|
4205
|
+
});
|
|
4206
|
+
|
|
4207
|
+
it('should not affect other tasks when CONTACT_MERGED is received', () => {
|
|
4208
|
+
const otherTaskId = 'other-unrelated-task';
|
|
4209
|
+
const otherPayload = {
|
|
4210
|
+
data: {
|
|
4211
|
+
type: CC_EVENTS.AGENT_CONTACT_RESERVED,
|
|
4212
|
+
interactionId: otherTaskId,
|
|
4213
|
+
agentId: taskDataMock.agentId,
|
|
4214
|
+
interaction: {mediaType: 'chat'},
|
|
4215
|
+
},
|
|
4216
|
+
};
|
|
4217
|
+
webSocketManagerMock.emit('message', JSON.stringify(otherPayload));
|
|
4218
|
+
|
|
4219
|
+
const otherTask = taskManager.getTask(otherTaskId);
|
|
4220
|
+
const otherTaskEmitSpy = jest.spyOn(otherTask, 'emit');
|
|
4221
|
+
|
|
4222
|
+
// Send CONTACT_MERGED for the original task
|
|
4223
|
+
const mergedPayload = {
|
|
4224
|
+
data: {
|
|
4225
|
+
type: CC_EVENTS.CONTACT_MERGED,
|
|
4226
|
+
interactionId: taskId,
|
|
4227
|
+
agentId: taskDataMock.agentId,
|
|
4228
|
+
interaction: {
|
|
4229
|
+
mediaType: 'telephony',
|
|
4230
|
+
state: 'merged',
|
|
4231
|
+
},
|
|
4232
|
+
},
|
|
4233
|
+
};
|
|
4234
|
+
|
|
4235
|
+
webSocketManagerMock.emit('message', JSON.stringify(mergedPayload));
|
|
4236
|
+
|
|
4237
|
+
// Verify other task was not affected
|
|
4238
|
+
expect(otherTaskEmitSpy).not.toHaveBeenCalled();
|
|
4239
|
+
expect(otherTask.data.interaction.mediaType).toBe('chat');
|
|
4240
|
+
|
|
4241
|
+
// Verify original task was updated
|
|
4242
|
+
expect(managerEmitSpy).toHaveBeenCalledWith(
|
|
4243
|
+
TASK_EVENTS.TASK_MERGED,
|
|
4244
|
+
expect.objectContaining({
|
|
4245
|
+
data: expect.objectContaining({
|
|
4246
|
+
interactionId: taskId,
|
|
4247
|
+
}),
|
|
4248
|
+
})
|
|
4249
|
+
);
|
|
4250
|
+
});
|
|
4251
|
+
});
|
|
4252
|
+
|
|
4253
|
+
describe('Campaign Preview Reservation', () => {
|
|
4254
|
+
it('should create a task and emit TASK_CAMPAIGN_PREVIEW_RESERVATION when AgentOfferCampaignReservation is received', () => {
|
|
4255
|
+
const campaignPayload = {
|
|
4256
|
+
data: {
|
|
4257
|
+
type: CC_EVENTS.AGENT_OFFER_CAMPAIGN_RESERVATION,
|
|
4258
|
+
interactionId: 'campaign-interaction-123',
|
|
4259
|
+
agentId: taskDataMock.agentId,
|
|
4260
|
+
orgId: taskDataMock.orgId,
|
|
4261
|
+
trackingId: 'campaign-tracking-456',
|
|
4262
|
+
interaction: {
|
|
4263
|
+
mediaType: 'telephony',
|
|
4264
|
+
callProcessingDetails: {
|
|
4265
|
+
campaignId: 'campaign-789',
|
|
4266
|
+
},
|
|
4267
|
+
},
|
|
4268
|
+
},
|
|
4269
|
+
};
|
|
4270
|
+
|
|
4271
|
+
const managerEmitSpy = jest.spyOn(taskManager, 'emit');
|
|
4272
|
+
|
|
4273
|
+
webSocketManagerMock.emit('message', JSON.stringify(campaignPayload));
|
|
4274
|
+
|
|
4275
|
+
// Should emit with a task object (not raw data)
|
|
4276
|
+
expect(managerEmitSpy).toHaveBeenCalledWith(
|
|
4277
|
+
TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_RESERVATION,
|
|
4278
|
+
expect.objectContaining({
|
|
4279
|
+
data: expect.objectContaining({
|
|
4280
|
+
interactionId: 'campaign-interaction-123',
|
|
4281
|
+
type: CC_EVENTS.AGENT_OFFER_CAMPAIGN_RESERVATION,
|
|
4282
|
+
wrapUpRequired: false,
|
|
4283
|
+
isAutoAnswering: false,
|
|
4284
|
+
}),
|
|
4285
|
+
})
|
|
4286
|
+
);
|
|
4287
|
+
|
|
4288
|
+
// Task should be in the collection so subsequent events (e.g. AGENT_CONTACT_ASSIGNED) can find it
|
|
4289
|
+
expect(taskManager['taskCollection']['campaign-interaction-123']).toBeDefined();
|
|
4290
|
+
});
|
|
4291
|
+
|
|
4292
|
+
it('should re-key task and emit TASK_ASSIGNED when AgentContactAssigned uses a new interactionId', () => {
|
|
4293
|
+
const reservationInteractionId = 'campaign-reservation-id';
|
|
4294
|
+
const assignedInteractionId = 'campaign-assigned-id';
|
|
4295
|
+
|
|
4296
|
+
webSocketManagerMock.emit(
|
|
4297
|
+
'message',
|
|
4298
|
+
JSON.stringify({
|
|
4299
|
+
data: {
|
|
4300
|
+
type: CC_EVENTS.AGENT_OFFER_CAMPAIGN_RESERVATION,
|
|
4301
|
+
interactionId: reservationInteractionId,
|
|
4302
|
+
agentId: taskDataMock.agentId,
|
|
4303
|
+
orgId: taskDataMock.orgId,
|
|
4304
|
+
trackingId: 'campaign-tracking-456',
|
|
4305
|
+
interaction: {
|
|
4306
|
+
mediaType: 'telephony',
|
|
4307
|
+
callProcessingDetails: {campaignId: 'campaign-789'},
|
|
4308
|
+
},
|
|
4309
|
+
},
|
|
4310
|
+
})
|
|
4311
|
+
);
|
|
4312
|
+
|
|
4313
|
+
const taskBeforeAssign = taskManager['taskCollection'][reservationInteractionId];
|
|
4314
|
+
expect(taskBeforeAssign).toBeDefined();
|
|
4315
|
+
|
|
4316
|
+
const taskEmitSpy = jest.spyOn(taskBeforeAssign, 'emit');
|
|
4317
|
+
|
|
4318
|
+
webSocketManagerMock.emit(
|
|
4319
|
+
'message',
|
|
4320
|
+
JSON.stringify({
|
|
4321
|
+
data: {
|
|
4322
|
+
type: CC_EVENTS.AGENT_CONTACT_ASSIGNED,
|
|
4323
|
+
interactionId: assignedInteractionId,
|
|
4324
|
+
reservationInteractionId,
|
|
4325
|
+
agentId: taskDataMock.agentId,
|
|
4326
|
+
orgId: taskDataMock.orgId,
|
|
4327
|
+
trackingId: 'campaign-tracking-assigned',
|
|
4328
|
+
interaction: {mediaType: 'telephony', state: 'connected'},
|
|
4329
|
+
},
|
|
4330
|
+
})
|
|
4331
|
+
);
|
|
4332
|
+
|
|
4333
|
+
expect(taskManager['taskCollection'][reservationInteractionId]).toBeUndefined();
|
|
4334
|
+
expect(taskManager['taskCollection'][assignedInteractionId]).toBe(taskBeforeAssign);
|
|
4335
|
+
expect(taskEmitSpy).toHaveBeenCalledWith(TASK_EVENTS.TASK_ASSIGNED, taskBeforeAssign);
|
|
4336
|
+
|
|
4337
|
+
taskEmitSpy.mockRestore();
|
|
4338
|
+
});
|
|
4339
|
+
|
|
4340
|
+
it('should not emit TASK_INCOMING for campaign preview reservation when incoming WebRTC call arrives', () => {
|
|
4341
|
+
const campaignPayload = {
|
|
4342
|
+
data: {
|
|
4343
|
+
type: CC_EVENTS.AGENT_OFFER_CAMPAIGN_RESERVATION,
|
|
4344
|
+
interactionId: 'campaign-interaction-123',
|
|
4345
|
+
agentId: taskDataMock.agentId,
|
|
4346
|
+
orgId: taskDataMock.orgId,
|
|
4347
|
+
trackingId: 'campaign-tracking-456',
|
|
4348
|
+
interaction: {
|
|
4349
|
+
mediaType: 'telephony',
|
|
4350
|
+
callProcessingDetails: {
|
|
4351
|
+
campaignId: 'campaign-789',
|
|
4352
|
+
},
|
|
4353
|
+
},
|
|
4354
|
+
},
|
|
4355
|
+
};
|
|
4356
|
+
|
|
4357
|
+
// Remove the default task so only the campaign preview task is in the collection
|
|
4358
|
+
delete taskManager['taskCollection'][taskId];
|
|
4359
|
+
|
|
4360
|
+
// Create campaign preview task via the reservation event
|
|
4361
|
+
webSocketManagerMock.emit('message', JSON.stringify(campaignPayload));
|
|
4362
|
+
|
|
4363
|
+
const managerEmitSpy = jest.spyOn(taskManager, 'emit');
|
|
4364
|
+
|
|
4365
|
+
// Simulate an incoming WebRTC call
|
|
4366
|
+
const incomingCallCb = onSpy.mock.calls[0][1];
|
|
4367
|
+
incomingCallCb(mockCall);
|
|
4368
|
+
|
|
4369
|
+
// TASK_INCOMING should NOT be emitted because the only telephony task is a campaign preview
|
|
4370
|
+
expect(managerEmitSpy).not.toHaveBeenCalledWith(TASK_EVENTS.TASK_INCOMING, expect.anything());
|
|
4371
|
+
});
|
|
4372
|
+
|
|
4373
|
+
it('should update existing task when AgentOfferCampaignReservation is received for known interactionId', () => {
|
|
4374
|
+
const campaignPayload = {
|
|
4375
|
+
data: {
|
|
4376
|
+
type: CC_EVENTS.AGENT_OFFER_CAMPAIGN_RESERVATION,
|
|
4377
|
+
interactionId: 'campaign-interaction-123',
|
|
4378
|
+
agentId: taskDataMock.agentId,
|
|
4379
|
+
orgId: taskDataMock.orgId,
|
|
4380
|
+
trackingId: 'campaign-tracking-456',
|
|
4381
|
+
interaction: {
|
|
4382
|
+
mediaType: 'telephony',
|
|
4383
|
+
callProcessingDetails: {
|
|
4384
|
+
campaignId: 'campaign-789',
|
|
4385
|
+
},
|
|
4386
|
+
},
|
|
4387
|
+
},
|
|
4388
|
+
};
|
|
4389
|
+
|
|
4390
|
+
// Send the first reservation to create the task
|
|
4391
|
+
webSocketManagerMock.emit('message', JSON.stringify(campaignPayload));
|
|
4392
|
+
|
|
4393
|
+
const managerEmitSpy = jest.spyOn(taskManager, 'emit');
|
|
4394
|
+
|
|
4395
|
+
// Send a second reservation for the same interactionId
|
|
4396
|
+
webSocketManagerMock.emit('message', JSON.stringify(campaignPayload));
|
|
4397
|
+
|
|
4398
|
+
expect(managerEmitSpy).toHaveBeenCalledWith(
|
|
4399
|
+
TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_RESERVATION,
|
|
4400
|
+
expect.objectContaining({
|
|
4401
|
+
data: expect.objectContaining({
|
|
4402
|
+
interactionId: 'campaign-interaction-123',
|
|
4403
|
+
}),
|
|
4404
|
+
})
|
|
4405
|
+
);
|
|
4406
|
+
});
|
|
4407
|
+
|
|
4408
|
+
it('should emit TASK_CAMPAIGN_CONTACT_UPDATED and NOT remove task when CampaignContactUpdated is received', () => {
|
|
4409
|
+
const campaignInteractionId = 'campaign-interaction-123';
|
|
4410
|
+
|
|
4411
|
+
// First create a campaign preview task
|
|
4412
|
+
const reservationPayload = {
|
|
4413
|
+
data: {
|
|
4414
|
+
type: CC_EVENTS.AGENT_OFFER_CAMPAIGN_RESERVATION,
|
|
4415
|
+
interactionId: campaignInteractionId,
|
|
4416
|
+
agentId: taskDataMock.agentId,
|
|
4417
|
+
orgId: taskDataMock.orgId,
|
|
4418
|
+
trackingId: 'campaign-tracking-456',
|
|
4419
|
+
interaction: {
|
|
4420
|
+
mediaType: 'telephony',
|
|
4421
|
+
callProcessingDetails: {
|
|
4422
|
+
campaignId: 'campaign-789',
|
|
4423
|
+
},
|
|
4424
|
+
},
|
|
4425
|
+
},
|
|
4426
|
+
};
|
|
4427
|
+
|
|
4428
|
+
webSocketManagerMock.emit('message', JSON.stringify(reservationPayload));
|
|
4429
|
+
|
|
4430
|
+
// Verify task exists in collection
|
|
4431
|
+
const task = taskManager['taskCollection'][campaignInteractionId];
|
|
4432
|
+
expect(task).toBeDefined();
|
|
4433
|
+
|
|
4434
|
+
const taskEmitSpy = jest.spyOn(task, 'emit');
|
|
4435
|
+
|
|
4436
|
+
// Now send CampaignContactUpdated
|
|
4437
|
+
const campaignContactUpdatedPayload = {
|
|
4438
|
+
data: {
|
|
4439
|
+
type: CC_EVENTS.CAMPAIGN_CONTACT_UPDATED,
|
|
4440
|
+
interactionId: campaignInteractionId,
|
|
4441
|
+
agentId: taskDataMock.agentId,
|
|
4442
|
+
orgId: taskDataMock.orgId,
|
|
4443
|
+
interaction: {
|
|
4444
|
+
mediaType: 'telephony',
|
|
4445
|
+
state: 'new',
|
|
4446
|
+
callProcessingDetails: {
|
|
4447
|
+
campaignId: 'campaign-789',
|
|
4448
|
+
},
|
|
4449
|
+
},
|
|
4450
|
+
},
|
|
4451
|
+
};
|
|
4452
|
+
|
|
4453
|
+
webSocketManagerMock.emit('message', JSON.stringify(campaignContactUpdatedPayload));
|
|
4454
|
+
|
|
4455
|
+
// Task should still exist in collection (not removed — non-terminal event)
|
|
4456
|
+
expect(taskManager['taskCollection'][campaignInteractionId]).toBeDefined();
|
|
4457
|
+
|
|
4458
|
+
// TASK_CAMPAIGN_CONTACT_UPDATED should have been emitted
|
|
4459
|
+
expect(taskEmitSpy).toHaveBeenCalledWith(
|
|
4460
|
+
TASK_EVENTS.TASK_CAMPAIGN_CONTACT_UPDATED,
|
|
4461
|
+
expect.objectContaining({
|
|
4462
|
+
data: expect.objectContaining({
|
|
4463
|
+
interactionId: campaignInteractionId,
|
|
4464
|
+
}),
|
|
4465
|
+
})
|
|
4466
|
+
);
|
|
4467
|
+
|
|
4468
|
+
// TASK_END should NOT have been emitted
|
|
4469
|
+
expect(taskEmitSpy).not.toHaveBeenCalledWith(TASK_EVENTS.TASK_END, expect.anything());
|
|
4470
|
+
});
|
|
4471
|
+
|
|
4472
|
+
it('should emit TASK_CAMPAIGN_PREVIEW_ACCEPT_FAILED when CampaignPreviewAcceptFailed is received', () => {
|
|
4473
|
+
const campaignInteractionId = 'campaign-interaction-123';
|
|
4474
|
+
|
|
4475
|
+
// First create a campaign preview task
|
|
4476
|
+
const reservationPayload = {
|
|
4477
|
+
data: {
|
|
4478
|
+
type: CC_EVENTS.AGENT_OFFER_CAMPAIGN_RESERVATION,
|
|
4479
|
+
interactionId: campaignInteractionId,
|
|
4480
|
+
agentId: taskDataMock.agentId,
|
|
4481
|
+
orgId: taskDataMock.orgId,
|
|
4482
|
+
trackingId: 'campaign-tracking-456',
|
|
4483
|
+
interaction: {
|
|
4484
|
+
mediaType: 'telephony',
|
|
4485
|
+
callProcessingDetails: {
|
|
4486
|
+
campaignId: 'campaign-789',
|
|
4487
|
+
},
|
|
4488
|
+
},
|
|
4489
|
+
},
|
|
4490
|
+
};
|
|
4491
|
+
|
|
4492
|
+
webSocketManagerMock.emit('message', JSON.stringify(reservationPayload));
|
|
4493
|
+
|
|
4494
|
+
const task = taskManager['taskCollection'][campaignInteractionId];
|
|
4495
|
+
expect(task).toBeDefined();
|
|
4496
|
+
|
|
4497
|
+
const taskEmitSpy = jest.spyOn(task, 'emit');
|
|
4498
|
+
|
|
4499
|
+
const failPayload = {
|
|
4500
|
+
data: {
|
|
4501
|
+
type: CC_EVENTS.CAMPAIGN_PREVIEW_ACCEPT_FAILED,
|
|
4502
|
+
interactionId: campaignInteractionId,
|
|
4503
|
+
campaignId: 'campaign-789',
|
|
4504
|
+
reason: 'INTERNAL_ERROR',
|
|
4505
|
+
},
|
|
4506
|
+
};
|
|
4507
|
+
|
|
4508
|
+
webSocketManagerMock.emit('message', JSON.stringify(failPayload));
|
|
4509
|
+
|
|
4510
|
+
expect(taskEmitSpy).toHaveBeenCalledWith(
|
|
4511
|
+
TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_ACCEPT_FAILED,
|
|
4512
|
+
expect.objectContaining({
|
|
4513
|
+
data: expect.objectContaining({interactionId: campaignInteractionId}),
|
|
4514
|
+
})
|
|
4515
|
+
);
|
|
4516
|
+
// Task should still exist (failure is non-terminal)
|
|
4517
|
+
expect(taskManager['taskCollection'][campaignInteractionId]).toBeDefined();
|
|
4518
|
+
|
|
4519
|
+
// Failure payload (reason, campaignId) should be merged into task.data
|
|
4520
|
+
expect(task.data.reason).toBe('INTERNAL_ERROR');
|
|
4521
|
+
|
|
4522
|
+
// Original reservation data must be preserved
|
|
4523
|
+
expect(task.data.interaction).toBeDefined();
|
|
4524
|
+
expect(task.data.interaction.callProcessingDetails.campaignId).toBe('campaign-789');
|
|
4525
|
+
});
|
|
4526
|
+
|
|
4527
|
+
it('should emit TASK_CAMPAIGN_PREVIEW_SKIP_FAILED when CampaignPreviewSkipFailed is received', () => {
|
|
4528
|
+
const campaignInteractionId = 'campaign-interaction-123';
|
|
4529
|
+
|
|
4530
|
+
const reservationPayload = {
|
|
4531
|
+
data: {
|
|
4532
|
+
type: CC_EVENTS.AGENT_OFFER_CAMPAIGN_RESERVATION,
|
|
4533
|
+
interactionId: campaignInteractionId,
|
|
4534
|
+
agentId: taskDataMock.agentId,
|
|
4535
|
+
orgId: taskDataMock.orgId,
|
|
4536
|
+
trackingId: 'campaign-tracking-456',
|
|
4537
|
+
interaction: {
|
|
4538
|
+
mediaType: 'telephony',
|
|
4539
|
+
callProcessingDetails: {
|
|
4540
|
+
campaignId: 'campaign-789',
|
|
4541
|
+
},
|
|
4542
|
+
},
|
|
4543
|
+
},
|
|
4544
|
+
};
|
|
4545
|
+
|
|
4546
|
+
webSocketManagerMock.emit('message', JSON.stringify(reservationPayload));
|
|
4547
|
+
|
|
4548
|
+
const task = taskManager['taskCollection'][campaignInteractionId];
|
|
4549
|
+
expect(task).toBeDefined();
|
|
4550
|
+
|
|
4551
|
+
const taskEmitSpy = jest.spyOn(task, 'emit');
|
|
4552
|
+
|
|
4553
|
+
const failPayload = {
|
|
4554
|
+
data: {
|
|
4555
|
+
type: CC_EVENTS.CAMPAIGN_PREVIEW_SKIP_FAILED,
|
|
4556
|
+
interactionId: campaignInteractionId,
|
|
4557
|
+
campaignId: 'campaign-789',
|
|
4558
|
+
reason: 'INTERNAL_ERROR',
|
|
4559
|
+
},
|
|
4560
|
+
};
|
|
4561
|
+
|
|
4562
|
+
webSocketManagerMock.emit('message', JSON.stringify(failPayload));
|
|
4563
|
+
|
|
4564
|
+
expect(taskEmitSpy).toHaveBeenCalledWith(
|
|
4565
|
+
TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_SKIP_FAILED,
|
|
4566
|
+
expect.objectContaining({
|
|
4567
|
+
data: expect.objectContaining({interactionId: campaignInteractionId}),
|
|
4568
|
+
})
|
|
4569
|
+
);
|
|
4570
|
+
expect(taskManager['taskCollection'][campaignInteractionId]).toBeDefined();
|
|
4571
|
+
|
|
4572
|
+
// Failure payload (reason) should be merged into task.data
|
|
4573
|
+
expect(task.data.reason).toBe('INTERNAL_ERROR');
|
|
4574
|
+
|
|
4575
|
+
// Original reservation data must be preserved
|
|
4576
|
+
expect(task.data.interaction).toBeDefined();
|
|
4577
|
+
expect(task.data.interaction.callProcessingDetails.campaignId).toBe('campaign-789');
|
|
4578
|
+
});
|
|
4579
|
+
|
|
4580
|
+
it('should emit TASK_CAMPAIGN_PREVIEW_REMOVE_FAILED when CampaignPreviewRemoveFailed is received', () => {
|
|
4581
|
+
const campaignInteractionId = 'campaign-interaction-123';
|
|
4582
|
+
|
|
4583
|
+
const reservationPayload = {
|
|
4584
|
+
data: {
|
|
4585
|
+
type: CC_EVENTS.AGENT_OFFER_CAMPAIGN_RESERVATION,
|
|
4586
|
+
interactionId: campaignInteractionId,
|
|
4587
|
+
agentId: taskDataMock.agentId,
|
|
4588
|
+
orgId: taskDataMock.orgId,
|
|
4589
|
+
trackingId: 'campaign-tracking-456',
|
|
4590
|
+
interaction: {
|
|
4591
|
+
mediaType: 'telephony',
|
|
4592
|
+
callProcessingDetails: {
|
|
4593
|
+
campaignId: 'campaign-789',
|
|
4594
|
+
},
|
|
4595
|
+
},
|
|
4596
|
+
},
|
|
4597
|
+
};
|
|
4598
|
+
|
|
4599
|
+
webSocketManagerMock.emit('message', JSON.stringify(reservationPayload));
|
|
4600
|
+
|
|
4601
|
+
const task = taskManager['taskCollection'][campaignInteractionId];
|
|
4602
|
+
expect(task).toBeDefined();
|
|
4603
|
+
|
|
4604
|
+
const taskEmitSpy = jest.spyOn(task, 'emit');
|
|
4605
|
+
|
|
4606
|
+
const failPayload = {
|
|
4607
|
+
data: {
|
|
4608
|
+
type: CC_EVENTS.CAMPAIGN_PREVIEW_REMOVE_FAILED,
|
|
4609
|
+
interactionId: campaignInteractionId,
|
|
4610
|
+
campaignId: 'campaign-789',
|
|
4611
|
+
reason: 'INTERNAL_ERROR',
|
|
4612
|
+
},
|
|
4613
|
+
};
|
|
4614
|
+
|
|
4615
|
+
webSocketManagerMock.emit('message', JSON.stringify(failPayload));
|
|
4616
|
+
|
|
4617
|
+
expect(taskEmitSpy).toHaveBeenCalledWith(
|
|
4618
|
+
TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_REMOVE_FAILED,
|
|
4619
|
+
expect.objectContaining({
|
|
4620
|
+
data: expect.objectContaining({interactionId: campaignInteractionId}),
|
|
4621
|
+
})
|
|
4622
|
+
);
|
|
4623
|
+
expect(taskManager['taskCollection'][campaignInteractionId]).toBeDefined();
|
|
4624
|
+
|
|
4625
|
+
// Failure payload (reason) should be merged into task.data
|
|
4626
|
+
expect(task.data.reason).toBe('INTERNAL_ERROR');
|
|
4627
|
+
|
|
4628
|
+
// Original reservation data must be preserved
|
|
4629
|
+
expect(task.data.interaction).toBeDefined();
|
|
4630
|
+
expect(task.data.interaction.callProcessingDetails.campaignId).toBe('campaign-789');
|
|
4631
|
+
});
|
|
4632
|
+
|
|
4633
|
+
it('should not emit campaign preview failure events when task does not exist', () => {
|
|
4634
|
+
const nonExistentId = 'non-existent-interaction';
|
|
4635
|
+
|
|
4636
|
+
const failPayload = {
|
|
4637
|
+
data: {
|
|
4638
|
+
type: CC_EVENTS.CAMPAIGN_PREVIEW_SKIP_FAILED,
|
|
4639
|
+
interactionId: nonExistentId,
|
|
4640
|
+
campaignId: 'campaign-789',
|
|
4641
|
+
reason: 'INTERNAL_ERROR',
|
|
4642
|
+
},
|
|
4643
|
+
};
|
|
4644
|
+
|
|
4645
|
+
// Should not throw when task is not found
|
|
4646
|
+
expect(() => {
|
|
4647
|
+
webSocketManagerMock.emit('message', JSON.stringify(failPayload));
|
|
4648
|
+
}).not.toThrow();
|
|
4649
|
+
|
|
4650
|
+
expect(taskManager['taskCollection'][nonExistentId]).toBeUndefined();
|
|
4651
|
+
>>>>>>> 81672300901738f14289fd2a7414c6c4b1389aa6
|
|
2463
4652
|
});
|
|
2464
4653
|
});
|
|
2465
4654
|
});
|