@webex/contact-center 3.10.0-next.8 → 3.10.0-wxc-disconnect.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.
Files changed (46) hide show
  1. package/dist/cc.js +1 -12
  2. package/dist/cc.js.map +1 -1
  3. package/dist/index.js.map +1 -1
  4. package/dist/services/config/types.js +2 -2
  5. package/dist/services/config/types.js.map +1 -1
  6. package/dist/services/core/Utils.js +71 -90
  7. package/dist/services/core/Utils.js.map +1 -1
  8. package/dist/services/core/constants.js +1 -17
  9. package/dist/services/core/constants.js.map +1 -1
  10. package/dist/services/task/TaskManager.js +17 -89
  11. package/dist/services/task/TaskManager.js.map +1 -1
  12. package/dist/services/task/constants.js +1 -20
  13. package/dist/services/task/constants.js.map +1 -1
  14. package/dist/services/task/index.js +98 -123
  15. package/dist/services/task/index.js.map +1 -1
  16. package/dist/services/task/types.js +4 -2
  17. package/dist/services/task/types.js.map +1 -1
  18. package/dist/types/cc.d.ts +0 -6
  19. package/dist/types/index.d.ts +1 -1
  20. package/dist/types/services/config/types.d.ts +4 -4
  21. package/dist/types/services/core/Utils.d.ts +17 -32
  22. package/dist/types/services/core/constants.d.ts +0 -14
  23. package/dist/types/services/task/constants.d.ts +0 -17
  24. package/dist/types/services/task/index.d.ts +2 -41
  25. package/dist/types/services/task/types.d.ts +33 -133
  26. package/dist/webex.js +1 -1
  27. package/package.json +9 -9
  28. package/src/cc.ts +1 -12
  29. package/src/index.ts +0 -1
  30. package/src/services/config/types.ts +2 -2
  31. package/src/services/core/Utils.ts +85 -101
  32. package/src/services/core/constants.ts +0 -16
  33. package/src/services/task/TaskManager.ts +15 -112
  34. package/src/services/task/constants.ts +0 -19
  35. package/src/services/task/index.ts +82 -108
  36. package/src/services/task/types.ts +33 -142
  37. package/test/unit/spec/services/core/Utils.ts +31 -262
  38. package/test/unit/spec/services/task/TaskManager.ts +6 -620
  39. package/test/unit/spec/services/task/index.ts +79 -502
  40. package/umd/contact-center.min.js +2 -2
  41. package/umd/contact-center.min.js.map +1 -1
  42. package/dist/services/task/TaskUtils.js +0 -104
  43. package/dist/services/task/TaskUtils.js.map +0 -1
  44. package/dist/types/services/task/TaskUtils.d.ts +0 -42
  45. package/src/services/task/TaskUtils.ts +0 -113
  46. package/test/unit/spec/services/task/TaskUtils.ts +0 -131
