@webex/contact-center 3.9.0 → 3.10.0-next.2

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 (111) hide show
  1. package/dist/cc.js +196 -47
  2. package/dist/cc.js.map +1 -1
  3. package/dist/constants.js +1 -0
  4. package/dist/constants.js.map +1 -1
  5. package/dist/index.js +9 -0
  6. package/dist/index.js.map +1 -1
  7. package/dist/logger-proxy.js +24 -1
  8. package/dist/logger-proxy.js.map +1 -1
  9. package/dist/metrics/MetricsManager.js +1 -1
  10. package/dist/metrics/MetricsManager.js.map +1 -1
  11. package/dist/metrics/behavioral-events.js +89 -0
  12. package/dist/metrics/behavioral-events.js.map +1 -1
  13. package/dist/metrics/constants.js +32 -2
  14. package/dist/metrics/constants.js.map +1 -1
  15. package/dist/services/AddressBook.js +271 -0
  16. package/dist/services/AddressBook.js.map +1 -0
  17. package/dist/services/EntryPoint.js +227 -0
  18. package/dist/services/EntryPoint.js.map +1 -0
  19. package/dist/services/Queue.js +261 -0
  20. package/dist/services/Queue.js.map +1 -0
  21. package/dist/services/config/constants.js +36 -2
  22. package/dist/services/config/constants.js.map +1 -1
  23. package/dist/services/config/index.js +29 -21
  24. package/dist/services/config/index.js.map +1 -1
  25. package/dist/services/config/types.js +33 -1
  26. package/dist/services/config/types.js.map +1 -1
  27. package/dist/services/core/GlobalTypes.js.map +1 -1
  28. package/dist/services/core/Utils.js +162 -2
  29. package/dist/services/core/Utils.js.map +1 -1
  30. package/dist/services/core/aqm-reqs.js +0 -4
  31. package/dist/services/core/aqm-reqs.js.map +1 -1
  32. package/dist/services/core/websocket/WebSocketManager.js +0 -4
  33. package/dist/services/core/websocket/WebSocketManager.js.map +1 -1
  34. package/dist/services/task/TaskManager.js +114 -3
  35. package/dist/services/task/TaskManager.js.map +1 -1
  36. package/dist/services/task/TaskUtils.js +76 -0
  37. package/dist/services/task/TaskUtils.js.map +1 -0
  38. package/dist/services/task/constants.js +26 -1
  39. package/dist/services/task/constants.js.map +1 -1
  40. package/dist/services/task/contact.js +86 -0
  41. package/dist/services/task/contact.js.map +1 -1
  42. package/dist/services/task/index.js +418 -87
  43. package/dist/services/task/index.js.map +1 -1
  44. package/dist/services/task/types.js +14 -0
  45. package/dist/services/task/types.js.map +1 -1
  46. package/dist/types/cc.d.ts +115 -35
  47. package/dist/types/constants.d.ts +1 -0
  48. package/dist/types/index.d.ts +3 -2
  49. package/dist/types/metrics/constants.d.ts +25 -1
  50. package/dist/types/services/AddressBook.d.ts +74 -0
  51. package/dist/types/services/EntryPoint.d.ts +67 -0
  52. package/dist/types/services/Queue.d.ts +76 -0
  53. package/dist/types/services/config/constants.d.ts +35 -1
  54. package/dist/types/services/config/index.d.ts +6 -9
  55. package/dist/types/services/config/types.d.ts +79 -58
  56. package/dist/types/services/core/GlobalTypes.d.ts +25 -0
  57. package/dist/types/services/core/Utils.d.ts +40 -1
  58. package/dist/types/services/task/TaskUtils.d.ts +28 -0
  59. package/dist/types/services/task/constants.d.ts +23 -0
  60. package/dist/types/services/task/contact.d.ts +10 -0
  61. package/dist/types/services/task/index.d.ts +85 -4
  62. package/dist/types/services/task/types.d.ts +233 -21
  63. package/dist/types/types.d.ts +162 -0
  64. package/dist/types/utils/PageCache.d.ts +173 -0
  65. package/dist/types.js +17 -0
  66. package/dist/types.js.map +1 -1
  67. package/dist/utils/PageCache.js +192 -0
  68. package/dist/utils/PageCache.js.map +1 -0
  69. package/dist/webex.js +1 -1
  70. package/package.json +10 -9
  71. package/src/cc.ts +221 -52
  72. package/src/constants.ts +1 -0
  73. package/src/index.ts +16 -2
  74. package/src/logger-proxy.ts +24 -1
  75. package/src/metrics/MetricsManager.ts +1 -1
  76. package/src/metrics/behavioral-events.ts +94 -0
  77. package/src/metrics/constants.ts +37 -1
  78. package/src/services/AddressBook.ts +291 -0
  79. package/src/services/EntryPoint.ts +241 -0
  80. package/src/services/Queue.ts +277 -0
  81. package/src/services/config/constants.ts +42 -2
  82. package/src/services/config/index.ts +30 -30
  83. package/src/services/config/types.ts +59 -58
  84. package/src/services/core/GlobalTypes.ts +27 -0
  85. package/src/services/core/Utils.ts +199 -1
  86. package/src/services/core/aqm-reqs.ts +0 -5
  87. package/src/services/core/websocket/WebSocketManager.ts +0 -4
  88. package/src/services/task/TaskManager.ts +123 -5
  89. package/src/services/task/TaskUtils.ts +81 -0
  90. package/src/services/task/constants.ts +25 -0
  91. package/src/services/task/contact.ts +80 -0
  92. package/src/services/task/index.ts +510 -71
  93. package/src/services/task/types.ts +251 -20
  94. package/src/types.ts +180 -0
  95. package/src/utils/PageCache.ts +252 -0
  96. package/test/unit/spec/cc.ts +282 -85
  97. package/test/unit/spec/metrics/MetricsManager.ts +0 -1
  98. package/test/unit/spec/metrics/behavioral-events.ts +42 -0
  99. package/test/unit/spec/services/AddressBook.ts +332 -0
  100. package/test/unit/spec/services/EntryPoint.ts +259 -0
  101. package/test/unit/spec/services/Queue.ts +323 -0
  102. package/test/unit/spec/services/config/index.ts +279 -65
  103. package/test/unit/spec/services/core/Utils.ts +50 -0
  104. package/test/unit/spec/services/core/aqm-reqs.ts +1 -3
  105. package/test/unit/spec/services/core/websocket/WebSocketManager.ts +0 -4
  106. package/test/unit/spec/services/task/TaskManager.ts +390 -1
  107. package/test/unit/spec/services/task/TaskUtils.ts +131 -0
  108. package/test/unit/spec/services/task/contact.ts +31 -1
  109. package/test/unit/spec/services/task/index.ts +585 -130
  110. package/umd/contact-center.min.js +2 -2
  111. package/umd/contact-center.min.js.map +1 -1
