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

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 +294 -32
  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 +91 -1
  97. package/test/unit/spec/services/core/websocket/connection-service.ts +4 -5
  98. package/test/unit/spec/services/task/TaskManager.ts +4 -2
  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', {
@@ -373,12 +403,7 @@ describe('webex.cc', () => {
373
403
  isEndConsultEnabled: mockAgentProfile.isEndConsultEnabled,
374
404
  webRtcEnabled: mockAgentProfile.webRtcEnabled,
375
405
  autoWrapup: mockAgentProfile.wrapUpData.wrapUpProps.autoWrapup ?? false,
376
- });
377
- expect(mockTaskManager.setConfigFlags).toHaveBeenCalledWith({
378
- isEndTaskEnabled: mockAgentProfile.isEndTaskEnabled,
379
- isEndConsultEnabled: mockAgentProfile.isEndConsultEnabled,
380
- webRtcEnabled: mockAgentProfile.webRtcEnabled,
381
- autoWrapup: mockAgentProfile.wrapUpData.wrapUpProps.autoWrapup ?? false,
406
+ aiFeature: mockAgentProfile.aiFeature,
382
407
  });
383
408
  expect(reloadSpy).toHaveBeenCalled();
384
409
  expect(result).toEqual(mockAgentProfile);
@@ -417,7 +442,7 @@ describe('webex.cc', () => {
417
442
  clientType: 'WebexCCSDK',
418
443
  allowMultiLogin: true,
419
444
  },
420
- resource: 'v1/notification/subscribe',
445
+ resource: SUBSCRIBE_API,
421
446
  });
422
447
  expect(configSpy).toHaveBeenCalled();
