@webex/contact-center 3.12.0-next.8 → 3.12.0-task-refactor.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/AGENTS.md +438 -0
- package/ai-docs/README.md +131 -0
- package/ai-docs/RULES.md +455 -0
- package/ai-docs/patterns/event-driven-patterns.md +485 -0
- package/ai-docs/patterns/testing-patterns.md +480 -0
- package/ai-docs/patterns/typescript-patterns.md +365 -0
- package/ai-docs/templates/README.md +102 -0
- package/ai-docs/templates/documentation/create-agents-md.md +240 -0
- package/ai-docs/templates/documentation/create-architecture-md.md +295 -0
- package/ai-docs/templates/existing-service/bug-fix.md +254 -0
- package/ai-docs/templates/existing-service/feature-enhancement.md +450 -0
- package/ai-docs/templates/new-method/00-master.md +80 -0
- package/ai-docs/templates/new-method/01-requirements.md +232 -0
- package/ai-docs/templates/new-method/02-implementation.md +295 -0
- package/ai-docs/templates/new-method/03-tests.md +201 -0
- package/ai-docs/templates/new-method/04-validation.md +141 -0
- package/ai-docs/templates/new-service/00-master.md +109 -0
- package/ai-docs/templates/new-service/01-pre-questions.md +159 -0
- package/ai-docs/templates/new-service/02-code-generation.md +346 -0
- package/ai-docs/templates/new-service/03-integration.md +178 -0
- package/ai-docs/templates/new-service/04-test-generation.md +205 -0
- package/ai-docs/templates/new-service/05-validation.md +145 -0
- package/dist/cc.js +65 -123
- package/dist/cc.js.map +1 -1
- package/dist/constants.js +13 -2
- package/dist/constants.js.map +1 -1
- package/dist/index.js +13 -5
- package/dist/index.js.map +1 -1
- package/dist/metrics/behavioral-events.js +26 -13
- package/dist/metrics/behavioral-events.js.map +1 -1
- package/dist/metrics/constants.js +7 -6
- package/dist/metrics/constants.js.map +1 -1
- package/dist/services/ApiAiAssistant.js +0 -3
- package/dist/services/ApiAiAssistant.js.map +1 -1
- package/dist/services/config/Util.js +2 -3
- package/dist/services/config/Util.js.map +1 -1
- package/dist/services/config/types.js +16 -14
- package/dist/services/config/types.js.map +1 -1
- package/dist/services/constants.js +0 -1
- package/dist/services/constants.js.map +1 -1
- package/dist/services/core/Err.js.map +1 -1
- package/dist/services/core/Utils.js +79 -55
- package/dist/services/core/Utils.js.map +1 -1
- package/dist/services/core/aqm-reqs.js +17 -92
- package/dist/services/core/aqm-reqs.js.map +1 -1
- package/dist/services/core/websocket/WebSocketManager.js +5 -25
- package/dist/services/core/websocket/WebSocketManager.js.map +1 -1
- package/dist/services/core/websocket/types.js.map +1 -1
- package/dist/services/index.js +1 -2
- package/dist/services/index.js.map +1 -1
- package/dist/services/task/Task.js +644 -0
- package/dist/services/task/Task.js.map +1 -0
- package/dist/services/task/TaskFactory.js +45 -0
- package/dist/services/task/TaskFactory.js.map +1 -0
- package/dist/services/task/TaskManager.js +556 -532
- package/dist/services/task/TaskManager.js.map +1 -1
- package/dist/services/task/TaskUtils.js +132 -28
- package/dist/services/task/TaskUtils.js.map +1 -1
- package/dist/services/task/constants.js +7 -6
- package/dist/services/task/constants.js.map +1 -1
- package/dist/services/task/dialer.js +0 -51
- package/dist/services/task/dialer.js.map +1 -1
- package/dist/services/task/digital/Digital.js +77 -0
- package/dist/services/task/digital/Digital.js.map +1 -0
- package/dist/services/task/state-machine/TaskStateMachine.js +634 -0
- package/dist/services/task/state-machine/TaskStateMachine.js.map +1 -0
- package/dist/services/task/state-machine/actions.js +366 -0
- package/dist/services/task/state-machine/actions.js.map +1 -0
- package/dist/services/task/state-machine/constants.js +139 -0
- package/dist/services/task/state-machine/constants.js.map +1 -0
- package/dist/services/task/state-machine/guards.js +256 -0
- package/dist/services/task/state-machine/guards.js.map +1 -0
- package/dist/services/task/state-machine/index.js +53 -0
- package/dist/services/task/state-machine/index.js.map +1 -0
- package/dist/services/task/state-machine/types.js +54 -0
- package/dist/services/task/state-machine/types.js.map +1 -0
- package/dist/services/task/state-machine/uiControlsComputer.js +369 -0
- package/dist/services/task/state-machine/uiControlsComputer.js.map +1 -0
- package/dist/services/task/taskDataNormalizer.js +99 -0
- package/dist/services/task/taskDataNormalizer.js.map +1 -0
- package/dist/services/task/types.js +157 -18
- package/dist/services/task/types.js.map +1 -1
- package/dist/services/task/voice/Voice.js +1031 -0
- package/dist/services/task/voice/Voice.js.map +1 -0
- package/dist/services/task/voice/WebRTC.js +149 -0
- package/dist/services/task/voice/WebRTC.js.map +1 -0
- package/dist/types/cc.d.ts +4 -33
- package/dist/types/constants.d.ts +13 -2
- package/dist/types/index.d.ts +11 -5
- package/dist/types/metrics/constants.d.ts +5 -3
- package/dist/types/services/ApiAiAssistant.d.ts +1 -1
- package/dist/types/services/config/types.d.ts +97 -25
- package/dist/types/services/core/Err.d.ts +0 -2
- package/dist/types/services/core/Utils.d.ts +25 -23
- package/dist/types/services/core/aqm-reqs.d.ts +0 -49
- package/dist/types/services/core/websocket/WebSocketManager.d.ts +1 -1
- package/dist/types/services/core/websocket/connection-service.d.ts +0 -1
- package/dist/types/services/core/websocket/types.d.ts +1 -1
- package/dist/types/services/index.d.ts +1 -1
- package/dist/types/services/task/Task.d.ts +146 -0
- package/dist/types/services/task/TaskFactory.d.ts +12 -0
- package/dist/types/services/task/TaskUtils.d.ts +39 -8
- package/dist/types/services/task/constants.d.ts +5 -4
- package/dist/types/services/task/dialer.d.ts +0 -15
- package/dist/types/services/task/digital/Digital.d.ts +22 -0
- package/dist/types/services/task/state-machine/TaskStateMachine.d.ts +906 -0
- package/dist/types/services/task/state-machine/actions.d.ts +8 -0
- package/dist/types/services/task/state-machine/constants.d.ts +91 -0
- package/dist/types/services/task/state-machine/guards.d.ts +78 -0
- package/dist/types/services/task/state-machine/index.d.ts +13 -0
- package/dist/types/services/task/state-machine/types.d.ts +256 -0
- package/dist/types/services/task/state-machine/uiControlsComputer.d.ts +9 -0
- package/dist/types/services/task/taskDataNormalizer.d.ts +10 -0
- package/dist/types/services/task/types.d.ts +539 -88
- package/dist/types/services/task/voice/Voice.d.ts +183 -0
- package/dist/types/services/task/voice/WebRTC.d.ts +53 -0
- package/dist/types/types.d.ts +68 -0
- package/dist/types/webex.d.ts +1 -0
- package/dist/types.js +70 -0
- package/dist/types.js.map +1 -1
- package/dist/webex.js +14 -2
- package/dist/webex.js.map +1 -1
- package/package.json +14 -11
- package/src/cc.ts +91 -177
- package/src/constants.ts +13 -2
- package/src/index.ts +14 -5
- package/src/metrics/ai-docs/AGENTS.md +348 -0
- package/src/metrics/ai-docs/ARCHITECTURE.md +336 -0
- package/src/metrics/behavioral-events.ts +28 -14
- package/src/metrics/constants.ts +7 -8
- package/src/services/ApiAiAssistant.ts +2 -4
- package/src/services/agent/ai-docs/AGENTS.md +238 -0
- package/src/services/agent/ai-docs/ARCHITECTURE.md +302 -0
- package/src/services/ai-docs/AGENTS.md +384 -0
- package/src/services/config/Util.ts +2 -3
- package/src/services/config/ai-docs/AGENTS.md +253 -0
- package/src/services/config/ai-docs/ARCHITECTURE.md +424 -0
- package/src/services/config/types.ts +108 -20
- package/src/services/constants.ts +0 -1
- package/src/services/core/Err.ts +0 -1
- package/src/services/core/Utils.ts +90 -67
- package/src/services/core/ai-docs/AGENTS.md +379 -0
- package/src/services/core/ai-docs/ARCHITECTURE.md +696 -0
- package/src/services/core/aqm-reqs.ts +22 -100
- package/src/services/core/websocket/WebSocketManager.ts +4 -23
- package/src/services/core/websocket/types.ts +1 -1
- package/src/services/index.ts +1 -2
- package/src/services/task/Task.ts +785 -0
- package/src/services/task/TaskFactory.ts +55 -0
- package/src/services/task/TaskManager.ts +567 -633
- package/src/services/task/TaskUtils.ts +175 -31
- package/src/services/task/ai-docs/AGENTS.md +448 -0
- package/src/services/task/ai-docs/ARCHITECTURE.md +573 -0
- package/src/services/task/constants.ts +5 -4
- package/src/services/task/dialer.ts +1 -56
- package/src/services/task/digital/Digital.ts +95 -0
- package/src/services/task/state-machine/TaskStateMachine.ts +793 -0
- package/src/services/task/state-machine/actions.ts +409 -0
- package/src/services/task/state-machine/ai-docs/AGENTS.md +495 -0
- package/src/services/task/state-machine/ai-docs/ARCHITECTURE.md +1135 -0
- package/src/services/task/state-machine/constants.ts +150 -0
- package/src/services/task/state-machine/guards.ts +295 -0
- package/src/services/task/state-machine/index.ts +28 -0
- package/src/services/task/state-machine/types.ts +228 -0
- package/src/services/task/state-machine/uiControlsComputer.ts +529 -0
- package/src/services/task/taskDataNormalizer.ts +137 -0
- package/src/services/task/types.ts +641 -95
- package/src/services/task/voice/Voice.ts +1255 -0
- package/src/services/task/voice/WebRTC.ts +187 -0
- package/src/types.ts +88 -5
- package/src/utils/AGENTS.md +276 -0
- package/src/webex.js +2 -0
- package/test/unit/spec/cc.ts +59 -142
- package/test/unit/spec/logger-proxy.ts +70 -0
- package/test/unit/spec/services/ApiAiAssistant.ts +17 -0
- package/test/unit/spec/services/config/index.ts +26 -55
- package/test/unit/spec/services/core/Utils.ts +103 -52
- package/test/unit/spec/services/core/websocket/WebSocketManager.ts +48 -112
- package/test/unit/spec/services/core/websocket/connection-service.ts +5 -4
- package/test/unit/spec/services/task/AutoWrapup.ts +63 -0
- package/test/unit/spec/services/task/Task.ts +416 -0
- package/test/unit/spec/services/task/TaskFactory.ts +62 -0
- package/test/unit/spec/services/task/TaskManager.ts +781 -1735
- package/test/unit/spec/services/task/TaskUtils.ts +125 -0
- package/test/unit/spec/services/task/dialer.ts +112 -198
- package/test/unit/spec/services/task/digital/Digital.ts +105 -0
- package/test/unit/spec/services/task/state-machine/TaskStateMachine.ts +473 -0
- package/test/unit/spec/services/task/state-machine/guards.ts +288 -0
- package/test/unit/spec/services/task/state-machine/types.ts +18 -0
- package/test/unit/spec/services/task/state-machine/uiControlsComputer.ts +147 -0
- package/test/unit/spec/services/task/taskTestUtils.ts +87 -0
- package/test/unit/spec/services/task/voice/Voice.ts +587 -0
- package/test/unit/spec/services/task/voice/WebRTC.ts +242 -0
- package/umd/contact-center.min.js +2 -2
- package/umd/contact-center.min.js.map +1 -1
- package/dist/services/task/index.js +0 -1525
- package/dist/services/task/index.js.map +0 -1
- package/dist/types/services/task/index.d.ts +0 -650
- package/src/services/task/index.ts +0 -1801
- package/test/unit/spec/services/task/index.ts +0 -2184
|
@@ -1,2184 +0,0 @@
|
|
|
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 generateTaskErrorObjectSpy;
|
|
37
|
-
let mockWebexRequest;
|
|
38
|
-
let webex: WebexSDK;
|
|
39
|
-
let loggerInfoSpy;
|
|
40
|
-
let loggerLogSpy;
|
|
41
|
-
let loggerErrorSpy;
|
|
42
|
-
let calculateDestAgentIdSpy;
|
|
43
|
-
let calculateDestTypeSpy;
|
|
44
|
-
|
|
45
|
-
const taskId = '0ae913a4-c857-4705-8d49-76dd3dde75e4';
|
|
46
|
-
const mockTrack = {} as MediaStreamTrack;
|
|
47
|
-
const mockStream = {
|
|
48
|
-
outputStream: {
|
|
49
|
-
getAudioTracks: jest.fn().mockReturnValue([mockTrack]),
|
|
50
|
-
},
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
beforeEach(() => {
|
|
54
|
-
webex = {
|
|
55
|
-
logger: {
|
|
56
|
-
log: jest.fn(),
|
|
57
|
-
error: jest.fn(),
|
|
58
|
-
info: jest.fn(),
|
|
59
|
-
},
|
|
60
|
-
} as unknown as WebexSDK;
|
|
61
|
-
|
|
62
|
-
loggerInfoSpy = jest.spyOn(LoggerProxy, 'info');
|
|
63
|
-
loggerLogSpy = jest.spyOn(LoggerProxy, 'log');
|
|
64
|
-
loggerErrorSpy = jest.spyOn(LoggerProxy, 'error');
|
|
65
|
-
|
|
66
|
-
contactMock = {
|
|
67
|
-
accept: jest.fn().mockResolvedValue({}),
|
|
68
|
-
hold: jest.fn().mockResolvedValue({}),
|
|
69
|
-
unHold: jest.fn().mockResolvedValue({}),
|
|
70
|
-
consult: jest.fn().mockResolvedValue({}),
|
|
71
|
-
consultEnd: jest.fn().mockResolvedValue({}),
|
|
72
|
-
blindTransfer: jest.fn().mockResolvedValue({}),
|
|
73
|
-
vteamTransfer: jest.fn().mockResolvedValue({}),
|
|
74
|
-
consultTransfer: jest.fn().mockResolvedValue({}),
|
|
75
|
-
end: jest.fn().mockResolvedValue({}),
|
|
76
|
-
wrapup: jest.fn().mockResolvedValue({}),
|
|
77
|
-
pauseRecording: jest.fn().mockResolvedValue({}),
|
|
78
|
-
resumeRecording: jest.fn().mockResolvedValue({}),
|
|
79
|
-
consultConference: jest.fn().mockResolvedValue({}),
|
|
80
|
-
exitConference: jest.fn().mockResolvedValue({}),
|
|
81
|
-
conferenceTransfer: jest.fn().mockResolvedValue({}),
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
mockMetricsManager = {
|
|
85
|
-
trackEvent: jest.fn(),
|
|
86
|
-
timeEvent: jest.fn(),
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
jest.spyOn(MetricsManager, 'getInstance').mockReturnValue(mockMetricsManager);
|
|
90
|
-
|
|
91
|
-
webCallingService = new WebCallingService(
|
|
92
|
-
webex,
|
|
93
|
-
config.cc.callingClientConfig as CallingClientConfig
|
|
94
|
-
);
|
|
95
|
-
|
|
96
|
-
mockWebexRequest = {
|
|
97
|
-
request: jest.fn(),
|
|
98
|
-
uploadLogs: jest.fn(),
|
|
99
|
-
};
|
|
100
|
-
|
|
101
|
-
jest.spyOn(WebexRequest, 'getInstance').mockReturnValue(mockWebexRequest);
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
webCallingService.loginOption = LoginOption.BROWSER;
|
|
105
|
-
onSpy = jest.spyOn(webCallingService, 'on');
|
|
106
|
-
|
|
107
|
-
// Mock task data
|
|
108
|
-
taskDataMock = {
|
|
109
|
-
type: CC_EVENTS.AGENT_CONTACT_RESERVED,
|
|
110
|
-
agentId: '723a8ffb-a26e-496d-b14a-ff44fb83b64f',
|
|
111
|
-
eventTime: 1733211616959,
|
|
112
|
-
eventType: 'RoutingMessage',
|
|
113
|
-
interactionId: taskId,
|
|
114
|
-
orgId: '6ecef209-9a34-4ed1-a07a-7ddd1dbe925a',
|
|
115
|
-
trackingId: '575c0ec2-618c-42af-a61c-53aeb0a221ee',
|
|
116
|
-
mediaResourceId: '0ae913a4-c857-4705-8d49-76dd3dde75e4',
|
|
117
|
-
destAgentId: 'ebeb893b-ba67-4f36-8418-95c7492b28c2',
|
|
118
|
-
owner: '723a8ffb-a26e-496d-b14a-ff44fb83b64f',
|
|
119
|
-
queueMgr: 'aqm',
|
|
120
|
-
interaction: {
|
|
121
|
-
mediaType: 'telephony',
|
|
122
|
-
mainInteractionId: taskId,
|
|
123
|
-
participants: {
|
|
124
|
-
'723a8ffb-a26e-496d-b14a-ff44fb83b64f': {
|
|
125
|
-
pType: 'Agent',
|
|
126
|
-
type: 'AGENT',
|
|
127
|
-
id: '723a8ffb-a26e-496d-b14a-ff44fb83b64f',
|
|
128
|
-
hasLeft: false,
|
|
129
|
-
hasJoined: true,
|
|
130
|
-
isWrapUp: false,
|
|
131
|
-
},
|
|
132
|
-
'f520d6b5-28ad-4f2f-b83e-781bb64af617': {
|
|
133
|
-
pType: 'Agent',
|
|
134
|
-
type: 'AGENT',
|
|
135
|
-
id: 'f520d6b5-28ad-4f2f-b83e-781bb64af617',
|
|
136
|
-
hasLeft: false,
|
|
137
|
-
hasJoined: true,
|
|
138
|
-
isWrapUp: false,
|
|
139
|
-
},
|
|
140
|
-
'ebeb893b-ba67-4f36-8418-95c7492b28c2': {
|
|
141
|
-
pType: 'Agent',
|
|
142
|
-
type: 'AGENT',
|
|
143
|
-
id: 'ebeb893b-ba67-4f36-8418-95c7492b28c2',
|
|
144
|
-
hasLeft: false,
|
|
145
|
-
hasJoined: true,
|
|
146
|
-
isWrapUp: false,
|
|
147
|
-
},
|
|
148
|
-
},
|
|
149
|
-
media: {
|
|
150
|
-
'58a45567-4e61-4f4b-a580-5bc86357bef0': {
|
|
151
|
-
holdTimestamp: null,
|
|
152
|
-
isHold: false,
|
|
153
|
-
mType: 'consult',
|
|
154
|
-
mediaMgr: 'callmm',
|
|
155
|
-
mediaResourceId: '58a45567-4e61-4f4b-a580-5bc86357bef0',
|
|
156
|
-
mediaType: 'telephony',
|
|
157
|
-
participants: [
|
|
158
|
-
'f520d6b5-28ad-4f2f-b83e-781bb64af617',
|
|
159
|
-
'723a8ffb-a26e-496d-b14a-ff44fb83b64f',
|
|
160
|
-
],
|
|
161
|
-
},
|
|
162
|
-
[taskId]: {
|
|
163
|
-
holdTimestamp: 1734667567279,
|
|
164
|
-
isHold: true,
|
|
165
|
-
mType: 'mainCall',
|
|
166
|
-
mediaMgr: 'callmm',
|
|
167
|
-
mediaResourceId: taskId,
|
|
168
|
-
mediaType: 'telephony',
|
|
169
|
-
participants: ['+14696762938', '723a8ffb-a26e-496d-b14a-ff44fb83b64f'],
|
|
170
|
-
},
|
|
171
|
-
},
|
|
172
|
-
},
|
|
173
|
-
};
|
|
174
|
-
|
|
175
|
-
// Mock calculateDestAgentId to return the expected destination agent
|
|
176
|
-
calculateDestAgentIdSpy = jest.spyOn(Utils, 'calculateDestAgentId').mockReturnValue(taskDataMock.destAgentId);
|
|
177
|
-
|
|
178
|
-
// Mock calculateDestType to return 'agent' by default
|
|
179
|
-
calculateDestTypeSpy = jest.spyOn(Utils, 'calculateDestType').mockReturnValue('agent');
|
|
180
|
-
|
|
181
|
-
// Create an instance of Task with wrapupData and agentId
|
|
182
|
-
task = new Task(contactMock, webCallingService, taskDataMock, {
|
|
183
|
-
wrapUpProps: { wrapUpReasonList: [] },
|
|
184
|
-
autoWrapEnabled: false,
|
|
185
|
-
autoWrapAfterSeconds: 0
|
|
186
|
-
}, taskDataMock.agentId);
|
|
187
|
-
|
|
188
|
-
// Mock navigator.mediaDevices
|
|
189
|
-
global.navigator.mediaDevices = {
|
|
190
|
-
getUserMedia: jest.fn(() =>
|
|
191
|
-
Promise.resolve({
|
|
192
|
-
getAudioTracks: jest.fn().mockReturnValue([mockTrack]),
|
|
193
|
-
})
|
|
194
|
-
),
|
|
195
|
-
};
|
|
196
|
-
|
|
197
|
-
// Mock MediaStream (if needed)
|
|
198
|
-
global.MediaStream = jest.fn().mockImplementation((tracks) => {
|
|
199
|
-
return mockStream;
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
generateTaskErrorObjectSpy = jest.spyOn(Utils, 'generateTaskErrorObject');
|
|
203
|
-
generateTaskErrorObjectSpy.mockImplementation((error: any, methodName: string) => {
|
|
204
|
-
const trackingId = error?.details?.trackingId;
|
|
205
|
-
const msg = error?.details?.msg;
|
|
206
|
-
const legacyReason = error?.details?.data?.reason;
|
|
207
|
-
const errorMessage = msg?.errorMessage || legacyReason || `Error while performing ${methodName}`;
|
|
208
|
-
const errorType = msg?.errorType || '';
|
|
209
|
-
const errorData = msg?.errorData || '';
|
|
210
|
-
const reasonCode = msg?.reasonCode || 0;
|
|
211
|
-
const reason = legacyReason || (errorType ? `${errorType}: ${errorMessage}` : errorMessage);
|
|
212
|
-
const err: any = new Error(reason);
|
|
213
|
-
err.data = {
|
|
214
|
-
trackingId,
|
|
215
|
-
message: errorMessage,
|
|
216
|
-
errorType,
|
|
217
|
-
errorData,
|
|
218
|
-
reasonCode,
|
|
219
|
-
};
|
|
220
|
-
return err;
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
(global as any).makeFailure = (reason: string, trackingId = '1234', orgId = 'org1') => ({
|
|
224
|
-
type: 'failure_event',
|
|
225
|
-
orgId,
|
|
226
|
-
trackingId,
|
|
227
|
-
data: {
|
|
228
|
-
agentId: 'agent1',
|
|
229
|
-
reason,
|
|
230
|
-
reasonCode: 0,
|
|
231
|
-
},
|
|
232
|
-
});
|
|
233
|
-
});
|
|
234
|
-
|
|
235
|
-
afterEach(() => {
|
|
236
|
-
jest.clearAllMocks();
|
|
237
|
-
jest.restoreAllMocks();
|
|
238
|
-
});
|
|
239
|
-
|
|
240
|
-
it('test the on spy', async () => {
|
|
241
|
-
const taskEmitSpy = jest.spyOn(task, 'emit');
|
|
242
|
-
const remoteMediaCb = onSpy.mock.calls[0][1];
|
|
243
|
-
|
|
244
|
-
expect(onSpy).toHaveBeenCalledWith(CALL_EVENT_KEYS.REMOTE_MEDIA, remoteMediaCb);
|
|
245
|
-
|
|
246
|
-
remoteMediaCb(mockTrack);
|
|
247
|
-
|
|
248
|
-
expect(taskEmitSpy).toHaveBeenCalledWith(TASK_EVENTS.TASK_MEDIA, mockTrack);
|
|
249
|
-
});
|
|
250
|
-
|
|
251
|
-
describe('updateTaskData cases', () => {
|
|
252
|
-
it('updates the task data by overwrite', async () => {
|
|
253
|
-
const newData = {
|
|
254
|
-
type: CC_EVENTS.AGENT_CONTACT_ASSIGNED,
|
|
255
|
-
agentId: '723a8ffb-a26e-496d-b14a-ff44fb83b64f',
|
|
256
|
-
eventTime: 1733211616959,
|
|
257
|
-
eventType: 'RoutingMessage',
|
|
258
|
-
interactionId: taskId,
|
|
259
|
-
orgId: '6ecef209-9a34-4ed1-a07a-7ddd1dbe925a',
|
|
260
|
-
trackingId: '575c0ec2-618c-42af-a61c-53aeb0a221ee',
|
|
261
|
-
mediaResourceId: '0ae913a4-c857-4705-8d49-76dd3dde75e4',
|
|
262
|
-
destAgentId: 'ebeb893b-ba67-4f36-8418-95c7492b28c2',
|
|
263
|
-
owner: '723a8ffb-a26e-496d-b14a-ff44fb83b64f',
|
|
264
|
-
queueMgr: 'aqm',
|
|
265
|
-
interaction: {
|
|
266
|
-
mainInteractionId: taskId,
|
|
267
|
-
media: {
|
|
268
|
-
'58a45567-4e61-4f4b-a580-5bc86357bef0': {
|
|
269
|
-
holdTimestamp: null,
|
|
270
|
-
isHold: false,
|
|
271
|
-
mType: 'consult',
|
|
272
|
-
mediaMgr: 'callmm',
|
|
273
|
-
mediaResourceId: '58a45567-4e61-4f4b-a580-5bc86357bef0',
|
|
274
|
-
mediaType: 'telephony',
|
|
275
|
-
participants: [
|
|
276
|
-
'f520d6b5-28ad-4f2f-b83e-781bb64af617',
|
|
277
|
-
'723a8ffb-a26e-496d-b14a-ff44fb83b64f',
|
|
278
|
-
],
|
|
279
|
-
},
|
|
280
|
-
[taskId]: {
|
|
281
|
-
holdTimestamp: 1734667567279,
|
|
282
|
-
isHold: true,
|
|
283
|
-
mType: 'mainCall',
|
|
284
|
-
mediaMgr: 'callmm',
|
|
285
|
-
mediaResourceId: taskId,
|
|
286
|
-
mediaType: 'telephony',
|
|
287
|
-
participants: ['+14696762938', '723a8ffb-a26e-496d-b14a-ff44fb83b64f'],
|
|
288
|
-
},
|
|
289
|
-
},
|
|
290
|
-
},
|
|
291
|
-
};
|
|
292
|
-
|
|
293
|
-
expect(task.data).toEqual(taskDataMock);
|
|
294
|
-
|
|
295
|
-
const shouldOverwrite = true;
|
|
296
|
-
task.updateTaskData(newData, shouldOverwrite);
|
|
297
|
-
|
|
298
|
-
expect(task.data).toEqual(newData);
|
|
299
|
-
});
|
|
300
|
-
|
|
301
|
-
it('updates the task data by merging with key removal', async () => {
|
|
302
|
-
const newData = {
|
|
303
|
-
// Purposefully omit other keys to test remove and merge behavior
|
|
304
|
-
isConsulting: true, // Add a new custom key to test persistence
|
|
305
|
-
interaction: {
|
|
306
|
-
// Purposefully omit other interaction keys to test removal
|
|
307
|
-
media: {
|
|
308
|
-
'58a45567-4e61-4f4b-a580-5bc86357bef0': {
|
|
309
|
-
holdTimestamp: null,
|
|
310
|
-
isHold: true,
|
|
311
|
-
mType: 'consult',
|
|
312
|
-
mediaMgr: 'callmm',
|
|
313
|
-
mediaResourceId: '58a45567-4e61-4f4b-a580-5bc86357bef0',
|
|
314
|
-
mediaType: 'telephony',
|
|
315
|
-
participants: [
|
|
316
|
-
'f520d6b5-28ad-4f2f-b83e-781bb64af617',
|
|
317
|
-
'723a8ffb-a26e-496d-b14a-ff44fb83b64f',
|
|
318
|
-
],
|
|
319
|
-
},
|
|
320
|
-
[taskId]: {
|
|
321
|
-
holdTimestamp: 1734667567279,
|
|
322
|
-
isHold: false,
|
|
323
|
-
mType: 'mainCall',
|
|
324
|
-
mediaMgr: 'callmm',
|
|
325
|
-
mediaResourceId: taskId,
|
|
326
|
-
mediaType: 'telephony',
|
|
327
|
-
participants: ['+14696762938', '723a8ffb-a26e-496d-b14a-ff44fb83b64f'],
|
|
328
|
-
},
|
|
329
|
-
},
|
|
330
|
-
},
|
|
331
|
-
};
|
|
332
|
-
|
|
333
|
-
// The reconcileData method removes keys from oldData that are not in newData
|
|
334
|
-
// This means only keys present in newData will remain in the final result
|
|
335
|
-
const expectedData: TaskData = {
|
|
336
|
-
isConsulting: true, // New key is added
|
|
337
|
-
interaction: {
|
|
338
|
-
// Only the media key from newData.interaction remains
|
|
339
|
-
media: {
|
|
340
|
-
'58a45567-4e61-4f4b-a580-5bc86357bef0': {
|
|
341
|
-
holdTimestamp: null,
|
|
342
|
-
isHold: true,
|
|
343
|
-
mType: 'consult',
|
|
344
|
-
mediaMgr: 'callmm',
|
|
345
|
-
mediaResourceId: '58a45567-4e61-4f4b-a580-5bc86357bef0',
|
|
346
|
-
mediaType: 'telephony',
|
|
347
|
-
participants: [
|
|
348
|
-
'f520d6b5-28ad-4f2f-b83e-781bb64af617',
|
|
349
|
-
'723a8ffb-a26e-496d-b14a-ff44fb83b64f',
|
|
350
|
-
],
|
|
351
|
-
},
|
|
352
|
-
[taskId]: {
|
|
353
|
-
holdTimestamp: 1734667567279,
|
|
354
|
-
isHold: false,
|
|
355
|
-
mType: 'mainCall',
|
|
356
|
-
mediaMgr: 'callmm',
|
|
357
|
-
mediaResourceId: taskId,
|
|
358
|
-
mediaType: 'telephony',
|
|
359
|
-
participants: ['+14696762938', '723a8ffb-a26e-496d-b14a-ff44fb83b64f'],
|
|
360
|
-
},
|
|
361
|
-
},
|
|
362
|
-
},
|
|
363
|
-
};
|
|
364
|
-
|
|
365
|
-
expect(task.data).toEqual(taskDataMock);
|
|
366
|
-
const shouldOverwrite = false;
|
|
367
|
-
task.updateTaskData(newData, shouldOverwrite);
|
|
368
|
-
|
|
369
|
-
expect(task.data).toEqual(expectedData);
|
|
370
|
-
});
|
|
371
|
-
|
|
372
|
-
it('updates the task data by merging and preserving existing keys', async () => {
|
|
373
|
-
const newData = {
|
|
374
|
-
...taskDataMock, // Include all existing keys to test merge without removal
|
|
375
|
-
isConsulting: true, // Add a new custom key
|
|
376
|
-
interaction: {
|
|
377
|
-
...taskDataMock.interaction, // Include existing interaction data
|
|
378
|
-
media: {
|
|
379
|
-
...taskDataMock.interaction.media, // Include existing media
|
|
380
|
-
'58a45567-4e61-4f4b-a580-5bc86357bef0': {
|
|
381
|
-
holdTimestamp: null,
|
|
382
|
-
isHold: true,
|
|
383
|
-
mType: 'consult',
|
|
384
|
-
mediaMgr: 'callmm',
|
|
385
|
-
mediaResourceId: '58a45567-4e61-4f4b-a580-5bc86357bef0',
|
|
386
|
-
mediaType: 'telephony',
|
|
387
|
-
participants: [
|
|
388
|
-
'f520d6b5-28ad-4f2f-b83e-781bb64af617',
|
|
389
|
-
'723a8ffb-a26e-496d-b14a-ff44fb83b64f',
|
|
390
|
-
],
|
|
391
|
-
},
|
|
392
|
-
},
|
|
393
|
-
},
|
|
394
|
-
};
|
|
395
|
-
|
|
396
|
-
const expectedData: TaskData = {
|
|
397
|
-
...taskDataMock,
|
|
398
|
-
isConsulting: true,
|
|
399
|
-
interaction: {
|
|
400
|
-
...taskDataMock.interaction,
|
|
401
|
-
media: {
|
|
402
|
-
...taskDataMock.interaction.media,
|
|
403
|
-
'58a45567-4e61-4f4b-a580-5bc86357bef0': {
|
|
404
|
-
holdTimestamp: null,
|
|
405
|
-
isHold: true,
|
|
406
|
-
mType: 'consult',
|
|
407
|
-
mediaMgr: 'callmm',
|
|
408
|
-
mediaResourceId: '58a45567-4e61-4f4b-a580-5bc86357bef0',
|
|
409
|
-
mediaType: 'telephony',
|
|
410
|
-
participants: [
|
|
411
|
-
'f520d6b5-28ad-4f2f-b83e-781bb64af617',
|
|
412
|
-
'723a8ffb-a26e-496d-b14a-ff44fb83b64f',
|
|
413
|
-
],
|
|
414
|
-
},
|
|
415
|
-
},
|
|
416
|
-
},
|
|
417
|
-
};
|
|
418
|
-
|
|
419
|
-
expect(task.data).toEqual(taskDataMock);
|
|
420
|
-
const shouldOverwrite = false;
|
|
421
|
-
task.updateTaskData(newData, shouldOverwrite);
|
|
422
|
-
|
|
423
|
-
expect(task.data).toEqual(expectedData);
|
|
424
|
-
});
|
|
425
|
-
});
|
|
426
|
-
|
|
427
|
-
it('should accept a task and answer call when using BROWSER login option', async () => {
|
|
428
|
-
const answerCallSpy = jest.spyOn(webCallingService, 'answerCall');
|
|
429
|
-
|
|
430
|
-
await task.accept();
|
|
431
|
-
|
|
432
|
-
expect(navigator.mediaDevices.getUserMedia).toHaveBeenCalledWith({audio: true});
|
|
433
|
-
expect(LocalMicrophoneStream).toHaveBeenCalledWith(mockStream);
|
|
434
|
-
expect(answerCallSpy).toHaveBeenCalledWith(expect.any(LocalMicrophoneStream), taskId);
|
|
435
|
-
expect(loggerInfoSpy).toHaveBeenCalledWith(`Accepting task`, {
|
|
436
|
-
module: TASK_FILE,
|
|
437
|
-
method: 'accept',
|
|
438
|
-
interactionId: task.data.interactionId,
|
|
439
|
-
});
|
|
440
|
-
expect(loggerLogSpy).toHaveBeenCalledWith(
|
|
441
|
-
`Task accepted successfully with webrtc calling`,
|
|
442
|
-
{
|
|
443
|
-
module: TASK_FILE,
|
|
444
|
-
method: 'accept',
|
|
445
|
-
interactionId: task.data.interactionId,
|
|
446
|
-
}
|
|
447
|
-
);
|
|
448
|
-
expect(mockMetricsManager.trackEvent).toHaveBeenNthCalledWith(
|
|
449
|
-
1,
|
|
450
|
-
METRIC_EVENT_NAMES.TASK_ACCEPT_SUCCESS,
|
|
451
|
-
{
|
|
452
|
-
taskId: task.data.interactionId,
|
|
453
|
-
...MetricsManager.getCommonTrackingFieldForAQMResponse(task),
|
|
454
|
-
eventType: 'AgentContactReserved',
|
|
455
|
-
notifTrackingId: '575c0ec2-618c-42af-a61c-53aeb0a221ee',
|
|
456
|
-
trackingId: undefined,
|
|
457
|
-
},
|
|
458
|
-
['operational', 'behavioral', 'business']
|
|
459
|
-
);
|
|
460
|
-
});
|
|
461
|
-
|
|
462
|
-
it('should accept a task when mediaType chat', async () => {
|
|
463
|
-
task.data.interaction.mediaType = 'chat';
|
|
464
|
-
const answerCallSpy = jest.spyOn(webCallingService, 'answerCall');
|
|
465
|
-
const response = {};
|
|
466
|
-
contactMock.accept.mockResolvedValue(response);
|
|
467
|
-
|
|
468
|
-
await task.accept();
|
|
469
|
-
|
|
470
|
-
expect(contactMock.accept).toHaveBeenCalledWith({
|
|
471
|
-
interactionId: taskId,
|
|
472
|
-
});
|
|
473
|
-
expect(answerCallSpy).not.toHaveBeenCalled();
|
|
474
|
-
expect(mockMetricsManager.timeEvent).toHaveBeenCalledWith([
|
|
475
|
-
METRIC_EVENT_NAMES.TASK_ACCEPT_SUCCESS,
|
|
476
|
-
METRIC_EVENT_NAMES.TASK_ACCEPT_FAILED,
|
|
477
|
-
]);
|
|
478
|
-
const expectedMetrics = {
|
|
479
|
-
taskId: task.data.interactionId,
|
|
480
|
-
agentId: task.data.agentId,
|
|
481
|
-
eventType: task.data.type,
|
|
482
|
-
notifTrackingId: task.data.trackingId,
|
|
483
|
-
orgId: task.data.orgId,
|
|
484
|
-
};
|
|
485
|
-
expect(mockMetricsManager.trackEvent).toHaveBeenCalledWith(
|
|
486
|
-
METRIC_EVENT_NAMES.TASK_ACCEPT_SUCCESS,
|
|
487
|
-
expectedMetrics,
|
|
488
|
-
['operational', 'behavioral', 'business']
|
|
489
|
-
);
|
|
490
|
-
expect(loggerInfoSpy).toHaveBeenCalledWith(`Accepting task`, {
|
|
491
|
-
module: TASK_FILE,
|
|
492
|
-
method: 'accept',
|
|
493
|
-
interactionId: task.data.interactionId,
|
|
494
|
-
});
|
|
495
|
-
expect(loggerLogSpy).toHaveBeenCalledWith(`Task accepted successfully`, {
|
|
496
|
-
module: TASK_FILE,
|
|
497
|
-
method: 'accept',
|
|
498
|
-
interactionId: task.data.interactionId,
|
|
499
|
-
});
|
|
500
|
-
});
|
|
501
|
-
|
|
502
|
-
it('should accept a task when mediaType email', async () => {
|
|
503
|
-
task.data.interaction.mediaType = 'email';
|
|
504
|
-
const answerCallSpy = jest.spyOn(webCallingService, 'answerCall');
|
|
505
|
-
const response = {};
|
|
506
|
-
contactMock.accept.mockResolvedValue(response);
|
|
507
|
-
|
|
508
|
-
await task.accept();
|
|
509
|
-
|
|
510
|
-
expect(contactMock.accept).toHaveBeenCalledWith({
|
|
511
|
-
interactionId: taskId,
|
|
512
|
-
});
|
|
513
|
-
expect(answerCallSpy).not.toHaveBeenCalled();
|
|
514
|
-
expect(mockMetricsManager.timeEvent).toHaveBeenCalledWith([
|
|
515
|
-
METRIC_EVENT_NAMES.TASK_ACCEPT_SUCCESS,
|
|
516
|
-
METRIC_EVENT_NAMES.TASK_ACCEPT_FAILED,
|
|
517
|
-
]);
|
|
518
|
-
const expectedMetrics = {
|
|
519
|
-
taskId: task.data.interactionId,
|
|
520
|
-
agentId: task.data.agentId,
|
|
521
|
-
eventType: task.data.type,
|
|
522
|
-
notifTrackingId: task.data.trackingId,
|
|
523
|
-
orgId: task.data.orgId,
|
|
524
|
-
};
|
|
525
|
-
expect(mockMetricsManager.trackEvent).toHaveBeenCalledWith(
|
|
526
|
-
METRIC_EVENT_NAMES.TASK_ACCEPT_SUCCESS,
|
|
527
|
-
expectedMetrics,
|
|
528
|
-
['operational', 'behavioral', 'business']
|
|
529
|
-
);
|
|
530
|
-
expect(loggerInfoSpy).toHaveBeenCalledWith(`Accepting task`, {
|
|
531
|
-
module: TASK_FILE,
|
|
532
|
-
method: 'accept',
|
|
533
|
-
interactionId: task.data.interactionId,
|
|
534
|
-
});
|
|
535
|
-
expect(loggerLogSpy).toHaveBeenCalledWith(`Task accepted successfully`, {
|
|
536
|
-
module: TASK_FILE,
|
|
537
|
-
method: 'accept',
|
|
538
|
-
interactionId: task.data.interactionId,
|
|
539
|
-
});
|
|
540
|
-
});
|
|
541
|
-
|
|
542
|
-
it('should handle errors in accept method', async () => {
|
|
543
|
-
const error = {details: (global as any).makeFailure('Accept Failed')};
|
|
544
|
-
|
|
545
|
-
jest.spyOn(webCallingService, 'answerCall').mockImplementation(() => {
|
|
546
|
-
throw error;
|
|
547
|
-
});
|
|
548
|
-
|
|
549
|
-
await expect(task.accept()).rejects.toThrow(new Error(error.details.data.reason));
|
|
550
|
-
expect(generateTaskErrorObjectSpy).toHaveBeenCalledWith(error, 'accept', TASK_FILE);
|
|
551
|
-
const expectedTaskErrorFields = {
|
|
552
|
-
trackingId: error.details.trackingId,
|
|
553
|
-
errorMessage: error.details.data.reason,
|
|
554
|
-
errorType: '',
|
|
555
|
-
errorData: '',
|
|
556
|
-
reasonCode: 0,
|
|
557
|
-
};
|
|
558
|
-
expect(mockMetricsManager.trackEvent).toHaveBeenNthCalledWith(
|
|
559
|
-
1,
|
|
560
|
-
METRIC_EVENT_NAMES.TASK_ACCEPT_FAILED,
|
|
561
|
-
{
|
|
562
|
-
taskId: taskDataMock.interactionId,
|
|
563
|
-
error: error.toString(),
|
|
564
|
-
...expectedTaskErrorFields,
|
|
565
|
-
...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details),
|
|
566
|
-
},
|
|
567
|
-
['operational', 'behavioral', 'business']
|
|
568
|
-
);
|
|
569
|
-
});
|
|
570
|
-
|
|
571
|
-
it('should decline call using webCallingService', async () => {
|
|
572
|
-
const declineCallSpy = jest.spyOn(webCallingService, 'declineCall');
|
|
573
|
-
const offSpy = jest.spyOn(webCallingService, 'off');
|
|
574
|
-
|
|
575
|
-
await task.decline();
|
|
576
|
-
|
|
577
|
-
expect(declineCallSpy).toHaveBeenCalledWith(taskId);
|
|
578
|
-
expect(offSpy).toHaveBeenCalledWith(CALL_EVENT_KEYS.REMOTE_MEDIA, offSpy.mock.calls[0][1]);
|
|
579
|
-
expect(loggerInfoSpy).toHaveBeenCalledWith(`Declining task`, {
|
|
580
|
-
module: TASK_FILE,
|
|
581
|
-
method: 'decline',
|
|
582
|
-
interactionId: task.data.interactionId,
|
|
583
|
-
});
|
|
584
|
-
expect(loggerLogSpy).toHaveBeenCalledWith(`Task declined successfully`, {
|
|
585
|
-
module: TASK_FILE,
|
|
586
|
-
method: 'decline',
|
|
587
|
-
interactionId: task.data.interactionId,
|
|
588
|
-
});
|
|
589
|
-
expect(mockMetricsManager.trackEvent).toHaveBeenNthCalledWith(
|
|
590
|
-
1,
|
|
591
|
-
METRIC_EVENT_NAMES.TASK_DECLINE_SUCCESS,
|
|
592
|
-
{
|
|
593
|
-
taskId: taskDataMock.interactionId,
|
|
594
|
-
},
|
|
595
|
-
['operational', 'behavioral']
|
|
596
|
-
);
|
|
597
|
-
});
|
|
598
|
-
|
|
599
|
-
it('should handle errors in decline method', async () => {
|
|
600
|
-
const error = {details: (global as any).makeFailure('Decline Failed')};
|
|
601
|
-
|
|
602
|
-
jest.spyOn(webCallingService, 'declineCall').mockImplementation(() => {
|
|
603
|
-
throw error;
|
|
604
|
-
});
|
|
605
|
-
await expect(task.decline()).rejects.toThrow(new Error(error.details.data.reason));
|
|
606
|
-
expect(generateTaskErrorObjectSpy).toHaveBeenCalledWith(error, 'decline', TASK_FILE);
|
|
607
|
-
const expectedTaskErrorFieldsDecline = {
|
|
608
|
-
trackingId: error.details.trackingId,
|
|
609
|
-
errorMessage: error.details.data.reason,
|
|
610
|
-
errorType: '',
|
|
611
|
-
errorData: '',
|
|
612
|
-
reasonCode: 0,
|
|
613
|
-
};
|
|
614
|
-
expect(mockMetricsManager.trackEvent).toHaveBeenNthCalledWith(
|
|
615
|
-
1,
|
|
616
|
-
METRIC_EVENT_NAMES.TASK_DECLINE_FAILED,
|
|
617
|
-
{
|
|
618
|
-
taskId: taskDataMock.interactionId,
|
|
619
|
-
error: error.toString(),
|
|
620
|
-
...expectedTaskErrorFieldsDecline,
|
|
621
|
-
...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details),
|
|
622
|
-
},
|
|
623
|
-
['operational', 'behavioral']
|
|
624
|
-
);
|
|
625
|
-
});
|
|
626
|
-
|
|
627
|
-
it('should hold the task and return the expected response', async () => {
|
|
628
|
-
const expectedResponse: TaskResponse = {data: {interactionId: taskId}} as AgentContact;
|
|
629
|
-
contactMock.hold.mockResolvedValue(expectedResponse);
|
|
630
|
-
|
|
631
|
-
const response = await task.hold();
|
|
632
|
-
|
|
633
|
-
expect(contactMock.hold).toHaveBeenCalledWith({
|
|
634
|
-
interactionId: taskId,
|
|
635
|
-
data: {mediaResourceId: taskDataMock.mediaResourceId},
|
|
636
|
-
});
|
|
637
|
-
expect(response).toEqual(expectedResponse);
|
|
638
|
-
expect(loggerInfoSpy).toHaveBeenCalledWith(`Holding task`, {
|
|
639
|
-
module: TASK_FILE,
|
|
640
|
-
method: 'hold',
|
|
641
|
-
interactionId: task.data.interactionId,
|
|
642
|
-
});
|
|
643
|
-
expect(loggerLogSpy).toHaveBeenCalledWith(`Task placed on hold successfully`, {
|
|
644
|
-
module: TASK_FILE,
|
|
645
|
-
method: 'hold',
|
|
646
|
-
interactionId: task.data.interactionId,
|
|
647
|
-
});
|
|
648
|
-
expect(mockMetricsManager.trackEvent).toHaveBeenNthCalledWith(
|
|
649
|
-
1,
|
|
650
|
-
METRIC_EVENT_NAMES.TASK_HOLD_SUCCESS,
|
|
651
|
-
{
|
|
652
|
-
...MetricsManager.getCommonTrackingFieldForAQMResponse(expectedResponse),
|
|
653
|
-
taskId: taskDataMock.interactionId,
|
|
654
|
-
mediaResourceId: taskDataMock.mediaResourceId,
|
|
655
|
-
},
|
|
656
|
-
['operational', 'behavioral']
|
|
657
|
-
);
|
|
658
|
-
});
|
|
659
|
-
|
|
660
|
-
it('should hold the task with custom mediaResourceId and return the expected response', async () => {
|
|
661
|
-
const customMediaResourceId = 'custom-media-resource-id-123';
|
|
662
|
-
const expectedResponse: TaskResponse = {data: {interactionId: taskId}} as AgentContact;
|
|
663
|
-
contactMock.hold.mockResolvedValue(expectedResponse);
|
|
664
|
-
|
|
665
|
-
const response = await task.hold(customMediaResourceId);
|
|
666
|
-
|
|
667
|
-
expect(contactMock.hold).toHaveBeenCalledWith({
|
|
668
|
-
interactionId: taskId,
|
|
669
|
-
data: {mediaResourceId: customMediaResourceId},
|
|
670
|
-
});
|
|
671
|
-
expect(response).toEqual(expectedResponse);
|
|
672
|
-
expect(loggerInfoSpy).toHaveBeenCalledWith(`Holding task`, {
|
|
673
|
-
module: TASK_FILE,
|
|
674
|
-
method: 'hold',
|
|
675
|
-
interactionId: task.data.interactionId,
|
|
676
|
-
});
|
|
677
|
-
expect(loggerLogSpy).toHaveBeenCalledWith(`Task placed on hold successfully`, {
|
|
678
|
-
module: TASK_FILE,
|
|
679
|
-
method: 'hold',
|
|
680
|
-
interactionId: task.data.interactionId,
|
|
681
|
-
});
|
|
682
|
-
expect(mockMetricsManager.trackEvent).toHaveBeenNthCalledWith(
|
|
683
|
-
1,
|
|
684
|
-
METRIC_EVENT_NAMES.TASK_HOLD_SUCCESS,
|
|
685
|
-
{
|
|
686
|
-
...MetricsManager.getCommonTrackingFieldForAQMResponse(expectedResponse),
|
|
687
|
-
taskId: taskDataMock.interactionId,
|
|
688
|
-
mediaResourceId: customMediaResourceId,
|
|
689
|
-
},
|
|
690
|
-
['operational', 'behavioral']
|
|
691
|
-
);
|
|
692
|
-
});
|
|
693
|
-
|
|
694
|
-
it('should handle errors in hold method', async () => {
|
|
695
|
-
const error = {details: (global as any).makeFailure('Hold Failed')};
|
|
696
|
-
contactMock.hold.mockImplementation(() => {
|
|
697
|
-
throw error;
|
|
698
|
-
});
|
|
699
|
-
|
|
700
|
-
await expect(task.hold()).rejects.toThrow(error.details.data.reason);
|
|
701
|
-
expect(generateTaskErrorObjectSpy).toHaveBeenCalledWith(error, 'hold', TASK_FILE);
|
|
702
|
-
const expectedTaskErrorFieldsHold = {
|
|
703
|
-
trackingId: error.details.trackingId,
|
|
704
|
-
errorMessage: error.details.data.reason,
|
|
705
|
-
errorType: '',
|
|
706
|
-
errorData: '',
|
|
707
|
-
reasonCode: 0,
|
|
708
|
-
};
|
|
709
|
-
expect(mockMetricsManager.trackEvent).toHaveBeenNthCalledWith(
|
|
710
|
-
1,
|
|
711
|
-
METRIC_EVENT_NAMES.TASK_HOLD_FAILED,
|
|
712
|
-
{
|
|
713
|
-
taskId: taskDataMock.interactionId,
|
|
714
|
-
mediaResourceId: taskDataMock.mediaResourceId,
|
|
715
|
-
error: error.toString(),
|
|
716
|
-
...expectedTaskErrorFieldsHold,
|
|
717
|
-
...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details),
|
|
718
|
-
},
|
|
719
|
-
['operational', 'behavioral']
|
|
720
|
-
);
|
|
721
|
-
});
|
|
722
|
-
|
|
723
|
-
it('should handle errors in hold method with custom mediaResourceId', async () => {
|
|
724
|
-
const customMediaResourceId = 'custom-media-resource-id-456';
|
|
725
|
-
const error = {details: (global as any).makeFailure('Hold Failed with custom mediaResourceId')};
|
|
726
|
-
contactMock.hold.mockImplementation(() => {
|
|
727
|
-
throw error;
|
|
728
|
-
});
|
|
729
|
-
|
|
730
|
-
await expect(task.hold(customMediaResourceId)).rejects.toThrow(error.details.data.reason);
|
|
731
|
-
expect(generateTaskErrorObjectSpy).toHaveBeenCalledWith(error, 'hold', TASK_FILE);
|
|
732
|
-
const expectedTaskErrorFieldsHold = {
|
|
733
|
-
trackingId: error.details.trackingId,
|
|
734
|
-
errorMessage: error.details.data.reason,
|
|
735
|
-
errorType: '',
|
|
736
|
-
errorData: '',
|
|
737
|
-
reasonCode: 0,
|
|
738
|
-
};
|
|
739
|
-
expect(mockMetricsManager.trackEvent).toHaveBeenNthCalledWith(
|
|
740
|
-
1,
|
|
741
|
-
METRIC_EVENT_NAMES.TASK_HOLD_FAILED,
|
|
742
|
-
{
|
|
743
|
-
taskId: taskDataMock.interactionId,
|
|
744
|
-
mediaResourceId: customMediaResourceId,
|
|
745
|
-
error: error.toString(),
|
|
746
|
-
...expectedTaskErrorFieldsHold,
|
|
747
|
-
...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details),
|
|
748
|
-
},
|
|
749
|
-
['operational', 'behavioral']
|
|
750
|
-
);
|
|
751
|
-
});
|
|
752
|
-
|
|
753
|
-
it('should resume the task and return the expected response', async () => {
|
|
754
|
-
const expectedResponse: TaskResponse = {data: {interactionId: taskId}} as AgentContact;
|
|
755
|
-
contactMock.unHold.mockResolvedValue(expectedResponse);
|
|
756
|
-
const response = await task.resume();
|
|
757
|
-
expect(contactMock.unHold).toHaveBeenCalledWith({
|
|
758
|
-
interactionId: taskId,
|
|
759
|
-
data: {mediaResourceId: taskDataMock.mediaResourceId},
|
|
760
|
-
});
|
|
761
|
-
expect(response).toEqual(expectedResponse);
|
|
762
|
-
expect(mockMetricsManager.trackEvent).toHaveBeenNthCalledWith(
|
|
763
|
-
1,
|
|
764
|
-
METRIC_EVENT_NAMES.TASK_RESUME_SUCCESS,
|
|
765
|
-
{
|
|
766
|
-
taskId: taskDataMock.interactionId,
|
|
767
|
-
mainInteractionId: taskDataMock.interaction.mainInteractionId,
|
|
768
|
-
mediaResourceId:
|
|
769
|
-
taskDataMock.interaction.media[taskDataMock.interaction.mainInteractionId]
|
|
770
|
-
.mediaResourceId,
|
|
771
|
-
...MetricsManager.getCommonTrackingFieldForAQMResponse(expectedResponse),
|
|
772
|
-
},
|
|
773
|
-
['operational', 'behavioral']
|
|
774
|
-
);
|
|
775
|
-
});
|
|
776
|
-
|
|
777
|
-
it('should resume the task with custom mediaResourceId and return the expected response', async () => {
|
|
778
|
-
const customMediaResourceId = 'custom-media-resource-id-789';
|
|
779
|
-
const expectedResponse: TaskResponse = {data: {interactionId: taskId}} as AgentContact;
|
|
780
|
-
contactMock.unHold.mockResolvedValue(expectedResponse);
|
|
781
|
-
const response = await task.resume(customMediaResourceId);
|
|
782
|
-
expect(contactMock.unHold).toHaveBeenCalledWith({
|
|
783
|
-
interactionId: taskId,
|
|
784
|
-
data: {mediaResourceId: customMediaResourceId},
|
|
785
|
-
});
|
|
786
|
-
expect(response).toEqual(expectedResponse);
|
|
787
|
-
expect(mockMetricsManager.trackEvent).toHaveBeenNthCalledWith(
|
|
788
|
-
1,
|
|
789
|
-
METRIC_EVENT_NAMES.TASK_RESUME_SUCCESS,
|
|
790
|
-
{
|
|
791
|
-
taskId: taskDataMock.interactionId,
|
|
792
|
-
mainInteractionId: taskDataMock.interaction.mainInteractionId,
|
|
793
|
-
mediaResourceId: customMediaResourceId,
|
|
794
|
-
...MetricsManager.getCommonTrackingFieldForAQMResponse(expectedResponse),
|
|
795
|
-
},
|
|
796
|
-
['operational', 'behavioral']
|
|
797
|
-
);
|
|
798
|
-
});
|
|
799
|
-
|
|
800
|
-
it('should handle errors in resume method', async () => {
|
|
801
|
-
const error = {details: (global as any).makeFailure('Resume Failed')};
|
|
802
|
-
contactMock.unHold.mockImplementation(() => {
|
|
803
|
-
throw error;
|
|
804
|
-
});
|
|
805
|
-
|
|
806
|
-
await expect(task.resume()).rejects.toThrow(error.details.data.reason);
|
|
807
|
-
expect(generateTaskErrorObjectSpy).toHaveBeenCalledWith(error, 'resume', TASK_FILE);
|
|
808
|
-
const expectedTaskErrorFieldsResume = {
|
|
809
|
-
trackingId: error.details.trackingId,
|
|
810
|
-
errorMessage: error.details.data.reason,
|
|
811
|
-
errorType: '',
|
|
812
|
-
errorData: '',
|
|
813
|
-
reasonCode: 0,
|
|
814
|
-
};
|
|
815
|
-
expect(mockMetricsManager.trackEvent).toHaveBeenNthCalledWith(
|
|
816
|
-
1,
|
|
817
|
-
METRIC_EVENT_NAMES.TASK_RESUME_FAILED,
|
|
818
|
-
{
|
|
819
|
-
taskId: taskDataMock.interactionId,
|
|
820
|
-
mainInteractionId: taskDataMock.interaction.mainInteractionId,
|
|
821
|
-
mediaResourceId:
|
|
822
|
-
taskDataMock.interaction.media[taskDataMock.interaction.mainInteractionId]
|
|
823
|
-
.mediaResourceId,
|
|
824
|
-
...expectedTaskErrorFieldsResume,
|
|
825
|
-
...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details),
|
|
826
|
-
},
|
|
827
|
-
['operational', 'behavioral']
|
|
828
|
-
);
|
|
829
|
-
});
|
|
830
|
-
|
|
831
|
-
it('should handle errors in resume method with custom mediaResourceId', async () => {
|
|
832
|
-
const customMediaResourceId = 'custom-media-resource-id-999';
|
|
833
|
-
const error = {details: (global as any).makeFailure('Resume Failed with custom mediaResourceId')};
|
|
834
|
-
contactMock.unHold.mockImplementation(() => {
|
|
835
|
-
throw error;
|
|
836
|
-
});
|
|
837
|
-
|
|
838
|
-
await expect(task.resume(customMediaResourceId)).rejects.toThrow(error.details.data.reason);
|
|
839
|
-
expect(generateTaskErrorObjectSpy).toHaveBeenCalledWith(error, 'resume', TASK_FILE);
|
|
840
|
-
const expectedTaskErrorFieldsResume = {
|
|
841
|
-
trackingId: error.details.trackingId,
|
|
842
|
-
errorMessage: error.details.data.reason,
|
|
843
|
-
errorType: '',
|
|
844
|
-
errorData: '',
|
|
845
|
-
reasonCode: 0,
|
|
846
|
-
};
|
|
847
|
-
expect(mockMetricsManager.trackEvent).toHaveBeenNthCalledWith(
|
|
848
|
-
1,
|
|
849
|
-
METRIC_EVENT_NAMES.TASK_RESUME_FAILED,
|
|
850
|
-
{
|
|
851
|
-
taskId: taskDataMock.interactionId,
|
|
852
|
-
mainInteractionId: taskDataMock.interaction.mainInteractionId,
|
|
853
|
-
mediaResourceId: customMediaResourceId,
|
|
854
|
-
...expectedTaskErrorFieldsResume,
|
|
855
|
-
...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details),
|
|
856
|
-
},
|
|
857
|
-
['operational', 'behavioral']
|
|
858
|
-
);
|
|
859
|
-
});
|
|
860
|
-
|
|
861
|
-
it('should initiate a consult call and return the expected response', async () => {
|
|
862
|
-
const consultPayload = {
|
|
863
|
-
to: '1234',
|
|
864
|
-
destinationType: DESTINATION_TYPE.AGENT,
|
|
865
|
-
};
|
|
866
|
-
const expectedResponse: TaskResponse = {data: {interactionId: taskId}, trackingId: '1234'} as AgentContact;
|
|
867
|
-
contactMock.consult.mockResolvedValue(expectedResponse);
|
|
868
|
-
|
|
869
|
-
const response = await task.consult(consultPayload);
|
|
870
|
-
|
|
871
|
-
expect(contactMock.consult).toHaveBeenCalledWith({interactionId: taskId, data: consultPayload});
|
|
872
|
-
expect(response).toEqual(expectedResponse);
|
|
873
|
-
expect(loggerInfoSpy).toHaveBeenCalledWith(`Starting consult`, {
|
|
874
|
-
module: TASK_FILE,
|
|
875
|
-
method: 'consult',
|
|
876
|
-
interactionId: task.data.interactionId,
|
|
877
|
-
});
|
|
878
|
-
expect(loggerLogSpy).toHaveBeenCalledWith(`Consult started successfully to ${consultPayload.to}`, {
|
|
879
|
-
module: TASK_FILE,
|
|
880
|
-
method: 'consult',
|
|
881
|
-
interactionId: task.data.interactionId,
|
|
882
|
-
trackingId: '1234',
|
|
883
|
-
});
|
|
884
|
-
expect(mockMetricsManager.trackEvent).toHaveBeenCalledWith(
|
|
885
|
-
METRIC_EVENT_NAMES.TASK_CONSULT_START_SUCCESS,
|
|
886
|
-
{
|
|
887
|
-
taskId: taskDataMock.interactionId,
|
|
888
|
-
destination: consultPayload.to,
|
|
889
|
-
destinationType: consultPayload.destinationType,
|
|
890
|
-
...MetricsManager.getCommonTrackingFieldForAQMResponse(expectedResponse),
|
|
891
|
-
},
|
|
892
|
-
['operational', 'behavioral', 'business']
|
|
893
|
-
);
|
|
894
|
-
});
|
|
895
|
-
|
|
896
|
-
it('should handle errors in consult method', async () => {
|
|
897
|
-
const error = {details: (global as any).makeFailure('Consult Failed')};
|
|
898
|
-
contactMock.consult.mockImplementation(() => {
|
|
899
|
-
throw error;
|
|
900
|
-
});
|
|
901
|
-
|
|
902
|
-
const consultPayload = {
|
|
903
|
-
to: '1234',
|
|
904
|
-
destinationType: DESTINATION_TYPE.AGENT,
|
|
905
|
-
};
|
|
906
|
-
|
|
907
|
-
await expect(task.consult(consultPayload)).rejects.toThrow(error.details.data.reason);
|
|
908
|
-
expect(generateTaskErrorObjectSpy).toHaveBeenCalledWith(error, 'consult', TASK_FILE);
|
|
909
|
-
expect(loggerInfoSpy).toHaveBeenCalledWith(`Starting consult`, {
|
|
910
|
-
module: TASK_FILE,
|
|
911
|
-
method: 'consult',
|
|
912
|
-
interactionId: task.data.interactionId,
|
|
913
|
-
});
|
|
914
|
-
const expectedTaskErrorFieldsConsult = {
|
|
915
|
-
trackingId: error.details.trackingId,
|
|
916
|
-
errorMessage: error.details.data.reason,
|
|
917
|
-
errorType: '',
|
|
918
|
-
errorData: '',
|
|
919
|
-
reasonCode: 0,
|
|
920
|
-
};
|
|
921
|
-
expect(mockMetricsManager.trackEvent).toHaveBeenCalledWith(
|
|
922
|
-
METRIC_EVENT_NAMES.TASK_CONSULT_START_FAILED,
|
|
923
|
-
{
|
|
924
|
-
taskId: taskDataMock.interactionId,
|
|
925
|
-
destination: consultPayload.to,
|
|
926
|
-
destinationType: consultPayload.destinationType,
|
|
927
|
-
error: error.toString(),
|
|
928
|
-
...expectedTaskErrorFieldsConsult,
|
|
929
|
-
...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details),
|
|
930
|
-
},
|
|
931
|
-
['operational', 'behavioral', 'business']
|
|
932
|
-
);
|
|
933
|
-
});
|
|
934
|
-
|
|
935
|
-
it('should end the consult call and return the expected response', async () => {
|
|
936
|
-
const expectedResponse: TaskResponse = {data: {interactionId: taskId}} as AgentContact;
|
|
937
|
-
contactMock.consultEnd.mockResolvedValue(expectedResponse);
|
|
938
|
-
|
|
939
|
-
const consultEndPayload: ConsultEndPayload = {
|
|
940
|
-
isConsult: true,
|
|
941
|
-
taskId: taskId,
|
|
942
|
-
};
|
|
943
|
-
const response = await task.endConsult(consultEndPayload);
|
|
944
|
-
|
|
945
|
-
expect(contactMock.consultEnd).toHaveBeenCalledWith({
|
|
946
|
-
interactionId: taskId,
|
|
947
|
-
data: consultEndPayload,
|
|
948
|
-
});
|
|
949
|
-
expect(response).toEqual(expectedResponse);
|
|
950
|
-
expect(mockMetricsManager.trackEvent).toHaveBeenNthCalledWith(
|
|
951
|
-
1,
|
|
952
|
-
METRIC_EVENT_NAMES.TASK_CONSULT_END_SUCCESS,
|
|
953
|
-
{
|
|
954
|
-
taskId: taskDataMock.interactionId,
|
|
955
|
-
...MetricsManager.getCommonTrackingFieldForAQMResponse(expectedResponse),
|
|
956
|
-
},
|
|
957
|
-
['operational', 'behavioral', 'business']
|
|
958
|
-
);
|
|
959
|
-
});
|
|
960
|
-
|
|
961
|
-
it('should handle errors in endConsult method', async () => {
|
|
962
|
-
const error = {details: (global as any).makeFailure('End Consult Failed')};
|
|
963
|
-
contactMock.consultEnd.mockImplementation(() => {
|
|
964
|
-
throw error;
|
|
965
|
-
});
|
|
966
|
-
|
|
967
|
-
const consultEndPayload: ConsultEndPayload = {
|
|
968
|
-
isConsult: true,
|
|
969
|
-
taskId: taskId,
|
|
970
|
-
};
|
|
971
|
-
|
|
972
|
-
await expect(task.endConsult(consultEndPayload)).rejects.toThrow(error.details.data.reason);
|
|
973
|
-
expect(generateTaskErrorObjectSpy).toHaveBeenCalledWith(error, 'endConsult', TASK_FILE);
|
|
974
|
-
const expectedTaskErrorFieldsEndConsult = {
|
|
975
|
-
trackingId: error.details.trackingId,
|
|
976
|
-
errorMessage: error.details.data.reason,
|
|
977
|
-
errorType: '',
|
|
978
|
-
errorData: '',
|
|
979
|
-
reasonCode: 0,
|
|
980
|
-
};
|
|
981
|
-
expect(mockMetricsManager.trackEvent).toHaveBeenNthCalledWith(
|
|
982
|
-
1,
|
|
983
|
-
METRIC_EVENT_NAMES.TASK_CONSULT_END_FAILED,
|
|
984
|
-
{
|
|
985
|
-
taskId: taskDataMock.interactionId,
|
|
986
|
-
error: error.toString(),
|
|
987
|
-
...expectedTaskErrorFieldsEndConsult,
|
|
988
|
-
...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details),
|
|
989
|
-
},
|
|
990
|
-
['operational', 'behavioral', 'business']
|
|
991
|
-
);
|
|
992
|
-
});
|
|
993
|
-
|
|
994
|
-
it('should do consult transfer the task to consulted agent and return the expected response', async () => {
|
|
995
|
-
const consultPayload = {
|
|
996
|
-
destination: '1234',
|
|
997
|
-
destinationType: DESTINATION_TYPE.AGENT,
|
|
998
|
-
};
|
|
999
|
-
const expectedResponse: TaskResponse = {data: {interactionId: taskId}} as AgentContact;
|
|
1000
|
-
contactMock.consult.mockResolvedValue(expectedResponse);
|
|
1001
|
-
|
|
1002
|
-
const response = await task.consult(consultPayload);
|
|
1003
|
-
|
|
1004
|
-
expect(contactMock.consult).toHaveBeenCalledWith({interactionId: taskId, data: consultPayload});
|
|
1005
|
-
expect(response).toEqual(expectedResponse);
|
|
1006
|
-
|
|
1007
|
-
const consultTransferResponse = await task.consultTransfer();
|
|
1008
|
-
expect(contactMock.consultTransfer).toHaveBeenCalledWith({
|
|
1009
|
-
interactionId: taskId,
|
|
1010
|
-
data: {
|
|
1011
|
-
to: taskDataMock.destAgentId,
|
|
1012
|
-
destinationType: CONSULT_TRANSFER_DESTINATION_TYPE.AGENT,
|
|
1013
|
-
},
|
|
1014
|
-
});
|
|
1015
|
-
expect(mockMetricsManager.trackEvent).toHaveBeenNthCalledWith(
|
|
1016
|
-
2,
|
|
1017
|
-
METRIC_EVENT_NAMES.TASK_TRANSFER_SUCCESS,
|
|
1018
|
-
{
|
|
1019
|
-
taskId: taskDataMock.interactionId,
|
|
1020
|
-
destination: taskDataMock.destAgentId,
|
|
1021
|
-
destinationType: CONSULT_TRANSFER_DESTINATION_TYPE.AGENT,
|
|
1022
|
-
isConsultTransfer: true,
|
|
1023
|
-
},
|
|
1024
|
-
['operational', 'behavioral', 'business']
|
|
1025
|
-
);
|
|
1026
|
-
});
|
|
1027
|
-
|
|
1028
|
-
it('should send DIALNUMBER when calculateDestType returns dialNumber during consultTransfer', async () => {
|
|
1029
|
-
const expectedResponse: TaskResponse = {data: {interactionId: taskId}} as AgentContact;
|
|
1030
|
-
contactMock.consultTransfer.mockResolvedValue(expectedResponse);
|
|
1031
|
-
|
|
1032
|
-
// Mock calculateDestType to return dialNumber
|
|
1033
|
-
calculateDestTypeSpy.mockReturnValue(CONSULT_TRANSFER_DESTINATION_TYPE.DIALNUMBER);
|
|
1034
|
-
|
|
1035
|
-
await task.consultTransfer();
|
|
1036
|
-
|
|
1037
|
-
expect(calculateDestTypeSpy).toHaveBeenCalledWith(taskDataMock.interaction, taskDataMock.agentId);
|
|
1038
|
-
expect(contactMock.consultTransfer).toHaveBeenCalledWith({
|
|
1039
|
-
interactionId: taskId,
|
|
1040
|
-
data: {
|
|
1041
|
-
to: taskDataMock.destAgentId,
|
|
1042
|
-
destinationType: CONSULT_TRANSFER_DESTINATION_TYPE.DIALNUMBER,
|
|
1043
|
-
},
|
|
1044
|
-
});
|
|
1045
|
-
});
|
|
1046
|
-
|
|
1047
|
-
it('should send ENTRYPOINT when calculateDestType returns entryPoint during consultTransfer', async () => {
|
|
1048
|
-
const expectedResponse: TaskResponse = {data: {interactionId: taskId}} as AgentContact;
|
|
1049
|
-
contactMock.consultTransfer.mockResolvedValue(expectedResponse);
|
|
1050
|
-
|
|
1051
|
-
// Mock calculateDestType to return entryPoint
|
|
1052
|
-
calculateDestTypeSpy.mockReturnValue(CONSULT_TRANSFER_DESTINATION_TYPE.ENTRYPOINT);
|
|
1053
|
-
|
|
1054
|
-
await task.consultTransfer();
|
|
1055
|
-
|
|
1056
|
-
expect(calculateDestTypeSpy).toHaveBeenCalledWith(taskDataMock.interaction, taskDataMock.agentId);
|
|
1057
|
-
expect(contactMock.consultTransfer).toHaveBeenCalledWith({
|
|
1058
|
-
interactionId: taskId,
|
|
1059
|
-
data: {
|
|
1060
|
-
to: taskDataMock.destAgentId,
|
|
1061
|
-
destinationType: CONSULT_TRANSFER_DESTINATION_TYPE.ENTRYPOINT,
|
|
1062
|
-
},
|
|
1063
|
-
});
|
|
1064
|
-
});
|
|
1065
|
-
|
|
1066
|
-
it('should use AGENT when calculateDestType returns agent during consultTransfer', async () => {
|
|
1067
|
-
const expectedResponse: TaskResponse = {data: {interactionId: taskId}} as AgentContact;
|
|
1068
|
-
contactMock.consultTransfer.mockResolvedValue(expectedResponse);
|
|
1069
|
-
|
|
1070
|
-
// Mock calculateDestType to return agent (default behavior)
|
|
1071
|
-
calculateDestTypeSpy.mockReturnValue(CONSULT_TRANSFER_DESTINATION_TYPE.AGENT);
|
|
1072
|
-
|
|
1073
|
-
await task.consultTransfer();
|
|
1074
|
-
|
|
1075
|
-
expect(calculateDestTypeSpy).toHaveBeenCalledWith(taskDataMock.interaction, taskDataMock.agentId);
|
|
1076
|
-
expect(contactMock.consultTransfer).toHaveBeenCalledWith({
|
|
1077
|
-
interactionId: taskId,
|
|
1078
|
-
data: {
|
|
1079
|
-
to: taskDataMock.destAgentId,
|
|
1080
|
-
destinationType: CONSULT_TRANSFER_DESTINATION_TYPE.AGENT,
|
|
1081
|
-
},
|
|
1082
|
-
});
|
|
1083
|
-
});
|
|
1084
|
-
|
|
1085
|
-
it('should do consult transfer to a queue by using the destAgentId from task data', async () => {
|
|
1086
|
-
const expectedResponse: TaskResponse = {data: {interactionId: taskId}} as AgentContact;
|
|
1087
|
-
contactMock.consultTransfer.mockResolvedValue(expectedResponse);
|
|
1088
|
-
|
|
1089
|
-
const queueConsultTransferPayload: ConsultTransferPayLoad = {
|
|
1090
|
-
to: 'some-queue-id',
|
|
1091
|
-
destinationType: CONSULT_TRANSFER_DESTINATION_TYPE.QUEUE,
|
|
1092
|
-
};
|
|
1093
|
-
|
|
1094
|
-
const expectedPayload = {
|
|
1095
|
-
to: taskDataMock.destAgentId,
|
|
1096
|
-
destinationType: CONSULT_TRANSFER_DESTINATION_TYPE.AGENT,
|
|
1097
|
-
};
|
|
1098
|
-
|
|
1099
|
-
const response = await task.consultTransfer(queueConsultTransferPayload);
|
|
1100
|
-
|
|
1101
|
-
expect(contactMock.consultTransfer).toHaveBeenCalledWith({
|
|
1102
|
-
interactionId: taskId,
|
|
1103
|
-
data: expectedPayload,
|
|
1104
|
-
});
|
|
1105
|
-
expect(response).toEqual(expectedResponse);
|
|
1106
|
-
});
|
|
1107
|
-
|
|
1108
|
-
it('should throw error when attempting to transfer to queue with no destAgentId', async () => {
|
|
1109
|
-
const taskWithoutDestAgentId = new Task(contactMock, webCallingService, {
|
|
1110
|
-
...taskDataMock,
|
|
1111
|
-
destAgentId: undefined,
|
|
1112
|
-
}, {
|
|
1113
|
-
wrapUpProps: { wrapUpReasonList: [] },
|
|
1114
|
-
autoWrapEnabled: false,
|
|
1115
|
-
autoWrapAfterSeconds: 0
|
|
1116
|
-
}, taskDataMock.agentId);
|
|
1117
|
-
|
|
1118
|
-
const queueConsultTransferPayload: ConsultTransferPayLoad = {
|
|
1119
|
-
to: 'some-queue-id',
|
|
1120
|
-
destinationType: CONSULT_TRANSFER_DESTINATION_TYPE.QUEUE,
|
|
1121
|
-
};
|
|
1122
|
-
|
|
1123
|
-
// For this negative case, ensure computed destination is empty
|
|
1124
|
-
calculateDestAgentIdSpy.mockReturnValueOnce('');
|
|
1125
|
-
|
|
1126
|
-
await expect(
|
|
1127
|
-
taskWithoutDestAgentId.consultTransfer(queueConsultTransferPayload)
|
|
1128
|
-
).rejects.toThrow('No agent has accepted this queue consult yet');
|
|
1129
|
-
});
|
|
1130
|
-
|
|
1131
|
-
describe('consultTransfer', () => {
|
|
1132
|
-
it('should successfully perform consult transfer with agent destination', async () => {
|
|
1133
|
-
const expectedResponse: TaskResponse = {
|
|
1134
|
-
data: {interactionId: taskId},
|
|
1135
|
-
trackingId: 'test-tracking-id'
|
|
1136
|
-
} as AgentContact;
|
|
1137
|
-
contactMock.consultTransfer.mockResolvedValue(expectedResponse);
|
|
1138
|
-
|
|
1139
|
-
calculateDestTypeSpy.mockReturnValue(CONSULT_TRANSFER_DESTINATION_TYPE.AGENT);
|
|
1140
|
-
|
|
1141
|
-
const result = await task.consultTransfer();
|
|
1142
|
-
|
|
1143
|
-
expect(calculateDestAgentIdSpy).toHaveBeenCalledWith(taskDataMock.interaction, taskDataMock.agentId);
|
|
1144
|
-
expect(calculateDestTypeSpy).toHaveBeenCalledWith(taskDataMock.interaction, taskDataMock.agentId);
|
|
1145
|
-
expect(contactMock.consultTransfer).toHaveBeenCalledWith({
|
|
1146
|
-
interactionId: taskId,
|
|
1147
|
-
data: {
|
|
1148
|
-
to: taskDataMock.destAgentId,
|
|
1149
|
-
destinationType: CONSULT_TRANSFER_DESTINATION_TYPE.AGENT,
|
|
1150
|
-
},
|
|
1151
|
-
});
|
|
1152
|
-
expect(result).toEqual(expectedResponse);
|
|
1153
|
-
expect(loggerInfoSpy).toHaveBeenCalledWith(
|
|
1154
|
-
`Initiating consult transfer to ${taskDataMock.destAgentId}`,
|
|
1155
|
-
{
|
|
1156
|
-
module: TASK_FILE,
|
|
1157
|
-
method: 'consultTransfer',
|
|
1158
|
-
interactionId: taskId,
|
|
1159
|
-
}
|
|
1160
|
-
);
|
|
1161
|
-
expect(loggerLogSpy).toHaveBeenCalledWith(
|
|
1162
|
-
`Consult transfer completed successfully to ${taskDataMock.destAgentId}`,
|
|
1163
|
-
{
|
|
1164
|
-
module: TASK_FILE,
|
|
1165
|
-
method: 'consultTransfer',
|
|
1166
|
-
trackingId: expectedResponse.trackingId,
|
|
1167
|
-
interactionId: taskId,
|
|
1168
|
-
}
|
|
1169
|
-
);
|
|
1170
|
-
});
|
|
1171
|
-
|
|
1172
|
-
it('should track metrics on successful consult transfer', async () => {
|
|
1173
|
-
const expectedResponse: TaskResponse = {
|
|
1174
|
-
data: {interactionId: taskId},
|
|
1175
|
-
trackingId: 'test-tracking-id'
|
|
1176
|
-
} as AgentContact;
|
|
1177
|
-
contactMock.consultTransfer.mockResolvedValue(expectedResponse);
|
|
1178
|
-
|
|
1179
|
-
await task.consultTransfer();
|
|
1180
|
-
|
|
1181
|
-
expect(mockMetricsManager.trackEvent).toHaveBeenCalledWith(
|
|
1182
|
-
METRIC_EVENT_NAMES.TASK_TRANSFER_SUCCESS,
|
|
1183
|
-
{
|
|
1184
|
-
taskId: taskDataMock.interactionId,
|
|
1185
|
-
destination: taskDataMock.destAgentId,
|
|
1186
|
-
destinationType: 'agent',
|
|
1187
|
-
isConsultTransfer: true,
|
|
1188
|
-
...MetricsManager.getCommonTrackingFieldForAQMResponse(expectedResponse),
|
|
1189
|
-
},
|
|
1190
|
-
['operational', 'behavioral', 'business']
|
|
1191
|
-
);
|
|
1192
|
-
});
|
|
1193
|
-
|
|
1194
|
-
it('should throw error when no destination agent is found', async () => {
|
|
1195
|
-
calculateDestAgentIdSpy.mockReturnValue('');
|
|
1196
|
-
|
|
1197
|
-
await expect(task.consultTransfer()).rejects.toThrow('No agent has accepted this queue consult yet');
|
|
1198
|
-
|
|
1199
|
-
expect(contactMock.consultTransfer).not.toHaveBeenCalled();
|
|
1200
|
-
});
|
|
1201
|
-
|
|
1202
|
-
it('should handle and rethrow contact method errors', async () => {
|
|
1203
|
-
const mockError = new Error('Consult Transfer Failed');
|
|
1204
|
-
contactMock.consultTransfer.mockRejectedValue(mockError);
|
|
1205
|
-
generateTaskErrorObjectSpy.mockReturnValue(mockError);
|
|
1206
|
-
|
|
1207
|
-
await expect(task.consultTransfer()).rejects.toThrow('Consult Transfer Failed');
|
|
1208
|
-
|
|
1209
|
-
expect(generateTaskErrorObjectSpy).toHaveBeenCalledWith(mockError, 'consultTransfer', TASK_FILE);
|
|
1210
|
-
expect(mockMetricsManager.trackEvent).toHaveBeenCalledWith(
|
|
1211
|
-
METRIC_EVENT_NAMES.TASK_TRANSFER_FAILED,
|
|
1212
|
-
expect.objectContaining({
|
|
1213
|
-
taskId: taskDataMock.interactionId,
|
|
1214
|
-
destination: taskDataMock.destAgentId,
|
|
1215
|
-
destinationType: 'agent',
|
|
1216
|
-
isConsultTransfer: true,
|
|
1217
|
-
error: mockError.toString(),
|
|
1218
|
-
}),
|
|
1219
|
-
['operational', 'behavioral', 'business']
|
|
1220
|
-
);
|
|
1221
|
-
});
|
|
1222
|
-
|
|
1223
|
-
it('should dynamically calculate destAgentId when not available', async () => {
|
|
1224
|
-
const consultedAgentId = 'dynamic-agent-123';
|
|
1225
|
-
calculateDestAgentIdSpy.mockReturnValue(consultedAgentId);
|
|
1226
|
-
|
|
1227
|
-
const expectedResponse: TaskResponse = {data: {interactionId: taskId}} as AgentContact;
|
|
1228
|
-
contactMock.consultTransfer.mockResolvedValue(expectedResponse);
|
|
1229
|
-
|
|
1230
|
-
await task.consultTransfer();
|
|
1231
|
-
|
|
1232
|
-
expect(calculateDestAgentIdSpy).toHaveBeenCalledWith(taskDataMock.interaction, taskDataMock.agentId);
|
|
1233
|
-
expect(contactMock.consultTransfer).toHaveBeenCalledWith({
|
|
1234
|
-
interactionId: taskId,
|
|
1235
|
-
data: {
|
|
1236
|
-
to: consultedAgentId,
|
|
1237
|
-
destinationType: 'agent',
|
|
1238
|
-
},
|
|
1239
|
-
});
|
|
1240
|
-
});
|
|
1241
|
-
});
|
|
1242
|
-
|
|
1243
|
-
it('should do vteamTransfer if destinationType is queue and return the expected response', async () => {
|
|
1244
|
-
const transferPayload: TransferPayLoad = {
|
|
1245
|
-
to: '1234',
|
|
1246
|
-
destinationType: DESTINATION_TYPE.QUEUE,
|
|
1247
|
-
};
|
|
1248
|
-
|
|
1249
|
-
const expectedResponse: TaskResponse = {data: {interactionId: taskId}} as AgentContact;
|
|
1250
|
-
contactMock.vteamTransfer.mockResolvedValue(expectedResponse);
|
|
1251
|
-
|
|
1252
|
-
const response = await task.transfer(transferPayload);
|
|
1253
|
-
|
|
1254
|
-
expect(contactMock.vteamTransfer).toHaveBeenCalledWith({
|
|
1255
|
-
interactionId: taskId,
|
|
1256
|
-
data: transferPayload,
|
|
1257
|
-
});
|
|
1258
|
-
expect(response).toEqual(expectedResponse);
|
|
1259
|
-
expect(mockMetricsManager.trackEvent).toHaveBeenNthCalledWith(
|
|
1260
|
-
1,
|
|
1261
|
-
METRIC_EVENT_NAMES.TASK_TRANSFER_SUCCESS,
|
|
1262
|
-
{
|
|
1263
|
-
taskId: taskDataMock.interactionId,
|
|
1264
|
-
destination: transferPayload.to,
|
|
1265
|
-
destinationType: transferPayload.destinationType,
|
|
1266
|
-
isConsultTransfer: false,
|
|
1267
|
-
...MetricsManager.getCommonTrackingFieldForAQMResponse(expectedResponse),
|
|
1268
|
-
},
|
|
1269
|
-
['operational', 'behavioral', 'business']
|
|
1270
|
-
);
|
|
1271
|
-
});
|
|
1272
|
-
|
|
1273
|
-
it('should do blindTransfer if destinationType is anything other than queue and return the expected response', async () => {
|
|
1274
|
-
const transferPayload: TransferPayLoad = {
|
|
1275
|
-
to: '1234',
|
|
1276
|
-
destinationType: DESTINATION_TYPE.AGENT,
|
|
1277
|
-
};
|
|
1278
|
-
|
|
1279
|
-
const expectedResponse: TaskResponse = {data: {interactionId: taskId}} as AgentContact;
|
|
1280
|
-
contactMock.blindTransfer.mockResolvedValue(expectedResponse);
|
|
1281
|
-
|
|
1282
|
-
const response = await task.transfer(transferPayload);
|
|
1283
|
-
|
|
1284
|
-
expect(contactMock.blindTransfer).toHaveBeenCalledWith({
|
|
1285
|
-
interactionId: taskId,
|
|
1286
|
-
data: transferPayload,
|
|
1287
|
-
});
|
|
1288
|
-
expect(response).toEqual(expectedResponse);
|
|
1289
|
-
expect(mockMetricsManager.trackEvent).toHaveBeenNthCalledWith(
|
|
1290
|
-
1,
|
|
1291
|
-
METRIC_EVENT_NAMES.TASK_TRANSFER_SUCCESS,
|
|
1292
|
-
{
|
|
1293
|
-
taskId: taskDataMock.interactionId,
|
|
1294
|
-
destination: transferPayload.to,
|
|
1295
|
-
destinationType: transferPayload.destinationType,
|
|
1296
|
-
isConsultTransfer: false,
|
|
1297
|
-
...MetricsManager.getCommonTrackingFieldForAQMResponse(expectedResponse),
|
|
1298
|
-
},
|
|
1299
|
-
['operational', 'behavioral', 'business']
|
|
1300
|
-
);
|
|
1301
|
-
});
|
|
1302
|
-
|
|
1303
|
-
it('should handle errors in transfer method', async () => {
|
|
1304
|
-
const error = {details: (global as any).makeFailure('Consult Transfer Failed')};
|
|
1305
|
-
contactMock.blindTransfer.mockImplementation(() => {
|
|
1306
|
-
throw error;
|
|
1307
|
-
});
|
|
1308
|
-
|
|
1309
|
-
const blindTransferPayload: TransferPayLoad = {
|
|
1310
|
-
to: '1234',
|
|
1311
|
-
destinationType: DESTINATION_TYPE.AGENT,
|
|
1312
|
-
};
|
|
1313
|
-
|
|
1314
|
-
await expect(task.transfer(blindTransferPayload)).rejects.toThrow(error.details.data.reason);
|
|
1315
|
-
expect(generateTaskErrorObjectSpy).toHaveBeenCalledWith(error, 'transfer', TASK_FILE);
|
|
1316
|
-
const expectedTaskErrorFieldsTransfer = {
|
|
1317
|
-
trackingId: error.details.trackingId,
|
|
1318
|
-
errorMessage: error.details.data.reason,
|
|
1319
|
-
errorType: '',
|
|
1320
|
-
errorData: '',
|
|
1321
|
-
reasonCode: 0,
|
|
1322
|
-
};
|
|
1323
|
-
expect(mockMetricsManager.trackEvent).toHaveBeenNthCalledWith(
|
|
1324
|
-
1,
|
|
1325
|
-
METRIC_EVENT_NAMES.TASK_TRANSFER_FAILED,
|
|
1326
|
-
{
|
|
1327
|
-
taskId: taskDataMock.interactionId,
|
|
1328
|
-
destination: blindTransferPayload.to,
|
|
1329
|
-
destinationType: blindTransferPayload.destinationType,
|
|
1330
|
-
isConsultTransfer: false,
|
|
1331
|
-
error: error.toString(),
|
|
1332
|
-
...expectedTaskErrorFieldsTransfer,
|
|
1333
|
-
...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details),
|
|
1334
|
-
},
|
|
1335
|
-
['operational', 'behavioral', 'business']
|
|
1336
|
-
);
|
|
1337
|
-
});
|
|
1338
|
-
|
|
1339
|
-
it('should end the task and return the expected response', async () => {
|
|
1340
|
-
const expectedResponse: TaskResponse = {data: {interactionId: taskId}} as AgentContact;
|
|
1341
|
-
contactMock.end.mockResolvedValue(expectedResponse);
|
|
1342
|
-
|
|
1343
|
-
const response = await task.end();
|
|
1344
|
-
|
|
1345
|
-
expect(contactMock.end).toHaveBeenCalledWith({interactionId: taskId});
|
|
1346
|
-
expect(response).toEqual(expectedResponse);
|
|
1347
|
-
expect(loggerInfoSpy).toHaveBeenCalledWith(`Ending task`, {
|
|
1348
|
-
module: TASK_FILE,
|
|
1349
|
-
method: 'end',
|
|
1350
|
-
interactionId: expectedResponse.data.interactionId,
|
|
1351
|
-
});
|
|
1352
|
-
expect(loggerLogSpy).toHaveBeenCalledWith(`Task ended successfully`, {
|
|
1353
|
-
module: TASK_FILE,
|
|
1354
|
-
method: 'end',
|
|
1355
|
-
interactionId: expectedResponse.data.interactionId,
|
|
1356
|
-
});
|
|
1357
|
-
expect(mockMetricsManager.trackEvent).toHaveBeenNthCalledWith(
|
|
1358
|
-
1,
|
|
1359
|
-
METRIC_EVENT_NAMES.TASK_END_SUCCESS,
|
|
1360
|
-
{
|
|
1361
|
-
taskId: taskDataMock.interactionId,
|
|
1362
|
-
...MetricsManager.getCommonTrackingFieldForAQMResponse(expectedResponse),
|
|
1363
|
-
},
|
|
1364
|
-
['operational', 'behavioral', 'business']
|
|
1365
|
-
);
|
|
1366
|
-
});
|
|
1367
|
-
|
|
1368
|
-
it('should handle errors in end method', async () => {
|
|
1369
|
-
const error = {details: (global as any).makeFailure('End Failed')};
|
|
1370
|
-
contactMock.end.mockImplementation(() => {
|
|
1371
|
-
throw error;
|
|
1372
|
-
});
|
|
1373
|
-
|
|
1374
|
-
await expect(task.end()).rejects.toThrow(error.details.data.reason);
|
|
1375
|
-
expect(generateTaskErrorObjectSpy).toHaveBeenCalledWith(error, 'end', TASK_FILE);
|
|
1376
|
-
const expectedTaskErrorFieldsEnd = {
|
|
1377
|
-
trackingId: error.details.trackingId,
|
|
1378
|
-
errorMessage: error.details.data.reason,
|
|
1379
|
-
errorType: '',
|
|
1380
|
-
errorData: '',
|
|
1381
|
-
reasonCode: 0,
|
|
1382
|
-
};
|
|
1383
|
-
expect(mockMetricsManager.trackEvent).toHaveBeenNthCalledWith(
|
|
1384
|
-
1,
|
|
1385
|
-
METRIC_EVENT_NAMES.TASK_END_FAILED,
|
|
1386
|
-
{
|
|
1387
|
-
taskId: taskDataMock.interactionId,
|
|
1388
|
-
...expectedTaskErrorFieldsEnd,
|
|
1389
|
-
...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details),
|
|
1390
|
-
},
|
|
1391
|
-
['operational', 'behavioral', 'business']
|
|
1392
|
-
);
|
|
1393
|
-
});
|
|
1394
|
-
|
|
1395
|
-
it('should wrap up the task and return the expected response', async () => {
|
|
1396
|
-
const expectedResponse: TaskResponse = {data: {interactionId: taskId}} as AgentContact;
|
|
1397
|
-
const wrapupPayload = {
|
|
1398
|
-
wrapUpReason: 'Customer request',
|
|
1399
|
-
auxCodeId: 'auxCodeId123',
|
|
1400
|
-
};
|
|
1401
|
-
contactMock.wrapup.mockResolvedValue(expectedResponse);
|
|
1402
|
-
|
|
1403
|
-
const response = await task.wrapup(wrapupPayload);
|
|
1404
|
-
|
|
1405
|
-
expect(contactMock.wrapup).toHaveBeenCalledWith({interactionId: taskId, data: wrapupPayload});
|
|
1406
|
-
expect(response).toEqual(expectedResponse);
|
|
1407
|
-
expect(mockMetricsManager.trackEvent).toHaveBeenNthCalledWith(
|
|
1408
|
-
1,
|
|
1409
|
-
METRIC_EVENT_NAMES.TASK_WRAPUP_SUCCESS,
|
|
1410
|
-
{
|
|
1411
|
-
taskId: taskDataMock.interactionId,
|
|
1412
|
-
wrapUpCode: wrapupPayload.auxCodeId,
|
|
1413
|
-
wrapUpReason: wrapupPayload.wrapUpReason,
|
|
1414
|
-
...MetricsManager.getCommonTrackingFieldForAQMResponse(expectedResponse),
|
|
1415
|
-
},
|
|
1416
|
-
['operational', 'behavioral', 'business']
|
|
1417
|
-
);
|
|
1418
|
-
});
|
|
1419
|
-
|
|
1420
|
-
it('should handle errors in wrapup method', async () => {
|
|
1421
|
-
const error = {details: (global as any).makeFailure('Wrapup Failed')};
|
|
1422
|
-
contactMock.wrapup.mockImplementation(() => {
|
|
1423
|
-
throw error;
|
|
1424
|
-
});
|
|
1425
|
-
|
|
1426
|
-
const wrapupPayload = {
|
|
1427
|
-
wrapUpReason: 'Customer request',
|
|
1428
|
-
auxCodeId: 'auxCodeId123',
|
|
1429
|
-
};
|
|
1430
|
-
|
|
1431
|
-
await expect(task.wrapup(wrapupPayload)).rejects.toThrow(error.details.data.reason);
|
|
1432
|
-
expect(generateTaskErrorObjectSpy).toHaveBeenCalledWith(error, 'wrapup', TASK_FILE);
|
|
1433
|
-
const expectedTaskErrorFieldsWrapup = {
|
|
1434
|
-
trackingId: error.details.trackingId,
|
|
1435
|
-
errorMessage: error.details.data.reason,
|
|
1436
|
-
errorType: '',
|
|
1437
|
-
errorData: '',
|
|
1438
|
-
reasonCode: 0,
|
|
1439
|
-
};
|
|
1440
|
-
expect(mockMetricsManager.trackEvent).toHaveBeenNthCalledWith(
|
|
1441
|
-
1,
|
|
1442
|
-
METRIC_EVENT_NAMES.TASK_WRAPUP_FAILED,
|
|
1443
|
-
{
|
|
1444
|
-
taskId: taskDataMock.interactionId,
|
|
1445
|
-
wrapUpCode: wrapupPayload.auxCodeId,
|
|
1446
|
-
wrapUpReason: wrapupPayload.wrapUpReason,
|
|
1447
|
-
...expectedTaskErrorFieldsWrapup,
|
|
1448
|
-
...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details),
|
|
1449
|
-
},
|
|
1450
|
-
['operational', 'behavioral', 'business']
|
|
1451
|
-
);
|
|
1452
|
-
});
|
|
1453
|
-
|
|
1454
|
-
it('should throw an error if auxCodeId is missing in wrapup method', async () => {
|
|
1455
|
-
const wrapupPayload = {
|
|
1456
|
-
wrapUpReason: 'Customer request',
|
|
1457
|
-
auxCodeId: '',
|
|
1458
|
-
};
|
|
1459
|
-
await expect(task.wrapup(wrapupPayload)).rejects.toThrow();
|
|
1460
|
-
});
|
|
1461
|
-
|
|
1462
|
-
it('should throw an error if wrapUpReason is missing in wrapup method', async () => {
|
|
1463
|
-
const wrapupPayload = {
|
|
1464
|
-
wrapUpReason: '',
|
|
1465
|
-
auxCodeId: 'auxCodeId123',
|
|
1466
|
-
};
|
|
1467
|
-
await expect(task.wrapup(wrapupPayload)).rejects.toThrow();
|
|
1468
|
-
});
|
|
1469
|
-
|
|
1470
|
-
it('should throw an error if this.data is missing when wrapup is invoked', async () => {
|
|
1471
|
-
const wrapupPayload = {
|
|
1472
|
-
wrapUpReason: 'Customer request',
|
|
1473
|
-
auxCodeId: 'auxCodeId123',
|
|
1474
|
-
};
|
|
1475
|
-
|
|
1476
|
-
task.data = undefined;
|
|
1477
|
-
await expect(task.wrapup(wrapupPayload)).rejects.toThrow();
|
|
1478
|
-
});
|
|
1479
|
-
|
|
1480
|
-
it('should pause the recording of the task', async () => {
|
|
1481
|
-
await task.pauseRecording();
|
|
1482
|
-
|
|
1483
|
-
expect(contactMock.pauseRecording).toHaveBeenCalledWith({interactionId: taskId});
|
|
1484
|
-
expect(loggerInfoSpy).toHaveBeenCalledWith(`Pausing recording`, {
|
|
1485
|
-
module: TASK_FILE,
|
|
1486
|
-
method: 'pauseRecording',
|
|
1487
|
-
interactionId: task.data.interactionId,
|
|
1488
|
-
});
|
|
1489
|
-
expect(loggerLogSpy).toHaveBeenCalledWith(`Recording paused successfully`, {
|
|
1490
|
-
module: TASK_FILE,
|
|
1491
|
-
method: 'pauseRecording',
|
|
1492
|
-
interactionId: task.data.interactionId,
|
|
1493
|
-
});
|
|
1494
|
-
expect(mockMetricsManager.trackEvent).toHaveBeenNthCalledWith(
|
|
1495
|
-
1,
|
|
1496
|
-
METRIC_EVENT_NAMES.TASK_PAUSE_RECORDING_SUCCESS,
|
|
1497
|
-
{
|
|
1498
|
-
taskId: taskDataMock.interactionId,
|
|
1499
|
-
},
|
|
1500
|
-
['operational', 'behavioral', 'business']
|
|
1501
|
-
);
|
|
1502
|
-
});
|
|
1503
|
-
|
|
1504
|
-
it('should handle errors in pauseRecording method', async () => {
|
|
1505
|
-
const error = {details: (global as any).makeFailure('Pause Recording Failed')};
|
|
1506
|
-
contactMock.pauseRecording.mockImplementation(() => {
|
|
1507
|
-
throw error;
|
|
1508
|
-
});
|
|
1509
|
-
|
|
1510
|
-
await expect(task.pauseRecording()).rejects.toThrow(error.details.data.reason);
|
|
1511
|
-
expect(generateTaskErrorObjectSpy).toHaveBeenCalledWith(error, 'pauseRecording', TASK_FILE);
|
|
1512
|
-
const expectedTaskErrorFieldsPause = {
|
|
1513
|
-
trackingId: error.details.trackingId,
|
|
1514
|
-
errorMessage: error.details.data.reason,
|
|
1515
|
-
errorType: '',
|
|
1516
|
-
errorData: '',
|
|
1517
|
-
reasonCode: 0,
|
|
1518
|
-
};
|
|
1519
|
-
expect(mockMetricsManager.trackEvent).toHaveBeenNthCalledWith(
|
|
1520
|
-
1,
|
|
1521
|
-
METRIC_EVENT_NAMES.TASK_PAUSE_RECORDING_FAILED,
|
|
1522
|
-
{
|
|
1523
|
-
taskId: taskDataMock.interactionId,
|
|
1524
|
-
error: error.toString(),
|
|
1525
|
-
...expectedTaskErrorFieldsPause,
|
|
1526
|
-
...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details),
|
|
1527
|
-
},
|
|
1528
|
-
['operational', 'behavioral', 'business']
|
|
1529
|
-
);
|
|
1530
|
-
});
|
|
1531
|
-
|
|
1532
|
-
it('should resume the recording of the task', async () => {
|
|
1533
|
-
const resumePayload = {
|
|
1534
|
-
autoResumed: true,
|
|
1535
|
-
interactionId: taskId,
|
|
1536
|
-
};
|
|
1537
|
-
|
|
1538
|
-
await task.resumeRecording(resumePayload);
|
|
1539
|
-
|
|
1540
|
-
expect(contactMock.resumeRecording).toHaveBeenCalledWith({
|
|
1541
|
-
interactionId: resumePayload.interactionId,
|
|
1542
|
-
data: resumePayload,
|
|
1543
|
-
});
|
|
1544
|
-
expect(loggerInfoSpy).toHaveBeenCalledWith(`Resuming recording`, {
|
|
1545
|
-
module: TASK_FILE,
|
|
1546
|
-
method: 'resumeRecording',
|
|
1547
|
-
interactionId: task.data.interactionId,
|
|
1548
|
-
});
|
|
1549
|
-
expect(loggerLogSpy).toHaveBeenCalledWith(`Recording resumed successfully`, {
|
|
1550
|
-
module: TASK_FILE,
|
|
1551
|
-
method: 'resumeRecording',
|
|
1552
|
-
interactionId: task.data.interactionId,
|
|
1553
|
-
});
|
|
1554
|
-
expect(mockMetricsManager.trackEvent).toHaveBeenNthCalledWith(
|
|
1555
|
-
1,
|
|
1556
|
-
METRIC_EVENT_NAMES.TASK_RESUME_RECORDING_SUCCESS,
|
|
1557
|
-
{
|
|
1558
|
-
taskId: taskDataMock.interactionId,
|
|
1559
|
-
},
|
|
1560
|
-
['operational', 'behavioral', 'business']
|
|
1561
|
-
);
|
|
1562
|
-
});
|
|
1563
|
-
|
|
1564
|
-
it('should resume the recording of the task if the payload is empty', async () => {
|
|
1565
|
-
const resumePayload = {
|
|
1566
|
-
autoResumed: false,
|
|
1567
|
-
};
|
|
1568
|
-
|
|
1569
|
-
await task.resumeRecording();
|
|
1570
|
-
|
|
1571
|
-
expect(contactMock.resumeRecording).toHaveBeenCalledWith({
|
|
1572
|
-
interactionId: taskId,
|
|
1573
|
-
data: resumePayload,
|
|
1574
|
-
});
|
|
1575
|
-
expect(mockMetricsManager.trackEvent).toHaveBeenNthCalledWith(
|
|
1576
|
-
1,
|
|
1577
|
-
METRIC_EVENT_NAMES.TASK_RESUME_RECORDING_SUCCESS,
|
|
1578
|
-
{
|
|
1579
|
-
taskId: taskDataMock.interactionId,
|
|
1580
|
-
},
|
|
1581
|
-
['operational', 'behavioral', 'business']
|
|
1582
|
-
);
|
|
1583
|
-
});
|
|
1584
|
-
|
|
1585
|
-
it('should handle errors in resumeRecording method', async () => {
|
|
1586
|
-
const error = {details: (global as any).makeFailure('Resume Recording Failed')};
|
|
1587
|
-
contactMock.resumeRecording.mockImplementation(() => {
|
|
1588
|
-
throw error;
|
|
1589
|
-
});
|
|
1590
|
-
|
|
1591
|
-
const resumePayload = {
|
|
1592
|
-
autoResumed: true,
|
|
1593
|
-
};
|
|
1594
|
-
|
|
1595
|
-
await expect(task.resumeRecording(resumePayload)).rejects.toThrow(error.details.data.reason);
|
|
1596
|
-
expect(generateTaskErrorObjectSpy).toHaveBeenCalledWith(error, 'resumeRecording', TASK_FILE);
|
|
1597
|
-
const expectedTaskErrorFieldsResumeRec = {
|
|
1598
|
-
trackingId: error.details.trackingId,
|
|
1599
|
-
errorMessage: error.details.data.reason,
|
|
1600
|
-
errorType: '',
|
|
1601
|
-
errorData: '',
|
|
1602
|
-
reasonCode: 0,
|
|
1603
|
-
};
|
|
1604
|
-
expect(mockMetricsManager.trackEvent).toHaveBeenNthCalledWith(
|
|
1605
|
-
1,
|
|
1606
|
-
METRIC_EVENT_NAMES.TASK_RESUME_RECORDING_FAILED,
|
|
1607
|
-
{
|
|
1608
|
-
taskId: taskDataMock.interactionId,
|
|
1609
|
-
error: error.toString(),
|
|
1610
|
-
...expectedTaskErrorFieldsResumeRec,
|
|
1611
|
-
...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details),
|
|
1612
|
-
},
|
|
1613
|
-
['operational', 'behavioral', 'business']
|
|
1614
|
-
);
|
|
1615
|
-
});
|
|
1616
|
-
|
|
1617
|
-
it('should mute call for Desktop login mode', async () => {
|
|
1618
|
-
task.localAudioStream = mockStream;
|
|
1619
|
-
const muteCallSpy = jest.spyOn(webCallingService, 'muteUnmuteCall');
|
|
1620
|
-
|
|
1621
|
-
await task.toggleMute();
|
|
1622
|
-
|
|
1623
|
-
expect(muteCallSpy).toHaveBeenCalledWith(mockStream);
|
|
1624
|
-
expect(loggerInfoSpy).toHaveBeenCalledWith(`Toggling mute state`, {
|
|
1625
|
-
module: TASK_FILE,
|
|
1626
|
-
method: 'toggleMute',
|
|
1627
|
-
interactionId: task.data.interactionId,
|
|
1628
|
-
});
|
|
1629
|
-
expect(loggerLogSpy).toHaveBeenCalledWith(`Mute state toggled successfully isCallMuted: ${webCallingService.isCallMuted()}`, {
|
|
1630
|
-
module: TASK_FILE,
|
|
1631
|
-
method: 'toggleMute',
|
|
1632
|
-
interactionId: task.data.interactionId,
|
|
1633
|
-
});
|
|
1634
|
-
});
|
|
1635
|
-
|
|
1636
|
-
it('should handle errors in mute method', async () => {
|
|
1637
|
-
const error = {
|
|
1638
|
-
details: {
|
|
1639
|
-
trackingId: '1234',
|
|
1640
|
-
data: {
|
|
1641
|
-
reason: 'Mute Failed',
|
|
1642
|
-
},
|
|
1643
|
-
},
|
|
1644
|
-
};
|
|
1645
|
-
|
|
1646
|
-
jest.spyOn(webCallingService, 'muteUnmuteCall').mockImplementation(() => {
|
|
1647
|
-
throw error;
|
|
1648
|
-
});
|
|
1649
|
-
await expect(task.toggleMute()).rejects.toThrow(new Error(error.details.data.reason));
|
|
1650
|
-
expect(generateTaskErrorObjectSpy).toHaveBeenCalledWith(error, 'toggleMute', TASK_FILE);
|
|
1651
|
-
expect(loggerInfoSpy).toHaveBeenCalledWith(`Toggling mute state`, {
|
|
1652
|
-
module: TASK_FILE,
|
|
1653
|
-
method: 'toggleMute',
|
|
1654
|
-
interactionId: task.data.interactionId,
|
|
1655
|
-
});
|
|
1656
|
-
});
|
|
1657
|
-
|
|
1658
|
-
describe('AutoWrapup initialization tests', () => {
|
|
1659
|
-
beforeEach(() => {
|
|
1660
|
-
jest.useFakeTimers();
|
|
1661
|
-
});
|
|
1662
|
-
|
|
1663
|
-
afterEach(() => {
|
|
1664
|
-
jest.restoreAllMocks();
|
|
1665
|
-
jest.useRealTimers();
|
|
1666
|
-
});
|
|
1667
|
-
|
|
1668
|
-
it('should not initialize AutoWrapup if wrapUpRequired is false', () => {
|
|
1669
|
-
const wrapupProps = {
|
|
1670
|
-
wrapUpProps: {
|
|
1671
|
-
autoWrapup: true,
|
|
1672
|
-
autoWrapupInterval: 5000,
|
|
1673
|
-
wrapUpReasonList: [{ isDefault: true, name: 'Default Reason', id: '123', isSystem: false }]
|
|
1674
|
-
}
|
|
1675
|
-
};
|
|
1676
|
-
|
|
1677
|
-
const taskData = { ...taskDataMock, wrapUpRequired: false };
|
|
1678
|
-
const taskInstance = new Task(contactMock, webCallingService, taskData, wrapupProps);
|
|
1679
|
-
|
|
1680
|
-
expect(taskInstance.autoWrapup).toBeUndefined();
|
|
1681
|
-
});
|
|
1682
|
-
|
|
1683
|
-
it('should not initialize AutoWrapup if autoWrapup is set to false', () => {
|
|
1684
|
-
const wrapupProps = {
|
|
1685
|
-
wrapUpProps: {
|
|
1686
|
-
autoWrapup: false,
|
|
1687
|
-
autoWrapupInterval: 5000,
|
|
1688
|
-
wrapUpReasonList: [{ isDefault: true, name: 'Default Reason', id: '123', isSystem: false }]
|
|
1689
|
-
}
|
|
1690
|
-
};
|
|
1691
|
-
|
|
1692
|
-
const taskData = { ...taskDataMock, wrapUpRequired: true };
|
|
1693
|
-
const taskInstance = new Task(contactMock, webCallingService, taskData, wrapupProps);
|
|
1694
|
-
|
|
1695
|
-
expect(taskInstance.autoWrapup).toBeUndefined();
|
|
1696
|
-
expect(loggerInfoSpy).toHaveBeenCalledWith('Auto wrap-up is not required for this task', {
|
|
1697
|
-
module: TASK_FILE,
|
|
1698
|
-
method: 'setupAutoWrapupTimer',
|
|
1699
|
-
interactionId: taskData.interactionId,
|
|
1700
|
-
});
|
|
1701
|
-
});
|
|
1702
|
-
|
|
1703
|
-
it('should initialize AutoWrapup with custom interval when specified', () => {
|
|
1704
|
-
const customInterval = 15000;
|
|
1705
|
-
const wrapupProps = {
|
|
1706
|
-
wrapUpProps: {
|
|
1707
|
-
autoWrapup: true,
|
|
1708
|
-
autoWrapupInterval: customInterval,
|
|
1709
|
-
wrapUpReasonList: [{ isDefault: true, name: 'Default Reason', id: '123', isSystem: false }]
|
|
1710
|
-
}
|
|
1711
|
-
};
|
|
1712
|
-
|
|
1713
|
-
const taskData = { ...taskDataMock, wrapUpRequired: true };
|
|
1714
|
-
const taskInstance = new Task(contactMock, webCallingService, taskData, wrapupProps);
|
|
1715
|
-
|
|
1716
|
-
expect(taskInstance.autoWrapup).toBeDefined();
|
|
1717
|
-
});
|
|
1718
|
-
|
|
1719
|
-
it('should cancel AutoWrapup timer when wrapup is called', async () => {
|
|
1720
|
-
const wrapupProps = {
|
|
1721
|
-
wrapUpProps: {
|
|
1722
|
-
autoWrapup: true,
|
|
1723
|
-
autoWrapupInterval: 5000,
|
|
1724
|
-
wrapUpReasonList: [{ isDefault: true, name: 'Default Reason', id: '123', isSystem: false }]
|
|
1725
|
-
}
|
|
1726
|
-
};
|
|
1727
|
-
|
|
1728
|
-
const taskData = { ...taskDataMock, wrapUpRequired: true };
|
|
1729
|
-
const taskInstance = new Task(contactMock, webCallingService, taskData, wrapupProps);
|
|
1730
|
-
|
|
1731
|
-
// Mock the autoWrapup object and its clear method
|
|
1732
|
-
const clearSpy = jest.spyOn(taskInstance.autoWrapup, 'clear');
|
|
1733
|
-
|
|
1734
|
-
// Call wrapup method which should cancel the timer
|
|
1735
|
-
await taskInstance.wrapup({ wrapUpReason: 'Test Reason', auxCodeId: '123' });
|
|
1736
|
-
|
|
1737
|
-
// Verify that clear was called
|
|
1738
|
-
expect(clearSpy).toHaveBeenCalled();
|
|
1739
|
-
expect(loggerInfoSpy).toHaveBeenCalledWith('Auto wrap-up timer cancelled', {
|
|
1740
|
-
module: TASK_FILE,
|
|
1741
|
-
method: 'cancelAutoWrapupTimer',
|
|
1742
|
-
interactionId: taskData.interactionId,
|
|
1743
|
-
});
|
|
1744
|
-
});
|
|
1745
|
-
|
|
1746
|
-
it('should directly call cancelAutoWrapUpTimer successfully', () => {
|
|
1747
|
-
const wrapupProps = {
|
|
1748
|
-
wrapUpProps: {
|
|
1749
|
-
autoWrapup: true,
|
|
1750
|
-
autoWrapupInterval: 5000,
|
|
1751
|
-
wrapUpReasonList: [{ isDefault: true, name: 'Default Reason', id: '123', isSystem: false }]
|
|
1752
|
-
}
|
|
1753
|
-
};
|
|
1754
|
-
|
|
1755
|
-
const taskData = { ...taskDataMock, wrapUpRequired: true };
|
|
1756
|
-
const taskInstance = new Task(contactMock, webCallingService, taskData, wrapupProps);
|
|
1757
|
-
|
|
1758
|
-
const clearSpy = jest.spyOn(taskInstance.autoWrapup, 'clear');
|
|
1759
|
-
taskInstance.cancelAutoWrapupTimer();
|
|
1760
|
-
|
|
1761
|
-
expect(clearSpy).toHaveBeenCalled();
|
|
1762
|
-
expect(loggerInfoSpy).toHaveBeenCalledWith('Auto wrap-up timer cancelled', {
|
|
1763
|
-
module: TASK_FILE,
|
|
1764
|
-
method: 'cancelAutoWrapupTimer',
|
|
1765
|
-
interactionId: taskData.interactionId,
|
|
1766
|
-
});
|
|
1767
|
-
});
|
|
1768
|
-
|
|
1769
|
-
it('should use default interval when autoWrapupInterval is not specified', () => {
|
|
1770
|
-
const wrapupProps = {
|
|
1771
|
-
wrapUpProps: {
|
|
1772
|
-
autoWrapup: true,
|
|
1773
|
-
wrapUpReasonList: [{ isDefault: true, name: 'Default Reason', id: '123', isSystem: false }]
|
|
1774
|
-
}
|
|
1775
|
-
};
|
|
1776
|
-
|
|
1777
|
-
const taskData = { ...taskDataMock, wrapUpRequired: true };
|
|
1778
|
-
const taskInstance = new Task(contactMock, webCallingService, taskData, wrapupProps);
|
|
1779
|
-
|
|
1780
|
-
expect(taskInstance.autoWrapup).toBeDefined();
|
|
1781
|
-
});
|
|
1782
|
-
|
|
1783
|
-
it('should setup autoWrapup with a callback that executes wrapup', () => {
|
|
1784
|
-
// Create a task with AutoWrapup enabled and a default wrapup reason
|
|
1785
|
-
const defaultWrapUpReason = { isDefault: true, name: 'Default Reason', id: '123', isSystem: false };
|
|
1786
|
-
const wrapupProps = {
|
|
1787
|
-
wrapUpProps: {
|
|
1788
|
-
autoWrapup: true,
|
|
1789
|
-
autoWrapupInterval: 5000,
|
|
1790
|
-
wrapUpReasonList: [defaultWrapUpReason]
|
|
1791
|
-
}
|
|
1792
|
-
};
|
|
1793
|
-
|
|
1794
|
-
const taskData = { ...taskDataMock, wrapUpRequired: true };
|
|
1795
|
-
|
|
1796
|
-
let capturedCallback;
|
|
1797
|
-
jest.spyOn(global, 'setTimeout').mockImplementation((callback, timeout) => {
|
|
1798
|
-
capturedCallback = callback;
|
|
1799
|
-
return {} as any;
|
|
1800
|
-
});
|
|
1801
|
-
|
|
1802
|
-
// Create our task instance
|
|
1803
|
-
const taskInstance = new Task(contactMock, webCallingService, taskData, wrapupProps);
|
|
1804
|
-
|
|
1805
|
-
// Mock the wrapup method to verify it gets called with correct parameters
|
|
1806
|
-
const wrapupMock = jest.fn().mockResolvedValue({});
|
|
1807
|
-
taskInstance.wrapup = wrapupMock;
|
|
1808
|
-
|
|
1809
|
-
// Verify autoWrapup was initialized
|
|
1810
|
-
expect(taskInstance.autoWrapup).toBeDefined();
|
|
1811
|
-
|
|
1812
|
-
if (capturedCallback) {
|
|
1813
|
-
capturedCallback();
|
|
1814
|
-
}
|
|
1815
|
-
|
|
1816
|
-
// Verify wrapup was called with correct parameters
|
|
1817
|
-
expect(wrapupMock).toHaveBeenCalledWith({
|
|
1818
|
-
wrapUpReason: defaultWrapUpReason.name,
|
|
1819
|
-
auxCodeId: defaultWrapUpReason.id
|
|
1820
|
-
});
|
|
1821
|
-
});
|
|
1822
|
-
|
|
1823
|
-
it('should handle case when no default wrapup reason is found', () => {
|
|
1824
|
-
// Create a task with AutoWrapup enabled but NO default wrapup reason
|
|
1825
|
-
const wrapupProps = {
|
|
1826
|
-
wrapUpProps: {
|
|
1827
|
-
autoWrapup: true,
|
|
1828
|
-
autoWrapupInterval: 5000,
|
|
1829
|
-
wrapUpReasonList: [
|
|
1830
|
-
{ isDefault: false, name: 'Reason 1', id: '123', isSystem: false },
|
|
1831
|
-
{ isDefault: false, name: 'Reason 2', id: '456', isSystem: false }
|
|
1832
|
-
]
|
|
1833
|
-
}
|
|
1834
|
-
};
|
|
1835
|
-
|
|
1836
|
-
const taskData = { ...taskDataMock, wrapUpRequired: true };
|
|
1837
|
-
|
|
1838
|
-
// Create our task instance
|
|
1839
|
-
const taskInstance = new Task(contactMock, webCallingService, taskData, wrapupProps);
|
|
1840
|
-
|
|
1841
|
-
// Mock the wrapup method to verify if it gets called
|
|
1842
|
-
const wrapupSpy = jest.fn().mockResolvedValue({});
|
|
1843
|
-
taskInstance.wrapup = wrapupSpy;
|
|
1844
|
-
|
|
1845
|
-
jest.runOnlyPendingTimers();
|
|
1846
|
-
|
|
1847
|
-
// Verify wrapup was called with the first reason (since no default exists)
|
|
1848
|
-
expect(wrapupSpy).toHaveBeenCalledWith({
|
|
1849
|
-
wrapUpReason: wrapupProps.wrapUpProps.wrapUpReasonList[0].name,
|
|
1850
|
-
auxCodeId: wrapupProps.wrapUpProps.wrapUpReasonList[0].id
|
|
1851
|
-
});
|
|
1852
|
-
});
|
|
1853
|
-
});
|
|
1854
|
-
|
|
1855
|
-
describe('Conference methods', () => {
|
|
1856
|
-
beforeEach(() => {
|
|
1857
|
-
contactMock = {
|
|
1858
|
-
consultConference: jest.fn(),
|
|
1859
|
-
exitConference: jest.fn(),
|
|
1860
|
-
conferenceTransfer: jest.fn(),
|
|
1861
|
-
};
|
|
1862
|
-
|
|
1863
|
-
task = new Task(contactMock, webCallingService, taskDataMock, {
|
|
1864
|
-
wrapUpProps: { wrapUpReasonList: [] },
|
|
1865
|
-
autoWrapEnabled: false,
|
|
1866
|
-
autoWrapAfterSeconds: 0
|
|
1867
|
-
}, taskDataMock.agentId);
|
|
1868
|
-
});
|
|
1869
|
-
|
|
1870
|
-
describe('consultConference', () => {
|
|
1871
|
-
|
|
1872
|
-
it('should successfully start conference and emit event', async () => {
|
|
1873
|
-
const mockResponse = {
|
|
1874
|
-
trackingId: 'test-tracking-id',
|
|
1875
|
-
interactionId: taskId,
|
|
1876
|
-
};
|
|
1877
|
-
contactMock.consultConference.mockResolvedValue(mockResponse);
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
const result = await task.consultConference();
|
|
1881
|
-
|
|
1882
|
-
expect(contactMock.consultConference).toHaveBeenCalledWith({
|
|
1883
|
-
interactionId: taskId,
|
|
1884
|
-
data: {
|
|
1885
|
-
agentId: taskDataMock.agentId, // From task data agent ID
|
|
1886
|
-
to: taskDataMock.destAgentId, // From calculateDestAgentId() using task participants
|
|
1887
|
-
destinationType: 'agent', // From consultation data
|
|
1888
|
-
},
|
|
1889
|
-
});
|
|
1890
|
-
expect(result).toEqual(mockResponse);
|
|
1891
|
-
expect(LoggerProxy.info).toHaveBeenCalledWith(`Initiating consult conference to ${taskDataMock.destAgentId}`, {
|
|
1892
|
-
module: TASK_FILE,
|
|
1893
|
-
method: 'consultConference',
|
|
1894
|
-
interactionId: taskId,
|
|
1895
|
-
});
|
|
1896
|
-
expect(LoggerProxy.log).toHaveBeenCalledWith('Consult conference started successfully', {
|
|
1897
|
-
module: TASK_FILE,
|
|
1898
|
-
method: 'consultConference',
|
|
1899
|
-
interactionId: taskId,
|
|
1900
|
-
});
|
|
1901
|
-
});
|
|
1902
|
-
|
|
1903
|
-
it('should handle basic validation scenarios', async () => {
|
|
1904
|
-
// Agent Desktop logic validates data structure but not participant availability
|
|
1905
|
-
// This test confirms the method works with the Agent Desktop data flow
|
|
1906
|
-
const mockResponse = {
|
|
1907
|
-
trackingId: 'test-tracking-validation',
|
|
1908
|
-
interactionId: taskId,
|
|
1909
|
-
};
|
|
1910
|
-
contactMock.consultConference.mockResolvedValue(mockResponse);
|
|
1911
|
-
|
|
1912
|
-
const result = await task.consultConference();
|
|
1913
|
-
expect(result).toEqual(mockResponse);
|
|
1914
|
-
});
|
|
1915
|
-
|
|
1916
|
-
it('should handle and rethrow contact method errors', async () => {
|
|
1917
|
-
const mockError = new Error('Conference start failed');
|
|
1918
|
-
contactMock.consultConference.mockRejectedValue(mockError);
|
|
1919
|
-
generateTaskErrorObjectSpy.mockReturnValue(mockError);
|
|
1920
|
-
|
|
1921
|
-
await expect(task.consultConference()).rejects.toThrow('Conference start failed');
|
|
1922
|
-
expect(LoggerProxy.error).toHaveBeenCalledWith('Failed to start consult conference', {
|
|
1923
|
-
module: TASK_FILE,
|
|
1924
|
-
method: 'consultConference',
|
|
1925
|
-
interactionId: taskId,
|
|
1926
|
-
});
|
|
1927
|
-
});
|
|
1928
|
-
|
|
1929
|
-
it('should dynamically calculate destAgentId from participants when this.data.destAgentId is null', async () => {
|
|
1930
|
-
// Simulate scenario where destAgentId is not preserved (e.g., after hold/unhold)
|
|
1931
|
-
task.data.destAgentId = null;
|
|
1932
|
-
|
|
1933
|
-
const consultedAgentId = 'consulted-agent-123';
|
|
1934
|
-
calculateDestAgentIdSpy.mockReturnValue(consultedAgentId);
|
|
1935
|
-
|
|
1936
|
-
const mockResponse = {
|
|
1937
|
-
trackingId: 'test-tracking-dynamic',
|
|
1938
|
-
interactionId: taskId,
|
|
1939
|
-
};
|
|
1940
|
-
contactMock.consultConference.mockResolvedValue(mockResponse);
|
|
1941
|
-
|
|
1942
|
-
const result = await task.consultConference();
|
|
1943
|
-
|
|
1944
|
-
// Verify calculateDestAgentId was called to dynamically calculate the destination
|
|
1945
|
-
expect(calculateDestAgentIdSpy).toHaveBeenCalledWith(
|
|
1946
|
-
taskDataMock.interaction,
|
|
1947
|
-
taskDataMock.agentId
|
|
1948
|
-
);
|
|
1949
|
-
|
|
1950
|
-
// Verify the conference was called with the dynamically calculated destAgentId
|
|
1951
|
-
expect(contactMock.consultConference).toHaveBeenCalledWith({
|
|
1952
|
-
interactionId: taskId,
|
|
1953
|
-
data: {
|
|
1954
|
-
agentId: taskDataMock.agentId,
|
|
1955
|
-
to: consultedAgentId, // Dynamically calculated value
|
|
1956
|
-
destinationType: 'agent',
|
|
1957
|
-
},
|
|
1958
|
-
});
|
|
1959
|
-
expect(result).toEqual(mockResponse);
|
|
1960
|
-
});
|
|
1961
|
-
|
|
1962
|
-
it('should throw error when no destination agent is found (queue consult not accepted)', async () => {
|
|
1963
|
-
// Simulate queue consult scenario where no agent has accepted yet
|
|
1964
|
-
calculateDestAgentIdSpy.mockReturnValue(''); // No agent found
|
|
1965
|
-
|
|
1966
|
-
await expect(task.consultConference()).rejects.toThrow('No agent has accepted this queue consult yet');
|
|
1967
|
-
|
|
1968
|
-
// Verify the conference was NOT called
|
|
1969
|
-
expect(contactMock.consultConference).not.toHaveBeenCalled();
|
|
1970
|
-
});
|
|
1971
|
-
|
|
1972
|
-
it('should calculate destination type from participant type for regular agents', async () => {
|
|
1973
|
-
const destAgentId = 'consulted-agent-456';
|
|
1974
|
-
|
|
1975
|
-
calculateDestAgentIdSpy = jest.spyOn(Utils, 'calculateDestAgentId').mockReturnValue(destAgentId);
|
|
1976
|
-
calculateDestTypeSpy = jest.spyOn(Utils, 'calculateDestType').mockReturnValue('agent');
|
|
1977
|
-
|
|
1978
|
-
const mockResponse = {trackingId: 'test-tracking-id', interactionId: taskId};
|
|
1979
|
-
contactMock.consultConference.mockResolvedValue(mockResponse);
|
|
1980
|
-
|
|
1981
|
-
await task.consultConference();
|
|
1982
|
-
|
|
1983
|
-
expect(calculateDestTypeSpy).toHaveBeenCalledWith(
|
|
1984
|
-
task.data.interaction,
|
|
1985
|
-
taskDataMock.agentId
|
|
1986
|
-
);
|
|
1987
|
-
|
|
1988
|
-
expect(contactMock.consultConference).toHaveBeenCalledWith({
|
|
1989
|
-
interactionId: taskId,
|
|
1990
|
-
data: {
|
|
1991
|
-
agentId: taskDataMock.agentId,
|
|
1992
|
-
to: destAgentId,
|
|
1993
|
-
destinationType: 'agent',
|
|
1994
|
-
},
|
|
1995
|
-
});
|
|
1996
|
-
});
|
|
1997
|
-
|
|
1998
|
-
it('should use DN destination type for dial number participants', async () => {
|
|
1999
|
-
const destAgentId = 'dn-uuid-123';
|
|
2000
|
-
|
|
2001
|
-
calculateDestAgentIdSpy = jest.spyOn(Utils, 'calculateDestAgentId').mockReturnValue(destAgentId);
|
|
2002
|
-
calculateDestTypeSpy = jest.spyOn(Utils, 'calculateDestType').mockReturnValue('dialNumber');
|
|
2003
|
-
|
|
2004
|
-
const mockResponse = {trackingId: 'test-tracking-id-dn', interactionId: taskId};
|
|
2005
|
-
contactMock.consultConference.mockResolvedValue(mockResponse);
|
|
2006
|
-
|
|
2007
|
-
await task.consultConference();
|
|
2008
|
-
|
|
2009
|
-
expect(contactMock.consultConference).toHaveBeenCalledWith({
|
|
2010
|
-
interactionId: taskId,
|
|
2011
|
-
data: {
|
|
2012
|
-
agentId: taskDataMock.agentId,
|
|
2013
|
-
to: destAgentId,
|
|
2014
|
-
destinationType: 'dialNumber',
|
|
2015
|
-
},
|
|
2016
|
-
});
|
|
2017
|
-
});
|
|
2018
|
-
|
|
2019
|
-
it('should use EpDn destination type for entry point dial number participants', async () => {
|
|
2020
|
-
const destAgentId = 'epdn-uuid-456';
|
|
2021
|
-
|
|
2022
|
-
calculateDestAgentIdSpy = jest.spyOn(Utils, 'calculateDestAgentId').mockReturnValue(destAgentId);
|
|
2023
|
-
calculateDestTypeSpy = jest.spyOn(Utils, 'calculateDestType').mockReturnValue('entryPoint');
|
|
2024
|
-
|
|
2025
|
-
const mockResponse = {trackingId: 'test-tracking-id-epdn', interactionId: taskId};
|
|
2026
|
-
contactMock.consultConference.mockResolvedValue(mockResponse);
|
|
2027
|
-
|
|
2028
|
-
await task.consultConference();
|
|
2029
|
-
|
|
2030
|
-
expect(contactMock.consultConference).toHaveBeenCalledWith({
|
|
2031
|
-
interactionId: taskId,
|
|
2032
|
-
data: {
|
|
2033
|
-
agentId: taskDataMock.agentId,
|
|
2034
|
-
to: destAgentId,
|
|
2035
|
-
destinationType: 'entryPoint',
|
|
2036
|
-
},
|
|
2037
|
-
});
|
|
2038
|
-
});
|
|
2039
|
-
|
|
2040
|
-
it('should fall back to task.data.destinationType when calculateDestType returns empty', async () => {
|
|
2041
|
-
const destAgentId = 'consulted-agent-789';
|
|
2042
|
-
|
|
2043
|
-
calculateDestAgentIdSpy = jest.spyOn(Utils, 'calculateDestAgentId').mockReturnValue(destAgentId);
|
|
2044
|
-
calculateDestTypeSpy = jest.spyOn(Utils, 'calculateDestType').mockReturnValue(''); // No type found
|
|
2045
|
-
|
|
2046
|
-
task.data.destinationType = 'EPDN';
|
|
2047
|
-
|
|
2048
|
-
const mockResponse = {trackingId: 'test-tracking-id-fallback', interactionId: taskId};
|
|
2049
|
-
contactMock.consultConference.mockResolvedValue(mockResponse);
|
|
2050
|
-
|
|
2051
|
-
await task.consultConference();
|
|
2052
|
-
|
|
2053
|
-
expect(contactMock.consultConference).toHaveBeenCalledWith({
|
|
2054
|
-
interactionId: taskId,
|
|
2055
|
-
data: {
|
|
2056
|
-
agentId: taskDataMock.agentId,
|
|
2057
|
-
to: destAgentId,
|
|
2058
|
-
destinationType: 'EPDN', // Falls back to task.data.destinationType
|
|
2059
|
-
},
|
|
2060
|
-
});
|
|
2061
|
-
});
|
|
2062
|
-
|
|
2063
|
-
it('should handle CBT scenarios with correct destination type', async () => {
|
|
2064
|
-
const destAgentId = 'agent-cbt-uuid';
|
|
2065
|
-
|
|
2066
|
-
calculateDestAgentIdSpy = jest.spyOn(Utils, 'calculateDestAgentId').mockReturnValue(destAgentId);
|
|
2067
|
-
calculateDestTypeSpy = jest.spyOn(Utils, 'calculateDestType').mockReturnValue('dialNumber');
|
|
2068
|
-
|
|
2069
|
-
const mockResponse = {trackingId: 'test-tracking-id-cbt', interactionId: taskId};
|
|
2070
|
-
contactMock.consultConference.mockResolvedValue(mockResponse);
|
|
2071
|
-
|
|
2072
|
-
await task.consultConference();
|
|
2073
|
-
|
|
2074
|
-
expect(calculateDestTypeSpy).toHaveBeenCalledWith(
|
|
2075
|
-
task.data.interaction,
|
|
2076
|
-
taskDataMock.agentId
|
|
2077
|
-
);
|
|
2078
|
-
|
|
2079
|
-
expect(contactMock.consultConference).toHaveBeenCalledWith({
|
|
2080
|
-
interactionId: taskId,
|
|
2081
|
-
data: {
|
|
2082
|
-
agentId: taskDataMock.agentId,
|
|
2083
|
-
to: destAgentId,
|
|
2084
|
-
destinationType: 'dialNumber', // dialNumber for CBT scenarios
|
|
2085
|
-
},
|
|
2086
|
-
});
|
|
2087
|
-
});
|
|
2088
|
-
});
|
|
2089
|
-
|
|
2090
|
-
describe('exitConference', () => {
|
|
2091
|
-
it('should successfully end conference and emit event', async () => {
|
|
2092
|
-
const mockResponse = {
|
|
2093
|
-
trackingId: 'test-tracking-id-end',
|
|
2094
|
-
interactionId: taskId,
|
|
2095
|
-
};
|
|
2096
|
-
contactMock.exitConference.mockResolvedValue(mockResponse);
|
|
2097
|
-
|
|
2098
|
-
const result = await task.exitConference();
|
|
2099
|
-
|
|
2100
|
-
expect(contactMock.exitConference).toHaveBeenCalledWith({
|
|
2101
|
-
interactionId: taskId,
|
|
2102
|
-
});
|
|
2103
|
-
expect(result).toEqual(mockResponse);
|
|
2104
|
-
expect(LoggerProxy.info).toHaveBeenCalledWith('Exiting consult conference', {
|
|
2105
|
-
module: TASK_FILE,
|
|
2106
|
-
method: 'exitConference',
|
|
2107
|
-
interactionId: taskId,
|
|
2108
|
-
});
|
|
2109
|
-
expect(LoggerProxy.log).toHaveBeenCalledWith('Consult conference exited successfully', {
|
|
2110
|
-
module: TASK_FILE,
|
|
2111
|
-
method: 'exitConference',
|
|
2112
|
-
interactionId: taskId,
|
|
2113
|
-
});
|
|
2114
|
-
});
|
|
2115
|
-
|
|
2116
|
-
it('should throw error for invalid interaction ID', async () => {
|
|
2117
|
-
task.data.interactionId = '';
|
|
2118
|
-
|
|
2119
|
-
await expect(task.exitConference()).rejects.toThrow('Error while performing exitConference');
|
|
2120
|
-
expect(contactMock.exitConference).not.toHaveBeenCalled();
|
|
2121
|
-
});
|
|
2122
|
-
|
|
2123
|
-
it('should handle and rethrow contact method errors', async () => {
|
|
2124
|
-
const mockError = new Error('Conference end failed');
|
|
2125
|
-
contactMock.exitConference.mockRejectedValue(mockError);
|
|
2126
|
-
generateTaskErrorObjectSpy.mockReturnValue(mockError);
|
|
2127
|
-
|
|
2128
|
-
await expect(task.exitConference()).rejects.toThrow('Conference end failed');
|
|
2129
|
-
expect(LoggerProxy.error).toHaveBeenCalledWith('Failed to exit consult conference', {
|
|
2130
|
-
module: TASK_FILE,
|
|
2131
|
-
method: 'exitConference',
|
|
2132
|
-
interactionId: taskId,
|
|
2133
|
-
});
|
|
2134
|
-
});
|
|
2135
|
-
});
|
|
2136
|
-
|
|
2137
|
-
describe('transferConference', () => {
|
|
2138
|
-
it('should successfully transfer conference', async () => {
|
|
2139
|
-
const mockResponse = {
|
|
2140
|
-
trackingId: 'test-tracking-id-transfer',
|
|
2141
|
-
interactionId: taskId,
|
|
2142
|
-
};
|
|
2143
|
-
contactMock.conferenceTransfer.mockResolvedValue(mockResponse);
|
|
2144
|
-
|
|
2145
|
-
const result = await task.transferConference();
|
|
2146
|
-
|
|
2147
|
-
expect(contactMock.conferenceTransfer).toHaveBeenCalledWith({
|
|
2148
|
-
interactionId: taskId,
|
|
2149
|
-
});
|
|
2150
|
-
expect(result).toEqual(mockResponse);
|
|
2151
|
-
expect(LoggerProxy.info).toHaveBeenCalledWith('Transferring conference', {
|
|
2152
|
-
module: TASK_FILE,
|
|
2153
|
-
method: 'transferConference',
|
|
2154
|
-
interactionId: taskId,
|
|
2155
|
-
});
|
|
2156
|
-
expect(LoggerProxy.log).toHaveBeenCalledWith('Conference transferred successfully', {
|
|
2157
|
-
module: TASK_FILE,
|
|
2158
|
-
method: 'transferConference',
|
|
2159
|
-
interactionId: taskId,
|
|
2160
|
-
});
|
|
2161
|
-
});
|
|
2162
|
-
|
|
2163
|
-
it('should throw error for invalid interaction ID', async () => {
|
|
2164
|
-
task.data.interactionId = '';
|
|
2165
|
-
|
|
2166
|
-
await expect(task.transferConference()).rejects.toThrow('Error while performing transferConference');
|
|
2167
|
-
expect(contactMock.conferenceTransfer).not.toHaveBeenCalled();
|
|
2168
|
-
});
|
|
2169
|
-
|
|
2170
|
-
it('should handle and rethrow contact method errors', async () => {
|
|
2171
|
-
const mockError = new Error('Conference transfer failed');
|
|
2172
|
-
contactMock.conferenceTransfer.mockRejectedValue(mockError);
|
|
2173
|
-
generateTaskErrorObjectSpy.mockReturnValue(mockError);
|
|
2174
|
-
|
|
2175
|
-
await expect(task.transferConference()).rejects.toThrow('Conference transfer failed');
|
|
2176
|
-
expect(LoggerProxy.error).toHaveBeenCalledWith('Failed to transfer conference', {
|
|
2177
|
-
module: TASK_FILE,
|
|
2178
|
-
method: 'transferConference',
|
|
2179
|
-
interactionId: taskId,
|
|
2180
|
-
});
|
|
2181
|
-
});
|
|
2182
|
-
});
|
|
2183
|
-
});
|
|
2184
|
-
});
|