@webex/contact-center 3.12.0-next.6 → 3.12.0-next.60
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 +161 -21
- package/dist/cc.js.map +1 -1
- package/dist/constants.js +2 -0
- package/dist/constants.js.map +1 -1
- package/dist/metrics/behavioral-events.js +26 -0
- package/dist/metrics/behavioral-events.js.map +1 -1
- package/dist/metrics/constants.js +4 -0
- package/dist/metrics/constants.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/constants.js +1 -1
- package/dist/services/config/constants.js.map +1 -1
- package/dist/services/config/types.js +4 -0
- package/dist/services/config/types.js.map +1 -1
- package/dist/services/core/Err.js.map +1 -1
- package/dist/services/core/Utils.js +37 -9
- package/dist/services/core/Utils.js.map +1 -1
- package/dist/services/task/TaskManager.js +90 -8
- package/dist/services/task/TaskManager.js.map +1 -1
- package/dist/services/task/constants.js +3 -1
- package/dist/services/task/constants.js.map +1 -1
- package/dist/services/task/dialer.js +78 -0
- package/dist/services/task/dialer.js.map +1 -1
- package/dist/services/task/index.js +7 -2
- package/dist/services/task/index.js.map +1 -1
- package/dist/services/task/types.js +44 -0
- package/dist/services/task/types.js.map +1 -1
- package/dist/types/cc.d.ts +44 -0
- package/dist/types/constants.d.ts +2 -0
- package/dist/types/metrics/constants.d.ts +4 -0
- package/dist/types/services/config/types.d.ts +10 -1
- package/dist/types/services/core/Err.d.ts +4 -0
- package/dist/types/services/core/Utils.d.ts +10 -3
- package/dist/types/services/task/constants.d.ts +2 -0
- package/dist/types/services/task/dialer.d.ts +30 -0
- package/dist/types/services/task/types.d.ts +53 -1
- package/dist/webex.js +1 -1
- package/package.json +9 -9
- package/src/cc.ts +196 -22
- package/src/constants.ts +2 -0
- package/src/metrics/behavioral-events.ts +28 -0
- package/src/metrics/constants.ts +4 -0
- package/src/services/config/Util.ts +1 -1
- package/src/services/config/constants.ts +1 -1
- package/src/services/config/types.ts +6 -1
- package/src/services/core/Err.ts +2 -0
- package/src/services/core/Utils.ts +43 -8
- package/src/services/task/TaskManager.ts +102 -22
- package/src/services/task/constants.ts +2 -0
- package/src/services/task/dialer.ts +80 -0
- package/src/services/task/index.ts +7 -2
- package/src/services/task/types.ts +56 -0
- package/test/unit/spec/cc.ts +154 -20
- package/test/unit/spec/services/config/index.ts +3 -3
- package/test/unit/spec/services/core/Utils.ts +90 -7
- package/test/unit/spec/services/task/TaskManager.ts +238 -7
- package/test/unit/spec/services/task/dialer.ts +190 -0
- package/test/unit/spec/services/task/index.ts +21 -0
- package/umd/contact-center.min.js +2 -2
- package/umd/contact-center.min.js.map +1 -1
|
@@ -7,6 +7,8 @@ import {
|
|
|
7
7
|
TASK_API,
|
|
8
8
|
DIALER_API,
|
|
9
9
|
CAMPAIGN_PREVIEW_ACCEPT,
|
|
10
|
+
CAMPAIGN_PREVIEW_SKIP,
|
|
11
|
+
CAMPAIGN_PREVIEW_REMOVE,
|
|
10
12
|
TIMEOUT_PREVIEW_ACCEPT,
|
|
11
13
|
} from './constants';
|
|
12
14
|
import * as Contact from './types';
|
|
@@ -103,5 +105,83 @@ export default function aqmDialer(aqm: AqmReqs) {
|
|
|
103
105
|
errId: 'Service.aqm.dialer.acceptPreviewContact',
|
|
104
106
|
},
|
|
105
107
|
})),
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Skips a campaign preview contact, requesting the next contact from the campaign.
|
|
111
|
+
*
|
|
112
|
+
* @param {Object} p - Parameters object.
|
|
113
|
+
* @param {Contact.PreviewContactPayload} p.data - Payload containing interactionId and campaignId.
|
|
114
|
+
* @returns {Promise<Contact.AgentContact>} A promise that resolves with agent contact on success.
|
|
115
|
+
*
|
|
116
|
+
* Emits:
|
|
117
|
+
* - `CC_EVENTS.CAMPAIGN_CONTACT_UPDATED` or `CC_EVENTS.CONTACT_ENDED` on success
|
|
118
|
+
* - `CC_EVENTS.CAMPAIGN_PREVIEW_SKIP_FAILED` on failure
|
|
119
|
+
* @ignore
|
|
120
|
+
*/
|
|
121
|
+
skipPreviewContact: aqm.req((p: {data: Contact.PreviewContactPayload}) => ({
|
|
122
|
+
url: `${DIALER_API}/campaign/${encodeURIComponent(p.data.campaignId)}/preview-task/${
|
|
123
|
+
p.data.interactionId
|
|
124
|
+
}${CAMPAIGN_PREVIEW_SKIP}`,
|
|
125
|
+
host: WCC_API_GATEWAY,
|
|
126
|
+
data: {},
|
|
127
|
+
method: HTTP_METHODS.POST,
|
|
128
|
+
err,
|
|
129
|
+
notifSuccess: {
|
|
130
|
+
bind: {
|
|
131
|
+
type: TASK_MESSAGE_TYPE,
|
|
132
|
+
data: {
|
|
133
|
+
type: [CC_EVENTS.CAMPAIGN_CONTACT_UPDATED, CC_EVENTS.CONTACT_ENDED],
|
|
134
|
+
interactionId: p.data.interactionId,
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
msg: {} as Contact.AgentContact,
|
|
138
|
+
},
|
|
139
|
+
notifFail: {
|
|
140
|
+
bind: {
|
|
141
|
+
type: TASK_MESSAGE_TYPE,
|
|
142
|
+
data: {type: CC_EVENTS.CAMPAIGN_PREVIEW_SKIP_FAILED, campaignId: p.data.campaignId},
|
|
143
|
+
},
|
|
144
|
+
errId: 'Service.aqm.dialer.skipPreviewContact',
|
|
145
|
+
},
|
|
146
|
+
})),
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Removes a campaign preview contact from the campaign list entirely.
|
|
150
|
+
*
|
|
151
|
+
* @param {Object} p - Parameters object.
|
|
152
|
+
* @param {Contact.PreviewContactPayload} p.data - Payload containing interactionId and campaignId.
|
|
153
|
+
* @returns {Promise<Contact.AgentContact>} A promise that resolves with agent contact on success.
|
|
154
|
+
*
|
|
155
|
+
* Emits:
|
|
156
|
+
* - `CC_EVENTS.CAMPAIGN_CONTACT_UPDATED` or `CC_EVENTS.CONTACT_ENDED` on success
|
|
157
|
+
* - `CC_EVENTS.CAMPAIGN_PREVIEW_REMOVE_FAILED` on failure
|
|
158
|
+
* @ignore
|
|
159
|
+
*/
|
|
160
|
+
removePreviewContact: aqm.req((p: {data: Contact.PreviewContactPayload}) => ({
|
|
161
|
+
url: `${DIALER_API}/campaign/${encodeURIComponent(p.data.campaignId)}/preview-task/${
|
|
162
|
+
p.data.interactionId
|
|
163
|
+
}${CAMPAIGN_PREVIEW_REMOVE}`,
|
|
164
|
+
host: WCC_API_GATEWAY,
|
|
165
|
+
data: {},
|
|
166
|
+
method: HTTP_METHODS.POST,
|
|
167
|
+
err,
|
|
168
|
+
notifSuccess: {
|
|
169
|
+
bind: {
|
|
170
|
+
type: TASK_MESSAGE_TYPE,
|
|
171
|
+
data: {
|
|
172
|
+
type: [CC_EVENTS.CAMPAIGN_CONTACT_UPDATED, CC_EVENTS.CONTACT_ENDED],
|
|
173
|
+
interactionId: p.data.interactionId,
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
msg: {} as Contact.AgentContact,
|
|
177
|
+
},
|
|
178
|
+
notifFail: {
|
|
179
|
+
bind: {
|
|
180
|
+
type: TASK_MESSAGE_TYPE,
|
|
181
|
+
data: {type: CC_EVENTS.CAMPAIGN_PREVIEW_REMOVE_FAILED, campaignId: p.data.campaignId},
|
|
182
|
+
},
|
|
183
|
+
errId: 'Service.aqm.dialer.removePreviewContact',
|
|
184
|
+
},
|
|
185
|
+
})),
|
|
106
186
|
};
|
|
107
187
|
}
|
|
@@ -565,7 +565,10 @@ export default class Task extends EventEmitter implements ITask {
|
|
|
565
565
|
METRIC_EVENT_NAMES.TASK_HOLD_FAILED,
|
|
566
566
|
]);
|
|
567
567
|
|
|
568
|
-
const
|
|
568
|
+
const {mainInteractionId} = this.data.interaction;
|
|
569
|
+
const defaultMediaResourceId =
|
|
570
|
+
this.data.interaction.media[mainInteractionId]?.mediaResourceId;
|
|
571
|
+
const effectiveMediaResourceId = mediaResourceId ?? defaultMediaResourceId;
|
|
569
572
|
|
|
570
573
|
const response = await this.contact.hold({
|
|
571
574
|
interactionId: this.data.interactionId,
|
|
@@ -599,7 +602,9 @@ export default class Task extends EventEmitter implements ITask {
|
|
|
599
602
|
errorData: err.data?.errorData,
|
|
600
603
|
reasonCode: err.data?.reasonCode,
|
|
601
604
|
};
|
|
602
|
-
const
|
|
605
|
+
const defaultMediaResourceId =
|
|
606
|
+
this.data.interaction.media[this.data.interaction.mainInteractionId]?.mediaResourceId;
|
|
607
|
+
const effectiveMediaResourceId = mediaResourceId ?? defaultMediaResourceId;
|
|
603
608
|
|
|
604
609
|
this.metricsManager.trackEvent(
|
|
605
610
|
METRIC_EVENT_NAMES.TASK_HOLD_FAILED,
|
|
@@ -554,6 +554,54 @@ export enum TASK_EVENTS {
|
|
|
554
554
|
* ```
|
|
555
555
|
*/
|
|
556
556
|
TASK_CAMPAIGN_PREVIEW_RESERVATION = 'task:campaignPreviewReservation',
|
|
557
|
+
|
|
558
|
+
/**
|
|
559
|
+
* Triggered when accepting a campaign preview contact fails
|
|
560
|
+
* @example
|
|
561
|
+
* ```typescript
|
|
562
|
+
* task.on(TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_ACCEPT_FAILED, (task: ITask) => {
|
|
563
|
+
* console.log('Campaign preview accept failed:', task.data.interactionId);
|
|
564
|
+
* // Handle accept failure
|
|
565
|
+
* });
|
|
566
|
+
* ```
|
|
567
|
+
*/
|
|
568
|
+
TASK_CAMPAIGN_PREVIEW_ACCEPT_FAILED = 'task:campaignPreviewAcceptFailed',
|
|
569
|
+
|
|
570
|
+
/**
|
|
571
|
+
* Triggered when skipping a campaign preview contact fails
|
|
572
|
+
* @example
|
|
573
|
+
* ```typescript
|
|
574
|
+
* task.on(TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_SKIP_FAILED, (task: ITask) => {
|
|
575
|
+
* console.log('Campaign preview skip failed:', task.data.interactionId);
|
|
576
|
+
* // Handle skip failure
|
|
577
|
+
* });
|
|
578
|
+
* ```
|
|
579
|
+
*/
|
|
580
|
+
TASK_CAMPAIGN_PREVIEW_SKIP_FAILED = 'task:campaignPreviewSkipFailed',
|
|
581
|
+
|
|
582
|
+
/**
|
|
583
|
+
* Triggered when removing a campaign preview contact fails
|
|
584
|
+
* @example
|
|
585
|
+
* ```typescript
|
|
586
|
+
* task.on(TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_REMOVE_FAILED, (task: ITask) => {
|
|
587
|
+
* console.log('Campaign preview remove failed:', task.data.interactionId);
|
|
588
|
+
* // Handle remove failure
|
|
589
|
+
* });
|
|
590
|
+
* ```
|
|
591
|
+
*/
|
|
592
|
+
TASK_CAMPAIGN_PREVIEW_REMOVE_FAILED = 'task:campaignPreviewRemoveFailed',
|
|
593
|
+
|
|
594
|
+
/**
|
|
595
|
+
* Triggered when a campaign contact is updated (e.g., after skip or remove, when the next contact is offered)
|
|
596
|
+
* @example
|
|
597
|
+
* ```typescript
|
|
598
|
+
* task.on(TASK_EVENTS.TASK_CAMPAIGN_CONTACT_UPDATED, (task: ITask) => {
|
|
599
|
+
* console.log('Campaign contact updated:', task.data.interactionId);
|
|
600
|
+
* // Handle updated campaign contact (e.g., display next contact)
|
|
601
|
+
* });
|
|
602
|
+
* ```
|
|
603
|
+
*/
|
|
604
|
+
TASK_CAMPAIGN_CONTACT_UPDATED = 'task:campaignContactUpdated',
|
|
557
605
|
}
|
|
558
606
|
|
|
559
607
|
/**
|
|
@@ -702,6 +750,14 @@ export type Interaction = {
|
|
|
702
750
|
fcDesktopView?: string;
|
|
703
751
|
/** Agent ID who initiated the outdial call */
|
|
704
752
|
outdialAgentId?: string;
|
|
753
|
+
/** Indicates if the skip action is disabled for campaign preview contacts */
|
|
754
|
+
campaignPreviewSkipDisabled?: string;
|
|
755
|
+
/** Indicates if the remove action is disabled for campaign preview contacts */
|
|
756
|
+
campaignPreviewRemoveDisabled?: string;
|
|
757
|
+
/** Auto-action to perform when campaign preview offer times out (ACCEPT, SKIP, REMOVE) */
|
|
758
|
+
campaignPreviewAutoAction?: string;
|
|
759
|
+
/** Timestamp (ms) when the campaign preview offer expires */
|
|
760
|
+
campaignPreviewOfferTimeout?: string;
|
|
705
761
|
};
|
|
706
762
|
/** Main interaction identifier for related interactions */
|
|
707
763
|
mainInteractionId?: string;
|
package/test/unit/spec/cc.ts
CHANGED
|
@@ -139,6 +139,8 @@ describe('webex.cc', () => {
|
|
|
139
139
|
dialer: {
|
|
140
140
|
startOutdial: jest.fn(),
|
|
141
141
|
acceptPreviewContact: jest.fn(),
|
|
142
|
+
skipPreviewContact: jest.fn(),
|
|
143
|
+
removePreviewContact: jest.fn(),
|
|
142
144
|
},
|
|
143
145
|
apiAIAssistant: {
|
|
144
146
|
sendEvent: jest.fn(),
|
|
@@ -300,7 +302,7 @@ describe('webex.cc', () => {
|
|
|
300
302
|
const result = await webex.cc.register();
|
|
301
303
|
|
|
302
304
|
// Verify logging calls
|
|
303
|
-
expect(LoggerProxy.
|
|
305
|
+
expect(LoggerProxy.log).toHaveBeenCalledWith('Starting CC SDK registration', {
|
|
304
306
|
module: CC_FILE,
|
|
305
307
|
method: 'register',
|
|
306
308
|
});
|
|
@@ -413,7 +415,7 @@ describe('webex.cc', () => {
|
|
|
413
415
|
|
|
414
416
|
await expect(webex.cc.register()).rejects.toThrow('Error while performing register');
|
|
415
417
|
|
|
416
|
-
expect(LoggerProxy.
|
|
418
|
+
expect(LoggerProxy.log).toHaveBeenCalledWith('Starting CC SDK registration', {
|
|
417
419
|
module: CC_FILE,
|
|
418
420
|
method: 'register',
|
|
419
421
|
});
|
|
@@ -661,10 +663,8 @@ describe('webex.cc', () => {
|
|
|
661
663
|
|
|
662
664
|
expect(emitSpy).toHaveBeenCalledTimes(1);
|
|
663
665
|
expect(emitSpy).toHaveBeenCalledWith(TASK_EVENTS.TASK_INCOMING, mockTask);
|
|
664
|
-
// Verify message
|
|
665
|
-
const messageCallback =
|
|
666
|
-
(call) => call[0] === 'message'
|
|
667
|
-
)[1];
|
|
666
|
+
// Verify websocket message handling
|
|
667
|
+
const messageCallback = webex.cc['handleWebsocketMessage'];
|
|
668
668
|
const agentStateChangeEventData = {
|
|
669
669
|
type: CC_EVENTS.AGENT_STATE_CHANGE,
|
|
670
670
|
data: {some: 'data'},
|
|
@@ -814,10 +814,13 @@ describe('webex.cc', () => {
|
|
|
814
814
|
const result = await webex.cc.stationLogin(options);
|
|
815
815
|
|
|
816
816
|
// Verify logging calls
|
|
817
|
-
expect(LoggerProxy.
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
817
|
+
expect(LoggerProxy.log).toHaveBeenCalledWith(
|
|
818
|
+
`Starting agent station login | loginOption: ${options.loginOption} teamId: ${options.teamId}`,
|
|
819
|
+
{
|
|
820
|
+
module: CC_FILE,
|
|
821
|
+
method: 'stationLogin',
|
|
822
|
+
}
|
|
823
|
+
);
|
|
821
824
|
expect(LoggerProxy.log).toHaveBeenCalledWith(
|
|
822
825
|
`Agent station login completed successfully agentId: ${mockData.data.agentId} loginOption: ${mockData.data.loginOption} teamId: ${mockData.data.teamId}`,
|
|
823
826
|
{
|
|
@@ -868,10 +871,13 @@ describe('webex.cc', () => {
|
|
|
868
871
|
|
|
869
872
|
await expect(webex.cc.stationLogin(options)).rejects.toThrow(error.details.data.reason);
|
|
870
873
|
|
|
871
|
-
expect(LoggerProxy.
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
874
|
+
expect(LoggerProxy.log).toHaveBeenCalledWith(
|
|
875
|
+
`Starting agent station login | loginOption: ${options.loginOption} teamId: ${options.teamId}`,
|
|
876
|
+
{
|
|
877
|
+
module: CC_FILE,
|
|
878
|
+
method: 'stationLogin',
|
|
879
|
+
}
|
|
880
|
+
);
|
|
875
881
|
expect(LoggerProxy.error).toHaveBeenCalledWith(
|
|
876
882
|
`stationLogin failed with reason: ${error.details.data.reason}`,
|
|
877
883
|
{module: CC_FILE, method: 'stationLogin', trackingId: error.details.trackingId}
|
|
@@ -1210,11 +1216,11 @@ describe('webex.cc', () => {
|
|
|
1210
1216
|
const webSocketManagerOnSpy = jest.spyOn(webex.cc.services.webSocketManager, 'on');
|
|
1211
1217
|
await webex.cc['silentRelogin']();
|
|
1212
1218
|
|
|
1213
|
-
expect(LoggerProxy.
|
|
1219
|
+
expect(LoggerProxy.log).toHaveBeenCalledWith('Starting silent relogin process', {
|
|
1214
1220
|
module: CC_FILE,
|
|
1215
1221
|
method: 'silentRelogin',
|
|
1216
1222
|
});
|
|
1217
|
-
expect(LoggerProxy.
|
|
1223
|
+
expect(LoggerProxy.log).toHaveBeenCalledWith(
|
|
1218
1224
|
'event=requestAutoStateChange | Requesting state change to available on socket reconnect',
|
|
1219
1225
|
{module: CC_FILE, method: 'silentRelogin'}
|
|
1220
1226
|
);
|
|
@@ -1259,7 +1265,7 @@ describe('webex.cc', () => {
|
|
|
1259
1265
|
|
|
1260
1266
|
jest.spyOn(webex.cc.services.agent, 'reload').mockRejectedValue(error);
|
|
1261
1267
|
await webex.cc['silentRelogin']();
|
|
1262
|
-
expect(LoggerProxy.
|
|
1268
|
+
expect(LoggerProxy.log).toHaveBeenCalledWith('Starting silent relogin process', {
|
|
1263
1269
|
module: CC_FILE,
|
|
1264
1270
|
method: 'silentRelogin',
|
|
1265
1271
|
});
|
|
@@ -1274,7 +1280,7 @@ describe('webex.cc', () => {
|
|
|
1274
1280
|
jest.spyOn(webex.cc.services.agent, 'reload').mockRejectedValue(error);
|
|
1275
1281
|
|
|
1276
1282
|
await expect(webex.cc['silentRelogin']()).rejects.toThrow(error);
|
|
1277
|
-
expect(LoggerProxy.
|
|
1283
|
+
expect(LoggerProxy.log).toHaveBeenCalledWith('Starting silent relogin process', {
|
|
1278
1284
|
module: CC_FILE,
|
|
1279
1285
|
method: 'silentRelogin',
|
|
1280
1286
|
});
|
|
@@ -1316,7 +1322,7 @@ describe('webex.cc', () => {
|
|
|
1316
1322
|
|
|
1317
1323
|
await webex.cc['silentRelogin']();
|
|
1318
1324
|
|
|
1319
|
-
expect(LoggerProxy.
|
|
1325
|
+
expect(LoggerProxy.log).toHaveBeenCalledWith('Starting silent relogin process', {
|
|
1320
1326
|
module: CC_FILE,
|
|
1321
1327
|
method: 'silentRelogin',
|
|
1322
1328
|
});
|
|
@@ -1726,7 +1732,7 @@ describe('webex.cc', () => {
|
|
|
1726
1732
|
|
|
1727
1733
|
beforeEach(() => {
|
|
1728
1734
|
emitSpy = jest.spyOn(webex.cc, 'emit');
|
|
1729
|
-
messageCallback =
|
|
1735
|
+
messageCallback = webex.cc['handleWebsocketMessage'];
|
|
1730
1736
|
});
|
|
1731
1737
|
|
|
1732
1738
|
it('should emit AGENT_STATION_LOGIN_SUCCESS on CC_EVENTS.AGENT_STATION_LOGIN_SUCCESS with mapped payload', () => {
|
|
@@ -2320,4 +2326,132 @@ describe('webex.cc', () => {
|
|
|
2320
2326
|
expect(getErrorDetailsSpy).toHaveBeenCalledWith(error, 'acceptPreviewContact', CC_FILE);
|
|
2321
2327
|
});
|
|
2322
2328
|
});
|
|
2329
|
+
|
|
2330
|
+
describe('skipPreviewContact', () => {
|
|
2331
|
+
const previewPayload = {
|
|
2332
|
+
interactionId: 'interaction-123',
|
|
2333
|
+
campaignId: 'campaign-456',
|
|
2334
|
+
};
|
|
2335
|
+
|
|
2336
|
+
it('should skip preview contact successfully', async () => {
|
|
2337
|
+
const mockResponse = {trackingId: 'track-123'} as AgentContact;
|
|
2338
|
+
|
|
2339
|
+
const skipPreviewContactMock = jest
|
|
2340
|
+
.spyOn(webex.cc.services.dialer, 'skipPreviewContact')
|
|
2341
|
+
.mockResolvedValue(mockResponse);
|
|
2342
|
+
|
|
2343
|
+
const result = await webex.cc.skipPreviewContact(previewPayload);
|
|
2344
|
+
|
|
2345
|
+
expect(LoggerProxy.info).toHaveBeenCalledWith('Skipping campaign preview contact', {
|
|
2346
|
+
module: CC_FILE,
|
|
2347
|
+
method: 'skipPreviewContact',
|
|
2348
|
+
});
|
|
2349
|
+
expect(LoggerProxy.log).toHaveBeenCalledWith(
|
|
2350
|
+
'Campaign preview contact skipped successfully',
|
|
2351
|
+
{
|
|
2352
|
+
module: CC_FILE,
|
|
2353
|
+
method: 'skipPreviewContact',
|
|
2354
|
+
trackingId: 'track-123',
|
|
2355
|
+
interactionId: previewPayload.interactionId,
|
|
2356
|
+
}
|
|
2357
|
+
);
|
|
2358
|
+
|
|
2359
|
+
expect(skipPreviewContactMock).toHaveBeenCalledWith({data: previewPayload});
|
|
2360
|
+
expect(result).toEqual(mockResponse);
|
|
2361
|
+
});
|
|
2362
|
+
|
|
2363
|
+
it('should handle error during skipPreviewContact', async () => {
|
|
2364
|
+
getErrorDetailsSpy.mockRestore();
|
|
2365
|
+
getErrorDetailsSpy = jest.spyOn(Utils, 'getErrorDetails');
|
|
2366
|
+
|
|
2367
|
+
const error = {
|
|
2368
|
+
details: {
|
|
2369
|
+
trackingId: '1234',
|
|
2370
|
+
data: {
|
|
2371
|
+
reason: 'Error while performing skipPreviewContact',
|
|
2372
|
+
},
|
|
2373
|
+
},
|
|
2374
|
+
};
|
|
2375
|
+
|
|
2376
|
+
jest.spyOn(webex.cc.services.dialer, 'skipPreviewContact').mockRejectedValue(error);
|
|
2377
|
+
|
|
2378
|
+
await expect(webex.cc.skipPreviewContact(previewPayload)).rejects.toThrow(
|
|
2379
|
+
error.details.data.reason
|
|
2380
|
+
);
|
|
2381
|
+
|
|
2382
|
+
expect(LoggerProxy.info).toHaveBeenCalledWith('Skipping campaign preview contact', {
|
|
2383
|
+
module: CC_FILE,
|
|
2384
|
+
method: 'skipPreviewContact',
|
|
2385
|
+
});
|
|
2386
|
+
expect(LoggerProxy.error).toHaveBeenCalledWith(
|
|
2387
|
+
`skipPreviewContact failed with reason: ${error.details.data.reason}`,
|
|
2388
|
+
{module: CC_FILE, method: 'skipPreviewContact', trackingId: error.details.trackingId}
|
|
2389
|
+
);
|
|
2390
|
+
expect(getErrorDetailsSpy).toHaveBeenCalledWith(error, 'skipPreviewContact', CC_FILE);
|
|
2391
|
+
});
|
|
2392
|
+
});
|
|
2393
|
+
|
|
2394
|
+
describe('removePreviewContact', () => {
|
|
2395
|
+
const previewPayload = {
|
|
2396
|
+
interactionId: 'interaction-123',
|
|
2397
|
+
campaignId: 'campaign-456',
|
|
2398
|
+
};
|
|
2399
|
+
|
|
2400
|
+
it('should remove preview contact successfully', async () => {
|
|
2401
|
+
const mockResponse = {trackingId: 'track-123'} as AgentContact;
|
|
2402
|
+
|
|
2403
|
+
const removePreviewContactMock = jest
|
|
2404
|
+
.spyOn(webex.cc.services.dialer, 'removePreviewContact')
|
|
2405
|
+
.mockResolvedValue(mockResponse);
|
|
2406
|
+
|
|
2407
|
+
const result = await webex.cc.removePreviewContact(previewPayload);
|
|
2408
|
+
|
|
2409
|
+
expect(LoggerProxy.info).toHaveBeenCalledWith('Removing campaign preview contact', {
|
|
2410
|
+
module: CC_FILE,
|
|
2411
|
+
method: 'removePreviewContact',
|
|
2412
|
+
});
|
|
2413
|
+
expect(LoggerProxy.log).toHaveBeenCalledWith(
|
|
2414
|
+
'Campaign preview contact removed successfully',
|
|
2415
|
+
{
|
|
2416
|
+
module: CC_FILE,
|
|
2417
|
+
method: 'removePreviewContact',
|
|
2418
|
+
trackingId: 'track-123',
|
|
2419
|
+
interactionId: previewPayload.interactionId,
|
|
2420
|
+
}
|
|
2421
|
+
);
|
|
2422
|
+
|
|
2423
|
+
expect(removePreviewContactMock).toHaveBeenCalledWith({data: previewPayload});
|
|
2424
|
+
expect(result).toEqual(mockResponse);
|
|
2425
|
+
});
|
|
2426
|
+
|
|
2427
|
+
it('should handle error during removePreviewContact', async () => {
|
|
2428
|
+
getErrorDetailsSpy.mockRestore();
|
|
2429
|
+
getErrorDetailsSpy = jest.spyOn(Utils, 'getErrorDetails');
|
|
2430
|
+
|
|
2431
|
+
const error = {
|
|
2432
|
+
details: {
|
|
2433
|
+
trackingId: '1234',
|
|
2434
|
+
data: {
|
|
2435
|
+
reason: 'Error while performing removePreviewContact',
|
|
2436
|
+
},
|
|
2437
|
+
},
|
|
2438
|
+
};
|
|
2439
|
+
|
|
2440
|
+
jest.spyOn(webex.cc.services.dialer, 'removePreviewContact').mockRejectedValue(error);
|
|
2441
|
+
|
|
2442
|
+
await expect(webex.cc.removePreviewContact(previewPayload)).rejects.toThrow(
|
|
2443
|
+
error.details.data.reason
|
|
2444
|
+
);
|
|
2445
|
+
|
|
2446
|
+
expect(LoggerProxy.info).toHaveBeenCalledWith('Removing campaign preview contact', {
|
|
2447
|
+
module: CC_FILE,
|
|
2448
|
+
method: 'removePreviewContact',
|
|
2449
|
+
});
|
|
2450
|
+
expect(LoggerProxy.error).toHaveBeenCalledWith(
|
|
2451
|
+
`removePreviewContact failed with reason: ${error.details.data.reason}`,
|
|
2452
|
+
{module: CC_FILE, method: 'removePreviewContact', trackingId: error.details.trackingId}
|
|
2453
|
+
);
|
|
2454
|
+
expect(getErrorDetailsSpy).toHaveBeenCalledWith(error, 'removePreviewContact', CC_FILE);
|
|
2455
|
+
});
|
|
2456
|
+
});
|
|
2323
2457
|
});
|
|
@@ -263,7 +263,7 @@ describe('AgentConfigService', () => {
|
|
|
263
263
|
|
|
264
264
|
expect(mockWebexRequest.request).toHaveBeenCalledWith({
|
|
265
265
|
service: mockWccAPIURL,
|
|
266
|
-
resource: `organization/${mockOrgId}/v2/auxiliary-code?page=${page}&pageSize=${pageSize}&filter=id=in=(${filter})&attributes=${attributes}`,
|
|
266
|
+
resource: `organization/${mockOrgId}/v2/auxiliary-code?page=${page}&pageSize=${pageSize}&filter=id=in=(${filter})&attributes=${attributes}&desktopProfileFilter=true`,
|
|
267
267
|
method: 'GET',
|
|
268
268
|
});
|
|
269
269
|
expect(result).toEqual(mockResponse.body);
|
|
@@ -724,7 +724,7 @@ describe('AgentConfigService', () => {
|
|
|
724
724
|
agentProfileId: 'profile123',
|
|
725
725
|
siteId: 'site789',
|
|
726
726
|
dbId: 'db123',
|
|
727
|
-
|
|
727
|
+
deafultDialledNumber: '1234567890',
|
|
728
728
|
id: 'user001',
|
|
729
729
|
teamIds: ['team1', 'team2'],
|
|
730
730
|
};
|
|
@@ -864,7 +864,7 @@ describe('AgentConfigService', () => {
|
|
|
864
864
|
skillProfileId: 'skillProfile456',
|
|
865
865
|
siteId: 'site789',
|
|
866
866
|
dbId: 'db123',
|
|
867
|
-
|
|
867
|
+
deafultDialledNumber: '1234567890',
|
|
868
868
|
id: 'user001',
|
|
869
869
|
teamIds: ['team1', 'team2'],
|
|
870
870
|
};
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import * as Utils from '../../../../../src/services/core/Utils';
|
|
2
|
-
import {FALLBACK_DIAL_NUMBER_REGEX} from '../../../../../src/services/core/Utils';
|
|
3
2
|
import LoggerProxy from '../../../../../src/logger-proxy';
|
|
4
3
|
import WebexRequest from '../../../../../src/services/core/WebexRequest';
|
|
5
4
|
import {LoginOption, WebexRequestPayload} from '../../../../../src/types';
|
|
@@ -12,6 +11,7 @@ jest.mock('../../../../../src/logger-proxy', () => ({
|
|
|
12
11
|
log: jest.fn(),
|
|
13
12
|
error: jest.fn(),
|
|
14
13
|
info: jest.fn(),
|
|
14
|
+
warn: jest.fn(),
|
|
15
15
|
initialize: jest.fn(),
|
|
16
16
|
},
|
|
17
17
|
}));
|
|
@@ -569,7 +569,7 @@ describe('Utils', () => {
|
|
|
569
569
|
const usOnlyEntry = {
|
|
570
570
|
name: 'US',
|
|
571
571
|
prefix: '1',
|
|
572
|
-
regex:
|
|
572
|
+
regex: '1[0-9]{3}[2-9][0-9]{6}([,]{1,10}[0-9]+){0,1}',
|
|
573
573
|
strippedChars: '( )-',
|
|
574
574
|
};
|
|
575
575
|
|
|
@@ -585,6 +585,11 @@ describe('Utils', () => {
|
|
|
585
585
|
const result = Utils.isValidDialNumber('+442030484377', dialPlanEntries);
|
|
586
586
|
expect(result).toBe(true);
|
|
587
587
|
});
|
|
588
|
+
|
|
589
|
+
it('should return true for a European number', () => {
|
|
590
|
+
const result = Utils.isValidDialNumber('6955577166', dialPlanEntries);
|
|
591
|
+
expect(result).toBe(true);
|
|
592
|
+
});
|
|
588
593
|
});
|
|
589
594
|
|
|
590
595
|
describe('with US-only dial plan entry', () => {
|
|
@@ -606,17 +611,95 @@ describe('Utils', () => {
|
|
|
606
611
|
});
|
|
607
612
|
});
|
|
608
613
|
|
|
609
|
-
describe('with empty dial plan entries (
|
|
610
|
-
it('should return true for
|
|
611
|
-
|
|
614
|
+
describe('with empty dial plan entries (defers to server)', () => {
|
|
615
|
+
it('should return true for any dial number', () => {
|
|
616
|
+
expect(Utils.isValidDialNumber('12223334567', [])).toBe(true);
|
|
617
|
+
});
|
|
618
|
+
|
|
619
|
+
it('should return true for a UK phone number', () => {
|
|
620
|
+
expect(Utils.isValidDialNumber('+442030484377', [])).toBe(true);
|
|
621
|
+
});
|
|
622
|
+
|
|
623
|
+
it('should return true for a European number', () => {
|
|
624
|
+
expect(Utils.isValidDialNumber('6955577166', [])).toBe(true);
|
|
625
|
+
});
|
|
626
|
+
});
|
|
627
|
+
|
|
628
|
+
describe('strippedChars handling', () => {
|
|
629
|
+
it('should strip characters before regex matching', () => {
|
|
630
|
+
const strictEntry = {
|
|
631
|
+
name: 'Digits Only',
|
|
632
|
+
prefix: '',
|
|
633
|
+
regex: '^[0-9]{10,15}$',
|
|
634
|
+
strippedChars: '( )-+',
|
|
635
|
+
};
|
|
636
|
+
const result = Utils.isValidDialNumber('+44 (203) 048-4377', [strictEntry]);
|
|
612
637
|
expect(result).toBe(true);
|
|
613
638
|
});
|
|
614
639
|
|
|
615
|
-
it('should
|
|
616
|
-
const
|
|
640
|
+
it('should handle entries with no strippedChars', () => {
|
|
641
|
+
const noStripEntry = {
|
|
642
|
+
name: 'No Strip',
|
|
643
|
+
prefix: '',
|
|
644
|
+
regex: '^[0-9]+$',
|
|
645
|
+
strippedChars: '',
|
|
646
|
+
};
|
|
647
|
+
expect(Utils.isValidDialNumber('12345', [noStripEntry])).toBe(true);
|
|
648
|
+
expect(Utils.isValidDialNumber('+12345', [noStripEntry])).toBe(false);
|
|
649
|
+
});
|
|
650
|
+
});
|
|
651
|
+
|
|
652
|
+
describe('empty or undefined dial number', () => {
|
|
653
|
+
it('should return false and log warning for undefined dial number', () => {
|
|
654
|
+
const result = Utils.isValidDialNumber(undefined as any, [anyFormatEntry]);
|
|
655
|
+
expect(result).toBe(false);
|
|
656
|
+
expect(LoggerProxy.warn).toHaveBeenCalledWith(
|
|
657
|
+
'Dial number is empty or undefined.',
|
|
658
|
+
expect.objectContaining({module: 'Utils', method: 'isValidDialNumber'})
|
|
659
|
+
);
|
|
660
|
+
});
|
|
661
|
+
|
|
662
|
+
it('should return false and log warning for empty string dial number', () => {
|
|
663
|
+
const result = Utils.isValidDialNumber('', [anyFormatEntry]);
|
|
664
|
+
expect(result).toBe(false);
|
|
665
|
+
expect(LoggerProxy.warn).toHaveBeenCalledWith(
|
|
666
|
+
'Dial number is empty or undefined.',
|
|
667
|
+
expect.objectContaining({module: 'Utils', method: 'isValidDialNumber'})
|
|
668
|
+
);
|
|
669
|
+
});
|
|
670
|
+
});
|
|
671
|
+
|
|
672
|
+
describe('invalid regex handling', () => {
|
|
673
|
+
it('should return false and log warning for invalid regex pattern', () => {
|
|
674
|
+
const badEntry = {
|
|
675
|
+
name: 'Bad Regex',
|
|
676
|
+
prefix: '',
|
|
677
|
+
regex: '[invalid(',
|
|
678
|
+
strippedChars: '',
|
|
679
|
+
};
|
|
680
|
+
const result = Utils.isValidDialNumber('12345', [badEntry]);
|
|
617
681
|
expect(result).toBe(false);
|
|
682
|
+
expect(LoggerProxy.warn).toHaveBeenCalledWith(
|
|
683
|
+
expect.stringContaining('Failed to validate dial number against entry "Bad Regex"'),
|
|
684
|
+
expect.objectContaining({module: 'Utils', method: 'isValidDialNumber'})
|
|
685
|
+
);
|
|
618
686
|
});
|
|
619
687
|
});
|
|
620
688
|
});
|
|
621
689
|
|
|
690
|
+
describe('stripDialPlanChars', () => {
|
|
691
|
+
it('should remove specified characters from input', () => {
|
|
692
|
+
expect(Utils.stripDialPlanChars('+44 (203) 048-4377', '( )-+')).toBe('442030484377');
|
|
693
|
+
});
|
|
694
|
+
|
|
695
|
+
it('should return input unchanged when strippedChars is empty', () => {
|
|
696
|
+
expect(Utils.stripDialPlanChars('+442030484377', '')).toBe('+442030484377');
|
|
697
|
+
});
|
|
698
|
+
|
|
699
|
+
it('should return input unchanged when strippedChars is null/undefined', () => {
|
|
700
|
+
expect(Utils.stripDialPlanChars('12345', null as any)).toBe('12345');
|
|
701
|
+
expect(Utils.stripDialPlanChars('12345', undefined as any)).toBe('12345');
|
|
702
|
+
});
|
|
703
|
+
});
|
|
704
|
+
|
|
622
705
|
});
|