423
448
  expect(LoggerProxy.log).toHaveBeenCalledWith('Agent config is fetched successfully', {
@@ -439,7 +464,7 @@ describe('webex.cc', () => {
439
464
 
440
465
  await expect(webex.cc.register()).rejects.toThrow('Error while performing register');
441
466
 
442
- expect(LoggerProxy.info).toHaveBeenCalledWith('Starting CC SDK registration', {
467
+ expect(LoggerProxy.log).toHaveBeenCalledWith('Starting CC SDK registration', {
443
468
  module: CC_FILE,
444
469
  method: 'register',
445
470
  });
@@ -497,7 +522,7 @@ describe('webex.cc', () => {
497
522
  clientType: 'WebexCCSDK',
498
523
  allowMultiLogin: false,
499
524
  },
500
- resource: 'v1/notification/subscribe',
525
+ resource: SUBSCRIBE_API,
501
526
  });
502
527
 
503
528
  expect(mockTaskManager.on).toHaveBeenCalledWith(
@@ -555,6 +580,7 @@ describe('webex.cc', () => {
555
580
 
556
581
  it('should not attempt for mercury connection when webrtc is disabled', async () => {
557
582
  mockAgentProfile.webRtcEnabled = false;
583
+ mockAgentProfile.aiFeature = {realtimeTranscripts: {enable: false}} as any;
558
584
  const mercurySpy = jest.spyOn(webex.internal.mercury, 'connect');
559
585
  const connectWebsocketSpy = jest.spyOn(webex.cc, 'connectWebsocket');
560
586
  const setupEventListenersSpy = jest.spyOn(webex.cc, 'setupEventListeners');
@@ -584,13 +610,39 @@ describe('webex.cc', () => {
584
610
  clientType: 'WebexCCSDK',
585
611
  allowMultiLogin: false,
586
612
  },
587
- resource: 'v1/notification/subscribe',
613
+ resource: SUBSCRIBE_API,
588
614
  });
589
615
 
590
616
  expect(configSpy).toHaveBeenCalled();
591
617
  expect(mercurySpy).not.toHaveBeenCalled();
618
+ expect(webex.cc.services.rtdWebSocketManager.initWebSocket).not.toHaveBeenCalled();
592
619
  expect(result).toEqual(mockAgentProfile);
593
620
  });
621
+
622
+ it('should not connect RTD websocket when realtime transcripts feature is disabled', async () => {
623
+ mockAgentProfile.aiFeature = {realtimeTranscripts: {enable: false}} as any;
624
+ jest.spyOn(webex.internal.mercury, 'connect').mockResolvedValue(true);
625
+ jest.spyOn(webex.cc.services.agent, 'reload').mockResolvedValue({
626
+ data: {
627
+ auxCodeId: 'auxCodeId',
628
+ agentId: 'agentId',
629
+ deviceType: LoginOption.EXTENSION,
630
+ dn: '12345',
631
+ },
632
+ });
633
+ jest.spyOn(webex.cc.services.config, 'getAgentConfig').mockResolvedValue(mockAgentProfile);
634
+ mockWebSocketManager.initWebSocket.mockResolvedValue({
635
+ agentId: 'agent123',
636
+ });
637
+
638
+ await webex.cc.register();
639
+
640
+ expect(webex.cc.services.rtdWebSocketManager.initWebSocket).not.toHaveBeenCalled();
641
+ expect(webex.cc.services.rtdWebSocketManager.on).not.toHaveBeenCalledWith(
642
+ 'message',
643
+ expect.any(Function)
644
+ );
645
+ });
594
646
  });
595
647
 
596
648
  describe('stationLogin', () => {
@@ -694,10 +746,8 @@ describe('webex.cc', () => {
694
746
 
695
747
  expect(emitSpy).toHaveBeenCalledTimes(1);
696
748
  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];
749
+ // Verify websocket message handling
750
+ const messageCallback = webex.cc['handleWebsocketMessage'];
701
751
  const agentStateChangeEventData = {
702
752
  type: CC_EVENTS.AGENT_STATE_CHANGE,
703
753
  data: {some: 'data'},
@@ -900,10 +950,13 @@ describe('webex.cc', () => {
900
950
  const result = await webex.cc.stationLogin(options);
901
951
 
902
952
  // Verify logging calls
903
- expect(LoggerProxy.info).toHaveBeenCalledWith('Starting agent station login', {
904
- module: CC_FILE,
905
- method: 'stationLogin',
906
- });
953
+ expect(LoggerProxy.log).toHaveBeenCalledWith(
954
+ `Starting agent station login | loginOption: ${options.loginOption} teamId: ${options.teamId}`,
955
+ {
956
+ module: CC_FILE,
957
+ method: 'stationLogin',
958
+ }
959
+ );
907
960
  expect(LoggerProxy.log).toHaveBeenCalledWith(
908
961
  `Agent station login completed successfully agentId: ${mockData.data.agentId} loginOption: ${mockData.data.loginOption} teamId: ${mockData.data.teamId}`,
909
962
  {
@@ -954,10 +1007,13 @@ describe('webex.cc', () => {
954
1007
 
955
1008
  await expect(webex.cc.stationLogin(options)).rejects.toThrow(error.details.data.reason);
956
1009
 
957
- expect(LoggerProxy.info).toHaveBeenCalledWith('Starting agent station login', {
958
- module: CC_FILE,
959
- method: 'stationLogin',
960
- });
1010
+ expect(LoggerProxy.log).toHaveBeenCalledWith(
1011
+ `Starting agent station login | loginOption: ${options.loginOption} teamId: ${options.teamId}`,
1012
+ {
1013
+ module: CC_FILE,
1014
+ method: 'stationLogin',
1015
+ }
1016
+ );
961
1017
  expect(LoggerProxy.error).toHaveBeenCalledWith(
962
1018
  `stationLogin failed with reason: ${error.details.data.reason}`,
963
1019
  {module: CC_FILE, method: 'stationLogin', trackingId: error.details.trackingId}
@@ -1296,11 +1352,11 @@ describe('webex.cc', () => {
1296
1352
  const webSocketManagerOnSpy = jest.spyOn(webex.cc.services.webSocketManager, 'on');
1297
1353
  await webex.cc['silentRelogin']();
1298
1354
 
1299
- expect(LoggerProxy.info).toHaveBeenCalledWith('Starting silent relogin process', {
1355
+ expect(LoggerProxy.log).toHaveBeenCalledWith('Starting silent relogin process', {
1300
1356
  module: CC_FILE,
1301
1357
  method: 'silentRelogin',
1302
1358
  });
1303
- expect(LoggerProxy.info).toHaveBeenCalledWith(
1359
+ expect(LoggerProxy.log).toHaveBeenCalledWith(
1304
1360
  'event=requestAutoStateChange | Requesting state change to available on socket reconnect',
1305
1361
  {module: CC_FILE, method: 'silentRelogin'}
1306
1362
  );
@@ -1345,7 +1401,7 @@ describe('webex.cc', () => {
1345
1401
 
1346
1402
  jest.spyOn(webex.cc.services.agent, 'reload').mockRejectedValue(error);
1347
1403
  await webex.cc['silentRelogin']();
1348
- expect(LoggerProxy.info).toHaveBeenCalledWith('Starting silent relogin process', {
1404
+ expect(LoggerProxy.log).toHaveBeenCalledWith('Starting silent relogin process', {
1349
1405
  module: CC_FILE,
1350
1406
  method: 'silentRelogin',
1351
1407
  });
@@ -1360,7 +1416,7 @@ describe('webex.cc', () => {
1360
1416
  jest.spyOn(webex.cc.services.agent, 'reload').mockRejectedValue(error);
1361
1417
 
1362
1418
  await expect(webex.cc['silentRelogin']()).rejects.toThrow(error);
1363
- expect(LoggerProxy.info).toHaveBeenCalledWith('Starting silent relogin process', {
1419
+ expect(LoggerProxy.log).toHaveBeenCalledWith('Starting silent relogin process', {
1364
1420
  module: CC_FILE,
1365
1421
  method: 'silentRelogin',
1366
1422
  });
@@ -1402,7 +1458,7 @@ describe('webex.cc', () => {
1402
1458
 
1403
1459
  await webex.cc['silentRelogin']();
1404
1460
 
1405
- expect(LoggerProxy.info).toHaveBeenCalledWith('Starting silent relogin process', {
1461
+ expect(LoggerProxy.log).toHaveBeenCalledWith('Starting silent relogin process', {
1406
1462
  module: CC_FILE,
1407
1463
  method: 'silentRelogin',
1408
1464
  });
@@ -1678,6 +1734,13 @@ describe('webex.cc', () => {
1678
1734
  });
1679
1735
 
1680
1736
  it('should unregister successfully and clean up all resources when webrtc is enabled', async () => {
1737
+ webex.cc.services.rtdWebSocketManager = {
1738
+ isSocketClosed: false,
1739
+ close: jest.fn(),
1740
+ off: jest.fn(),
1741
+ on: jest.fn(),
1742
+ } as any;
1743
+
1681
1744
  await webex.cc.deregister();
1682
1745
 
1683
1746
  expect(mockTaskManager.off).toHaveBeenCalledWith(
@@ -1693,13 +1756,20 @@ describe('webex.cc', () => {
1693
1756
  expect.any(Function)
1694
1757
  );
1695
1758
  expect(mockWebSocketManager.off).toHaveBeenCalledWith('message', expect.any(Function));
1759
+ expect(webex.cc.services.rtdWebSocketManager.off).toHaveBeenCalledWith(
1760
+ 'message',
1761
+ expect.any(Function)
1762
+ );
1696
1763
  expect(webex.cc.services.connectionService.off).toHaveBeenCalledWith(
1697
1764
  'connectionLost',
1698
1765
  expect.any(Function)
1699
1766
  );
1700
1767
 
1701
1768
  expect(mockWebSocketManager.close).toHaveBeenCalledWith(false, 'Unregistering the SDK');
1702
- expect(mockRTDWebSocketManager.close).toHaveBeenCalledWith(false, 'Unregistering the SDK');
1769
+ expect(webex.cc.services.rtdWebSocketManager.close).toHaveBeenCalledWith(
1770
+ false,
1771
+ 'Unregistering the RTD websocket'
1772
+ );
1703
1773
  expect(webex.cc.agentConfig).toBeNull();
1704
1774
 
1705
1775
  expect(webex.internal.mercury.off).toHaveBeenCalledWith('online');
@@ -1848,7 +1918,7 @@ describe('webex.cc', () => {
1848
1918
 
1849
1919
  beforeEach(() => {
1850
1920
  emitSpy = jest.spyOn(webex.cc, 'emit');
1851
- messageCallback = mockWebSocketManager.on.mock.calls.find((c) => c[0] === 'message')[1];
1921
+ messageCallback = webex.cc['handleWebsocketMessage'];
1852
1922
  });
1853
1923
 
1854
1924
  it('should emit AGENT_STATION_LOGIN_SUCCESS on CC_EVENTS.AGENT_STATION_LOGIN_SUCCESS with mapped payload', () => {
@@ -2378,4 +2448,196 @@ describe('webex.cc', () => {
2378
2448
  );
2379
2449
  });
2380
2450
  });
2451
+
2452
+ describe('acceptPreviewContact', () => {
2453
+ const previewPayload = {
2454
+ interactionId: 'interaction-123',
2455
+ campaignId: 'campaign-456',
2456
+ };
2457
+
2458
+ it('should accept preview contact successfully', async () => {
2459
+ const mockResponse = {trackingId: 'track-123'} as AgentContact;
2460
+
2461
+ const acceptPreviewContactMock = jest
2462
+ .spyOn(webex.cc.services.dialer, 'acceptPreviewContact')
2463
+ .mockResolvedValue(mockResponse);
2464
+
2465
+ const result = await webex.cc.acceptPreviewContact(previewPayload);
2466
+
2467
+ expect(LoggerProxy.info).toHaveBeenCalledWith('Accepting campaign preview contact', {
2468
+ module: CC_FILE,
2469
+ method: 'acceptPreviewContact',
2470
+ });
2471
+ expect(LoggerProxy.log).toHaveBeenCalledWith(
2472
+ 'Campaign preview contact accepted successfully',
2473
+ {
2474
+ module: CC_FILE,
2475
+ method: 'acceptPreviewContact',
2476
+ trackingId: 'track-123',
2477
+ interactionId: previewPayload.interactionId,
2478
+ }
2479
+ );
2480
+
2481
+ expect(acceptPreviewContactMock).toHaveBeenCalledWith({data: previewPayload});
2482
+ expect(result).toEqual(mockResponse);
2483
+ });
2484
+
2485
+ it('should handle error during acceptPreviewContact', async () => {
2486
+ getErrorDetailsSpy.mockRestore();
2487
+ getErrorDetailsSpy = jest.spyOn(Utils, 'getErrorDetails');
2488
+
2489
+ const error = {
2490
+ details: {
2491
+ trackingId: '1234',
2492
+ data: {
2493
+ reason: 'Error while performing acceptPreviewContact',
2494
+ },
2495
+ },
2496
+ };
2497
+
2498
+ jest.spyOn(webex.cc.services.dialer, 'acceptPreviewContact').mockRejectedValue(error);
2499
+
2500
+ await expect(webex.cc.acceptPreviewContact(previewPayload)).rejects.toThrow(
2501
+ error.details.data.reason
2502
+ );
2503
+
2504
+ expect(LoggerProxy.info).toHaveBeenCalledWith('Accepting campaign preview contact', {
2505
+ module: CC_FILE,
2506
+ method: 'acceptPreviewContact',
2507
+ });
2508
+ expect(LoggerProxy.error).toHaveBeenCalledWith(
2509
+ `acceptPreviewContact failed with reason: ${error.details.data.reason}`,
2510
+ {module: CC_FILE, method: 'acceptPreviewContact', trackingId: error.details.trackingId}
2511
+ );
2512
+ expect(getErrorDetailsSpy).toHaveBeenCalledWith(error, 'acceptPreviewContact', CC_FILE);
2513
+ });
2514
+ });
2515
+
2516
+ describe('skipPreviewContact', () => {
2517
+ const previewPayload = {
2518
+ interactionId: 'interaction-123',
2519
+ campaignId: 'campaign-456',
2520
+ };
2521
+
2522
+ it('should skip preview contact successfully', async () => {
2523
+ const mockResponse = {trackingId: 'track-123'} as AgentContact;
2524
+
2525
+ const skipPreviewContactMock = jest
2526
+ .spyOn(webex.cc.services.dialer, 'skipPreviewContact')
2527
+ .mockResolvedValue(mockResponse);
2528
+
2529
+ const result = await webex.cc.skipPreviewContact(previewPayload);
2530
+
2531
+ expect(LoggerProxy.info).toHaveBeenCalledWith('Skipping campaign preview contact', {
2532
+ module: CC_FILE,
2533
+ method: 'skipPreviewContact',
2534
+ });
2535
+ expect(LoggerProxy.log).toHaveBeenCalledWith(
2536
+ 'Campaign preview contact skipped successfully',
2537
+ {
2538
+ module: CC_FILE,
2539
+ method: 'skipPreviewContact',
2540
+ trackingId: 'track-123',
2541
+ interactionId: previewPayload.interactionId,
2542
+ }
2543
+ );
2544
+
2545
+ expect(skipPreviewContactMock).toHaveBeenCalledWith({data: previewPayload});
2546
+ expect(result).toEqual(mockResponse);
2547
+ });
2548
+
2549
+ it('should handle error during skipPreviewContact', async () => {
2550
+ getErrorDetailsSpy.mockRestore();
2551
+ getErrorDetailsSpy = jest.spyOn(Utils, 'getErrorDetails');
2552
+
2553
+ const error = {
2554
+ details: {
2555
+ trackingId: '1234',
2556
+ data: {
2557
+ reason: 'Error while performing skipPreviewContact',
2558
+ },
2559
+ },
2560
+ };
2561
+
2562
+ jest.spyOn(webex.cc.services.dialer, 'skipPreviewContact').mockRejectedValue(error);
2563
+
2564
+ await expect(webex.cc.skipPreviewContact(previewPayload)).rejects.toThrow(
2565
+ error.details.data.reason
2566
+ );
2567
+
2568
+ expect(LoggerProxy.info).toHaveBeenCalledWith('Skipping campaign preview contact', {
2569
+ module: CC_FILE,
2570
+ method: 'skipPreviewContact',
2571
+ });
2572
+ expect(LoggerProxy.error).toHaveBeenCalledWith(
2573
+ `skipPreviewContact failed with reason: ${error.details.data.reason}`,
2574
+ {module: CC_FILE, method: 'skipPreviewContact', trackingId: error.details.trackingId}
2575
+ );
2576
+ expect(getErrorDetailsSpy).toHaveBeenCalledWith(error, 'skipPreviewContact', CC_FILE);
2577
+ });
2578
+ });
2579
+
2580
+ describe('removePreviewContact', () => {
2581
+ const previewPayload = {
2582
+ interactionId: 'interaction-123',
2583
+ campaignId: 'campaign-456',
2584
+ };
2585
+
2586
+ it('should remove preview contact successfully', async () => {
2587
+ const mockResponse = {trackingId: 'track-123'} as AgentContact;
2588
+
2589
+ const removePreviewContactMock = jest
2590
+ .spyOn(webex.cc.services.dialer, 'removePreviewContact')
2591
+ .mockResolvedValue(mockResponse);
2592
+
2593
+ const result = await webex.cc.removePreviewContact(previewPayload);
2594
+
2595
+ expect(LoggerProxy.info).toHaveBeenCalledWith('Removing campaign preview contact', {
2596
+ module: CC_FILE,
2597
+ method: 'removePreviewContact',
2598
+ });
2599
+ expect(LoggerProxy.log).toHaveBeenCalledWith(
2600
+ 'Campaign preview contact removed successfully',
2601
+ {
2602
+ module: CC_FILE,
2603
+ method: 'removePreviewContact',
2604
+ trackingId: 'track-123',
2605
+ interactionId: previewPayload.interactionId,
2606
+ }
2607
+ );
2608
+
2609
+ expect(removePreviewContactMock).toHaveBeenCalledWith({data: previewPayload});
2610
+ expect(result).toEqual(mockResponse);
2611
+ });
2612
+
2613
+ it('should handle error during removePreviewContact', async () => {
2614
+ getErrorDetailsSpy.mockRestore();
2615
+ getErrorDetailsSpy = jest.spyOn(Utils, 'getErrorDetails');
2616
+
2617
+ const error = {
2618
+ details: {
2619
+ trackingId: '1234',
2620
+ data: {
2621
+ reason: 'Error while performing removePreviewContact',
2622
+ },
2623
+ },
2624
+ };
2625
+
2626
+ jest.spyOn(webex.cc.services.dialer, 'removePreviewContact').mockRejectedValue(error);
2627
+
2628
+ await expect(webex.cc.removePreviewContact(previewPayload)).rejects.toThrow(
2629
+ error.details.data.reason
2630
+ );
2631
+
2632
+ expect(LoggerProxy.info).toHaveBeenCalledWith('Removing campaign preview contact', {
2633
+ module: CC_FILE,
2634
+ method: 'removePreviewContact',
2635
+ });
2636
+ expect(LoggerProxy.error).toHaveBeenCalledWith(
2637
+ `removePreviewContact failed with reason: ${error.details.data.reason}`,
2638
+ {module: CC_FILE, method: 'removePreviewContact', trackingId: error.details.trackingId}
2639
+ );
2640
+ expect(getErrorDetailsSpy).toHaveBeenCalledWith(error, 'removePreviewContact', CC_FILE);
2641
+ });
2642
+ });
2381
2643
  });