@webex/contact-center 3.10.0-next.2 → 3.10.0-next.21

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 (103) hide show
  1. package/dist/cc.js +13 -1
  2. package/dist/cc.js.map +1 -1
  3. package/dist/config.js.map +1 -1
  4. package/dist/constants.js.map +1 -1
  5. package/dist/index.js +17 -1
  6. package/dist/index.js.map +1 -1
  7. package/dist/logger-proxy.js.map +1 -1
  8. package/dist/metrics/MetricsManager.js +2 -1
  9. package/dist/metrics/MetricsManager.js.map +1 -1
  10. package/dist/metrics/behavioral-events.js +12 -0
  11. package/dist/metrics/behavioral-events.js.map +1 -1
  12. package/dist/metrics/constants.js +4 -0
  13. package/dist/metrics/constants.js.map +1 -1
  14. package/dist/services/AddressBook.js +2 -3
  15. package/dist/services/AddressBook.js.map +1 -1
  16. package/dist/services/EntryPoint.js +2 -3
  17. package/dist/services/EntryPoint.js.map +1 -1
  18. package/dist/services/Queue.js +2 -3
  19. package/dist/services/Queue.js.map +1 -1
  20. package/dist/services/WebCallingService.js +1 -1
  21. package/dist/services/WebCallingService.js.map +1 -1
  22. package/dist/services/agent/index.js +1 -2
  23. package/dist/services/agent/index.js.map +1 -1
  24. package/dist/services/agent/types.js +10 -0
  25. package/dist/services/agent/types.js.map +1 -1
  26. package/dist/services/config/Util.js.map +1 -1
  27. package/dist/services/config/constants.js.map +1 -1
  28. package/dist/services/config/index.js +1 -1
  29. package/dist/services/config/index.js.map +1 -1
  30. package/dist/services/config/types.js +2 -2
  31. package/dist/services/config/types.js.map +1 -1
  32. package/dist/services/constants.js.map +1 -1
  33. package/dist/services/core/Err.js.map +1 -1
  34. package/dist/services/core/GlobalTypes.js.map +1 -1
  35. package/dist/services/core/Utils.js +92 -74
  36. package/dist/services/core/Utils.js.map +1 -1
  37. package/dist/services/core/WebexRequest.js +1 -2
  38. package/dist/services/core/WebexRequest.js.map +1 -1
  39. package/dist/services/core/aqm-reqs.js +2 -3
  40. package/dist/services/core/aqm-reqs.js.map +1 -1
  41. package/dist/services/core/constants.js +17 -1
  42. package/dist/services/core/constants.js.map +1 -1
  43. package/dist/services/core/types.js.map +1 -1
  44. package/dist/services/core/websocket/WebSocketManager.js +1 -2
  45. package/dist/services/core/websocket/WebSocketManager.js.map +1 -1
  46. package/dist/services/core/websocket/connection-service.js +1 -1
  47. package/dist/services/core/websocket/connection-service.js.map +1 -1
  48. package/dist/services/core/websocket/keepalive.worker.js.map +1 -1
  49. package/dist/services/core/websocket/types.js.map +1 -1
  50. package/dist/services/index.js +1 -1
  51. package/dist/services/index.js.map +1 -1
  52. package/dist/services/task/AutoWrapup.js +1 -1
  53. package/dist/services/task/AutoWrapup.js.map +1 -1
  54. package/dist/services/task/TaskManager.js +177 -56
  55. package/dist/services/task/TaskManager.js.map +1 -1
  56. package/dist/services/task/TaskUtils.js +122 -5
  57. package/dist/services/task/TaskUtils.js.map +1 -1
  58. package/dist/services/task/constants.js +3 -1
  59. package/dist/services/task/constants.js.map +1 -1
  60. package/dist/services/task/contact.js +0 -2
  61. package/dist/services/task/contact.js.map +1 -1
  62. package/dist/services/task/dialer.js.map +1 -1
  63. package/dist/services/task/index.js +46 -40
  64. package/dist/services/task/index.js.map +1 -1
  65. package/dist/services/task/types.js +377 -4
  66. package/dist/services/task/types.js.map +1 -1
  67. package/dist/types/cc.d.ts +6 -0
  68. package/dist/types/index.d.ts +1 -1
  69. package/dist/types/metrics/constants.d.ts +4 -0
  70. package/dist/types/services/config/types.d.ts +4 -4
  71. package/dist/types/services/core/Utils.d.ts +32 -17
  72. package/dist/types/services/core/constants.d.ts +14 -0
  73. package/dist/types/services/task/TaskUtils.d.ts +59 -3
  74. package/dist/types/services/task/constants.d.ts +2 -0
  75. package/dist/types/services/task/types.d.ts +57 -13
  76. package/dist/types.js +5 -0
  77. package/dist/types.js.map +1 -1
  78. package/dist/utils/PageCache.js +1 -1
  79. package/dist/utils/PageCache.js.map +1 -1
  80. package/dist/webex-config.js.map +1 -1
  81. package/dist/webex.js +2 -2
  82. package/dist/webex.js.map +1 -1
  83. package/package.json +8 -8
  84. package/src/cc.ts +12 -0
  85. package/src/index.ts +1 -0
  86. package/src/metrics/behavioral-events.ts +12 -0
  87. package/src/metrics/constants.ts +4 -0
  88. package/src/services/config/types.ts +2 -2
  89. package/src/services/core/Utils.ts +101 -85
  90. package/src/services/core/constants.ts +16 -0
  91. package/src/services/task/TaskManager.ts +204 -36
  92. package/src/services/task/TaskUtils.ts +145 -5
  93. package/src/services/task/constants.ts +2 -0
  94. package/src/services/task/index.ts +50 -63
  95. package/src/services/task/types.ts +60 -13
  96. package/test/unit/spec/cc.ts +1 -0
  97. package/test/unit/spec/metrics/behavioral-events.ts +14 -0
  98. package/test/unit/spec/services/core/Utils.ts +262 -31
  99. package/test/unit/spec/services/task/TaskManager.ts +748 -5
  100. package/test/unit/spec/services/task/TaskUtils.ts +311 -9
  101. package/test/unit/spec/services/task/index.ts +323 -68
  102. package/umd/contact-center.min.js +2 -2
  103. package/umd/contact-center.min.js.map +1 -1
