@webex/contact-center 3.11.0 → 3.12.0-mobius-socket.1
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 +121 -28
- package/dist/cc.js.map +1 -1
- package/dist/constants.js +5 -1
- package/dist/constants.js.map +1 -1
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -1
- package/dist/metrics/behavioral-events.js +13 -0
- package/dist/metrics/behavioral-events.js.map +1 -1
- package/dist/metrics/constants.js +9 -1
- package/dist/metrics/constants.js.map +1 -1
- package/dist/services/ApiAiAssistant.js +173 -0
- package/dist/services/ApiAiAssistant.js.map +1 -0
- package/dist/services/agent/types.js.map +1 -1
- package/dist/services/config/Util.js +6 -2
- package/dist/services/config/Util.js.map +1 -1
- package/dist/services/config/constants.js +12 -0
- package/dist/services/config/constants.js.map +1 -1
- package/dist/services/config/index.js +41 -2
- package/dist/services/config/index.js.map +1 -1
- package/dist/services/config/types.js +19 -1
- package/dist/services/config/types.js.map +1 -1
- package/dist/services/constants.js +27 -1
- package/dist/services/constants.js.map +1 -1
- package/dist/services/core/Err.js.map +1 -1
- package/dist/services/core/Utils.js +28 -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 +20 -5
- package/dist/services/core/websocket/WebSocketManager.js.map +1 -1
- package/dist/services/core/websocket/connection-service.js +3 -1
- package/dist/services/core/websocket/connection-service.js.map +1 -1
- package/dist/services/index.js +6 -0
- package/dist/services/index.js.map +1 -1
- package/dist/services/task/TaskManager.js +117 -24
- package/dist/services/task/TaskManager.js.map +1 -1
- package/dist/services/task/TaskUtils.js +16 -3
- package/dist/services/task/TaskUtils.js.map +1 -1
- package/dist/services/task/constants.js +15 -1
- package/dist/services/task/constants.js.map +1 -1
- package/dist/services/task/dialer.js +51 -0
- package/dist/services/task/dialer.js.map +1 -1
- package/dist/services/task/types.js +15 -0
- package/dist/services/task/types.js.map +1 -1
- package/dist/types/cc.d.ts +801 -0
- package/dist/types/config.d.ts +66 -0
- package/dist/types/constants.d.ts +50 -0
- package/dist/types/index.d.ts +184 -0
- package/dist/types/logger-proxy.d.ts +71 -0
- package/dist/types/metrics/MetricsManager.d.ts +223 -0
- package/dist/types/metrics/behavioral-events.d.ts +29 -0
- package/dist/types/metrics/constants.d.ts +161 -0
- package/dist/types/services/AddressBook.d.ts +74 -0
- package/dist/types/services/ApiAiAssistant.d.ts +31 -0
- package/dist/types/services/EntryPoint.d.ts +67 -0
- package/dist/types/services/Queue.d.ts +76 -0
- package/dist/types/services/WebCallingService.d.ts +1 -0
- package/dist/types/services/agent/index.d.ts +46 -0
- package/dist/types/services/agent/types.d.ts +413 -0
- package/dist/types/services/config/Util.d.ts +20 -0
- package/dist/types/services/config/constants.d.ts +249 -0
- package/dist/types/services/config/index.d.ts +177 -0
- package/dist/types/services/config/types.d.ts +1207 -0
- package/dist/types/services/constants.d.ts +110 -0
- package/dist/types/services/core/Err.d.ts +121 -0
- package/dist/types/services/core/GlobalTypes.d.ts +58 -0
- package/dist/types/services/core/Utils.d.ts +101 -0
- package/dist/types/services/core/WebexRequest.d.ts +22 -0
- package/dist/types/services/core/aqm-reqs.d.ts +65 -0
- package/dist/types/services/core/constants.d.ts +99 -0
- package/dist/types/services/core/types.d.ts +47 -0
- package/dist/types/services/core/websocket/WebSocketManager.d.ts +35 -0
- package/dist/types/services/core/websocket/connection-service.d.ts +27 -0
- package/dist/types/services/core/websocket/keepalive.worker.d.ts +2 -0
- package/dist/types/services/core/websocket/types.d.ts +37 -0
- package/dist/types/services/index.d.ts +54 -0
- package/dist/types/services/task/AutoWrapup.d.ts +40 -0
- package/dist/types/services/task/TaskManager.d.ts +1 -0
- package/dist/types/services/task/TaskUtils.d.ts +92 -0
- package/dist/types/services/task/constants.d.ts +84 -0
- package/dist/types/services/task/contact.d.ts +69 -0
- package/dist/types/services/task/dialer.d.ts +43 -0
- package/dist/types/services/task/index.d.ts +650 -0
- package/dist/types/services/task/types.d.ts +1319 -0
- package/dist/types/types.d.ts +643 -0
- package/dist/types/utils/PageCache.d.ts +173 -0
- package/dist/types/webex-config.d.ts +53 -0
- package/dist/types/webex.d.ts +7 -0
- package/dist/types.js +14 -1
- package/dist/types.js.map +1 -1
- package/dist/webex.js +1 -1
- package/package.json +9 -9
- package/src/cc.ts +157 -30
- package/src/constants.ts +4 -0
- package/src/index.ts +1 -0
- package/src/metrics/behavioral-events.ts +14 -0
- package/src/metrics/constants.ts +11 -0
- package/src/services/ApiAiAssistant.ts +217 -0
- package/src/services/agent/types.ts +1 -1
- package/src/services/config/Util.ts +8 -0
- package/src/services/config/constants.ts +12 -0
- package/src/services/config/index.ts +45 -1
- package/src/services/config/types.ts +67 -0
- package/src/services/constants.ts +29 -0
- package/src/services/core/Err.ts +1 -0
- package/src/services/core/Utils.ts +32 -5
- package/src/services/core/aqm-reqs.ts +100 -22
- package/src/services/core/websocket/WebSocketManager.ts +21 -6
- package/src/services/core/websocket/connection-service.ts +5 -1
- package/src/services/index.ts +4 -0
- package/src/services/task/TaskManager.ts +174 -27
- package/src/services/task/TaskUtils.ts +12 -0
- package/src/services/task/constants.ts +16 -0
- package/src/services/task/dialer.ts +56 -1
- package/src/services/task/types.ts +24 -0
- package/src/types.ts +40 -1
- package/test/unit/spec/cc.ts +163 -23
- package/test/unit/spec/services/ApiAiAssistant.ts +115 -0
- package/test/unit/spec/services/config/index.ts +56 -0
- package/test/unit/spec/services/core/Utils.ts +63 -1
- package/test/unit/spec/services/core/websocket/WebSocketManager.ts +82 -12
- package/test/unit/spec/services/core/websocket/connection-service.ts +3 -1
- package/test/unit/spec/services/task/TaskManager.ts +1119 -251
- package/test/unit/spec/services/task/dialer.ts +198 -112
- package/umd/contact-center.min.js +2 -2
- package/umd/contact-center.min.js.map +1 -1
|
@@ -13,6 +13,7 @@ import {CC_TASK_EVENTS} from '../../../../../src/services/config/types';
|
|
|
13
13
|
|
|
14
14
|
describe('TaskManager', () => {
|
|
15
15
|
let mockCall;
|
|
16
|
+
let mockApiAIAssistant;
|
|
16
17
|
let webSocketManagerMock;
|
|
17
18
|
let onSpy;
|
|
18
19
|
let offSpy;
|
|
@@ -45,6 +46,15 @@ describe('TaskManager', () => {
|
|
|
45
46
|
beforeEach(() => {
|
|
46
47
|
contactMock = contact;
|
|
47
48
|
webSocketManagerMock = new EventEmitter();
|
|
49
|
+
mockApiAIAssistant = {
|
|
50
|
+
sendEvent: jest.fn().mockResolvedValue({}),
|
|
51
|
+
setAIFeatureFlags: jest.fn(),
|
|
52
|
+
aiFeature: {
|
|
53
|
+
realtimeTranscripts: {
|
|
54
|
+
enable: true,
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
};
|
|
48
58
|
|
|
49
59
|
webex = {
|
|
50
60
|
logger: {
|
|
@@ -74,14 +84,19 @@ describe('TaskManager', () => {
|
|
|
74
84
|
onSpy = jest.spyOn(webCallingService, 'on');
|
|
75
85
|
offSpy = jest.spyOn(webCallingService, 'off');
|
|
76
86
|
|
|
77
|
-
taskManager = new TaskManager(contactMock, webCallingService, webSocketManagerMock);
|
|
78
|
-
|
|
87
|
+
taskManager = new TaskManager(mockApiAIAssistant, contactMock, webCallingService, webSocketManagerMock);
|
|
88
|
+
const taskMock = {
|
|
79
89
|
emit: jest.fn(),
|
|
80
90
|
accept: jest.fn(),
|
|
81
91
|
decline: jest.fn(),
|
|
82
|
-
updateTaskData: jest.fn()
|
|
92
|
+
updateTaskData: jest.fn().mockImplementation((updatedData) => {
|
|
93
|
+
taskMock.data = {...taskMock.data, ...updatedData};
|
|
94
|
+
return taskMock;
|
|
95
|
+
}),
|
|
83
96
|
data: taskDataMock,
|
|
84
97
|
};
|
|
98
|
+
taskManager.taskCollection[taskId] = taskMock;
|
|
99
|
+
taskManager.agentId = 'test-agent-id';
|
|
85
100
|
taskManager.call = mockCall;
|
|
86
101
|
});
|
|
87
102
|
|
|
@@ -102,14 +117,15 @@ describe('TaskManager', () => {
|
|
|
102
117
|
|
|
103
118
|
incomingCallCb(mockCall);
|
|
104
119
|
|
|
105
|
-
expect(taskEmitSpy).toHaveBeenCalledWith(
|
|
120
|
+
expect(taskEmitSpy).toHaveBeenCalledWith(
|
|
121
|
+
TASK_EVENTS.TASK_INCOMING,
|
|
122
|
+
taskManager.getTask(taskId)
|
|
123
|
+
);
|
|
106
124
|
});
|
|
107
125
|
|
|
108
126
|
it('should re-emit task related events', () => {
|
|
109
127
|
const dummyPayload = {
|
|
110
|
-
data: {...taskDataMock,
|
|
111
|
-
type: CC_TASK_EVENTS.AGENT_CONSULTING,
|
|
112
|
-
},
|
|
128
|
+
data: {...taskDataMock, type: CC_TASK_EVENTS.AGENT_CONSULTING},
|
|
113
129
|
};
|
|
114
130
|
webSocketManagerMock.emit('message', JSON.stringify({data: taskDataMock}));
|
|
115
131
|
const taskEmitSpy = jest.spyOn(taskManager.getTask(taskId), 'emit');
|
|
@@ -123,6 +139,106 @@ describe('TaskManager', () => {
|
|
|
123
139
|
expect(taskEmitSpy).toHaveBeenCalledWith(dummyPayload.data.type, dummyPayload.data);
|
|
124
140
|
});
|
|
125
141
|
|
|
142
|
+
it('should invoke sendEvent for configured start/stop backend events', () => {
|
|
143
|
+
const message = (type: CC_EVENTS) =>
|
|
144
|
+
JSON.stringify({
|
|
145
|
+
data: {
|
|
146
|
+
...taskDataMock,
|
|
147
|
+
taskId,
|
|
148
|
+
type,
|
|
149
|
+
},
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
webSocketManagerMock.emit('message', message(CC_EVENTS.AGENT_CONTACT_ASSIGNED));
|
|
153
|
+
webSocketManagerMock.emit('message', message(CC_EVENTS.AGENT_CONSULTING));
|
|
154
|
+
webSocketManagerMock.emit('message', message(CC_EVENTS.AGENT_CONSULT_CONFERENCED));
|
|
155
|
+
webSocketManagerMock.emit('message', message(CC_EVENTS.AGENT_CONSULT_ENDED));
|
|
156
|
+
webSocketManagerMock.emit('message', message(CC_EVENTS.AGENT_WRAPUP));
|
|
157
|
+
webSocketManagerMock.emit('message', message(CC_EVENTS.PARTICIPANT_LEFT_CONFERENCE));
|
|
158
|
+
|
|
159
|
+
expect(mockApiAIAssistant.sendEvent).toHaveBeenCalledTimes(6);
|
|
160
|
+
expect(mockApiAIAssistant.sendEvent).toHaveBeenCalledWith(
|
|
161
|
+
'test-agent-id',
|
|
162
|
+
taskId,
|
|
163
|
+
'CUSTOM_EVENT',
|
|
164
|
+
'GET_TRANSCRIPTS',
|
|
165
|
+
'START'
|
|
166
|
+
);
|
|
167
|
+
expect(mockApiAIAssistant.sendEvent).toHaveBeenCalledWith(
|
|
168
|
+
'test-agent-id',
|
|
169
|
+
taskId,
|
|
170
|
+
'CUSTOM_EVENT',
|
|
171
|
+
'GET_TRANSCRIPTS',
|
|
172
|
+
'STOP'
|
|
173
|
+
);
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
it('should not invoke sendEvent for transcript events when realtime transcript feature is disabled', () => {
|
|
177
|
+
mockApiAIAssistant.aiFeature = {
|
|
178
|
+
realtimeTranscripts: {
|
|
179
|
+
enable: false,
|
|
180
|
+
},
|
|
181
|
+
};
|
|
182
|
+
mockApiAIAssistant.setAIFeatureFlags(mockApiAIAssistant.aiFeature);
|
|
183
|
+
|
|
184
|
+
const message = (type: CC_EVENTS) =>
|
|
185
|
+
JSON.stringify({
|
|
186
|
+
data: {
|
|
187
|
+
...taskDataMock,
|
|
188
|
+
taskId,
|
|
189
|
+
type,
|
|
190
|
+
},
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
webSocketManagerMock.emit('message', message(CC_EVENTS.AGENT_CONTACT_ASSIGNED));
|
|
194
|
+
webSocketManagerMock.emit('message', message(CC_EVENTS.AGENT_CONSULTING));
|
|
195
|
+
webSocketManagerMock.emit('message', message(CC_EVENTS.AGENT_CONSULT_CONFERENCED));
|
|
196
|
+
webSocketManagerMock.emit('message', message(CC_EVENTS.AGENT_CONSULT_ENDED));
|
|
197
|
+
webSocketManagerMock.emit('message', message(CC_EVENTS.AGENT_WRAPUP));
|
|
198
|
+
webSocketManagerMock.emit('message', message(CC_EVENTS.PARTICIPANT_LEFT_CONFERENCE));
|
|
199
|
+
|
|
200
|
+
expect(mockApiAIAssistant.sendEvent).not.toHaveBeenCalled();
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
it('should emit REAL_TIME_TRANSCRIPTION from task object', () => {
|
|
204
|
+
const task = taskManager.getTask(taskId);
|
|
205
|
+
const taskEmitSpy = jest.spyOn(task, 'emit');
|
|
206
|
+
const realtimePayload = {
|
|
207
|
+
data: {
|
|
208
|
+
...taskDataMock,
|
|
209
|
+
type: CC_EVENTS.REAL_TIME_TRANSCRIPTION,
|
|
210
|
+
data: {
|
|
211
|
+
content: 'hello from transcript',
|
|
212
|
+
},
|
|
213
|
+
},
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
webSocketManagerMock.emit('message', JSON.stringify(realtimePayload));
|
|
217
|
+
|
|
218
|
+
expect(taskEmitSpy).toHaveBeenCalledWith(
|
|
219
|
+
CC_EVENTS.REAL_TIME_TRANSCRIPTION,
|
|
220
|
+
realtimePayload.data
|
|
221
|
+
);
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
it('should emit REAL_TIME_TRANSCRIPTION from RTD websocket payload on task object', () => {
|
|
225
|
+
const task = taskManager.getTask(taskId);
|
|
226
|
+
const taskEmitSpy = jest.spyOn(task, 'emit');
|
|
227
|
+
const realtimePayload = {
|
|
228
|
+
data: {
|
|
229
|
+
notifType: CC_EVENTS.REAL_TIME_TRANSCRIPTION,
|
|
230
|
+
data: {
|
|
231
|
+
conversationId: taskId,
|
|
232
|
+
content: 'hello from rtd websocket',
|
|
233
|
+
},
|
|
234
|
+
},
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
taskManager.handleRealtimeWebsocketEvent(JSON.stringify(realtimePayload));
|
|
238
|
+
|
|
239
|
+
expect(taskEmitSpy).toHaveBeenCalledWith(CC_EVENTS.REAL_TIME_TRANSCRIPTION, realtimePayload.data);
|
|
240
|
+
});
|
|
241
|
+
|
|
126
242
|
it('should not re-emit agent related events', () => {
|
|
127
243
|
const dummyPayload = {
|
|
128
244
|
data: {
|
|
@@ -187,7 +303,10 @@ describe('TaskManager', () => {
|
|
|
187
303
|
},
|
|
188
304
|
};
|
|
189
305
|
|
|
190
|
-
const currentTaskAssignedSpy = jest.spyOn(
|
|
306
|
+
const currentTaskAssignedSpy = jest.spyOn(
|
|
307
|
+
taskManager.getTask(payload.data.interactionId),
|
|
308
|
+
'emit'
|
|
309
|
+
);
|
|
191
310
|
|
|
192
311
|
webSocketManagerMock.emit('message', JSON.stringify(assignedPayload));
|
|
193
312
|
|
|
@@ -311,7 +430,10 @@ describe('TaskManager', () => {
|
|
|
311
430
|
webSocketManagerMock.emit('message', JSON.stringify(initalPayload));
|
|
312
431
|
|
|
313
432
|
const taskEmitSpy = jest.spyOn(taskManager.getTask(taskId), 'emit');
|
|
314
|
-
const webCallListenerSpy = jest.spyOn(
|
|
433
|
+
const webCallListenerSpy = jest.spyOn(
|
|
434
|
+
taskManager.getTask(taskId),
|
|
435
|
+
'unregisterWebCallListeners'
|
|
436
|
+
);
|
|
315
437
|
const callOffSpy = jest.spyOn(mockCall, 'off');
|
|
316
438
|
const payload = {
|
|
317
439
|
data: {
|
|
@@ -331,11 +453,9 @@ describe('TaskManager', () => {
|
|
|
331
453
|
};
|
|
332
454
|
|
|
333
455
|
taskManager.getTask(taskId).data = payload.data;
|
|
334
|
-
const task = taskManager.getTask(taskId)
|
|
456
|
+
const task = taskManager.getTask(taskId);
|
|
335
457
|
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
336
|
-
expect(taskEmitSpy).toHaveBeenCalledWith(
|
|
337
|
-
TASK_EVENTS.TASK_END, task
|
|
338
|
-
);
|
|
458
|
+
expect(taskEmitSpy).toHaveBeenCalledWith(TASK_EVENTS.TASK_END, task);
|
|
339
459
|
expect(webCallListenerSpy).toHaveBeenCalledWith();
|
|
340
460
|
expect(callOffSpy).toHaveBeenCalledWith(
|
|
341
461
|
CALL_EVENT_KEYS.REMOTE_MEDIA,
|
|
@@ -371,55 +491,42 @@ describe('TaskManager', () => {
|
|
|
371
491
|
|
|
372
492
|
taskManager.getTask(taskId).updateTaskData(payload.data);
|
|
373
493
|
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
374
|
-
expect(taskEmitSpy).toHaveBeenCalledWith(
|
|
375
|
-
|
|
376
|
-
{ ...payload.data}
|
|
377
|
-
);
|
|
378
|
-
expect(taskEmitSpy).toHaveBeenCalledWith(
|
|
379
|
-
TASK_EVENTS.TASK_END,
|
|
380
|
-
taskManager.getTask(taskId)
|
|
381
|
-
);
|
|
494
|
+
expect(taskEmitSpy).toHaveBeenCalledWith(CC_EVENTS.CONTACT_ENDED, {...payload.data});
|
|
495
|
+
expect(taskEmitSpy).toHaveBeenCalledWith(TASK_EVENTS.TASK_END, taskManager.getTask(taskId));
|
|
382
496
|
});
|
|
383
497
|
|
|
384
498
|
it('should emit TASK_REJECT event on AGENT_INVITE_FAILED event', () => {
|
|
385
499
|
webSocketManagerMock.emit('message', JSON.stringify(initalPayload));
|
|
386
500
|
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
501
|
+
const taskEmitSpy = jest.spyOn(taskManager.getTask(taskId), 'emit');
|
|
502
|
+
const metricsTrackSpy = jest.spyOn(taskManager.metricsManager, 'trackEvent');
|
|
503
|
+
const payload = {
|
|
504
|
+
data: {
|
|
505
|
+
type: CC_EVENTS.AGENT_INVITE_FAILED,
|
|
506
|
+
agentId: '723a8ffb-a26e-496d-b14a-ff44fb83b64f',
|
|
507
|
+
eventTime: 1733211616959,
|
|
508
|
+
eventType: 'RoutingMessage',
|
|
509
|
+
interaction: {state: 'connected'},
|
|
510
|
+
interactionId: taskId,
|
|
511
|
+
orgId: '6ecef209-9a34-4ed1-a07a-7ddd1dbe925a',
|
|
512
|
+
trackingId: '575c0ec2-618c-42af-a61c-53aeb0a221ee',
|
|
513
|
+
mediaResourceId: '0ae913a4-c857-4705-8d49-76dd3dde75e4',
|
|
514
|
+
destAgentId: 'ebeb893b-ba67-4f36-8418-95c7492b28c2',
|
|
515
|
+
owner: '723a8ffb-a26e-496d-b14a-ff44fb83b64f',
|
|
516
|
+
queueMgr: 'aqm',
|
|
517
|
+
reason: 'INVITE_FAILED',
|
|
518
|
+
},
|
|
519
|
+
};
|
|
406
520
|
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
TASK_EVENTS.TASK_REJECT,
|
|
415
|
-
payload.data.reason
|
|
416
|
-
);
|
|
417
|
-
// Verify the correct metric event name is used for AGENT_INVITE_FAILED
|
|
418
|
-
expect(metricsTrackSpy).toHaveBeenCalled();
|
|
419
|
-
expect(metricsTrackSpy.mock.calls[0][0]).toBe('Agent Invite Failed');
|
|
521
|
+
taskManager.getTask(taskId).updateTaskData(payload.data);
|
|
522
|
+
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
523
|
+
expect(taskEmitSpy).toHaveBeenCalledWith(CC_EVENTS.AGENT_INVITE_FAILED, {...payload.data});
|
|
524
|
+
expect(taskEmitSpy).toHaveBeenCalledWith(TASK_EVENTS.TASK_REJECT, payload.data.reason);
|
|
525
|
+
// Verify the correct metric event name is used for AGENT_INVITE_FAILED
|
|
526
|
+
expect(metricsTrackSpy).toHaveBeenCalled();
|
|
527
|
+
expect(metricsTrackSpy.mock.calls[0][0]).toBe('Agent Invite Failed');
|
|
420
528
|
});
|
|
421
529
|
|
|
422
|
-
|
|
423
530
|
it('should not emit TASK_HYDRATE if task is already present in taskManager', () => {
|
|
424
531
|
const payload = {
|
|
425
532
|
data: {
|
|
@@ -483,7 +590,7 @@ describe('TaskManager', () => {
|
|
|
483
590
|
const testAgentId = '723a8ffb-a26e-496d-b14a-ff44fb83b64f';
|
|
484
591
|
taskManager.setAgentId(testAgentId);
|
|
485
592
|
taskManager.taskCollection = [];
|
|
486
|
-
|
|
593
|
+
|
|
487
594
|
const payload = {
|
|
488
595
|
data: {
|
|
489
596
|
...initalPayload.data,
|
|
@@ -492,9 +599,9 @@ describe('TaskManager', () => {
|
|
|
492
599
|
mediaType: 'telephony',
|
|
493
600
|
state: 'conference',
|
|
494
601
|
participants: {
|
|
495
|
-
[testAgentId]: {
|
|
496
|
-
'agent-2': {
|
|
497
|
-
'customer-1': {
|
|
602
|
+
[testAgentId]: {pType: 'Agent', hasLeft: false},
|
|
603
|
+
'agent-2': {pType: 'Agent', hasLeft: false},
|
|
604
|
+
'customer-1': {pType: 'Customer', hasLeft: false},
|
|
498
605
|
},
|
|
499
606
|
media: {
|
|
500
607
|
[taskId]: {
|
|
@@ -517,7 +624,7 @@ describe('TaskManager', () => {
|
|
|
517
624
|
const testAgentId = '723a8ffb-a26e-496d-b14a-ff44fb83b64f';
|
|
518
625
|
taskManager.setAgentId(testAgentId);
|
|
519
626
|
taskManager.taskCollection = [];
|
|
520
|
-
|
|
627
|
+
|
|
521
628
|
const payload = {
|
|
522
629
|
data: {
|
|
523
630
|
...initalPayload.data,
|
|
@@ -526,8 +633,8 @@ describe('TaskManager', () => {
|
|
|
526
633
|
mediaType: 'telephony',
|
|
527
634
|
state: 'connected',
|
|
528
635
|
participants: {
|
|
529
|
-
[testAgentId]: {
|
|
530
|
-
'customer-1': {
|
|
636
|
+
[testAgentId]: {pType: 'Agent', hasLeft: false},
|
|
637
|
+
'customer-1': {pType: 'Customer', hasLeft: false},
|
|
531
638
|
},
|
|
532
639
|
media: {
|
|
533
640
|
[taskId]: {
|
|
@@ -563,7 +670,7 @@ describe('TaskManager', () => {
|
|
|
563
670
|
destAgentId: 'ebeb893b-ba67-4f36-8418-95c7492b28c2',
|
|
564
671
|
owner: '723a8ffb-a26e-496d-b14a-ff44fb83b64f',
|
|
565
672
|
queueMgr: 'aqm',
|
|
566
|
-
wrapUpRequired: true
|
|
673
|
+
wrapUpRequired: true,
|
|
567
674
|
},
|
|
568
675
|
};
|
|
569
676
|
|
|
@@ -716,7 +823,9 @@ describe('TaskManager', () => {
|
|
|
716
823
|
|
|
717
824
|
const task = taskManager.getTask(taskId);
|
|
718
825
|
const taskEmitSpy = jest.spyOn(task, 'emit');
|
|
719
|
-
const taskAcceptSpy = jest
|
|
826
|
+
const taskAcceptSpy = jest
|
|
827
|
+
.spyOn(task, 'accept')
|
|
828
|
+
.mockRejectedValue(new Error('Accept failed'));
|
|
720
829
|
|
|
721
830
|
// Step 2: Trigger AGENT_OFFER_CONTACT with auto-answer (will fail)
|
|
722
831
|
const autoAnswerPayload = {
|
|
@@ -778,7 +887,7 @@ describe('TaskManager', () => {
|
|
|
778
887
|
// Verify BOTH events were emitted
|
|
779
888
|
expect(taskEmitSpy).toHaveBeenCalledWith(TASK_EVENTS.TASK_OFFER_CONSULT, task);
|
|
780
889
|
expect(taskEmitSpy).toHaveBeenCalledWith(TASK_EVENTS.TASK_AUTO_ANSWERED, task);
|
|
781
|
-
|
|
890
|
+
|
|
782
891
|
// Verify isConsulted flag is set correctly
|
|
783
892
|
expect(task.data.isConsulted).toBe(true);
|
|
784
893
|
});
|
|
@@ -814,7 +923,10 @@ describe('TaskManager', () => {
|
|
|
814
923
|
expect(taskAcceptSpy).not.toHaveBeenCalled();
|
|
815
924
|
|
|
816
925
|
// Verify TASK_AUTO_ANSWERED event was NOT emitted
|
|
817
|
-
expect(taskEmitSpy).not.toHaveBeenCalledWith(
|
|
926
|
+
expect(taskEmitSpy).not.toHaveBeenCalledWith(
|
|
927
|
+
TASK_EVENTS.TASK_AUTO_ANSWERED,
|
|
928
|
+
expect.anything()
|
|
929
|
+
);
|
|
818
930
|
});
|
|
819
931
|
});
|
|
820
932
|
|
|
@@ -896,6 +1008,9 @@ describe('TaskManager', () => {
|
|
|
896
1008
|
});
|
|
897
1009
|
|
|
898
1010
|
it('should NOT remove OUTDIAL task on CONTACT_ENDED when agentsPendingWrapUp exists', () => {
|
|
1011
|
+
const agentId = '723a8ffb-a26e-496d-b14a-ff44fb83b64f';
|
|
1012
|
+
taskManager.setAgentId(agentId);
|
|
1013
|
+
|
|
899
1014
|
const task = taskManager.getTask(taskId);
|
|
900
1015
|
task.updateTaskData = jest.fn().mockImplementation((newData) => {
|
|
901
1016
|
task.data = {
|
|
@@ -907,7 +1022,7 @@ describe('TaskManager', () => {
|
|
|
907
1022
|
state: 'new',
|
|
908
1023
|
mediaType: 'telephony',
|
|
909
1024
|
},
|
|
910
|
-
agentsPendingWrapUp: [
|
|
1025
|
+
agentsPendingWrapUp: [agentId],
|
|
911
1026
|
};
|
|
912
1027
|
return task;
|
|
913
1028
|
});
|
|
@@ -923,7 +1038,7 @@ describe('TaskManager', () => {
|
|
|
923
1038
|
state: 'new',
|
|
924
1039
|
mediaType: 'telephony',
|
|
925
1040
|
},
|
|
926
|
-
agentsPendingWrapUp: [
|
|
1041
|
+
agentsPendingWrapUp: [agentId],
|
|
927
1042
|
},
|
|
928
1043
|
};
|
|
929
1044
|
|
|
@@ -1023,107 +1138,370 @@ describe('TaskManager', () => {
|
|
|
1023
1138
|
}).not.toThrow();
|
|
1024
1139
|
});
|
|
1025
1140
|
|
|
1026
|
-
|
|
1027
|
-
const
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1141
|
+
describe('wrapUpRequired logic in CONTACT_ENDED event', () => {
|
|
1142
|
+
const agentId = '723a8ffb-a26e-496d-b14a-ff44fb83b64f';
|
|
1143
|
+
|
|
1144
|
+
beforeEach(() => {
|
|
1145
|
+
// Set the agent ID on taskManager
|
|
1146
|
+
taskManager.setAgentId(agentId);
|
|
1147
|
+
});
|
|
1148
|
+
|
|
1149
|
+
it('should set wrapUpRequired to true when agent is in agentsPendingWrapUp array', () => {
|
|
1150
|
+
const task = taskManager.getTask(taskId);
|
|
1151
|
+
task.updateTaskData = jest.fn().mockImplementation((newData) => {
|
|
1152
|
+
task.data = {
|
|
1153
|
+
...task.data,
|
|
1154
|
+
...newData,
|
|
1155
|
+
};
|
|
1156
|
+
return task;
|
|
1157
|
+
});
|
|
1158
|
+
task.unregisterWebCallListeners = jest.fn();
|
|
1159
|
+
|
|
1160
|
+
const payload = {
|
|
1161
|
+
data: {
|
|
1162
|
+
type: CC_EVENTS.CONTACT_ENDED,
|
|
1163
|
+
interactionId: taskId,
|
|
1164
|
+
interaction: {
|
|
1165
|
+
state: 'connected',
|
|
1166
|
+
mediaType: 'telephony',
|
|
1167
|
+
},
|
|
1168
|
+
agentsPendingWrapUp: [agentId, 'other-agent-id'],
|
|
1038
1169
|
},
|
|
1039
1170
|
};
|
|
1040
|
-
|
|
1171
|
+
|
|
1172
|
+
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
1173
|
+
|
|
1174
|
+
expect(task.updateTaskData).toHaveBeenCalledWith(
|
|
1175
|
+
expect.objectContaining({
|
|
1176
|
+
wrapUpRequired: true,
|
|
1177
|
+
})
|
|
1178
|
+
);
|
|
1041
1179
|
});
|
|
1042
|
-
task.unregisterWebCallListeners = jest.fn();
|
|
1043
|
-
const removeTaskSpy = jest.spyOn(taskManager, 'removeTaskFromCollection');
|
|
1044
1180
|
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1181
|
+
it('should set wrapUpRequired to false when agent is not in agentsPendingWrapUp array', () => {
|
|
1182
|
+
const task = taskManager.getTask(taskId);
|
|
1183
|
+
task.updateTaskData = jest.fn().mockImplementation((newData) => {
|
|
1184
|
+
task.data = {
|
|
1185
|
+
...task.data,
|
|
1186
|
+
...newData,
|
|
1187
|
+
};
|
|
1188
|
+
return task;
|
|
1189
|
+
});
|
|
1190
|
+
task.unregisterWebCallListeners = jest.fn();
|
|
1191
|
+
|
|
1192
|
+
const payload = {
|
|
1193
|
+
data: {
|
|
1194
|
+
type: CC_EVENTS.CONTACT_ENDED,
|
|
1195
|
+
interactionId: taskId,
|
|
1196
|
+
interaction: {
|
|
1197
|
+
state: 'connected',
|
|
1198
|
+
mediaType: 'telephony',
|
|
1199
|
+
},
|
|
1200
|
+
agentsPendingWrapUp: ['other-agent-id', 'another-agent-id'],
|
|
1055
1201
|
},
|
|
1056
|
-
|
|
1057
|
-
orgId: '6ecef209-9a34-4ed1-a07a-7ddd1dbe925a',
|
|
1058
|
-
trackingId: '575c0ec2-618c-42af-a61c-53aeb0a221ee',
|
|
1059
|
-
mediaResourceId: '0ae913a4-c857-4705-8d49-76dd3dde75e4',
|
|
1060
|
-
destAgentId: 'ebeb893b-ba67-4f36-8418-95c7492b28c2',
|
|
1061
|
-
owner: '723a8ffb-a26e-496d-b14a-ff44fb83b64f',
|
|
1062
|
-
queueMgr: 'aqm',
|
|
1063
|
-
reason: 'USER_DECLINED',
|
|
1064
|
-
reasonCode: 156,
|
|
1065
|
-
},
|
|
1066
|
-
};
|
|
1202
|
+
};
|
|
1067
1203
|
|
|
1068
|
-
|
|
1204
|
+
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
1069
1205
|
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1206
|
+
expect(task.updateTaskData).toHaveBeenCalledWith(
|
|
1207
|
+
expect.objectContaining({
|
|
1208
|
+
wrapUpRequired: false,
|
|
1209
|
+
})
|
|
1210
|
+
);
|
|
1211
|
+
});
|
|
1073
1212
|
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1213
|
+
it('should set wrapUpRequired to false when agentsPendingWrapUp is an empty array', () => {
|
|
1214
|
+
const task = taskManager.getTask(taskId);
|
|
1215
|
+
task.updateTaskData = jest.fn().mockImplementation((newData) => {
|
|
1216
|
+
task.data = {
|
|
1217
|
+
...task.data,
|
|
1218
|
+
...newData,
|
|
1219
|
+
};
|
|
1220
|
+
return task;
|
|
1221
|
+
});
|
|
1222
|
+
task.unregisterWebCallListeners = jest.fn();
|
|
1081
1223
|
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1224
|
+
const payload = {
|
|
1225
|
+
data: {
|
|
1226
|
+
type: CC_EVENTS.CONTACT_ENDED,
|
|
1227
|
+
interactionId: taskId,
|
|
1228
|
+
interaction: {
|
|
1229
|
+
state: 'connected',
|
|
1230
|
+
mediaType: 'telephony',
|
|
1231
|
+
},
|
|
1232
|
+
agentsPendingWrapUp: [],
|
|
1233
|
+
},
|
|
1234
|
+
};
|
|
1089
1235
|
|
|
1090
|
-
|
|
1236
|
+
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
1091
1237
|
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1238
|
+
expect(task.updateTaskData).toHaveBeenCalledWith(
|
|
1239
|
+
expect.objectContaining({
|
|
1240
|
+
wrapUpRequired: false,
|
|
1241
|
+
})
|
|
1242
|
+
);
|
|
1095
1243
|
});
|
|
1096
|
-
expect(task.data.isConsulted).toBe(true);
|
|
1097
|
-
expect(taskEmitSpy).toHaveBeenCalledWith(TASK_EVENTS.TASK_OFFER_CONSULT, task);
|
|
1098
|
-
});
|
|
1099
1244
|
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1245
|
+
it('should set wrapUpRequired to false when agentsPendingWrapUp is undefined', () => {
|
|
1246
|
+
const task = taskManager.getTask(taskId);
|
|
1247
|
+
task.updateTaskData = jest.fn().mockImplementation((newData) => {
|
|
1248
|
+
task.data = {
|
|
1249
|
+
...task.data,
|
|
1250
|
+
...newData,
|
|
1251
|
+
};
|
|
1252
|
+
return task;
|
|
1253
|
+
});
|
|
1254
|
+
task.unregisterWebCallListeners = jest.fn();
|
|
1107
1255
|
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1256
|
+
const payload = {
|
|
1257
|
+
data: {
|
|
1258
|
+
type: CC_EVENTS.CONTACT_ENDED,
|
|
1259
|
+
interactionId: taskId,
|
|
1260
|
+
interaction: {
|
|
1261
|
+
state: 'connected',
|
|
1262
|
+
mediaType: 'telephony',
|
|
1263
|
+
},
|
|
1264
|
+
// agentsPendingWrapUp is not defined
|
|
1265
|
+
},
|
|
1266
|
+
};
|
|
1114
1267
|
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1268
|
+
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
1269
|
+
|
|
1270
|
+
expect(task.updateTaskData).toHaveBeenCalledWith(
|
|
1271
|
+
expect.objectContaining({
|
|
1272
|
+
wrapUpRequired: false,
|
|
1273
|
+
})
|
|
1274
|
+
);
|
|
1119
1275
|
});
|
|
1120
1276
|
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1277
|
+
it('should set wrapUpRequired to false when agentsPendingWrapUp is null', () => {
|
|
1278
|
+
const task = taskManager.getTask(taskId);
|
|
1279
|
+
task.updateTaskData = jest.fn().mockImplementation((newData) => {
|
|
1280
|
+
task.data = {
|
|
1281
|
+
...task.data,
|
|
1282
|
+
...newData,
|
|
1283
|
+
};
|
|
1284
|
+
return task;
|
|
1285
|
+
});
|
|
1286
|
+
task.unregisterWebCallListeners = jest.fn();
|
|
1287
|
+
|
|
1288
|
+
const payload = {
|
|
1289
|
+
data: {
|
|
1290
|
+
type: CC_EVENTS.CONTACT_ENDED,
|
|
1291
|
+
interactionId: taskId,
|
|
1292
|
+
interaction: {
|
|
1293
|
+
state: 'connected',
|
|
1294
|
+
mediaType: 'telephony',
|
|
1295
|
+
},
|
|
1296
|
+
agentsPendingWrapUp: null,
|
|
1297
|
+
},
|
|
1298
|
+
};
|
|
1299
|
+
|
|
1300
|
+
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
1301
|
+
|
|
1302
|
+
expect(task.updateTaskData).toHaveBeenCalledWith(
|
|
1303
|
+
expect.objectContaining({
|
|
1304
|
+
wrapUpRequired: false,
|
|
1305
|
+
})
|
|
1306
|
+
);
|
|
1307
|
+
});
|
|
1308
|
+
|
|
1309
|
+
it('should set wrapUpRequired correctly when agent is the only one in agentsPendingWrapUp', () => {
|
|
1310
|
+
const task = taskManager.getTask(taskId);
|
|
1311
|
+
task.updateTaskData = jest.fn().mockImplementation((newData) => {
|
|
1312
|
+
task.data = {
|
|
1313
|
+
...task.data,
|
|
1314
|
+
...newData,
|
|
1315
|
+
};
|
|
1316
|
+
return task;
|
|
1317
|
+
});
|
|
1318
|
+
task.unregisterWebCallListeners = jest.fn();
|
|
1319
|
+
|
|
1320
|
+
const payload = {
|
|
1321
|
+
data: {
|
|
1322
|
+
type: CC_EVENTS.CONTACT_ENDED,
|
|
1323
|
+
interactionId: taskId,
|
|
1324
|
+
interaction: {
|
|
1325
|
+
state: 'connected',
|
|
1326
|
+
mediaType: 'telephony',
|
|
1327
|
+
},
|
|
1328
|
+
agentsPendingWrapUp: [agentId],
|
|
1329
|
+
},
|
|
1330
|
+
};
|
|
1331
|
+
|
|
1332
|
+
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
1333
|
+
|
|
1334
|
+
expect(task.updateTaskData).toHaveBeenCalledWith(
|
|
1335
|
+
expect.objectContaining({
|
|
1336
|
+
wrapUpRequired: true,
|
|
1337
|
+
})
|
|
1338
|
+
);
|
|
1339
|
+
});
|
|
1340
|
+
|
|
1341
|
+
it('should work correctly for different interaction states when agent is in agentsPendingWrapUp', () => {
|
|
1342
|
+
const task = taskManager.getTask(taskId);
|
|
1343
|
+
task.updateTaskData = jest.fn().mockImplementation((newData) => {
|
|
1344
|
+
task.data = {
|
|
1345
|
+
...task.data,
|
|
1346
|
+
...newData,
|
|
1347
|
+
interaction: {
|
|
1348
|
+
...task.data.interaction,
|
|
1349
|
+
...newData.interaction,
|
|
1350
|
+
},
|
|
1351
|
+
};
|
|
1352
|
+
return task;
|
|
1353
|
+
});
|
|
1354
|
+
task.unregisterWebCallListeners = jest.fn();
|
|
1355
|
+
|
|
1356
|
+
// Test with 'connected' state
|
|
1357
|
+
const payloadConnected = {
|
|
1358
|
+
data: {
|
|
1359
|
+
type: CC_EVENTS.CONTACT_ENDED,
|
|
1360
|
+
interactionId: taskId,
|
|
1361
|
+
interaction: {
|
|
1362
|
+
state: 'connected',
|
|
1363
|
+
mediaType: 'telephony',
|
|
1364
|
+
},
|
|
1365
|
+
agentsPendingWrapUp: [agentId],
|
|
1366
|
+
},
|
|
1367
|
+
};
|
|
1368
|
+
|
|
1369
|
+
webSocketManagerMock.emit('message', JSON.stringify(payloadConnected));
|
|
1370
|
+
|
|
1371
|
+
// First call should set wrapUpRequired to true
|
|
1372
|
+
expect(task.updateTaskData).toHaveBeenNthCalledWith(
|
|
1373
|
+
1,
|
|
1374
|
+
expect.objectContaining({
|
|
1375
|
+
wrapUpRequired: true,
|
|
1376
|
+
})
|
|
1377
|
+
);
|
|
1378
|
+
|
|
1379
|
+
// Test with 'held' state to verify it still works regardless of state
|
|
1380
|
+
const payloadHeld = {
|
|
1381
|
+
data: {
|
|
1382
|
+
type: CC_EVENTS.CONTACT_ENDED,
|
|
1383
|
+
interactionId: taskId,
|
|
1384
|
+
interaction: {
|
|
1385
|
+
state: 'held',
|
|
1386
|
+
mediaType: 'telephony',
|
|
1387
|
+
},
|
|
1388
|
+
agentsPendingWrapUp: [agentId],
|
|
1389
|
+
},
|
|
1390
|
+
};
|
|
1391
|
+
|
|
1392
|
+
webSocketManagerMock.emit('message', JSON.stringify(payloadHeld));
|
|
1393
|
+
|
|
1394
|
+
// Second call should also set wrapUpRequired to true
|
|
1395
|
+
expect(task.updateTaskData).toHaveBeenNthCalledWith(
|
|
1396
|
+
2,
|
|
1397
|
+
expect.objectContaining({
|
|
1398
|
+
wrapUpRequired: true,
|
|
1399
|
+
})
|
|
1400
|
+
);
|
|
1401
|
+
});
|
|
1402
|
+
});
|
|
1403
|
+
|
|
1404
|
+
it('should remove OUTDIAL task from taskCollection on AGENT_CONTACT_ASSIGN_FAILED when NOT terminated (user-declined)', () => {
|
|
1405
|
+
const task = taskManager.getTask(taskId);
|
|
1406
|
+
task.updateTaskData = jest.fn().mockImplementation((newData) => {
|
|
1407
|
+
task.data = {
|
|
1408
|
+
...task.data,
|
|
1409
|
+
...newData,
|
|
1410
|
+
interaction: {
|
|
1411
|
+
...task.data.interaction,
|
|
1412
|
+
...newData.interaction,
|
|
1413
|
+
outboundType: 'OUTDIAL',
|
|
1414
|
+
state: 'new',
|
|
1415
|
+
isTerminated: false,
|
|
1416
|
+
},
|
|
1417
|
+
};
|
|
1418
|
+
return task;
|
|
1419
|
+
});
|
|
1420
|
+
task.unregisterWebCallListeners = jest.fn();
|
|
1421
|
+
const removeTaskSpy = jest.spyOn(taskManager, 'removeTaskFromCollection');
|
|
1422
|
+
|
|
1423
|
+
const payload = {
|
|
1424
|
+
data: {
|
|
1425
|
+
type: CC_EVENTS.AGENT_CONTACT_ASSIGN_FAILED,
|
|
1426
|
+
agentId: '723a8ffb-a26e-496d-b14a-ff44fb83b64f',
|
|
1427
|
+
eventTime: 1733211616959,
|
|
1428
|
+
eventType: 'RoutingMessage',
|
|
1429
|
+
interaction: {
|
|
1430
|
+
outboundType: 'OUTDIAL',
|
|
1431
|
+
state: 'new',
|
|
1432
|
+
isTerminated: false,
|
|
1433
|
+
},
|
|
1434
|
+
interactionId: taskId,
|
|
1435
|
+
orgId: '6ecef209-9a34-4ed1-a07a-7ddd1dbe925a',
|
|
1436
|
+
trackingId: '575c0ec2-618c-42af-a61c-53aeb0a221ee',
|
|
1437
|
+
mediaResourceId: '0ae913a4-c857-4705-8d49-76dd3dde75e4',
|
|
1438
|
+
destAgentId: 'ebeb893b-ba67-4f36-8418-95c7492b28c2',
|
|
1439
|
+
owner: '723a8ffb-a26e-496d-b14a-ff44fb83b64f',
|
|
1440
|
+
queueMgr: 'aqm',
|
|
1441
|
+
reason: 'USER_DECLINED',
|
|
1442
|
+
reasonCode: 156,
|
|
1443
|
+
},
|
|
1444
|
+
};
|
|
1445
|
+
|
|
1446
|
+
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
1447
|
+
|
|
1448
|
+
expect(taskManager.getTask(taskId)).toBeUndefined();
|
|
1449
|
+
expect(removeTaskSpy).toHaveBeenCalled();
|
|
1450
|
+
});
|
|
1451
|
+
|
|
1452
|
+
it('handle AGENT_OFFER_CONSULT event', () => {
|
|
1453
|
+
const payload = {
|
|
1454
|
+
data: {
|
|
1455
|
+
...initalPayload.data,
|
|
1456
|
+
type: CC_EVENTS.AGENT_OFFER_CONSULT,
|
|
1457
|
+
},
|
|
1458
|
+
};
|
|
1459
|
+
|
|
1460
|
+
webSocketManagerMock.emit('message', JSON.stringify(initalPayload));
|
|
1461
|
+
const task = taskManager.getTask(taskId);
|
|
1462
|
+
task.updateTaskData = jest.fn().mockImplementation((newData) => {
|
|
1463
|
+
task.data = {...newData, isConsulted: true};
|
|
1464
|
+
return task;
|
|
1465
|
+
});
|
|
1466
|
+
const taskEmitSpy = jest.spyOn(task, 'emit');
|
|
1467
|
+
|
|
1468
|
+
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
1469
|
+
|
|
1470
|
+
expect(task.updateTaskData).toHaveBeenCalledWith({
|
|
1471
|
+
...payload.data,
|
|
1472
|
+
isConsulted: true,
|
|
1473
|
+
});
|
|
1474
|
+
expect(task.data.isConsulted).toBe(true);
|
|
1475
|
+
expect(taskEmitSpy).toHaveBeenCalledWith(TASK_EVENTS.TASK_OFFER_CONSULT, task);
|
|
1476
|
+
});
|
|
1477
|
+
|
|
1478
|
+
it('should emit TASK_CONSULT_ACCEPTED event on AGENT_CONSULTING event', () => {
|
|
1479
|
+
const initialConsultingPayload = {
|
|
1480
|
+
data: {
|
|
1481
|
+
...initalPayload.data,
|
|
1482
|
+
type: CC_EVENTS.AGENT_OFFER_CONSULT,
|
|
1483
|
+
},
|
|
1484
|
+
};
|
|
1485
|
+
|
|
1486
|
+
const consultingPayload = {
|
|
1487
|
+
data: {
|
|
1488
|
+
...initalPayload.data,
|
|
1489
|
+
type: CC_EVENTS.AGENT_CONSULTING,
|
|
1490
|
+
},
|
|
1491
|
+
};
|
|
1492
|
+
|
|
1493
|
+
webSocketManagerMock.emit('message', JSON.stringify(initalPayload));
|
|
1494
|
+
taskManager.getTask(taskId).updateTaskData = jest.fn().mockImplementation((newData) => {
|
|
1495
|
+
taskManager.getTask(taskId).data = {...newData, isConsulted: true};
|
|
1496
|
+
return taskManager.getTask(taskId);
|
|
1497
|
+
});
|
|
1498
|
+
|
|
1499
|
+
const taskEmitSpy = jest.spyOn(taskManager.getTask(taskId), 'emit');
|
|
1500
|
+
webSocketManagerMock.emit('message', JSON.stringify(initialConsultingPayload));
|
|
1501
|
+
webSocketManagerMock.emit('message', JSON.stringify(consultingPayload));
|
|
1502
|
+
expect(taskManager.getTask(taskId).data.isConsulted).toBe(true);
|
|
1503
|
+
expect(taskEmitSpy).toHaveBeenCalledWith(
|
|
1504
|
+
TASK_EVENTS.TASK_CONSULT_ACCEPTED,
|
|
1127
1505
|
taskManager.getTask(taskId)
|
|
1128
1506
|
);
|
|
1129
1507
|
});
|
|
@@ -1141,7 +1519,10 @@ describe('TaskManager', () => {
|
|
|
1141
1519
|
const taskUpdateTaskDataSpy = jest.spyOn(taskManager.getTask(taskId), 'updateTaskData');
|
|
1142
1520
|
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
1143
1521
|
expect(taskUpdateTaskDataSpy).toHaveBeenCalledWith(payload.data);
|
|
1144
|
-
expect(taskEmitSpy).toHaveBeenCalledWith(
|
|
1522
|
+
expect(taskEmitSpy).toHaveBeenCalledWith(
|
|
1523
|
+
TASK_EVENTS.TASK_CONSULT_END,
|
|
1524
|
+
taskManager.getTask(taskId)
|
|
1525
|
+
);
|
|
1145
1526
|
});
|
|
1146
1527
|
|
|
1147
1528
|
it('should emit TASK_CONSULT_ENDED event and remove currentTask when on AGENT_CONSULT_ENDED event when requested for a consult', () => {
|
|
@@ -1322,7 +1703,10 @@ describe('TaskManager', () => {
|
|
|
1322
1703
|
webSocketManagerMock.emit('message', JSON.stringify(assignFailedPayload));
|
|
1323
1704
|
|
|
1324
1705
|
expect(taskUpdateDataSpy).toHaveBeenCalledWith(assignFailedPayload.data);
|
|
1325
|
-
expect(taskEmitSpy).toHaveBeenCalledWith(
|
|
1706
|
+
expect(taskEmitSpy).toHaveBeenCalledWith(
|
|
1707
|
+
TASK_EVENTS.TASK_REJECT,
|
|
1708
|
+
assignFailedPayload.data.reason
|
|
1709
|
+
);
|
|
1326
1710
|
// Verify the correct metric event name is used for AGENT_CONTACT_ASSIGN_FAILED
|
|
1327
1711
|
expect(metricsTrackSpy).toHaveBeenCalled();
|
|
1328
1712
|
expect(metricsTrackSpy.mock.calls[0][0]).toBe('Agent Contact Assign Failed');
|
|
@@ -1397,7 +1781,10 @@ describe('TaskManager', () => {
|
|
|
1397
1781
|
};
|
|
1398
1782
|
webSocketManagerMock.emit('message', JSON.stringify(consultingPayload));
|
|
1399
1783
|
expect(taskEmitSpy).toHaveBeenCalledWith(CC_EVENTS.AGENT_CONSULTING, consultingPayload.data);
|
|
1400
|
-
expect(taskEmitSpy).toHaveBeenCalledWith(
|
|
1784
|
+
expect(taskEmitSpy).toHaveBeenCalledWith(
|
|
1785
|
+
TASK_EVENTS.TASK_CONSULTING,
|
|
1786
|
+
taskManager.getTask(taskId)
|
|
1787
|
+
);
|
|
1401
1788
|
});
|
|
1402
1789
|
|
|
1403
1790
|
it('should emit TASK_END event on AGENT_CONTACT_UNASSIGNED', () => {
|
|
@@ -1420,7 +1807,10 @@ describe('TaskManager', () => {
|
|
|
1420
1807
|
},
|
|
1421
1808
|
};
|
|
1422
1809
|
webSocketManagerMock.emit('message', JSON.stringify(unassignedPayload));
|
|
1423
|
-
expect(taskEmitSpy).toHaveBeenCalledWith(
|
|
1810
|
+
expect(taskEmitSpy).toHaveBeenCalledWith(
|
|
1811
|
+
CC_EVENTS.AGENT_CONTACT_UNASSIGNED,
|
|
1812
|
+
unassignedPayload.data
|
|
1813
|
+
);
|
|
1424
1814
|
expect(taskEmitSpy).toHaveBeenCalledWith(TASK_EVENTS.TASK_END, taskManager.getTask(taskId));
|
|
1425
1815
|
});
|
|
1426
1816
|
|
|
@@ -1429,12 +1819,12 @@ describe('TaskManager', () => {
|
|
|
1429
1819
|
const chatPayload = {
|
|
1430
1820
|
data: {
|
|
1431
1821
|
...initalPayload.data,
|
|
1432
|
-
interaction: {
|
|
1822
|
+
interaction: {mediaType: 'chat'},
|
|
1433
1823
|
},
|
|
1434
1824
|
};
|
|
1435
1825
|
|
|
1436
1826
|
const taskIncomingSpy = jest.spyOn(taskManager, 'emit');
|
|
1437
|
-
|
|
1827
|
+
|
|
1438
1828
|
// Simulate receiving a chat task
|
|
1439
1829
|
webSocketManagerMock.emit('message', JSON.stringify(chatPayload));
|
|
1440
1830
|
|
|
@@ -1451,12 +1841,12 @@ describe('TaskManager', () => {
|
|
|
1451
1841
|
const emailPayload = {
|
|
1452
1842
|
data: {
|
|
1453
1843
|
...initalPayload.data,
|
|
1454
|
-
interaction: {
|
|
1844
|
+
interaction: {mediaType: 'email'},
|
|
1455
1845
|
},
|
|
1456
1846
|
};
|
|
1457
1847
|
|
|
1458
1848
|
const taskIncomingSpy = jest.spyOn(taskManager, 'emit');
|
|
1459
|
-
|
|
1849
|
+
|
|
1460
1850
|
// Simulate receiving an email task
|
|
1461
1851
|
webSocketManagerMock.emit('message', JSON.stringify(emailPayload));
|
|
1462
1852
|
|
|
@@ -1474,18 +1864,18 @@ describe('TaskManager', () => {
|
|
|
1474
1864
|
data: {
|
|
1475
1865
|
...initalPayload.data,
|
|
1476
1866
|
type: CC_EVENTS.AGENT_CONTACT_RESERVED,
|
|
1477
|
-
interaction: {
|
|
1867
|
+
interaction: {mediaType: 'chat'},
|
|
1478
1868
|
},
|
|
1479
1869
|
};
|
|
1480
|
-
|
|
1870
|
+
|
|
1481
1871
|
const taskIncomingSpy = jest.spyOn(taskManager, 'emit');
|
|
1482
1872
|
webSocketManagerMock.emit('message', JSON.stringify(chatReservedPayload));
|
|
1483
|
-
|
|
1873
|
+
|
|
1484
1874
|
expect(taskIncomingSpy).toHaveBeenCalledWith(
|
|
1485
1875
|
TASK_EVENTS.TASK_INCOMING,
|
|
1486
1876
|
taskManager.getTask(chatReservedPayload.data.interactionId)
|
|
1487
1877
|
);
|
|
1488
|
-
|
|
1878
|
+
|
|
1489
1879
|
// 2. Chat task is assigned
|
|
1490
1880
|
const chatAssignedPayload = {
|
|
1491
1881
|
data: {
|
|
@@ -1493,20 +1883,20 @@ describe('TaskManager', () => {
|
|
|
1493
1883
|
type: CC_EVENTS.AGENT_CONTACT_ASSIGNED,
|
|
1494
1884
|
},
|
|
1495
1885
|
};
|
|
1496
|
-
|
|
1886
|
+
|
|
1497
1887
|
const task = taskManager.getTask(chatReservedPayload.data.interactionId);
|
|
1498
1888
|
const taskEmitSpy = jest.spyOn(task, 'emit');
|
|
1499
|
-
|
|
1889
|
+
|
|
1500
1890
|
webSocketManagerMock.emit('message', JSON.stringify(chatAssignedPayload));
|
|
1501
|
-
|
|
1891
|
+
|
|
1502
1892
|
expect(taskEmitSpy).toHaveBeenCalledWith(TASK_EVENTS.TASK_ASSIGNED, task);
|
|
1503
|
-
|
|
1893
|
+
|
|
1504
1894
|
// 3. Chat task is ended with state 'new' to trigger cleanup
|
|
1505
1895
|
const chatEndedPayload = {
|
|
1506
1896
|
data: {
|
|
1507
1897
|
...chatReservedPayload.data,
|
|
1508
1898
|
type: CC_EVENTS.CONTACT_ENDED,
|
|
1509
|
-
interaction: {
|
|
1899
|
+
interaction: {mediaType: 'chat', state: 'new'}, // Change to 'new' state
|
|
1510
1900
|
},
|
|
1511
1901
|
};
|
|
1512
1902
|
|
|
@@ -1523,40 +1913,46 @@ describe('TaskManager', () => {
|
|
|
1523
1913
|
data: {
|
|
1524
1914
|
...initalPayload.data,
|
|
1525
1915
|
interactionId: 'telephony-task-id',
|
|
1526
|
-
interaction: {
|
|
1916
|
+
interaction: {mediaType: 'telephony'},
|
|
1527
1917
|
},
|
|
1528
1918
|
};
|
|
1529
|
-
|
|
1919
|
+
|
|
1530
1920
|
const chatPayload = {
|
|
1531
1921
|
data: {
|
|
1532
1922
|
...initalPayload.data,
|
|
1533
1923
|
interactionId: 'chat-task-id',
|
|
1534
|
-
interaction: {
|
|
1924
|
+
interaction: {mediaType: 'chat'},
|
|
1535
1925
|
},
|
|
1536
1926
|
};
|
|
1537
|
-
|
|
1927
|
+
|
|
1538
1928
|
const emailPayload = {
|
|
1539
1929
|
data: {
|
|
1540
1930
|
...initalPayload.data,
|
|
1541
1931
|
interactionId: 'email-task-id',
|
|
1542
|
-
interaction: {
|
|
1932
|
+
interaction: {mediaType: 'email'},
|
|
1543
1933
|
},
|
|
1544
1934
|
};
|
|
1545
|
-
|
|
1935
|
+
|
|
1546
1936
|
// Simulate receiving tasks of different types
|
|
1547
1937
|
webSocketManagerMock.emit('message', JSON.stringify(telephonyPayload));
|
|
1548
1938
|
webSocketManagerMock.emit('message', JSON.stringify(chatPayload));
|
|
1549
1939
|
webSocketManagerMock.emit('message', JSON.stringify(emailPayload));
|
|
1550
|
-
|
|
1940
|
+
|
|
1551
1941
|
// Verify all tasks are in the collection
|
|
1552
1942
|
expect(taskManager.getAllTasks()).toHaveProperty(telephonyPayload.data.interactionId);
|
|
1553
1943
|
expect(taskManager.getAllTasks()).toHaveProperty(chatPayload.data.interactionId);
|
|
1554
1944
|
expect(taskManager.getAllTasks()).toHaveProperty(emailPayload.data.interactionId);
|
|
1555
|
-
|
|
1945
|
+
|
|
1556
1946
|
// Verify the task media types are correctly set
|
|
1557
|
-
expect(
|
|
1558
|
-
|
|
1559
|
-
|
|
1947
|
+
expect(
|
|
1948
|
+
taskManager.getTask(telephonyPayload.data.interactionId).data.interaction.mediaType
|
|
1949
|
+
).toBe('telephony');
|
|
1950
|
+
expect(taskManager.getTask(chatPayload.data.interactionId).data.interaction.mediaType).toBe(
|
|
1951
|
+
'chat'
|
|
1952
|
+
);
|
|
1953
|
+
expect(taskManager.getTask(emailPayload.data.interactionId).data.interaction.mediaType).toBe(
|
|
1954
|
+
'email'
|
|
1955
|
+
);
|
|
1560
1956
|
});
|
|
1561
1957
|
|
|
1562
1958
|
it('should properly handle one task ending when multiple tasks are active', () => {
|
|
@@ -1565,87 +1961,87 @@ describe('TaskManager', () => {
|
|
|
1565
1961
|
data: {
|
|
1566
1962
|
...initalPayload.data,
|
|
1567
1963
|
interactionId: 'task-id-1',
|
|
1568
|
-
interaction: {
|
|
1964
|
+
interaction: {mediaType: 'telephony'},
|
|
1569
1965
|
},
|
|
1570
1966
|
};
|
|
1571
|
-
|
|
1967
|
+
|
|
1572
1968
|
const task2Payload = {
|
|
1573
1969
|
data: {
|
|
1574
1970
|
...initalPayload.data,
|
|
1575
1971
|
interactionId: 'task-id-2',
|
|
1576
|
-
interaction: {
|
|
1972
|
+
interaction: {mediaType: 'chat'},
|
|
1577
1973
|
},
|
|
1578
1974
|
};
|
|
1579
|
-
|
|
1975
|
+
|
|
1580
1976
|
const task3Payload = {
|
|
1581
1977
|
data: {
|
|
1582
1978
|
...initalPayload.data,
|
|
1583
1979
|
interactionId: 'task-id-3',
|
|
1584
|
-
interaction: {
|
|
1980
|
+
interaction: {mediaType: 'email'},
|
|
1585
1981
|
},
|
|
1586
1982
|
};
|
|
1587
|
-
|
|
1983
|
+
|
|
1588
1984
|
// Initialize all tasks
|
|
1589
1985
|
webSocketManagerMock.emit('message', JSON.stringify(task1Payload));
|
|
1590
1986
|
webSocketManagerMock.emit('message', JSON.stringify(task2Payload));
|
|
1591
1987
|
webSocketManagerMock.emit('message', JSON.stringify(task3Payload));
|
|
1592
|
-
|
|
1988
|
+
|
|
1593
1989
|
// Verify all tasks are in the collection
|
|
1594
1990
|
expect(taskManager.getAllTasks()).toHaveProperty(task1Payload.data.interactionId);
|
|
1595
1991
|
expect(taskManager.getAllTasks()).toHaveProperty(task2Payload.data.interactionId);
|
|
1596
1992
|
expect(taskManager.getAllTasks()).toHaveProperty(task3Payload.data.interactionId);
|
|
1597
|
-
|
|
1993
|
+
|
|
1598
1994
|
// Create spies for all tasks
|
|
1599
1995
|
const task1EmitSpy = jest.spyOn(taskManager.getTask(task1Payload.data.interactionId), 'emit');
|
|
1600
1996
|
const task2EmitSpy = jest.spyOn(taskManager.getTask(task2Payload.data.interactionId), 'emit');
|
|
1601
1997
|
const task3EmitSpy = jest.spyOn(taskManager.getTask(task3Payload.data.interactionId), 'emit');
|
|
1602
|
-
|
|
1998
|
+
|
|
1603
1999
|
// Store reference to task2 before it gets removed
|
|
1604
2000
|
const task2 = taskManager.getTask(task2Payload.data.interactionId);
|
|
1605
|
-
|
|
2001
|
+
|
|
1606
2002
|
// End only the second task (chat task)
|
|
1607
2003
|
const chatEndedPayload = {
|
|
1608
2004
|
data: {
|
|
1609
2005
|
...task2Payload.data,
|
|
1610
2006
|
type: CC_EVENTS.CONTACT_ENDED,
|
|
1611
|
-
interaction: {
|
|
2007
|
+
interaction: {mediaType: 'chat', state: 'new'}, // Using 'new' to trigger cleanup
|
|
1612
2008
|
},
|
|
1613
2009
|
};
|
|
1614
|
-
|
|
2010
|
+
|
|
1615
2011
|
webSocketManagerMock.emit('message', JSON.stringify(chatEndedPayload));
|
|
1616
|
-
|
|
2012
|
+
|
|
1617
2013
|
// Verify only task2 emitted TASK_END
|
|
1618
2014
|
expect(task1EmitSpy).not.toHaveBeenCalledWith(TASK_EVENTS.TASK_END);
|
|
1619
2015
|
expect(task2EmitSpy).toHaveBeenCalledWith(TASK_EVENTS.TASK_END, task2);
|
|
1620
2016
|
expect(task3EmitSpy).not.toHaveBeenCalledWith(TASK_EVENTS.TASK_END);
|
|
1621
|
-
|
|
2017
|
+
|
|
1622
2018
|
// Verify task2 was removed from collection (since state was 'new')
|
|
1623
2019
|
expect(taskManager.getTask(task2Payload.data.interactionId)).toBeUndefined();
|
|
1624
|
-
|
|
2020
|
+
|
|
1625
2021
|
// Verify other tasks remain in the collection
|
|
1626
2022
|
expect(taskManager.getTask(task1Payload.data.interactionId)).toBeDefined();
|
|
1627
2023
|
expect(taskManager.getTask(task3Payload.data.interactionId)).toBeDefined();
|
|
1628
|
-
|
|
2024
|
+
|
|
1629
2025
|
// Store reference to task3 before we end it
|
|
1630
2026
|
const task3 = taskManager.getTask(task3Payload.data.interactionId);
|
|
1631
|
-
|
|
2027
|
+
|
|
1632
2028
|
// Now end task3 with a state that doesn't trigger cleanup
|
|
1633
2029
|
const emailEndedPayload = {
|
|
1634
2030
|
data: {
|
|
1635
2031
|
...task3Payload.data,
|
|
1636
2032
|
type: CC_EVENTS.CONTACT_ENDED,
|
|
1637
|
-
interaction: {
|
|
2033
|
+
interaction: {mediaType: 'email', state: 'connected'}, // Using 'connected' to NOT trigger cleanup
|
|
1638
2034
|
},
|
|
1639
2035
|
};
|
|
1640
|
-
|
|
2036
|
+
|
|
1641
2037
|
webSocketManagerMock.emit('message', JSON.stringify(emailEndedPayload));
|
|
1642
|
-
|
|
2038
|
+
|
|
1643
2039
|
// Verify task3 emitted TASK_END
|
|
1644
2040
|
expect(task3EmitSpy).toHaveBeenCalledWith(TASK_EVENTS.TASK_END, task3);
|
|
1645
|
-
|
|
2041
|
+
|
|
1646
2042
|
// Verify task3 is still in collection (since state was 'connected')
|
|
1647
2043
|
expect(taskManager.getTask(task3Payload.data.interactionId)).toBeDefined();
|
|
1648
|
-
|
|
2044
|
+
|
|
1649
2045
|
// Verify task1 remains unaffected
|
|
1650
2046
|
expect(task1EmitSpy).not.toHaveBeenCalledWith(TASK_EVENTS.TASK_END);
|
|
1651
2047
|
expect(taskManager.getTask(task1Payload.data.interactionId)).toBeDefined();
|
|
@@ -1654,13 +2050,13 @@ describe('TaskManager', () => {
|
|
|
1654
2050
|
it('should emit TASK_END event on AGENT_VTEAM_TRANSFERRED event', () => {
|
|
1655
2051
|
// First create a task by emitting the initial payload
|
|
1656
2052
|
webSocketManagerMock.emit('message', JSON.stringify(initalPayload));
|
|
1657
|
-
|
|
2053
|
+
|
|
1658
2054
|
// Get a reference to the task from taskCollection
|
|
1659
2055
|
const task = taskManager.getTask(taskId);
|
|
1660
|
-
|
|
2056
|
+
|
|
1661
2057
|
// Now spy on the task's emit method
|
|
1662
2058
|
const taskEmitSpy = jest.spyOn(task, 'emit');
|
|
1663
|
-
|
|
2059
|
+
|
|
1664
2060
|
const vteamTransferredPayload = {
|
|
1665
2061
|
data: {
|
|
1666
2062
|
type: CC_EVENTS.AGENT_VTEAM_TRANSFERRED,
|
|
@@ -1677,31 +2073,31 @@ describe('TaskManager', () => {
|
|
|
1677
2073
|
queueMgr: initalPayload.data.queueMgr,
|
|
1678
2074
|
},
|
|
1679
2075
|
};
|
|
1680
|
-
|
|
2076
|
+
|
|
1681
2077
|
// No need to explicitly set the task in the collection as it's already there
|
|
1682
2078
|
// from the initial message processing
|
|
1683
|
-
|
|
2079
|
+
|
|
1684
2080
|
webSocketManagerMock.emit('message', JSON.stringify(vteamTransferredPayload));
|
|
1685
|
-
|
|
2081
|
+
|
|
1686
2082
|
// Check that task.emit was called with TASK_END event
|
|
1687
2083
|
expect(taskEmitSpy).toHaveBeenCalledWith(TASK_EVENTS.TASK_END, task);
|
|
1688
|
-
|
|
2084
|
+
|
|
1689
2085
|
// The task should still exist in the collection based on current implementation
|
|
1690
2086
|
expect(taskManager.getTask(taskId)).toBeDefined();
|
|
1691
2087
|
});
|
|
1692
2088
|
|
|
1693
2089
|
it('should update task data on AGENT_WRAPUP event', () => {
|
|
1694
2090
|
const payload = {
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
2091
|
+
data: {
|
|
2092
|
+
type: CC_EVENTS.AGENT_WRAPUP,
|
|
2093
|
+
interactionId: taskId,
|
|
2094
|
+
wrapUpRequired: true,
|
|
2095
|
+
},
|
|
1700
2096
|
};
|
|
1701
2097
|
const task = taskManager.getTask(taskId);
|
|
1702
2098
|
const updateSpy = jest.spyOn(task, 'updateTaskData').mockImplementation((data) => {
|
|
1703
|
-
|
|
1704
|
-
|
|
2099
|
+
task.data = {...(task.data || {}), ...(data || {})};
|
|
2100
|
+
return task;
|
|
1705
2101
|
});
|
|
1706
2102
|
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
1707
2103
|
expect(updateSpy).toHaveBeenCalledWith(payload.data);
|
|
@@ -1716,7 +2112,7 @@ describe('TaskManager', () => {
|
|
|
1716
2112
|
data: {
|
|
1717
2113
|
type: CC_EVENTS.AGENT_CONTACT_UNASSIGNED,
|
|
1718
2114
|
agentId: initalPayload.data.agentId,
|
|
1719
|
-
interaction: {
|
|
2115
|
+
interaction: {mediaType: 'telephony'},
|
|
1720
2116
|
interactionId: initalPayload.data.interactionId,
|
|
1721
2117
|
orgId: initalPayload.data.orgId,
|
|
1722
2118
|
trackingId: initalPayload.data.trackingId,
|
|
@@ -1735,7 +2131,7 @@ describe('TaskManager', () => {
|
|
|
1735
2131
|
data: {
|
|
1736
2132
|
type: CC_EVENTS.AGENT_WRAPUP,
|
|
1737
2133
|
interactionId: taskId,
|
|
1738
|
-
interaction: {
|
|
2134
|
+
interaction: {mediaType: 'telephony'},
|
|
1739
2135
|
},
|
|
1740
2136
|
};
|
|
1741
2137
|
webSocketManagerMock.emit('message', JSON.stringify(wrapupPayload));
|
|
@@ -1752,7 +2148,7 @@ describe('TaskManager', () => {
|
|
|
1752
2148
|
data: {
|
|
1753
2149
|
type: CC_EVENTS.AGENT_VTEAM_TRANSFERRED,
|
|
1754
2150
|
agentId: initalPayload.data.agentId,
|
|
1755
|
-
interaction: {
|
|
2151
|
+
interaction: {mediaType: 'telephony'},
|
|
1756
2152
|
interactionId: initalPayload.data.interactionId,
|
|
1757
2153
|
orgId: initalPayload.data.orgId,
|
|
1758
2154
|
trackingId: initalPayload.data.trackingId,
|
|
@@ -1771,7 +2167,7 @@ describe('TaskManager', () => {
|
|
|
1771
2167
|
data: {
|
|
1772
2168
|
type: CC_EVENTS.AGENT_WRAPUP,
|
|
1773
2169
|
interactionId: taskId,
|
|
1774
|
-
interaction: {
|
|
2170
|
+
interaction: {mediaType: 'telephony'},
|
|
1775
2171
|
},
|
|
1776
2172
|
};
|
|
1777
2173
|
webSocketManagerMock.emit('message', JSON.stringify(wrapupPayload));
|
|
@@ -1793,22 +2189,22 @@ describe('TaskManager', () => {
|
|
|
1793
2189
|
expect(spy).toHaveBeenCalledWith(taskEvent, task);
|
|
1794
2190
|
});
|
|
1795
2191
|
});
|
|
1796
|
-
});
|
|
2192
|
+
});
|
|
1797
2193
|
|
|
1798
2194
|
describe('Conference event handling', () => {
|
|
1799
2195
|
let task;
|
|
1800
2196
|
const agentId = '723a8ffb-a26e-496d-b14a-ff44fb83b64f';
|
|
1801
|
-
|
|
2197
|
+
|
|
1802
2198
|
beforeEach(() => {
|
|
1803
2199
|
// Set the agentId on taskManager before tests run
|
|
1804
2200
|
taskManager.setAgentId(agentId);
|
|
1805
|
-
|
|
2201
|
+
|
|
1806
2202
|
task = {
|
|
1807
|
-
data: {
|
|
2203
|
+
data: {interactionId: taskId},
|
|
1808
2204
|
emit: jest.fn(),
|
|
1809
2205
|
updateTaskData: jest.fn().mockImplementation((updatedData) => {
|
|
1810
2206
|
// Mock the updateTaskData method to actually update task.data
|
|
1811
|
-
task.data = {
|
|
2207
|
+
task.data = {...task.data, ...updatedData};
|
|
1812
2208
|
return task;
|
|
1813
2209
|
}),
|
|
1814
2210
|
};
|
|
@@ -1870,8 +2266,8 @@ describe('TaskManager', () => {
|
|
|
1870
2266
|
participantType: 'agent',
|
|
1871
2267
|
interaction: {
|
|
1872
2268
|
participants: {
|
|
1873
|
-
[agentId]: {
|
|
1874
|
-
'new-participant-123': {
|
|
2269
|
+
[agentId]: {pType: 'Agent', hasLeft: false},
|
|
2270
|
+
'new-participant-123': {pType: 'Agent', hasLeft: false},
|
|
1875
2271
|
},
|
|
1876
2272
|
media: {
|
|
1877
2273
|
[taskId]: {
|
|
@@ -1898,10 +2294,10 @@ describe('TaskManager', () => {
|
|
|
1898
2294
|
participantId: 'new-agent-789',
|
|
1899
2295
|
interaction: {
|
|
1900
2296
|
participants: {
|
|
1901
|
-
[agentId]: {
|
|
1902
|
-
'agent-2': {
|
|
1903
|
-
'new-agent-789': {
|
|
1904
|
-
'customer-1': {
|
|
2297
|
+
[agentId]: {pType: 'Agent', hasLeft: false},
|
|
2298
|
+
'agent-2': {pType: 'Agent', hasLeft: false},
|
|
2299
|
+
'new-agent-789': {pType: 'Agent', hasLeft: false},
|
|
2300
|
+
'customer-1': {pType: 'Customer', hasLeft: false},
|
|
1905
2301
|
},
|
|
1906
2302
|
media: {
|
|
1907
2303
|
[taskId]: {
|
|
@@ -1914,12 +2310,12 @@ describe('TaskManager', () => {
|
|
|
1914
2310
|
};
|
|
1915
2311
|
|
|
1916
2312
|
const updateTaskDataSpy = jest.spyOn(task, 'updateTaskData');
|
|
1917
|
-
|
|
2313
|
+
|
|
1918
2314
|
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
1919
2315
|
|
|
1920
2316
|
// Verify updateTaskData was called exactly once
|
|
1921
2317
|
expect(updateTaskDataSpy).toHaveBeenCalledTimes(1);
|
|
1922
|
-
|
|
2318
|
+
|
|
1923
2319
|
// Verify it was called with isConferenceInProgress already calculated
|
|
1924
2320
|
expect(updateTaskDataSpy).toHaveBeenCalledWith(
|
|
1925
2321
|
expect.objectContaining({
|
|
@@ -1927,7 +2323,7 @@ describe('TaskManager', () => {
|
|
|
1927
2323
|
isConferenceInProgress: true, // 3 active agents
|
|
1928
2324
|
})
|
|
1929
2325
|
);
|
|
1930
|
-
|
|
2326
|
+
|
|
1931
2327
|
expect(task.emit).toHaveBeenCalledWith(TASK_EVENTS.TASK_PARTICIPANT_JOINED, task);
|
|
1932
2328
|
});
|
|
1933
2329
|
|
|
@@ -1939,9 +2335,9 @@ describe('TaskManager', () => {
|
|
|
1939
2335
|
interactionId: taskId,
|
|
1940
2336
|
interaction: {
|
|
1941
2337
|
participants: {
|
|
1942
|
-
[agentId]: {
|
|
1943
|
-
'agent-2': {
|
|
1944
|
-
'customer-1': {
|
|
2338
|
+
[agentId]: {pType: 'Agent', hasLeft: false},
|
|
2339
|
+
'agent-2': {pType: 'Agent', hasLeft: true}, // This agent left
|
|
2340
|
+
'customer-1': {pType: 'Customer', hasLeft: false},
|
|
1945
2341
|
},
|
|
1946
2342
|
media: {
|
|
1947
2343
|
[taskId]: {
|
|
@@ -1954,19 +2350,19 @@ describe('TaskManager', () => {
|
|
|
1954
2350
|
};
|
|
1955
2351
|
|
|
1956
2352
|
const updateTaskDataSpy = jest.spyOn(task, 'updateTaskData');
|
|
1957
|
-
|
|
2353
|
+
|
|
1958
2354
|
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
1959
2355
|
|
|
1960
2356
|
// Verify updateTaskData was called exactly once
|
|
1961
2357
|
expect(updateTaskDataSpy).toHaveBeenCalledTimes(1);
|
|
1962
|
-
|
|
2358
|
+
|
|
1963
2359
|
// Verify it was called with isConferenceInProgress already calculated
|
|
1964
2360
|
expect(updateTaskDataSpy).toHaveBeenCalledWith(
|
|
1965
2361
|
expect.objectContaining({
|
|
1966
2362
|
isConferenceInProgress: false, // Only 1 active agent remains
|
|
1967
2363
|
})
|
|
1968
2364
|
);
|
|
1969
|
-
|
|
2365
|
+
|
|
1970
2366
|
expect(task.emit).toHaveBeenCalledWith(TASK_EVENTS.TASK_PARTICIPANT_LEFT, task);
|
|
1971
2367
|
});
|
|
1972
2368
|
|
|
@@ -2243,7 +2639,7 @@ describe('TaskManager', () => {
|
|
|
2243
2639
|
it('should only update task for matching interactionId', () => {
|
|
2244
2640
|
const otherTaskId = 'other-task-id';
|
|
2245
2641
|
const otherTask = {
|
|
2246
|
-
data: {
|
|
2642
|
+
data: {interactionId: otherTaskId},
|
|
2247
2643
|
emit: jest.fn(),
|
|
2248
2644
|
};
|
|
2249
2645
|
taskManager.taskCollection[otherTaskId] = otherTask;
|
|
@@ -2261,16 +2657,328 @@ describe('TaskManager', () => {
|
|
|
2261
2657
|
// Only the matching task should be updated
|
|
2262
2658
|
expect(task.data.isConferencing).toBe(true);
|
|
2263
2659
|
expect(task.emit).toHaveBeenCalledWith(TASK_EVENTS.TASK_CONFERENCE_STARTED, task);
|
|
2264
|
-
|
|
2660
|
+
|
|
2265
2661
|
// Other task should not be affected
|
|
2266
2662
|
expect(otherTask.data.isConferencing).toBeUndefined();
|
|
2267
2663
|
expect(otherTask.emit).not.toHaveBeenCalled();
|
|
2268
2664
|
});
|
|
2269
2665
|
});
|
|
2270
2666
|
|
|
2271
|
-
describe('
|
|
2272
|
-
|
|
2273
|
-
|
|
2667
|
+
describe('handleTaskCleanup - stage changes', () => {
|
|
2668
|
+
const agentId = '723a8ffb-a26e-496d-b14a-ff44fb83b64f';
|
|
2669
|
+
|
|
2670
|
+
beforeEach(() => {
|
|
2671
|
+
taskManager.setAgentId(agentId);
|
|
2672
|
+
});
|
|
2673
|
+
|
|
2674
|
+
it('should remove OUTDIAL task on CONTACT_ENDED when current agent is NOT in agentsPendingWrapUp', () => {
|
|
2675
|
+
const task = taskManager.getTask(taskId);
|
|
2676
|
+
task.updateTaskData = jest.fn().mockImplementation((newData) => {
|
|
2677
|
+
task.data = {
|
|
2678
|
+
...task.data,
|
|
2679
|
+
...newData,
|
|
2680
|
+
interaction: {
|
|
2681
|
+
...task.data.interaction,
|
|
2682
|
+
outboundType: 'OUTDIAL',
|
|
2683
|
+
state: 'new',
|
|
2684
|
+
mediaType: 'telephony',
|
|
2685
|
+
},
|
|
2686
|
+
agentsPendingWrapUp: ['different-agent-123'], // Current agent not in the list
|
|
2687
|
+
};
|
|
2688
|
+
return task;
|
|
2689
|
+
});
|
|
2690
|
+
task.unregisterWebCallListeners = jest.fn();
|
|
2691
|
+
const removeTaskSpy = jest.spyOn(taskManager, 'removeTaskFromCollection');
|
|
2692
|
+
|
|
2693
|
+
const payload = {
|
|
2694
|
+
data: {
|
|
2695
|
+
type: CC_EVENTS.CONTACT_ENDED,
|
|
2696
|
+
interactionId: taskId,
|
|
2697
|
+
interaction: {
|
|
2698
|
+
outboundType: 'OUTDIAL',
|
|
2699
|
+
state: 'new',
|
|
2700
|
+
mediaType: 'telephony',
|
|
2701
|
+
},
|
|
2702
|
+
agentsPendingWrapUp: ['different-agent-123'], // Current agent not in the list
|
|
2703
|
+
},
|
|
2704
|
+
};
|
|
2705
|
+
|
|
2706
|
+
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
2707
|
+
|
|
2708
|
+
expect(removeTaskSpy).toHaveBeenCalled();
|
|
2709
|
+
expect(taskManager.getTask(taskId)).toBeUndefined();
|
|
2710
|
+
});
|
|
2711
|
+
|
|
2712
|
+
it('should NOT remove OUTDIAL task on CONTACT_ENDED when current agent IS in agentsPendingWrapUp', () => {
|
|
2713
|
+
const task = taskManager.getTask(taskId);
|
|
2714
|
+
task.updateTaskData = jest.fn().mockImplementation((newData) => {
|
|
2715
|
+
task.data = {
|
|
2716
|
+
...task.data,
|
|
2717
|
+
...newData,
|
|
2718
|
+
interaction: {
|
|
2719
|
+
...task.data.interaction,
|
|
2720
|
+
outboundType: 'OUTDIAL',
|
|
2721
|
+
state: 'new',
|
|
2722
|
+
mediaType: 'telephony',
|
|
2723
|
+
},
|
|
2724
|
+
agentsPendingWrapUp: [agentId, 'other-agent-456'], // Current agent IS in the list
|
|
2725
|
+
};
|
|
2726
|
+
return task;
|
|
2727
|
+
});
|
|
2728
|
+
task.unregisterWebCallListeners = jest.fn();
|
|
2729
|
+
const removeTaskSpy = jest.spyOn(taskManager, 'removeTaskFromCollection');
|
|
2730
|
+
|
|
2731
|
+
const payload = {
|
|
2732
|
+
data: {
|
|
2733
|
+
type: CC_EVENTS.CONTACT_ENDED,
|
|
2734
|
+
interactionId: taskId,
|
|
2735
|
+
interaction: {
|
|
2736
|
+
outboundType: 'OUTDIAL',
|
|
2737
|
+
state: 'new',
|
|
2738
|
+
mediaType: 'telephony',
|
|
2739
|
+
},
|
|
2740
|
+
agentsPendingWrapUp: [agentId, 'other-agent-456'], // Current agent IS in the list
|
|
2741
|
+
},
|
|
2742
|
+
};
|
|
2743
|
+
|
|
2744
|
+
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
2745
|
+
|
|
2746
|
+
expect(removeTaskSpy).not.toHaveBeenCalled();
|
|
2747
|
+
expect(taskManager.getTask(taskId)).toBeDefined();
|
|
2748
|
+
});
|
|
2749
|
+
|
|
2750
|
+
it('should remove OUTDIAL task when needsWrapUp is false and task is outdial', () => {
|
|
2751
|
+
const task = taskManager.getTask(taskId);
|
|
2752
|
+
task.updateTaskData = jest.fn().mockImplementation((newData) => {
|
|
2753
|
+
task.data = {
|
|
2754
|
+
...task.data,
|
|
2755
|
+
...newData,
|
|
2756
|
+
interaction: {
|
|
2757
|
+
...task.data.interaction,
|
|
2758
|
+
outboundType: 'OUTDIAL',
|
|
2759
|
+
state: 'WRAPUP', // Not 'new' state
|
|
2760
|
+
mediaType: 'telephony',
|
|
2761
|
+
},
|
|
2762
|
+
agentsPendingWrapUp: [], // No agents pending wrap-up
|
|
2763
|
+
};
|
|
2764
|
+
return task;
|
|
2765
|
+
});
|
|
2766
|
+
task.unregisterWebCallListeners = jest.fn();
|
|
2767
|
+
const removeTaskSpy = jest.spyOn(taskManager, 'removeTaskFromCollection');
|
|
2768
|
+
|
|
2769
|
+
const payload = {
|
|
2770
|
+
data: {
|
|
2771
|
+
type: CC_EVENTS.CONTACT_ENDED,
|
|
2772
|
+
interactionId: taskId,
|
|
2773
|
+
interaction: {
|
|
2774
|
+
outboundType: 'OUTDIAL',
|
|
2775
|
+
state: 'WRAPUP', // Not 'new' state
|
|
2776
|
+
mediaType: 'telephony',
|
|
2777
|
+
},
|
|
2778
|
+
agentsPendingWrapUp: [], // No agents pending wrap-up
|
|
2779
|
+
},
|
|
2780
|
+
};
|
|
2781
|
+
|
|
2782
|
+
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
2783
|
+
|
|
2784
|
+
expect(removeTaskSpy).toHaveBeenCalled();
|
|
2785
|
+
expect(taskManager.getTask(taskId)).toBeUndefined();
|
|
2786
|
+
});
|
|
2787
|
+
|
|
2788
|
+
it('should remove OUTDIAL task when needsWrapUp is false (agentsPendingWrapUp is undefined)', () => {
|
|
2789
|
+
const task = taskManager.getTask(taskId);
|
|
2790
|
+
task.updateTaskData = jest.fn().mockImplementation((newData) => {
|
|
2791
|
+
task.data = {
|
|
2792
|
+
...task.data,
|
|
2793
|
+
...newData,
|
|
2794
|
+
interaction: {
|
|
2795
|
+
...task.data.interaction,
|
|
2796
|
+
outboundType: 'OUTDIAL',
|
|
2797
|
+
state: 'WRAPUP',
|
|
2798
|
+
mediaType: 'telephony',
|
|
2799
|
+
},
|
|
2800
|
+
agentsPendingWrapUp: undefined, // No agentsPendingWrapUp field
|
|
2801
|
+
};
|
|
2802
|
+
return task;
|
|
2803
|
+
});
|
|
2804
|
+
task.unregisterWebCallListeners = jest.fn();
|
|
2805
|
+
const removeTaskSpy = jest.spyOn(taskManager, 'removeTaskFromCollection');
|
|
2806
|
+
|
|
2807
|
+
const payload = {
|
|
2808
|
+
data: {
|
|
2809
|
+
type: CC_EVENTS.CONTACT_ENDED,
|
|
2810
|
+
interactionId: taskId,
|
|
2811
|
+
interaction: {
|
|
2812
|
+
outboundType: 'OUTDIAL',
|
|
2813
|
+
state: 'WRAPUP',
|
|
2814
|
+
mediaType: 'telephony',
|
|
2815
|
+
},
|
|
2816
|
+
// agentsPendingWrapUp not included
|
|
2817
|
+
},
|
|
2818
|
+
};
|
|
2819
|
+
|
|
2820
|
+
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
2821
|
+
|
|
2822
|
+
expect(removeTaskSpy).toHaveBeenCalled();
|
|
2823
|
+
expect(taskManager.getTask(taskId)).toBeUndefined();
|
|
2824
|
+
});
|
|
2825
|
+
|
|
2826
|
+
it('should NOT remove OUTDIAL task when needsWrapUp is true (current agent in agentsPendingWrapUp) even if state is WRAPUP', () => {
|
|
2827
|
+
const task = taskManager.getTask(taskId);
|
|
2828
|
+
task.updateTaskData = jest.fn().mockImplementation((newData) => {
|
|
2829
|
+
task.data = {
|
|
2830
|
+
...task.data,
|
|
2831
|
+
...newData,
|
|
2832
|
+
interaction: {
|
|
2833
|
+
...task.data.interaction,
|
|
2834
|
+
outboundType: 'OUTDIAL',
|
|
2835
|
+
state: 'WRAPUP',
|
|
2836
|
+
mediaType: 'telephony',
|
|
2837
|
+
},
|
|
2838
|
+
agentsPendingWrapUp: [agentId], // Current agent needs wrap-up
|
|
2839
|
+
};
|
|
2840
|
+
return task;
|
|
2841
|
+
});
|
|
2842
|
+
task.unregisterWebCallListeners = jest.fn();
|
|
2843
|
+
const removeTaskSpy = jest.spyOn(taskManager, 'removeTaskFromCollection');
|
|
2844
|
+
|
|
2845
|
+
const payload = {
|
|
2846
|
+
data: {
|
|
2847
|
+
type: CC_EVENTS.CONTACT_ENDED,
|
|
2848
|
+
interactionId: taskId,
|
|
2849
|
+
interaction: {
|
|
2850
|
+
outboundType: 'OUTDIAL',
|
|
2851
|
+
state: 'WRAPUP',
|
|
2852
|
+
mediaType: 'telephony',
|
|
2853
|
+
},
|
|
2854
|
+
agentsPendingWrapUp: [agentId], // Current agent needs wrap-up
|
|
2855
|
+
},
|
|
2856
|
+
};
|
|
2857
|
+
|
|
2858
|
+
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
2859
|
+
|
|
2860
|
+
expect(removeTaskSpy).not.toHaveBeenCalled();
|
|
2861
|
+
expect(taskManager.getTask(taskId)).toBeDefined();
|
|
2862
|
+
});
|
|
2863
|
+
|
|
2864
|
+
it('should remove non-OUTDIAL task when state is new regardless of agentsPendingWrapUp', () => {
|
|
2865
|
+
const task = taskManager.getTask(taskId);
|
|
2866
|
+
task.updateTaskData = jest.fn().mockImplementation((newData) => {
|
|
2867
|
+
task.data = {
|
|
2868
|
+
...task.data,
|
|
2869
|
+
...newData,
|
|
2870
|
+
interaction: {
|
|
2871
|
+
...task.data.interaction,
|
|
2872
|
+
outboundType: 'PREVIEW', // Not OUTDIAL
|
|
2873
|
+
state: 'new',
|
|
2874
|
+
mediaType: 'telephony',
|
|
2875
|
+
},
|
|
2876
|
+
agentsPendingWrapUp: [agentId],
|
|
2877
|
+
};
|
|
2878
|
+
return task;
|
|
2879
|
+
});
|
|
2880
|
+
task.unregisterWebCallListeners = jest.fn();
|
|
2881
|
+
const removeTaskSpy = jest.spyOn(taskManager, 'removeTaskFromCollection');
|
|
2882
|
+
|
|
2883
|
+
const payload = {
|
|
2884
|
+
data: {
|
|
2885
|
+
type: CC_EVENTS.CONTACT_ENDED,
|
|
2886
|
+
interactionId: taskId,
|
|
2887
|
+
interaction: {
|
|
2888
|
+
outboundType: 'PREVIEW',
|
|
2889
|
+
state: 'new',
|
|
2890
|
+
mediaType: 'telephony',
|
|
2891
|
+
},
|
|
2892
|
+
agentsPendingWrapUp: [agentId],
|
|
2893
|
+
},
|
|
2894
|
+
};
|
|
2895
|
+
|
|
2896
|
+
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
2897
|
+
|
|
2898
|
+
expect(removeTaskSpy).toHaveBeenCalled();
|
|
2899
|
+
expect(taskManager.getTask(taskId)).toBeUndefined();
|
|
2900
|
+
});
|
|
2901
|
+
|
|
2902
|
+
it('should handle agentsPendingWrapUp with multiple agents correctly - remove if current agent not in list', () => {
|
|
2903
|
+
const task = taskManager.getTask(taskId);
|
|
2904
|
+
task.updateTaskData = jest.fn().mockImplementation((newData) => {
|
|
2905
|
+
task.data = {
|
|
2906
|
+
...task.data,
|
|
2907
|
+
...newData,
|
|
2908
|
+
interaction: {
|
|
2909
|
+
...task.data.interaction,
|
|
2910
|
+
outboundType: 'OUTDIAL',
|
|
2911
|
+
state: 'new',
|
|
2912
|
+
mediaType: 'telephony',
|
|
2913
|
+
},
|
|
2914
|
+
agentsPendingWrapUp: ['agent-1', 'agent-2', 'agent-3'], // Current agent not in the list
|
|
2915
|
+
};
|
|
2916
|
+
return task;
|
|
2917
|
+
});
|
|
2918
|
+
task.unregisterWebCallListeners = jest.fn();
|
|
2919
|
+
const removeTaskSpy = jest.spyOn(taskManager, 'removeTaskFromCollection');
|
|
2920
|
+
|
|
2921
|
+
const payload = {
|
|
2922
|
+
data: {
|
|
2923
|
+
type: CC_EVENTS.CONTACT_ENDED,
|
|
2924
|
+
interactionId: taskId,
|
|
2925
|
+
interaction: {
|
|
2926
|
+
outboundType: 'OUTDIAL',
|
|
2927
|
+
state: 'new',
|
|
2928
|
+
mediaType: 'telephony',
|
|
2929
|
+
},
|
|
2930
|
+
agentsPendingWrapUp: ['agent-1', 'agent-2', 'agent-3'],
|
|
2931
|
+
},
|
|
2932
|
+
};
|
|
2933
|
+
|
|
2934
|
+
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
2935
|
+
|
|
2936
|
+
expect(removeTaskSpy).toHaveBeenCalled();
|
|
2937
|
+
expect(taskManager.getTask(taskId)).toBeUndefined();
|
|
2938
|
+
});
|
|
2939
|
+
|
|
2940
|
+
it('should handle agentsPendingWrapUp with multiple agents correctly - keep if current agent is in list', () => {
|
|
2941
|
+
const task = taskManager.getTask(taskId);
|
|
2942
|
+
task.updateTaskData = jest.fn().mockImplementation((newData) => {
|
|
2943
|
+
task.data = {
|
|
2944
|
+
...task.data,
|
|
2945
|
+
...newData,
|
|
2946
|
+
interaction: {
|
|
2947
|
+
...task.data.interaction,
|
|
2948
|
+
outboundType: 'OUTDIAL',
|
|
2949
|
+
state: 'new',
|
|
2950
|
+
mediaType: 'telephony',
|
|
2951
|
+
},
|
|
2952
|
+
agentsPendingWrapUp: ['agent-1', agentId, 'agent-3'], // Current agent IS in the list
|
|
2953
|
+
};
|
|
2954
|
+
return task;
|
|
2955
|
+
});
|
|
2956
|
+
task.unregisterWebCallListeners = jest.fn();
|
|
2957
|
+
const removeTaskSpy = jest.spyOn(taskManager, 'removeTaskFromCollection');
|
|
2958
|
+
|
|
2959
|
+
const payload = {
|
|
2960
|
+
data: {
|
|
2961
|
+
type: CC_EVENTS.CONTACT_ENDED,
|
|
2962
|
+
interactionId: taskId,
|
|
2963
|
+
interaction: {
|
|
2964
|
+
outboundType: 'OUTDIAL',
|
|
2965
|
+
state: 'new',
|
|
2966
|
+
mediaType: 'telephony',
|
|
2967
|
+
},
|
|
2968
|
+
agentsPendingWrapUp: ['agent-1', agentId, 'agent-3'],
|
|
2969
|
+
},
|
|
2970
|
+
};
|
|
2971
|
+
|
|
2972
|
+
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
2973
|
+
|
|
2974
|
+
expect(removeTaskSpy).not.toHaveBeenCalled();
|
|
2975
|
+
expect(taskManager.getTask(taskId)).toBeDefined();
|
|
2976
|
+
});
|
|
2977
|
+
});
|
|
2978
|
+
|
|
2979
|
+
describe('CONTACT_MERGED event handling', () => {
|
|
2980
|
+
let task;
|
|
2981
|
+
let taskEmitSpy;
|
|
2274
2982
|
let managerEmitSpy;
|
|
2275
2983
|
|
|
2276
2984
|
beforeEach(() => {
|
|
@@ -2339,7 +3047,7 @@ describe('TaskManager', () => {
|
|
|
2339
3047
|
it('should remove child task when childInteractionId is present in CONTACT_MERGED', () => {
|
|
2340
3048
|
const childTaskId = 'child-task-id';
|
|
2341
3049
|
const parentTaskId = 'parent-task-id';
|
|
2342
|
-
|
|
3050
|
+
|
|
2343
3051
|
// Create child task
|
|
2344
3052
|
const childPayload = {
|
|
2345
3053
|
data: {
|
|
@@ -2350,7 +3058,7 @@ describe('TaskManager', () => {
|
|
|
2350
3058
|
},
|
|
2351
3059
|
};
|
|
2352
3060
|
webSocketManagerMock.emit('message', JSON.stringify(childPayload));
|
|
2353
|
-
|
|
3061
|
+
|
|
2354
3062
|
// Verify child task exists
|
|
2355
3063
|
expect(taskManager.getTask(childTaskId)).toBeDefined();
|
|
2356
3064
|
|
|
@@ -2383,10 +3091,10 @@ describe('TaskManager', () => {
|
|
|
2383
3091
|
|
|
2384
3092
|
// Verify child task was removed
|
|
2385
3093
|
expect(taskManager.getTask(childTaskId)).toBeUndefined();
|
|
2386
|
-
|
|
3094
|
+
|
|
2387
3095
|
// Verify parent task still exists
|
|
2388
3096
|
expect(taskManager.getTask(parentTaskId)).toBeDefined();
|
|
2389
|
-
|
|
3097
|
+
|
|
2390
3098
|
// Verify TASK_MERGED event was emitted
|
|
2391
3099
|
expect(managerEmitSpy).toHaveBeenCalledWith(
|
|
2392
3100
|
TASK_EVENTS.TASK_MERGED,
|
|
@@ -2444,7 +3152,7 @@ describe('TaskManager', () => {
|
|
|
2444
3152
|
},
|
|
2445
3153
|
};
|
|
2446
3154
|
webSocketManagerMock.emit('message', JSON.stringify(otherPayload));
|
|
2447
|
-
|
|
3155
|
+
|
|
2448
3156
|
const otherTask = taskManager.getTask(otherTaskId);
|
|
2449
3157
|
const otherTaskEmitSpy = jest.spyOn(otherTask, 'emit');
|
|
2450
3158
|
|
|
@@ -2466,7 +3174,7 @@ describe('TaskManager', () => {
|
|
|
2466
3174
|
// Verify other task was not affected
|
|
2467
3175
|
expect(otherTaskEmitSpy).not.toHaveBeenCalled();
|
|
2468
3176
|
expect(otherTask.data.interaction.mediaType).toBe('chat');
|
|
2469
|
-
|
|
3177
|
+
|
|
2470
3178
|
// Verify original task was updated
|
|
2471
3179
|
expect(managerEmitSpy).toHaveBeenCalledWith(
|
|
2472
3180
|
TASK_EVENTS.TASK_MERGED,
|
|
@@ -2479,5 +3187,165 @@ describe('TaskManager', () => {
|
|
|
2479
3187
|
});
|
|
2480
3188
|
});
|
|
2481
3189
|
|
|
2482
|
-
|
|
3190
|
+
describe('Campaign Preview Reservation', () => {
|
|
3191
|
+
it('should create a task and emit TASK_CAMPAIGN_PREVIEW_RESERVATION when AgentOfferCampaignReservation is received', () => {
|
|
3192
|
+
const campaignPayload = {
|
|
3193
|
+
data: {
|
|
3194
|
+
type: CC_EVENTS.AGENT_OFFER_CAMPAIGN_RESERVATION,
|
|
3195
|
+
interactionId: 'campaign-interaction-123',
|
|
3196
|
+
agentId: taskDataMock.agentId,
|
|
3197
|
+
orgId: taskDataMock.orgId,
|
|
3198
|
+
trackingId: 'campaign-tracking-456',
|
|
3199
|
+
interaction: {
|
|
3200
|
+
mediaType: 'telephony',
|
|
3201
|
+
callProcessingDetails: {
|
|
3202
|
+
campaignId: 'campaign-789',
|
|
3203
|
+
},
|
|
3204
|
+
},
|
|
3205
|
+
},
|
|
3206
|
+
};
|
|
3207
|
+
|
|
3208
|
+
const managerEmitSpy = jest.spyOn(taskManager, 'emit');
|
|
3209
|
+
|
|
3210
|
+
webSocketManagerMock.emit('message', JSON.stringify(campaignPayload));
|
|
2483
3211
|
|
|
3212
|
+
// Should emit with a task object (not raw data)
|
|
3213
|
+
expect(managerEmitSpy).toHaveBeenCalledWith(
|
|
3214
|
+
TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_RESERVATION,
|
|
3215
|
+
expect.objectContaining({
|
|
3216
|
+
data: expect.objectContaining({
|
|
3217
|
+
interactionId: 'campaign-interaction-123',
|
|
3218
|
+
type: CC_EVENTS.AGENT_OFFER_CAMPAIGN_RESERVATION,
|
|
3219
|
+
wrapUpRequired: false,
|
|
3220
|
+
isAutoAnswering: false,
|
|
3221
|
+
}),
|
|
3222
|
+
})
|
|
3223
|
+
);
|
|
3224
|
+
|
|
3225
|
+
// Task should be in the collection so subsequent events (e.g. AGENT_CONTACT_ASSIGNED) can find it
|
|
3226
|
+
expect(taskManager['taskCollection']['campaign-interaction-123']).toBeDefined();
|
|
3227
|
+
});
|
|
3228
|
+
|
|
3229
|
+
it('should not emit TASK_INCOMING for campaign preview reservation when incoming WebRTC call arrives', () => {
|
|
3230
|
+
const campaignPayload = {
|
|
3231
|
+
data: {
|
|
3232
|
+
type: CC_EVENTS.AGENT_OFFER_CAMPAIGN_RESERVATION,
|
|
3233
|
+
interactionId: 'campaign-interaction-123',
|
|
3234
|
+
agentId: taskDataMock.agentId,
|
|
3235
|
+
orgId: taskDataMock.orgId,
|
|
3236
|
+
trackingId: 'campaign-tracking-456',
|
|
3237
|
+
interaction: {
|
|
3238
|
+
mediaType: 'telephony',
|
|
3239
|
+
callProcessingDetails: {
|
|
3240
|
+
campaignId: 'campaign-789',
|
|
3241
|
+
},
|
|
3242
|
+
},
|
|
3243
|
+
},
|
|
3244
|
+
};
|
|
3245
|
+
|
|
3246
|
+
// Remove the default task so only the campaign preview task is in the collection
|
|
3247
|
+
delete taskManager['taskCollection'][taskId];
|
|
3248
|
+
|
|
3249
|
+
// Create campaign preview task via the reservation event
|
|
3250
|
+
webSocketManagerMock.emit('message', JSON.stringify(campaignPayload));
|
|
3251
|
+
|
|
3252
|
+
const managerEmitSpy = jest.spyOn(taskManager, 'emit');
|
|
3253
|
+
|
|
3254
|
+
// Simulate an incoming WebRTC call
|
|
3255
|
+
const incomingCallCb = onSpy.mock.calls[0][1];
|
|
3256
|
+
incomingCallCb(mockCall);
|
|
3257
|
+
|
|
3258
|
+
// TASK_INCOMING should NOT be emitted because the only telephony task is a campaign preview
|
|
3259
|
+
expect(managerEmitSpy).not.toHaveBeenCalledWith(TASK_EVENTS.TASK_INCOMING, expect.anything());
|
|
3260
|
+
});
|
|
3261
|
+
|
|
3262
|
+
it('should update existing task when AgentOfferCampaignReservation is received for known interactionId', () => {
|
|
3263
|
+
const campaignPayload = {
|
|
3264
|
+
data: {
|
|
3265
|
+
type: CC_EVENTS.AGENT_OFFER_CAMPAIGN_RESERVATION,
|
|
3266
|
+
interactionId: 'campaign-interaction-123',
|
|
3267
|
+
agentId: taskDataMock.agentId,
|
|
3268
|
+
orgId: taskDataMock.orgId,
|
|
3269
|
+
trackingId: 'campaign-tracking-456',
|
|
3270
|
+
interaction: {
|
|
3271
|
+
mediaType: 'telephony',
|
|
3272
|
+
callProcessingDetails: {
|
|
3273
|
+
campaignId: 'campaign-789',
|
|
3274
|
+
},
|
|
3275
|
+
},
|
|
3276
|
+
},
|
|
3277
|
+
};
|
|
3278
|
+
|
|
3279
|
+
// Send the first reservation to create the task
|
|
3280
|
+
webSocketManagerMock.emit('message', JSON.stringify(campaignPayload));
|
|
3281
|
+
|
|
3282
|
+
const managerEmitSpy = jest.spyOn(taskManager, 'emit');
|
|
3283
|
+
|
|
3284
|
+
// Send a second reservation for the same interactionId
|
|
3285
|
+
webSocketManagerMock.emit('message', JSON.stringify(campaignPayload));
|
|
3286
|
+
|
|
3287
|
+
expect(managerEmitSpy).toHaveBeenCalledWith(
|
|
3288
|
+
TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_RESERVATION,
|
|
3289
|
+
expect.objectContaining({
|
|
3290
|
+
data: expect.objectContaining({
|
|
3291
|
+
interactionId: 'campaign-interaction-123',
|
|
3292
|
+
}),
|
|
3293
|
+
})
|
|
3294
|
+
);
|
|
3295
|
+
});
|
|
3296
|
+
|
|
3297
|
+
it('should update task data but NOT remove task when CampaignContactUpdated is received', () => {
|
|
3298
|
+
const campaignInteractionId = 'campaign-interaction-123';
|
|
3299
|
+
|
|
3300
|
+
// First create a campaign preview task
|
|
3301
|
+
const reservationPayload = {
|
|
3302
|
+
data: {
|
|
3303
|
+
type: CC_EVENTS.AGENT_OFFER_CAMPAIGN_RESERVATION,
|
|
3304
|
+
interactionId: campaignInteractionId,
|
|
3305
|
+
agentId: taskDataMock.agentId,
|
|
3306
|
+
orgId: taskDataMock.orgId,
|
|
3307
|
+
trackingId: 'campaign-tracking-456',
|
|
3308
|
+
interaction: {
|
|
3309
|
+
mediaType: 'telephony',
|
|
3310
|
+
callProcessingDetails: {
|
|
3311
|
+
campaignId: 'campaign-789',
|
|
3312
|
+
},
|
|
3313
|
+
},
|
|
3314
|
+
},
|
|
3315
|
+
};
|
|
3316
|
+
|
|
3317
|
+
webSocketManagerMock.emit('message', JSON.stringify(reservationPayload));
|
|
3318
|
+
|
|
3319
|
+
// Verify task exists in collection
|
|
3320
|
+
const task = taskManager['taskCollection'][campaignInteractionId];
|
|
3321
|
+
expect(task).toBeDefined();
|
|
3322
|
+
|
|
3323
|
+
const taskEmitSpy = jest.spyOn(task, 'emit');
|
|
3324
|
+
|
|
3325
|
+
// Now send CampaignContactUpdated
|
|
3326
|
+
const campaignContactUpdatedPayload = {
|
|
3327
|
+
data: {
|
|
3328
|
+
type: CC_EVENTS.CAMPAIGN_CONTACT_UPDATED,
|
|
3329
|
+
interactionId: campaignInteractionId,
|
|
3330
|
+
agentId: taskDataMock.agentId,
|
|
3331
|
+
orgId: taskDataMock.orgId,
|
|
3332
|
+
interaction: {
|
|
3333
|
+
mediaType: 'telephony',
|
|
3334
|
+
state: 'new',
|
|
3335
|
+
callProcessingDetails: {
|
|
3336
|
+
campaignId: 'campaign-789',
|
|
3337
|
+
},
|
|
3338
|
+
},
|
|
3339
|
+
},
|
|
3340
|
+
};
|
|
3341
|
+
|
|
3342
|
+
webSocketManagerMock.emit('message', JSON.stringify(campaignContactUpdatedPayload));
|
|
3343
|
+
|
|
3344
|
+
// Task should still exist in collection (not removed — non-terminal event)
|
|
3345
|
+
expect(taskManager['taskCollection'][campaignInteractionId]).toBeDefined();
|
|
3346
|
+
|
|
3347
|
+
// TASK_END should NOT have been emitted
|
|
3348
|
+
expect(taskEmitSpy).not.toHaveBeenCalledWith(TASK_EVENTS.TASK_END, expect.anything());
|
|
3349
|
+
});
|
|
3350
|
+
});
|
|
3351
|
+
});
|