@webex/contact-center 3.9.0-next.10 → 3.9.0-next.11

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 -0
  2. package/dist/cc.js.map +1 -1
  3. package/dist/metrics/behavioral-events.js +37 -0
  4. package/dist/metrics/behavioral-events.js.map +1 -1
  5. package/dist/metrics/constants.js +14 -0
  6. package/dist/metrics/constants.js.map +1 -1
  7. package/dist/services/config/types.js +22 -0
  8. package/dist/services/config/types.js.map +1 -1
  9. package/dist/services/core/Utils.js +42 -1
  10. package/dist/services/core/Utils.js.map +1 -1
  11. package/dist/services/task/TaskManager.js +73 -2
  12. package/dist/services/task/TaskManager.js.map +1 -1
  13. package/dist/services/task/constants.js +7 -1
  14. package/dist/services/task/constants.js.map +1 -1
  15. package/dist/services/task/contact.js +86 -0
  16. package/dist/services/task/contact.js.map +1 -1
  17. package/dist/services/task/index.js +239 -1
  18. package/dist/services/task/index.js.map +1 -1
  19. package/dist/services/task/types.js +14 -0
  20. package/dist/services/task/types.js.map +1 -1
  21. package/dist/types/metrics/constants.d.ts +13 -0
  22. package/dist/types/services/config/types.d.ts +44 -0
  23. package/dist/types/services/core/Utils.d.ts +14 -1
  24. package/dist/types/services/task/constants.d.ts +6 -0
  25. package/dist/types/services/task/contact.d.ts +10 -0
  26. package/dist/types/services/task/index.d.ts +43 -1
  27. package/dist/types/services/task/types.d.ts +123 -1
  28. package/dist/webex.js +1 -1
  29. package/package.json +1 -1
  30. package/src/cc.ts +1 -0
  31. package/src/metrics/behavioral-events.ts +38 -0
  32. package/src/metrics/constants.ts +15 -0
  33. package/src/services/config/types.ts +22 -0
  34. package/src/services/core/Utils.ts +44 -0
  35. package/src/services/task/TaskManager.ts +78 -3
  36. package/src/services/task/constants.ts +6 -0
  37. package/src/services/task/contact.ts +80 -0
  38. package/src/services/task/index.ts +285 -1
  39. package/src/services/task/types.ts +133 -0
  40. package/test/unit/spec/cc.ts +1 -0
  41. package/test/unit/spec/metrics/behavioral-events.ts +42 -0
  42. package/test/unit/spec/services/task/TaskManager.ts +137 -0
  43. package/test/unit/spec/services/task/contact.ts +31 -1
  44. package/test/unit/spec/services/task/index.ts +184 -1
  45. package/umd/contact-center.min.js +2 -2
  46. package/umd/contact-center.min.js.map +1 -1