@@ -39,8 +39,7 @@ describe('Task', () => {
39
39
  let loggerInfoSpy;
40
40
  let loggerLogSpy;
41
41
  let loggerErrorSpy;
42
- let calculateDestAgentIdSpy;
43
- let calculateDestTypeSpy;
42
+ let getDestinationAgentIdSpy;
44
43
 
45
44
  const taskId = '0ae913a4-c857-4705-8d49-76dd3dde75e4';
46
45
  const mockTrack = {} as MediaStreamTrack;
@@ -120,32 +119,6 @@ describe('Task', () => {
120
119
  interaction: {
121
120
  mediaType: 'telephony',
122
121
  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
- },
149
122
  media: {
150
123
  '58a45567-4e61-4f4b-a580-5bc86357bef0': {
151
124
  holdTimestamp: null,
@@ -172,18 +145,13 @@ describe('Task', () => {
172
145
  },
173
146
  };
174
147
 
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');
148
+ // Mock destination agent id resolution from participants
149
+ getDestinationAgentIdSpy = jest
150
+ .spyOn(Utils, 'getDestinationAgentId')
151
+ .mockReturnValue(taskDataMock.destAgentId);
180
152
 
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);
153
+ // Create an instance of Task
154
+ task = new Task(contactMock, webCallingService, taskDataMock);
187
155
 
188
156
  // Mock navigator.mediaDevices
189
157
  global.navigator.mediaDevices = {
@@ -249,7 +217,7 @@ describe('Task', () => {
249
217
  });
250
218
 
251
219
  describe('updateTaskData cases', () => {
252
- it('updates the task data by overwrite', async () => {
220
+ it('test updating the task data by overwrite', async () => {
253
221
  const newData = {
254
222
  type: CC_EVENTS.AGENT_CONTACT_ASSIGNED,
255
223
  agentId: '723a8ffb-a26e-496d-b14a-ff44fb83b64f',
@@ -298,12 +266,12 @@ describe('Task', () => {
298
266
  expect(task.data).toEqual(newData);
299
267
  });
300
268
 
301
- it('updates the task data by merging with key removal', async () => {
269
+ it('test updating the task data by merging', async () => {
302
270
  const newData = {
303
- // Purposefully omit other keys to test remove and merge behavior
271
+ // ...taskDataMock, // Purposefully omit this to test scenario when other keys isn't present
304
272
  isConsulting: true, // Add a new custom key to test persistence
305
273
  interaction: {
306
- // Purposefully omit other interaction keys to test removal
274
+ // ...taskDataMock.interaction, // Purposefully omit this to test scenario when a nested key isn't present
307
275
  media: {
308
276
  '58a45567-4e61-4f4b-a580-5bc86357bef0': {
309
277
  holdTimestamp: null,
@@ -330,12 +298,11 @@ describe('Task', () => {
330
298
  },
331
299
  };
332
300
 
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
335
301
  const expectedData: TaskData = {
336
- isConsulting: true, // New key is added
302
+ ...taskDataMock,
303
+ isConsulting: true,
337
304
  interaction: {
338
- // Only the media key from newData.interaction remains
305
+ ...taskDataMock.interaction,
339
306
  media: {
340
307
  '58a45567-4e61-4f4b-a580-5bc86357bef0': {
341
308
  holdTimestamp: null,
@@ -368,60 +335,6 @@ describe('Task', () => {
368
335
 
369
336
  expect(task.data).toEqual(expectedData);
370
337
  });
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
- });
425
338
  });
426
339
 
427
340
  it('should accept a task and answer call when using BROWSER login option', async () => {
@@ -657,40 +570,6 @@ describe('Task', () => {
657
570
  );
658
571
  });
659
572
 
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
-
694
573
  it('should handle errors in hold method', async () => {
695
574
  const error = {details: (global as any).makeFailure('Hold Failed')};
696
575
  contactMock.hold.mockImplementation(() => {
@@ -720,36 +599,6 @@ describe('Task', () => {
720
599
  );
721
600
  });
722
601
 
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
-
753
602
  it('should resume the task and return the expected response', async () => {
754
603
  const expectedResponse: TaskResponse = {data: {interactionId: taskId}} as AgentContact;
755
604
  contactMock.unHold.mockResolvedValue(expectedResponse);
@@ -774,29 +623,6 @@ describe('Task', () => {
774
623
  );
775
624
  });
776
625
 
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
-
800
626
  it('should handle errors in resume method', async () => {
801
627
  const error = {details: (global as any).makeFailure('Resume Failed')};
802
628
  contactMock.unHold.mockImplementation(() => {
@@ -828,36 +654,6 @@ describe('Task', () => {
828
654
  );
829
655
  });
830
656
 
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
-
861
657
  it('should initiate a consult call and return the expected response', async () => {
862
658
  const consultPayload = {
863
659
  to: '1234',
@@ -1025,16 +821,15 @@ describe('Task', () => {
1025
821
  );
1026
822
  });
1027
823
 
1028
- it('should send DIALNUMBER when calculateDestType returns dialNumber during consultTransfer', async () => {
824
+ it('should send DIALNUMBER when task destinationType is DN during consultTransfer', async () => {
1029
825
  const expectedResponse: TaskResponse = {data: {interactionId: taskId}} as AgentContact;
1030
826
  contactMock.consultTransfer.mockResolvedValue(expectedResponse);
1031
827
 
1032
- // Mock calculateDestType to return dialNumber
1033
- calculateDestTypeSpy.mockReturnValue(CONSULT_TRANSFER_DESTINATION_TYPE.DIALNUMBER);
828
+ // Ensure task data indicates DN scenario
829
+ task.data.destinationType = 'DN' as unknown as string;
1034
830
 
1035
831
  await task.consultTransfer();
1036
832
 
1037
- expect(calculateDestTypeSpy).toHaveBeenCalledWith(taskDataMock.interaction, taskDataMock.agentId);
1038
833
  expect(contactMock.consultTransfer).toHaveBeenCalledWith({
1039
834
  interactionId: taskId,
1040
835
  data: {
@@ -1044,16 +839,15 @@ describe('Task', () => {
1044
839
  });
1045
840
  });
1046
841
 
1047
- it('should send ENTRYPOINT when calculateDestType returns entryPoint during consultTransfer', async () => {
842
+ it('should send ENTRYPOINT when task destinationType is EPDN during consultTransfer', async () => {
1048
843
  const expectedResponse: TaskResponse = {data: {interactionId: taskId}} as AgentContact;
1049
844
  contactMock.consultTransfer.mockResolvedValue(expectedResponse);
1050
845
 
1051
- // Mock calculateDestType to return entryPoint
1052
- calculateDestTypeSpy.mockReturnValue(CONSULT_TRANSFER_DESTINATION_TYPE.ENTRYPOINT);
846
+ // Ensure task data indicates EP/EPDN scenario
847
+ task.data.destinationType = 'EPDN' as unknown as string;
1053
848
 
1054
849
  await task.consultTransfer();
1055
850
 
1056
- expect(calculateDestTypeSpy).toHaveBeenCalledWith(taskDataMock.interaction, taskDataMock.agentId);
1057
851
  expect(contactMock.consultTransfer).toHaveBeenCalledWith({
1058
852
  interactionId: taskId,
1059
853
  data: {
@@ -1063,16 +857,15 @@ describe('Task', () => {
1063
857
  });
1064
858
  });
1065
859
 
1066
- it('should use AGENT when calculateDestType returns agent during consultTransfer', async () => {
860
+ it('should keep AGENT when task destinationType is neither DN nor EPDN/ENTRYPOINT', async () => {
1067
861
  const expectedResponse: TaskResponse = {data: {interactionId: taskId}} as AgentContact;
1068
862
  contactMock.consultTransfer.mockResolvedValue(expectedResponse);
1069
863
 
1070
- // Mock calculateDestType to return agent (default behavior)
1071
- calculateDestTypeSpy.mockReturnValue(CONSULT_TRANSFER_DESTINATION_TYPE.AGENT);
864
+ // Ensure task data indicates non-DN and non-EP/EPDN scenario
865
+ task.data.destinationType = 'SOMETHING_ELSE' as unknown as string;
1072
866
 
1073
867
  await task.consultTransfer();
1074
868
 
1075
- expect(calculateDestTypeSpy).toHaveBeenCalledWith(taskDataMock.interaction, taskDataMock.agentId);
1076
869
  expect(contactMock.consultTransfer).toHaveBeenCalledWith({
1077
870
  interactionId: taskId,
1078
871
  data: {
@@ -1109,11 +902,7 @@ describe('Task', () => {
1109
902
  const taskWithoutDestAgentId = new Task(contactMock, webCallingService, {
1110
903
  ...taskDataMock,
1111
904
  destAgentId: undefined,
1112
- }, {
1113
- wrapUpProps: { wrapUpReasonList: [] },
1114
- autoWrapEnabled: false,
1115
- autoWrapAfterSeconds: 0
1116
- }, taskDataMock.agentId);
905
+ });
1117
906
 
1118
907
  const queueConsultTransferPayload: ConsultTransferPayLoad = {
1119
908
  to: 'some-queue-id',
@@ -1121,123 +910,61 @@ describe('Task', () => {
1121
910
  };
1122
911
 
1123
912
  // For this negative case, ensure computed destination is empty
1124
- calculateDestAgentIdSpy.mockReturnValueOnce('');
913
+ getDestinationAgentIdSpy.mockReturnValueOnce('');
1125
914
 
1126
915
  await expect(
1127
916
  taskWithoutDestAgentId.consultTransfer(queueConsultTransferPayload)
1128
- ).rejects.toThrow('No agent has accepted this queue consult yet');
917
+ ).rejects.toThrow('Error while performing consultTransfer');
1129
918
  });
1130
919
 
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);
920
+ it('should handle errors in consult transfer', async () => {
921
+ const consultPayload = {
922
+ destination: '1234',
923
+ destinationType: DESTINATION_TYPE.AGENT,
924
+ };
925
+ const expectedResponse: TaskResponse = {data: {interactionId: taskId}} as AgentContact;
926
+ contactMock.consult.mockResolvedValue(expectedResponse);
1140
927
 
1141
- const result = await task.consultTransfer();
928
+ const response = await task.consult(consultPayload);
1142
929
 
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
- });
930
+ expect(contactMock.consult).toHaveBeenCalledWith({interactionId: taskId, data: consultPayload});
931
+ expect(response).toEqual(expectedResponse);
1193
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
+ const error = {details: (global as any).makeFailure('Consult Transfer Failed')};
934
+ contactMock.consultTransfer.mockImplementation(() => {
935
+ throw error;
1200
936
  });
1201
937
 
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);
1206
-
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();
938
+ const consultTransferPayload: ConsultTransferPayLoad = {
939
+ to: '1234',
940
+ destinationType: CONSULT_TRANSFER_DESTINATION_TYPE.AGENT,
941
+ };
1231
942
 
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
- });
943
+ await expect(task.consultTransfer(consultTransferPayload)).rejects.toThrow(
944
+ error.details.data.reason
945
+ );
946
+ expect(generateTaskErrorObjectSpy).toHaveBeenCalledWith(error, 'consultTransfer', TASK_FILE);
947
+ const expectedTaskErrorFieldsConsultTransfer = {
948
+ trackingId: error.details.trackingId,
949
+ errorMessage: error.details.data.reason,
950
+ errorType: '',
951
+ errorData: '',
952
+ reasonCode: 0,
953
+ };
954
+ expect(mockMetricsManager.trackEvent).toHaveBeenNthCalledWith(
955
+ 2,
956
+ METRIC_EVENT_NAMES.TASK_TRANSFER_FAILED,
957
+ {
958
+ taskId: taskDataMock.interactionId,
959
+ destination: taskDataMock.destAgentId,
960
+ destinationType: CONSULT_TRANSFER_DESTINATION_TYPE.AGENT,
961
+ isConsultTransfer: true,
962
+ error: error.toString(),
963
+ ...expectedTaskErrorFieldsConsultTransfer,
964
+ ...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details),
965
+ },
966
+ ['operational', 'behavioral', 'business']
967
+ );
1241
968
  });
1242
969
 
1243
970
  it('should do vteamTransfer if destinationType is queue and return the expected response', async () => {
@@ -1860,6 +1587,12 @@ describe('Task', () => {
1860
1587
  conferenceTransfer: jest.fn(),
1861
1588
  };
1862
1589
 
1590
+ // Re-setup the getDestinationAgentId spy for conference methods
1591
+ getDestinationAgentIdSpy = jest
1592
+ .spyOn(Utils, 'getDestinationAgentId')
1593
+ .mockReturnValue(taskDataMock.destAgentId);
1594
+
1595
+
1863
1596
  task = new Task(contactMock, webCallingService, taskDataMock, {
1864
1597
  wrapUpProps: { wrapUpReasonList: [] },
1865
1598
  autoWrapEnabled: false,
@@ -1883,7 +1616,7 @@ describe('Task', () => {
1883
1616
  interactionId: taskId,
1884
1617
  data: {
1885
1618
  agentId: taskDataMock.agentId, // From task data agent ID
1886
- to: taskDataMock.destAgentId, // From calculateDestAgentId() using task participants
1619
+ to: taskDataMock.destAgentId, // From getDestinationAgentId() using task participants
1887
1620
  destinationType: 'agent', // From consultation data
1888
1621
  },
1889
1622
  });
@@ -1925,166 +1658,6 @@ describe('Task', () => {
1925
1658
  interactionId: taskId,
1926
1659
  });
1927
1660
  });
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
1661
  });
2089
1662
 
2090
1663
  describe('exitConference', () => {
@@ -2134,6 +1707,9 @@ describe('Task', () => {
2134
1707
  });
2135
1708
  });
2136
1709
 
1710
+ // TODO: Uncomment this test section in future PR for Multi-Party Conference support (>3 participants)
1711
+ // Conference transfer tests will be uncommented when implementing enhanced multi-party conference functionality
1712
+ /*
2137
1713
  describe('transferConference', () => {
2138
1714
  it('should successfully transfer conference', async () => {
2139
1715
  const mockResponse = {
@@ -2180,5 +1756,6 @@ describe('Task', () => {
2180
1756
  });
2181
1757
  });
2182
1758
  });
1759
+ */
2183
1760
  });
2184
1761
  });