@@ -33,12 +33,13 @@ describe('Task', () => {
33
33
  let mockMetricsManager;
34
34
  let taskDataMock;
35
35
  let webCallingService;
36
- let getErrorDetailsSpy;
36
+ let generateTaskErrorObjectSpy;
37
37
  let mockWebexRequest;
38
38
  let webex: WebexSDK;
39
39
  let loggerInfoSpy;
40
40
  let loggerLogSpy;
41
41
  let loggerErrorSpy;
42
+ let getDestinationAgentIdSpy;
42
43
 
43
44
  const taskId = '0ae913a4-c857-4705-8d49-76dd3dde75e4';
44
45
  const mockTrack = {} as MediaStreamTrack;
@@ -74,6 +75,9 @@ describe('Task', () => {
74
75
  wrapup: jest.fn().mockResolvedValue({}),
75
76
  pauseRecording: jest.fn().mockResolvedValue({}),
76
77
  resumeRecording: jest.fn().mockResolvedValue({}),
78
+ consultConference: jest.fn().mockResolvedValue({}),
79
+ exitConference: jest.fn().mockResolvedValue({}),
80
+ conferenceTransfer: jest.fn().mockResolvedValue({}),
77
81
  };
78
82
 
79
83
  mockMetricsManager = {
@@ -141,6 +145,11 @@ describe('Task', () => {
141
145
  },
142
146
  };
143
147
 
148
+ // Mock destination agent id resolution from participants
149
+ getDestinationAgentIdSpy = jest
150
+ .spyOn(Utils, 'getDestinationAgentId')
151
+ .mockReturnValue(taskDataMock.destAgentId);
152
+
144
153
  // Create an instance of Task
145
154
  task = new Task(contactMock, webCallingService, taskDataMock);
146
155
 
@@ -158,11 +167,42 @@ describe('Task', () => {
158
167
  return mockStream;
159
168
  });
160
169
 
161
- getErrorDetailsSpy = jest.spyOn(Utils, 'getErrorDetails');
170
+ generateTaskErrorObjectSpy = jest.spyOn(Utils, 'generateTaskErrorObject');
171
+ generateTaskErrorObjectSpy.mockImplementation((error: any, methodName: string) => {
172
+ const trackingId = error?.details?.trackingId;
173
+ const msg = error?.details?.msg;
174
+ const legacyReason = error?.details?.data?.reason;
175
+ const errorMessage = msg?.errorMessage || legacyReason || `Error while performing ${methodName}`;
176
+ const errorType = msg?.errorType || '';
177
+ const errorData = msg?.errorData || '';
178
+ const reasonCode = msg?.reasonCode || 0;
179
+ const reason = legacyReason || (errorType ? `${errorType}: ${errorMessage}` : errorMessage);
180
+ const err: any = new Error(reason);
181
+ err.data = {
182
+ trackingId,
183
+ message: errorMessage,
184
+ errorType,
185
+ errorData,
186
+ reasonCode,
187
+ };
188
+ return err;
189
+ });
190
+
191
+ (global as any).makeFailure = (reason: string, trackingId = '1234', orgId = 'org1') => ({
192
+ type: 'failure_event',
193
+ orgId,
194
+ trackingId,
195
+ data: {
196
+ agentId: 'agent1',
197
+ reason,
198
+ reasonCode: 0,
199
+ },
200
+ });
162
201
  });
163
202
 
164
203
  afterEach(() => {
165
204
  jest.clearAllMocks();
205
+ jest.restoreAllMocks();
166
206
  });
167
207
 
168
208
  it('test the on spy', async () => {
@@ -177,7 +217,7 @@ describe('Task', () => {
177
217
  });
178
218
 
179
219
  describe('updateTaskData cases', () => {
180
- it('test updating the task data by overwrite', async () => {
220
+ it('updates the task data by overwrite', async () => {
181
221
  const newData = {
182
222
  type: CC_EVENTS.AGENT_CONTACT_ASSIGNED,
183
223
  agentId: '723a8ffb-a26e-496d-b14a-ff44fb83b64f',
@@ -226,12 +266,12 @@ describe('Task', () => {
226
266
  expect(task.data).toEqual(newData);
227
267
  });
228
268
 
229
- it('test updating the task data by merging', async () => {
269
+ it('updates the task data by merging with key removal', async () => {
230
270
  const newData = {
231
- // ...taskDataMock, // Purposefully omit this to test scenario when other keys isn't present
271
+ // Purposefully omit other keys to test remove and merge behavior
232
272
  isConsulting: true, // Add a new custom key to test persistence
233
273
  interaction: {
234
- // ...taskDataMock.interaction, // Purposefully omit this to test scenario when a nested key isn't present
274
+ // Purposefully omit other interaction keys to test removal
235
275
  media: {
236
276
  '58a45567-4e61-4f4b-a580-5bc86357bef0': {
237
277
  holdTimestamp: null,
@@ -258,11 +298,12 @@ describe('Task', () => {
258
298
  },
259
299
  };
260
300
 
301
+ // The reconcileData method removes keys from oldData that are not in newData
302
+ // This means only keys present in newData will remain in the final result
261
303
  const expectedData: TaskData = {
262
- ...taskDataMock,
263
- isConsulting: true,
304
+ isConsulting: true, // New key is added
264
305
  interaction: {
265
- ...taskDataMock.interaction,
306
+ // Only the media key from newData.interaction remains
266
307
  media: {
267
308
  '58a45567-4e61-4f4b-a580-5bc86357bef0': {
268
309
  holdTimestamp: null,
@@ -295,6 +336,60 @@ describe('Task', () => {
295
336
 
296
337
  expect(task.data).toEqual(expectedData);
297
338
  });
339
+
340
+ it('updates the task data by merging and preserving existing keys', async () => {
341
+ const newData = {
342
+ ...taskDataMock, // Include all existing keys to test merge without removal
343
+ isConsulting: true, // Add a new custom key
344
+ interaction: {
345
+ ...taskDataMock.interaction, // Include existing interaction data
346
+ media: {
347
+ ...taskDataMock.interaction.media, // Include existing media
348
+ '58a45567-4e61-4f4b-a580-5bc86357bef0': {
349
+ holdTimestamp: null,
350
+ isHold: true,
351
+ mType: 'consult',
352
+ mediaMgr: 'callmm',
353
+ mediaResourceId: '58a45567-4e61-4f4b-a580-5bc86357bef0',
354
+ mediaType: 'telephony',
355
+ participants: [
356
+ 'f520d6b5-28ad-4f2f-b83e-781bb64af617',
357
+ '723a8ffb-a26e-496d-b14a-ff44fb83b64f',
358
+ ],
359
+ },
360
+ },
361
+ },
362
+ };
363
+
364
+ const expectedData: TaskData = {
365
+ ...taskDataMock,
366
+ isConsulting: true,
367
+ interaction: {
368
+ ...taskDataMock.interaction,
369
+ media: {
370
+ ...taskDataMock.interaction.media,
371
+ '58a45567-4e61-4f4b-a580-5bc86357bef0': {
372
+ holdTimestamp: null,
373
+ isHold: true,
374
+ mType: 'consult',
375
+ mediaMgr: 'callmm',
376
+ mediaResourceId: '58a45567-4e61-4f4b-a580-5bc86357bef0',
377
+ mediaType: 'telephony',
378
+ participants: [
379
+ 'f520d6b5-28ad-4f2f-b83e-781bb64af617',
380
+ '723a8ffb-a26e-496d-b14a-ff44fb83b64f',
381
+ ],
382
+ },
383
+ },
384
+ },
385
+ };
386
+
387
+ expect(task.data).toEqual(taskDataMock);
388
+ const shouldOverwrite = false;
389
+ task.updateTaskData(newData, shouldOverwrite);
390
+
391
+ expect(task.data).toEqual(expectedData);
392
+ });
298
393
  });
299
394
 
300
395
  it('should accept a task and answer call when using BROWSER login option', async () => {
@@ -413,27 +508,28 @@ describe('Task', () => {
413
508
  });
414
509
 
415
510
  it('should handle errors in accept method', async () => {
416
- const error = {
417
- details: {
418
- trackingId: '1234',
419
- data: {
420
- reason: 'Accept Failed',
421
- },
422
- },
423
- };
511
+ const error = {details: (global as any).makeFailure('Accept Failed')};
424
512
 
425
513
  jest.spyOn(webCallingService, 'answerCall').mockImplementation(() => {
426
514
  throw error;
427
515
  });
428
516
 
429
517
  await expect(task.accept()).rejects.toThrow(new Error(error.details.data.reason));
430
- expect(getErrorDetailsSpy).toHaveBeenCalledWith(error, 'accept', TASK_FILE);
518
+ expect(generateTaskErrorObjectSpy).toHaveBeenCalledWith(error, 'accept', TASK_FILE);
519
+ const expectedTaskErrorFields = {
520
+ trackingId: error.details.trackingId,
521
+ errorMessage: error.details.data.reason,
522
+ errorType: '',
523
+ errorData: '',
524
+ reasonCode: 0,
525
+ };
431
526
  expect(mockMetricsManager.trackEvent).toHaveBeenNthCalledWith(
432
527
  1,
433
528
  METRIC_EVENT_NAMES.TASK_ACCEPT_FAILED,
434
529
  {
435
530
  taskId: taskDataMock.interactionId,
436
531
  error: error.toString(),
532
+ ...expectedTaskErrorFields,
437
533
  ...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details),
438
534
  },
439
535
  ['operational', 'behavioral', 'business']
@@ -469,26 +565,27 @@ describe('Task', () => {
469
565
  });
470
566
 
471
567
  it('should handle errors in decline method', async () => {
472
- const error = {
473
- details: {
474
- trackingId: '1234',
475
- data: {
476
- reason: 'Decline Failed',
477
- },
478
- },
479
- };
568
+ const error = {details: (global as any).makeFailure('Decline Failed')};
480
569
 
481
570
  jest.spyOn(webCallingService, 'declineCall').mockImplementation(() => {
482
571
  throw error;
483
572
  });
484
573
  await expect(task.decline()).rejects.toThrow(new Error(error.details.data.reason));
485
- expect(getErrorDetailsSpy).toHaveBeenCalledWith(error, 'decline', TASK_FILE);
574
+ expect(generateTaskErrorObjectSpy).toHaveBeenCalledWith(error, 'decline', TASK_FILE);
575
+ const expectedTaskErrorFieldsDecline = {
576
+ trackingId: error.details.trackingId,
577
+ errorMessage: error.details.data.reason,
578
+ errorType: '',
579
+ errorData: '',
580
+ reasonCode: 0,
581
+ };
486
582
  expect(mockMetricsManager.trackEvent).toHaveBeenNthCalledWith(
487
583
  1,
488
584
  METRIC_EVENT_NAMES.TASK_DECLINE_FAILED,
489
585
  {
490
586
  taskId: taskDataMock.interactionId,
491
587
  error: error.toString(),
588
+ ...expectedTaskErrorFieldsDecline,
492
589
  ...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details),
493
590
  },
494
591
  ['operational', 'behavioral']
@@ -528,21 +625,55 @@ describe('Task', () => {
528
625
  );
529
626
  });
530
627
 
531
- it('should handle errors in hold method', async () => {
532
- const error = {
533
- details: {
534
- trackingId: '1234',
535
- data: {
536
- reason: 'Hold Failed',
537
- },
628
+ it('should hold the task with custom mediaResourceId and return the expected response', async () => {
629
+ const customMediaResourceId = 'custom-media-resource-id-123';
630
+ const expectedResponse: TaskResponse = {data: {interactionId: taskId}} as AgentContact;
631
+ contactMock.hold.mockResolvedValue(expectedResponse);
632
+
633
+ const response = await task.hold(customMediaResourceId);
634
+
635
+ expect(contactMock.hold).toHaveBeenCalledWith({
636
+ interactionId: taskId,
637
+ data: {mediaResourceId: customMediaResourceId},
638
+ });
639
+ expect(response).toEqual(expectedResponse);
640
+ expect(loggerInfoSpy).toHaveBeenCalledWith(`Holding task`, {
641
+ module: TASK_FILE,
642
+ method: 'hold',
643
+ interactionId: task.data.interactionId,
644
+ });
645
+ expect(loggerLogSpy).toHaveBeenCalledWith(`Task placed on hold successfully`, {
646
+ module: TASK_FILE,
647
+ method: 'hold',
648
+ interactionId: task.data.interactionId,
649
+ });
650
+ expect(mockMetricsManager.trackEvent).toHaveBeenNthCalledWith(
651
+ 1,
652
+ METRIC_EVENT_NAMES.TASK_HOLD_SUCCESS,
653
+ {
654
+ ...MetricsManager.getCommonTrackingFieldForAQMResponse(expectedResponse),
655
+ taskId: taskDataMock.interactionId,
656
+ mediaResourceId: customMediaResourceId,
538
657
  },
539
- };
658
+ ['operational', 'behavioral']
659
+ );
660
+ });
661
+
662
+ it('should handle errors in hold method', async () => {
663
+ const error = {details: (global as any).makeFailure('Hold Failed')};
540
664
  contactMock.hold.mockImplementation(() => {
541
665
  throw error;
542
666
  });
543
667
 
544
668
  await expect(task.hold()).rejects.toThrow(error.details.data.reason);
545
- expect(getErrorDetailsSpy).toHaveBeenCalledWith(error, 'hold', TASK_FILE);
669
+ expect(generateTaskErrorObjectSpy).toHaveBeenCalledWith(error, 'hold', TASK_FILE);
670
+ const expectedTaskErrorFieldsHold = {
671
+ trackingId: error.details.trackingId,
672
+ errorMessage: error.details.data.reason,
673
+ errorType: '',
674
+ errorData: '',
675
+ reasonCode: 0,
676
+ };
546
677
  expect(mockMetricsManager.trackEvent).toHaveBeenNthCalledWith(
547
678
  1,
548
679
  METRIC_EVENT_NAMES.TASK_HOLD_FAILED,
@@ -550,6 +681,37 @@ describe('Task', () => {
550
681
  taskId: taskDataMock.interactionId,
551
682
  mediaResourceId: taskDataMock.mediaResourceId,
552
683
  error: error.toString(),
684
+ ...expectedTaskErrorFieldsHold,
685
+ ...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details),
686
+ },
687
+ ['operational', 'behavioral']
688
+ );
689
+ });
690
+
691
+ it('should handle errors in hold method with custom mediaResourceId', async () => {
692
+ const customMediaResourceId = 'custom-media-resource-id-456';
693
+ const error = {details: (global as any).makeFailure('Hold Failed with custom mediaResourceId')};
694
+ contactMock.hold.mockImplementation(() => {
695
+ throw error;
696
+ });
697
+
698
+ await expect(task.hold(customMediaResourceId)).rejects.toThrow(error.details.data.reason);
699
+ expect(generateTaskErrorObjectSpy).toHaveBeenCalledWith(error, 'hold', TASK_FILE);
700
+ const expectedTaskErrorFieldsHold = {
701
+ trackingId: error.details.trackingId,
702
+ errorMessage: error.details.data.reason,
703
+ errorType: '',
704
+ errorData: '',
705
+ reasonCode: 0,
706
+ };
707
+ expect(mockMetricsManager.trackEvent).toHaveBeenNthCalledWith(
708
+ 1,
709
+ METRIC_EVENT_NAMES.TASK_HOLD_FAILED,
710
+ {
711
+ taskId: taskDataMock.interactionId,
712
+ mediaResourceId: customMediaResourceId,
713
+ error: error.toString(),
714
+ ...expectedTaskErrorFieldsHold,
553
715
  ...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details),
554
716
  },
555
717
  ['operational', 'behavioral']
@@ -580,21 +742,44 @@ describe('Task', () => {
580
742
  );
581
743
  });
582
744
 
583
- it('should handle errors in resume method', async () => {
584
- const error = {
585
- details: {
586
- trackingId: '1234',
587
- data: {
588
- reason: 'Resume Failed',
589
- },
745
+ it('should resume the task with custom mediaResourceId and return the expected response', async () => {
746
+ const customMediaResourceId = 'custom-media-resource-id-789';
747
+ const expectedResponse: TaskResponse = {data: {interactionId: taskId}} as AgentContact;
748
+ contactMock.unHold.mockResolvedValue(expectedResponse);
749
+ const response = await task.resume(customMediaResourceId);
750
+ expect(contactMock.unHold).toHaveBeenCalledWith({
751
+ interactionId: taskId,
752
+ data: {mediaResourceId: customMediaResourceId},
753
+ });
754
+ expect(response).toEqual(expectedResponse);
755
+ expect(mockMetricsManager.trackEvent).toHaveBeenNthCalledWith(
756
+ 1,
757
+ METRIC_EVENT_NAMES.TASK_RESUME_SUCCESS,
758
+ {
759
+ taskId: taskDataMock.interactionId,
760
+ mainInteractionId: taskDataMock.interaction.mainInteractionId,
761
+ mediaResourceId: customMediaResourceId,
762
+ ...MetricsManager.getCommonTrackingFieldForAQMResponse(expectedResponse),
590
763
  },
591
- };
764
+ ['operational', 'behavioral']
765
+ );
766
+ });
767
+
768
+ it('should handle errors in resume method', async () => {
769
+ const error = {details: (global as any).makeFailure('Resume Failed')};
592
770
  contactMock.unHold.mockImplementation(() => {
593
771
  throw error;
594
772
  });
595
773
 
596
774
  await expect(task.resume()).rejects.toThrow(error.details.data.reason);
597
- expect(getErrorDetailsSpy).toHaveBeenCalledWith(error, 'resume', TASK_FILE);
775
+ expect(generateTaskErrorObjectSpy).toHaveBeenCalledWith(error, 'resume', TASK_FILE);
776
+ const expectedTaskErrorFieldsResume = {
777
+ trackingId: error.details.trackingId,
778
+ errorMessage: error.details.data.reason,
779
+ errorType: '',
780
+ errorData: '',
781
+ reasonCode: 0,
782
+ };
598
783
  expect(mockMetricsManager.trackEvent).toHaveBeenNthCalledWith(
599
784
  1,
600
785
  METRIC_EVENT_NAMES.TASK_RESUME_FAILED,
@@ -604,6 +789,37 @@ describe('Task', () => {
604
789
  mediaResourceId:
605
790
  taskDataMock.interaction.media[taskDataMock.interaction.mainInteractionId]
606
791
  .mediaResourceId,
792
+ ...expectedTaskErrorFieldsResume,
793
+ ...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details),
794
+ },
795
+ ['operational', 'behavioral']
796
+ );
797
+ });
798
+
799
+ it('should handle errors in resume method with custom mediaResourceId', async () => {
800
+ const customMediaResourceId = 'custom-media-resource-id-999';
801
+ const error = {details: (global as any).makeFailure('Resume Failed with custom mediaResourceId')};
802
+ contactMock.unHold.mockImplementation(() => {
803
+ throw error;
804
+ });
805
+
806
+ await expect(task.resume(customMediaResourceId)).rejects.toThrow(error.details.data.reason);
807
+ expect(generateTaskErrorObjectSpy).toHaveBeenCalledWith(error, 'resume', TASK_FILE);
808
+ const expectedTaskErrorFieldsResume = {
809
+ trackingId: error.details.trackingId,
810
+ errorMessage: error.details.data.reason,
811
+ errorType: '',
812
+ errorData: '',
813
+ reasonCode: 0,
814
+ };
815
+ expect(mockMetricsManager.trackEvent).toHaveBeenNthCalledWith(
816
+ 1,
817
+ METRIC_EVENT_NAMES.TASK_RESUME_FAILED,
818
+ {
819
+ taskId: taskDataMock.interactionId,
820
+ mainInteractionId: taskDataMock.interaction.mainInteractionId,
821
+ mediaResourceId: customMediaResourceId,
822
+ ...expectedTaskErrorFieldsResume,
607
823
  ...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details),
608
824
  },
609
825
  ['operational', 'behavioral']
@@ -630,8 +846,8 @@ describe('Task', () => {
630
846
  expect(loggerLogSpy).toHaveBeenCalledWith(`Consult started successfully to ${consultPayload.to}`, {
631
847
  module: TASK_FILE,
632
848
  method: 'consult',
633
- trackingId: expectedResponse.trackingId,
634
849
  interactionId: task.data.interactionId,
850
+ trackingId: '1234',
635
851
  });
636
852
  expect(mockMetricsManager.trackEvent).toHaveBeenCalledWith(
637
853
  METRIC_EVENT_NAMES.TASK_CONSULT_START_SUCCESS,
@@ -646,14 +862,7 @@ describe('Task', () => {
646
862
  });
647
863
 
648
864
  it('should handle errors in consult method', async () => {
649
- const error = {
650
- details: {
651
- trackingId: '1234',
652
- data: {
653
- reason: 'Consult Failed',
654
- },
655
- },
656
- };
865
+ const error = {details: (global as any).makeFailure('Consult Failed')};
657
866
  contactMock.consult.mockImplementation(() => {
658
867
  throw error;
659
868
  });
@@ -664,12 +873,19 @@ describe('Task', () => {
664
873
  };
665
874
 
666
875
  await expect(task.consult(consultPayload)).rejects.toThrow(error.details.data.reason);
667
- expect(getErrorDetailsSpy).toHaveBeenCalledWith(error, 'consult', TASK_FILE);
876
+ expect(generateTaskErrorObjectSpy).toHaveBeenCalledWith(error, 'consult', TASK_FILE);
668
877
  expect(loggerInfoSpy).toHaveBeenCalledWith(`Starting consult`, {
669
878
  module: TASK_FILE,
670
879
  method: 'consult',
671
880
  interactionId: task.data.interactionId,
672
881
  });
882
+ const expectedTaskErrorFieldsConsult = {
883
+ trackingId: error.details.trackingId,
884
+ errorMessage: error.details.data.reason,
885
+ errorType: '',
886
+ errorData: '',
887
+ reasonCode: 0,
888
+ };
673
889
  expect(mockMetricsManager.trackEvent).toHaveBeenCalledWith(
674
890
  METRIC_EVENT_NAMES.TASK_CONSULT_START_FAILED,
675
891
  {
@@ -677,6 +893,7 @@ describe('Task', () => {
677
893
  destination: consultPayload.to,
678
894
  destinationType: consultPayload.destinationType,
679
895
  error: error.toString(),
896
+ ...expectedTaskErrorFieldsConsult,
680
897
  ...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details),
681
898
  },
682
899
  ['operational', 'behavioral', 'business']
@@ -710,14 +927,7 @@ describe('Task', () => {
710
927
  });
711
928
 
712
929
  it('should handle errors in endConsult method', async () => {
713
- const error = {
714
- details: {
715
- trackingId: '1234',
716
- data: {
717
- reason: 'End Consult Failed',
718
- },
719
- },
720
- };
930
+ const error = {details: (global as any).makeFailure('End Consult Failed')};
721
931
  contactMock.consultEnd.mockImplementation(() => {
722
932
  throw error;
723
933
  });
@@ -728,13 +938,21 @@ describe('Task', () => {
728
938
  };
729
939
 
730
940
  await expect(task.endConsult(consultEndPayload)).rejects.toThrow(error.details.data.reason);
731
- expect(getErrorDetailsSpy).toHaveBeenCalledWith(error, 'endConsult', TASK_FILE);
941
+ expect(generateTaskErrorObjectSpy).toHaveBeenCalledWith(error, 'endConsult', TASK_FILE);
942
+ const expectedTaskErrorFieldsEndConsult = {
943
+ trackingId: error.details.trackingId,
944
+ errorMessage: error.details.data.reason,
945
+ errorType: '',
946
+ errorData: '',
947
+ reasonCode: 0,
948
+ };
732
949
  expect(mockMetricsManager.trackEvent).toHaveBeenNthCalledWith(
733
950
  1,
734
951
  METRIC_EVENT_NAMES.TASK_CONSULT_END_FAILED,
735
952
  {
736
953
  taskId: taskDataMock.interactionId,
737
954
  error: error.toString(),
955
+ ...expectedTaskErrorFieldsEndConsult,
738
956
  ...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details),
739
957
  },
740
958
  ['operational', 'behavioral', 'business']
@@ -754,29 +972,81 @@ describe('Task', () => {
754
972
  expect(contactMock.consult).toHaveBeenCalledWith({interactionId: taskId, data: consultPayload});
755
973
  expect(response).toEqual(expectedResponse);
756
974
 
757
- const consultTransferPayload: ConsultTransferPayLoad = {
758
- to: '1234',
759
- destinationType: CONSULT_TRANSFER_DESTINATION_TYPE.AGENT,
760
- };
761
-
762
- const consultTransferResponse = await task.consultTransfer(consultTransferPayload);
975
+ const consultTransferResponse = await task.consultTransfer();
763
976
  expect(contactMock.consultTransfer).toHaveBeenCalledWith({
764
977
  interactionId: taskId,
765
- data: consultTransferPayload,
978
+ data: {
979
+ to: taskDataMock.destAgentId,
980
+ destinationType: CONSULT_TRANSFER_DESTINATION_TYPE.AGENT,
981
+ },
766
982
  });
767
983
  expect(mockMetricsManager.trackEvent).toHaveBeenNthCalledWith(
768
984
  2,
769
985
  METRIC_EVENT_NAMES.TASK_TRANSFER_SUCCESS,
770
986
  {
771
987
  taskId: taskDataMock.interactionId,
772
- destination: consultTransferPayload.to,
773
- destinationType: consultTransferPayload.destinationType,
988
+ destination: taskDataMock.destAgentId,
989
+ destinationType: CONSULT_TRANSFER_DESTINATION_TYPE.AGENT,
774
990
  isConsultTransfer: true,
775
991
  },
776
992
  ['operational', 'behavioral', 'business']
777
993
  );
778
994
  });
779
995
 
996
+ it('should send DIALNUMBER when task destinationType is DN during consultTransfer', async () => {
997
+ const expectedResponse: TaskResponse = {data: {interactionId: taskId}} as AgentContact;
998
+ contactMock.consultTransfer.mockResolvedValue(expectedResponse);
999
+
1000
+ // Ensure task data indicates DN scenario
1001
+ task.data.destinationType = 'DN' as unknown as string;
1002
+
1003
+ await task.consultTransfer();
1004
+
1005
+ expect(contactMock.consultTransfer).toHaveBeenCalledWith({
1006
+ interactionId: taskId,
1007
+ data: {
1008
+ to: taskDataMock.destAgentId,
1009
+ destinationType: CONSULT_TRANSFER_DESTINATION_TYPE.DIALNUMBER,
1010
+ },
1011
+ });
1012
+ });
1013
+
1014
+ it('should send ENTRYPOINT when task destinationType is EPDN during consultTransfer', async () => {
1015
+ const expectedResponse: TaskResponse = {data: {interactionId: taskId}} as AgentContact;
1016
+ contactMock.consultTransfer.mockResolvedValue(expectedResponse);
1017
+
1018
+ // Ensure task data indicates EP/EPDN scenario
1019
+ task.data.destinationType = 'EPDN' as unknown as string;
1020
+
1021
+ await task.consultTransfer();
1022
+
1023
+ expect(contactMock.consultTransfer).toHaveBeenCalledWith({
1024
+ interactionId: taskId,
1025
+ data: {
1026
+ to: taskDataMock.destAgentId,
1027
+ destinationType: CONSULT_TRANSFER_DESTINATION_TYPE.ENTRYPOINT,
1028
+ },
1029
+ });
1030
+ });
1031
+
1032
+ it('should keep AGENT when task destinationType is neither DN nor EPDN/ENTRYPOINT', async () => {
1033
+ const expectedResponse: TaskResponse = {data: {interactionId: taskId}} as AgentContact;
1034
+ contactMock.consultTransfer.mockResolvedValue(expectedResponse);
1035
+
1036
+ // Ensure task data indicates non-DN and non-EP/EPDN scenario
1037
+ task.data.destinationType = 'SOMETHING_ELSE' as unknown as string;
1038
+
1039
+ await task.consultTransfer();
1040
+
1041
+ expect(contactMock.consultTransfer).toHaveBeenCalledWith({
1042
+ interactionId: taskId,
1043
+ data: {
1044
+ to: taskDataMock.destAgentId,
1045
+ destinationType: CONSULT_TRANSFER_DESTINATION_TYPE.AGENT,
1046
+ },
1047
+ });
1048
+ });
1049
+
780
1050
  it('should do consult transfer to a queue by using the destAgentId from task data', async () => {
781
1051
  const expectedResponse: TaskResponse = {data: {interactionId: taskId}} as AgentContact;
782
1052
  contactMock.consultTransfer.mockResolvedValue(expectedResponse);
@@ -811,6 +1081,9 @@ describe('Task', () => {
811
1081
  destinationType: CONSULT_TRANSFER_DESTINATION_TYPE.QUEUE,
812
1082
  };
813
1083
 
1084
+ // For this negative case, ensure computed destination is empty
1085
+ getDestinationAgentIdSpy.mockReturnValueOnce('');
1086
+
814
1087
  await expect(
815
1088
  taskWithoutDestAgentId.consultTransfer(queueConsultTransferPayload)
816
1089
  ).rejects.toThrow('Error while performing consultTransfer');
@@ -829,14 +1102,7 @@ describe('Task', () => {
829
1102
  expect(contactMock.consult).toHaveBeenCalledWith({interactionId: taskId, data: consultPayload});
830
1103
  expect(response).toEqual(expectedResponse);
831
1104
 
832
- const error = {
833
- details: {
834
- trackingId: '1234',
835
- data: {
836
- reason: 'Consult Transfer Failed',
837
- },
838
- },
839
- };
1105
+ const error = {details: (global as any).makeFailure('Consult Transfer Failed')};
840
1106
  contactMock.consultTransfer.mockImplementation(() => {
841
1107
  throw error;
842
1108
  });
@@ -849,16 +1115,24 @@ describe('Task', () => {
849
1115
  await expect(task.consultTransfer(consultTransferPayload)).rejects.toThrow(
850
1116
  error.details.data.reason
851
1117
  );
852
- expect(getErrorDetailsSpy).toHaveBeenCalledWith(error, 'consultTransfer', TASK_FILE);
1118
+ expect(generateTaskErrorObjectSpy).toHaveBeenCalledWith(error, 'consultTransfer', TASK_FILE);
1119
+ const expectedTaskErrorFieldsConsultTransfer = {
1120
+ trackingId: error.details.trackingId,
1121
+ errorMessage: error.details.data.reason,
1122
+ errorType: '',
1123
+ errorData: '',
1124
+ reasonCode: 0,
1125
+ };
853
1126
  expect(mockMetricsManager.trackEvent).toHaveBeenNthCalledWith(
854
1127
  2,
855
1128
  METRIC_EVENT_NAMES.TASK_TRANSFER_FAILED,
856
1129
  {
857
1130
  taskId: taskDataMock.interactionId,
858
- destination: consultTransferPayload.to,
859
- destinationType: consultTransferPayload.destinationType,
1131
+ destination: taskDataMock.destAgentId,
1132
+ destinationType: CONSULT_TRANSFER_DESTINATION_TYPE.AGENT,
860
1133
  isConsultTransfer: true,
861
1134
  error: error.toString(),
1135
+ ...expectedTaskErrorFieldsConsultTransfer,
862
1136
  ...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details),
863
1137
  },
864
1138
  ['operational', 'behavioral', 'business']
@@ -926,14 +1200,7 @@ describe('Task', () => {
926
1200
  });
927
1201
 
928
1202
  it('should handle errors in transfer method', async () => {
929
- const error = {
930
- details: {
931
- trackingId: '1234',
932
- data: {
933
- reason: 'Consult Transfer Failed',
934
- },
935
- },
936
- };
1203
+ const error = {details: (global as any).makeFailure('Consult Transfer Failed')};
937
1204
  contactMock.blindTransfer.mockImplementation(() => {
938
1205
  throw error;
939
1206
  });
@@ -944,7 +1211,14 @@ describe('Task', () => {
944
1211
  };
945
1212
 
946
1213
  await expect(task.transfer(blindTransferPayload)).rejects.toThrow(error.details.data.reason);
947
- expect(getErrorDetailsSpy).toHaveBeenCalledWith(error, 'transfer', TASK_FILE);
1214
+ expect(generateTaskErrorObjectSpy).toHaveBeenCalledWith(error, 'transfer', TASK_FILE);
1215
+ const expectedTaskErrorFieldsTransfer = {
1216
+ trackingId: error.details.trackingId,
1217
+ errorMessage: error.details.data.reason,
1218
+ errorType: '',
1219
+ errorData: '',
1220
+ reasonCode: 0,
1221
+ };
948
1222
  expect(mockMetricsManager.trackEvent).toHaveBeenNthCalledWith(
949
1223
  1,
950
1224
  METRIC_EVENT_NAMES.TASK_TRANSFER_FAILED,
@@ -954,6 +1228,7 @@ describe('Task', () => {
954
1228
  destinationType: blindTransferPayload.destinationType,
955
1229
  isConsultTransfer: false,
956
1230
  error: error.toString(),
1231
+ ...expectedTaskErrorFieldsTransfer,
957
1232
  ...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details),
958
1233
  },
959
1234
  ['operational', 'behavioral', 'business']
@@ -990,25 +1265,26 @@ describe('Task', () => {
990
1265
  });
991
1266
 
992
1267
  it('should handle errors in end method', async () => {
993
- const error = {
994
- details: {
995
- trackingId: '1234',
996
- data: {
997
- reason: 'End Failed',
998
- },
999
- },
1000
- };
1268
+ const error = {details: (global as any).makeFailure('End Failed')};
1001
1269
  contactMock.end.mockImplementation(() => {
1002
1270
  throw error;
1003
1271
  });
1004
1272
 
1005
1273
  await expect(task.end()).rejects.toThrow(error.details.data.reason);
1006
- expect(getErrorDetailsSpy).toHaveBeenCalledWith(error, 'end', TASK_FILE);
1274
+ expect(generateTaskErrorObjectSpy).toHaveBeenCalledWith(error, 'end', TASK_FILE);
1275
+ const expectedTaskErrorFieldsEnd = {
1276
+ trackingId: error.details.trackingId,
1277
+ errorMessage: error.details.data.reason,
1278
+ errorType: '',
1279
+ errorData: '',
1280
+ reasonCode: 0,
1281
+ };
1007
1282
  expect(mockMetricsManager.trackEvent).toHaveBeenNthCalledWith(
1008
1283
  1,
1009
1284
  METRIC_EVENT_NAMES.TASK_END_FAILED,
1010
1285
  {
1011
1286
  taskId: taskDataMock.interactionId,
1287
+ ...expectedTaskErrorFieldsEnd,
1012
1288
  ...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details),
1013
1289
  },
1014
1290
  ['operational', 'behavioral', 'business']
@@ -1041,14 +1317,7 @@ describe('Task', () => {
1041
1317
  });
1042
1318
 
1043
1319
  it('should handle errors in wrapup method', async () => {
1044
- const error = {
1045
- details: {
1046
- trackingId: '1234',
1047
- data: {
1048
- reason: 'Wrapup Failed',
1049
- },
1050
- },
1051
- };
1320
+ const error = {details: (global as any).makeFailure('Wrapup Failed')};
1052
1321
  contactMock.wrapup.mockImplementation(() => {
1053
1322
  throw error;
1054
1323
  });
@@ -1059,7 +1328,14 @@ describe('Task', () => {
1059
1328
  };
1060
1329
 
1061
1330
  await expect(task.wrapup(wrapupPayload)).rejects.toThrow(error.details.data.reason);
1062
- expect(getErrorDetailsSpy).toHaveBeenCalledWith(error, 'wrapup', TASK_FILE);
1331
+ expect(generateTaskErrorObjectSpy).toHaveBeenCalledWith(error, 'wrapup', TASK_FILE);
1332
+ const expectedTaskErrorFieldsWrapup = {
1333
+ trackingId: error.details.trackingId,
1334
+ errorMessage: error.details.data.reason,
1335
+ errorType: '',
1336
+ errorData: '',
1337
+ reasonCode: 0,
1338
+ };
1063
1339
  expect(mockMetricsManager.trackEvent).toHaveBeenNthCalledWith(
1064
1340
  1,
1065
1341
  METRIC_EVENT_NAMES.TASK_WRAPUP_FAILED,
@@ -1067,6 +1343,7 @@ describe('Task', () => {
1067
1343
  taskId: taskDataMock.interactionId,
1068
1344
  wrapUpCode: wrapupPayload.auxCodeId,
1069
1345
  wrapUpReason: wrapupPayload.wrapUpReason,
1346
+ ...expectedTaskErrorFieldsWrapup,
1070
1347
  ...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details),
1071
1348
  },
1072
1349
  ['operational', 'behavioral', 'business']
@@ -1124,26 +1401,27 @@ describe('Task', () => {
1124
1401
  });
1125
1402
 
1126
1403
  it('should handle errors in pauseRecording method', async () => {
1127
- const error = {
1128
- details: {
1129
- trackingId: '1234',
1130
- data: {
1131
- reason: 'Pause Recording Failed',
1132
- },
1133
- },
1134
- };
1404
+ const error = {details: (global as any).makeFailure('Pause Recording Failed')};
1135
1405
  contactMock.pauseRecording.mockImplementation(() => {
1136
1406
  throw error;
1137
1407
  });
1138
1408
 
1139
1409
  await expect(task.pauseRecording()).rejects.toThrow(error.details.data.reason);
1140
- expect(getErrorDetailsSpy).toHaveBeenCalledWith(error, 'pauseRecording', TASK_FILE);
1410
+ expect(generateTaskErrorObjectSpy).toHaveBeenCalledWith(error, 'pauseRecording', TASK_FILE);
1411
+ const expectedTaskErrorFieldsPause = {
1412
+ trackingId: error.details.trackingId,
1413
+ errorMessage: error.details.data.reason,
1414
+ errorType: '',
1415
+ errorData: '',
1416
+ reasonCode: 0,
1417
+ };
1141
1418
  expect(mockMetricsManager.trackEvent).toHaveBeenNthCalledWith(
1142
1419
  1,
1143
1420
  METRIC_EVENT_NAMES.TASK_PAUSE_RECORDING_FAILED,
1144
1421
  {
1145
1422
  taskId: taskDataMock.interactionId,
1146
1423
  error: error.toString(),
1424
+ ...expectedTaskErrorFieldsPause,
1147
1425
  ...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details),
1148
1426
  },
1149
1427
  ['operational', 'behavioral', 'business']
@@ -1204,14 +1482,7 @@ describe('Task', () => {
1204
1482
  });
1205
1483
 
1206
1484
  it('should handle errors in resumeRecording method', async () => {
1207
- const error = {
1208
- details: {
1209
- trackingId: '1234',
1210
- data: {
1211
- reason: 'Resume Recording Failed',
1212
- },
1213
- },
1214
- };
1485
+ const error = {details: (global as any).makeFailure('Resume Recording Failed')};
1215
1486
  contactMock.resumeRecording.mockImplementation(() => {
1216
1487
  throw error;
1217
1488
  });
@@ -1221,13 +1492,21 @@ describe('Task', () => {
1221
1492
  };
1222
1493
 
1223
1494
  await expect(task.resumeRecording(resumePayload)).rejects.toThrow(error.details.data.reason);
1224
- expect(getErrorDetailsSpy).toHaveBeenCalledWith(error, 'resumeRecording', TASK_FILE);
1495
+ expect(generateTaskErrorObjectSpy).toHaveBeenCalledWith(error, 'resumeRecording', TASK_FILE);
1496
+ const expectedTaskErrorFieldsResumeRec = {
1497
+ trackingId: error.details.trackingId,
1498
+ errorMessage: error.details.data.reason,
1499
+ errorType: '',
1500
+ errorData: '',
1501
+ reasonCode: 0,
1502
+ };
1225
1503
  expect(mockMetricsManager.trackEvent).toHaveBeenNthCalledWith(
1226
1504
  1,
1227
1505
  METRIC_EVENT_NAMES.TASK_RESUME_RECORDING_FAILED,
1228
1506
  {
1229
1507
  taskId: taskDataMock.interactionId,
1230
1508
  error: error.toString(),
1509
+ ...expectedTaskErrorFieldsResumeRec,
1231
1510
  ...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details),
1232
1511
  },
1233
1512
  ['operational', 'behavioral', 'business']
@@ -1267,7 +1546,7 @@ describe('Task', () => {
1267
1546
  throw error;
1268
1547
  });
1269
1548
  await expect(task.toggleMute()).rejects.toThrow(new Error(error.details.data.reason));
1270
- expect(getErrorDetailsSpy).toHaveBeenCalledWith(error, 'toggleMute', TASK_FILE);
1549
+ expect(generateTaskErrorObjectSpy).toHaveBeenCalledWith(error, 'toggleMute', TASK_FILE);
1271
1550
  expect(loggerInfoSpy).toHaveBeenCalledWith(`Toggling mute state`, {
1272
1551
  module: TASK_FILE,
1273
1552
  method: 'toggleMute',
@@ -1471,4 +1750,180 @@ describe('Task', () => {
1471
1750
  });
1472
1751
  });
1473
1752
  });
1753
+
1754
+ describe('Conference methods', () => {
1755
+ beforeEach(() => {
1756
+ contactMock = {
1757
+ consultConference: jest.fn(),
1758
+ exitConference: jest.fn(),
1759
+ conferenceTransfer: jest.fn(),
1760
+ };
1761
+
1762
+ // Re-setup the getDestinationAgentId spy for conference methods
1763
+ getDestinationAgentIdSpy = jest
1764
+ .spyOn(Utils, 'getDestinationAgentId')
1765
+ .mockReturnValue(taskDataMock.destAgentId);
1766
+
1767
+
1768
+ task = new Task(contactMock, webCallingService, taskDataMock, {
1769
+ wrapUpProps: { wrapUpReasonList: [] },
1770
+ autoWrapEnabled: false,
1771
+ autoWrapAfterSeconds: 0
1772
+ }, taskDataMock.agentId);
1773
+ });
1774
+
1775
+ describe('consultConference', () => {
1776
+
1777
+ it('should successfully start conference and emit event', async () => {
1778
+ const mockResponse = {
1779
+ trackingId: 'test-tracking-id',
1780
+ interactionId: taskId,
1781
+ };
1782
+ contactMock.consultConference.mockResolvedValue(mockResponse);
1783
+
1784
+
1785
+ const result = await task.consultConference();
1786
+
1787
+ expect(contactMock.consultConference).toHaveBeenCalledWith({
1788
+ interactionId: taskId,
1789
+ data: {
1790
+ agentId: taskDataMock.agentId, // From task data agent ID
1791
+ to: taskDataMock.destAgentId, // From getDestinationAgentId() using task participants
1792
+ destinationType: 'agent', // From consultation data
1793
+ },
1794
+ });
1795
+ expect(result).toEqual(mockResponse);
1796
+ expect(LoggerProxy.info).toHaveBeenCalledWith(`Initiating consult conference to ${taskDataMock.destAgentId}`, {
1797
+ module: TASK_FILE,
1798
+ method: 'consultConference',
1799
+ interactionId: taskId,
1800
+ });
1801
+ expect(LoggerProxy.log).toHaveBeenCalledWith('Consult conference started successfully', {
1802
+ module: TASK_FILE,
1803
+ method: 'consultConference',
1804
+ interactionId: taskId,
1805
+ });
1806
+ });
1807
+
1808
+ it('should handle basic validation scenarios', async () => {
1809
+ // Agent Desktop logic validates data structure but not participant availability
1810
+ // This test confirms the method works with the Agent Desktop data flow
1811
+ const mockResponse = {
1812
+ trackingId: 'test-tracking-validation',
1813
+ interactionId: taskId,
1814
+ };
1815
+ contactMock.consultConference.mockResolvedValue(mockResponse);
1816
+
1817
+ const result = await task.consultConference();
1818
+ expect(result).toEqual(mockResponse);
1819
+ });
1820
+
1821
+ it('should handle and rethrow contact method errors', async () => {
1822
+ const mockError = new Error('Conference start failed');
1823
+ contactMock.consultConference.mockRejectedValue(mockError);
1824
+ generateTaskErrorObjectSpy.mockReturnValue(mockError);
1825
+
1826
+ await expect(task.consultConference()).rejects.toThrow('Conference start failed');
1827
+ expect(LoggerProxy.error).toHaveBeenCalledWith('Failed to start consult conference', {
1828
+ module: TASK_FILE,
1829
+ method: 'consultConference',
1830
+ interactionId: taskId,
1831
+ });
1832
+ });
1833
+ });
1834
+
1835
+ describe('exitConference', () => {
1836
+ it('should successfully end conference and emit event', async () => {
1837
+ const mockResponse = {
1838
+ trackingId: 'test-tracking-id-end',
1839
+ interactionId: taskId,
1840
+ };
1841
+ contactMock.exitConference.mockResolvedValue(mockResponse);
1842
+
1843
+ const result = await task.exitConference();
1844
+
1845
+ expect(contactMock.exitConference).toHaveBeenCalledWith({
1846
+ interactionId: taskId,
1847
+ });
1848
+ expect(result).toEqual(mockResponse);
1849
+ expect(LoggerProxy.info).toHaveBeenCalledWith('Exiting consult conference', {
1850
+ module: TASK_FILE,
1851
+ method: 'exitConference',
1852
+ interactionId: taskId,
1853
+ });
1854
+ expect(LoggerProxy.log).toHaveBeenCalledWith('Consult conference exited successfully', {
1855
+ module: TASK_FILE,
1856
+ method: 'exitConference',
1857
+ interactionId: taskId,
1858
+ });
1859
+ });
1860
+
1861
+ it('should throw error for invalid interaction ID', async () => {
1862
+ task.data.interactionId = '';
1863
+
1864
+ await expect(task.exitConference()).rejects.toThrow('Error while performing exitConference');
1865
+ expect(contactMock.exitConference).not.toHaveBeenCalled();
1866
+ });
1867
+
1868
+ it('should handle and rethrow contact method errors', async () => {
1869
+ const mockError = new Error('Conference end failed');
1870
+ contactMock.exitConference.mockRejectedValue(mockError);
1871
+ generateTaskErrorObjectSpy.mockReturnValue(mockError);
1872
+
1873
+ await expect(task.exitConference()).rejects.toThrow('Conference end failed');
1874
+ expect(LoggerProxy.error).toHaveBeenCalledWith('Failed to exit consult conference', {
1875
+ module: TASK_FILE,
1876
+ method: 'exitConference',
1877
+ interactionId: taskId,
1878
+ });
1879
+ });
1880
+ });
1881
+
1882
+ describe('transferConference', () => {
1883
+ it('should successfully transfer conference', async () => {
1884
+ const mockResponse = {
1885
+ trackingId: 'test-tracking-id-transfer',
1886
+ interactionId: taskId,
1887
+ };
1888
+ contactMock.conferenceTransfer.mockResolvedValue(mockResponse);
1889
+
1890
+ const result = await task.transferConference();
1891
+
1892
+ expect(contactMock.conferenceTransfer).toHaveBeenCalledWith({
1893
+ interactionId: taskId,
1894
+ });
1895
+ expect(result).toEqual(mockResponse);
1896
+ expect(LoggerProxy.info).toHaveBeenCalledWith('Transferring conference', {
1897
+ module: TASK_FILE,
1898
+ method: 'transferConference',
1899
+ interactionId: taskId,
1900
+ });
1901
+ expect(LoggerProxy.log).toHaveBeenCalledWith('Conference transferred successfully', {
1902
+ module: TASK_FILE,
1903
+ method: 'transferConference',
1904
+ interactionId: taskId,
1905
+ });
1906
+ });
1907
+
1908
+ it('should throw error for invalid interaction ID', async () => {
1909
+ task.data.interactionId = '';
1910
+
1911
+ await expect(task.transferConference()).rejects.toThrow('Error while performing transferConference');
1912
+ expect(contactMock.conferenceTransfer).not.toHaveBeenCalled();
1913
+ });
1914
+
1915
+ it('should handle and rethrow contact method errors', async () => {
1916
+ const mockError = new Error('Conference transfer failed');
1917
+ contactMock.conferenceTransfer.mockRejectedValue(mockError);
1918
+ generateTaskErrorObjectSpy.mockReturnValue(mockError);
1919
+
1920
+ await expect(task.transferConference()).rejects.toThrow('Conference transfer failed');
1921
+ expect(LoggerProxy.error).toHaveBeenCalledWith('Failed to transfer conference', {
1922
+ module: TASK_FILE,
1923
+ method: 'transferConference',
1924
+ interactionId: taskId,
1925
+ });
1926
+ });
1927
+ });
1928
+ });
1474
1929
  });