@webex/contact-center 3.12.0-task-refactor.5 → 3.12.0-task-refactor.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/services/task/TaskUtils.js +8 -6
- package/dist/services/task/TaskUtils.js.map +1 -1
- package/dist/services/task/state-machine/TaskStateMachine.js +72 -14
- package/dist/services/task/state-machine/TaskStateMachine.js.map +1 -1
- package/dist/services/task/state-machine/actions.js +85 -13
- package/dist/services/task/state-machine/actions.js.map +1 -1
- package/dist/services/task/state-machine/guards.js +35 -0
- package/dist/services/task/state-machine/guards.js.map +1 -1
- package/dist/services/task/state-machine/uiControlsComputer.js +72 -7
- package/dist/services/task/state-machine/uiControlsComputer.js.map +1 -1
- package/dist/services/task/voice/Voice.js +1 -1
- package/dist/services/task/voice/Voice.js.map +1 -1
- package/dist/types/services/task/state-machine/TaskStateMachine.d.ts +60 -8
- package/dist/types/services/task/state-machine/guards.d.ts +5 -0
- package/dist/webex.js +1 -1
- package/package.json +1 -1
- package/src/services/task/TaskUtils.ts +8 -6
- package/src/services/task/state-machine/TaskStateMachine.ts +95 -16
- package/src/services/task/state-machine/actions.ts +148 -24
- package/src/services/task/state-machine/guards.ts +46 -0
- package/src/services/task/state-machine/uiControlsComputer.ts +163 -9
- package/src/services/task/voice/Voice.ts +4 -1
- package/test/unit/spec/services/WebCallingService.ts +7 -1
- package/test/unit/spec/services/task/TaskUtils.ts +16 -0
- package/test/unit/spec/services/task/state-machine/TaskStateMachine.ts +512 -0
- package/test/unit/spec/services/task/state-machine/guards.ts +88 -0
- package/test/unit/spec/services/task/state-machine/uiControlsComputer.ts +1000 -7
- package/test/unit/spec/services/task/voice/Voice.ts +20 -0
- package/umd/contact-center.min.js +2 -2
- package/umd/contact-center.min.js.map +1 -1
|
@@ -144,16 +144,27 @@ function computeVoiceInteractionUIControls(
|
|
|
144
144
|
Boolean(selfAgentId) &&
|
|
145
145
|
Boolean(mainCallId) &&
|
|
146
146
|
Boolean(interaction?.media?.[mainCallId]?.participants?.includes(selfAgentId as string));
|
|
147
|
+
const consultMedia = Object.values(interaction?.media ?? {}).find(
|
|
148
|
+
(media: any) =>
|
|
149
|
+
media?.mediaResourceId === taskData?.consultMediaResourceId || media?.mType === 'consult'
|
|
150
|
+
) as {participants?: string[]} | undefined;
|
|
151
|
+
const selfParticipant = selfAgentId ? interaction?.participants?.[selfAgentId] : null;
|
|
152
|
+
const selfInConsultCall =
|
|
153
|
+
Boolean(selfAgentId) && Boolean(consultMedia?.participants?.includes(selfAgentId as string));
|
|
147
154
|
const conferenceActive = isConferencing || conferenceFromBackend || consultFromConference;
|
|
148
155
|
// Treat consult initiator as "in conference" even if mainCall participant list lags while consulting.
|
|
149
156
|
const inConference = conferenceActive && (isConferencing || selfInMainCall || consultInitiator);
|
|
150
157
|
|
|
151
158
|
// Check if this is a consulted agent (must be after isConsulting is computed).
|
|
152
|
-
const isSoleAgentOnCall =
|
|
159
|
+
const isSoleAgentOnCall =
|
|
160
|
+
participantCount <= 1 && selfInMainCall && !isConsulting && !inConference;
|
|
153
161
|
const isConsulted =
|
|
154
162
|
inConference || isSoleAgentOnCall
|
|
155
163
|
? false
|
|
156
|
-
: getIsConsultedAgentForControls(taskData, context, isConsulting)
|
|
164
|
+
: getIsConsultedAgentForControls(taskData, context, isConsulting) ||
|
|
165
|
+
(!consultInitiator &&
|
|
166
|
+
(selfParticipant?.isConsulted === true ||
|
|
167
|
+
selfParticipant?.consultState === 'consulting'));
|
|
157
168
|
|
|
158
169
|
// Active call = can perform call operations
|
|
159
170
|
const isActive =
|
|
@@ -173,12 +184,102 @@ function computeVoiceInteractionUIControls(
|
|
|
173
184
|
taskData?.consultMediaResourceId ||
|
|
174
185
|
Object.values(interaction?.media ?? {}).some((media: any) => media?.mType === 'consult')
|
|
175
186
|
);
|
|
187
|
+
const selfConsultPendingOnConsultMedia =
|
|
188
|
+
selfParticipant?.consultState === 'consultInitiated' &&
|
|
189
|
+
!taskData?.isConsulted &&
|
|
190
|
+
hasConsultMedia;
|
|
191
|
+
const ownerParticipant = interaction?.owner
|
|
192
|
+
? interaction.participants?.[interaction.owner]
|
|
193
|
+
: undefined;
|
|
194
|
+
const otherAgentConsultInProgress = Boolean(
|
|
195
|
+
interaction?.participants &&
|
|
196
|
+
Object.values(interaction.participants).some((participant: any) => {
|
|
197
|
+
if (!participant || participant.hasLeft) return false;
|
|
198
|
+
if (participant.id === selfAgentId) return false;
|
|
199
|
+
if (participant.pType !== 'AGENT') return false;
|
|
200
|
+
|
|
201
|
+
return (
|
|
202
|
+
participant.consultState === 'consultInitiated' ||
|
|
203
|
+
participant.consultState === 'consultReserved' ||
|
|
204
|
+
participant.consultState === 'consulting' ||
|
|
205
|
+
participant.currentState === 'consulting'
|
|
206
|
+
);
|
|
207
|
+
})
|
|
208
|
+
);
|
|
209
|
+
const isHydratedConferenceConsultPending =
|
|
210
|
+
inConference && selfConsultPendingOnConsultMedia && !consultDestinationAgentJoined;
|
|
176
211
|
const hasParallelConsultLeg =
|
|
177
212
|
consultOwnedBySelf &&
|
|
178
213
|
!isConsulting &&
|
|
179
214
|
!isConsulted &&
|
|
180
215
|
(consultInProgress || consultCallHeld || hasConsultMedia);
|
|
216
|
+
const activeLegForConferenceConsult = consultCallHeld ? 'main' : 'consult';
|
|
217
|
+
const isCurrentLegActive = currentLeg === activeLegForConferenceConsult;
|
|
218
|
+
const isConferenceConsultTransferContext =
|
|
219
|
+
inConference && consultInitiator && hasConsultMedia && isConsultDestinationReady;
|
|
181
220
|
const consultLegOnHold = isConsulting && consultCallHeld;
|
|
221
|
+
const callProcessingDetails = interaction?.callProcessingDetails as
|
|
222
|
+
| {conferenceHoldParticipant?: boolean | string}
|
|
223
|
+
| undefined;
|
|
224
|
+
const conferenceHoldParticipant =
|
|
225
|
+
callProcessingDetails?.conferenceHoldParticipant === true ||
|
|
226
|
+
callProcessingDetails?.conferenceHoldParticipant === 'true';
|
|
227
|
+
const postDeclineHeldMainLeg =
|
|
228
|
+
consultInitiator &&
|
|
229
|
+
!consultDestinationAgentJoined &&
|
|
230
|
+
isHeld &&
|
|
231
|
+
inConference &&
|
|
232
|
+
conferenceHoldParticipant;
|
|
233
|
+
const postConsultCompletedHeldMainLeg =
|
|
234
|
+
selfParticipant?.consultState === 'consultCompleted' && isHeld && inConference && !isConsulting;
|
|
235
|
+
const nonOwnerPostConsultCompletedHeldMainLeg =
|
|
236
|
+
isHeld &&
|
|
237
|
+
inConference &&
|
|
238
|
+
!isConsulting &&
|
|
239
|
+
!consultInitiator &&
|
|
240
|
+
Boolean(selfAgentId) &&
|
|
241
|
+
Boolean(interaction?.owner) &&
|
|
242
|
+
selfAgentId !== interaction?.owner &&
|
|
243
|
+
ownerParticipant?.consultState === 'consultCompleted';
|
|
244
|
+
const isConsultPendingBeforeJoin =
|
|
245
|
+
selfParticipant?.consultState === 'consultInitiated' && !consultDestinationAgentJoined;
|
|
246
|
+
const hideExitConferenceWhileConsultPending =
|
|
247
|
+
currentLeg === 'main' &&
|
|
248
|
+
inConference &&
|
|
249
|
+
isConsultPendingBeforeJoin &&
|
|
250
|
+
(consultFromConference ||
|
|
251
|
+
consultInitiator ||
|
|
252
|
+
taskData?.type === 'AgentConsultCreated' ||
|
|
253
|
+
consultInProgress ||
|
|
254
|
+
isConsulting);
|
|
255
|
+
const hideExitConferenceDuringActiveConsultFromConference =
|
|
256
|
+
inConference &&
|
|
257
|
+
consultInitiator &&
|
|
258
|
+
consultDestinationAgentJoined &&
|
|
259
|
+
(isConsulting ||
|
|
260
|
+
taskData?.type === 'AgentConsulting' ||
|
|
261
|
+
selfParticipant?.consultState === 'consulting');
|
|
262
|
+
const hideExitConferenceOnMainLegForEpDnConsultFromConference =
|
|
263
|
+
currentLeg === 'main' &&
|
|
264
|
+
inConference &&
|
|
265
|
+
consultFromConference &&
|
|
266
|
+
consultInitiator &&
|
|
267
|
+
(isConsulting ||
|
|
268
|
+
consultInProgress ||
|
|
269
|
+
taskData?.type === 'AgentConsultCreated' ||
|
|
270
|
+
taskData?.type === 'AgentConsulting' ||
|
|
271
|
+
selfParticipant?.consultState === 'consultInitiated' ||
|
|
272
|
+
selfParticipant?.consultState === 'consulting');
|
|
273
|
+
const forceHeldPostConsultControls =
|
|
274
|
+
!hideExitConferenceWhileConsultPending &&
|
|
275
|
+
(postDeclineHeldMainLeg || postConsultCompletedHeldMainLeg);
|
|
276
|
+
const selfOnConsultLeg =
|
|
277
|
+
selfParticipant?.consultState === 'consulting' ||
|
|
278
|
+
selfParticipant?.currentState === 'consulting';
|
|
279
|
+
const showMainLegConferenceControlsDuringConsult =
|
|
280
|
+
currentLeg === 'main' && inConference && consultInProgress && !selfOnConsultLeg;
|
|
281
|
+
const allowHeldMainLegControlsForNonInitiator =
|
|
282
|
+
showMainLegConferenceControlsDuringConsult && !isHydratedConferenceConsultPending;
|
|
182
283
|
|
|
183
284
|
return {
|
|
184
285
|
// Accept/Decline: Voice tasks in offered state
|
|
@@ -198,6 +299,7 @@ function computeVoiceInteractionUIControls(
|
|
|
198
299
|
// Hold: visible in connected/held/conference, disabled in conference/consulting
|
|
199
300
|
hold: (() => {
|
|
200
301
|
if (!hasFullControls) return DISABLED;
|
|
302
|
+
if (forceHeldPostConsultControls) return VISIBLE_ENABLED;
|
|
201
303
|
if (consultOwnedBySelf && (isConsulting || hasParallelConsultLeg || consultCallHeld)) {
|
|
202
304
|
return DISABLED;
|
|
203
305
|
}
|
|
@@ -216,6 +318,8 @@ function computeVoiceInteractionUIControls(
|
|
|
216
318
|
mute: (() => {
|
|
217
319
|
if (!isWebrtc) return DISABLED;
|
|
218
320
|
if (isWrappingUp) return DISABLED;
|
|
321
|
+
if (currentLeg === 'consult' && !selfInConsultCall) return DISABLED;
|
|
322
|
+
if ((isConsulting || hasParallelConsultLeg) && !isCurrentLegActive) return VISIBLE_DISABLED;
|
|
219
323
|
if (isConsulting) return VISIBLE_ENABLED;
|
|
220
324
|
|
|
221
325
|
if (isConnected || isHeld || isConferencing) {
|
|
@@ -229,6 +333,9 @@ function computeVoiceInteractionUIControls(
|
|
|
229
333
|
|
|
230
334
|
// End: varies by state; during consulting only on main leg (consult held)
|
|
231
335
|
end: (() => {
|
|
336
|
+
if (allowHeldMainLegControlsForNonInitiator) return VISIBLE_ENABLED;
|
|
337
|
+
if (showMainLegConferenceControlsDuringConsult) return VISIBLE_DISABLED;
|
|
338
|
+
if (isHydratedConferenceConsultPending && currentLeg === 'main') return VISIBLE_DISABLED;
|
|
232
339
|
if (!config.isEndTaskEnabled) return DISABLED;
|
|
233
340
|
if (hasParallelConsultLeg) {
|
|
234
341
|
return isConnected && isEpDnConsult ? VISIBLE_ENABLED : VISIBLE_DISABLED;
|
|
@@ -242,6 +349,7 @@ function computeVoiceInteractionUIControls(
|
|
|
242
349
|
|
|
243
350
|
if (inConference) {
|
|
244
351
|
if (isConsulted) return DISABLED;
|
|
352
|
+
if (forceHeldPostConsultControls) return VISIBLE_DISABLED;
|
|
245
353
|
|
|
246
354
|
if (consultInProgress) return VISIBLE_DISABLED;
|
|
247
355
|
|
|
@@ -255,6 +363,11 @@ function computeVoiceInteractionUIControls(
|
|
|
255
363
|
|
|
256
364
|
// Transfer: connected/held/conference
|
|
257
365
|
transfer: (() => {
|
|
366
|
+
if (isHydratedConferenceConsultPending) return VISIBLE_DISABLED;
|
|
367
|
+
if (isConferenceConsultTransferContext && currentLeg === 'main' && isCurrentLegActive) {
|
|
368
|
+
return DISABLED;
|
|
369
|
+
}
|
|
370
|
+
if (inConference && isConsulting && consultInitiator) return DISABLED;
|
|
258
371
|
if (hasParallelConsultLeg) {
|
|
259
372
|
if (!customerPresent) return DISABLED;
|
|
260
373
|
if (state === TaskState.CONNECTED) return VISIBLE_ENABLED;
|
|
@@ -282,6 +395,7 @@ function computeVoiceInteractionUIControls(
|
|
|
282
395
|
consult: (() => {
|
|
283
396
|
const isConnectedOrHeld = state === TaskState.CONNECTED || state === TaskState.HELD;
|
|
284
397
|
|
|
398
|
+
if (inConference && nonOwnerPostConsultCompletedHeldMainLeg) return VISIBLE_DISABLED;
|
|
285
399
|
if (hasParallelConsultLeg) return DISABLED;
|
|
286
400
|
if (!hasFullControls || !(isConnectedOrHeld || inConference)) {
|
|
287
401
|
return DISABLED;
|
|
@@ -293,7 +407,11 @@ function computeVoiceInteractionUIControls(
|
|
|
293
407
|
if (participantCount <= 1) return VISIBLE_DISABLED;
|
|
294
408
|
// Real conference: consult enabled if conditions met
|
|
295
409
|
const canFromConference =
|
|
296
|
-
!maxParticipants &&
|
|
410
|
+
!maxParticipants &&
|
|
411
|
+
customerPresent &&
|
|
412
|
+
!consultInProgress &&
|
|
413
|
+
!otherAgentConsultInProgress &&
|
|
414
|
+
!isConsulting;
|
|
297
415
|
|
|
298
416
|
return {isVisible: true, isEnabled: canFromConference};
|
|
299
417
|
}
|
|
@@ -339,6 +457,7 @@ function computeVoiceInteractionUIControls(
|
|
|
339
457
|
// Conference: during consulting, enabled on both legs when agent joined
|
|
340
458
|
// Label changes based on leg: "Conference" on main leg, "Merge" on consult leg
|
|
341
459
|
conference: (() => {
|
|
460
|
+
if (isHydratedConferenceConsultPending && currentLeg === 'main') return VISIBLE_DISABLED;
|
|
342
461
|
if (hasParallelConsultLeg) {
|
|
343
462
|
if (!customerPresent) return DISABLED;
|
|
344
463
|
if (state === TaskState.CONNECTED) {
|
|
@@ -361,9 +480,16 @@ function computeVoiceInteractionUIControls(
|
|
|
361
480
|
|
|
362
481
|
// ExitConference: in conference with multiple agents in main call
|
|
363
482
|
exitConference: (() => {
|
|
483
|
+
if (hideExitConferenceWhileConsultPending) return DISABLED;
|
|
484
|
+
if (hideExitConferenceOnMainLegForEpDnConsultFromConference) return DISABLED;
|
|
485
|
+
if (allowHeldMainLegControlsForNonInitiator) return VISIBLE_ENABLED;
|
|
486
|
+
if (showMainLegConferenceControlsDuringConsult) return VISIBLE_DISABLED;
|
|
487
|
+
if (hideExitConferenceDuringActiveConsultFromConference) return DISABLED;
|
|
488
|
+
if (forceHeldPostConsultControls) return VISIBLE_DISABLED;
|
|
364
489
|
if (isConsulted && !isConferencing) return DISABLED;
|
|
365
490
|
if (!inConference) return DISABLED;
|
|
366
491
|
if (participantCount <= 1) return DISABLED;
|
|
492
|
+
if (consultInProgress) return VISIBLE_DISABLED;
|
|
367
493
|
const consultingFromConference = consultInitiator && isConsulting && conferenceFromBackend;
|
|
368
494
|
|
|
369
495
|
return consultingFromConference ? VISIBLE_DISABLED : VISIBLE_ENABLED;
|
|
@@ -371,8 +497,19 @@ function computeVoiceInteractionUIControls(
|
|
|
371
497
|
|
|
372
498
|
// TransferConference: in conference with active consult, owner consulting from conference
|
|
373
499
|
transferConference: (() => {
|
|
374
|
-
if (
|
|
375
|
-
|
|
500
|
+
if (isConferenceConsultTransferContext && !isCurrentLegActive) return VISIBLE_DISABLED;
|
|
501
|
+
const consultLegTransferAvailable =
|
|
502
|
+
currentLeg === 'consult' && inConference && consultInitiator && hasConsultMedia;
|
|
503
|
+
const selfConsultingOnParticipantState = selfParticipant?.consultState === 'consulting';
|
|
504
|
+
const conferenceTransferAvailable =
|
|
505
|
+
consultLegTransferAvailable ||
|
|
506
|
+
(inConference &&
|
|
507
|
+
consultInitiator &&
|
|
508
|
+
hasConsultMedia &&
|
|
509
|
+
(isConsulting || consultInProgress || selfConsultingOnParticipantState));
|
|
510
|
+
if (consultLegOnHold) return DISABLED;
|
|
511
|
+
if (hasParallelConsultLeg && !conferenceTransferAvailable) return DISABLED;
|
|
512
|
+
if (!conferenceTransferAvailable && (!inConference || !isConsulting)) return DISABLED;
|
|
376
513
|
if (!consultInitiator || isConsulted) return DISABLED;
|
|
377
514
|
|
|
378
515
|
return isConsultDestinationReady ? VISIBLE_ENABLED : VISIBLE_DISABLED;
|
|
@@ -380,6 +517,7 @@ function computeVoiceInteractionUIControls(
|
|
|
380
517
|
|
|
381
518
|
// MergeToConference: mirrors conference control, enabled on both legs
|
|
382
519
|
mergeToConference: (() => {
|
|
520
|
+
if (isHydratedConferenceConsultPending && currentLeg === 'consult') return VISIBLE_DISABLED;
|
|
383
521
|
if (!isConsulting || !consultInitiator) return DISABLED;
|
|
384
522
|
if (!customerPresent) return VISIBLE_DISABLED;
|
|
385
523
|
if (consultLegOnHold) return VISIBLE_DISABLED;
|
|
@@ -389,6 +527,7 @@ function computeVoiceInteractionUIControls(
|
|
|
389
527
|
|
|
390
528
|
// Switch: visible only on the currently active leg
|
|
391
529
|
switch: (() => {
|
|
530
|
+
if (isHydratedConferenceConsultPending && currentLeg === 'consult') return VISIBLE_DISABLED;
|
|
392
531
|
if (!customerPresent && hasParallelConsultLeg) return DISABLED;
|
|
393
532
|
if (currentLeg === 'consult') {
|
|
394
533
|
if (!isConsulting || !consultInitiator || consultCallHeld) return DISABLED;
|
|
@@ -472,10 +611,18 @@ function getVoiceLegState(
|
|
|
472
611
|
taskData?.consultMediaResourceId ||
|
|
473
612
|
Object.values(interaction?.media ?? {}).some((media: any) => media?.mType === 'consult')
|
|
474
613
|
);
|
|
614
|
+
const selfParticipant = selfAgentId ? interaction?.participants?.[selfAgentId] : null;
|
|
615
|
+
const selfConsultPendingOnConsultMedia =
|
|
616
|
+
selfParticipant?.consultState === 'consultInitiated' &&
|
|
617
|
+
!taskData?.isConsulted &&
|
|
618
|
+
hasConsultMedia;
|
|
619
|
+
const selfConsultingOnConsultMedia =
|
|
620
|
+
selfParticipant?.consultState === 'consulting' && hasConsultMedia;
|
|
475
621
|
const hasConsultLeg = Boolean(
|
|
476
|
-
|
|
477
|
-
!taskData?.isConsulted
|
|
478
|
-
|
|
622
|
+
!interaction?.isTerminated &&
|
|
623
|
+
((consultOwnedBySelf && !taskData?.isConsulted) ||
|
|
624
|
+
selfConsultingOnConsultMedia ||
|
|
625
|
+
selfConsultPendingOnConsultMedia) &&
|
|
479
626
|
(consultInProgress || isConsultingState || context.consultCallHeld || hasConsultMedia)
|
|
480
627
|
);
|
|
481
628
|
|
|
@@ -488,10 +635,17 @@ function getVoiceLegState(
|
|
|
488
635
|
};
|
|
489
636
|
}
|
|
490
637
|
|
|
638
|
+
let mainState = TaskState.HELD;
|
|
639
|
+
if (currentState === TaskState.CONFERENCING) {
|
|
640
|
+
mainState = TaskState.CONFERENCING;
|
|
641
|
+
} else if (context.consultCallHeld) {
|
|
642
|
+
mainState = TaskState.CONNECTED;
|
|
643
|
+
}
|
|
644
|
+
|
|
491
645
|
return {
|
|
492
646
|
hasConsultLeg: true,
|
|
493
647
|
activeLeg: context.consultCallHeld ? 'main' : 'consult',
|
|
494
|
-
mainState
|
|
648
|
+
mainState,
|
|
495
649
|
consultState: isConsultingState ? currentState : TaskState.CONSULTING,
|
|
496
650
|
};
|
|
497
651
|
}
|
|
@@ -150,7 +150,10 @@ export default class Voice extends Task implements IVoice {
|
|
|
150
150
|
});
|
|
151
151
|
throw error;
|
|
152
152
|
}
|
|
153
|
-
} else if (
|
|
153
|
+
} else if (
|
|
154
|
+
!state.matches(TaskState.HELD) &&
|
|
155
|
+
!(state.matches(TaskState.CONFERENCING) && mediaHoldState === true)
|
|
156
|
+
) {
|
|
154
157
|
const error = new Error(`Cannot resume call in current state: ${currentState}`);
|
|
155
158
|
LoggerProxy.error('Resume operation not allowed', {
|
|
156
159
|
module: CC_FILE,
|
|
@@ -200,11 +200,17 @@ describe('WebCallingService', () => {
|
|
|
200
200
|
|
|
201
201
|
it('should reject if registration times out', async () => {
|
|
202
202
|
line = callingClient.getLines().line1 as ILine;
|
|
203
|
+
jest.spyOn(global, 'setTimeout').mockImplementation(((handler: TimerHandler) => {
|
|
204
|
+
if (typeof handler === 'function') {
|
|
205
|
+
handler();
|
|
206
|
+
}
|
|
207
|
+
return 0 as unknown as NodeJS.Timeout;
|
|
208
|
+
}) as typeof setTimeout);
|
|
203
209
|
|
|
204
210
|
const promise = webRTCCalling.registerWebCallingLine();
|
|
205
211
|
|
|
206
212
|
await expect(promise).rejects.toThrow('WebCallingService Registration timed out');
|
|
207
|
-
}
|
|
213
|
+
});
|
|
208
214
|
|
|
209
215
|
it('should handle incoming calls', async () => {
|
|
210
216
|
line = callingClient.getLines().line1 as ILine;
|
|
@@ -450,6 +450,14 @@ describe('TaskUtils', () => {
|
|
|
450
450
|
expect(getIsCustomerInCall(interaction, interactionId)).toBe(true);
|
|
451
451
|
});
|
|
452
452
|
|
|
453
|
+
it('getIsCustomerInCall returns false when participants map is missing', () => {
|
|
454
|
+
const interaction = createInteraction(
|
|
455
|
+
{[interactionId]: {mType: 'mainCall', participants: ['c1']}},
|
|
456
|
+
undefined
|
|
457
|
+
);
|
|
458
|
+
expect(getIsCustomerInCall(interaction, interactionId)).toBe(false);
|
|
459
|
+
});
|
|
460
|
+
|
|
453
461
|
it('getConferenceParticipantsCount counts active agents only', () => {
|
|
454
462
|
const interaction = createInteraction(
|
|
455
463
|
{[interactionId]: {mType: 'mainCall', participants: ['a1', 'a2', 'c1']}},
|
|
@@ -458,6 +466,14 @@ describe('TaskUtils', () => {
|
|
|
458
466
|
expect(getConferenceParticipantsCount(interaction, interactionId)).toBe(2);
|
|
459
467
|
});
|
|
460
468
|
|
|
469
|
+
it('getConferenceParticipantsCount returns 0 when participants map is missing', () => {
|
|
470
|
+
const interaction = createInteraction(
|
|
471
|
+
{[interactionId]: {mType: 'mainCall', participants: ['a1', 'a2', 'c1']}},
|
|
472
|
+
undefined
|
|
473
|
+
);
|
|
474
|
+
expect(getConferenceParticipantsCount(interaction, interactionId)).toBe(0);
|
|
475
|
+
});
|
|
476
|
+
|
|
461
477
|
it('isSecondaryAgent returns true for consult with parentInteractionId', () => {
|
|
462
478
|
const interaction = createInteraction();
|
|
463
479
|
interaction.callProcessingDetails = {relationshipType: 'consult', parentInteractionId: 'parent-456'};
|