@webex/contact-center 3.12.0-task-refactor.11 → 3.12.0-task-refactor.12

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 (101) hide show
  1. package/dist/cc.js +297 -83
  2. package/dist/cc.js.map +1 -1
  3. package/dist/constants.js +3 -0
  4. package/dist/constants.js.map +1 -1
  5. package/dist/metrics/behavioral-events.js +39 -0
  6. package/dist/metrics/behavioral-events.js.map +1 -1
  7. package/dist/metrics/constants.js +8 -1
  8. package/dist/metrics/constants.js.map +1 -1
  9. package/dist/services/ApiAiAssistant.js +3 -0
  10. package/dist/services/ApiAiAssistant.js.map +1 -1
  11. package/dist/services/config/Util.js +1 -1
  12. package/dist/services/config/Util.js.map +1 -1
  13. package/dist/services/config/types.js +10 -0
  14. package/dist/services/config/types.js.map +1 -1
  15. package/dist/services/constants.js +1 -0
  16. package/dist/services/constants.js.map +1 -1
  17. package/dist/services/core/Err.js.map +1 -1
  18. package/dist/services/core/Utils.js +57 -6
  19. package/dist/services/core/Utils.js.map +1 -1
  20. package/dist/services/core/aqm-reqs.js +92 -17
  21. package/dist/services/core/aqm-reqs.js.map +1 -1
  22. package/dist/services/core/websocket/WebSocketManager.js +24 -3
  23. package/dist/services/core/websocket/WebSocketManager.js.map +1 -1
  24. package/dist/services/index.js +1 -0
  25. package/dist/services/index.js.map +1 -1
  26. package/dist/services/task/Task.js +6 -0
  27. package/dist/services/task/Task.js.map +1 -1
  28. package/dist/services/task/TaskManager.js +155 -6
  29. package/dist/services/task/TaskManager.js.map +1 -1
  30. package/dist/services/task/TaskUtils.js +39 -11
  31. package/dist/services/task/TaskUtils.js.map +1 -1
  32. package/dist/services/task/constants.js +7 -1
  33. package/dist/services/task/constants.js.map +1 -1
  34. package/dist/services/task/dialer.js +129 -0
  35. package/dist/services/task/dialer.js.map +1 -1
  36. package/dist/services/task/state-machine/TaskStateMachine.js +30 -3
  37. package/dist/services/task/state-machine/TaskStateMachine.js.map +1 -1
  38. package/dist/services/task/state-machine/actions.js +4 -0
  39. package/dist/services/task/state-machine/actions.js.map +1 -1
  40. package/dist/services/task/state-machine/constants.js +3 -0
  41. package/dist/services/task/state-machine/constants.js.map +1 -1
  42. package/dist/services/task/state-machine/guards.js +23 -0
  43. package/dist/services/task/state-machine/guards.js.map +1 -1
  44. package/dist/services/task/state-machine/types.js.map +1 -1
  45. package/dist/services/task/types.js +59 -0
  46. package/dist/services/task/types.js.map +1 -1
  47. package/dist/types/cc.d.ts +76 -2
  48. package/dist/types/constants.d.ts +3 -0
  49. package/dist/types/metrics/constants.d.ts +6 -0
  50. package/dist/types/services/config/types.d.ts +22 -1
  51. package/dist/types/services/core/Err.d.ts +6 -0
  52. package/dist/types/services/core/Utils.d.ts +20 -2
  53. package/dist/types/services/core/aqm-reqs.d.ts +49 -0
  54. package/dist/types/services/core/websocket/WebSocketManager.d.ts +1 -0
  55. package/dist/types/services/core/websocket/connection-service.d.ts +1 -0
  56. package/dist/types/services/task/Task.d.ts +1 -0
  57. package/dist/types/services/task/TaskUtils.d.ts +13 -0
  58. package/dist/types/services/task/constants.d.ts +6 -0
  59. package/dist/types/services/task/dialer.d.ts +45 -0
  60. package/dist/types/services/task/state-machine/TaskStateMachine.d.ts +36 -8
  61. package/dist/types/services/task/state-machine/constants.d.ts +3 -0
  62. package/dist/types/services/task/state-machine/guards.d.ts +2 -0
  63. package/dist/types/services/task/state-machine/types.d.ts +11 -0
  64. package/dist/types/services/task/types.d.ts +75 -1
  65. package/dist/types.js.map +1 -1
  66. package/dist/webex.js +1 -1
  67. package/package.json +2 -2
  68. package/src/cc.ts +389 -111
  69. package/src/constants.ts +3 -0
  70. package/src/metrics/behavioral-events.ts +42 -0
  71. package/src/metrics/constants.ts +9 -1
  72. package/src/services/ApiAiAssistant.ts +3 -0
  73. package/src/services/config/Util.ts +1 -1
  74. package/src/services/config/types.ts +12 -1
  75. package/src/services/constants.ts +1 -0
  76. package/src/services/core/Err.ts +3 -0
  77. package/src/services/core/Utils.ts +68 -5
  78. package/src/services/core/aqm-reqs.ts +100 -22
  79. package/src/services/core/websocket/WebSocketManager.ts +23 -2
  80. package/src/services/index.ts +1 -0
  81. package/src/services/task/Task.ts +13 -0
  82. package/src/services/task/TaskManager.ts +171 -7
  83. package/src/services/task/TaskUtils.ts +33 -1
  84. package/src/services/task/constants.ts +6 -0
  85. package/src/services/task/dialer.ts +136 -1
  86. package/src/services/task/state-machine/TaskStateMachine.ts +37 -5
  87. package/src/services/task/state-machine/actions.ts +4 -0
  88. package/src/services/task/state-machine/constants.ts +3 -0
  89. package/src/services/task/state-machine/guards.ts +30 -0
  90. package/src/services/task/state-machine/types.ts +14 -1
  91. package/src/services/task/types.ts +80 -0
  92. package/src/types.ts +4 -1
  93. package/test/unit/spec/cc.ts +293 -26
  94. package/test/unit/spec/services/config/index.ts +30 -2
  95. package/test/unit/spec/services/core/Utils.ts +367 -1
  96. package/test/unit/spec/services/core/websocket/WebSocketManager.ts +90 -1
  97. package/test/unit/spec/services/core/websocket/connection-service.ts +4 -5
  98. package/test/unit/spec/services/task/TaskManager.ts +2325 -150
  99. package/test/unit/spec/services/task/dialer.ts +372 -96
  100. package/umd/contact-center.min.js +2 -2
  101. package/umd/contact-center.min.js.map +1 -1
