@webex/contact-center 3.9.0-next.25 → 3.9.0-next.26
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 +1 -1
- package/dist/cc.js.map +1 -1
- package/dist/services/task/TaskManager.js +49 -10
- package/dist/services/task/TaskManager.js.map +1 -1
- package/dist/services/task/TaskUtils.js +76 -0
- package/dist/services/task/TaskUtils.js.map +1 -0
- package/dist/services/task/constants.js +20 -1
- package/dist/services/task/constants.js.map +1 -1
- package/dist/services/task/index.js +46 -48
- package/dist/services/task/index.js.map +1 -1
- package/dist/services/task/types.js.map +1 -1
- package/dist/types/services/task/TaskUtils.d.ts +28 -0
- package/dist/types/services/task/constants.d.ts +17 -0
- package/dist/types/services/task/index.d.ts +21 -0
- package/dist/types/services/task/types.d.ts +96 -18
- package/dist/webex.js +1 -1
- package/package.json +1 -1
- package/src/cc.ts +1 -1
- package/src/services/task/TaskManager.ts +49 -7
- package/src/services/task/TaskUtils.ts +81 -0
- package/src/services/task/constants.ts +19 -0
- package/src/services/task/index.ts +20 -8
- package/src/services/task/types.ts +104 -18
- package/test/unit/spec/services/task/TaskManager.ts +256 -11
- package/test/unit/spec/services/task/TaskUtils.ts +131 -0
- package/test/unit/spec/services/task/index.ts +62 -11
- package/umd/contact-center.min.js +2 -2
- package/umd/contact-center.min.js.map +1 -1
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { checkParticipantNotInInteraction,
|
|
2
|
+
getIsConferenceInProgress,
|
|
3
|
+
isParticipantInMainInteraction,
|
|
4
|
+
isPrimary,} from '../../../../../src/services/task/TaskUtils';
|
|
5
|
+
import {ITask} from '../../../../../src/services/task/types';
|
|
6
|
+
|
|
7
|
+
describe('TaskUtils', () => {
|
|
8
|
+
let mockTask: ITask;
|
|
9
|
+
const mockAgentId = 'agent-123';
|
|
10
|
+
const mockOtherAgentId = 'agent-456';
|
|
11
|
+
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
mockTask = {
|
|
14
|
+
data: {
|
|
15
|
+
interactionId: 'interaction-123',
|
|
16
|
+
agentId: mockAgentId,
|
|
17
|
+
interaction: {
|
|
18
|
+
owner: mockAgentId,
|
|
19
|
+
participants: {
|
|
20
|
+
[mockAgentId]: { hasLeft: false },
|
|
21
|
+
[mockOtherAgentId]: { hasLeft: false },
|
|
22
|
+
},
|
|
23
|
+
media: {
|
|
24
|
+
'media-1': {
|
|
25
|
+
mType: 'mainCall',
|
|
26
|
+
participants: [mockAgentId, mockOtherAgentId],
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
emit: jest.fn(),
|
|
32
|
+
updateTaskData: jest.fn(),
|
|
33
|
+
} as any;
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
describe('isPrimary', () => {
|
|
37
|
+
it('should return true when agent is the owner', () => {
|
|
38
|
+
expect(isPrimary(mockTask, mockAgentId)).toBe(true);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('should return false when agent is not the owner', () => {
|
|
42
|
+
expect(isPrimary(mockTask, mockOtherAgentId)).toBe(false);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('should fallback to data.agentId when owner is not set', () => {
|
|
46
|
+
mockTask.data.interaction.owner = undefined;
|
|
47
|
+
expect(isPrimary(mockTask, mockAgentId)).toBe(true);
|
|
48
|
+
expect(isPrimary(mockTask, mockOtherAgentId)).toBe(false);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
describe('isParticipantInMainInteraction', () => {
|
|
53
|
+
it('should return true when agent is in mainCall media', () => {
|
|
54
|
+
expect(isParticipantInMainInteraction(mockTask, mockAgentId)).toBe(true);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('should return false when agent is not in mainCall media', () => {
|
|
58
|
+
mockTask.data.interaction.media['media-1'].participants = [mockOtherAgentId];
|
|
59
|
+
expect(isParticipantInMainInteraction(mockTask, mockAgentId)).toBe(false);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('should return false when no mainCall media exists', () => {
|
|
63
|
+
mockTask.data.interaction.media['media-1'].mType = 'consult';
|
|
64
|
+
expect(isParticipantInMainInteraction(mockTask, mockAgentId)).toBe(false);
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
describe('checkParticipantNotInInteraction', () => {
|
|
69
|
+
it('should return false when agent is active participant', () => {
|
|
70
|
+
expect(checkParticipantNotInInteraction(mockTask, mockAgentId)).toBe(false);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('should return true when agent is not in participants', () => {
|
|
74
|
+
delete mockTask.data.interaction.participants[mockAgentId];
|
|
75
|
+
expect(checkParticipantNotInInteraction(mockTask, mockAgentId)).toBe(true);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('should return true when agent has left', () => {
|
|
79
|
+
mockTask.data.interaction.participants[mockAgentId].hasLeft = true;
|
|
80
|
+
expect(checkParticipantNotInInteraction(mockTask, mockAgentId)).toBe(true);
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
describe('getIsConferenceInProgress', () => {
|
|
85
|
+
beforeEach(() => {
|
|
86
|
+
// Set up mock task with proper media structure for conference detection
|
|
87
|
+
mockTask.data.interaction.media = {
|
|
88
|
+
[mockTask.data.interactionId]: {
|
|
89
|
+
mType: 'mainCall',
|
|
90
|
+
participants: [mockAgentId, mockOtherAgentId, 'customer-123'],
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
mockTask.data.interaction.participants = {
|
|
94
|
+
[mockAgentId]: { pType: 'Agent', hasLeft: false },
|
|
95
|
+
[mockOtherAgentId]: { pType: 'Agent', hasLeft: false },
|
|
96
|
+
'customer-123': { pType: 'Customer', hasLeft: false },
|
|
97
|
+
};
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('should return true when there are 2 or more active agents', () => {
|
|
101
|
+
expect(getIsConferenceInProgress(mockTask)).toBe(true);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('should return false when there is only 1 active agent', () => {
|
|
105
|
+
mockTask.data.interaction.participants[mockOtherAgentId].hasLeft = true;
|
|
106
|
+
expect(getIsConferenceInProgress(mockTask)).toBe(false);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('should exclude customers from agent count', () => {
|
|
110
|
+
// Remove one agent, should still be false with only 1 agent + customer
|
|
111
|
+
delete mockTask.data.interaction.participants[mockOtherAgentId];
|
|
112
|
+
mockTask.data.interaction.media[mockTask.data.interactionId].participants = [mockAgentId, 'customer-123'];
|
|
113
|
+
expect(getIsConferenceInProgress(mockTask)).toBe(false);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('should exclude supervisors from agent count', () => {
|
|
117
|
+
mockTask.data.interaction.participants[mockOtherAgentId].pType = 'Supervisor';
|
|
118
|
+
expect(getIsConferenceInProgress(mockTask)).toBe(false);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('should exclude VVA from agent count', () => {
|
|
122
|
+
mockTask.data.interaction.participants[mockOtherAgentId].pType = 'VVA';
|
|
123
|
+
expect(getIsConferenceInProgress(mockTask)).toBe(false);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it('should return false when no main call media exists', () => {
|
|
127
|
+
mockTask.data.interaction.media = {};
|
|
128
|
+
expect(getIsConferenceInProgress(mockTask)).toBe(false);
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
});
|
|
@@ -217,7 +217,7 @@ describe('Task', () => {
|
|
|
217
217
|
});
|
|
218
218
|
|
|
219
219
|
describe('updateTaskData cases', () => {
|
|
220
|
-
it('
|
|
220
|
+
it('updates the task data by overwrite', async () => {
|
|
221
221
|
const newData = {
|
|
222
222
|
type: CC_EVENTS.AGENT_CONTACT_ASSIGNED,
|
|
223
223
|
agentId: '723a8ffb-a26e-496d-b14a-ff44fb83b64f',
|
|
@@ -266,12 +266,12 @@ describe('Task', () => {
|
|
|
266
266
|
expect(task.data).toEqual(newData);
|
|
267
267
|
});
|
|
268
268
|
|
|
269
|
-
it('
|
|
269
|
+
it('updates the task data by merging with key removal', async () => {
|
|
270
270
|
const newData = {
|
|
271
|
-
//
|
|
271
|
+
// Purposefully omit other keys to test remove and merge behavior
|
|
272
272
|
isConsulting: true, // Add a new custom key to test persistence
|
|
273
273
|
interaction: {
|
|
274
|
-
//
|
|
274
|
+
// Purposefully omit other interaction keys to test removal
|
|
275
275
|
media: {
|
|
276
276
|
'58a45567-4e61-4f4b-a580-5bc86357bef0': {
|
|
277
277
|
holdTimestamp: null,
|
|
@@ -298,11 +298,12 @@ describe('Task', () => {
|
|
|
298
298
|
},
|
|
299
299
|
};
|
|
300
300
|
|
|
301
|
+
// The reconcileData method removes keys from oldData that are not in newData
|
|
302
|
+
// This means only keys present in newData will remain in the final result
|
|
301
303
|
const expectedData: TaskData = {
|
|
302
|
-
|
|
303
|
-
isConsulting: true,
|
|
304
|
+
isConsulting: true, // New key is added
|
|
304
305
|
interaction: {
|
|
305
|
-
|
|
306
|
+
// Only the media key from newData.interaction remains
|
|
306
307
|
media: {
|
|
307
308
|
'58a45567-4e61-4f4b-a580-5bc86357bef0': {
|
|
308
309
|
holdTimestamp: null,
|
|
@@ -335,6 +336,60 @@ describe('Task', () => {
|
|
|
335
336
|
|
|
336
337
|
expect(task.data).toEqual(expectedData);
|
|
337
338
|
});
|
|
339
|
+
|
|
340
|
+
it('updates the task data by merging and preserving existing keys', async () => {
|
|
341
|
+
const newData = {
|
|
342
|
+
...taskDataMock, // Include all existing keys to test merge without removal
|
|
343
|
+
isConsulting: true, // Add a new custom key
|
|
344
|
+
interaction: {
|
|
345
|
+
...taskDataMock.interaction, // Include existing interaction data
|
|
346
|
+
media: {
|
|
347
|
+
...taskDataMock.interaction.media, // Include existing media
|
|
348
|
+
'58a45567-4e61-4f4b-a580-5bc86357bef0': {
|
|
349
|
+
holdTimestamp: null,
|
|
350
|
+
isHold: true,
|
|
351
|
+
mType: 'consult',
|
|
352
|
+
mediaMgr: 'callmm',
|
|
353
|
+
mediaResourceId: '58a45567-4e61-4f4b-a580-5bc86357bef0',
|
|
354
|
+
mediaType: 'telephony',
|
|
355
|
+
participants: [
|
|
356
|
+
'f520d6b5-28ad-4f2f-b83e-781bb64af617',
|
|
357
|
+
'723a8ffb-a26e-496d-b14a-ff44fb83b64f',
|
|
358
|
+
],
|
|
359
|
+
},
|
|
360
|
+
},
|
|
361
|
+
},
|
|
362
|
+
};
|
|
363
|
+
|
|
364
|
+
const expectedData: TaskData = {
|
|
365
|
+
...taskDataMock,
|
|
366
|
+
isConsulting: true,
|
|
367
|
+
interaction: {
|
|
368
|
+
...taskDataMock.interaction,
|
|
369
|
+
media: {
|
|
370
|
+
...taskDataMock.interaction.media,
|
|
371
|
+
'58a45567-4e61-4f4b-a580-5bc86357bef0': {
|
|
372
|
+
holdTimestamp: null,
|
|
373
|
+
isHold: true,
|
|
374
|
+
mType: 'consult',
|
|
375
|
+
mediaMgr: 'callmm',
|
|
376
|
+
mediaResourceId: '58a45567-4e61-4f4b-a580-5bc86357bef0',
|
|
377
|
+
mediaType: 'telephony',
|
|
378
|
+
participants: [
|
|
379
|
+
'f520d6b5-28ad-4f2f-b83e-781bb64af617',
|
|
380
|
+
'723a8ffb-a26e-496d-b14a-ff44fb83b64f',
|
|
381
|
+
],
|
|
382
|
+
},
|
|
383
|
+
},
|
|
384
|
+
},
|
|
385
|
+
};
|
|
386
|
+
|
|
387
|
+
expect(task.data).toEqual(taskDataMock);
|
|
388
|
+
const shouldOverwrite = false;
|
|
389
|
+
task.updateTaskData(newData, shouldOverwrite);
|
|
390
|
+
|
|
391
|
+
expect(task.data).toEqual(expectedData);
|
|
392
|
+
});
|
|
338
393
|
});
|
|
339
394
|
|
|
340
395
|
it('should accept a task and answer call when using BROWSER login option', async () => {
|
|
@@ -1707,9 +1762,6 @@ describe('Task', () => {
|
|
|
1707
1762
|
});
|
|
1708
1763
|
});
|
|
1709
1764
|
|
|
1710
|
-
// TODO: Uncomment this test section in future PR for Multi-Party Conference support (>3 participants)
|
|
1711
|
-
// Conference transfer tests will be uncommented when implementing enhanced multi-party conference functionality
|
|
1712
|
-
/*
|
|
1713
1765
|
describe('transferConference', () => {
|
|
1714
1766
|
it('should successfully transfer conference', async () => {
|
|
1715
1767
|
const mockResponse = {
|
|
@@ -1756,6 +1808,5 @@ describe('Task', () => {
|
|
|
1756
1808
|
});
|
|
1757
1809
|
});
|
|
1758
1810
|
});
|
|
1759
|
-
*/
|
|
1760
1811
|
});
|
|
1761
1812
|
});
|