@@ -370,6 +370,126 @@ export enum TASK_EVENTS {
370
370
  * ```
371
371
  */
372
372
  TASK_OFFER_CONTACT = 'task:offerContact',
373
+
374
+ /**
375
+ * Triggered when a conference is being established
376
+ * @example
377
+ * ```typescript
378
+ * task.on(TASK_EVENTS.TASK_CONFERENCE_ESTABLISHING, (task: ITask) => {
379
+ * console.log('Conference establishing:', task.data.interactionId);
380
+ * // Handle conference setup in progress
381
+ * });
382
+ * ```
383
+ */
384
+ TASK_CONFERENCE_ESTABLISHING = 'task:conferenceEstablishing',
385
+
386
+ /**
387
+ * Triggered when a conference is started successfully
388
+ * @example
389
+ * ```typescript
390
+ * task.on(TASK_EVENTS.TASK_CONFERENCE_STARTED, (task: ITask) => {
391
+ * console.log('Conference started:', task.data.interactionId);
392
+ * // Handle conference start
393
+ * });
394
+ * ```
395
+ */
396
+ TASK_CONFERENCE_STARTED = 'task:conferenceStarted',
397
+
398
+ /**
399
+ * Triggered when a conference fails to start
400
+ * @example
401
+ * ```typescript
402
+ * task.on(TASK_EVENTS.TASK_CONFERENCE_FAILED, (task: ITask) => {
403
+ * console.log('Conference failed:', task.data.interactionId);
404
+ * // Handle conference failure
405
+ * });
406
+ * ```
407
+ */
408
+ TASK_CONFERENCE_FAILED = 'task:conferenceFailed',
409
+
410
+ /**
411
+ * Triggered when a conference is ended successfully
412
+ * @example
413
+ * ```typescript
414
+ * task.on(TASK_EVENTS.TASK_CONFERENCE_ENDED, (task: ITask) => {
415
+ * console.log('Conference ended:', task.data.interactionId);
416
+ * // Handle conference end
417
+ * });
418
+ * ```
419
+ */
420
+ TASK_CONFERENCE_ENDED = 'task:conferenceEnded',
421
+
422
+ /**
423
+ * Triggered when a participant joins the conference
424
+ * @example
425
+ * ```typescript
426
+ * task.on(TASK_EVENTS.TASK_PARTICIPANT_JOINED, (task: ITask) => {
427
+ * console.log('Participant joined conference:', task.data.interactionId);
428
+ * // Handle participant joining
429
+ * });
430
+ * ```
431
+ */
432
+ TASK_PARTICIPANT_JOINED = 'task:participantJoined',
433
+
434
+ /**
435
+ * Triggered when a participant leaves the conference
436
+ * @example
437
+ * ```typescript
438
+ * task.on(TASK_EVENTS.TASK_PARTICIPANT_LEFT, (task: ITask) => {
439
+ * console.log('Participant left conference:', task.data.interactionId);
440
+ * // Handle participant leaving
441
+ * });
442
+ * ```
443
+ */
444
+ TASK_PARTICIPANT_LEFT = 'task:participantLeft',
445
+
446
+ /**
447
+ * Triggered when conference transfer is successful
448
+ * @example
449
+ * ```typescript
450
+ * task.on(TASK_EVENTS.TASK_CONFERENCE_TRANSFERRED, (task: ITask) => {
451
+ * console.log('Conference transferred:', task.data.interactionId);
452
+ * // Handle successful conference transfer
453
+ * });
454
+ * ```
455
+ */
456
+ TASK_CONFERENCE_TRANSFERRED = 'task:conferenceTransferred',
457
+
458
+ /**
459
+ * Triggered when conference transfer fails
460
+ * @example
461
+ * ```typescript
462
+ * task.on(TASK_EVENTS.TASK_CONFERENCE_TRANSFER_FAILED, (task: ITask) => {
463
+ * console.log('Conference transfer failed:', task.data.interactionId);
464
+ * // Handle failed conference transfer
465
+ * });
466
+ * ```
467
+ */
468
+ TASK_CONFERENCE_TRANSFER_FAILED = 'task:conferenceTransferFailed',
469
+
470
+ /**
471
+ * Triggered when ending a conference fails
472
+ * @example
473
+ * ```typescript
474
+ * task.on(TASK_EVENTS.TASK_CONFERENCE_END_FAILED, (task: ITask) => {
475
+ * console.log('Conference end failed:', task.data.interactionId);
476
+ * // Handle failed conference end
477
+ * });
478
+ * ```
479
+ */
480
+ TASK_CONFERENCE_END_FAILED = 'task:conferenceEndFailed',
481
+
482
+ /**
483
+ * Triggered when participant exit from conference fails
484
+ * @example
485
+ * ```typescript
486
+ * task.on(TASK_EVENTS.TASK_PARTICIPANT_LEFT_FAILED, (task: ITask) => {
487
+ * console.log('Participant failed to leave conference:', task.data.interactionId);
488
+ * // Handle failed participant exit
489
+ * });
490
+ * ```
491
+ */
492
+ TASK_PARTICIPANT_LEFT_FAILED = 'task:participantLeftFailed',
373
493
  }
374
494
 
375
495
  /**
@@ -883,6 +1003,19 @@ export type ConsultConferenceData = {
883
1003
  destinationType: string;
884
1004
  };
885
1005
 
1006
+ /**
1007
+ * Legacy consultation conference data type matching Agent Desktop
1008
+ * @public
1009
+ */
1010
+ export type consultConferencePayloadData = {
1011
+ /** Identifier of the agent initiating consult/conference */
1012
+ agentId: string;
1013
+ /** Type of destination (e.g., 'agent', 'queue') */
1014
+ destinationType: string;
1015
+ /** Identifier of the destination agent */
1016
+ destAgentId: string;
1017
+ };
1018
+
886
1019
  /**
887
1020
  * Parameters required for cancelling a consult to queue operation
888
1021
  * @public
@@ -139,6 +139,7 @@ describe('webex.cc', () => {
139
139
  webSocketManager: mockWebSocketManager,
140
140
  task: undefined,
141
141
  setWrapupData: jest.fn(),
142
+ setAgentId: jest.fn(),
142
143
  registerIncomingCallEvent: jest.fn(),
143
144
  registerTaskListeners: jest.fn(),
144
145
  getTask: jest.fn(),
@@ -110,6 +110,48 @@ describe('metrics/behavioral-events', () => {
110
110
  verb: 'fail',
111
111
  });
112
112
 
113
+ expect(getEventTaxonomy(METRIC_EVENT_NAMES.TASK_CONFERENCE_START_SUCCESS)).toEqual({
114
+ product,
115
+ agent: 'user',
116
+ target: 'task_conference_start',
117
+ verb: 'complete',
118
+ });
119
+
120
+ expect(getEventTaxonomy(METRIC_EVENT_NAMES.TASK_CONFERENCE_START_FAILED)).toEqual({
121
+ product,
122
+ agent: 'user',
123
+ target: 'task_conference_start',
124
+ verb: 'fail',
125
+ });
126
+
127
+ expect(getEventTaxonomy(METRIC_EVENT_NAMES.TASK_CONFERENCE_END_SUCCESS)).toEqual({
128
+ product,
129
+ agent: 'user',
130
+ target: 'task_conference_end',
131
+ verb: 'complete',
132
+ });
133
+
134
+ expect(getEventTaxonomy(METRIC_EVENT_NAMES.TASK_CONFERENCE_END_FAILED)).toEqual({
135
+ product,
136
+ agent: 'user',
137
+ target: 'task_conference_end',
138
+ verb: 'fail',
139
+ });
140
+
141
+ expect(getEventTaxonomy(METRIC_EVENT_NAMES.TASK_CONFERENCE_TRANSFER_SUCCESS)).toEqual({
142
+ product,
143
+ agent: 'user',
144
+ target: 'task_conference_transfer',
145
+ verb: 'complete',
146
+ });
147
+
148
+ expect(getEventTaxonomy(METRIC_EVENT_NAMES.TASK_CONFERENCE_TRANSFER_FAILED)).toEqual({
149
+ product,
150
+ agent: 'user',
151
+ target: 'task_conference_transfer',
152
+ verb: 'fail',
153
+ });
154
+
113
155
  expect(getEventTaxonomy('' as METRIC_EVENT_NAMES)).toEqual(undefined);
114
156
  });
115
157
  });
@@ -1353,6 +1353,143 @@ describe('TaskManager', () => {
1353
1353
  expect(spy).toHaveBeenCalledWith(taskEvent, task);
1354
1354
  });
1355
1355
  });
1356
+ });
1357
+
1358
+ describe('Conference event handling', () => {
1359
+ let task;
1360
+
1361
+ beforeEach(() => {
1362
+ task = {
1363
+ data: { interactionId: taskId },
1364
+ emit: jest.fn(),
1365
+ updateTaskData: jest.fn().mockImplementation((updatedData) => {
1366
+ // Mock the updateTaskData method to actually update task.data
1367
+ task.data = { ...task.data, ...updatedData };
1368
+ return task;
1369
+ }),
1370
+ };
1371
+ taskManager.taskCollection[taskId] = task;
1372
+ });
1373
+
1374
+ it('should handle AGENT_CONSULT_CONFERENCED event', () => {
1375
+ const payload = {
1376
+ data: {
1377
+ type: CC_EVENTS.AGENT_CONSULT_CONFERENCED,
1378
+ interactionId: taskId,
1379
+ isConferencing: true,
1380
+ },
1381
+ };
1382
+
1383
+ webSocketManagerMock.emit('message', JSON.stringify(payload));
1384
+
1385
+ expect(task.data.isConferencing).toBe(true);
1386
+ expect(task.emit).toHaveBeenCalledWith(TASK_EVENTS.TASK_CONFERENCE_STARTED, task);
1387
+ });
1388
+
1389
+ it('should handle AGENT_CONSULT_CONFERENCING event', () => {
1390
+ const payload = {
1391
+ data: {
1392
+ type: CC_EVENTS.AGENT_CONSULT_CONFERENCING,
1393
+ interactionId: taskId,
1394
+ isConferencing: true,
1395
+ },
1396
+ };
1397
+
1398
+ webSocketManagerMock.emit('message', JSON.stringify(payload));
1399
+
1400
+ expect(task.data.isConferencing).toBe(true);
1401
+ // No task event emission for conferencing - only for conferenced (completed)
1402
+ expect(task.emit).not.toHaveBeenCalledWith(TASK_EVENTS.TASK_CONFERENCE_STARTED, task);
1403
+ });
1404
+
1405
+ it('should handle AGENT_CONSULT_CONFERENCE_FAILED event', () => {
1406
+ const payload = {
1407
+ data: {
1408
+ type: CC_EVENTS.AGENT_CONSULT_CONFERENCE_FAILED,
1409
+ interactionId: taskId,
1410
+ reason: 'Network error',
1411
+ },
1412
+ };
1413
+
1414
+ webSocketManagerMock.emit('message', JSON.stringify(payload));
1415
+
1416
+ expect(task.data.reason).toBe('Network error');
1417
+ // No event emission expected for failure - handled by contact method promise rejection
1418
+ });
1419
+
1420
+ it('should handle PARTICIPANT_JOINED_CONFERENCE event', () => {
1421
+ const payload = {
1422
+ data: {
1423
+ type: CC_EVENTS.PARTICIPANT_JOINED_CONFERENCE,
1424
+ interactionId: taskId,
1425
+ participantId: 'new-participant-123',
1426
+ participantType: 'agent',
1427
+ },
1428
+ };
1429
+
1430
+ webSocketManagerMock.emit('message', JSON.stringify(payload));
1431
+
1432
+ expect(task.data.participantId).toBe('new-participant-123');
1433
+ expect(task.data.participantType).toBe('agent');
1434
+ // No specific task event emission for participant joined - just data update
1435
+ });
1436
+
1437
+ it('should handle PARTICIPANT_LEFT_CONFERENCE event', () => {
1438
+ const payload = {
1439
+ data: {
1440
+ type: CC_EVENTS.PARTICIPANT_LEFT_CONFERENCE,
1441
+ interactionId: taskId,
1442
+ isConferencing: false,
1443
+ },
1444
+ };
1445
+
1446
+ webSocketManagerMock.emit('message', JSON.stringify(payload));
1447
+
1448
+ expect(task.data.isConferencing).toBe(false);
1449
+ expect(task.emit).toHaveBeenCalledWith(TASK_EVENTS.TASK_PARTICIPANT_LEFT, task);
1450
+ });
1451
+
1452
+ it('should handle PARTICIPANT_LEFT_CONFERENCE_FAILED event', () => {
1453
+ const payload = {
1454
+ data: {
1455
+ type: CC_EVENTS.PARTICIPANT_LEFT_CONFERENCE_FAILED,
1456
+ interactionId: taskId,
1457
+ reason: 'Exit failed',
1458
+ },
1459
+ };
1460
+
1461
+ webSocketManagerMock.emit('message', JSON.stringify(payload));
1462
+
1463
+ expect(task.data.reason).toBe('Exit failed');
1464
+ // No event emission expected for failure - handled by contact method promise rejection
1465
+ });
1466
+
1467
+ it('should only update task for matching interactionId', () => {
1468
+ const otherTaskId = 'other-task-id';
1469
+ const otherTask = {
1470
+ data: { interactionId: otherTaskId },
1471
+ emit: jest.fn(),
1472
+ };
1473
+ taskManager.taskCollection[otherTaskId] = otherTask;
1474
+
1475
+ const payload = {
1476
+ data: {
1477
+ type: CC_EVENTS.AGENT_CONSULT_CONFERENCED,
1478
+ interactionId: taskId,
1479
+ isConferencing: true,
1480
+ },
1481
+ };
1482
+
1483
+ webSocketManagerMock.emit('message', JSON.stringify(payload));
1484
+
1485
+ // Only the matching task should be updated
1486
+ expect(task.data.isConferencing).toBe(true);
1487
+ expect(task.emit).toHaveBeenCalledWith(TASK_EVENTS.TASK_CONFERENCE_STARTED, task);
1488
+
1489
+ // Other task should not be affected
1490
+ expect(otherTask.data.isConferencing).toBeUndefined();
1491
+ expect(otherTask.emit).not.toHaveBeenCalled();
1492
+ });
1356
1493
  });
1357
1494
  });
1358
1495
 
@@ -197,7 +197,37 @@ describe("Routing contacts", () => {
197
197
  const req = contact.wrapup({
198
198
  interactionId: "interactionId",
199
199
  data: { wrapUpReason: "testWrapUpReason", auxCodeId: "auxCodeID1234", isAutoWrapup: "on" }
200
- } as any);
200
+ } as any);
201
+ expect(req).toBeDefined();
202
+ });
203
+
204
+ it("consultConference", () => {
205
+ fakeAqm.pendingRequests = {};
206
+ const consultData = {
207
+ agentId: "current-agent-id",
208
+ to: "destination-agent-id",
209
+ destinationType: "agent"
210
+ };
211
+ const req = contact.consultConference({
212
+ interactionId: "test-interaction-123",
213
+ data: consultData
214
+ });
215
+ expect(req).toBeDefined();
216
+ });
217
+
218
+ it("exitConference", () => {
219
+ fakeAqm.pendingRequests = {};
220
+ const req = contact.exitConference({
221
+ interactionId: "test-interaction-456"
222
+ });
223
+ expect(req).toBeDefined();
224
+ });
225
+
226
+ it("conferenceTransfer", () => {
227
+ fakeAqm.pendingRequests = {};
228
+ const req = contact.conferenceTransfer({
229
+ interactionId: "test-interaction-transfer-123"
230
+ });
201
231
  expect(req).toBeDefined();
202
232
  });
203
233
  });
@@ -75,6 +75,9 @@ describe('Task', () => {
75
75
  wrapup: jest.fn().mockResolvedValue({}),
76
76
  pauseRecording: jest.fn().mockResolvedValue({}),
77
77
  resumeRecording: jest.fn().mockResolvedValue({}),
78
+ consultConference: jest.fn().mockResolvedValue({}),
79
+ exitConference: jest.fn().mockResolvedValue({}),
80
+ conferenceTransfer: jest.fn().mockResolvedValue({}),
78
81
  };
79
82
 
80
83
  mockMetricsManager = {
@@ -671,8 +674,8 @@ describe('Task', () => {
671
674
  expect(loggerLogSpy).toHaveBeenCalledWith(`Consult started successfully to ${consultPayload.to}`, {
672
675
  module: TASK_FILE,
673
676
  method: 'consult',
674
- trackingId: expectedResponse.trackingId,
675
677
  interactionId: task.data.interactionId,
678
+ trackingId: '1234',
676
679
  });
677
680
  expect(mockMetricsManager.trackEvent).toHaveBeenCalledWith(
678
681
  METRIC_EVENT_NAMES.TASK_CONSULT_START_SUCCESS,
@@ -1575,4 +1578,184 @@ describe('Task', () => {
1575
1578
  });
1576
1579
  });
1577
1580
  });
1581
+
1582
+ describe('Conference methods', () => {
1583
+ beforeEach(() => {
1584
+ contactMock = {
1585
+ consultConference: jest.fn(),
1586
+ exitConference: jest.fn(),
1587
+ conferenceTransfer: jest.fn(),
1588
+ };
1589
+
1590
+ // Re-setup the getDestinationAgentId spy for conference methods
1591
+ getDestinationAgentIdSpy = jest
1592
+ .spyOn(Utils, 'getDestinationAgentId')
1593
+ .mockReturnValue(taskDataMock.destAgentId);
1594
+
1595
+
1596
+ task = new Task(contactMock, webCallingService, taskDataMock, {
1597
+ wrapUpProps: { wrapUpReasonList: [] },
1598
+ autoWrapEnabled: false,
1599
+ autoWrapAfterSeconds: 0
1600
+ }, taskDataMock.agentId);
1601
+ });
1602
+
1603
+ describe('consultConference', () => {
1604
+
1605
+ it('should successfully start conference and emit event', async () => {
1606
+ const mockResponse = {
1607
+ trackingId: 'test-tracking-id',
1608
+ interactionId: taskId,
1609
+ };
1610
+ contactMock.consultConference.mockResolvedValue(mockResponse);
1611
+
1612
+
1613
+ const result = await task.consultConference();
1614
+
1615
+ expect(contactMock.consultConference).toHaveBeenCalledWith({
1616
+ interactionId: taskId,
1617
+ data: {
1618
+ agentId: taskDataMock.agentId, // From task data agent ID
1619
+ to: taskDataMock.destAgentId, // From getDestinationAgentId() using task participants
1620
+ destinationType: 'agent', // From consultation data
1621
+ },
1622
+ });
1623
+ expect(result).toEqual(mockResponse);
1624
+ expect(LoggerProxy.info).toHaveBeenCalledWith(`Initiating consult conference to ${taskDataMock.destAgentId}`, {
1625
+ module: TASK_FILE,
1626
+ method: 'consultConference',
1627
+ interactionId: taskId,
1628
+ });
1629
+ expect(LoggerProxy.log).toHaveBeenCalledWith('Consult conference started successfully', {
1630
+ module: TASK_FILE,
1631
+ method: 'consultConference',
1632
+ interactionId: taskId,
1633
+ });
1634
+ });
1635
+
1636
+ it('should handle basic validation scenarios', async () => {
1637
+ // Agent Desktop logic validates data structure but not participant availability
1638
+ // This test confirms the method works with the Agent Desktop data flow
1639
+ const mockResponse = {
1640
+ trackingId: 'test-tracking-validation',
1641
+ interactionId: taskId,
1642
+ };
1643
+ contactMock.consultConference.mockResolvedValue(mockResponse);
1644
+
1645
+ const result = await task.consultConference();
1646
+ expect(result).toEqual(mockResponse);
1647
+ });
1648
+
1649
+ it('should handle and rethrow contact method errors', async () => {
1650
+ const mockError = new Error('Conference start failed');
1651
+ contactMock.consultConference.mockRejectedValue(mockError);
1652
+ generateTaskErrorObjectSpy.mockReturnValue(mockError);
1653
+
1654
+ await expect(task.consultConference()).rejects.toThrow('Conference start failed');
1655
+ expect(LoggerProxy.error).toHaveBeenCalledWith('Failed to start consult conference', {
1656
+ module: TASK_FILE,
1657
+ method: 'consultConference',
1658
+ interactionId: taskId,
1659
+ });
1660
+ });
1661
+ });
1662
+
1663
+ describe('exitConference', () => {
1664
+ it('should successfully end conference and emit event', async () => {
1665
+ const mockResponse = {
1666
+ trackingId: 'test-tracking-id-end',
1667
+ interactionId: taskId,
1668
+ };
1669
+ contactMock.exitConference.mockResolvedValue(mockResponse);
1670
+
1671
+ const result = await task.exitConference();
1672
+
1673
+ expect(contactMock.exitConference).toHaveBeenCalledWith({
1674
+ interactionId: taskId,
1675
+ });
1676
+ expect(result).toEqual(mockResponse);
1677
+ expect(LoggerProxy.info).toHaveBeenCalledWith('Exiting consult conference', {
1678
+ module: TASK_FILE,
1679
+ method: 'exitConference',
1680
+ interactionId: taskId,
1681
+ });
1682
+ expect(LoggerProxy.log).toHaveBeenCalledWith('Consult conference exited successfully', {
1683
+ module: TASK_FILE,
1684
+ method: 'exitConference',
1685
+ interactionId: taskId,
1686
+ });
1687
+ });
1688
+
1689
+ it('should throw error for invalid interaction ID', async () => {
1690
+ task.data.interactionId = '';
1691
+
1692
+ await expect(task.exitConference()).rejects.toThrow('Error while performing exitConference');
1693
+ expect(contactMock.exitConference).not.toHaveBeenCalled();
1694
+ });
1695
+
1696
+ it('should handle and rethrow contact method errors', async () => {
1697
+ const mockError = new Error('Conference end failed');
1698
+ contactMock.exitConference.mockRejectedValue(mockError);
1699
+ generateTaskErrorObjectSpy.mockReturnValue(mockError);
1700
+
1701
+ await expect(task.exitConference()).rejects.toThrow('Conference end failed');
1702
+ expect(LoggerProxy.error).toHaveBeenCalledWith('Failed to exit consult conference', {
1703
+ module: TASK_FILE,
1704
+ method: 'exitConference',
1705
+ interactionId: taskId,
1706
+ });
1707
+ });
1708
+ });
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
+ /*
1713
+ describe('transferConference', () => {
1714
+ it('should successfully transfer conference', async () => {
1715
+ const mockResponse = {
1716
+ trackingId: 'test-tracking-id-transfer',
1717
+ interactionId: taskId,
1718
+ };
1719
+ contactMock.conferenceTransfer.mockResolvedValue(mockResponse);
1720
+
1721
+ const result = await task.transferConference();
1722
+
1723
+ expect(contactMock.conferenceTransfer).toHaveBeenCalledWith({
1724
+ interactionId: taskId,
1725
+ });
1726
+ expect(result).toEqual(mockResponse);
1727
+ expect(LoggerProxy.info).toHaveBeenCalledWith('Transferring conference', {
1728
+ module: TASK_FILE,
1729
+ method: 'transferConference',
1730
+ interactionId: taskId,
1731
+ });
1732
+ expect(LoggerProxy.log).toHaveBeenCalledWith('Conference transferred successfully', {
1733
+ module: TASK_FILE,
1734
+ method: 'transferConference',
1735
+ interactionId: taskId,
1736
+ });
1737
+ });
1738
+
1739
+ it('should throw error for invalid interaction ID', async () => {
1740
+ task.data.interactionId = '';
1741
+
1742
+ await expect(task.transferConference()).rejects.toThrow('Error while performing transferConference');
1743
+ expect(contactMock.conferenceTransfer).not.toHaveBeenCalled();
1744
+ });
1745
+
1746
+ it('should handle and rethrow contact method errors', async () => {
1747
+ const mockError = new Error('Conference transfer failed');
1748
+ contactMock.conferenceTransfer.mockRejectedValue(mockError);
1749
+ generateTaskErrorObjectSpy.mockReturnValue(mockError);
1750
+
1751
+ await expect(task.transferConference()).rejects.toThrow('Conference transfer failed');
1752
+ expect(LoggerProxy.error).toHaveBeenCalledWith('Failed to transfer conference', {
1753
+ module: TASK_FILE,
1754
+ method: 'transferConference',
1755
+ interactionId: taskId,
1756
+ });
1757
+ });
1758
+ });
1759
+ */
1760
+ });
1578
1761
  });