@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.
- package/dist/cc.js +297 -83
- package/dist/cc.js.map +1 -1
- package/dist/constants.js +3 -0
- package/dist/constants.js.map +1 -1
- package/dist/metrics/behavioral-events.js +39 -0
- package/dist/metrics/behavioral-events.js.map +1 -1
- package/dist/metrics/constants.js +8 -1
- package/dist/metrics/constants.js.map +1 -1
- package/dist/services/ApiAiAssistant.js +3 -0
- package/dist/services/ApiAiAssistant.js.map +1 -1
- package/dist/services/config/Util.js +1 -1
- package/dist/services/config/Util.js.map +1 -1
- package/dist/services/config/types.js +10 -0
- package/dist/services/config/types.js.map +1 -1
- package/dist/services/constants.js +1 -0
- package/dist/services/constants.js.map +1 -1
- package/dist/services/core/Err.js.map +1 -1
- package/dist/services/core/Utils.js +57 -6
- package/dist/services/core/Utils.js.map +1 -1
- package/dist/services/core/aqm-reqs.js +92 -17
- package/dist/services/core/aqm-reqs.js.map +1 -1
- package/dist/services/core/websocket/WebSocketManager.js +24 -3
- package/dist/services/core/websocket/WebSocketManager.js.map +1 -1
- package/dist/services/index.js +1 -0
- package/dist/services/index.js.map +1 -1
- package/dist/services/task/Task.js +6 -0
- package/dist/services/task/Task.js.map +1 -1
- package/dist/services/task/TaskManager.js +155 -6
- package/dist/services/task/TaskManager.js.map +1 -1
- package/dist/services/task/TaskUtils.js +39 -11
- package/dist/services/task/TaskUtils.js.map +1 -1
- package/dist/services/task/constants.js +7 -1
- package/dist/services/task/constants.js.map +1 -1
- package/dist/services/task/dialer.js +129 -0
- package/dist/services/task/dialer.js.map +1 -1
- package/dist/services/task/state-machine/TaskStateMachine.js +30 -3
- package/dist/services/task/state-machine/TaskStateMachine.js.map +1 -1
- package/dist/services/task/state-machine/actions.js +4 -0
- package/dist/services/task/state-machine/actions.js.map +1 -1
- package/dist/services/task/state-machine/constants.js +3 -0
- package/dist/services/task/state-machine/constants.js.map +1 -1
- package/dist/services/task/state-machine/guards.js +23 -0
- package/dist/services/task/state-machine/guards.js.map +1 -1
- package/dist/services/task/state-machine/types.js.map +1 -1
- package/dist/services/task/types.js +59 -0
- package/dist/services/task/types.js.map +1 -1
- package/dist/types/cc.d.ts +76 -2
- package/dist/types/constants.d.ts +3 -0
- package/dist/types/metrics/constants.d.ts +6 -0
- package/dist/types/services/config/types.d.ts +22 -1
- package/dist/types/services/core/Err.d.ts +6 -0
- package/dist/types/services/core/Utils.d.ts +20 -2
- package/dist/types/services/core/aqm-reqs.d.ts +49 -0
- package/dist/types/services/core/websocket/WebSocketManager.d.ts +1 -0
- package/dist/types/services/core/websocket/connection-service.d.ts +1 -0
- package/dist/types/services/task/Task.d.ts +1 -0
- package/dist/types/services/task/TaskUtils.d.ts +13 -0
- package/dist/types/services/task/constants.d.ts +6 -0
- package/dist/types/services/task/dialer.d.ts +45 -0
- package/dist/types/services/task/state-machine/TaskStateMachine.d.ts +36 -8
- package/dist/types/services/task/state-machine/constants.d.ts +3 -0
- package/dist/types/services/task/state-machine/guards.d.ts +2 -0
- package/dist/types/services/task/state-machine/types.d.ts +11 -0
- package/dist/types/services/task/types.d.ts +75 -1
- package/dist/types.js.map +1 -1
- package/dist/webex.js +1 -1
- package/package.json +2 -2
- package/src/cc.ts +389 -111
- package/src/constants.ts +3 -0
- package/src/metrics/behavioral-events.ts +42 -0
- package/src/metrics/constants.ts +9 -1
- package/src/services/ApiAiAssistant.ts +3 -0
- package/src/services/config/Util.ts +1 -1
- package/src/services/config/types.ts +12 -1
- package/src/services/constants.ts +1 -0
- package/src/services/core/Err.ts +3 -0
- package/src/services/core/Utils.ts +68 -5
- package/src/services/core/aqm-reqs.ts +100 -22
- package/src/services/core/websocket/WebSocketManager.ts +23 -2
- package/src/services/index.ts +1 -0
- package/src/services/task/Task.ts +13 -0
- package/src/services/task/TaskManager.ts +171 -7
- package/src/services/task/TaskUtils.ts +33 -1
- package/src/services/task/constants.ts +6 -0
- package/src/services/task/dialer.ts +136 -1
- package/src/services/task/state-machine/TaskStateMachine.ts +37 -5
- package/src/services/task/state-machine/actions.ts +4 -0
- package/src/services/task/state-machine/constants.ts +3 -0
- package/src/services/task/state-machine/guards.ts +30 -0
- package/src/services/task/state-machine/types.ts +14 -1
- package/src/services/task/types.ts +80 -0
- package/src/types.ts +4 -1
- package/test/unit/spec/cc.ts +293 -26
- package/test/unit/spec/services/config/index.ts +30 -2
- package/test/unit/spec/services/core/Utils.ts +367 -1
- package/test/unit/spec/services/core/websocket/WebSocketManager.ts +90 -1
- package/test/unit/spec/services/core/websocket/connection-service.ts +4 -5
- package/test/unit/spec/services/task/TaskManager.ts +2325 -150
- package/test/unit/spec/services/task/dialer.ts +372 -96
- package/umd/contact-center.min.js +2 -2
- 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.
|
package/test/unit/spec/cc.ts
CHANGED
|
@@ -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.
|
|
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:
|
|
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:
|
|
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.
|
|
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:
|
|
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:
|
|
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
|
|
698
|
-
const messageCallback =
|
|
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.
|
|
904
|
-
|
|
905
|
-
|
|
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.
|
|
958
|
-
|
|
959
|
-
|
|
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.
|
|
1360
|
+
expect(LoggerProxy.log).toHaveBeenCalledWith('Starting silent relogin process', {
|
|
1300
1361
|
module: CC_FILE,
|
|
1301
1362
|
method: 'silentRelogin',
|
|
1302
1363
|
});
|
|
1303
|
-
expect(LoggerProxy.
|
|
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.
|
|
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.
|
|
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.
|
|
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(
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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);
|