@@ -1,8 +1,16 @@
1
- import { checkParticipantNotInInteraction,
1
+ import {
2
+ checkParticipantNotInInteraction,
2
3
  getIsConferenceInProgress,
3
4
  isParticipantInMainInteraction,
4
- isPrimary,} from '../../../../../src/services/task/TaskUtils';
5
- import {ITask} from '../../../../../src/services/task/types';
5
+ isPrimary,
6
+ isAutoAnswerEnabled,
7
+ isWebRTCCall,
8
+ isDigitalOutbound,
9
+ hasAgentInitiatedOutdial,
10
+ shouldAutoAnswerTask,
11
+ } from '../../../../../src/services/task/TaskUtils';
12
+ import {ITask, Interaction, TaskData} from '../../../../../src/services/task/types';
13
+ import {LoginOption} from '../../../../../src/types';
6
14
 
7
15
  describe('TaskUtils', () => {
8
16
  let mockTask: ITask;
@@ -98,34 +106,328 @@ describe('TaskUtils', () => {
98
106
  });
99
107
 
100
108
  it('should return true when there are 2 or more active agents', () => {
101
- expect(getIsConferenceInProgress(mockTask)).toBe(true);
109
+ expect(getIsConferenceInProgress(mockTask.data)).toBe(true);
102
110
  });
103
111
 
104
112
  it('should return false when there is only 1 active agent', () => {
105
113
  mockTask.data.interaction.participants[mockOtherAgentId].hasLeft = true;
106
- expect(getIsConferenceInProgress(mockTask)).toBe(false);
114
+ expect(getIsConferenceInProgress(mockTask.data)).toBe(false);
107
115
  });
108
116
 
109
117
  it('should exclude customers from agent count', () => {
110
118
  // Remove one agent, should still be false with only 1 agent + customer
111
119
  delete mockTask.data.interaction.participants[mockOtherAgentId];
112
120
  mockTask.data.interaction.media[mockTask.data.interactionId].participants = [mockAgentId, 'customer-123'];
113
- expect(getIsConferenceInProgress(mockTask)).toBe(false);
121
+ expect(getIsConferenceInProgress(mockTask.data)).toBe(false);
114
122
  });
115
123
 
116
124
  it('should exclude supervisors from agent count', () => {
117
125
  mockTask.data.interaction.participants[mockOtherAgentId].pType = 'Supervisor';
118
- expect(getIsConferenceInProgress(mockTask)).toBe(false);
126
+ expect(getIsConferenceInProgress(mockTask.data)).toBe(false);
119
127
  });
120
128
 
121
129
  it('should exclude VVA from agent count', () => {
122
130
  mockTask.data.interaction.participants[mockOtherAgentId].pType = 'VVA';
123
- expect(getIsConferenceInProgress(mockTask)).toBe(false);
131
+ expect(getIsConferenceInProgress(mockTask.data)).toBe(false);
124
132
  });
125
133
 
126
134
  it('should return false when no main call media exists', () => {
127
135
  mockTask.data.interaction.media = {};
128
- expect(getIsConferenceInProgress(mockTask)).toBe(false);
136
+ expect(getIsConferenceInProgress(mockTask.data)).toBe(false);
137
+ });
138
+ });
139
+
140
+ describe('Auto-Answer Helper Functions', () => {
141
+ let mockInteraction: Interaction;
142
+
143
+ beforeEach(() => {
144
+ mockInteraction = {
145
+ interactionId: 'interaction-123',
146
+ mediaType: 'telephony',
147
+ mediaChannel: 'telephony',
148
+ participants: {
149
+ [mockAgentId]: {
150
+ id: mockAgentId,
151
+ pType: 'Agent',
152
+ autoAnswerEnabled: false,
153
+ hasJoined: false,
154
+ hasLeft: false,
155
+ },
156
+ },
157
+ owner: mockAgentId,
158
+ contactDirection: {type: 'INBOUND'},
159
+ outboundType: null,
160
+ callProcessingDetails: {
161
+ QMgrName: 'aqm',
162
+ taskToBeSelfServiced: 'false',
163
+ ani: '+1234567890',
164
+ displayAni: '+1234567890',
165
+ dnis: '+0987654321',
166
+ tenantId: 'tenant-123',
167
+ QueueId: 'queue-123',
168
+ vteamId: 'vteam-123',
169
+ customerName: 'Test Customer',
170
+ virtualTeamName: 'Test Team',
171
+ ronaTimeout: '30',
172
+ category: 'Support',
173
+ reason: 'General',
174
+ sourceNumber: '+1234567890',
175
+ sourcePage: 'web',
176
+ appUser: 'test-app',
177
+ customerNumber: '+1234567890',
178
+ reasonCode: '100',
179
+ IvrPath: '/ivr/path',
180
+ pathId: 'path-123',
181
+ fromAddress: 'customer@example.com',
182
+ },
183
+ previousVTeams: [],
184
+ state: 'new',
185
+ currentVTeam: 'vteam-123',
186
+ isFcManaged: false,
187
+ isTerminated: false,
188
+ orgId: 'org-123',
189
+ createdTimestamp: Date.now(),
190
+ media: {},
191
+ } as any;
192
+ });
193
+
194
+ describe('isAutoAnswerEnabled', () => {
195
+ it('should return true when autoAnswerEnabled is true', () => {
196
+ mockInteraction.participants[mockAgentId].autoAnswerEnabled = true;
197
+ expect(isAutoAnswerEnabled(mockInteraction, mockAgentId)).toBe(true);
198
+ });
199
+
200
+ it('should return false when autoAnswerEnabled is false', () => {
201
+ mockInteraction.participants[mockAgentId].autoAnswerEnabled = false;
202
+ expect(isAutoAnswerEnabled(mockInteraction, mockAgentId)).toBe(false);
203
+ });
204
+
205
+ it('should return false when autoAnswerEnabled is not set', () => {
206
+ delete mockInteraction.participants[mockAgentId].autoAnswerEnabled;
207
+ expect(isAutoAnswerEnabled(mockInteraction, mockAgentId)).toBe(false);
208
+ });
209
+
210
+ it('should return false when participant does not exist', () => {
211
+ expect(isAutoAnswerEnabled(mockInteraction, 'non-existent-agent')).toBe(false);
212
+ });
213
+ });
214
+
215
+ describe('isWebRTCCall', () => {
216
+ it('should return true for BROWSER login with telephony media type and webRTC enabled', () => {
217
+ expect(isWebRTCCall(mockInteraction, LoginOption.BROWSER, true)).toBe(true);
218
+ });
219
+
220
+ it('should return false when webRTC is disabled', () => {
221
+ expect(isWebRTCCall(mockInteraction, LoginOption.BROWSER, false)).toBe(false);
222
+ });
223
+
224
+ it('should return false for AGENT_DN login', () => {
225
+ expect(isWebRTCCall(mockInteraction, LoginOption.AGENT_DN, true)).toBe(false);
226
+ });
227
+
228
+ it('should return false for EXTENSION login', () => {
229
+ expect(isWebRTCCall(mockInteraction, LoginOption.EXTENSION, true)).toBe(false);
230
+ });
231
+
232
+ it('should return false for BROWSER login with non-telephony media type', () => {
233
+ mockInteraction.mediaType = 'email';
234
+ expect(isWebRTCCall(mockInteraction, LoginOption.BROWSER, true)).toBe(false);
235
+ });
236
+ });
237
+
238
+ describe('isDigitalOutbound', () => {
239
+ it('should return true for email outdial', () => {
240
+ mockInteraction.contactDirection = {type: 'OUTBOUND'};
241
+ mockInteraction.outboundType = 'OUTDIAL';
242
+ mockInteraction.mediaChannel = 'email';
243
+ expect(isDigitalOutbound(mockInteraction)).toBe(true);
244
+ });
245
+
246
+ it('should return true for SMS outdial', () => {
247
+ mockInteraction.contactDirection = {type: 'OUTBOUND'};
248
+ mockInteraction.outboundType = 'OUTDIAL';
249
+ mockInteraction.mediaChannel = 'sms';
250
+ expect(isDigitalOutbound(mockInteraction)).toBe(true);
251
+ });
252
+
253
+ it('should return false for telephony outdial', () => {
254
+ mockInteraction.contactDirection = {type: 'OUTBOUND'};
255
+ mockInteraction.outboundType = 'OUTDIAL';
256
+ mockInteraction.mediaChannel = 'telephony';
257
+ expect(isDigitalOutbound(mockInteraction)).toBe(false);
258
+ });
259
+
260
+ it('should return false for inbound email', () => {
261
+ mockInteraction.contactDirection = {type: 'INBOUND'};
262
+ mockInteraction.mediaChannel = 'email';
263
+ expect(isDigitalOutbound(mockInteraction)).toBe(false);
264
+ });
265
+
266
+ it('should return false when outboundType is not OUTDIAL', () => {
267
+ mockInteraction.contactDirection = {type: 'OUTBOUND'};
268
+ mockInteraction.outboundType = 'CALLBACK';
269
+ mockInteraction.mediaChannel = 'email';
270
+ expect(isDigitalOutbound(mockInteraction)).toBe(false);
271
+ });
272
+ });
273
+
274
+ describe('hasAgentInitiatedOutdial', () => {
275
+ beforeEach(() => {
276
+ mockInteraction.contactDirection = {type: 'OUTBOUND'};
277
+ mockInteraction.outboundType = 'OUTDIAL';
278
+ mockInteraction.owner = mockAgentId;
279
+ mockInteraction.callProcessingDetails.outdialAgentId = mockAgentId;
280
+ mockInteraction.callProcessingDetails.BLIND_TRANSFER_IN_PROGRESS = false;
281
+ });
282
+
283
+ it('should return true for agent-initiated outdial', () => {
284
+ expect(hasAgentInitiatedOutdial(mockInteraction, mockAgentId)).toBe(true);
285
+ });
286
+
287
+ it('should return false when not outbound', () => {
288
+ mockInteraction.contactDirection = {type: 'INBOUND'};
289
+ expect(hasAgentInitiatedOutdial(mockInteraction, mockAgentId)).toBe(false);
290
+ });
291
+
292
+ it('should return false when not OUTDIAL type', () => {
293
+ mockInteraction.outboundType = 'CALLBACK';
294
+ expect(hasAgentInitiatedOutdial(mockInteraction, mockAgentId)).toBe(false);
295
+ });
296
+
297
+ it('should return false when outdialAgentId does not match', () => {
298
+ mockInteraction.callProcessingDetails.outdialAgentId = mockOtherAgentId;
299
+ expect(hasAgentInitiatedOutdial(mockInteraction, mockAgentId)).toBe(false);
300
+ });
301
+
302
+ it('should return false when owner does not match', () => {
303
+ mockInteraction.owner = mockOtherAgentId;
304
+ expect(hasAgentInitiatedOutdial(mockInteraction, mockAgentId)).toBe(false);
305
+ });
306
+
307
+ it('should return false when blind transfer is in progress', () => {
308
+ mockInteraction.callProcessingDetails.BLIND_TRANSFER_IN_PROGRESS = true;
309
+ expect(hasAgentInitiatedOutdial(mockInteraction, mockAgentId)).toBe(false);
310
+ });
311
+ });
312
+
313
+ describe('shouldAutoAnswerTask', () => {
314
+ let mockTaskData: TaskData;
315
+
316
+ beforeEach(() => {
317
+ mockTaskData = {
318
+ interactionId: 'interaction-123',
319
+ agentId: mockAgentId,
320
+ interaction: mockInteraction,
321
+ } as any;
322
+ });
323
+
324
+ describe('WebRTC scenarios', () => {
325
+ beforeEach(() => {
326
+ mockInteraction.mediaType = 'telephony';
327
+ });
328
+
329
+ it('should return true when auto-answer is enabled for WebRTC call', () => {
330
+ mockInteraction.participants[mockAgentId].autoAnswerEnabled = true;
331
+ expect(shouldAutoAnswerTask(mockTaskData, mockAgentId, LoginOption.BROWSER, true)).toBe(
332
+ true
333
+ );
334
+ });
335
+
336
+ it('should return true for agent-initiated WebRTC outdial', () => {
337
+ mockInteraction.contactDirection = {type: 'OUTBOUND'};
338
+ mockInteraction.outboundType = 'OUTDIAL';
339
+ mockInteraction.owner = mockAgentId;
340
+ mockInteraction.callProcessingDetails.outdialAgentId = mockAgentId;
341
+ mockInteraction.callProcessingDetails.BLIND_TRANSFER_IN_PROGRESS = false;
342
+ expect(shouldAutoAnswerTask(mockTaskData, mockAgentId, LoginOption.BROWSER, true)).toBe(
343
+ true
344
+ );
345
+ });
346
+
347
+ it('should return false for WebRTC call without auto-answer or outdial', () => {
348
+ mockInteraction.participants[mockAgentId].autoAnswerEnabled = false;
349
+ expect(shouldAutoAnswerTask(mockTaskData, mockAgentId, LoginOption.BROWSER, true)).toBe(
350
+ false
351
+ );
352
+ });
353
+
354
+ it('should return false when webRTC is disabled', () => {
355
+ mockInteraction.participants[mockAgentId].autoAnswerEnabled = true;
356
+ expect(shouldAutoAnswerTask(mockTaskData, mockAgentId, LoginOption.BROWSER, false)).toBe(
357
+ false
358
+ );
359
+ });
360
+
361
+ it('should return false for non-BROWSER login', () => {
362
+ mockInteraction.participants[mockAgentId].autoAnswerEnabled = true;
363
+ expect(shouldAutoAnswerTask(mockTaskData, mockAgentId, LoginOption.AGENT_DN, true)).toBe(
364
+ false
365
+ );
366
+ });
367
+ });
368
+
369
+ describe('Digital outbound scenarios', () => {
370
+ beforeEach(() => {
371
+ mockInteraction.contactDirection = {type: 'OUTBOUND'};
372
+ mockInteraction.outboundType = 'OUTDIAL';
373
+ mockInteraction.owner = mockAgentId;
374
+ mockInteraction.callProcessingDetails.outdialAgentId = mockAgentId;
375
+ mockInteraction.previousVTeams = [];
376
+ });
377
+
378
+ it('should return true for agent-initiated email outdial', () => {
379
+ mockInteraction.mediaType = 'email';
380
+ mockInteraction.mediaChannel = 'email';
381
+ expect(shouldAutoAnswerTask(mockTaskData, mockAgentId, LoginOption.BROWSER, true)).toBe(
382
+ true
383
+ );
384
+ });
385
+
386
+ it('should return true for agent-initiated SMS outdial', () => {
387
+ mockInteraction.mediaType = 'sms';
388
+ mockInteraction.mediaChannel = 'sms';
389
+ expect(shouldAutoAnswerTask(mockTaskData, mockAgentId, LoginOption.BROWSER, true)).toBe(
390
+ true
391
+ );
392
+ });
393
+
394
+ it('should return false when digital outbound has previous vteams', () => {
395
+ mockInteraction.mediaType = 'email';
396
+ mockInteraction.mediaChannel = 'email';
397
+ mockInteraction.previousVTeams = ['vteam-1'];
398
+ expect(shouldAutoAnswerTask(mockTaskData, mockAgentId, LoginOption.BROWSER, true)).toBe(
399
+ false
400
+ );
401
+ });
402
+
403
+ it('should return false when digital outbound is not agent-initiated', () => {
404
+ mockInteraction.mediaType = 'email';
405
+ mockInteraction.mediaChannel = 'email';
406
+ mockInteraction.owner = mockOtherAgentId;
407
+ expect(shouldAutoAnswerTask(mockTaskData, mockAgentId, LoginOption.BROWSER, true)).toBe(
408
+ false
409
+ );
410
+ });
411
+ });
412
+
413
+ describe('Edge cases', () => {
414
+ it('should return false when interaction is null', () => {
415
+ mockTaskData.interaction = null as any;
416
+ expect(shouldAutoAnswerTask(mockTaskData, mockAgentId, LoginOption.BROWSER, true)).toBe(
417
+ false
418
+ );
419
+ });
420
+
421
+ it('should return false when agentId is null', () => {
422
+ expect(shouldAutoAnswerTask(mockTaskData, null as any, LoginOption.BROWSER, true)).toBe(
423
+ false
424
+ );
425
+ });
426
+
427
+ it('should return false when agentId is empty string', () => {
428
+ expect(shouldAutoAnswerTask(mockTaskData, '', LoginOption.BROWSER, true)).toBe(false);
429
+ });
430
+ });
129
431
  });
130
432
  });
131
433
  });