@webex/contact-center 3.9.0-next.9 → 3.10.0-multi-llms.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 +193 -47
- package/dist/cc.js.map +1 -1
- package/dist/constants.js +1 -0
- package/dist/constants.js.map +1 -1
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -1
- package/dist/logger-proxy.js +24 -1
- package/dist/logger-proxy.js.map +1 -1
- package/dist/metrics/behavioral-events.js +89 -0
- package/dist/metrics/behavioral-events.js.map +1 -1
- package/dist/metrics/constants.js +30 -2
- package/dist/metrics/constants.js.map +1 -1
- package/dist/services/AddressBook.js +271 -0
- package/dist/services/AddressBook.js.map +1 -0
- package/dist/services/EntryPoint.js +227 -0
- package/dist/services/EntryPoint.js.map +1 -0
- package/dist/services/Queue.js +261 -0
- package/dist/services/Queue.js.map +1 -0
- package/dist/services/config/constants.js +36 -2
- package/dist/services/config/constants.js.map +1 -1
- package/dist/services/config/index.js +29 -21
- package/dist/services/config/index.js.map +1 -1
- package/dist/services/config/types.js +33 -1
- package/dist/services/config/types.js.map +1 -1
- package/dist/services/core/Utils.js +91 -31
- package/dist/services/core/Utils.js.map +1 -1
- package/dist/services/core/constants.js +17 -1
- package/dist/services/core/constants.js.map +1 -1
- package/dist/services/task/TaskManager.js +150 -7
- package/dist/services/task/TaskManager.js.map +1 -1
- package/dist/services/task/TaskUtils.js +104 -0
- package/dist/services/task/TaskUtils.js.map +1 -0
- package/dist/services/task/constants.js +26 -1
- package/dist/services/task/constants.js.map +1 -1
- package/dist/services/task/contact.js +86 -0
- package/dist/services/task/contact.js.map +1 -1
- package/dist/services/task/index.js +302 -39
- package/dist/services/task/index.js.map +1 -1
- package/dist/services/task/types.js +12 -0
- package/dist/services/task/types.js.map +1 -1
- package/dist/types/cc.d.ts +121 -35
- package/dist/types/constants.d.ts +1 -0
- package/dist/types/index.d.ts +4 -3
- package/dist/types/metrics/constants.d.ts +24 -1
- package/dist/types/services/AddressBook.d.ts +74 -0
- package/dist/types/services/EntryPoint.d.ts +67 -0
- package/dist/types/services/Queue.d.ts +76 -0
- package/dist/types/services/config/constants.d.ts +35 -1
- package/dist/types/services/config/index.d.ts +6 -9
- package/dist/types/services/config/types.d.ts +79 -58
- package/dist/types/services/core/Utils.d.ts +33 -5
- package/dist/types/services/core/constants.d.ts +14 -0
- package/dist/types/services/task/TaskUtils.d.ts +42 -0
- package/dist/types/services/task/constants.d.ts +23 -0
- package/dist/types/services/task/contact.d.ts +10 -0
- package/dist/types/services/task/index.d.ts +84 -3
- package/dist/types/services/task/types.d.ts +245 -21
- package/dist/types/types.d.ts +162 -0
- package/dist/types/utils/PageCache.d.ts +173 -0
- package/dist/types.js +17 -0
- package/dist/types.js.map +1 -1
- package/dist/utils/PageCache.js +192 -0
- package/dist/utils/PageCache.js.map +1 -0
- package/dist/webex.js +1 -1
- package/package.json +10 -9
- package/src/cc.ts +217 -52
- package/src/constants.ts +1 -0
- package/src/index.ts +17 -2
- package/src/logger-proxy.ts +24 -1
- package/src/metrics/behavioral-events.ts +94 -0
- package/src/metrics/constants.ts +34 -1
- package/src/services/AddressBook.ts +291 -0
- package/src/services/EntryPoint.ts +241 -0
- package/src/services/Queue.ts +277 -0
- package/src/services/config/constants.ts +42 -2
- package/src/services/config/index.ts +30 -30
- package/src/services/config/types.ts +59 -58
- package/src/services/core/Utils.ts +101 -41
- package/src/services/core/constants.ts +16 -0
- package/src/services/task/TaskManager.ts +181 -9
- package/src/services/task/TaskUtils.ts +113 -0
- package/src/services/task/constants.ts +25 -0
- package/src/services/task/contact.ts +80 -0
- package/src/services/task/index.ts +364 -54
- package/src/services/task/types.ts +264 -20
- package/src/types.ts +180 -0
- package/src/utils/PageCache.ts +252 -0
- package/test/unit/spec/cc.ts +282 -85
- package/test/unit/spec/metrics/behavioral-events.ts +42 -0
- package/test/unit/spec/services/AddressBook.ts +332 -0
- package/test/unit/spec/services/EntryPoint.ts +259 -0
- package/test/unit/spec/services/Queue.ts +323 -0
- package/test/unit/spec/services/config/index.ts +279 -65
- package/test/unit/spec/services/core/Utils.ts +262 -31
- package/test/unit/spec/services/task/TaskManager.ts +752 -1
- package/test/unit/spec/services/task/TaskUtils.ts +131 -0
- package/test/unit/spec/services/task/contact.ts +31 -1
- package/test/unit/spec/services/task/index.ts +675 -69
- package/umd/contact-center.min.js +2 -2
- package/umd/contact-center.min.js.map +1 -1
|
@@ -39,7 +39,8 @@ describe('Task', () => {
|
|
|
39
39
|
let loggerInfoSpy;
|
|
40
40
|
let loggerLogSpy;
|
|
41
41
|
let loggerErrorSpy;
|
|
42
|
-
let
|
|
42
|
+
let calculateDestAgentIdSpy;
|
|
43
|
+
let calculateDestTypeSpy;
|
|
43
44
|
|
|
44
45
|
const taskId = '0ae913a4-c857-4705-8d49-76dd3dde75e4';
|
|
45
46
|
const mockTrack = {} as MediaStreamTrack;
|
|
@@ -75,6 +76,9 @@ describe('Task', () => {
|
|
|
75
76
|
wrapup: jest.fn().mockResolvedValue({}),
|
|
76
77
|
pauseRecording: jest.fn().mockResolvedValue({}),
|
|
77
78
|
resumeRecording: jest.fn().mockResolvedValue({}),
|
|
79
|
+
consultConference: jest.fn().mockResolvedValue({}),
|
|
80
|
+
exitConference: jest.fn().mockResolvedValue({}),
|
|
81
|
+
conferenceTransfer: jest.fn().mockResolvedValue({}),
|
|
78
82
|
};
|
|
79
83
|
|
|
80
84
|
mockMetricsManager = {
|
|
@@ -116,6 +120,32 @@ describe('Task', () => {
|
|
|
116
120
|
interaction: {
|
|
117
121
|
mediaType: 'telephony',
|
|
118
122
|
mainInteractionId: taskId,
|
|
123
|
+
participants: {
|
|
124
|
+
'723a8ffb-a26e-496d-b14a-ff44fb83b64f': {
|
|
125
|
+
pType: 'Agent',
|
|
126
|
+
type: 'AGENT',
|
|
127
|
+
id: '723a8ffb-a26e-496d-b14a-ff44fb83b64f',
|
|
128
|
+
hasLeft: false,
|
|
129
|
+
hasJoined: true,
|
|
130
|
+
isWrapUp: false,
|
|
131
|
+
},
|
|
132
|
+
'f520d6b5-28ad-4f2f-b83e-781bb64af617': {
|
|
133
|
+
pType: 'Agent',
|
|
134
|
+
type: 'AGENT',
|
|
135
|
+
id: 'f520d6b5-28ad-4f2f-b83e-781bb64af617',
|
|
136
|
+
hasLeft: false,
|
|
137
|
+
hasJoined: true,
|
|
138
|
+
isWrapUp: false,
|
|
139
|
+
},
|
|
140
|
+
'ebeb893b-ba67-4f36-8418-95c7492b28c2': {
|
|
141
|
+
pType: 'Agent',
|
|
142
|
+
type: 'AGENT',
|
|
143
|
+
id: 'ebeb893b-ba67-4f36-8418-95c7492b28c2',
|
|
144
|
+
hasLeft: false,
|
|
145
|
+
hasJoined: true,
|
|
146
|
+
isWrapUp: false,
|
|
147
|
+
},
|
|
148
|
+
},
|
|
119
149
|
media: {
|
|
120
150
|
'58a45567-4e61-4f4b-a580-5bc86357bef0': {
|
|
121
151
|
holdTimestamp: null,
|
|
@@ -142,13 +172,18 @@ describe('Task', () => {
|
|
|
142
172
|
},
|
|
143
173
|
};
|
|
144
174
|
|
|
145
|
-
// Mock
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
175
|
+
// Mock calculateDestAgentId to return the expected destination agent
|
|
176
|
+
calculateDestAgentIdSpy = jest.spyOn(Utils, 'calculateDestAgentId').mockReturnValue(taskDataMock.destAgentId);
|
|
177
|
+
|
|
178
|
+
// Mock calculateDestType to return 'agent' by default
|
|
179
|
+
calculateDestTypeSpy = jest.spyOn(Utils, 'calculateDestType').mockReturnValue('agent');
|
|
149
180
|
|
|
150
|
-
// Create an instance of Task
|
|
151
|
-
task = new Task(contactMock, webCallingService, taskDataMock
|
|
181
|
+
// Create an instance of Task with wrapupData and agentId
|
|
182
|
+
task = new Task(contactMock, webCallingService, taskDataMock, {
|
|
183
|
+
wrapUpProps: { wrapUpReasonList: [] },
|
|
184
|
+
autoWrapEnabled: false,
|
|
185
|
+
autoWrapAfterSeconds: 0
|
|
186
|
+
}, taskDataMock.agentId);
|
|
152
187
|
|
|
153
188
|
// Mock navigator.mediaDevices
|
|
154
189
|
global.navigator.mediaDevices = {
|
|
@@ -214,7 +249,7 @@ describe('Task', () => {
|
|
|
214
249
|
});
|
|
215
250
|
|
|
216
251
|
describe('updateTaskData cases', () => {
|
|
217
|
-
it('
|
|
252
|
+
it('updates the task data by overwrite', async () => {
|
|
218
253
|
const newData = {
|
|
219
254
|
type: CC_EVENTS.AGENT_CONTACT_ASSIGNED,
|
|
220
255
|
agentId: '723a8ffb-a26e-496d-b14a-ff44fb83b64f',
|
|
@@ -263,12 +298,12 @@ describe('Task', () => {
|
|
|
263
298
|
expect(task.data).toEqual(newData);
|
|
264
299
|
});
|
|
265
300
|
|
|
266
|
-
it('
|
|
301
|
+
it('updates the task data by merging with key removal', async () => {
|
|
267
302
|
const newData = {
|
|
268
|
-
//
|
|
303
|
+
// Purposefully omit other keys to test remove and merge behavior
|
|
269
304
|
isConsulting: true, // Add a new custom key to test persistence
|
|
270
305
|
interaction: {
|
|
271
|
-
//
|
|
306
|
+
// Purposefully omit other interaction keys to test removal
|
|
272
307
|
media: {
|
|
273
308
|
'58a45567-4e61-4f4b-a580-5bc86357bef0': {
|
|
274
309
|
holdTimestamp: null,
|
|
@@ -295,11 +330,12 @@ describe('Task', () => {
|
|
|
295
330
|
},
|
|
296
331
|
};
|
|
297
332
|
|
|
333
|
+
// The reconcileData method removes keys from oldData that are not in newData
|
|
334
|
+
// This means only keys present in newData will remain in the final result
|
|
298
335
|
const expectedData: TaskData = {
|
|
299
|
-
|
|
300
|
-
isConsulting: true,
|
|
336
|
+
isConsulting: true, // New key is added
|
|
301
337
|
interaction: {
|
|
302
|
-
|
|
338
|
+
// Only the media key from newData.interaction remains
|
|
303
339
|
media: {
|
|
304
340
|
'58a45567-4e61-4f4b-a580-5bc86357bef0': {
|
|
305
341
|
holdTimestamp: null,
|
|
@@ -332,6 +368,60 @@ describe('Task', () => {
|
|
|
332
368
|
|
|
333
369
|
expect(task.data).toEqual(expectedData);
|
|
334
370
|
});
|
|
371
|
+
|
|
372
|
+
it('updates the task data by merging and preserving existing keys', async () => {
|
|
373
|
+
const newData = {
|
|
374
|
+
...taskDataMock, // Include all existing keys to test merge without removal
|
|
375
|
+
isConsulting: true, // Add a new custom key
|
|
376
|
+
interaction: {
|
|
377
|
+
...taskDataMock.interaction, // Include existing interaction data
|
|
378
|
+
media: {
|
|
379
|
+
...taskDataMock.interaction.media, // Include existing media
|
|
380
|
+
'58a45567-4e61-4f4b-a580-5bc86357bef0': {
|
|
381
|
+
holdTimestamp: null,
|
|
382
|
+
isHold: true,
|
|
383
|
+
mType: 'consult',
|
|
384
|
+
mediaMgr: 'callmm',
|
|
385
|
+
mediaResourceId: '58a45567-4e61-4f4b-a580-5bc86357bef0',
|
|
386
|
+
mediaType: 'telephony',
|
|
387
|
+
participants: [
|
|
388
|
+
'f520d6b5-28ad-4f2f-b83e-781bb64af617',
|
|
389
|
+
'723a8ffb-a26e-496d-b14a-ff44fb83b64f',
|
|
390
|
+
],
|
|
391
|
+
},
|
|
392
|
+
},
|
|
393
|
+
},
|
|
394
|
+
};
|
|
395
|
+
|
|
396
|
+
const expectedData: TaskData = {
|
|
397
|
+
...taskDataMock,
|
|
398
|
+
isConsulting: true,
|
|
399
|
+
interaction: {
|
|
400
|
+
...taskDataMock.interaction,
|
|
401
|
+
media: {
|
|
402
|
+
...taskDataMock.interaction.media,
|
|
403
|
+
'58a45567-4e61-4f4b-a580-5bc86357bef0': {
|
|
404
|
+
holdTimestamp: null,
|
|
405
|
+
isHold: true,
|
|
406
|
+
mType: 'consult',
|
|
407
|
+
mediaMgr: 'callmm',
|
|
408
|
+
mediaResourceId: '58a45567-4e61-4f4b-a580-5bc86357bef0',
|
|
409
|
+
mediaType: 'telephony',
|
|
410
|
+
participants: [
|
|
411
|
+
'f520d6b5-28ad-4f2f-b83e-781bb64af617',
|
|
412
|
+
'723a8ffb-a26e-496d-b14a-ff44fb83b64f',
|
|
413
|
+
],
|
|
414
|
+
},
|
|
415
|
+
},
|
|
416
|
+
},
|
|
417
|
+
};
|
|
418
|
+
|
|
419
|
+
expect(task.data).toEqual(taskDataMock);
|
|
420
|
+
const shouldOverwrite = false;
|
|
421
|
+
task.updateTaskData(newData, shouldOverwrite);
|
|
422
|
+
|
|
423
|
+
expect(task.data).toEqual(expectedData);
|
|
424
|
+
});
|
|
335
425
|
});
|
|
336
426
|
|
|
337
427
|
it('should accept a task and answer call when using BROWSER login option', async () => {
|
|
@@ -567,6 +657,40 @@ describe('Task', () => {
|
|
|
567
657
|
);
|
|
568
658
|
});
|
|
569
659
|
|
|
660
|
+
it('should hold the task with custom mediaResourceId and return the expected response', async () => {
|
|
661
|
+
const customMediaResourceId = 'custom-media-resource-id-123';
|
|
662
|
+
const expectedResponse: TaskResponse = {data: {interactionId: taskId}} as AgentContact;
|
|
663
|
+
contactMock.hold.mockResolvedValue(expectedResponse);
|
|
664
|
+
|
|
665
|
+
const response = await task.hold(customMediaResourceId);
|
|
666
|
+
|
|
667
|
+
expect(contactMock.hold).toHaveBeenCalledWith({
|
|
668
|
+
interactionId: taskId,
|
|
669
|
+
data: {mediaResourceId: customMediaResourceId},
|
|
670
|
+
});
|
|
671
|
+
expect(response).toEqual(expectedResponse);
|
|
672
|
+
expect(loggerInfoSpy).toHaveBeenCalledWith(`Holding task`, {
|
|
673
|
+
module: TASK_FILE,
|
|
674
|
+
method: 'hold',
|
|
675
|
+
interactionId: task.data.interactionId,
|
|
676
|
+
});
|
|
677
|
+
expect(loggerLogSpy).toHaveBeenCalledWith(`Task placed on hold successfully`, {
|
|
678
|
+
module: TASK_FILE,
|
|
679
|
+
method: 'hold',
|
|
680
|
+
interactionId: task.data.interactionId,
|
|
681
|
+
});
|
|
682
|
+
expect(mockMetricsManager.trackEvent).toHaveBeenNthCalledWith(
|
|
683
|
+
1,
|
|
684
|
+
METRIC_EVENT_NAMES.TASK_HOLD_SUCCESS,
|
|
685
|
+
{
|
|
686
|
+
...MetricsManager.getCommonTrackingFieldForAQMResponse(expectedResponse),
|
|
687
|
+
taskId: taskDataMock.interactionId,
|
|
688
|
+
mediaResourceId: customMediaResourceId,
|
|
689
|
+
},
|
|
690
|
+
['operational', 'behavioral']
|
|
691
|
+
);
|
|
692
|
+
});
|
|
693
|
+
|
|
570
694
|
it('should handle errors in hold method', async () => {
|
|
571
695
|
const error = {details: (global as any).makeFailure('Hold Failed')};
|
|
572
696
|
contactMock.hold.mockImplementation(() => {
|
|
@@ -596,6 +720,36 @@ describe('Task', () => {
|
|
|
596
720
|
);
|
|
597
721
|
});
|
|
598
722
|
|
|
723
|
+
it('should handle errors in hold method with custom mediaResourceId', async () => {
|
|
724
|
+
const customMediaResourceId = 'custom-media-resource-id-456';
|
|
725
|
+
const error = {details: (global as any).makeFailure('Hold Failed with custom mediaResourceId')};
|
|
726
|
+
contactMock.hold.mockImplementation(() => {
|
|
727
|
+
throw error;
|
|
728
|
+
});
|
|
729
|
+
|
|
730
|
+
await expect(task.hold(customMediaResourceId)).rejects.toThrow(error.details.data.reason);
|
|
731
|
+
expect(generateTaskErrorObjectSpy).toHaveBeenCalledWith(error, 'hold', TASK_FILE);
|
|
732
|
+
const expectedTaskErrorFieldsHold = {
|
|
733
|
+
trackingId: error.details.trackingId,
|
|
734
|
+
errorMessage: error.details.data.reason,
|
|
735
|
+
errorType: '',
|
|
736
|
+
errorData: '',
|
|
737
|
+
reasonCode: 0,
|
|
738
|
+
};
|
|
739
|
+
expect(mockMetricsManager.trackEvent).toHaveBeenNthCalledWith(
|
|
740
|
+
1,
|
|
741
|
+
METRIC_EVENT_NAMES.TASK_HOLD_FAILED,
|
|
742
|
+
{
|
|
743
|
+
taskId: taskDataMock.interactionId,
|
|
744
|
+
mediaResourceId: customMediaResourceId,
|
|
745
|
+
error: error.toString(),
|
|
746
|
+
...expectedTaskErrorFieldsHold,
|
|
747
|
+
...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details),
|
|
748
|
+
},
|
|
749
|
+
['operational', 'behavioral']
|
|
750
|
+
);
|
|
751
|
+
});
|
|
752
|
+
|
|
599
753
|
it('should resume the task and return the expected response', async () => {
|
|
600
754
|
const expectedResponse: TaskResponse = {data: {interactionId: taskId}} as AgentContact;
|
|
601
755
|
contactMock.unHold.mockResolvedValue(expectedResponse);
|
|
@@ -620,6 +774,29 @@ describe('Task', () => {
|
|
|
620
774
|
);
|
|
621
775
|
});
|
|
622
776
|
|
|
777
|
+
it('should resume the task with custom mediaResourceId and return the expected response', async () => {
|
|
778
|
+
const customMediaResourceId = 'custom-media-resource-id-789';
|
|
779
|
+
const expectedResponse: TaskResponse = {data: {interactionId: taskId}} as AgentContact;
|
|
780
|
+
contactMock.unHold.mockResolvedValue(expectedResponse);
|
|
781
|
+
const response = await task.resume(customMediaResourceId);
|
|
782
|
+
expect(contactMock.unHold).toHaveBeenCalledWith({
|
|
783
|
+
interactionId: taskId,
|
|
784
|
+
data: {mediaResourceId: customMediaResourceId},
|
|
785
|
+
});
|
|
786
|
+
expect(response).toEqual(expectedResponse);
|
|
787
|
+
expect(mockMetricsManager.trackEvent).toHaveBeenNthCalledWith(
|
|
788
|
+
1,
|
|
789
|
+
METRIC_EVENT_NAMES.TASK_RESUME_SUCCESS,
|
|
790
|
+
{
|
|
791
|
+
taskId: taskDataMock.interactionId,
|
|
792
|
+
mainInteractionId: taskDataMock.interaction.mainInteractionId,
|
|
793
|
+
mediaResourceId: customMediaResourceId,
|
|
794
|
+
...MetricsManager.getCommonTrackingFieldForAQMResponse(expectedResponse),
|
|
795
|
+
},
|
|
796
|
+
['operational', 'behavioral']
|
|
797
|
+
);
|
|
798
|
+
});
|
|
799
|
+
|
|
623
800
|
it('should handle errors in resume method', async () => {
|
|
624
801
|
const error = {details: (global as any).makeFailure('Resume Failed')};
|
|
625
802
|
contactMock.unHold.mockImplementation(() => {
|
|
@@ -651,6 +828,36 @@ describe('Task', () => {
|
|
|
651
828
|
);
|
|
652
829
|
});
|
|
653
830
|
|
|
831
|
+
it('should handle errors in resume method with custom mediaResourceId', async () => {
|
|
832
|
+
const customMediaResourceId = 'custom-media-resource-id-999';
|
|
833
|
+
const error = {details: (global as any).makeFailure('Resume Failed with custom mediaResourceId')};
|
|
834
|
+
contactMock.unHold.mockImplementation(() => {
|
|
835
|
+
throw error;
|
|
836
|
+
});
|
|
837
|
+
|
|
838
|
+
await expect(task.resume(customMediaResourceId)).rejects.toThrow(error.details.data.reason);
|
|
839
|
+
expect(generateTaskErrorObjectSpy).toHaveBeenCalledWith(error, 'resume', TASK_FILE);
|
|
840
|
+
const expectedTaskErrorFieldsResume = {
|
|
841
|
+
trackingId: error.details.trackingId,
|
|
842
|
+
errorMessage: error.details.data.reason,
|
|
843
|
+
errorType: '',
|
|
844
|
+
errorData: '',
|
|
845
|
+
reasonCode: 0,
|
|
846
|
+
};
|
|
847
|
+
expect(mockMetricsManager.trackEvent).toHaveBeenNthCalledWith(
|
|
848
|
+
1,
|
|
849
|
+
METRIC_EVENT_NAMES.TASK_RESUME_FAILED,
|
|
850
|
+
{
|
|
851
|
+
taskId: taskDataMock.interactionId,
|
|
852
|
+
mainInteractionId: taskDataMock.interaction.mainInteractionId,
|
|
853
|
+
mediaResourceId: customMediaResourceId,
|
|
854
|
+
...expectedTaskErrorFieldsResume,
|
|
855
|
+
...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details),
|
|
856
|
+
},
|
|
857
|
+
['operational', 'behavioral']
|
|
858
|
+
);
|
|
859
|
+
});
|
|
860
|
+
|
|
654
861
|
it('should initiate a consult call and return the expected response', async () => {
|
|
655
862
|
const consultPayload = {
|
|
656
863
|
to: '1234',
|
|
@@ -671,8 +878,8 @@ describe('Task', () => {
|
|
|
671
878
|
expect(loggerLogSpy).toHaveBeenCalledWith(`Consult started successfully to ${consultPayload.to}`, {
|
|
672
879
|
module: TASK_FILE,
|
|
673
880
|
method: 'consult',
|
|
674
|
-
trackingId: expectedResponse.trackingId,
|
|
675
881
|
interactionId: task.data.interactionId,
|
|
882
|
+
trackingId: '1234',
|
|
676
883
|
});
|
|
677
884
|
expect(mockMetricsManager.trackEvent).toHaveBeenCalledWith(
|
|
678
885
|
METRIC_EVENT_NAMES.TASK_CONSULT_START_SUCCESS,
|
|
@@ -818,15 +1025,16 @@ describe('Task', () => {
|
|
|
818
1025
|
);
|
|
819
1026
|
});
|
|
820
1027
|
|
|
821
|
-
it('should send DIALNUMBER when
|
|
1028
|
+
it('should send DIALNUMBER when calculateDestType returns dialNumber during consultTransfer', async () => {
|
|
822
1029
|
const expectedResponse: TaskResponse = {data: {interactionId: taskId}} as AgentContact;
|
|
823
1030
|
contactMock.consultTransfer.mockResolvedValue(expectedResponse);
|
|
824
1031
|
|
|
825
|
-
//
|
|
826
|
-
|
|
1032
|
+
// Mock calculateDestType to return dialNumber
|
|
1033
|
+
calculateDestTypeSpy.mockReturnValue(CONSULT_TRANSFER_DESTINATION_TYPE.DIALNUMBER);
|
|
827
1034
|
|
|
828
1035
|
await task.consultTransfer();
|
|
829
1036
|
|
|
1037
|
+
expect(calculateDestTypeSpy).toHaveBeenCalledWith(taskDataMock.interaction, taskDataMock.agentId);
|
|
830
1038
|
expect(contactMock.consultTransfer).toHaveBeenCalledWith({
|
|
831
1039
|
interactionId: taskId,
|
|
832
1040
|
data: {
|
|
@@ -836,15 +1044,16 @@ describe('Task', () => {
|
|
|
836
1044
|
});
|
|
837
1045
|
});
|
|
838
1046
|
|
|
839
|
-
it('should send ENTRYPOINT when
|
|
1047
|
+
it('should send ENTRYPOINT when calculateDestType returns entryPoint during consultTransfer', async () => {
|
|
840
1048
|
const expectedResponse: TaskResponse = {data: {interactionId: taskId}} as AgentContact;
|
|
841
1049
|
contactMock.consultTransfer.mockResolvedValue(expectedResponse);
|
|
842
1050
|
|
|
843
|
-
//
|
|
844
|
-
|
|
1051
|
+
// Mock calculateDestType to return entryPoint
|
|
1052
|
+
calculateDestTypeSpy.mockReturnValue(CONSULT_TRANSFER_DESTINATION_TYPE.ENTRYPOINT);
|
|
845
1053
|
|
|
846
1054
|
await task.consultTransfer();
|
|
847
1055
|
|
|
1056
|
+
expect(calculateDestTypeSpy).toHaveBeenCalledWith(taskDataMock.interaction, taskDataMock.agentId);
|
|
848
1057
|
expect(contactMock.consultTransfer).toHaveBeenCalledWith({
|
|
849
1058
|
interactionId: taskId,
|
|
850
1059
|
data: {
|
|
@@ -854,15 +1063,16 @@ describe('Task', () => {
|
|
|
854
1063
|
});
|
|
855
1064
|
});
|
|
856
1065
|
|
|
857
|
-
it('should
|
|
1066
|
+
it('should use AGENT when calculateDestType returns agent during consultTransfer', async () => {
|
|
858
1067
|
const expectedResponse: TaskResponse = {data: {interactionId: taskId}} as AgentContact;
|
|
859
1068
|
contactMock.consultTransfer.mockResolvedValue(expectedResponse);
|
|
860
1069
|
|
|
861
|
-
//
|
|
862
|
-
|
|
1070
|
+
// Mock calculateDestType to return agent (default behavior)
|
|
1071
|
+
calculateDestTypeSpy.mockReturnValue(CONSULT_TRANSFER_DESTINATION_TYPE.AGENT);
|
|
863
1072
|
|
|
864
1073
|
await task.consultTransfer();
|
|
865
1074
|
|
|
1075
|
+
expect(calculateDestTypeSpy).toHaveBeenCalledWith(taskDataMock.interaction, taskDataMock.agentId);
|
|
866
1076
|
expect(contactMock.consultTransfer).toHaveBeenCalledWith({
|
|
867
1077
|
interactionId: taskId,
|
|
868
1078
|
data: {
|
|
@@ -899,7 +1109,11 @@ describe('Task', () => {
|
|
|
899
1109
|
const taskWithoutDestAgentId = new Task(contactMock, webCallingService, {
|
|
900
1110
|
...taskDataMock,
|
|
901
1111
|
destAgentId: undefined,
|
|
902
|
-
}
|
|
1112
|
+
}, {
|
|
1113
|
+
wrapUpProps: { wrapUpReasonList: [] },
|
|
1114
|
+
autoWrapEnabled: false,
|
|
1115
|
+
autoWrapAfterSeconds: 0
|
|
1116
|
+
}, taskDataMock.agentId);
|
|
903
1117
|
|
|
904
1118
|
const queueConsultTransferPayload: ConsultTransferPayLoad = {
|
|
905
1119
|
to: 'some-queue-id',
|
|
@@ -907,61 +1121,123 @@ describe('Task', () => {
|
|
|
907
1121
|
};
|
|
908
1122
|
|
|
909
1123
|
// For this negative case, ensure computed destination is empty
|
|
910
|
-
|
|
1124
|
+
calculateDestAgentIdSpy.mockReturnValueOnce('');
|
|
911
1125
|
|
|
912
1126
|
await expect(
|
|
913
1127
|
taskWithoutDestAgentId.consultTransfer(queueConsultTransferPayload)
|
|
914
|
-
).rejects.toThrow('
|
|
1128
|
+
).rejects.toThrow('No agent has accepted this queue consult yet');
|
|
915
1129
|
});
|
|
916
1130
|
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
1131
|
+
describe('consultTransfer', () => {
|
|
1132
|
+
it('should successfully perform consult transfer with agent destination', async () => {
|
|
1133
|
+
const expectedResponse: TaskResponse = {
|
|
1134
|
+
data: {interactionId: taskId},
|
|
1135
|
+
trackingId: 'test-tracking-id'
|
|
1136
|
+
} as AgentContact;
|
|
1137
|
+
contactMock.consultTransfer.mockResolvedValue(expectedResponse);
|
|
1138
|
+
|
|
1139
|
+
calculateDestTypeSpy.mockReturnValue(CONSULT_TRANSFER_DESTINATION_TYPE.AGENT);
|
|
924
1140
|
|
|
925
|
-
|
|
1141
|
+
const result = await task.consultTransfer();
|
|
926
1142
|
|
|
927
|
-
|
|
928
|
-
|
|
1143
|
+
expect(calculateDestAgentIdSpy).toHaveBeenCalledWith(taskDataMock.interaction, taskDataMock.agentId);
|
|
1144
|
+
expect(calculateDestTypeSpy).toHaveBeenCalledWith(taskDataMock.interaction, taskDataMock.agentId);
|
|
1145
|
+
expect(contactMock.consultTransfer).toHaveBeenCalledWith({
|
|
1146
|
+
interactionId: taskId,
|
|
1147
|
+
data: {
|
|
1148
|
+
to: taskDataMock.destAgentId,
|
|
1149
|
+
destinationType: CONSULT_TRANSFER_DESTINATION_TYPE.AGENT,
|
|
1150
|
+
},
|
|
1151
|
+
});
|
|
1152
|
+
expect(result).toEqual(expectedResponse);
|
|
1153
|
+
expect(loggerInfoSpy).toHaveBeenCalledWith(
|
|
1154
|
+
`Initiating consult transfer to ${taskDataMock.destAgentId}`,
|
|
1155
|
+
{
|
|
1156
|
+
module: TASK_FILE,
|
|
1157
|
+
method: 'consultTransfer',
|
|
1158
|
+
interactionId: taskId,
|
|
1159
|
+
}
|
|
1160
|
+
);
|
|
1161
|
+
expect(loggerLogSpy).toHaveBeenCalledWith(
|
|
1162
|
+
`Consult transfer completed successfully to ${taskDataMock.destAgentId}`,
|
|
1163
|
+
{
|
|
1164
|
+
module: TASK_FILE,
|
|
1165
|
+
method: 'consultTransfer',
|
|
1166
|
+
trackingId: expectedResponse.trackingId,
|
|
1167
|
+
interactionId: taskId,
|
|
1168
|
+
}
|
|
1169
|
+
);
|
|
1170
|
+
});
|
|
1171
|
+
|
|
1172
|
+
it('should track metrics on successful consult transfer', async () => {
|
|
1173
|
+
const expectedResponse: TaskResponse = {
|
|
1174
|
+
data: {interactionId: taskId},
|
|
1175
|
+
trackingId: 'test-tracking-id'
|
|
1176
|
+
} as AgentContact;
|
|
1177
|
+
contactMock.consultTransfer.mockResolvedValue(expectedResponse);
|
|
1178
|
+
|
|
1179
|
+
await task.consultTransfer();
|
|
1180
|
+
|
|
1181
|
+
expect(mockMetricsManager.trackEvent).toHaveBeenCalledWith(
|
|
1182
|
+
METRIC_EVENT_NAMES.TASK_TRANSFER_SUCCESS,
|
|
1183
|
+
{
|
|
1184
|
+
taskId: taskDataMock.interactionId,
|
|
1185
|
+
destination: taskDataMock.destAgentId,
|
|
1186
|
+
destinationType: 'agent',
|
|
1187
|
+
isConsultTransfer: true,
|
|
1188
|
+
...MetricsManager.getCommonTrackingFieldForAQMResponse(expectedResponse),
|
|
1189
|
+
},
|
|
1190
|
+
['operational', 'behavioral', 'business']
|
|
1191
|
+
);
|
|
1192
|
+
});
|
|
929
1193
|
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
1194
|
+
it('should throw error when no destination agent is found', async () => {
|
|
1195
|
+
calculateDestAgentIdSpy.mockReturnValue('');
|
|
1196
|
+
|
|
1197
|
+
await expect(task.consultTransfer()).rejects.toThrow('No agent has accepted this queue consult yet');
|
|
1198
|
+
|
|
1199
|
+
expect(contactMock.consultTransfer).not.toHaveBeenCalled();
|
|
933
1200
|
});
|
|
934
1201
|
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
1202
|
+
it('should handle and rethrow contact method errors', async () => {
|
|
1203
|
+
const mockError = new Error('Consult Transfer Failed');
|
|
1204
|
+
contactMock.consultTransfer.mockRejectedValue(mockError);
|
|
1205
|
+
generateTaskErrorObjectSpy.mockReturnValue(mockError);
|
|
939
1206
|
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
1207
|
+
await expect(task.consultTransfer()).rejects.toThrow('Consult Transfer Failed');
|
|
1208
|
+
|
|
1209
|
+
expect(generateTaskErrorObjectSpy).toHaveBeenCalledWith(mockError, 'consultTransfer', TASK_FILE);
|
|
1210
|
+
expect(mockMetricsManager.trackEvent).toHaveBeenCalledWith(
|
|
1211
|
+
METRIC_EVENT_NAMES.TASK_TRANSFER_FAILED,
|
|
1212
|
+
expect.objectContaining({
|
|
1213
|
+
taskId: taskDataMock.interactionId,
|
|
1214
|
+
destination: taskDataMock.destAgentId,
|
|
1215
|
+
destinationType: 'agent',
|
|
1216
|
+
isConsultTransfer: true,
|
|
1217
|
+
error: mockError.toString(),
|
|
1218
|
+
}),
|
|
1219
|
+
['operational', 'behavioral', 'business']
|
|
1220
|
+
);
|
|
1221
|
+
});
|
|
1222
|
+
|
|
1223
|
+
it('should dynamically calculate destAgentId when not available', async () => {
|
|
1224
|
+
const consultedAgentId = 'dynamic-agent-123';
|
|
1225
|
+
calculateDestAgentIdSpy.mockReturnValue(consultedAgentId);
|
|
1226
|
+
|
|
1227
|
+
const expectedResponse: TaskResponse = {data: {interactionId: taskId}} as AgentContact;
|
|
1228
|
+
contactMock.consultTransfer.mockResolvedValue(expectedResponse);
|
|
1229
|
+
|
|
1230
|
+
await task.consultTransfer();
|
|
1231
|
+
|
|
1232
|
+
expect(calculateDestAgentIdSpy).toHaveBeenCalledWith(taskDataMock.interaction, taskDataMock.agentId);
|
|
1233
|
+
expect(contactMock.consultTransfer).toHaveBeenCalledWith({
|
|
1234
|
+
interactionId: taskId,
|
|
1235
|
+
data: {
|
|
1236
|
+
to: consultedAgentId,
|
|
1237
|
+
destinationType: 'agent',
|
|
1238
|
+
},
|
|
1239
|
+
});
|
|
1240
|
+
});
|
|
965
1241
|
});
|
|
966
1242
|
|
|
967
1243
|
it('should do vteamTransfer if destinationType is queue and return the expected response', async () => {
|
|
@@ -1575,4 +1851,334 @@ describe('Task', () => {
|
|
|
1575
1851
|
});
|
|
1576
1852
|
});
|
|
1577
1853
|
});
|
|
1854
|
+
|
|
1855
|
+
describe('Conference methods', () => {
|
|
1856
|
+
beforeEach(() => {
|
|
1857
|
+
contactMock = {
|
|
1858
|
+
consultConference: jest.fn(),
|
|
1859
|
+
exitConference: jest.fn(),
|
|
1860
|
+
conferenceTransfer: jest.fn(),
|
|
1861
|
+
};
|
|
1862
|
+
|
|
1863
|
+
task = new Task(contactMock, webCallingService, taskDataMock, {
|
|
1864
|
+
wrapUpProps: { wrapUpReasonList: [] },
|
|
1865
|
+
autoWrapEnabled: false,
|
|
1866
|
+
autoWrapAfterSeconds: 0
|
|
1867
|
+
}, taskDataMock.agentId);
|
|
1868
|
+
});
|
|
1869
|
+
|
|
1870
|
+
describe('consultConference', () => {
|
|
1871
|
+
|
|
1872
|
+
it('should successfully start conference and emit event', async () => {
|
|
1873
|
+
const mockResponse = {
|
|
1874
|
+
trackingId: 'test-tracking-id',
|
|
1875
|
+
interactionId: taskId,
|
|
1876
|
+
};
|
|
1877
|
+
contactMock.consultConference.mockResolvedValue(mockResponse);
|
|
1878
|
+
|
|
1879
|
+
|
|
1880
|
+
const result = await task.consultConference();
|
|
1881
|
+
|
|
1882
|
+
expect(contactMock.consultConference).toHaveBeenCalledWith({
|
|
1883
|
+
interactionId: taskId,
|
|
1884
|
+
data: {
|
|
1885
|
+
agentId: taskDataMock.agentId, // From task data agent ID
|
|
1886
|
+
to: taskDataMock.destAgentId, // From calculateDestAgentId() using task participants
|
|
1887
|
+
destinationType: 'agent', // From consultation data
|
|
1888
|
+
},
|
|
1889
|
+
});
|
|
1890
|
+
expect(result).toEqual(mockResponse);
|
|
1891
|
+
expect(LoggerProxy.info).toHaveBeenCalledWith(`Initiating consult conference to ${taskDataMock.destAgentId}`, {
|
|
1892
|
+
module: TASK_FILE,
|
|
1893
|
+
method: 'consultConference',
|
|
1894
|
+
interactionId: taskId,
|
|
1895
|
+
});
|
|
1896
|
+
expect(LoggerProxy.log).toHaveBeenCalledWith('Consult conference started successfully', {
|
|
1897
|
+
module: TASK_FILE,
|
|
1898
|
+
method: 'consultConference',
|
|
1899
|
+
interactionId: taskId,
|
|
1900
|
+
});
|
|
1901
|
+
});
|
|
1902
|
+
|
|
1903
|
+
it('should handle basic validation scenarios', async () => {
|
|
1904
|
+
// Agent Desktop logic validates data structure but not participant availability
|
|
1905
|
+
// This test confirms the method works with the Agent Desktop data flow
|
|
1906
|
+
const mockResponse = {
|
|
1907
|
+
trackingId: 'test-tracking-validation',
|
|
1908
|
+
interactionId: taskId,
|
|
1909
|
+
};
|
|
1910
|
+
contactMock.consultConference.mockResolvedValue(mockResponse);
|
|
1911
|
+
|
|
1912
|
+
const result = await task.consultConference();
|
|
1913
|
+
expect(result).toEqual(mockResponse);
|
|
1914
|
+
});
|
|
1915
|
+
|
|
1916
|
+
it('should handle and rethrow contact method errors', async () => {
|
|
1917
|
+
const mockError = new Error('Conference start failed');
|
|
1918
|
+
contactMock.consultConference.mockRejectedValue(mockError);
|
|
1919
|
+
generateTaskErrorObjectSpy.mockReturnValue(mockError);
|
|
1920
|
+
|
|
1921
|
+
await expect(task.consultConference()).rejects.toThrow('Conference start failed');
|
|
1922
|
+
expect(LoggerProxy.error).toHaveBeenCalledWith('Failed to start consult conference', {
|
|
1923
|
+
module: TASK_FILE,
|
|
1924
|
+
method: 'consultConference',
|
|
1925
|
+
interactionId: taskId,
|
|
1926
|
+
});
|
|
1927
|
+
});
|
|
1928
|
+
|
|
1929
|
+
it('should dynamically calculate destAgentId from participants when this.data.destAgentId is null', async () => {
|
|
1930
|
+
// Simulate scenario where destAgentId is not preserved (e.g., after hold/unhold)
|
|
1931
|
+
task.data.destAgentId = null;
|
|
1932
|
+
|
|
1933
|
+
const consultedAgentId = 'consulted-agent-123';
|
|
1934
|
+
calculateDestAgentIdSpy.mockReturnValue(consultedAgentId);
|
|
1935
|
+
|
|
1936
|
+
const mockResponse = {
|
|
1937
|
+
trackingId: 'test-tracking-dynamic',
|
|
1938
|
+
interactionId: taskId,
|
|
1939
|
+
};
|
|
1940
|
+
contactMock.consultConference.mockResolvedValue(mockResponse);
|
|
1941
|
+
|
|
1942
|
+
const result = await task.consultConference();
|
|
1943
|
+
|
|
1944
|
+
// Verify calculateDestAgentId was called to dynamically calculate the destination
|
|
1945
|
+
expect(calculateDestAgentIdSpy).toHaveBeenCalledWith(
|
|
1946
|
+
taskDataMock.interaction,
|
|
1947
|
+
taskDataMock.agentId
|
|
1948
|
+
);
|
|
1949
|
+
|
|
1950
|
+
// Verify the conference was called with the dynamically calculated destAgentId
|
|
1951
|
+
expect(contactMock.consultConference).toHaveBeenCalledWith({
|
|
1952
|
+
interactionId: taskId,
|
|
1953
|
+
data: {
|
|
1954
|
+
agentId: taskDataMock.agentId,
|
|
1955
|
+
to: consultedAgentId, // Dynamically calculated value
|
|
1956
|
+
destinationType: 'agent',
|
|
1957
|
+
},
|
|
1958
|
+
});
|
|
1959
|
+
expect(result).toEqual(mockResponse);
|
|
1960
|
+
});
|
|
1961
|
+
|
|
1962
|
+
it('should throw error when no destination agent is found (queue consult not accepted)', async () => {
|
|
1963
|
+
// Simulate queue consult scenario where no agent has accepted yet
|
|
1964
|
+
calculateDestAgentIdSpy.mockReturnValue(''); // No agent found
|
|
1965
|
+
|
|
1966
|
+
await expect(task.consultConference()).rejects.toThrow('No agent has accepted this queue consult yet');
|
|
1967
|
+
|
|
1968
|
+
// Verify the conference was NOT called
|
|
1969
|
+
expect(contactMock.consultConference).not.toHaveBeenCalled();
|
|
1970
|
+
});
|
|
1971
|
+
|
|
1972
|
+
it('should calculate destination type from participant type for regular agents', async () => {
|
|
1973
|
+
const destAgentId = 'consulted-agent-456';
|
|
1974
|
+
|
|
1975
|
+
calculateDestAgentIdSpy = jest.spyOn(Utils, 'calculateDestAgentId').mockReturnValue(destAgentId);
|
|
1976
|
+
calculateDestTypeSpy = jest.spyOn(Utils, 'calculateDestType').mockReturnValue('agent');
|
|
1977
|
+
|
|
1978
|
+
const mockResponse = {trackingId: 'test-tracking-id', interactionId: taskId};
|
|
1979
|
+
contactMock.consultConference.mockResolvedValue(mockResponse);
|
|
1980
|
+
|
|
1981
|
+
await task.consultConference();
|
|
1982
|
+
|
|
1983
|
+
expect(calculateDestTypeSpy).toHaveBeenCalledWith(
|
|
1984
|
+
task.data.interaction,
|
|
1985
|
+
taskDataMock.agentId
|
|
1986
|
+
);
|
|
1987
|
+
|
|
1988
|
+
expect(contactMock.consultConference).toHaveBeenCalledWith({
|
|
1989
|
+
interactionId: taskId,
|
|
1990
|
+
data: {
|
|
1991
|
+
agentId: taskDataMock.agentId,
|
|
1992
|
+
to: destAgentId,
|
|
1993
|
+
destinationType: 'agent',
|
|
1994
|
+
},
|
|
1995
|
+
});
|
|
1996
|
+
});
|
|
1997
|
+
|
|
1998
|
+
it('should use DN destination type for dial number participants', async () => {
|
|
1999
|
+
const destAgentId = 'dn-uuid-123';
|
|
2000
|
+
|
|
2001
|
+
calculateDestAgentIdSpy = jest.spyOn(Utils, 'calculateDestAgentId').mockReturnValue(destAgentId);
|
|
2002
|
+
calculateDestTypeSpy = jest.spyOn(Utils, 'calculateDestType').mockReturnValue('dialNumber');
|
|
2003
|
+
|
|
2004
|
+
const mockResponse = {trackingId: 'test-tracking-id-dn', interactionId: taskId};
|
|
2005
|
+
contactMock.consultConference.mockResolvedValue(mockResponse);
|
|
2006
|
+
|
|
2007
|
+
await task.consultConference();
|
|
2008
|
+
|
|
2009
|
+
expect(contactMock.consultConference).toHaveBeenCalledWith({
|
|
2010
|
+
interactionId: taskId,
|
|
2011
|
+
data: {
|
|
2012
|
+
agentId: taskDataMock.agentId,
|
|
2013
|
+
to: destAgentId,
|
|
2014
|
+
destinationType: 'dialNumber',
|
|
2015
|
+
},
|
|
2016
|
+
});
|
|
2017
|
+
});
|
|
2018
|
+
|
|
2019
|
+
it('should use EpDn destination type for entry point dial number participants', async () => {
|
|
2020
|
+
const destAgentId = 'epdn-uuid-456';
|
|
2021
|
+
|
|
2022
|
+
calculateDestAgentIdSpy = jest.spyOn(Utils, 'calculateDestAgentId').mockReturnValue(destAgentId);
|
|
2023
|
+
calculateDestTypeSpy = jest.spyOn(Utils, 'calculateDestType').mockReturnValue('entryPoint');
|
|
2024
|
+
|
|
2025
|
+
const mockResponse = {trackingId: 'test-tracking-id-epdn', interactionId: taskId};
|
|
2026
|
+
contactMock.consultConference.mockResolvedValue(mockResponse);
|
|
2027
|
+
|
|
2028
|
+
await task.consultConference();
|
|
2029
|
+
|
|
2030
|
+
expect(contactMock.consultConference).toHaveBeenCalledWith({
|
|
2031
|
+
interactionId: taskId,
|
|
2032
|
+
data: {
|
|
2033
|
+
agentId: taskDataMock.agentId,
|
|
2034
|
+
to: destAgentId,
|
|
2035
|
+
destinationType: 'entryPoint',
|
|
2036
|
+
},
|
|
2037
|
+
});
|
|
2038
|
+
});
|
|
2039
|
+
|
|
2040
|
+
it('should fall back to task.data.destinationType when calculateDestType returns empty', async () => {
|
|
2041
|
+
const destAgentId = 'consulted-agent-789';
|
|
2042
|
+
|
|
2043
|
+
calculateDestAgentIdSpy = jest.spyOn(Utils, 'calculateDestAgentId').mockReturnValue(destAgentId);
|
|
2044
|
+
calculateDestTypeSpy = jest.spyOn(Utils, 'calculateDestType').mockReturnValue(''); // No type found
|
|
2045
|
+
|
|
2046
|
+
task.data.destinationType = 'EPDN';
|
|
2047
|
+
|
|
2048
|
+
const mockResponse = {trackingId: 'test-tracking-id-fallback', interactionId: taskId};
|
|
2049
|
+
contactMock.consultConference.mockResolvedValue(mockResponse);
|
|
2050
|
+
|
|
2051
|
+
await task.consultConference();
|
|
2052
|
+
|
|
2053
|
+
expect(contactMock.consultConference).toHaveBeenCalledWith({
|
|
2054
|
+
interactionId: taskId,
|
|
2055
|
+
data: {
|
|
2056
|
+
agentId: taskDataMock.agentId,
|
|
2057
|
+
to: destAgentId,
|
|
2058
|
+
destinationType: 'EPDN', // Falls back to task.data.destinationType
|
|
2059
|
+
},
|
|
2060
|
+
});
|
|
2061
|
+
});
|
|
2062
|
+
|
|
2063
|
+
it('should handle CBT scenarios with correct destination type', async () => {
|
|
2064
|
+
const destAgentId = 'agent-cbt-uuid';
|
|
2065
|
+
|
|
2066
|
+
calculateDestAgentIdSpy = jest.spyOn(Utils, 'calculateDestAgentId').mockReturnValue(destAgentId);
|
|
2067
|
+
calculateDestTypeSpy = jest.spyOn(Utils, 'calculateDestType').mockReturnValue('dialNumber');
|
|
2068
|
+
|
|
2069
|
+
const mockResponse = {trackingId: 'test-tracking-id-cbt', interactionId: taskId};
|
|
2070
|
+
contactMock.consultConference.mockResolvedValue(mockResponse);
|
|
2071
|
+
|
|
2072
|
+
await task.consultConference();
|
|
2073
|
+
|
|
2074
|
+
expect(calculateDestTypeSpy).toHaveBeenCalledWith(
|
|
2075
|
+
task.data.interaction,
|
|
2076
|
+
taskDataMock.agentId
|
|
2077
|
+
);
|
|
2078
|
+
|
|
2079
|
+
expect(contactMock.consultConference).toHaveBeenCalledWith({
|
|
2080
|
+
interactionId: taskId,
|
|
2081
|
+
data: {
|
|
2082
|
+
agentId: taskDataMock.agentId,
|
|
2083
|
+
to: destAgentId,
|
|
2084
|
+
destinationType: 'dialNumber', // dialNumber for CBT scenarios
|
|
2085
|
+
},
|
|
2086
|
+
});
|
|
2087
|
+
});
|
|
2088
|
+
});
|
|
2089
|
+
|
|
2090
|
+
describe('exitConference', () => {
|
|
2091
|
+
it('should successfully end conference and emit event', async () => {
|
|
2092
|
+
const mockResponse = {
|
|
2093
|
+
trackingId: 'test-tracking-id-end',
|
|
2094
|
+
interactionId: taskId,
|
|
2095
|
+
};
|
|
2096
|
+
contactMock.exitConference.mockResolvedValue(mockResponse);
|
|
2097
|
+
|
|
2098
|
+
const result = await task.exitConference();
|
|
2099
|
+
|
|
2100
|
+
expect(contactMock.exitConference).toHaveBeenCalledWith({
|
|
2101
|
+
interactionId: taskId,
|
|
2102
|
+
});
|
|
2103
|
+
expect(result).toEqual(mockResponse);
|
|
2104
|
+
expect(LoggerProxy.info).toHaveBeenCalledWith('Exiting consult conference', {
|
|
2105
|
+
module: TASK_FILE,
|
|
2106
|
+
method: 'exitConference',
|
|
2107
|
+
interactionId: taskId,
|
|
2108
|
+
});
|
|
2109
|
+
expect(LoggerProxy.log).toHaveBeenCalledWith('Consult conference exited successfully', {
|
|
2110
|
+
module: TASK_FILE,
|
|
2111
|
+
method: 'exitConference',
|
|
2112
|
+
interactionId: taskId,
|
|
2113
|
+
});
|
|
2114
|
+
});
|
|
2115
|
+
|
|
2116
|
+
it('should throw error for invalid interaction ID', async () => {
|
|
2117
|
+
task.data.interactionId = '';
|
|
2118
|
+
|
|
2119
|
+
await expect(task.exitConference()).rejects.toThrow('Error while performing exitConference');
|
|
2120
|
+
expect(contactMock.exitConference).not.toHaveBeenCalled();
|
|
2121
|
+
});
|
|
2122
|
+
|
|
2123
|
+
it('should handle and rethrow contact method errors', async () => {
|
|
2124
|
+
const mockError = new Error('Conference end failed');
|
|
2125
|
+
contactMock.exitConference.mockRejectedValue(mockError);
|
|
2126
|
+
generateTaskErrorObjectSpy.mockReturnValue(mockError);
|
|
2127
|
+
|
|
2128
|
+
await expect(task.exitConference()).rejects.toThrow('Conference end failed');
|
|
2129
|
+
expect(LoggerProxy.error).toHaveBeenCalledWith('Failed to exit consult conference', {
|
|
2130
|
+
module: TASK_FILE,
|
|
2131
|
+
method: 'exitConference',
|
|
2132
|
+
interactionId: taskId,
|
|
2133
|
+
});
|
|
2134
|
+
});
|
|
2135
|
+
});
|
|
2136
|
+
|
|
2137
|
+
describe('transferConference', () => {
|
|
2138
|
+
it('should successfully transfer conference', async () => {
|
|
2139
|
+
const mockResponse = {
|
|
2140
|
+
trackingId: 'test-tracking-id-transfer',
|
|
2141
|
+
interactionId: taskId,
|
|
2142
|
+
};
|
|
2143
|
+
contactMock.conferenceTransfer.mockResolvedValue(mockResponse);
|
|
2144
|
+
|
|
2145
|
+
const result = await task.transferConference();
|
|
2146
|
+
|
|
2147
|
+
expect(contactMock.conferenceTransfer).toHaveBeenCalledWith({
|
|
2148
|
+
interactionId: taskId,
|
|
2149
|
+
});
|
|
2150
|
+
expect(result).toEqual(mockResponse);
|
|
2151
|
+
expect(LoggerProxy.info).toHaveBeenCalledWith('Transferring conference', {
|
|
2152
|
+
module: TASK_FILE,
|
|
2153
|
+
method: 'transferConference',
|
|
2154
|
+
interactionId: taskId,
|
|
2155
|
+
});
|
|
2156
|
+
expect(LoggerProxy.log).toHaveBeenCalledWith('Conference transferred successfully', {
|
|
2157
|
+
module: TASK_FILE,
|
|
2158
|
+
method: 'transferConference',
|
|
2159
|
+
interactionId: taskId,
|
|
2160
|
+
});
|
|
2161
|
+
});
|
|
2162
|
+
|
|
2163
|
+
it('should throw error for invalid interaction ID', async () => {
|
|
2164
|
+
task.data.interactionId = '';
|
|
2165
|
+
|
|
2166
|
+
await expect(task.transferConference()).rejects.toThrow('Error while performing transferConference');
|
|
2167
|
+
expect(contactMock.conferenceTransfer).not.toHaveBeenCalled();
|
|
2168
|
+
});
|
|
2169
|
+
|
|
2170
|
+
it('should handle and rethrow contact method errors', async () => {
|
|
2171
|
+
const mockError = new Error('Conference transfer failed');
|
|
2172
|
+
contactMock.conferenceTransfer.mockRejectedValue(mockError);
|
|
2173
|
+
generateTaskErrorObjectSpy.mockReturnValue(mockError);
|
|
2174
|
+
|
|
2175
|
+
await expect(task.transferConference()).rejects.toThrow('Conference transfer failed');
|
|
2176
|
+
expect(LoggerProxy.error).toHaveBeenCalledWith('Failed to transfer conference', {
|
|
2177
|
+
module: TASK_FILE,
|
|
2178
|
+
method: 'transferConference',
|
|
2179
|
+
interactionId: taskId,
|
|
2180
|
+
});
|
|
2181
|
+
});
|
|
2182
|
+
});
|
|
2183
|
+
});
|
|
1578
2184
|
});
|