@webex/contact-center 3.12.0-task-refactor.10 → 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 +345 -82
- package/dist/cc.js.map +1 -1
- package/dist/config.js +6 -0
- package/dist/config.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 +16 -4
- package/dist/services/task/Task.js.map +1 -1
- package/dist/services/task/TaskManager.js +158 -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/state-machine/uiControlsComputer.js +1 -1
- package/dist/services/task/state-machine/uiControlsComputer.js.map +1 -1
- package/dist/services/task/types.js +69 -0
- package/dist/services/task/types.js.map +1 -1
- package/dist/types/cc.d.ts +93 -2
- package/dist/types/config.d.ts +6 -0
- 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 +85 -1
- package/dist/types/types.d.ts +2 -0
- package/dist/types.js.map +1 -1
- package/dist/webex.js +1 -1
- package/package.json +2 -2
- package/src/cc.ts +452 -102
- package/src/config.ts +6 -0
- 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 +22 -4
- package/src/services/task/TaskManager.ts +175 -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/state-machine/uiControlsComputer.ts +1 -1
- package/src/services/task/types.ts +91 -0
- package/src/types.ts +6 -1
- package/test/unit/spec/cc.ts +432 -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 +2334 -145
- package/test/unit/spec/services/task/dialer.ts +372 -96
- package/test/unit/spec/services/task/state-machine/uiControlsComputer.ts +47 -0
- package/umd/contact-center.min.js +2 -2
- package/umd/contact-center.min.js.map +1 -1
|
@@ -1,157 +1,433 @@
|
|
|
1
|
-
import AqmReqs from
|
|
2
|
-
import aqmDialer from
|
|
1
|
+
import AqmReqs from '../../../../../src/services/core/aqm-reqs';
|
|
2
|
+
import aqmDialer from '../../../../../src/services/task/dialer';
|
|
3
3
|
|
|
4
4
|
jest.mock('../../../../../src/services/core/Utils', () => ({
|
|
5
|
-
|
|
6
5
|
createErrDetailsObject: jest.fn(),
|
|
7
6
|
getRoutingHost: jest.fn(),
|
|
8
|
-
|
|
9
7
|
}));
|
|
10
8
|
|
|
11
9
|
jest.mock('../../../../../src/services/core/aqm-reqs');
|
|
12
10
|
|
|
13
11
|
describe('AQM routing dialer', () => {
|
|
14
|
-
|
|
15
12
|
let fakeAqm: jest.Mocked<AqmReqs>;
|
|
16
13
|
|
|
17
14
|
beforeEach(() => {
|
|
18
|
-
|
|
19
15
|
jest.clearAllMocks();
|
|
20
16
|
|
|
21
17
|
fakeAqm = new AqmReqs() as jest.Mocked<AqmReqs>;
|
|
22
18
|
fakeAqm.reqEmpty = jest.fn().mockImplementation((fn) => fn);
|
|
23
19
|
fakeAqm.req = jest.fn().mockImplementation((fn) => fn);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
describe('Routing outbound dial', () => {
|
|
23
|
+
it('should call the startdial api', () => {
|
|
24
|
+
const fakeAqm = {
|
|
25
|
+
req: () =>
|
|
26
|
+
jest.fn().mockResolvedValue(() => {
|
|
27
|
+
Promise.resolve({data: 'outdial success'});
|
|
28
|
+
}),
|
|
29
|
+
evt: jest.fn(),
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const dialer = aqmDialer(fakeAqm as any);
|
|
33
|
+
|
|
34
|
+
dialer
|
|
35
|
+
.startOutdial({
|
|
36
|
+
data: {
|
|
37
|
+
entryPointId: '1212312',
|
|
38
|
+
destination: '+142356',
|
|
39
|
+
direction: 'OUTBOUND',
|
|
40
|
+
attributes: {},
|
|
41
|
+
mediaType: 'telephony',
|
|
42
|
+
outboundType: 'OUTDIAL',
|
|
43
|
+
},
|
|
44
|
+
})
|
|
45
|
+
.then((response) => {
|
|
46
|
+
expect(response.data).toBe('outdial success');
|
|
47
|
+
})
|
|
48
|
+
.catch(() => {
|
|
49
|
+
expect(true).toBe(true);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
expect(dialer.startOutdial).toHaveBeenCalled();
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('should handle network errors', () => {
|
|
56
|
+
const fakeAqm = {
|
|
57
|
+
req: () => jest.fn().mockRejectedValue(new Error('Network Error')),
|
|
58
|
+
evt: jest.fn(),
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const dialer = aqmDialer(fakeAqm as any);
|
|
24
62
|
|
|
63
|
+
return expect(
|
|
64
|
+
dialer.startOutdial({
|
|
65
|
+
data: {
|
|
66
|
+
entryPointId: '1212312',
|
|
67
|
+
destination: '+142356',
|
|
68
|
+
direction: 'OUTBOUND',
|
|
69
|
+
attributes: {},
|
|
70
|
+
mediaType: 'telephony',
|
|
71
|
+
outboundType: 'OUTDIAL',
|
|
72
|
+
},
|
|
73
|
+
})
|
|
74
|
+
).rejects.toThrow('Network Error');
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('should handle invalid payload', () => {
|
|
78
|
+
const fakeAqm = {
|
|
79
|
+
req: () => jest.fn().mockRejectedValue(new Error('Invalid Payload in request')),
|
|
80
|
+
evt: jest.fn(),
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const dialer = aqmDialer(fakeAqm as any);
|
|
84
|
+
|
|
85
|
+
return expect(
|
|
86
|
+
dialer.startOutdial({
|
|
87
|
+
data: {
|
|
88
|
+
entryPointId: '',
|
|
89
|
+
destination: '',
|
|
90
|
+
direction: 'OUTBOUND',
|
|
91
|
+
attributes: {},
|
|
92
|
+
mediaType: 'telephony',
|
|
93
|
+
outboundType: 'OUTDIAL',
|
|
94
|
+
},
|
|
95
|
+
})
|
|
96
|
+
).rejects.toThrow('Invalid Payload in request');
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('should handle servers errors', () => {
|
|
100
|
+
const fakeAqm = {
|
|
101
|
+
req: () => jest.fn().mockRejectedValue(new Error('Server Error')),
|
|
102
|
+
evt: jest.fn(),
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const dialer = aqmDialer(fakeAqm as any);
|
|
106
|
+
return expect(
|
|
107
|
+
dialer.startOutdial({
|
|
108
|
+
data: {
|
|
109
|
+
entryPointId: '123456',
|
|
110
|
+
destination: '+142356',
|
|
111
|
+
direction: 'OUTBOUND',
|
|
112
|
+
attributes: {},
|
|
113
|
+
mediaType: 'telephony',
|
|
114
|
+
outboundType: 'OUTDIAL',
|
|
115
|
+
},
|
|
116
|
+
})
|
|
117
|
+
).rejects.toThrow('Server Error');
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('should handle Timeout scenarios', () => {
|
|
121
|
+
const fakeAqm = {
|
|
122
|
+
req: () => jest.fn().mockRejectedValue(new Error('Request Timeout')),
|
|
123
|
+
evt: jest.fn(),
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const dialer = aqmDialer(fakeAqm as any);
|
|
127
|
+
return expect(
|
|
128
|
+
dialer.startOutdial({
|
|
129
|
+
data: {
|
|
130
|
+
entryPointId: '12345',
|
|
131
|
+
destination: '+123456',
|
|
132
|
+
direction: 'OUTBOUND',
|
|
133
|
+
attributes: {},
|
|
134
|
+
mediaType: 'telephony',
|
|
135
|
+
outboundType: 'OUTDIAL',
|
|
136
|
+
},
|
|
137
|
+
})
|
|
138
|
+
).rejects.toThrow('Request Timeout');
|
|
139
|
+
});
|
|
25
140
|
});
|
|
26
141
|
|
|
27
|
-
describe(
|
|
142
|
+
describe('Campaign preview contact operations', () => {
|
|
143
|
+
const previewPayload = {
|
|
144
|
+
interactionId: 'interaction-123',
|
|
145
|
+
campaignId: 'TestCampaignPreview',
|
|
146
|
+
};
|
|
28
147
|
|
|
29
|
-
|
|
148
|
+
describe('acceptPreviewContact', () => {
|
|
149
|
+
it('should construct the correct URL with campaignId and interactionId', () => {
|
|
150
|
+
const dialer = aqmDialer(fakeAqm as any);
|
|
151
|
+
const config = dialer.acceptPreviewContact({data: previewPayload}) as any;
|
|
30
152
|
|
|
153
|
+
expect(config.url).toBe(
|
|
154
|
+
`/v1/dialer/campaign/${previewPayload.campaignId}/preview-task/${previewPayload.interactionId}/accept`
|
|
155
|
+
);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it('should URL-encode campaignId when it contains reserved characters', () => {
|
|
159
|
+
const dialer = aqmDialer(fakeAqm as any);
|
|
160
|
+
const payloadWithSpecialChars = {
|
|
161
|
+
interactionId: 'interaction-456',
|
|
162
|
+
campaignId: 'My Campaign/Test #1',
|
|
163
|
+
};
|
|
164
|
+
const config = dialer.acceptPreviewContact({data: payloadWithSpecialChars}) as any;
|
|
165
|
+
|
|
166
|
+
expect(config.url).toBe(
|
|
167
|
+
`/v1/dialer/campaign/${encodeURIComponent(
|
|
168
|
+
payloadWithSpecialChars.campaignId
|
|
169
|
+
)}/preview-task/${payloadWithSpecialChars.interactionId}/accept`
|
|
170
|
+
);
|
|
171
|
+
expect(config.url).toContain('My%20Campaign%2FTest%20%231');
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
it('should call the acceptPreviewContact api', () => {
|
|
31
175
|
const fakeAqm = {
|
|
32
176
|
req: () =>
|
|
33
177
|
jest.fn().mockResolvedValue(() => {
|
|
34
|
-
Promise.resolve({
|
|
178
|
+
Promise.resolve({data: 'accept preview success'});
|
|
35
179
|
}),
|
|
36
|
-
evt: jest.fn()
|
|
180
|
+
evt: jest.fn(),
|
|
37
181
|
};
|
|
38
|
-
|
|
182
|
+
|
|
39
183
|
const dialer = aqmDialer(fakeAqm as any);
|
|
40
184
|
|
|
41
185
|
dialer
|
|
42
|
-
.
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
186
|
+
.acceptPreviewContact({data: previewPayload})
|
|
187
|
+
.then((response) => {
|
|
188
|
+
expect(response.data).toBe('accept preview success');
|
|
189
|
+
})
|
|
190
|
+
.catch(() => {
|
|
191
|
+
expect(true).toBe(true);
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
expect(dialer.acceptPreviewContact).toHaveBeenCalled();
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
it('should handle network errors', () => {
|
|
198
|
+
const fakeAqm = {
|
|
199
|
+
req: () => jest.fn().mockRejectedValue(new Error('Network Error')),
|
|
200
|
+
evt: jest.fn(),
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
const dialer = aqmDialer(fakeAqm as any);
|
|
204
|
+
|
|
205
|
+
return expect(
|
|
206
|
+
dialer.acceptPreviewContact({
|
|
207
|
+
data: previewPayload,
|
|
51
208
|
})
|
|
52
|
-
|
|
53
|
-
|
|
209
|
+
).rejects.toThrow('Network Error');
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
it('should handle server errors', () => {
|
|
213
|
+
const fakeAqm = {
|
|
214
|
+
req: () => jest.fn().mockRejectedValue(new Error('Server Error')),
|
|
215
|
+
evt: jest.fn(),
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
const dialer = aqmDialer(fakeAqm as any);
|
|
219
|
+
|
|
220
|
+
return expect(
|
|
221
|
+
dialer.acceptPreviewContact({
|
|
222
|
+
data: previewPayload,
|
|
223
|
+
})
|
|
224
|
+
).rejects.toThrow('Server Error');
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
it('should handle timeout scenarios', () => {
|
|
228
|
+
const fakeAqm = {
|
|
229
|
+
req: () => jest.fn().mockRejectedValue(new Error('Request Timeout')),
|
|
230
|
+
evt: jest.fn(),
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
const dialer = aqmDialer(fakeAqm as any);
|
|
234
|
+
|
|
235
|
+
return expect(
|
|
236
|
+
dialer.acceptPreviewContact({
|
|
237
|
+
data: previewPayload,
|
|
238
|
+
})
|
|
239
|
+
).rejects.toThrow('Request Timeout');
|
|
240
|
+
});
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
describe('skipPreviewContact', () => {
|
|
244
|
+
it('should construct the correct URL with campaignId and interactionId', () => {
|
|
245
|
+
const dialer = aqmDialer(fakeAqm as any);
|
|
246
|
+
const config = dialer.skipPreviewContact({data: previewPayload}) as any;
|
|
247
|
+
|
|
248
|
+
expect(config.url).toBe(
|
|
249
|
+
`/v1/dialer/campaign/${previewPayload.campaignId}/preview-task/${previewPayload.interactionId}/skip`
|
|
250
|
+
);
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
it('should URL-encode campaignId when it contains reserved characters', () => {
|
|
254
|
+
const dialer = aqmDialer(fakeAqm as any);
|
|
255
|
+
const payloadWithSpecialChars = {
|
|
256
|
+
interactionId: 'interaction-456',
|
|
257
|
+
campaignId: 'My Campaign/Test #1',
|
|
258
|
+
};
|
|
259
|
+
const config = dialer.skipPreviewContact({data: payloadWithSpecialChars}) as any;
|
|
260
|
+
|
|
261
|
+
expect(config.url).toBe(
|
|
262
|
+
`/v1/dialer/campaign/${encodeURIComponent(
|
|
263
|
+
payloadWithSpecialChars.campaignId
|
|
264
|
+
)}/preview-task/${payloadWithSpecialChars.interactionId}/skip`
|
|
265
|
+
);
|
|
266
|
+
expect(config.url).toContain('My%20Campaign%2FTest%20%231');
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
it('should call the skipPreviewContact api', () => {
|
|
270
|
+
const fakeAqm = {
|
|
271
|
+
req: () =>
|
|
272
|
+
jest.fn().mockResolvedValue(() => {
|
|
273
|
+
Promise.resolve({data: 'skip preview success'});
|
|
274
|
+
}),
|
|
275
|
+
evt: jest.fn(),
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
const dialer = aqmDialer(fakeAqm as any);
|
|
279
|
+
|
|
280
|
+
dialer
|
|
281
|
+
.skipPreviewContact({data: previewPayload})
|
|
282
|
+
.then((response) => {
|
|
283
|
+
expect(response.data).toBe('skip preview success');
|
|
54
284
|
})
|
|
55
285
|
.catch(() => {
|
|
56
286
|
expect(true).toBe(true);
|
|
57
287
|
});
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
288
|
+
|
|
289
|
+
expect(dialer.skipPreviewContact).toHaveBeenCalled();
|
|
61
290
|
});
|
|
62
291
|
|
|
63
|
-
it(
|
|
292
|
+
it('should handle network errors', () => {
|
|
293
|
+
const fakeAqm = {
|
|
294
|
+
req: () => jest.fn().mockRejectedValue(new Error('Network Error')),
|
|
295
|
+
evt: jest.fn(),
|
|
296
|
+
};
|
|
64
297
|
|
|
65
|
-
|
|
66
|
-
req: () => jest.fn().mockRejectedValue(new Error("Network Error")),
|
|
67
|
-
evt: jest.fn()
|
|
68
|
-
};
|
|
298
|
+
const dialer = aqmDialer(fakeAqm as any);
|
|
69
299
|
|
|
70
|
-
|
|
300
|
+
return expect(
|
|
301
|
+
dialer.skipPreviewContact({
|
|
302
|
+
data: previewPayload,
|
|
303
|
+
})
|
|
304
|
+
).rejects.toThrow('Network Error');
|
|
305
|
+
});
|
|
71
306
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
direction: "OUTBOUND",
|
|
78
|
-
attributes: {},
|
|
79
|
-
mediaType: "telephony",
|
|
80
|
-
outboundType: "OUTDIAL"
|
|
81
|
-
}
|
|
307
|
+
it('should handle server errors', () => {
|
|
308
|
+
const fakeAqm = {
|
|
309
|
+
req: () => jest.fn().mockRejectedValue(new Error('Server Error')),
|
|
310
|
+
evt: jest.fn(),
|
|
311
|
+
};
|
|
82
312
|
|
|
83
|
-
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
it("should handle invalid payload", () => {
|
|
313
|
+
const dialer = aqmDialer(fakeAqm as any);
|
|
87
314
|
|
|
88
|
-
|
|
315
|
+
return expect(
|
|
316
|
+
dialer.skipPreviewContact({
|
|
317
|
+
data: previewPayload,
|
|
318
|
+
})
|
|
319
|
+
).rejects.toThrow('Server Error');
|
|
320
|
+
});
|
|
89
321
|
|
|
90
|
-
|
|
91
|
-
|
|
322
|
+
it('should handle timeout scenarios', () => {
|
|
323
|
+
const fakeAqm = {
|
|
324
|
+
req: () => jest.fn().mockRejectedValue(new Error('Request Timeout')),
|
|
325
|
+
evt: jest.fn(),
|
|
326
|
+
};
|
|
92
327
|
|
|
93
|
-
|
|
328
|
+
const dialer = aqmDialer(fakeAqm as any);
|
|
94
329
|
|
|
95
|
-
|
|
330
|
+
return expect(
|
|
331
|
+
dialer.skipPreviewContact({
|
|
332
|
+
data: previewPayload,
|
|
333
|
+
})
|
|
334
|
+
).rejects.toThrow('Request Timeout');
|
|
335
|
+
});
|
|
336
|
+
});
|
|
96
337
|
|
|
97
|
-
|
|
338
|
+
describe('removePreviewContact', () => {
|
|
339
|
+
it('should construct the correct URL with campaignId and interactionId', () => {
|
|
340
|
+
const dialer = aqmDialer(fakeAqm as any);
|
|
341
|
+
const config = dialer.removePreviewContact({data: previewPayload}) as any;
|
|
98
342
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
attributes: {},
|
|
104
|
-
mediaType: "telephony",
|
|
105
|
-
outboundType: "OUTDIAL"
|
|
106
|
-
}
|
|
343
|
+
expect(config.url).toBe(
|
|
344
|
+
`/v1/dialer/campaign/${previewPayload.campaignId}/preview-task/${previewPayload.interactionId}/remove`
|
|
345
|
+
);
|
|
346
|
+
});
|
|
107
347
|
|
|
108
|
-
|
|
109
|
-
|
|
348
|
+
it('should URL-encode campaignId when it contains reserved characters', () => {
|
|
349
|
+
const dialer = aqmDialer(fakeAqm as any);
|
|
350
|
+
const payloadWithSpecialChars = {
|
|
351
|
+
interactionId: 'interaction-456',
|
|
352
|
+
campaignId: 'My Campaign/Test #1',
|
|
353
|
+
};
|
|
354
|
+
const config = dialer.removePreviewContact({data: payloadWithSpecialChars}) as any;
|
|
355
|
+
|
|
356
|
+
expect(config.url).toBe(
|
|
357
|
+
`/v1/dialer/campaign/${encodeURIComponent(
|
|
358
|
+
payloadWithSpecialChars.campaignId
|
|
359
|
+
)}/preview-task/${payloadWithSpecialChars.interactionId}/remove`
|
|
360
|
+
);
|
|
361
|
+
expect(config.url).toContain('My%20Campaign%2FTest%20%231');
|
|
362
|
+
});
|
|
110
363
|
|
|
364
|
+
it('should call the removePreviewContact api', () => {
|
|
365
|
+
const fakeAqm = {
|
|
366
|
+
req: () =>
|
|
367
|
+
jest.fn().mockResolvedValue(() => {
|
|
368
|
+
Promise.resolve({data: 'remove preview success'});
|
|
369
|
+
}),
|
|
370
|
+
evt: jest.fn(),
|
|
371
|
+
};
|
|
111
372
|
|
|
112
|
-
|
|
373
|
+
const dialer = aqmDialer(fakeAqm as any);
|
|
113
374
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
375
|
+
dialer
|
|
376
|
+
.removePreviewContact({data: previewPayload})
|
|
377
|
+
.then((response) => {
|
|
378
|
+
expect(response.data).toBe('remove preview success');
|
|
379
|
+
})
|
|
380
|
+
.catch(() => {
|
|
381
|
+
expect(true).toBe(true);
|
|
382
|
+
});
|
|
118
383
|
|
|
119
|
-
|
|
120
|
-
|
|
384
|
+
expect(dialer.removePreviewContact).toHaveBeenCalled();
|
|
385
|
+
});
|
|
121
386
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
mediaType: "telephony",
|
|
128
|
-
outboundType: "OUTDIAL"
|
|
129
|
-
}
|
|
387
|
+
it('should handle network errors', () => {
|
|
388
|
+
const fakeAqm = {
|
|
389
|
+
req: () => jest.fn().mockRejectedValue(new Error('Network Error')),
|
|
390
|
+
evt: jest.fn(),
|
|
391
|
+
};
|
|
130
392
|
|
|
131
|
-
|
|
393
|
+
const dialer = aqmDialer(fakeAqm as any);
|
|
132
394
|
|
|
133
|
-
|
|
395
|
+
return expect(
|
|
396
|
+
dialer.removePreviewContact({
|
|
397
|
+
data: previewPayload,
|
|
398
|
+
})
|
|
399
|
+
).rejects.toThrow('Network Error');
|
|
400
|
+
});
|
|
134
401
|
|
|
135
|
-
|
|
402
|
+
it('should handle server errors', () => {
|
|
403
|
+
const fakeAqm = {
|
|
404
|
+
req: () => jest.fn().mockRejectedValue(new Error('Server Error')),
|
|
405
|
+
evt: jest.fn(),
|
|
406
|
+
};
|
|
136
407
|
|
|
137
|
-
|
|
138
|
-
req: () => jest.fn().mockRejectedValue(new Error("Request Timeout")),
|
|
139
|
-
evt: jest.fn()
|
|
140
|
-
};
|
|
408
|
+
const dialer = aqmDialer(fakeAqm as any);
|
|
141
409
|
|
|
142
|
-
|
|
143
|
-
|
|
410
|
+
return expect(
|
|
411
|
+
dialer.removePreviewContact({
|
|
412
|
+
data: previewPayload,
|
|
413
|
+
})
|
|
414
|
+
).rejects.toThrow('Server Error');
|
|
415
|
+
});
|
|
144
416
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
mediaType: "telephony",
|
|
151
|
-
outboundType: "OUTDIAL"
|
|
152
|
-
}
|
|
417
|
+
it('should handle timeout scenarios', () => {
|
|
418
|
+
const fakeAqm = {
|
|
419
|
+
req: () => jest.fn().mockRejectedValue(new Error('Request Timeout')),
|
|
420
|
+
evt: jest.fn(),
|
|
421
|
+
};
|
|
153
422
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
423
|
+
const dialer = aqmDialer(fakeAqm as any);
|
|
424
|
+
|
|
425
|
+
return expect(
|
|
426
|
+
dialer.removePreviewContact({
|
|
427
|
+
data: previewPayload,
|
|
428
|
+
})
|
|
429
|
+
).rejects.toThrow('Request Timeout');
|
|
430
|
+
});
|
|
431
|
+
});
|
|
432
|
+
});
|
|
157
433
|
});
|
|
@@ -1784,6 +1784,53 @@ describe('uiControlsComputer outdial accept/decline controls', () => {
|
|
|
1784
1784
|
});
|
|
1785
1785
|
});
|
|
1786
1786
|
|
|
1787
|
+
describe('uiControlsComputer WebRTC call-control end button', () => {
|
|
1788
|
+
function createConnectedContext(voiceVariant: 'webrtc' | 'pstn'): TaskContext {
|
|
1789
|
+
const taskData = createTaskData({
|
|
1790
|
+
interaction: {
|
|
1791
|
+
state: 'connected',
|
|
1792
|
+
} as any,
|
|
1793
|
+
});
|
|
1794
|
+
|
|
1795
|
+
return {
|
|
1796
|
+
taskData,
|
|
1797
|
+
consultInitiator: false,
|
|
1798
|
+
exitingConference: false,
|
|
1799
|
+
consultFromConference: false,
|
|
1800
|
+
transferConferenceRequested: false,
|
|
1801
|
+
consultDestinationType: null,
|
|
1802
|
+
consultDestinationAgentId: null,
|
|
1803
|
+
consultDestinationAgentJoined: false,
|
|
1804
|
+
consultCallHeld: false,
|
|
1805
|
+
recordingControlsAvailable: true,
|
|
1806
|
+
recordingInProgress: true,
|
|
1807
|
+
uiControlConfig: {
|
|
1808
|
+
isEndTaskEnabled: false,
|
|
1809
|
+
isEndConsultEnabled: true,
|
|
1810
|
+
channelType: TASK_CHANNEL_TYPE.VOICE,
|
|
1811
|
+
isRecordingEnabled: true,
|
|
1812
|
+
agentId: taskData.agentId,
|
|
1813
|
+
voiceVariant,
|
|
1814
|
+
},
|
|
1815
|
+
uiControls: getDefaultUIControls(),
|
|
1816
|
+
};
|
|
1817
|
+
}
|
|
1818
|
+
|
|
1819
|
+
it('shows main end for connected WebRTC call-control tasks even when end task is disabled', () => {
|
|
1820
|
+
const context = createConnectedContext('webrtc');
|
|
1821
|
+
const uiControls = computeUIControls(TaskState.CONNECTED, context, context.taskData);
|
|
1822
|
+
|
|
1823
|
+
expect(uiControls.main.end).toEqual({isVisible: true, isEnabled: true});
|
|
1824
|
+
});
|
|
1825
|
+
|
|
1826
|
+
it('keeps main end hidden for non-WebRTC voice tasks when end task is disabled', () => {
|
|
1827
|
+
const context = createConnectedContext('pstn');
|
|
1828
|
+
const uiControls = computeUIControls(TaskState.CONNECTED, context, context.taskData);
|
|
1829
|
+
|
|
1830
|
+
expect(uiControls.main.end).toEqual({isVisible: false, isEnabled: false});
|
|
1831
|
+
});
|
|
1832
|
+
});
|
|
1833
|
+
|
|
1787
1834
|
describe('uiControlsComputer conference controls', () => {
|
|
1788
1835
|
function createConferenceTaskData(participantCount: number) {
|
|
1789
1836
|
const participants: Record<string, any> = {
|