@@ -639,6 +639,66 @@ export enum TASK_EVENTS {
639
639
  * ```
640
640
  */
641
641
  TASK_MULTI_LOGIN_HYDRATE = 'task:multiLoginHydrate',
642
+
643
+ /**
644
+ * Triggered when a campaign preview contact is offered to the agent
645
+ * @example
646
+ * ```typescript
647
+ * task.on(TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_RESERVATION, (data: AgentContact) => {
648
+ * console.log('Campaign preview contact received:', data.interactionId);
649
+ * // Handle campaign preview reservation
650
+ * });
651
+ * ```
652
+ */
653
+ TASK_CAMPAIGN_PREVIEW_RESERVATION = 'task:campaignPreviewReservation',
654
+
655
+ /**
656
+ * Triggered when accepting a campaign preview contact fails
657
+ * @example
658
+ * ```typescript
659
+ * task.on(TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_ACCEPT_FAILED, (task: ITask) => {
660
+ * console.log('Campaign preview accept failed:', task.data.interactionId);
661
+ * // Handle accept failure
662
+ * });
663
+ * ```
664
+ */
665
+ TASK_CAMPAIGN_PREVIEW_ACCEPT_FAILED = 'task:campaignPreviewAcceptFailed',
666
+
667
+ /**
668
+ * Triggered when skipping a campaign preview contact fails
669
+ * @example
670
+ * ```typescript
671
+ * task.on(TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_SKIP_FAILED, (task: ITask) => {
672
+ * console.log('Campaign preview skip failed:', task.data.interactionId);
673
+ * // Handle skip failure
674
+ * });
675
+ * ```
676
+ */
677
+ TASK_CAMPAIGN_PREVIEW_SKIP_FAILED = 'task:campaignPreviewSkipFailed',
678
+
679
+ /**
680
+ * Triggered when removing a campaign preview contact fails
681
+ * @example
682
+ * ```typescript
683
+ * task.on(TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_REMOVE_FAILED, (task: ITask) => {
684
+ * console.log('Campaign preview remove failed:', task.data.interactionId);
685
+ * // Handle remove failure
686
+ * });
687
+ * ```
688
+ */
689
+ TASK_CAMPAIGN_PREVIEW_REMOVE_FAILED = 'task:campaignPreviewRemoveFailed',
690
+
691
+ /**
692
+ * Triggered when a campaign contact is updated (e.g., after skip or remove, when the next contact is offered)
693
+ * @example
694
+ * ```typescript
695
+ * task.on(TASK_EVENTS.TASK_CAMPAIGN_CONTACT_UPDATED, (task: ITask) => {
696
+ * console.log('Campaign contact updated:', task.data.interactionId);
697
+ * // Handle updated campaign contact (e.g., display next contact)
698
+ * });
699
+ * ```
700
+ */
701
+ TASK_CAMPAIGN_CONTACT_UPDATED = 'task:campaignContactUpdated',
642
702
  }
643
703
 
644
704
  /**
@@ -948,6 +1008,14 @@ export type Interaction = {
948
1008
  outdialAgentId?: string;
949
1009
  /** Indicates if the customer has left the call during an active consult */
950
1010
  hasCustomerLeft?: string;
1011
+ /** Indicates if the skip action is disabled for campaign preview contacts */
1012
+ campaignPreviewSkipDisabled?: string;
1013
+ /** Indicates if the remove action is disabled for campaign preview contacts */
1014
+ campaignPreviewRemoveDisabled?: string;
1015
+ /** Auto-action to perform when campaign preview offer times out (ACCEPT, SKIP, REMOVE) */
1016
+ campaignPreviewAutoAction?: string;
1017
+ /** Timestamp (ms) when the campaign preview offer expires */
1018
+ campaignPreviewOfferTimeout?: string;
951
1019
  };
952
1020
  /** Main interaction identifier for related interactions */
953
1021
  mainInteractionId?: string;
@@ -1472,6 +1540,18 @@ export type DialerPayload = {
1472
1540
  origin: string;
1473
1541
  };
1474
1542
 
1543
+ /**
1544
+ * Payload for campaign preview contact operations (accept, skip, remove)
1545
+ * @public
1546
+ */
1547
+ export type PreviewContactPayload = {
1548
+ /** The interaction ID from the campaign reservation */
1549
+ interactionId: string;
1550
+ /** The campaign name (not a UUID). Available from the reservation event at
1551
+ * `task.data.interaction.callProcessingDetails.campaignId` or `task.data.campaignId`. */
1552
+ campaignId: string;
1553
+ };
1554
+
1475
1555
  /**
1476
1556
  * Data structure for cleaning up contact resources
1477
1557
  * @public
package/src/types.ts CHANGED
@@ -297,6 +297,8 @@ interface IWebexInternal {
297
297
  get: (service: string) => string;
298
298
  /** Wait for service catalog to be loaded */
299
299
  waitForCatalog: (service: string) => Promise<void>;
300
+ /** Check if current environment is INT (integration) */
301
+ isIntegrationEnvironment: () => boolean;
300
302
  /** Host catalog for service discovery */
301
303
  _hostCatalog: Record<string, ServiceHost[]>;
302
304
  /** Service URLs cache */
@@ -541,7 +543,8 @@ export type RequestBody =
541
543
  | Contact.ConsultTransferPayLoad
542
544
  | Contact.cancelCtq
543
545
  | Contact.WrapupPayLoad
544
- | Contact.DialerPayload;
546
+ | Contact.DialerPayload
547
+ | Contact.PreviewContactPayload;
545
548
 
546
549
  /**
547
550
  * Represents the options to fetch buddy agents for the logged in agent.
@@ -15,7 +15,7 @@ import type {ContactServiceQueuesResponse} from '../../../src/types';
15
15
  import MockWebex from '@webex/test-helper-mock-webex';
16
16
  import {StationLoginSuccess, AGENT_EVENTS} from '../../../src/services/agent/types';
17
17
  import {SetStateResponse} from '../../../src/types';
18
- import {AGENT, WEB_RTC_PREFIX} from '../../../src/services/constants';
18
+ import {AGENT, SUBSCRIBE_API, WEB_RTC_PREFIX} from '../../../src/services/constants';
19
19
  import Services from '../../../src/services';
20
20
  import config from '../../../src/config';
21
21
  import {CC_EVENTS} from '../../../src/services/config/types';
@@ -89,6 +89,8 @@ describe('webex.cc', () => {
89
89
  initWebSocket: jest.fn(),
90
90
  on: jest.fn(),
91
91
  off: jest.fn(),
92
+ close: jest.fn(),
93
+ isSocketClosed: false,
92
94
  };
93
95
 
94
96
  mockContact = {
@@ -130,6 +132,13 @@ describe('webex.cc', () => {
130
132
  getOutdialAniEntries: jest.fn(),
131
133
  },
132
134
  webSocketManager: mockWebSocketManager,
135
+ rtdWebSocketManager: {
136
+ initWebSocket: jest.fn().mockResolvedValue({}),
137
+ on: jest.fn(),
138
+ off: jest.fn(),
139
+ close: jest.fn(),
140
+ isSocketClosed: false,
141
+ },
133
142
  connectionService: {
134
143
  on: jest.fn(),
135
144
  off: jest.fn(),
@@ -138,6 +147,13 @@ describe('webex.cc', () => {
138
147
 
139
148
  dialer: {
140
149
  startOutdial: jest.fn(),
150
+ acceptPreviewContact: jest.fn(),
151
+ skipPreviewContact: jest.fn(),
152
+ removePreviewContact: jest.fn(),
153
+ },
154
+ apiAIAssistant: {
155
+ sendEvent: jest.fn(),
156
+ fetchHistoricTranscripts: jest.fn(),
141
157
  },
142
158
  };
143
159
 
@@ -302,6 +318,7 @@ describe('webex.cc', () => {
302
318
  };
303
319
 
304
320
  it('should register successfully and return agent profile', async () => {
321
+ mockAgentProfile.aiFeature = {realtimeTranscripts: {enable: true}} as any;
305
322
  const mercuryConnect = jest.spyOn(webex.internal.mercury, 'connect').mockResolvedValue(true);
306
323
  const connectWebsocketSpy = jest.spyOn(webex.cc, 'connectWebsocket');
307
324
  const setupEventListenersSpy = jest.spyOn(webex.cc, 'setupEventListeners');
@@ -323,7 +340,7 @@ describe('webex.cc', () => {
323
340
  const result = await webex.cc.register();
324
341
 
325
342
  // Verify logging calls
326
- expect(LoggerProxy.info).toHaveBeenCalledWith('Starting CC SDK registration', {
343
+ expect(LoggerProxy.log).toHaveBeenCalledWith('Starting CC SDK registration', {
327
344
  module: CC_FILE,
328
345
  method: 'register',
329
346
  });
@@ -345,7 +362,7 @@ describe('webex.cc', () => {
345
362
  clientType: 'WebexCCSDK',
346
363
  allowMultiLogin: false,
347
364
  },
348
- resource: 'v1/notification/subscribe',
365
+ resource: SUBSCRIBE_API,
349
366
  });
350
367
 
351
368
  // TODO: https://jira-eng-gpk2.cisco.com/jira/browse/SPARK-626777 Implement the de-register method and close the listener there
@@ -362,6 +379,19 @@ describe('webex.cc', () => {
362
379
  expect.any(Function)
363
380
  );
364
381
  expect(mockWebSocketManager.on).toHaveBeenCalledWith('message', expect.any(Function));
382
+ expect(webex.cc.services.rtdWebSocketManager.initWebSocket).toHaveBeenCalledWith({
383
+ body: {
384
+ force: true,
385
+ isKeepAliveEnabled: false,
386
+ clientType: 'WebexCCSDK',
387
+ allowMultiLogin: false,
388
+ },
389
+ resource: 'v1/realtime/subscribe',
390
+ });
391
+ expect(webex.cc.services.rtdWebSocketManager.on).toHaveBeenCalledWith(
392
+ 'message',
393
+ expect.any(Function)
394
+ );
365
395
 
366
396
  expect(configSpy).toHaveBeenCalled();
367
397
  expect(LoggerProxy.log).toHaveBeenCalledWith('Agent config is fetched successfully', {
@@ -417,7 +447,7 @@ describe('webex.cc', () => {
417
447
  clientType: 'WebexCCSDK',
418
448
  allowMultiLogin: true,
419
449
  },
420
- resource: 'v1/notification/subscribe',
450
+ resource: SUBSCRIBE_API,
421
451
  });
422
452
  expect(configSpy).toHaveBeenCalled();
423
453
  expect(LoggerProxy.log).toHaveBeenCalledWith('Agent config is fetched successfully', {
@@ -439,7 +469,7 @@ describe('webex.cc', () => {
439
469
 
440
470
  await expect(webex.cc.register()).rejects.toThrow('Error while performing register');
441
471
 
442
- expect(LoggerProxy.info).toHaveBeenCalledWith('Starting CC SDK registration', {
472
+ expect(LoggerProxy.log).toHaveBeenCalledWith('Starting CC SDK registration', {
443
473
  module: CC_FILE,
444
474
  method: 'register',
445
475
  });
@@ -497,7 +527,7 @@ describe('webex.cc', () => {
497
527
  clientType: 'WebexCCSDK',
498
528
  allowMultiLogin: false,
499
529
  },
500
- resource: 'v1/notification/subscribe',
530
+ resource: SUBSCRIBE_API,
501
531
  });
502
532
 
503
533
  expect(mockTaskManager.on).toHaveBeenCalledWith(
@@ -555,6 +585,7 @@ describe('webex.cc', () => {
555
585
 
556
586
  it('should not attempt for mercury connection when webrtc is disabled', async () => {
557
587
  mockAgentProfile.webRtcEnabled = false;
588
+ mockAgentProfile.aiFeature = {realtimeTranscripts: {enable: false}} as any;
558
589
  const mercurySpy = jest.spyOn(webex.internal.mercury, 'connect');
559
590
  const connectWebsocketSpy = jest.spyOn(webex.cc, 'connectWebsocket');
560
591
  const setupEventListenersSpy = jest.spyOn(webex.cc, 'setupEventListeners');
@@ -584,13 +615,39 @@ describe('webex.cc', () => {
584
615
  clientType: 'WebexCCSDK',
585
616
  allowMultiLogin: false,
586
617
  },
587
- resource: 'v1/notification/subscribe',
618
+ resource: SUBSCRIBE_API,
588
619
  });
589
620
 
590
621
  expect(configSpy).toHaveBeenCalled();
591
622
  expect(mercurySpy).not.toHaveBeenCalled();
623
+ expect(webex.cc.services.rtdWebSocketManager.initWebSocket).not.toHaveBeenCalled();
592
624
  expect(result).toEqual(mockAgentProfile);
593
625
  });
626
+
627
+ it('should not connect RTD websocket when realtime transcripts feature is disabled', async () => {
628
+ mockAgentProfile.aiFeature = {realtimeTranscripts: {enable: false}} as any;
629
+ jest.spyOn(webex.internal.mercury, 'connect').mockResolvedValue(true);
630
+ jest.spyOn(webex.cc.services.agent, 'reload').mockResolvedValue({
631
+ data: {
632
+ auxCodeId: 'auxCodeId',
633
+ agentId: 'agentId',
634
+ deviceType: LoginOption.EXTENSION,
635
+ dn: '12345',
636
+ },
637
+ });
638
+ jest.spyOn(webex.cc.services.config, 'getAgentConfig').mockResolvedValue(mockAgentProfile);
639
+ mockWebSocketManager.initWebSocket.mockResolvedValue({
640
+ agentId: 'agent123',
641
+ });
642
+
643
+ await webex.cc.register();
644
+
645
+ expect(webex.cc.services.rtdWebSocketManager.initWebSocket).not.toHaveBeenCalled();
646
+ expect(webex.cc.services.rtdWebSocketManager.on).not.toHaveBeenCalledWith(
647
+ 'message',
648
+ expect.any(Function)
649
+ );
650
+ });
594
651
  });
595
652
 
596
653
  describe('stationLogin', () => {
@@ -694,10 +751,8 @@ describe('webex.cc', () => {
694
751
 
695
752
  expect(emitSpy).toHaveBeenCalledTimes(1);
696
753
  expect(emitSpy).toHaveBeenCalledWith(TASK_EVENTS.TASK_INCOMING, mockTask);
697
- // Verify message event listener
698
- const messageCallback = mockWebSocketManager.on.mock.calls.find(
699
- (call) => call[0] === 'message'
700
- )[1];
754
+ // Verify websocket message handling
755
+ const messageCallback = webex.cc['handleWebsocketMessage'];
701
756
  const agentStateChangeEventData = {
702
757
  type: CC_EVENTS.AGENT_STATE_CHANGE,
703
758
  data: {some: 'data'},
@@ -900,10 +955,13 @@ describe('webex.cc', () => {
900
955
  const result = await webex.cc.stationLogin(options);
901
956
 
902
957
  // Verify logging calls
903
- expect(LoggerProxy.info).toHaveBeenCalledWith('Starting agent station login', {
904
- module: CC_FILE,
905
- method: 'stationLogin',
906
- });
958
+ expect(LoggerProxy.log).toHaveBeenCalledWith(
959
+ `Starting agent station login | loginOption: ${options.loginOption} teamId: ${options.teamId}`,
960
+ {
961
+ module: CC_FILE,
962
+ method: 'stationLogin',
963
+ }
964
+ );
907
965
  expect(LoggerProxy.log).toHaveBeenCalledWith(
908
966
  `Agent station login completed successfully agentId: ${mockData.data.agentId} loginOption: ${mockData.data.loginOption} teamId: ${mockData.data.teamId}`,
909
967
  {
@@ -954,10 +1012,13 @@ describe('webex.cc', () => {
954
1012
 
955
1013
  await expect(webex.cc.stationLogin(options)).rejects.toThrow(error.details.data.reason);
956
1014
 
957
- expect(LoggerProxy.info).toHaveBeenCalledWith('Starting agent station login', {
958
- module: CC_FILE,
959
- method: 'stationLogin',
960
- });
1015
+ expect(LoggerProxy.log).toHaveBeenCalledWith(
1016
+ `Starting agent station login | loginOption: ${options.loginOption} teamId: ${options.teamId}`,
1017
+ {
1018
+ module: CC_FILE,
1019
+ method: 'stationLogin',
1020
+ }
1021
+ );
961
1022
  expect(LoggerProxy.error).toHaveBeenCalledWith(
962
1023
  `stationLogin failed with reason: ${error.details.data.reason}`,
963
1024
  {module: CC_FILE, method: 'stationLogin', trackingId: error.details.trackingId}
@@ -1296,11 +1357,11 @@ describe('webex.cc', () => {
1296
1357
  const webSocketManagerOnSpy = jest.spyOn(webex.cc.services.webSocketManager, 'on');
1297
1358
  await webex.cc['silentRelogin']();
1298
1359
 
1299
- expect(LoggerProxy.info).toHaveBeenCalledWith('Starting silent relogin process', {
1360
+ expect(LoggerProxy.log).toHaveBeenCalledWith('Starting silent relogin process', {
1300
1361
  module: CC_FILE,
1301
1362
  method: 'silentRelogin',
1302
1363
  });
1303
- expect(LoggerProxy.info).toHaveBeenCalledWith(
1364
+ expect(LoggerProxy.log).toHaveBeenCalledWith(
1304
1365
  'event=requestAutoStateChange | Requesting state change to available on socket reconnect',
1305
1366
  {module: CC_FILE, method: 'silentRelogin'}
1306
1367
  );
@@ -1345,7 +1406,7 @@ describe('webex.cc', () => {
1345
1406
 
1346
1407
  jest.spyOn(webex.cc.services.agent, 'reload').mockRejectedValue(error);
1347
1408
  await webex.cc['silentRelogin']();
1348
- expect(LoggerProxy.info).toHaveBeenCalledWith('Starting silent relogin process', {
1409
+ expect(LoggerProxy.log).toHaveBeenCalledWith('Starting silent relogin process', {
1349
1410
  module: CC_FILE,
1350
1411
  method: 'silentRelogin',
1351
1412
  });
@@ -1360,7 +1421,7 @@ describe('webex.cc', () => {
1360
1421
  jest.spyOn(webex.cc.services.agent, 'reload').mockRejectedValue(error);
1361
1422
 
1362
1423
  await expect(webex.cc['silentRelogin']()).rejects.toThrow(error);
1363
- expect(LoggerProxy.info).toHaveBeenCalledWith('Starting silent relogin process', {
1424
+ expect(LoggerProxy.log).toHaveBeenCalledWith('Starting silent relogin process', {
1364
1425
  module: CC_FILE,
1365
1426
  method: 'silentRelogin',
1366
1427
  });
@@ -1402,7 +1463,7 @@ describe('webex.cc', () => {
1402
1463
 
1403
1464
  await webex.cc['silentRelogin']();
1404
1465
 
1405
- expect(LoggerProxy.info).toHaveBeenCalledWith('Starting silent relogin process', {
1466
+ expect(LoggerProxy.log).toHaveBeenCalledWith('Starting silent relogin process', {
1406
1467
  module: CC_FILE,
1407
1468
  method: 'silentRelogin',
1408
1469
  });
@@ -1678,6 +1739,13 @@ describe('webex.cc', () => {
1678
1739
  });
1679
1740
 
1680
1741
  it('should unregister successfully and clean up all resources when webrtc is enabled', async () => {
1742
+ webex.cc.services.rtdWebSocketManager = {
1743
+ isSocketClosed: false,
1744
+ close: jest.fn(),
1745
+ off: jest.fn(),
1746
+ on: jest.fn(),
1747
+ } as any;
1748
+
1681
1749
  await webex.cc.deregister();
1682
1750
 
1683
1751
  expect(mockTaskManager.off).toHaveBeenCalledWith(
@@ -1693,13 +1761,20 @@ describe('webex.cc', () => {
1693
1761
  expect.any(Function)
1694
1762
  );
1695
1763
  expect(mockWebSocketManager.off).toHaveBeenCalledWith('message', expect.any(Function));
1764
+ expect(webex.cc.services.rtdWebSocketManager.off).toHaveBeenCalledWith(
1765
+ 'message',
1766
+ expect.any(Function)
1767
+ );
1696
1768
  expect(webex.cc.services.connectionService.off).toHaveBeenCalledWith(
1697
1769
  'connectionLost',
1698
1770
  expect.any(Function)
1699
1771
  );
1700
1772
 
1701
1773
  expect(mockWebSocketManager.close).toHaveBeenCalledWith(false, 'Unregistering the SDK');
1702
- expect(mockRTDWebSocketManager.close).toHaveBeenCalledWith(false, 'Unregistering the SDK');
1774
+ expect(webex.cc.services.rtdWebSocketManager.close).toHaveBeenCalledWith(
1775
+ false,
1776
+ 'Unregistering the RTD websocket'
1777
+ );
1703
1778
  expect(webex.cc.agentConfig).toBeNull();
1704
1779
 
1705
1780
  expect(webex.internal.mercury.off).toHaveBeenCalledWith('online');
@@ -1848,7 +1923,7 @@ describe('webex.cc', () => {
1848
1923
 
1849
1924
  beforeEach(() => {
1850
1925
  emitSpy = jest.spyOn(webex.cc, 'emit');
1851
- messageCallback = mockWebSocketManager.on.mock.calls.find((c) => c[0] === 'message')[1];
1926
+ messageCallback = webex.cc['handleWebsocketMessage'];
1852
1927
  });
1853
1928
 
1854
1929
  it('should emit AGENT_STATION_LOGIN_SUCCESS on CC_EVENTS.AGENT_STATION_LOGIN_SUCCESS with mapped payload', () => {
@@ -2378,4 +2453,196 @@ describe('webex.cc', () => {
2378
2453
  );
2379
2454
  });
2380
2455
  });
2456
+
2457
+ describe('acceptPreviewContact', () => {
2458
+ const previewPayload = {
2459
+ interactionId: 'interaction-123',
2460
+ campaignId: 'campaign-456',
2461
+ };
2462
+
2463
+ it('should accept preview contact successfully', async () => {
2464
+ const mockResponse = {trackingId: 'track-123'} as AgentContact;
2465
+
2466
+ const acceptPreviewContactMock = jest
2467
+ .spyOn(webex.cc.services.dialer, 'acceptPreviewContact')
2468
+ .mockResolvedValue(mockResponse);
2469
+
2470
+ const result = await webex.cc.acceptPreviewContact(previewPayload);
2471
+
2472
+ expect(LoggerProxy.info).toHaveBeenCalledWith('Accepting campaign preview contact', {
2473
+ module: CC_FILE,
2474
+ method: 'acceptPreviewContact',
2475
+ });
2476
+ expect(LoggerProxy.log).toHaveBeenCalledWith(
2477
+ 'Campaign preview contact accepted successfully',
2478
+ {
2479
+ module: CC_FILE,
2480
+ method: 'acceptPreviewContact',
2481
+ trackingId: 'track-123',
2482
+ interactionId: previewPayload.interactionId,
2483
+ }
2484
+ );
2485
+
2486
+ expect(acceptPreviewContactMock).toHaveBeenCalledWith({data: previewPayload});
2487
+ expect(result).toEqual(mockResponse);
2488
+ });
2489
+
2490
+ it('should handle error during acceptPreviewContact', async () => {
2491
+ getErrorDetailsSpy.mockRestore();
2492
+ getErrorDetailsSpy = jest.spyOn(Utils, 'getErrorDetails');
2493
+
2494
+ const error = {
2495
+ details: {
2496
+ trackingId: '1234',
2497
+ data: {
2498
+ reason: 'Error while performing acceptPreviewContact',
2499
+ },
2500
+ },
2501
+ };
2502
+
2503
+ jest.spyOn(webex.cc.services.dialer, 'acceptPreviewContact').mockRejectedValue(error);
2504
+
2505
+ await expect(webex.cc.acceptPreviewContact(previewPayload)).rejects.toThrow(
2506
+ error.details.data.reason
2507
+ );
2508
+
2509
+ expect(LoggerProxy.info).toHaveBeenCalledWith('Accepting campaign preview contact', {
2510
+ module: CC_FILE,
2511
+ method: 'acceptPreviewContact',
2512
+ });
2513
+ expect(LoggerProxy.error).toHaveBeenCalledWith(
2514
+ `acceptPreviewContact failed with reason: ${error.details.data.reason}`,
2515
+ {module: CC_FILE, method: 'acceptPreviewContact', trackingId: error.details.trackingId}
2516
+ );
2517
+ expect(getErrorDetailsSpy).toHaveBeenCalledWith(error, 'acceptPreviewContact', CC_FILE);
2518
+ });
2519
+ });
2520
+
2521
+ describe('skipPreviewContact', () => {
2522
+ const previewPayload = {
2523
+ interactionId: 'interaction-123',
2524
+ campaignId: 'campaign-456',
2525
+ };
2526
+
2527
+ it('should skip preview contact successfully', async () => {
2528
+ const mockResponse = {trackingId: 'track-123'} as AgentContact;
2529
+
2530
+ const skipPreviewContactMock = jest
2531
+ .spyOn(webex.cc.services.dialer, 'skipPreviewContact')
2532
+ .mockResolvedValue(mockResponse);
2533
+
2534
+ const result = await webex.cc.skipPreviewContact(previewPayload);
2535
+
2536
+ expect(LoggerProxy.info).toHaveBeenCalledWith('Skipping campaign preview contact', {
2537
+ module: CC_FILE,
2538
+ method: 'skipPreviewContact',
2539
+ });
2540
+ expect(LoggerProxy.log).toHaveBeenCalledWith(
2541
+ 'Campaign preview contact skipped successfully',
2542
+ {
2543
+ module: CC_FILE,
2544
+ method: 'skipPreviewContact',
2545
+ trackingId: 'track-123',
2546
+ interactionId: previewPayload.interactionId,
2547
+ }
2548
+ );
2549
+
2550
+ expect(skipPreviewContactMock).toHaveBeenCalledWith({data: previewPayload});
2551
+ expect(result).toEqual(mockResponse);
2552
+ });
2553
+
2554
+ it('should handle error during skipPreviewContact', async () => {
2555
+ getErrorDetailsSpy.mockRestore();
2556
+ getErrorDetailsSpy = jest.spyOn(Utils, 'getErrorDetails');
2557
+
2558
+ const error = {
2559
+ details: {
2560
+ trackingId: '1234',
2561
+ data: {
2562
+ reason: 'Error while performing skipPreviewContact',
2563
+ },
2564
+ },
2565
+ };
2566
+
2567
+ jest.spyOn(webex.cc.services.dialer, 'skipPreviewContact').mockRejectedValue(error);
2568
+
2569
+ await expect(webex.cc.skipPreviewContact(previewPayload)).rejects.toThrow(
2570
+ error.details.data.reason
2571
+ );
2572
+
2573
+ expect(LoggerProxy.info).toHaveBeenCalledWith('Skipping campaign preview contact', {
2574
+ module: CC_FILE,
2575
+ method: 'skipPreviewContact',
2576
+ });
2577
+ expect(LoggerProxy.error).toHaveBeenCalledWith(
2578
+ `skipPreviewContact failed with reason: ${error.details.data.reason}`,
2579
+ {module: CC_FILE, method: 'skipPreviewContact', trackingId: error.details.trackingId}
2580
+ );
2581
+ expect(getErrorDetailsSpy).toHaveBeenCalledWith(error, 'skipPreviewContact', CC_FILE);
2582
+ });
2583
+ });
2584
+
2585
+ describe('removePreviewContact', () => {
2586
+ const previewPayload = {
2587
+ interactionId: 'interaction-123',
2588
+ campaignId: 'campaign-456',
2589
+ };
2590
+
2591
+ it('should remove preview contact successfully', async () => {
2592
+ const mockResponse = {trackingId: 'track-123'} as AgentContact;
2593
+
2594
+ const removePreviewContactMock = jest
2595
+ .spyOn(webex.cc.services.dialer, 'removePreviewContact')
2596
+ .mockResolvedValue(mockResponse);
2597
+
2598
+ const result = await webex.cc.removePreviewContact(previewPayload);
2599
+
2600
+ expect(LoggerProxy.info).toHaveBeenCalledWith('Removing campaign preview contact', {
2601
+ module: CC_FILE,
2602
+ method: 'removePreviewContact',
2603
+ });
2604
+ expect(LoggerProxy.log).toHaveBeenCalledWith(
2605
+ 'Campaign preview contact removed successfully',
2606
+ {
2607
+ module: CC_FILE,
2608
+ method: 'removePreviewContact',
2609
+ trackingId: 'track-123',
2610
+ interactionId: previewPayload.interactionId,
2611
+ }
2612
+ );
2613
+
2614
+ expect(removePreviewContactMock).toHaveBeenCalledWith({data: previewPayload});
2615
+ expect(result).toEqual(mockResponse);
2616
+ });
2617
+
2618
+ it('should handle error during removePreviewContact', async () => {
2619
+ getErrorDetailsSpy.mockRestore();
2620
+ getErrorDetailsSpy = jest.spyOn(Utils, 'getErrorDetails');
2621
+
2622
+ const error = {
2623
+ details: {
2624
+ trackingId: '1234',
2625
+ data: {
2626
+ reason: 'Error while performing removePreviewContact',
2627
+ },
2628
+ },
2629
+ };
2630
+
2631
+ jest.spyOn(webex.cc.services.dialer, 'removePreviewContact').mockRejectedValue(error);
2632
+
2633
+ await expect(webex.cc.removePreviewContact(previewPayload)).rejects.toThrow(
2634
+ error.details.data.reason
2635
+ );
2636
+
2637
+ expect(LoggerProxy.info).toHaveBeenCalledWith('Removing campaign preview contact', {
2638
+ module: CC_FILE,
2639
+ method: 'removePreviewContact',
2640
+ });
2641
+ expect(LoggerProxy.error).toHaveBeenCalledWith(
2642
+ `removePreviewContact failed with reason: ${error.details.data.reason}`,
2643
+ {module: CC_FILE, method: 'removePreviewContact', trackingId: error.details.trackingId}
2644
+ );
2645
+ expect(getErrorDetailsSpy).toHaveBeenCalledWith(error, 'removePreviewContact', CC_FILE);
2646
+ });
2647
+ });
2381
2648
  });
@@ -746,7 +746,7 @@ describe('AgentConfigService', () => {
746
746
  agentProfileId: 'profile123',
747
747
  siteId: 'site789',
748
748
  dbId: 'db123',
749
- defaultDialledNumber: '1234567890',
749
+ deafultDialledNumber: '1234567890',
750
750
  id: 'user001',
751
751
  teamIds: ['team1', 'team2'],
752
752
  };
@@ -775,6 +775,17 @@ describe('AgentConfigService', () => {
775
775
 
776
776
  const mockDialPlanData = [];
777
777
 
778
+ const mockTeamData = [
779
+ {id: 'team1', name: 'Support Team'},
780
+ {id: 'team2', name: 'Sales Team'},
781
+ ];
782
+
783
+ const mockOrgInfo = {
784
+ tenantId: 'tenant123',
785
+ timezone: 'GMT',
786
+ environment: 'produs1',
787
+ };
788
+
778
789
  const mockOrgSettings = {
779
790
  campaignManagerEnabled: true,
780
791
  webRtcEnabled: true,
@@ -798,6 +809,9 @@ describe('AgentConfigService', () => {
798
809
  {id: 'aux1', type: 'WRAP_UP_CODE', name: 'Wrap Up Code 1', isDefault: true},
799
810
  {id: 'aux2', type: 'IDLE_CODE', name: 'Idle Code 1', isDefault: true},
800
811
  ];
812
+ const mockAIFeatureFlags = {
813
+ data: [{realtimeTranscripts: {enable: true}}],
814
+ };
801
815
 
802
816
  const parseAgentConfigsSpy = jest.spyOn(util, 'parseAgentConfigs');
803
817
  agentConfigService.getUserUsingCI = jest.fn().mockResolvedValue(mockUserConfig);
@@ -861,7 +875,7 @@ describe('AgentConfigService', () => {
861
875
  skillProfileId: 'skillProfile456',
862
876
  siteId: 'site789',
863
877
  dbId: 'db123',
864
- defaultDialledNumber: '1234567890',
878
+ deafultDialledNumber: '1234567890',
865
879
  id: 'user001',
866
880
  teamIds: ['team1', 'team2'],
867
881
  };
@@ -895,6 +909,17 @@ describe('AgentConfigService', () => {
895
909
  {id: 'dialPlan2', name: 'Plan 2'},
896
910
  ];
897
911
 
912
+ const mockTeamData = [
913
+ {id: 'team1', name: 'Support Team'},
914
+ {id: 'team2', name: 'Sales Team'},
915
+ ];
916
+
917
+ const mockOrgInfo = {
918
+ tenantId: 'tenant123',
919
+ timezone: 'GMT',
920
+ environment: 'produs1',
921
+ };
922
+
898
923
  const mockOrgSettings = {
899
924
  campaignManagerEnabled: true,
900
925
  webRtcEnabled: true,
@@ -919,6 +944,9 @@ describe('AgentConfigService', () => {
919
944
  {id: 'aux1', type: 'WRAP_UP_CODE', name: 'Wrap Up Code 1'},
920
945
  {id: 'aux2', type: 'IDLE_CODE', name: 'Idle Code 1'},
921
946
  ];
947
+ const mockAIFeatureFlags = {
948
+ data: [{realtimeTranscripts: {enable: true}}],
949
+ };
922
950
 
923
951
  const parseAgentConfigsSpy = jest.spyOn(util, 'parseAgentConfigs');
924
952
  agentConfigService.getUserUsingCI = jest.fn().mockResolvedValue(mockUserConfig);