@webex/contact-center 0.0.0-next.1
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/README.md +81 -0
- package/__mocks__/workerMock.js +15 -0
- package/babel.config.js +15 -0
- package/dist/cc.js +1416 -0
- package/dist/cc.js.map +1 -0
- package/dist/config.js +72 -0
- package/dist/config.js.map +1 -0
- package/dist/constants.js +58 -0
- package/dist/constants.js.map +1 -0
- package/dist/index.js +142 -0
- package/dist/index.js.map +1 -0
- package/dist/logger-proxy.js +115 -0
- package/dist/logger-proxy.js.map +1 -0
- package/dist/metrics/MetricsManager.js +474 -0
- package/dist/metrics/MetricsManager.js.map +1 -0
- package/dist/metrics/behavioral-events.js +322 -0
- package/dist/metrics/behavioral-events.js.map +1 -0
- package/dist/metrics/constants.js +134 -0
- package/dist/metrics/constants.js.map +1 -0
- package/dist/services/WebCallingService.js +323 -0
- package/dist/services/WebCallingService.js.map +1 -0
- package/dist/services/agent/index.js +177 -0
- package/dist/services/agent/index.js.map +1 -0
- package/dist/services/agent/types.js +137 -0
- package/dist/services/agent/types.js.map +1 -0
- package/dist/services/config/Util.js +203 -0
- package/dist/services/config/Util.js.map +1 -0
- package/dist/services/config/constants.js +221 -0
- package/dist/services/config/constants.js.map +1 -0
- package/dist/services/config/index.js +607 -0
- package/dist/services/config/index.js.map +1 -0
- package/dist/services/config/types.js +334 -0
- package/dist/services/config/types.js.map +1 -0
- package/dist/services/constants.js +117 -0
- package/dist/services/constants.js.map +1 -0
- package/dist/services/core/Err.js +43 -0
- package/dist/services/core/Err.js.map +1 -0
- package/dist/services/core/GlobalTypes.js +6 -0
- package/dist/services/core/GlobalTypes.js.map +1 -0
- package/dist/services/core/Utils.js +126 -0
- package/dist/services/core/Utils.js.map +1 -0
- package/dist/services/core/WebexRequest.js +96 -0
- package/dist/services/core/WebexRequest.js.map +1 -0
- package/dist/services/core/aqm-reqs.js +246 -0
- package/dist/services/core/aqm-reqs.js.map +1 -0
- package/dist/services/core/constants.js +109 -0
- package/dist/services/core/constants.js.map +1 -0
- package/dist/services/core/types.js +6 -0
- package/dist/services/core/types.js.map +1 -0
- package/dist/services/core/websocket/WebSocketManager.js +187 -0
- package/dist/services/core/websocket/WebSocketManager.js.map +1 -0
- package/dist/services/core/websocket/connection-service.js +111 -0
- package/dist/services/core/websocket/connection-service.js.map +1 -0
- package/dist/services/core/websocket/keepalive.worker.js +94 -0
- package/dist/services/core/websocket/keepalive.worker.js.map +1 -0
- package/dist/services/core/websocket/types.js +6 -0
- package/dist/services/core/websocket/types.js.map +1 -0
- package/dist/services/index.js +78 -0
- package/dist/services/index.js.map +1 -0
- package/dist/services/task/AutoWrapup.js +88 -0
- package/dist/services/task/AutoWrapup.js.map +1 -0
- package/dist/services/task/TaskManager.js +369 -0
- package/dist/services/task/TaskManager.js.map +1 -0
- package/dist/services/task/constants.js +58 -0
- package/dist/services/task/constants.js.map +1 -0
- package/dist/services/task/contact.js +464 -0
- package/dist/services/task/contact.js.map +1 -0
- package/dist/services/task/dialer.js +60 -0
- package/dist/services/task/dialer.js.map +1 -0
- package/dist/services/task/index.js +1188 -0
- package/dist/services/task/index.js.map +1 -0
- package/dist/services/task/types.js +214 -0
- package/dist/services/task/types.js.map +1 -0
- package/dist/types/cc.d.ts +676 -0
- package/dist/types/config.d.ts +66 -0
- package/dist/types/constants.d.ts +45 -0
- package/dist/types/index.d.ts +178 -0
- package/dist/types/logger-proxy.d.ts +71 -0
- package/dist/types/metrics/MetricsManager.d.ts +223 -0
- package/dist/types/metrics/behavioral-events.d.ts +29 -0
- package/dist/types/metrics/constants.d.ts +127 -0
- package/dist/types/services/WebCallingService.d.ts +1 -0
- package/dist/types/services/agent/index.d.ts +46 -0
- package/dist/types/services/agent/types.d.ts +413 -0
- package/dist/types/services/config/Util.d.ts +19 -0
- package/dist/types/services/config/constants.d.ts +203 -0
- package/dist/types/services/config/index.d.ts +171 -0
- package/dist/types/services/config/types.d.ts +1113 -0
- package/dist/types/services/constants.d.ts +97 -0
- package/dist/types/services/core/Err.d.ts +119 -0
- package/dist/types/services/core/GlobalTypes.d.ts +33 -0
- package/dist/types/services/core/Utils.d.ts +36 -0
- package/dist/types/services/core/WebexRequest.d.ts +22 -0
- package/dist/types/services/core/aqm-reqs.d.ts +16 -0
- package/dist/types/services/core/constants.d.ts +85 -0
- package/dist/types/services/core/types.d.ts +47 -0
- package/dist/types/services/core/websocket/WebSocketManager.d.ts +34 -0
- package/dist/types/services/core/websocket/connection-service.d.ts +27 -0
- package/dist/types/services/core/websocket/keepalive.worker.d.ts +2 -0
- package/dist/types/services/core/websocket/types.d.ts +37 -0
- package/dist/types/services/index.d.ts +52 -0
- package/dist/types/services/task/AutoWrapup.d.ts +40 -0
- package/dist/types/services/task/TaskManager.d.ts +1 -0
- package/dist/types/services/task/constants.d.ts +46 -0
- package/dist/types/services/task/contact.d.ts +59 -0
- package/dist/types/services/task/dialer.d.ts +28 -0
- package/dist/types/services/task/index.d.ts +569 -0
- package/dist/types/services/task/types.d.ts +1041 -0
- package/dist/types/types.d.ts +452 -0
- package/dist/types/webex-config.d.ts +53 -0
- package/dist/types/webex.d.ts +7 -0
- package/dist/types.js +292 -0
- package/dist/types.js.map +1 -0
- package/dist/webex-config.js +60 -0
- package/dist/webex-config.js.map +1 -0
- package/dist/webex.js +99 -0
- package/dist/webex.js.map +1 -0
- package/jest.config.js +45 -0
- package/package.json +83 -0
- package/src/cc.ts +1618 -0
- package/src/config.ts +65 -0
- package/src/constants.ts +51 -0
- package/src/index.ts +220 -0
- package/src/logger-proxy.ts +110 -0
- package/src/metrics/MetricsManager.ts +512 -0
- package/src/metrics/behavioral-events.ts +332 -0
- package/src/metrics/constants.ts +135 -0
- package/src/services/WebCallingService.ts +351 -0
- package/src/services/agent/index.ts +149 -0
- package/src/services/agent/types.ts +440 -0
- package/src/services/config/Util.ts +261 -0
- package/src/services/config/constants.ts +249 -0
- package/src/services/config/index.ts +743 -0
- package/src/services/config/types.ts +1117 -0
- package/src/services/constants.ts +111 -0
- package/src/services/core/Err.ts +126 -0
- package/src/services/core/GlobalTypes.ts +34 -0
- package/src/services/core/Utils.ts +132 -0
- package/src/services/core/WebexRequest.ts +103 -0
- package/src/services/core/aqm-reqs.ts +272 -0
- package/src/services/core/constants.ts +106 -0
- package/src/services/core/types.ts +48 -0
- package/src/services/core/websocket/WebSocketManager.ts +196 -0
- package/src/services/core/websocket/connection-service.ts +142 -0
- package/src/services/core/websocket/keepalive.worker.js +88 -0
- package/src/services/core/websocket/types.ts +40 -0
- package/src/services/index.ts +71 -0
- package/src/services/task/AutoWrapup.ts +86 -0
- package/src/services/task/TaskManager.ts +420 -0
- package/src/services/task/constants.ts +52 -0
- package/src/services/task/contact.ts +429 -0
- package/src/services/task/dialer.ts +52 -0
- package/src/services/task/index.ts +1375 -0
- package/src/services/task/types.ts +1113 -0
- package/src/types.ts +639 -0
- package/src/webex-config.ts +54 -0
- package/src/webex.js +96 -0
- package/test/unit/spec/cc.ts +1985 -0
- package/test/unit/spec/metrics/MetricsManager.ts +491 -0
- package/test/unit/spec/metrics/behavioral-events.ts +102 -0
- package/test/unit/spec/services/WebCallingService.ts +416 -0
- package/test/unit/spec/services/agent/index.ts +65 -0
- package/test/unit/spec/services/config/index.ts +1035 -0
- package/test/unit/spec/services/core/Utils.ts +279 -0
- package/test/unit/spec/services/core/WebexRequest.ts +144 -0
- package/test/unit/spec/services/core/aqm-reqs.ts +570 -0
- package/test/unit/spec/services/core/websocket/WebSocketManager.ts +378 -0
- package/test/unit/spec/services/core/websocket/connection-service.ts +178 -0
- package/test/unit/spec/services/task/TaskManager.ts +1351 -0
- package/test/unit/spec/services/task/contact.ts +204 -0
- package/test/unit/spec/services/task/dialer.ts +157 -0
- package/test/unit/spec/services/task/index.ts +1474 -0
- package/tsconfig.json +6 -0
- package/typedoc.json +37 -0
- package/typedoc.md +240 -0
- package/umd/contact-center.min.js +3 -0
- package/umd/contact-center.min.js.map +1 -0
|
@@ -0,0 +1,1474 @@
|
|
|
1
|
+
import 'jsdom-global/register';
|
|
2
|
+
import {CALL_EVENT_KEYS, CallingClientConfig, LocalMicrophoneStream} from '@webex/calling';
|
|
3
|
+
import {LoginOption, WebexSDK} from '../../../../../src/types';
|
|
4
|
+
import {TASK_FILE} from '../../../../../src/constants';
|
|
5
|
+
import Task from '../../../../../src/services/task';
|
|
6
|
+
import * as Utils from '../../../../../src/services/core/Utils';
|
|
7
|
+
import {CC_EVENTS} from '../../../../../src/services/config/types';
|
|
8
|
+
import config from '../../../../../src/config';
|
|
9
|
+
import WebCallingService from '../../../../../src/services/WebCallingService';
|
|
10
|
+
import {
|
|
11
|
+
TASK_EVENTS,
|
|
12
|
+
TaskResponse,
|
|
13
|
+
AgentContact,
|
|
14
|
+
ConsultEndPayload,
|
|
15
|
+
TaskData,
|
|
16
|
+
DESTINATION_TYPE,
|
|
17
|
+
CONSULT_TRANSFER_DESTINATION_TYPE,
|
|
18
|
+
ConsultTransferPayLoad,
|
|
19
|
+
TransferPayLoad,
|
|
20
|
+
} from '../../../../../src/services/task/types';
|
|
21
|
+
import WebexRequest from '../../../../../src/services/core/WebexRequest';
|
|
22
|
+
import MetricsManager from '../../../../../src/metrics/MetricsManager';
|
|
23
|
+
import {METRIC_EVENT_NAMES} from '../../../../../src/metrics/constants';
|
|
24
|
+
import LoggerProxy from '../../../../../src/logger-proxy';
|
|
25
|
+
|
|
26
|
+
jest.mock('@webex/calling');
|
|
27
|
+
jest.mock('../../../../../src/logger-proxy');
|
|
28
|
+
|
|
29
|
+
describe('Task', () => {
|
|
30
|
+
let onSpy;
|
|
31
|
+
let task;
|
|
32
|
+
let contactMock;
|
|
33
|
+
let mockMetricsManager;
|
|
34
|
+
let taskDataMock;
|
|
35
|
+
let webCallingService;
|
|
36
|
+
let getErrorDetailsSpy;
|
|
37
|
+
let mockWebexRequest;
|
|
38
|
+
let webex: WebexSDK;
|
|
39
|
+
let loggerInfoSpy;
|
|
40
|
+
let loggerLogSpy;
|
|
41
|
+
let loggerErrorSpy;
|
|
42
|
+
|
|
43
|
+
const taskId = '0ae913a4-c857-4705-8d49-76dd3dde75e4';
|
|
44
|
+
const mockTrack = {} as MediaStreamTrack;
|
|
45
|
+
const mockStream = {
|
|
46
|
+
outputStream: {
|
|
47
|
+
getAudioTracks: jest.fn().mockReturnValue([mockTrack]),
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
beforeEach(() => {
|
|
52
|
+
webex = {
|
|
53
|
+
logger: {
|
|
54
|
+
log: jest.fn(),
|
|
55
|
+
error: jest.fn(),
|
|
56
|
+
info: jest.fn(),
|
|
57
|
+
},
|
|
58
|
+
} as unknown as WebexSDK;
|
|
59
|
+
|
|
60
|
+
loggerInfoSpy = jest.spyOn(LoggerProxy, 'info');
|
|
61
|
+
loggerLogSpy = jest.spyOn(LoggerProxy, 'log');
|
|
62
|
+
loggerErrorSpy = jest.spyOn(LoggerProxy, 'error');
|
|
63
|
+
|
|
64
|
+
contactMock = {
|
|
65
|
+
accept: jest.fn().mockResolvedValue({}),
|
|
66
|
+
hold: jest.fn().mockResolvedValue({}),
|
|
67
|
+
unHold: jest.fn().mockResolvedValue({}),
|
|
68
|
+
consult: jest.fn().mockResolvedValue({}),
|
|
69
|
+
consultEnd: jest.fn().mockResolvedValue({}),
|
|
70
|
+
blindTransfer: jest.fn().mockResolvedValue({}),
|
|
71
|
+
vteamTransfer: jest.fn().mockResolvedValue({}),
|
|
72
|
+
consultTransfer: jest.fn().mockResolvedValue({}),
|
|
73
|
+
end: jest.fn().mockResolvedValue({}),
|
|
74
|
+
wrapup: jest.fn().mockResolvedValue({}),
|
|
75
|
+
pauseRecording: jest.fn().mockResolvedValue({}),
|
|
76
|
+
resumeRecording: jest.fn().mockResolvedValue({}),
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
mockMetricsManager = {
|
|
80
|
+
trackEvent: jest.fn(),
|
|
81
|
+
timeEvent: jest.fn(),
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
jest.spyOn(MetricsManager, 'getInstance').mockReturnValue(mockMetricsManager);
|
|
85
|
+
|
|
86
|
+
webCallingService = new WebCallingService(
|
|
87
|
+
webex,
|
|
88
|
+
config.cc.callingClientConfig as CallingClientConfig
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
mockWebexRequest = {
|
|
92
|
+
request: jest.fn(),
|
|
93
|
+
uploadLogs: jest.fn(),
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
jest.spyOn(WebexRequest, 'getInstance').mockReturnValue(mockWebexRequest);
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
webCallingService.loginOption = LoginOption.BROWSER;
|
|
100
|
+
onSpy = jest.spyOn(webCallingService, 'on');
|
|
101
|
+
|
|
102
|
+
// Mock task data
|
|
103
|
+
taskDataMock = {
|
|
104
|
+
type: CC_EVENTS.AGENT_CONTACT_RESERVED,
|
|
105
|
+
agentId: '723a8ffb-a26e-496d-b14a-ff44fb83b64f',
|
|
106
|
+
eventTime: 1733211616959,
|
|
107
|
+
eventType: 'RoutingMessage',
|
|
108
|
+
interactionId: taskId,
|
|
109
|
+
orgId: '6ecef209-9a34-4ed1-a07a-7ddd1dbe925a',
|
|
110
|
+
trackingId: '575c0ec2-618c-42af-a61c-53aeb0a221ee',
|
|
111
|
+
mediaResourceId: '0ae913a4-c857-4705-8d49-76dd3dde75e4',
|
|
112
|
+
destAgentId: 'ebeb893b-ba67-4f36-8418-95c7492b28c2',
|
|
113
|
+
owner: '723a8ffb-a26e-496d-b14a-ff44fb83b64f',
|
|
114
|
+
queueMgr: 'aqm',
|
|
115
|
+
interaction: {
|
|
116
|
+
mediaType: 'telephony',
|
|
117
|
+
mainInteractionId: taskId,
|
|
118
|
+
media: {
|
|
119
|
+
'58a45567-4e61-4f4b-a580-5bc86357bef0': {
|
|
120
|
+
holdTimestamp: null,
|
|
121
|
+
isHold: false,
|
|
122
|
+
mType: 'consult',
|
|
123
|
+
mediaMgr: 'callmm',
|
|
124
|
+
mediaResourceId: '58a45567-4e61-4f4b-a580-5bc86357bef0',
|
|
125
|
+
mediaType: 'telephony',
|
|
126
|
+
participants: [
|
|
127
|
+
'f520d6b5-28ad-4f2f-b83e-781bb64af617',
|
|
128
|
+
'723a8ffb-a26e-496d-b14a-ff44fb83b64f',
|
|
129
|
+
],
|
|
130
|
+
},
|
|
131
|
+
[taskId]: {
|
|
132
|
+
holdTimestamp: 1734667567279,
|
|
133
|
+
isHold: true,
|
|
134
|
+
mType: 'mainCall',
|
|
135
|
+
mediaMgr: 'callmm',
|
|
136
|
+
mediaResourceId: taskId,
|
|
137
|
+
mediaType: 'telephony',
|
|
138
|
+
participants: ['+14696762938', '723a8ffb-a26e-496d-b14a-ff44fb83b64f'],
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
// Create an instance of Task
|
|
145
|
+
task = new Task(contactMock, webCallingService, taskDataMock);
|
|
146
|
+
|
|
147
|
+
// Mock navigator.mediaDevices
|
|
148
|
+
global.navigator.mediaDevices = {
|
|
149
|
+
getUserMedia: jest.fn(() =>
|
|
150
|
+
Promise.resolve({
|
|
151
|
+
getAudioTracks: jest.fn().mockReturnValue([mockTrack]),
|
|
152
|
+
})
|
|
153
|
+
),
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
// Mock MediaStream (if needed)
|
|
157
|
+
global.MediaStream = jest.fn().mockImplementation((tracks) => {
|
|
158
|
+
return mockStream;
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
getErrorDetailsSpy = jest.spyOn(Utils, 'getErrorDetails');
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
afterEach(() => {
|
|
165
|
+
jest.clearAllMocks();
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it('test the on spy', async () => {
|
|
169
|
+
const taskEmitSpy = jest.spyOn(task, 'emit');
|
|
170
|
+
const remoteMediaCb = onSpy.mock.calls[0][1];
|
|
171
|
+
|
|
172
|
+
expect(onSpy).toHaveBeenCalledWith(CALL_EVENT_KEYS.REMOTE_MEDIA, remoteMediaCb);
|
|
173
|
+
|
|
174
|
+
remoteMediaCb(mockTrack);
|
|
175
|
+
|
|
176
|
+
expect(taskEmitSpy).toHaveBeenCalledWith(TASK_EVENTS.TASK_MEDIA, mockTrack);
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
describe('updateTaskData cases', () => {
|
|
180
|
+
it('test updating the task data by overwrite', async () => {
|
|
181
|
+
const newData = {
|
|
182
|
+
type: CC_EVENTS.AGENT_CONTACT_ASSIGNED,
|
|
183
|
+
agentId: '723a8ffb-a26e-496d-b14a-ff44fb83b64f',
|
|
184
|
+
eventTime: 1733211616959,
|
|
185
|
+
eventType: 'RoutingMessage',
|
|
186
|
+
interactionId: taskId,
|
|
187
|
+
orgId: '6ecef209-9a34-4ed1-a07a-7ddd1dbe925a',
|
|
188
|
+
trackingId: '575c0ec2-618c-42af-a61c-53aeb0a221ee',
|
|
189
|
+
mediaResourceId: '0ae913a4-c857-4705-8d49-76dd3dde75e4',
|
|
190
|
+
destAgentId: 'ebeb893b-ba67-4f36-8418-95c7492b28c2',
|
|
191
|
+
owner: '723a8ffb-a26e-496d-b14a-ff44fb83b64f',
|
|
192
|
+
queueMgr: 'aqm',
|
|
193
|
+
interaction: {
|
|
194
|
+
mainInteractionId: taskId,
|
|
195
|
+
media: {
|
|
196
|
+
'58a45567-4e61-4f4b-a580-5bc86357bef0': {
|
|
197
|
+
holdTimestamp: null,
|
|
198
|
+
isHold: false,
|
|
199
|
+
mType: 'consult',
|
|
200
|
+
mediaMgr: 'callmm',
|
|
201
|
+
mediaResourceId: '58a45567-4e61-4f4b-a580-5bc86357bef0',
|
|
202
|
+
mediaType: 'telephony',
|
|
203
|
+
participants: [
|
|
204
|
+
'f520d6b5-28ad-4f2f-b83e-781bb64af617',
|
|
205
|
+
'723a8ffb-a26e-496d-b14a-ff44fb83b64f',
|
|
206
|
+
],
|
|
207
|
+
},
|
|
208
|
+
[taskId]: {
|
|
209
|
+
holdTimestamp: 1734667567279,
|
|
210
|
+
isHold: true,
|
|
211
|
+
mType: 'mainCall',
|
|
212
|
+
mediaMgr: 'callmm',
|
|
213
|
+
mediaResourceId: taskId,
|
|
214
|
+
mediaType: 'telephony',
|
|
215
|
+
participants: ['+14696762938', '723a8ffb-a26e-496d-b14a-ff44fb83b64f'],
|
|
216
|
+
},
|
|
217
|
+
},
|
|
218
|
+
},
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
expect(task.data).toEqual(taskDataMock);
|
|
222
|
+
|
|
223
|
+
const shouldOverwrite = true;
|
|
224
|
+
task.updateTaskData(newData, shouldOverwrite);
|
|
225
|
+
|
|
226
|
+
expect(task.data).toEqual(newData);
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
it('test updating the task data by merging', async () => {
|
|
230
|
+
const newData = {
|
|
231
|
+
// ...taskDataMock, // Purposefully omit this to test scenario when other keys isn't present
|
|
232
|
+
isConsulting: true, // Add a new custom key to test persistence
|
|
233
|
+
interaction: {
|
|
234
|
+
// ...taskDataMock.interaction, // Purposefully omit this to test scenario when a nested key isn't present
|
|
235
|
+
media: {
|
|
236
|
+
'58a45567-4e61-4f4b-a580-5bc86357bef0': {
|
|
237
|
+
holdTimestamp: null,
|
|
238
|
+
isHold: true,
|
|
239
|
+
mType: 'consult',
|
|
240
|
+
mediaMgr: 'callmm',
|
|
241
|
+
mediaResourceId: '58a45567-4e61-4f4b-a580-5bc86357bef0',
|
|
242
|
+
mediaType: 'telephony',
|
|
243
|
+
participants: [
|
|
244
|
+
'f520d6b5-28ad-4f2f-b83e-781bb64af617',
|
|
245
|
+
'723a8ffb-a26e-496d-b14a-ff44fb83b64f',
|
|
246
|
+
],
|
|
247
|
+
},
|
|
248
|
+
[taskId]: {
|
|
249
|
+
holdTimestamp: 1734667567279,
|
|
250
|
+
isHold: false,
|
|
251
|
+
mType: 'mainCall',
|
|
252
|
+
mediaMgr: 'callmm',
|
|
253
|
+
mediaResourceId: taskId,
|
|
254
|
+
mediaType: 'telephony',
|
|
255
|
+
participants: ['+14696762938', '723a8ffb-a26e-496d-b14a-ff44fb83b64f'],
|
|
256
|
+
},
|
|
257
|
+
},
|
|
258
|
+
},
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
const expectedData: TaskData = {
|
|
262
|
+
...taskDataMock,
|
|
263
|
+
isConsulting: true,
|
|
264
|
+
interaction: {
|
|
265
|
+
...taskDataMock.interaction,
|
|
266
|
+
media: {
|
|
267
|
+
'58a45567-4e61-4f4b-a580-5bc86357bef0': {
|
|
268
|
+
holdTimestamp: null,
|
|
269
|
+
isHold: true,
|
|
270
|
+
mType: 'consult',
|
|
271
|
+
mediaMgr: 'callmm',
|
|
272
|
+
mediaResourceId: '58a45567-4e61-4f4b-a580-5bc86357bef0',
|
|
273
|
+
mediaType: 'telephony',
|
|
274
|
+
participants: [
|
|
275
|
+
'f520d6b5-28ad-4f2f-b83e-781bb64af617',
|
|
276
|
+
'723a8ffb-a26e-496d-b14a-ff44fb83b64f',
|
|
277
|
+
],
|
|
278
|
+
},
|
|
279
|
+
[taskId]: {
|
|
280
|
+
holdTimestamp: 1734667567279,
|
|
281
|
+
isHold: false,
|
|
282
|
+
mType: 'mainCall',
|
|
283
|
+
mediaMgr: 'callmm',
|
|
284
|
+
mediaResourceId: taskId,
|
|
285
|
+
mediaType: 'telephony',
|
|
286
|
+
participants: ['+14696762938', '723a8ffb-a26e-496d-b14a-ff44fb83b64f'],
|
|
287
|
+
},
|
|
288
|
+
},
|
|
289
|
+
},
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
expect(task.data).toEqual(taskDataMock);
|
|
293
|
+
const shouldOverwrite = false;
|
|
294
|
+
task.updateTaskData(newData, shouldOverwrite);
|
|
295
|
+
|
|
296
|
+
expect(task.data).toEqual(expectedData);
|
|
297
|
+
});
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
it('should accept a task and answer call when using BROWSER login option', async () => {
|
|
301
|
+
const answerCallSpy = jest.spyOn(webCallingService, 'answerCall');
|
|
302
|
+
|
|
303
|
+
await task.accept();
|
|
304
|
+
|
|
305
|
+
expect(navigator.mediaDevices.getUserMedia).toHaveBeenCalledWith({audio: true});
|
|
306
|
+
expect(LocalMicrophoneStream).toHaveBeenCalledWith(mockStream);
|
|
307
|
+
expect(answerCallSpy).toHaveBeenCalledWith(expect.any(LocalMicrophoneStream), taskId);
|
|
308
|
+
expect(loggerInfoSpy).toHaveBeenCalledWith(`Accepting task`, {
|
|
309
|
+
module: TASK_FILE,
|
|
310
|
+
method: 'accept',
|
|
311
|
+
interactionId: task.data.interactionId,
|
|
312
|
+
});
|
|
313
|
+
expect(loggerLogSpy).toHaveBeenCalledWith(
|
|
314
|
+
`Task accepted successfully with webrtc calling`,
|
|
315
|
+
{
|
|
316
|
+
module: TASK_FILE,
|
|
317
|
+
method: 'accept',
|
|
318
|
+
interactionId: task.data.interactionId,
|
|
319
|
+
}
|
|
320
|
+
);
|
|
321
|
+
expect(mockMetricsManager.trackEvent).toHaveBeenNthCalledWith(
|
|
322
|
+
1,
|
|
323
|
+
METRIC_EVENT_NAMES.TASK_ACCEPT_SUCCESS,
|
|
324
|
+
{
|
|
325
|
+
taskId: task.data.interactionId,
|
|
326
|
+
...MetricsManager.getCommonTrackingFieldForAQMResponse(task),
|
|
327
|
+
eventType: 'AgentContactReserved',
|
|
328
|
+
notifTrackingId: '575c0ec2-618c-42af-a61c-53aeb0a221ee',
|
|
329
|
+
trackingId: undefined,
|
|
330
|
+
},
|
|
331
|
+
['operational', 'behavioral', 'business']
|
|
332
|
+
);
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
it('should accept a task when mediaType chat', async () => {
|
|
336
|
+
task.data.interaction.mediaType = 'chat';
|
|
337
|
+
const answerCallSpy = jest.spyOn(webCallingService, 'answerCall');
|
|
338
|
+
const response = {};
|
|
339
|
+
contactMock.accept.mockResolvedValue(response);
|
|
340
|
+
|
|
341
|
+
await task.accept();
|
|
342
|
+
|
|
343
|
+
expect(contactMock.accept).toHaveBeenCalledWith({
|
|
344
|
+
interactionId: taskId,
|
|
345
|
+
});
|
|
346
|
+
expect(answerCallSpy).not.toHaveBeenCalled();
|
|
347
|
+
expect(mockMetricsManager.timeEvent).toHaveBeenCalledWith([
|
|
348
|
+
METRIC_EVENT_NAMES.TASK_ACCEPT_SUCCESS,
|
|
349
|
+
METRIC_EVENT_NAMES.TASK_ACCEPT_FAILED,
|
|
350
|
+
]);
|
|
351
|
+
const expectedMetrics = {
|
|
352
|
+
taskId: task.data.interactionId,
|
|
353
|
+
agentId: task.data.agentId,
|
|
354
|
+
eventType: task.data.type,
|
|
355
|
+
notifTrackingId: task.data.trackingId,
|
|
356
|
+
orgId: task.data.orgId,
|
|
357
|
+
};
|
|
358
|
+
expect(mockMetricsManager.trackEvent).toHaveBeenCalledWith(
|
|
359
|
+
METRIC_EVENT_NAMES.TASK_ACCEPT_SUCCESS,
|
|
360
|
+
expectedMetrics,
|
|
361
|
+
['operational', 'behavioral', 'business']
|
|
362
|
+
);
|
|
363
|
+
expect(loggerInfoSpy).toHaveBeenCalledWith(`Accepting task`, {
|
|
364
|
+
module: TASK_FILE,
|
|
365
|
+
method: 'accept',
|
|
366
|
+
interactionId: task.data.interactionId,
|
|
367
|
+
});
|
|
368
|
+
expect(loggerLogSpy).toHaveBeenCalledWith(`Task accepted successfully`, {
|
|
369
|
+
module: TASK_FILE,
|
|
370
|
+
method: 'accept',
|
|
371
|
+
interactionId: task.data.interactionId,
|
|
372
|
+
});
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
it('should accept a task when mediaType email', async () => {
|
|
376
|
+
task.data.interaction.mediaType = 'email';
|
|
377
|
+
const answerCallSpy = jest.spyOn(webCallingService, 'answerCall');
|
|
378
|
+
const response = {};
|
|
379
|
+
contactMock.accept.mockResolvedValue(response);
|
|
380
|
+
|
|
381
|
+
await task.accept();
|
|
382
|
+
|
|
383
|
+
expect(contactMock.accept).toHaveBeenCalledWith({
|
|
384
|
+
interactionId: taskId,
|
|
385
|
+
});
|
|
386
|
+
expect(answerCallSpy).not.toHaveBeenCalled();
|
|
387
|
+
expect(mockMetricsManager.timeEvent).toHaveBeenCalledWith([
|
|
388
|
+
METRIC_EVENT_NAMES.TASK_ACCEPT_SUCCESS,
|
|
389
|
+
METRIC_EVENT_NAMES.TASK_ACCEPT_FAILED,
|
|
390
|
+
]);
|
|
391
|
+
const expectedMetrics = {
|
|
392
|
+
taskId: task.data.interactionId,
|
|
393
|
+
agentId: task.data.agentId,
|
|
394
|
+
eventType: task.data.type,
|
|
395
|
+
notifTrackingId: task.data.trackingId,
|
|
396
|
+
orgId: task.data.orgId,
|
|
397
|
+
};
|
|
398
|
+
expect(mockMetricsManager.trackEvent).toHaveBeenCalledWith(
|
|
399
|
+
METRIC_EVENT_NAMES.TASK_ACCEPT_SUCCESS,
|
|
400
|
+
expectedMetrics,
|
|
401
|
+
['operational', 'behavioral', 'business']
|
|
402
|
+
);
|
|
403
|
+
expect(loggerInfoSpy).toHaveBeenCalledWith(`Accepting task`, {
|
|
404
|
+
module: TASK_FILE,
|
|
405
|
+
method: 'accept',
|
|
406
|
+
interactionId: task.data.interactionId,
|
|
407
|
+
});
|
|
408
|
+
expect(loggerLogSpy).toHaveBeenCalledWith(`Task accepted successfully`, {
|
|
409
|
+
module: TASK_FILE,
|
|
410
|
+
method: 'accept',
|
|
411
|
+
interactionId: task.data.interactionId,
|
|
412
|
+
});
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
it('should handle errors in accept method', async () => {
|
|
416
|
+
const error = {
|
|
417
|
+
details: {
|
|
418
|
+
trackingId: '1234',
|
|
419
|
+
data: {
|
|
420
|
+
reason: 'Accept Failed',
|
|
421
|
+
},
|
|
422
|
+
},
|
|
423
|
+
};
|
|
424
|
+
|
|
425
|
+
jest.spyOn(webCallingService, 'answerCall').mockImplementation(() => {
|
|
426
|
+
throw error;
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
await expect(task.accept()).rejects.toThrow(new Error(error.details.data.reason));
|
|
430
|
+
expect(getErrorDetailsSpy).toHaveBeenCalledWith(error, 'accept', TASK_FILE);
|
|
431
|
+
expect(mockMetricsManager.trackEvent).toHaveBeenNthCalledWith(
|
|
432
|
+
1,
|
|
433
|
+
METRIC_EVENT_NAMES.TASK_ACCEPT_FAILED,
|
|
434
|
+
{
|
|
435
|
+
taskId: taskDataMock.interactionId,
|
|
436
|
+
error: error.toString(),
|
|
437
|
+
...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details),
|
|
438
|
+
},
|
|
439
|
+
['operational', 'behavioral', 'business']
|
|
440
|
+
);
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
it('should decline call using webCallingService', async () => {
|
|
444
|
+
const declineCallSpy = jest.spyOn(webCallingService, 'declineCall');
|
|
445
|
+
const offSpy = jest.spyOn(webCallingService, 'off');
|
|
446
|
+
|
|
447
|
+
await task.decline();
|
|
448
|
+
|
|
449
|
+
expect(declineCallSpy).toHaveBeenCalledWith(taskId);
|
|
450
|
+
expect(offSpy).toHaveBeenCalledWith(CALL_EVENT_KEYS.REMOTE_MEDIA, offSpy.mock.calls[0][1]);
|
|
451
|
+
expect(loggerInfoSpy).toHaveBeenCalledWith(`Declining task`, {
|
|
452
|
+
module: TASK_FILE,
|
|
453
|
+
method: 'decline',
|
|
454
|
+
interactionId: task.data.interactionId,
|
|
455
|
+
});
|
|
456
|
+
expect(loggerLogSpy).toHaveBeenCalledWith(`Task declined successfully`, {
|
|
457
|
+
module: TASK_FILE,
|
|
458
|
+
method: 'decline',
|
|
459
|
+
interactionId: task.data.interactionId,
|
|
460
|
+
});
|
|
461
|
+
expect(mockMetricsManager.trackEvent).toHaveBeenNthCalledWith(
|
|
462
|
+
1,
|
|
463
|
+
METRIC_EVENT_NAMES.TASK_DECLINE_SUCCESS,
|
|
464
|
+
{
|
|
465
|
+
taskId: taskDataMock.interactionId,
|
|
466
|
+
},
|
|
467
|
+
['operational', 'behavioral']
|
|
468
|
+
);
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
it('should handle errors in decline method', async () => {
|
|
472
|
+
const error = {
|
|
473
|
+
details: {
|
|
474
|
+
trackingId: '1234',
|
|
475
|
+
data: {
|
|
476
|
+
reason: 'Decline Failed',
|
|
477
|
+
},
|
|
478
|
+
},
|
|
479
|
+
};
|
|
480
|
+
|
|
481
|
+
jest.spyOn(webCallingService, 'declineCall').mockImplementation(() => {
|
|
482
|
+
throw error;
|
|
483
|
+
});
|
|
484
|
+
await expect(task.decline()).rejects.toThrow(new Error(error.details.data.reason));
|
|
485
|
+
expect(getErrorDetailsSpy).toHaveBeenCalledWith(error, 'decline', TASK_FILE);
|
|
486
|
+
expect(mockMetricsManager.trackEvent).toHaveBeenNthCalledWith(
|
|
487
|
+
1,
|
|
488
|
+
METRIC_EVENT_NAMES.TASK_DECLINE_FAILED,
|
|
489
|
+
{
|
|
490
|
+
taskId: taskDataMock.interactionId,
|
|
491
|
+
error: error.toString(),
|
|
492
|
+
...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details),
|
|
493
|
+
},
|
|
494
|
+
['operational', 'behavioral']
|
|
495
|
+
);
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
it('should hold the task and return the expected response', async () => {
|
|
499
|
+
const expectedResponse: TaskResponse = {data: {interactionId: taskId}} as AgentContact;
|
|
500
|
+
contactMock.hold.mockResolvedValue(expectedResponse);
|
|
501
|
+
|
|
502
|
+
const response = await task.hold();
|
|
503
|
+
|
|
504
|
+
expect(contactMock.hold).toHaveBeenCalledWith({
|
|
505
|
+
interactionId: taskId,
|
|
506
|
+
data: {mediaResourceId: taskDataMock.mediaResourceId},
|
|
507
|
+
});
|
|
508
|
+
expect(response).toEqual(expectedResponse);
|
|
509
|
+
expect(loggerInfoSpy).toHaveBeenCalledWith(`Holding task`, {
|
|
510
|
+
module: TASK_FILE,
|
|
511
|
+
method: 'hold',
|
|
512
|
+
interactionId: task.data.interactionId,
|
|
513
|
+
});
|
|
514
|
+
expect(loggerLogSpy).toHaveBeenCalledWith(`Task placed on hold successfully`, {
|
|
515
|
+
module: TASK_FILE,
|
|
516
|
+
method: 'hold',
|
|
517
|
+
interactionId: task.data.interactionId,
|
|
518
|
+
});
|
|
519
|
+
expect(mockMetricsManager.trackEvent).toHaveBeenNthCalledWith(
|
|
520
|
+
1,
|
|
521
|
+
METRIC_EVENT_NAMES.TASK_HOLD_SUCCESS,
|
|
522
|
+
{
|
|
523
|
+
...MetricsManager.getCommonTrackingFieldForAQMResponse(expectedResponse),
|
|
524
|
+
taskId: taskDataMock.interactionId,
|
|
525
|
+
mediaResourceId: taskDataMock.mediaResourceId,
|
|
526
|
+
},
|
|
527
|
+
['operational', 'behavioral']
|
|
528
|
+
);
|
|
529
|
+
});
|
|
530
|
+
|
|
531
|
+
it('should handle errors in hold method', async () => {
|
|
532
|
+
const error = {
|
|
533
|
+
details: {
|
|
534
|
+
trackingId: '1234',
|
|
535
|
+
data: {
|
|
536
|
+
reason: 'Hold Failed',
|
|
537
|
+
},
|
|
538
|
+
},
|
|
539
|
+
};
|
|
540
|
+
contactMock.hold.mockImplementation(() => {
|
|
541
|
+
throw error;
|
|
542
|
+
});
|
|
543
|
+
|
|
544
|
+
await expect(task.hold()).rejects.toThrow(error.details.data.reason);
|
|
545
|
+
expect(getErrorDetailsSpy).toHaveBeenCalledWith(error, 'hold', TASK_FILE);
|
|
546
|
+
expect(mockMetricsManager.trackEvent).toHaveBeenNthCalledWith(
|
|
547
|
+
1,
|
|
548
|
+
METRIC_EVENT_NAMES.TASK_HOLD_FAILED,
|
|
549
|
+
{
|
|
550
|
+
taskId: taskDataMock.interactionId,
|
|
551
|
+
mediaResourceId: taskDataMock.mediaResourceId,
|
|
552
|
+
error: error.toString(),
|
|
553
|
+
...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details),
|
|
554
|
+
},
|
|
555
|
+
['operational', 'behavioral']
|
|
556
|
+
);
|
|
557
|
+
});
|
|
558
|
+
|
|
559
|
+
it('should resume the task and return the expected response', async () => {
|
|
560
|
+
const expectedResponse: TaskResponse = {data: {interactionId: taskId}} as AgentContact;
|
|
561
|
+
contactMock.unHold.mockResolvedValue(expectedResponse);
|
|
562
|
+
const response = await task.resume();
|
|
563
|
+
expect(contactMock.unHold).toHaveBeenCalledWith({
|
|
564
|
+
interactionId: taskId,
|
|
565
|
+
data: {mediaResourceId: taskDataMock.mediaResourceId},
|
|
566
|
+
});
|
|
567
|
+
expect(response).toEqual(expectedResponse);
|
|
568
|
+
expect(mockMetricsManager.trackEvent).toHaveBeenNthCalledWith(
|
|
569
|
+
1,
|
|
570
|
+
METRIC_EVENT_NAMES.TASK_RESUME_SUCCESS,
|
|
571
|
+
{
|
|
572
|
+
taskId: taskDataMock.interactionId,
|
|
573
|
+
mainInteractionId: taskDataMock.interaction.mainInteractionId,
|
|
574
|
+
mediaResourceId:
|
|
575
|
+
taskDataMock.interaction.media[taskDataMock.interaction.mainInteractionId]
|
|
576
|
+
.mediaResourceId,
|
|
577
|
+
...MetricsManager.getCommonTrackingFieldForAQMResponse(expectedResponse),
|
|
578
|
+
},
|
|
579
|
+
['operational', 'behavioral']
|
|
580
|
+
);
|
|
581
|
+
});
|
|
582
|
+
|
|
583
|
+
it('should handle errors in resume method', async () => {
|
|
584
|
+
const error = {
|
|
585
|
+
details: {
|
|
586
|
+
trackingId: '1234',
|
|
587
|
+
data: {
|
|
588
|
+
reason: 'Resume Failed',
|
|
589
|
+
},
|
|
590
|
+
},
|
|
591
|
+
};
|
|
592
|
+
contactMock.unHold.mockImplementation(() => {
|
|
593
|
+
throw error;
|
|
594
|
+
});
|
|
595
|
+
|
|
596
|
+
await expect(task.resume()).rejects.toThrow(error.details.data.reason);
|
|
597
|
+
expect(getErrorDetailsSpy).toHaveBeenCalledWith(error, 'resume', TASK_FILE);
|
|
598
|
+
expect(mockMetricsManager.trackEvent).toHaveBeenNthCalledWith(
|
|
599
|
+
1,
|
|
600
|
+
METRIC_EVENT_NAMES.TASK_RESUME_FAILED,
|
|
601
|
+
{
|
|
602
|
+
taskId: taskDataMock.interactionId,
|
|
603
|
+
mainInteractionId: taskDataMock.interaction.mainInteractionId,
|
|
604
|
+
mediaResourceId:
|
|
605
|
+
taskDataMock.interaction.media[taskDataMock.interaction.mainInteractionId]
|
|
606
|
+
.mediaResourceId,
|
|
607
|
+
...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details),
|
|
608
|
+
},
|
|
609
|
+
['operational', 'behavioral']
|
|
610
|
+
);
|
|
611
|
+
});
|
|
612
|
+
|
|
613
|
+
it('should initiate a consult call and return the expected response', async () => {
|
|
614
|
+
const consultPayload = {
|
|
615
|
+
to: '1234',
|
|
616
|
+
destinationType: DESTINATION_TYPE.AGENT,
|
|
617
|
+
};
|
|
618
|
+
const expectedResponse: TaskResponse = {data: {interactionId: taskId}, trackingId: '1234'} as AgentContact;
|
|
619
|
+
contactMock.consult.mockResolvedValue(expectedResponse);
|
|
620
|
+
|
|
621
|
+
const response = await task.consult(consultPayload);
|
|
622
|
+
|
|
623
|
+
expect(contactMock.consult).toHaveBeenCalledWith({interactionId: taskId, data: consultPayload});
|
|
624
|
+
expect(response).toEqual(expectedResponse);
|
|
625
|
+
expect(loggerInfoSpy).toHaveBeenCalledWith(`Starting consult`, {
|
|
626
|
+
module: TASK_FILE,
|
|
627
|
+
method: 'consult',
|
|
628
|
+
interactionId: task.data.interactionId,
|
|
629
|
+
});
|
|
630
|
+
expect(loggerLogSpy).toHaveBeenCalledWith(`Consult started successfully to ${consultPayload.to}`, {
|
|
631
|
+
module: TASK_FILE,
|
|
632
|
+
method: 'consult',
|
|
633
|
+
trackingId: expectedResponse.trackingId,
|
|
634
|
+
interactionId: task.data.interactionId,
|
|
635
|
+
});
|
|
636
|
+
expect(mockMetricsManager.trackEvent).toHaveBeenCalledWith(
|
|
637
|
+
METRIC_EVENT_NAMES.TASK_CONSULT_START_SUCCESS,
|
|
638
|
+
{
|
|
639
|
+
taskId: taskDataMock.interactionId,
|
|
640
|
+
destination: consultPayload.to,
|
|
641
|
+
destinationType: consultPayload.destinationType,
|
|
642
|
+
...MetricsManager.getCommonTrackingFieldForAQMResponse(expectedResponse),
|
|
643
|
+
},
|
|
644
|
+
['operational', 'behavioral', 'business']
|
|
645
|
+
);
|
|
646
|
+
});
|
|
647
|
+
|
|
648
|
+
it('should handle errors in consult method', async () => {
|
|
649
|
+
const error = {
|
|
650
|
+
details: {
|
|
651
|
+
trackingId: '1234',
|
|
652
|
+
data: {
|
|
653
|
+
reason: 'Consult Failed',
|
|
654
|
+
},
|
|
655
|
+
},
|
|
656
|
+
};
|
|
657
|
+
contactMock.consult.mockImplementation(() => {
|
|
658
|
+
throw error;
|
|
659
|
+
});
|
|
660
|
+
|
|
661
|
+
const consultPayload = {
|
|
662
|
+
to: '1234',
|
|
663
|
+
destinationType: DESTINATION_TYPE.AGENT,
|
|
664
|
+
};
|
|
665
|
+
|
|
666
|
+
await expect(task.consult(consultPayload)).rejects.toThrow(error.details.data.reason);
|
|
667
|
+
expect(getErrorDetailsSpy).toHaveBeenCalledWith(error, 'consult', TASK_FILE);
|
|
668
|
+
expect(loggerInfoSpy).toHaveBeenCalledWith(`Starting consult`, {
|
|
669
|
+
module: TASK_FILE,
|
|
670
|
+
method: 'consult',
|
|
671
|
+
interactionId: task.data.interactionId,
|
|
672
|
+
});
|
|
673
|
+
expect(mockMetricsManager.trackEvent).toHaveBeenCalledWith(
|
|
674
|
+
METRIC_EVENT_NAMES.TASK_CONSULT_START_FAILED,
|
|
675
|
+
{
|
|
676
|
+
taskId: taskDataMock.interactionId,
|
|
677
|
+
destination: consultPayload.to,
|
|
678
|
+
destinationType: consultPayload.destinationType,
|
|
679
|
+
error: error.toString(),
|
|
680
|
+
...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details),
|
|
681
|
+
},
|
|
682
|
+
['operational', 'behavioral', 'business']
|
|
683
|
+
);
|
|
684
|
+
});
|
|
685
|
+
|
|
686
|
+
it('should end the consult call and return the expected response', async () => {
|
|
687
|
+
const expectedResponse: TaskResponse = {data: {interactionId: taskId}} as AgentContact;
|
|
688
|
+
contactMock.consultEnd.mockResolvedValue(expectedResponse);
|
|
689
|
+
|
|
690
|
+
const consultEndPayload: ConsultEndPayload = {
|
|
691
|
+
isConsult: true,
|
|
692
|
+
taskId: taskId,
|
|
693
|
+
};
|
|
694
|
+
const response = await task.endConsult(consultEndPayload);
|
|
695
|
+
|
|
696
|
+
expect(contactMock.consultEnd).toHaveBeenCalledWith({
|
|
697
|
+
interactionId: taskId,
|
|
698
|
+
data: consultEndPayload,
|
|
699
|
+
});
|
|
700
|
+
expect(response).toEqual(expectedResponse);
|
|
701
|
+
expect(mockMetricsManager.trackEvent).toHaveBeenNthCalledWith(
|
|
702
|
+
1,
|
|
703
|
+
METRIC_EVENT_NAMES.TASK_CONSULT_END_SUCCESS,
|
|
704
|
+
{
|
|
705
|
+
taskId: taskDataMock.interactionId,
|
|
706
|
+
...MetricsManager.getCommonTrackingFieldForAQMResponse(expectedResponse),
|
|
707
|
+
},
|
|
708
|
+
['operational', 'behavioral', 'business']
|
|
709
|
+
);
|
|
710
|
+
});
|
|
711
|
+
|
|
712
|
+
it('should handle errors in endConsult method', async () => {
|
|
713
|
+
const error = {
|
|
714
|
+
details: {
|
|
715
|
+
trackingId: '1234',
|
|
716
|
+
data: {
|
|
717
|
+
reason: 'End Consult Failed',
|
|
718
|
+
},
|
|
719
|
+
},
|
|
720
|
+
};
|
|
721
|
+
contactMock.consultEnd.mockImplementation(() => {
|
|
722
|
+
throw error;
|
|
723
|
+
});
|
|
724
|
+
|
|
725
|
+
const consultEndPayload: ConsultEndPayload = {
|
|
726
|
+
isConsult: true,
|
|
727
|
+
taskId: taskId,
|
|
728
|
+
};
|
|
729
|
+
|
|
730
|
+
await expect(task.endConsult(consultEndPayload)).rejects.toThrow(error.details.data.reason);
|
|
731
|
+
expect(getErrorDetailsSpy).toHaveBeenCalledWith(error, 'endConsult', TASK_FILE);
|
|
732
|
+
expect(mockMetricsManager.trackEvent).toHaveBeenNthCalledWith(
|
|
733
|
+
1,
|
|
734
|
+
METRIC_EVENT_NAMES.TASK_CONSULT_END_FAILED,
|
|
735
|
+
{
|
|
736
|
+
taskId: taskDataMock.interactionId,
|
|
737
|
+
error: error.toString(),
|
|
738
|
+
...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details),
|
|
739
|
+
},
|
|
740
|
+
['operational', 'behavioral', 'business']
|
|
741
|
+
);
|
|
742
|
+
});
|
|
743
|
+
|
|
744
|
+
it('should do consult transfer the task to consulted agent and return the expected response', async () => {
|
|
745
|
+
const consultPayload = {
|
|
746
|
+
destination: '1234',
|
|
747
|
+
destinationType: DESTINATION_TYPE.AGENT,
|
|
748
|
+
};
|
|
749
|
+
const expectedResponse: TaskResponse = {data: {interactionId: taskId}} as AgentContact;
|
|
750
|
+
contactMock.consult.mockResolvedValue(expectedResponse);
|
|
751
|
+
|
|
752
|
+
const response = await task.consult(consultPayload);
|
|
753
|
+
|
|
754
|
+
expect(contactMock.consult).toHaveBeenCalledWith({interactionId: taskId, data: consultPayload});
|
|
755
|
+
expect(response).toEqual(expectedResponse);
|
|
756
|
+
|
|
757
|
+
const consultTransferPayload: ConsultTransferPayLoad = {
|
|
758
|
+
to: '1234',
|
|
759
|
+
destinationType: CONSULT_TRANSFER_DESTINATION_TYPE.AGENT,
|
|
760
|
+
};
|
|
761
|
+
|
|
762
|
+
const consultTransferResponse = await task.consultTransfer(consultTransferPayload);
|
|
763
|
+
expect(contactMock.consultTransfer).toHaveBeenCalledWith({
|
|
764
|
+
interactionId: taskId,
|
|
765
|
+
data: consultTransferPayload,
|
|
766
|
+
});
|
|
767
|
+
expect(mockMetricsManager.trackEvent).toHaveBeenNthCalledWith(
|
|
768
|
+
2,
|
|
769
|
+
METRIC_EVENT_NAMES.TASK_TRANSFER_SUCCESS,
|
|
770
|
+
{
|
|
771
|
+
taskId: taskDataMock.interactionId,
|
|
772
|
+
destination: consultTransferPayload.to,
|
|
773
|
+
destinationType: consultTransferPayload.destinationType,
|
|
774
|
+
isConsultTransfer: true,
|
|
775
|
+
},
|
|
776
|
+
['operational', 'behavioral', 'business']
|
|
777
|
+
);
|
|
778
|
+
});
|
|
779
|
+
|
|
780
|
+
it('should do consult transfer to a queue by using the destAgentId from task data', async () => {
|
|
781
|
+
const expectedResponse: TaskResponse = {data: {interactionId: taskId}} as AgentContact;
|
|
782
|
+
contactMock.consultTransfer.mockResolvedValue(expectedResponse);
|
|
783
|
+
|
|
784
|
+
const queueConsultTransferPayload: ConsultTransferPayLoad = {
|
|
785
|
+
to: 'some-queue-id',
|
|
786
|
+
destinationType: CONSULT_TRANSFER_DESTINATION_TYPE.QUEUE,
|
|
787
|
+
};
|
|
788
|
+
|
|
789
|
+
const expectedPayload = {
|
|
790
|
+
to: taskDataMock.destAgentId,
|
|
791
|
+
destinationType: CONSULT_TRANSFER_DESTINATION_TYPE.AGENT,
|
|
792
|
+
};
|
|
793
|
+
|
|
794
|
+
const response = await task.consultTransfer(queueConsultTransferPayload);
|
|
795
|
+
|
|
796
|
+
expect(contactMock.consultTransfer).toHaveBeenCalledWith({
|
|
797
|
+
interactionId: taskId,
|
|
798
|
+
data: expectedPayload,
|
|
799
|
+
});
|
|
800
|
+
expect(response).toEqual(expectedResponse);
|
|
801
|
+
});
|
|
802
|
+
|
|
803
|
+
it('should throw error when attempting to transfer to queue with no destAgentId', async () => {
|
|
804
|
+
const taskWithoutDestAgentId = new Task(contactMock, webCallingService, {
|
|
805
|
+
...taskDataMock,
|
|
806
|
+
destAgentId: undefined,
|
|
807
|
+
});
|
|
808
|
+
|
|
809
|
+
const queueConsultTransferPayload: ConsultTransferPayLoad = {
|
|
810
|
+
to: 'some-queue-id',
|
|
811
|
+
destinationType: CONSULT_TRANSFER_DESTINATION_TYPE.QUEUE,
|
|
812
|
+
};
|
|
813
|
+
|
|
814
|
+
await expect(
|
|
815
|
+
taskWithoutDestAgentId.consultTransfer(queueConsultTransferPayload)
|
|
816
|
+
).rejects.toThrow('Error while performing consultTransfer');
|
|
817
|
+
});
|
|
818
|
+
|
|
819
|
+
it('should handle errors in consult transfer', async () => {
|
|
820
|
+
const consultPayload = {
|
|
821
|
+
destination: '1234',
|
|
822
|
+
destinationType: DESTINATION_TYPE.AGENT,
|
|
823
|
+
};
|
|
824
|
+
const expectedResponse: TaskResponse = {data: {interactionId: taskId}} as AgentContact;
|
|
825
|
+
contactMock.consult.mockResolvedValue(expectedResponse);
|
|
826
|
+
|
|
827
|
+
const response = await task.consult(consultPayload);
|
|
828
|
+
|
|
829
|
+
expect(contactMock.consult).toHaveBeenCalledWith({interactionId: taskId, data: consultPayload});
|
|
830
|
+
expect(response).toEqual(expectedResponse);
|
|
831
|
+
|
|
832
|
+
const error = {
|
|
833
|
+
details: {
|
|
834
|
+
trackingId: '1234',
|
|
835
|
+
data: {
|
|
836
|
+
reason: 'Consult Transfer Failed',
|
|
837
|
+
},
|
|
838
|
+
},
|
|
839
|
+
};
|
|
840
|
+
contactMock.consultTransfer.mockImplementation(() => {
|
|
841
|
+
throw error;
|
|
842
|
+
});
|
|
843
|
+
|
|
844
|
+
const consultTransferPayload: ConsultTransferPayLoad = {
|
|
845
|
+
to: '1234',
|
|
846
|
+
destinationType: CONSULT_TRANSFER_DESTINATION_TYPE.AGENT,
|
|
847
|
+
};
|
|
848
|
+
|
|
849
|
+
await expect(task.consultTransfer(consultTransferPayload)).rejects.toThrow(
|
|
850
|
+
error.details.data.reason
|
|
851
|
+
);
|
|
852
|
+
expect(getErrorDetailsSpy).toHaveBeenCalledWith(error, 'consultTransfer', TASK_FILE);
|
|
853
|
+
expect(mockMetricsManager.trackEvent).toHaveBeenNthCalledWith(
|
|
854
|
+
2,
|
|
855
|
+
METRIC_EVENT_NAMES.TASK_TRANSFER_FAILED,
|
|
856
|
+
{
|
|
857
|
+
taskId: taskDataMock.interactionId,
|
|
858
|
+
destination: consultTransferPayload.to,
|
|
859
|
+
destinationType: consultTransferPayload.destinationType,
|
|
860
|
+
isConsultTransfer: true,
|
|
861
|
+
error: error.toString(),
|
|
862
|
+
...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details),
|
|
863
|
+
},
|
|
864
|
+
['operational', 'behavioral', 'business']
|
|
865
|
+
);
|
|
866
|
+
});
|
|
867
|
+
|
|
868
|
+
it('should do vteamTransfer if destinationType is queue and return the expected response', async () => {
|
|
869
|
+
const transferPayload: TransferPayLoad = {
|
|
870
|
+
to: '1234',
|
|
871
|
+
destinationType: DESTINATION_TYPE.QUEUE,
|
|
872
|
+
};
|
|
873
|
+
|
|
874
|
+
const expectedResponse: TaskResponse = {data: {interactionId: taskId}} as AgentContact;
|
|
875
|
+
contactMock.vteamTransfer.mockResolvedValue(expectedResponse);
|
|
876
|
+
|
|
877
|
+
const response = await task.transfer(transferPayload);
|
|
878
|
+
|
|
879
|
+
expect(contactMock.vteamTransfer).toHaveBeenCalledWith({
|
|
880
|
+
interactionId: taskId,
|
|
881
|
+
data: transferPayload,
|
|
882
|
+
});
|
|
883
|
+
expect(response).toEqual(expectedResponse);
|
|
884
|
+
expect(mockMetricsManager.trackEvent).toHaveBeenNthCalledWith(
|
|
885
|
+
1,
|
|
886
|
+
METRIC_EVENT_NAMES.TASK_TRANSFER_SUCCESS,
|
|
887
|
+
{
|
|
888
|
+
taskId: taskDataMock.interactionId,
|
|
889
|
+
destination: transferPayload.to,
|
|
890
|
+
destinationType: transferPayload.destinationType,
|
|
891
|
+
isConsultTransfer: false,
|
|
892
|
+
...MetricsManager.getCommonTrackingFieldForAQMResponse(expectedResponse),
|
|
893
|
+
},
|
|
894
|
+
['operational', 'behavioral', 'business']
|
|
895
|
+
);
|
|
896
|
+
});
|
|
897
|
+
|
|
898
|
+
it('should do blindTransfer if destinationType is anything other than queue and return the expected response', async () => {
|
|
899
|
+
const transferPayload: TransferPayLoad = {
|
|
900
|
+
to: '1234',
|
|
901
|
+
destinationType: DESTINATION_TYPE.AGENT,
|
|
902
|
+
};
|
|
903
|
+
|
|
904
|
+
const expectedResponse: TaskResponse = {data: {interactionId: taskId}} as AgentContact;
|
|
905
|
+
contactMock.blindTransfer.mockResolvedValue(expectedResponse);
|
|
906
|
+
|
|
907
|
+
const response = await task.transfer(transferPayload);
|
|
908
|
+
|
|
909
|
+
expect(contactMock.blindTransfer).toHaveBeenCalledWith({
|
|
910
|
+
interactionId: taskId,
|
|
911
|
+
data: transferPayload,
|
|
912
|
+
});
|
|
913
|
+
expect(response).toEqual(expectedResponse);
|
|
914
|
+
expect(mockMetricsManager.trackEvent).toHaveBeenNthCalledWith(
|
|
915
|
+
1,
|
|
916
|
+
METRIC_EVENT_NAMES.TASK_TRANSFER_SUCCESS,
|
|
917
|
+
{
|
|
918
|
+
taskId: taskDataMock.interactionId,
|
|
919
|
+
destination: transferPayload.to,
|
|
920
|
+
destinationType: transferPayload.destinationType,
|
|
921
|
+
isConsultTransfer: false,
|
|
922
|
+
...MetricsManager.getCommonTrackingFieldForAQMResponse(expectedResponse),
|
|
923
|
+
},
|
|
924
|
+
['operational', 'behavioral', 'business']
|
|
925
|
+
);
|
|
926
|
+
});
|
|
927
|
+
|
|
928
|
+
it('should handle errors in transfer method', async () => {
|
|
929
|
+
const error = {
|
|
930
|
+
details: {
|
|
931
|
+
trackingId: '1234',
|
|
932
|
+
data: {
|
|
933
|
+
reason: 'Consult Transfer Failed',
|
|
934
|
+
},
|
|
935
|
+
},
|
|
936
|
+
};
|
|
937
|
+
contactMock.blindTransfer.mockImplementation(() => {
|
|
938
|
+
throw error;
|
|
939
|
+
});
|
|
940
|
+
|
|
941
|
+
const blindTransferPayload: TransferPayLoad = {
|
|
942
|
+
to: '1234',
|
|
943
|
+
destinationType: DESTINATION_TYPE.AGENT,
|
|
944
|
+
};
|
|
945
|
+
|
|
946
|
+
await expect(task.transfer(blindTransferPayload)).rejects.toThrow(error.details.data.reason);
|
|
947
|
+
expect(getErrorDetailsSpy).toHaveBeenCalledWith(error, 'transfer', TASK_FILE);
|
|
948
|
+
expect(mockMetricsManager.trackEvent).toHaveBeenNthCalledWith(
|
|
949
|
+
1,
|
|
950
|
+
METRIC_EVENT_NAMES.TASK_TRANSFER_FAILED,
|
|
951
|
+
{
|
|
952
|
+
taskId: taskDataMock.interactionId,
|
|
953
|
+
destination: blindTransferPayload.to,
|
|
954
|
+
destinationType: blindTransferPayload.destinationType,
|
|
955
|
+
isConsultTransfer: false,
|
|
956
|
+
error: error.toString(),
|
|
957
|
+
...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details),
|
|
958
|
+
},
|
|
959
|
+
['operational', 'behavioral', 'business']
|
|
960
|
+
);
|
|
961
|
+
});
|
|
962
|
+
|
|
963
|
+
it('should end the task and return the expected response', async () => {
|
|
964
|
+
const expectedResponse: TaskResponse = {data: {interactionId: taskId}} as AgentContact;
|
|
965
|
+
contactMock.end.mockResolvedValue(expectedResponse);
|
|
966
|
+
|
|
967
|
+
const response = await task.end();
|
|
968
|
+
|
|
969
|
+
expect(contactMock.end).toHaveBeenCalledWith({interactionId: taskId});
|
|
970
|
+
expect(response).toEqual(expectedResponse);
|
|
971
|
+
expect(loggerInfoSpy).toHaveBeenCalledWith(`Ending task`, {
|
|
972
|
+
module: TASK_FILE,
|
|
973
|
+
method: 'end',
|
|
974
|
+
interactionId: expectedResponse.data.interactionId,
|
|
975
|
+
});
|
|
976
|
+
expect(loggerLogSpy).toHaveBeenCalledWith(`Task ended successfully`, {
|
|
977
|
+
module: TASK_FILE,
|
|
978
|
+
method: 'end',
|
|
979
|
+
interactionId: expectedResponse.data.interactionId,
|
|
980
|
+
});
|
|
981
|
+
expect(mockMetricsManager.trackEvent).toHaveBeenNthCalledWith(
|
|
982
|
+
1,
|
|
983
|
+
METRIC_EVENT_NAMES.TASK_END_SUCCESS,
|
|
984
|
+
{
|
|
985
|
+
taskId: taskDataMock.interactionId,
|
|
986
|
+
...MetricsManager.getCommonTrackingFieldForAQMResponse(expectedResponse),
|
|
987
|
+
},
|
|
988
|
+
['operational', 'behavioral', 'business']
|
|
989
|
+
);
|
|
990
|
+
});
|
|
991
|
+
|
|
992
|
+
it('should handle errors in end method', async () => {
|
|
993
|
+
const error = {
|
|
994
|
+
details: {
|
|
995
|
+
trackingId: '1234',
|
|
996
|
+
data: {
|
|
997
|
+
reason: 'End Failed',
|
|
998
|
+
},
|
|
999
|
+
},
|
|
1000
|
+
};
|
|
1001
|
+
contactMock.end.mockImplementation(() => {
|
|
1002
|
+
throw error;
|
|
1003
|
+
});
|
|
1004
|
+
|
|
1005
|
+
await expect(task.end()).rejects.toThrow(error.details.data.reason);
|
|
1006
|
+
expect(getErrorDetailsSpy).toHaveBeenCalledWith(error, 'end', TASK_FILE);
|
|
1007
|
+
expect(mockMetricsManager.trackEvent).toHaveBeenNthCalledWith(
|
|
1008
|
+
1,
|
|
1009
|
+
METRIC_EVENT_NAMES.TASK_END_FAILED,
|
|
1010
|
+
{
|
|
1011
|
+
taskId: taskDataMock.interactionId,
|
|
1012
|
+
...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details),
|
|
1013
|
+
},
|
|
1014
|
+
['operational', 'behavioral', 'business']
|
|
1015
|
+
);
|
|
1016
|
+
});
|
|
1017
|
+
|
|
1018
|
+
it('should wrap up the task and return the expected response', async () => {
|
|
1019
|
+
const expectedResponse: TaskResponse = {data: {interactionId: taskId}} as AgentContact;
|
|
1020
|
+
const wrapupPayload = {
|
|
1021
|
+
wrapUpReason: 'Customer request',
|
|
1022
|
+
auxCodeId: 'auxCodeId123',
|
|
1023
|
+
};
|
|
1024
|
+
contactMock.wrapup.mockResolvedValue(expectedResponse);
|
|
1025
|
+
|
|
1026
|
+
const response = await task.wrapup(wrapupPayload);
|
|
1027
|
+
|
|
1028
|
+
expect(contactMock.wrapup).toHaveBeenCalledWith({interactionId: taskId, data: wrapupPayload});
|
|
1029
|
+
expect(response).toEqual(expectedResponse);
|
|
1030
|
+
expect(mockMetricsManager.trackEvent).toHaveBeenNthCalledWith(
|
|
1031
|
+
1,
|
|
1032
|
+
METRIC_EVENT_NAMES.TASK_WRAPUP_SUCCESS,
|
|
1033
|
+
{
|
|
1034
|
+
taskId: taskDataMock.interactionId,
|
|
1035
|
+
wrapUpCode: wrapupPayload.auxCodeId,
|
|
1036
|
+
wrapUpReason: wrapupPayload.wrapUpReason,
|
|
1037
|
+
...MetricsManager.getCommonTrackingFieldForAQMResponse(expectedResponse),
|
|
1038
|
+
},
|
|
1039
|
+
['operational', 'behavioral', 'business']
|
|
1040
|
+
);
|
|
1041
|
+
});
|
|
1042
|
+
|
|
1043
|
+
it('should handle errors in wrapup method', async () => {
|
|
1044
|
+
const error = {
|
|
1045
|
+
details: {
|
|
1046
|
+
trackingId: '1234',
|
|
1047
|
+
data: {
|
|
1048
|
+
reason: 'Wrapup Failed',
|
|
1049
|
+
},
|
|
1050
|
+
},
|
|
1051
|
+
};
|
|
1052
|
+
contactMock.wrapup.mockImplementation(() => {
|
|
1053
|
+
throw error;
|
|
1054
|
+
});
|
|
1055
|
+
|
|
1056
|
+
const wrapupPayload = {
|
|
1057
|
+
wrapUpReason: 'Customer request',
|
|
1058
|
+
auxCodeId: 'auxCodeId123',
|
|
1059
|
+
};
|
|
1060
|
+
|
|
1061
|
+
await expect(task.wrapup(wrapupPayload)).rejects.toThrow(error.details.data.reason);
|
|
1062
|
+
expect(getErrorDetailsSpy).toHaveBeenCalledWith(error, 'wrapup', TASK_FILE);
|
|
1063
|
+
expect(mockMetricsManager.trackEvent).toHaveBeenNthCalledWith(
|
|
1064
|
+
1,
|
|
1065
|
+
METRIC_EVENT_NAMES.TASK_WRAPUP_FAILED,
|
|
1066
|
+
{
|
|
1067
|
+
taskId: taskDataMock.interactionId,
|
|
1068
|
+
wrapUpCode: wrapupPayload.auxCodeId,
|
|
1069
|
+
wrapUpReason: wrapupPayload.wrapUpReason,
|
|
1070
|
+
...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details),
|
|
1071
|
+
},
|
|
1072
|
+
['operational', 'behavioral', 'business']
|
|
1073
|
+
);
|
|
1074
|
+
});
|
|
1075
|
+
|
|
1076
|
+
it('should throw an error if auxCodeId is missing in wrapup method', async () => {
|
|
1077
|
+
const wrapupPayload = {
|
|
1078
|
+
wrapUpReason: 'Customer request',
|
|
1079
|
+
auxCodeId: '',
|
|
1080
|
+
};
|
|
1081
|
+
await expect(task.wrapup(wrapupPayload)).rejects.toThrow();
|
|
1082
|
+
});
|
|
1083
|
+
|
|
1084
|
+
it('should throw an error if wrapUpReason is missing in wrapup method', async () => {
|
|
1085
|
+
const wrapupPayload = {
|
|
1086
|
+
wrapUpReason: '',
|
|
1087
|
+
auxCodeId: 'auxCodeId123',
|
|
1088
|
+
};
|
|
1089
|
+
await expect(task.wrapup(wrapupPayload)).rejects.toThrow();
|
|
1090
|
+
});
|
|
1091
|
+
|
|
1092
|
+
it('should throw an error if this.data is missing when wrapup is invoked', async () => {
|
|
1093
|
+
const wrapupPayload = {
|
|
1094
|
+
wrapUpReason: 'Customer request',
|
|
1095
|
+
auxCodeId: 'auxCodeId123',
|
|
1096
|
+
};
|
|
1097
|
+
|
|
1098
|
+
task.data = undefined;
|
|
1099
|
+
await expect(task.wrapup(wrapupPayload)).rejects.toThrow();
|
|
1100
|
+
});
|
|
1101
|
+
|
|
1102
|
+
it('should pause the recording of the task', async () => {
|
|
1103
|
+
await task.pauseRecording();
|
|
1104
|
+
|
|
1105
|
+
expect(contactMock.pauseRecording).toHaveBeenCalledWith({interactionId: taskId});
|
|
1106
|
+
expect(loggerInfoSpy).toHaveBeenCalledWith(`Pausing recording`, {
|
|
1107
|
+
module: TASK_FILE,
|
|
1108
|
+
method: 'pauseRecording',
|
|
1109
|
+
interactionId: task.data.interactionId,
|
|
1110
|
+
});
|
|
1111
|
+
expect(loggerLogSpy).toHaveBeenCalledWith(`Recording paused successfully`, {
|
|
1112
|
+
module: TASK_FILE,
|
|
1113
|
+
method: 'pauseRecording',
|
|
1114
|
+
interactionId: task.data.interactionId,
|
|
1115
|
+
});
|
|
1116
|
+
expect(mockMetricsManager.trackEvent).toHaveBeenNthCalledWith(
|
|
1117
|
+
1,
|
|
1118
|
+
METRIC_EVENT_NAMES.TASK_PAUSE_RECORDING_SUCCESS,
|
|
1119
|
+
{
|
|
1120
|
+
taskId: taskDataMock.interactionId,
|
|
1121
|
+
},
|
|
1122
|
+
['operational', 'behavioral', 'business']
|
|
1123
|
+
);
|
|
1124
|
+
});
|
|
1125
|
+
|
|
1126
|
+
it('should handle errors in pauseRecording method', async () => {
|
|
1127
|
+
const error = {
|
|
1128
|
+
details: {
|
|
1129
|
+
trackingId: '1234',
|
|
1130
|
+
data: {
|
|
1131
|
+
reason: 'Pause Recording Failed',
|
|
1132
|
+
},
|
|
1133
|
+
},
|
|
1134
|
+
};
|
|
1135
|
+
contactMock.pauseRecording.mockImplementation(() => {
|
|
1136
|
+
throw error;
|
|
1137
|
+
});
|
|
1138
|
+
|
|
1139
|
+
await expect(task.pauseRecording()).rejects.toThrow(error.details.data.reason);
|
|
1140
|
+
expect(getErrorDetailsSpy).toHaveBeenCalledWith(error, 'pauseRecording', TASK_FILE);
|
|
1141
|
+
expect(mockMetricsManager.trackEvent).toHaveBeenNthCalledWith(
|
|
1142
|
+
1,
|
|
1143
|
+
METRIC_EVENT_NAMES.TASK_PAUSE_RECORDING_FAILED,
|
|
1144
|
+
{
|
|
1145
|
+
taskId: taskDataMock.interactionId,
|
|
1146
|
+
error: error.toString(),
|
|
1147
|
+
...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details),
|
|
1148
|
+
},
|
|
1149
|
+
['operational', 'behavioral', 'business']
|
|
1150
|
+
);
|
|
1151
|
+
});
|
|
1152
|
+
|
|
1153
|
+
it('should resume the recording of the task', async () => {
|
|
1154
|
+
const resumePayload = {
|
|
1155
|
+
autoResumed: true,
|
|
1156
|
+
interactionId: taskId,
|
|
1157
|
+
};
|
|
1158
|
+
|
|
1159
|
+
await task.resumeRecording(resumePayload);
|
|
1160
|
+
|
|
1161
|
+
expect(contactMock.resumeRecording).toHaveBeenCalledWith({
|
|
1162
|
+
interactionId: resumePayload.interactionId,
|
|
1163
|
+
data: resumePayload,
|
|
1164
|
+
});
|
|
1165
|
+
expect(loggerInfoSpy).toHaveBeenCalledWith(`Resuming recording`, {
|
|
1166
|
+
module: TASK_FILE,
|
|
1167
|
+
method: 'resumeRecording',
|
|
1168
|
+
interactionId: task.data.interactionId,
|
|
1169
|
+
});
|
|
1170
|
+
expect(loggerLogSpy).toHaveBeenCalledWith(`Recording resumed successfully`, {
|
|
1171
|
+
module: TASK_FILE,
|
|
1172
|
+
method: 'resumeRecording',
|
|
1173
|
+
interactionId: task.data.interactionId,
|
|
1174
|
+
});
|
|
1175
|
+
expect(mockMetricsManager.trackEvent).toHaveBeenNthCalledWith(
|
|
1176
|
+
1,
|
|
1177
|
+
METRIC_EVENT_NAMES.TASK_RESUME_RECORDING_SUCCESS,
|
|
1178
|
+
{
|
|
1179
|
+
taskId: taskDataMock.interactionId,
|
|
1180
|
+
},
|
|
1181
|
+
['operational', 'behavioral', 'business']
|
|
1182
|
+
);
|
|
1183
|
+
});
|
|
1184
|
+
|
|
1185
|
+
it('should resume the recording of the task if the payload is empty', async () => {
|
|
1186
|
+
const resumePayload = {
|
|
1187
|
+
autoResumed: false,
|
|
1188
|
+
};
|
|
1189
|
+
|
|
1190
|
+
await task.resumeRecording();
|
|
1191
|
+
|
|
1192
|
+
expect(contactMock.resumeRecording).toHaveBeenCalledWith({
|
|
1193
|
+
interactionId: taskId,
|
|
1194
|
+
data: resumePayload,
|
|
1195
|
+
});
|
|
1196
|
+
expect(mockMetricsManager.trackEvent).toHaveBeenNthCalledWith(
|
|
1197
|
+
1,
|
|
1198
|
+
METRIC_EVENT_NAMES.TASK_RESUME_RECORDING_SUCCESS,
|
|
1199
|
+
{
|
|
1200
|
+
taskId: taskDataMock.interactionId,
|
|
1201
|
+
},
|
|
1202
|
+
['operational', 'behavioral', 'business']
|
|
1203
|
+
);
|
|
1204
|
+
});
|
|
1205
|
+
|
|
1206
|
+
it('should handle errors in resumeRecording method', async () => {
|
|
1207
|
+
const error = {
|
|
1208
|
+
details: {
|
|
1209
|
+
trackingId: '1234',
|
|
1210
|
+
data: {
|
|
1211
|
+
reason: 'Resume Recording Failed',
|
|
1212
|
+
},
|
|
1213
|
+
},
|
|
1214
|
+
};
|
|
1215
|
+
contactMock.resumeRecording.mockImplementation(() => {
|
|
1216
|
+
throw error;
|
|
1217
|
+
});
|
|
1218
|
+
|
|
1219
|
+
const resumePayload = {
|
|
1220
|
+
autoResumed: true,
|
|
1221
|
+
};
|
|
1222
|
+
|
|
1223
|
+
await expect(task.resumeRecording(resumePayload)).rejects.toThrow(error.details.data.reason);
|
|
1224
|
+
expect(getErrorDetailsSpy).toHaveBeenCalledWith(error, 'resumeRecording', TASK_FILE);
|
|
1225
|
+
expect(mockMetricsManager.trackEvent).toHaveBeenNthCalledWith(
|
|
1226
|
+
1,
|
|
1227
|
+
METRIC_EVENT_NAMES.TASK_RESUME_RECORDING_FAILED,
|
|
1228
|
+
{
|
|
1229
|
+
taskId: taskDataMock.interactionId,
|
|
1230
|
+
error: error.toString(),
|
|
1231
|
+
...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details),
|
|
1232
|
+
},
|
|
1233
|
+
['operational', 'behavioral', 'business']
|
|
1234
|
+
);
|
|
1235
|
+
});
|
|
1236
|
+
|
|
1237
|
+
it('should mute call for Desktop login mode', async () => {
|
|
1238
|
+
task.localAudioStream = mockStream;
|
|
1239
|
+
const muteCallSpy = jest.spyOn(webCallingService, 'muteUnmuteCall');
|
|
1240
|
+
|
|
1241
|
+
await task.toggleMute();
|
|
1242
|
+
|
|
1243
|
+
expect(muteCallSpy).toHaveBeenCalledWith(mockStream);
|
|
1244
|
+
expect(loggerInfoSpy).toHaveBeenCalledWith(`Toggling mute state`, {
|
|
1245
|
+
module: TASK_FILE,
|
|
1246
|
+
method: 'toggleMute',
|
|
1247
|
+
interactionId: task.data.interactionId,
|
|
1248
|
+
});
|
|
1249
|
+
expect(loggerLogSpy).toHaveBeenCalledWith(`Mute state toggled successfully isCallMuted: ${webCallingService.isCallMuted()}`, {
|
|
1250
|
+
module: TASK_FILE,
|
|
1251
|
+
method: 'toggleMute',
|
|
1252
|
+
interactionId: task.data.interactionId,
|
|
1253
|
+
});
|
|
1254
|
+
});
|
|
1255
|
+
|
|
1256
|
+
it('should handle errors in mute method', async () => {
|
|
1257
|
+
const error = {
|
|
1258
|
+
details: {
|
|
1259
|
+
trackingId: '1234',
|
|
1260
|
+
data: {
|
|
1261
|
+
reason: 'Mute Failed',
|
|
1262
|
+
},
|
|
1263
|
+
},
|
|
1264
|
+
};
|
|
1265
|
+
|
|
1266
|
+
jest.spyOn(webCallingService, 'muteUnmuteCall').mockImplementation(() => {
|
|
1267
|
+
throw error;
|
|
1268
|
+
});
|
|
1269
|
+
await expect(task.toggleMute()).rejects.toThrow(new Error(error.details.data.reason));
|
|
1270
|
+
expect(getErrorDetailsSpy).toHaveBeenCalledWith(error, 'toggleMute', TASK_FILE);
|
|
1271
|
+
expect(loggerInfoSpy).toHaveBeenCalledWith(`Toggling mute state`, {
|
|
1272
|
+
module: TASK_FILE,
|
|
1273
|
+
method: 'toggleMute',
|
|
1274
|
+
interactionId: task.data.interactionId,
|
|
1275
|
+
});
|
|
1276
|
+
});
|
|
1277
|
+
|
|
1278
|
+
describe('AutoWrapup initialization tests', () => {
|
|
1279
|
+
beforeEach(() => {
|
|
1280
|
+
jest.useFakeTimers();
|
|
1281
|
+
});
|
|
1282
|
+
|
|
1283
|
+
afterEach(() => {
|
|
1284
|
+
jest.restoreAllMocks();
|
|
1285
|
+
jest.useRealTimers();
|
|
1286
|
+
});
|
|
1287
|
+
|
|
1288
|
+
it('should not initialize AutoWrapup if wrapUpRequired is false', () => {
|
|
1289
|
+
const wrapupProps = {
|
|
1290
|
+
wrapUpProps: {
|
|
1291
|
+
autoWrapup: true,
|
|
1292
|
+
autoWrapupInterval: 5000,
|
|
1293
|
+
wrapUpReasonList: [{ isDefault: true, name: 'Default Reason', id: '123', isSystem: false }]
|
|
1294
|
+
}
|
|
1295
|
+
};
|
|
1296
|
+
|
|
1297
|
+
const taskData = { ...taskDataMock, wrapUpRequired: false };
|
|
1298
|
+
const taskInstance = new Task(contactMock, webCallingService, taskData, wrapupProps);
|
|
1299
|
+
|
|
1300
|
+
expect(taskInstance.autoWrapup).toBeUndefined();
|
|
1301
|
+
});
|
|
1302
|
+
|
|
1303
|
+
it('should not initialize AutoWrapup if autoWrapup is set to false', () => {
|
|
1304
|
+
const wrapupProps = {
|
|
1305
|
+
wrapUpProps: {
|
|
1306
|
+
autoWrapup: false,
|
|
1307
|
+
autoWrapupInterval: 5000,
|
|
1308
|
+
wrapUpReasonList: [{ isDefault: true, name: 'Default Reason', id: '123', isSystem: false }]
|
|
1309
|
+
}
|
|
1310
|
+
};
|
|
1311
|
+
|
|
1312
|
+
const taskData = { ...taskDataMock, wrapUpRequired: true };
|
|
1313
|
+
const taskInstance = new Task(contactMock, webCallingService, taskData, wrapupProps);
|
|
1314
|
+
|
|
1315
|
+
expect(taskInstance.autoWrapup).toBeUndefined();
|
|
1316
|
+
expect(loggerInfoSpy).toHaveBeenCalledWith('Auto wrap-up is not required for this task', {
|
|
1317
|
+
module: TASK_FILE,
|
|
1318
|
+
method: 'setupAutoWrapupTimer',
|
|
1319
|
+
interactionId: taskData.interactionId,
|
|
1320
|
+
});
|
|
1321
|
+
});
|
|
1322
|
+
|
|
1323
|
+
it('should initialize AutoWrapup with custom interval when specified', () => {
|
|
1324
|
+
const customInterval = 15000;
|
|
1325
|
+
const wrapupProps = {
|
|
1326
|
+
wrapUpProps: {
|
|
1327
|
+
autoWrapup: true,
|
|
1328
|
+
autoWrapupInterval: customInterval,
|
|
1329
|
+
wrapUpReasonList: [{ isDefault: true, name: 'Default Reason', id: '123', isSystem: false }]
|
|
1330
|
+
}
|
|
1331
|
+
};
|
|
1332
|
+
|
|
1333
|
+
const taskData = { ...taskDataMock, wrapUpRequired: true };
|
|
1334
|
+
const taskInstance = new Task(contactMock, webCallingService, taskData, wrapupProps);
|
|
1335
|
+
|
|
1336
|
+
expect(taskInstance.autoWrapup).toBeDefined();
|
|
1337
|
+
});
|
|
1338
|
+
|
|
1339
|
+
it('should cancel AutoWrapup timer when wrapup is called', async () => {
|
|
1340
|
+
const wrapupProps = {
|
|
1341
|
+
wrapUpProps: {
|
|
1342
|
+
autoWrapup: true,
|
|
1343
|
+
autoWrapupInterval: 5000,
|
|
1344
|
+
wrapUpReasonList: [{ isDefault: true, name: 'Default Reason', id: '123', isSystem: false }]
|
|
1345
|
+
}
|
|
1346
|
+
};
|
|
1347
|
+
|
|
1348
|
+
const taskData = { ...taskDataMock, wrapUpRequired: true };
|
|
1349
|
+
const taskInstance = new Task(contactMock, webCallingService, taskData, wrapupProps);
|
|
1350
|
+
|
|
1351
|
+
// Mock the autoWrapup object and its clear method
|
|
1352
|
+
const clearSpy = jest.spyOn(taskInstance.autoWrapup, 'clear');
|
|
1353
|
+
|
|
1354
|
+
// Call wrapup method which should cancel the timer
|
|
1355
|
+
await taskInstance.wrapup({ wrapUpReason: 'Test Reason', auxCodeId: '123' });
|
|
1356
|
+
|
|
1357
|
+
// Verify that clear was called
|
|
1358
|
+
expect(clearSpy).toHaveBeenCalled();
|
|
1359
|
+
expect(loggerInfoSpy).toHaveBeenCalledWith('Auto wrap-up timer cancelled', {
|
|
1360
|
+
module: TASK_FILE,
|
|
1361
|
+
method: 'cancelAutoWrapupTimer',
|
|
1362
|
+
interactionId: taskData.interactionId,
|
|
1363
|
+
});
|
|
1364
|
+
});
|
|
1365
|
+
|
|
1366
|
+
it('should directly call cancelAutoWrapUpTimer successfully', () => {
|
|
1367
|
+
const wrapupProps = {
|
|
1368
|
+
wrapUpProps: {
|
|
1369
|
+
autoWrapup: true,
|
|
1370
|
+
autoWrapupInterval: 5000,
|
|
1371
|
+
wrapUpReasonList: [{ isDefault: true, name: 'Default Reason', id: '123', isSystem: false }]
|
|
1372
|
+
}
|
|
1373
|
+
};
|
|
1374
|
+
|
|
1375
|
+
const taskData = { ...taskDataMock, wrapUpRequired: true };
|
|
1376
|
+
const taskInstance = new Task(contactMock, webCallingService, taskData, wrapupProps);
|
|
1377
|
+
|
|
1378
|
+
const clearSpy = jest.spyOn(taskInstance.autoWrapup, 'clear');
|
|
1379
|
+
taskInstance.cancelAutoWrapupTimer();
|
|
1380
|
+
|
|
1381
|
+
expect(clearSpy).toHaveBeenCalled();
|
|
1382
|
+
expect(loggerInfoSpy).toHaveBeenCalledWith('Auto wrap-up timer cancelled', {
|
|
1383
|
+
module: TASK_FILE,
|
|
1384
|
+
method: 'cancelAutoWrapupTimer',
|
|
1385
|
+
interactionId: taskData.interactionId,
|
|
1386
|
+
});
|
|
1387
|
+
});
|
|
1388
|
+
|
|
1389
|
+
it('should use default interval when autoWrapupInterval is not specified', () => {
|
|
1390
|
+
const wrapupProps = {
|
|
1391
|
+
wrapUpProps: {
|
|
1392
|
+
autoWrapup: true,
|
|
1393
|
+
wrapUpReasonList: [{ isDefault: true, name: 'Default Reason', id: '123', isSystem: false }]
|
|
1394
|
+
}
|
|
1395
|
+
};
|
|
1396
|
+
|
|
1397
|
+
const taskData = { ...taskDataMock, wrapUpRequired: true };
|
|
1398
|
+
const taskInstance = new Task(contactMock, webCallingService, taskData, wrapupProps);
|
|
1399
|
+
|
|
1400
|
+
expect(taskInstance.autoWrapup).toBeDefined();
|
|
1401
|
+
});
|
|
1402
|
+
|
|
1403
|
+
it('should setup autoWrapup with a callback that executes wrapup', () => {
|
|
1404
|
+
// Create a task with AutoWrapup enabled and a default wrapup reason
|
|
1405
|
+
const defaultWrapUpReason = { isDefault: true, name: 'Default Reason', id: '123', isSystem: false };
|
|
1406
|
+
const wrapupProps = {
|
|
1407
|
+
wrapUpProps: {
|
|
1408
|
+
autoWrapup: true,
|
|
1409
|
+
autoWrapupInterval: 5000,
|
|
1410
|
+
wrapUpReasonList: [defaultWrapUpReason]
|
|
1411
|
+
}
|
|
1412
|
+
};
|
|
1413
|
+
|
|
1414
|
+
const taskData = { ...taskDataMock, wrapUpRequired: true };
|
|
1415
|
+
|
|
1416
|
+
let capturedCallback;
|
|
1417
|
+
jest.spyOn(global, 'setTimeout').mockImplementation((callback, timeout) => {
|
|
1418
|
+
capturedCallback = callback;
|
|
1419
|
+
return {} as any;
|
|
1420
|
+
});
|
|
1421
|
+
|
|
1422
|
+
// Create our task instance
|
|
1423
|
+
const taskInstance = new Task(contactMock, webCallingService, taskData, wrapupProps);
|
|
1424
|
+
|
|
1425
|
+
// Mock the wrapup method to verify it gets called with correct parameters
|
|
1426
|
+
const wrapupMock = jest.fn().mockResolvedValue({});
|
|
1427
|
+
taskInstance.wrapup = wrapupMock;
|
|
1428
|
+
|
|
1429
|
+
// Verify autoWrapup was initialized
|
|
1430
|
+
expect(taskInstance.autoWrapup).toBeDefined();
|
|
1431
|
+
|
|
1432
|
+
if (capturedCallback) {
|
|
1433
|
+
capturedCallback();
|
|
1434
|
+
}
|
|
1435
|
+
|
|
1436
|
+
// Verify wrapup was called with correct parameters
|
|
1437
|
+
expect(wrapupMock).toHaveBeenCalledWith({
|
|
1438
|
+
wrapUpReason: defaultWrapUpReason.name,
|
|
1439
|
+
auxCodeId: defaultWrapUpReason.id
|
|
1440
|
+
});
|
|
1441
|
+
});
|
|
1442
|
+
|
|
1443
|
+
it('should handle case when no default wrapup reason is found', () => {
|
|
1444
|
+
// Create a task with AutoWrapup enabled but NO default wrapup reason
|
|
1445
|
+
const wrapupProps = {
|
|
1446
|
+
wrapUpProps: {
|
|
1447
|
+
autoWrapup: true,
|
|
1448
|
+
autoWrapupInterval: 5000,
|
|
1449
|
+
wrapUpReasonList: [
|
|
1450
|
+
{ isDefault: false, name: 'Reason 1', id: '123', isSystem: false },
|
|
1451
|
+
{ isDefault: false, name: 'Reason 2', id: '456', isSystem: false }
|
|
1452
|
+
]
|
|
1453
|
+
}
|
|
1454
|
+
};
|
|
1455
|
+
|
|
1456
|
+
const taskData = { ...taskDataMock, wrapUpRequired: true };
|
|
1457
|
+
|
|
1458
|
+
// Create our task instance
|
|
1459
|
+
const taskInstance = new Task(contactMock, webCallingService, taskData, wrapupProps);
|
|
1460
|
+
|
|
1461
|
+
// Mock the wrapup method to verify if it gets called
|
|
1462
|
+
const wrapupSpy = jest.fn().mockResolvedValue({});
|
|
1463
|
+
taskInstance.wrapup = wrapupSpy;
|
|
1464
|
+
|
|
1465
|
+
jest.runOnlyPendingTimers();
|
|
1466
|
+
|
|
1467
|
+
// Verify wrapup was called with the first reason (since no default exists)
|
|
1468
|
+
expect(wrapupSpy).toHaveBeenCalledWith({
|
|
1469
|
+
wrapUpReason: wrapupProps.wrapUpProps.wrapUpReasonList[0].name,
|
|
1470
|
+
auxCodeId: wrapupProps.wrapUpProps.wrapUpReasonList[0].id
|
|
1471
|
+
});
|
|
1472
|
+
});
|
|
1473
|
+
});
|
|
1474
|
+
});
|