@webex/contact-center 3.12.0-task-refactor.10 → 3.12.0-task-refactor.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cc.js +51 -2
- package/dist/cc.js.map +1 -1
- package/dist/config.js +6 -0
- package/dist/config.js.map +1 -1
- package/dist/services/task/Task.js +10 -4
- package/dist/services/task/Task.js.map +1 -1
- package/dist/services/task/TaskManager.js +3 -0
- package/dist/services/task/TaskManager.js.map +1 -1
- package/dist/services/task/state-machine/uiControlsComputer.js +1 -1
- package/dist/services/task/state-machine/uiControlsComputer.js.map +1 -1
- package/dist/services/task/types.js +10 -0
- package/dist/services/task/types.js.map +1 -1
- package/dist/types/cc.d.ts +17 -0
- package/dist/types/config.d.ts +6 -0
- package/dist/types/services/task/types.d.ts +11 -1
- package/dist/types/types.d.ts +2 -0
- package/dist/types.js.map +1 -1
- package/dist/webex.js +1 -1
- package/package.json +1 -1
- package/src/cc.ts +74 -2
- package/src/config.ts +6 -0
- package/src/services/task/Task.ts +9 -4
- package/src/services/task/TaskManager.ts +4 -0
- package/src/services/task/state-machine/uiControlsComputer.ts +1 -1
- package/src/services/task/types.ts +11 -0
- package/src/types.ts +2 -0
- package/test/unit/spec/cc.ts +139 -0
- package/test/unit/spec/services/task/TaskManager.ts +14 -0
- package/test/unit/spec/services/task/state-machine/uiControlsComputer.ts +47 -0
- package/umd/contact-center.min.js +2 -2
- package/umd/contact-center.min.js.map +1 -1
package/src/cc.ts
CHANGED
|
@@ -351,6 +351,7 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
351
351
|
this.$webex.once(READY, () => {
|
|
352
352
|
// @ts-ignore
|
|
353
353
|
this.$config = this.config;
|
|
354
|
+
this.validatePluginConfig();
|
|
354
355
|
|
|
355
356
|
/**
|
|
356
357
|
* This is used for handling the async requests by sending webex.request and wait for corresponding websocket event.
|
|
@@ -406,6 +407,16 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
406
407
|
this.trigger(TASK_EVENTS.TASK_HYDRATE, task);
|
|
407
408
|
};
|
|
408
409
|
|
|
410
|
+
/**
|
|
411
|
+
* Handles multi-login hydrate events for SDK instances without Mobius registration
|
|
412
|
+
* @private
|
|
413
|
+
* @param {ITask} task The task object associated with the multi-login hydrate
|
|
414
|
+
*/
|
|
415
|
+
private handleTaskMultiLoginHydrate = (task: ITask) => {
|
|
416
|
+
// @ts-ignore
|
|
417
|
+
this.trigger(TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE, task);
|
|
418
|
+
};
|
|
419
|
+
|
|
409
420
|
/**
|
|
410
421
|
* Handles task merged events when tasks are combined eg: EPDN merge/transfer
|
|
411
422
|
* @private
|
|
@@ -424,6 +435,7 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
424
435
|
private incomingTaskListener() {
|
|
425
436
|
this.taskManager.on(TASK_EVENTS.TASK_INCOMING, this.handleIncomingTask);
|
|
426
437
|
this.taskManager.on(TASK_EVENTS.TASK_HYDRATE, this.handleTaskHydrate);
|
|
438
|
+
this.taskManager.on(TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE, this.handleTaskMultiLoginHydrate);
|
|
427
439
|
this.taskManager.on(TASK_EVENTS.TASK_MERGED, this.handleTaskMerged);
|
|
428
440
|
}
|
|
429
441
|
|
|
@@ -553,6 +565,7 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
553
565
|
|
|
554
566
|
this.taskManager.off(TASK_EVENTS.TASK_INCOMING, this.handleIncomingTask);
|
|
555
567
|
this.taskManager.off(TASK_EVENTS.TASK_HYDRATE, this.handleTaskHydrate);
|
|
568
|
+
this.taskManager.off(TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE, this.handleTaskMultiLoginHydrate);
|
|
556
569
|
this.taskManager.unregisterIncomingCallEvent();
|
|
557
570
|
|
|
558
571
|
this.services.webSocketManager.off('message', this.handleWebsocketMessage);
|
|
@@ -769,7 +782,8 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
769
782
|
}
|
|
770
783
|
if (
|
|
771
784
|
this.agentConfig.webRtcEnabled &&
|
|
772
|
-
this.agentConfig.loginVoiceOptions.includes(LoginOption.BROWSER)
|
|
785
|
+
this.agentConfig.loginVoiceOptions.includes(LoginOption.BROWSER) &&
|
|
786
|
+
!this.isWebRTCRegistrationDisabled()
|
|
773
787
|
) {
|
|
774
788
|
this.$webex.internal.mercury
|
|
775
789
|
.connect()
|
|
@@ -785,6 +799,14 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
785
799
|
method: METHODS.CONNECT_WEBSOCKET,
|
|
786
800
|
});
|
|
787
801
|
});
|
|
802
|
+
} else if (this.isWebRTCRegistrationDisabled()) {
|
|
803
|
+
LoggerProxy.info(
|
|
804
|
+
'Skipping Mobius registration because disableWebRTCRegistration is enabled',
|
|
805
|
+
{
|
|
806
|
+
module: CC_FILE,
|
|
807
|
+
method: METHODS.CONNECT_WEBSOCKET,
|
|
808
|
+
}
|
|
809
|
+
);
|
|
788
810
|
}
|
|
789
811
|
if (this.$config && this.$config.allowAutomatedRelogin) {
|
|
790
812
|
await this.silentRelogin();
|
|
@@ -868,8 +890,24 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
868
890
|
},
|
|
869
891
|
});
|
|
870
892
|
|
|
871
|
-
if (
|
|
893
|
+
if (
|
|
894
|
+
this.agentConfig.webRtcEnabled &&
|
|
895
|
+
data.loginOption === LoginOption.BROWSER &&
|
|
896
|
+
!this.isWebRTCRegistrationDisabled()
|
|
897
|
+
) {
|
|
872
898
|
await this.webCallingService.registerWebCallingLine();
|
|
899
|
+
} else if (
|
|
900
|
+
this.agentConfig.webRtcEnabled &&
|
|
901
|
+
data.loginOption === LoginOption.BROWSER &&
|
|
902
|
+
this.isWebRTCRegistrationDisabled()
|
|
903
|
+
) {
|
|
904
|
+
LoggerProxy.info(
|
|
905
|
+
'Skipping web calling line registration because disableWebRTCRegistration is enabled',
|
|
906
|
+
{
|
|
907
|
+
module: CC_FILE,
|
|
908
|
+
method: METHODS.STATION_LOGIN,
|
|
909
|
+
}
|
|
910
|
+
);
|
|
873
911
|
}
|
|
874
912
|
|
|
875
913
|
const resp = await loginResponse;
|
|
@@ -1235,6 +1273,30 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
1235
1273
|
this.taskManager.handleRealtimeWebsocketEvent(event);
|
|
1236
1274
|
};
|
|
1237
1275
|
|
|
1276
|
+
/**
|
|
1277
|
+
* Checks whether Mobius/WebRTC registration should be skipped by config
|
|
1278
|
+
* @returns {boolean} True when browser WebRTC registration is disabled
|
|
1279
|
+
* @private
|
|
1280
|
+
*/
|
|
1281
|
+
private isWebRTCRegistrationDisabled(): boolean {
|
|
1282
|
+
return this.$config?.disableWebRTCRegistration === true;
|
|
1283
|
+
}
|
|
1284
|
+
|
|
1285
|
+
/**
|
|
1286
|
+
* Validates contact-center plugin configuration before service initialization
|
|
1287
|
+
* @private
|
|
1288
|
+
*/
|
|
1289
|
+
private validatePluginConfig(): void {
|
|
1290
|
+
if (
|
|
1291
|
+
this.$config?.disableWebRTCRegistration === true &&
|
|
1292
|
+
this.$config?.allowMultiLogin === false
|
|
1293
|
+
) {
|
|
1294
|
+
throw new Error(
|
|
1295
|
+
'Invalid Contact Center configuration: disableWebRTCRegistration cannot be true when allowMultiLogin is false. Enable allowMultiLogin or allow WebRTC registration so an SDK instance can receive Mobius/WebRTC task events.'
|
|
1296
|
+
);
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1238
1300
|
/**
|
|
1239
1301
|
* Initializes event listeners for the Contact Center service
|
|
1240
1302
|
* Sets up handlers for connection state changes and other core events
|
|
@@ -1380,6 +1442,16 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
1380
1442
|
this.agentConfig.deviceType = deviceType;
|
|
1381
1443
|
switch (deviceType) {
|
|
1382
1444
|
case LoginOption.BROWSER:
|
|
1445
|
+
if (this.isWebRTCRegistrationDisabled()) {
|
|
1446
|
+
LoggerProxy.info(
|
|
1447
|
+
'Skipping web calling line registration because disableWebRTCRegistration is enabled',
|
|
1448
|
+
{
|
|
1449
|
+
module: CC_FILE,
|
|
1450
|
+
method: METHODS.HANDLE_DEVICE_TYPE,
|
|
1451
|
+
}
|
|
1452
|
+
);
|
|
1453
|
+
break;
|
|
1454
|
+
}
|
|
1383
1455
|
try {
|
|
1384
1456
|
await this.webCallingService.registerWebCallingLine();
|
|
1385
1457
|
} catch (error) {
|
package/src/config.ts
CHANGED
|
@@ -25,6 +25,12 @@ export default {
|
|
|
25
25
|
* @default true
|
|
26
26
|
*/
|
|
27
27
|
allowAutomatedRelogin: true,
|
|
28
|
+
/**
|
|
29
|
+
* Whether to skip Mobius/WebRTC registration for browser login flows.
|
|
30
|
+
* @type {boolean}
|
|
31
|
+
* @default false
|
|
32
|
+
*/
|
|
33
|
+
disableWebRTCRegistration: false,
|
|
28
34
|
/**
|
|
29
35
|
* The type of client making the connection.
|
|
30
36
|
* @type {string}
|
|
@@ -278,7 +278,9 @@ export default abstract class Task extends EventEmitter implements ITask {
|
|
|
278
278
|
const currentState = snapshot.value as TaskState;
|
|
279
279
|
const context = snapshot.context as TaskContext;
|
|
280
280
|
|
|
281
|
-
|
|
281
|
+
const uiControls = computeUIControls(currentState, context, this.data);
|
|
282
|
+
|
|
283
|
+
return uiControls;
|
|
282
284
|
}
|
|
283
285
|
|
|
284
286
|
/**
|
|
@@ -418,9 +420,11 @@ export default abstract class Task extends EventEmitter implements ITask {
|
|
|
418
420
|
emitTaskOfferContact: this.createEmitSelfAction(TASK_EVENTS.TASK_OFFER_CONTACT, {
|
|
419
421
|
updateTaskData: true,
|
|
420
422
|
}),
|
|
421
|
-
emitTaskAssigned:
|
|
422
|
-
|
|
423
|
-
|
|
423
|
+
emitTaskAssigned: ({event}: TaskActionArgs) => {
|
|
424
|
+
this.updateTaskFromEvent(event);
|
|
425
|
+
this.emit(TASK_EVENTS.TASK_ASSIGNED, this);
|
|
426
|
+
this.emit(TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE, this);
|
|
427
|
+
},
|
|
424
428
|
emitTaskEnd: this.createEmitSelfAction(TASK_EVENTS.TASK_END, {updateTaskData: true}),
|
|
425
429
|
emitTaskOfferConsult: this.createEmitSelfAction(TASK_EVENTS.TASK_OFFER_CONSULT, {
|
|
426
430
|
updateTaskData: true,
|
|
@@ -435,6 +439,7 @@ export default abstract class Task extends EventEmitter implements ITask {
|
|
|
435
439
|
} else {
|
|
436
440
|
this.emit(TASK_EVENTS.TASK_CONSULTING, this);
|
|
437
441
|
}
|
|
442
|
+
this.emit(TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE, this);
|
|
438
443
|
},
|
|
439
444
|
emitTaskConsultAccepted: this.createEmitSelfAction(TASK_EVENTS.TASK_CONSULT_ACCEPTED),
|
|
440
445
|
emitTaskConsultEnd: this.createEmitSelfAction(TASK_EVENTS.TASK_CONSULT_END, {
|
|
@@ -630,6 +630,10 @@ export default class TaskManager extends EventEmitter {
|
|
|
630
630
|
this.emit(TASK_EVENTS.TASK_HYDRATE, t);
|
|
631
631
|
});
|
|
632
632
|
|
|
633
|
+
task.on(TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE, (t: ITask) => {
|
|
634
|
+
this.emit(TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE, t);
|
|
635
|
+
});
|
|
636
|
+
|
|
633
637
|
// Listen for internal cleanup signal emitted by the state machine
|
|
634
638
|
task.on(TASK_EVENTS.TASK_CLEANUP, (t: ITask, options?: {removeFromCollection?: boolean}) => {
|
|
635
639
|
this.handleTaskCleanup(t);
|
|
@@ -438,7 +438,7 @@ function computeVoiceInteractionUIControls(
|
|
|
438
438
|
if (allowHeldMainLegControlsForNonInitiator) return VISIBLE_ENABLED;
|
|
439
439
|
if (showMainLegConferenceControlsDuringConsult) return VISIBLE_DISABLED;
|
|
440
440
|
if (isHydratedConferenceConsultPending && currentLeg === 'main') return VISIBLE_DISABLED;
|
|
441
|
-
if (!config.isEndTaskEnabled) return DISABLED;
|
|
441
|
+
if (!config.isEndTaskEnabled && !isWebrtc) return DISABLED;
|
|
442
442
|
if (hasParallelConsultLeg) {
|
|
443
443
|
return isConnected && isEpDnConsult ? VISIBLE_ENABLED : VISIBLE_DISABLED;
|
|
444
444
|
}
|
|
@@ -628,6 +628,17 @@ export enum TASK_EVENTS {
|
|
|
628
628
|
* ```
|
|
629
629
|
*/
|
|
630
630
|
TASK_POST_CALL_ACTIVITY = 'task:postCallActivity',
|
|
631
|
+
|
|
632
|
+
/**
|
|
633
|
+
* Triggered when a multi-login task update should hydrate SDK instances without Mobius registration.
|
|
634
|
+
* @example
|
|
635
|
+
* ```typescript
|
|
636
|
+
* task.on(TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE, (task: ITask) => {
|
|
637
|
+
* console.log('Multi-login hydrate:', task.data.interactionId);
|
|
638
|
+
* });
|
|
639
|
+
* ```
|
|
640
|
+
*/
|
|
641
|
+
TASK_MULTI_LOGIN_HYDRATE = 'task:multiLoginHydrate',
|
|
631
642
|
}
|
|
632
643
|
|
|
633
644
|
/**
|
package/src/types.ts
CHANGED
|
@@ -156,6 +156,8 @@ export interface CCPluginConfig {
|
|
|
156
156
|
};
|
|
157
157
|
/** Configuration for the calling client */
|
|
158
158
|
callingClientConfig: CallingClientConfig;
|
|
159
|
+
/** Whether to skip Mobius/WebRTC registration for browser login flows */
|
|
160
|
+
disableWebRTCRegistration?: boolean;
|
|
159
161
|
}
|
|
160
162
|
|
|
161
163
|
/**
|
package/test/unit/spec/cc.ts
CHANGED
|
@@ -195,6 +195,35 @@ describe('webex.cc', () => {
|
|
|
195
195
|
webex.emit('ready');
|
|
196
196
|
});
|
|
197
197
|
|
|
198
|
+
it('should throw when WebRTC registration is disabled without multi-login', () => {
|
|
199
|
+
const invalidWebex = MockWebex({
|
|
200
|
+
children: {
|
|
201
|
+
mercury: Mercury,
|
|
202
|
+
},
|
|
203
|
+
logger: {
|
|
204
|
+
log: jest.fn(),
|
|
205
|
+
error: jest.fn(),
|
|
206
|
+
info: jest.fn(),
|
|
207
|
+
},
|
|
208
|
+
credentials: {
|
|
209
|
+
getOrgId: jest.fn(() => 'mockOrgId'),
|
|
210
|
+
},
|
|
211
|
+
config: {
|
|
212
|
+
...config,
|
|
213
|
+
cc: {
|
|
214
|
+
...config.cc,
|
|
215
|
+
allowMultiLogin: false,
|
|
216
|
+
disableWebRTCRegistration: true,
|
|
217
|
+
},
|
|
218
|
+
},
|
|
219
|
+
once: jest.fn((event, callback) => callback()),
|
|
220
|
+
}) as unknown as WebexSDK;
|
|
221
|
+
|
|
222
|
+
expect(() => new ContactCenter({parent: invalidWebex})).toThrow(
|
|
223
|
+
'Invalid Contact Center configuration: disableWebRTCRegistration cannot be true when allowMultiLogin is false. Enable allowMultiLogin or allow WebRTC registration so an SDK instance can receive Mobius/WebRTC task events.'
|
|
224
|
+
);
|
|
225
|
+
});
|
|
226
|
+
|
|
198
227
|
describe('cc.getDeviceId', () => {
|
|
199
228
|
it('should return dialNumber when loginOption is EXTENSION', () => {
|
|
200
229
|
const loginOption = LoginOption.EXTENSION;
|
|
@@ -328,6 +357,10 @@ describe('webex.cc', () => {
|
|
|
328
357
|
TASK_EVENTS.TASK_HYDRATE,
|
|
329
358
|
expect.any(Function)
|
|
330
359
|
);
|
|
360
|
+
expect(mockTaskManager.on).toHaveBeenCalledWith(
|
|
361
|
+
TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE,
|
|
362
|
+
expect.any(Function)
|
|
363
|
+
);
|
|
331
364
|
expect(mockWebSocketManager.on).toHaveBeenCalledWith('message', expect.any(Function));
|
|
332
365
|
|
|
333
366
|
expect(configSpy).toHaveBeenCalled();
|
|
@@ -475,6 +508,10 @@ describe('webex.cc', () => {
|
|
|
475
508
|
TASK_EVENTS.TASK_HYDRATE,
|
|
476
509
|
expect.any(Function)
|
|
477
510
|
);
|
|
511
|
+
expect(mockTaskManager.on).toHaveBeenCalledWith(
|
|
512
|
+
TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE,
|
|
513
|
+
expect.any(Function)
|
|
514
|
+
);
|
|
478
515
|
expect(mockWebSocketManager.on).toHaveBeenCalledWith('message', expect.any(Function));
|
|
479
516
|
|
|
480
517
|
expect(configSpy).toHaveBeenCalled();
|
|
@@ -486,6 +523,36 @@ describe('webex.cc', () => {
|
|
|
486
523
|
expect(result).toEqual(mockAgentProfile);
|
|
487
524
|
});
|
|
488
525
|
|
|
526
|
+
it('should skip mercury connection when disableWebRTCRegistration is enabled', async () => {
|
|
527
|
+
webex.cc.$config = {
|
|
528
|
+
...webex.cc.$config,
|
|
529
|
+
allowAutomatedRelogin: false,
|
|
530
|
+
disableWebRTCRegistration: true,
|
|
531
|
+
};
|
|
532
|
+
mockAgentProfile.webRtcEnabled = true;
|
|
533
|
+
const mercurySpy = jest.spyOn(webex.internal.mercury, 'connect');
|
|
534
|
+
const connectWebsocketSpy = jest.spyOn(webex.cc, 'connectWebsocket');
|
|
535
|
+
const setupEventListenersSpy = jest.spyOn(webex.cc, 'setupEventListeners');
|
|
536
|
+
jest.spyOn(webex.cc.services.config, 'getAgentConfig').mockResolvedValue(mockAgentProfile);
|
|
537
|
+
mockWebSocketManager.initWebSocket.mockResolvedValue({
|
|
538
|
+
agentId: 'agent123',
|
|
539
|
+
});
|
|
540
|
+
|
|
541
|
+
const result = await webex.cc.register();
|
|
542
|
+
|
|
543
|
+
expect(connectWebsocketSpy).toHaveBeenCalled();
|
|
544
|
+
expect(setupEventListenersSpy).toHaveBeenCalled();
|
|
545
|
+
expect(mercurySpy).not.toHaveBeenCalled();
|
|
546
|
+
expect(LoggerProxy.info).toHaveBeenCalledWith(
|
|
547
|
+
'Skipping Mobius registration because disableWebRTCRegistration is enabled',
|
|
548
|
+
{
|
|
549
|
+
module: CC_FILE,
|
|
550
|
+
method: 'connectWebsocket',
|
|
551
|
+
}
|
|
552
|
+
);
|
|
553
|
+
expect(result).toEqual(mockAgentProfile);
|
|
554
|
+
});
|
|
555
|
+
|
|
489
556
|
it('should not attempt for mercury connection when webrtc is disabled', async () => {
|
|
490
557
|
mockAgentProfile.webRtcEnabled = false;
|
|
491
558
|
const mercurySpy = jest.spyOn(webex.internal.mercury, 'connect');
|
|
@@ -658,6 +725,59 @@ describe('webex.cc', () => {
|
|
|
658
725
|
);
|
|
659
726
|
});
|
|
660
727
|
|
|
728
|
+
it('should skip web calling line registration when disableWebRTCRegistration is enabled', async () => {
|
|
729
|
+
const options = {
|
|
730
|
+
teamId: 'teamId',
|
|
731
|
+
loginOption: LoginOption.BROWSER,
|
|
732
|
+
};
|
|
733
|
+
|
|
734
|
+
webex.cc.$config = {
|
|
735
|
+
...webex.cc.$config,
|
|
736
|
+
disableWebRTCRegistration: true,
|
|
737
|
+
};
|
|
738
|
+
webex.cc.agentConfig = {
|
|
739
|
+
agentId: 'agentId',
|
|
740
|
+
webRtcEnabled: true,
|
|
741
|
+
loginVoiceOptions: ['BROWSER', 'EXTENSION', 'AGENT_DN'],
|
|
742
|
+
};
|
|
743
|
+
|
|
744
|
+
const registerWebCallingLineSpy = jest.spyOn(
|
|
745
|
+
webex.cc.webCallingService,
|
|
746
|
+
'registerWebCallingLine'
|
|
747
|
+
);
|
|
748
|
+
|
|
749
|
+
jest.spyOn(webex.cc.services.agent, 'stationLogin').mockResolvedValue({
|
|
750
|
+
data: {
|
|
751
|
+
loginOption: LoginOption.BROWSER,
|
|
752
|
+
agentId: 'agentId',
|
|
753
|
+
teamId: 'teamId',
|
|
754
|
+
siteId: 'siteId',
|
|
755
|
+
roles: [AGENT],
|
|
756
|
+
channelsMap: {
|
|
757
|
+
chat: [],
|
|
758
|
+
email: [],
|
|
759
|
+
social: [],
|
|
760
|
+
telephony: [],
|
|
761
|
+
},
|
|
762
|
+
},
|
|
763
|
+
trackingId: 'notifs_52628',
|
|
764
|
+
orgId: 'orgId',
|
|
765
|
+
type: 'StationLoginSuccess',
|
|
766
|
+
eventType: 'STATION_LOGIN',
|
|
767
|
+
} as unknown as StationLoginSuccess);
|
|
768
|
+
|
|
769
|
+
await webex.cc.stationLogin(options);
|
|
770
|
+
|
|
771
|
+
expect(registerWebCallingLineSpy).not.toHaveBeenCalled();
|
|
772
|
+
expect(LoggerProxy.info).toHaveBeenCalledWith(
|
|
773
|
+
'Skipping web calling line registration because disableWebRTCRegistration is enabled',
|
|
774
|
+
{
|
|
775
|
+
module: CC_FILE,
|
|
776
|
+
method: 'stationLogin',
|
|
777
|
+
}
|
|
778
|
+
);
|
|
779
|
+
});
|
|
780
|
+
|
|
661
781
|
it('should not attempt mobius registration for LoginOption.BROWSER if webrtc is disabled', async () => {
|
|
662
782
|
const options = {
|
|
663
783
|
teamId: 'teamId',
|
|
@@ -1568,6 +1688,10 @@ describe('webex.cc', () => {
|
|
|
1568
1688
|
TASK_EVENTS.TASK_HYDRATE,
|
|
1569
1689
|
expect.any(Function)
|
|
1570
1690
|
);
|
|
1691
|
+
expect(mockTaskManager.off).toHaveBeenCalledWith(
|
|
1692
|
+
TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE,
|
|
1693
|
+
expect.any(Function)
|
|
1694
|
+
);
|
|
1571
1695
|
expect(mockWebSocketManager.off).toHaveBeenCalledWith('message', expect.any(Function));
|
|
1572
1696
|
expect(webex.cc.services.connectionService.off).toHaveBeenCalledWith(
|
|
1573
1697
|
'connectionLost',
|
|
@@ -1617,6 +1741,13 @@ describe('webex.cc', () => {
|
|
|
1617
1741
|
const [, hydrateCallback] = hydrateCalls[0];
|
|
1618
1742
|
expect(hydrateCallback).toBe(webex.cc['handleTaskHydrate']);
|
|
1619
1743
|
|
|
1744
|
+
const multiLoginHydrateCalls = mockTaskManager.off.mock.calls.filter(
|
|
1745
|
+
([evt]) => evt === TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE
|
|
1746
|
+
);
|
|
1747
|
+
expect(multiLoginHydrateCalls).toHaveLength(1);
|
|
1748
|
+
const [, multiLoginHydrateCallback] = multiLoginHydrateCalls[0];
|
|
1749
|
+
expect(multiLoginHydrateCallback).toBe(webex.cc['handleTaskMultiLoginHydrate']);
|
|
1750
|
+
|
|
1620
1751
|
const messageCalls = mockWebSocketManager.off.mock.calls.filter(([evt]) => evt === 'message');
|
|
1621
1752
|
expect(messageCalls).toHaveLength(1);
|
|
1622
1753
|
const [, messageCallback] = messageCalls[0];
|
|
@@ -1642,6 +1773,10 @@ describe('webex.cc', () => {
|
|
|
1642
1773
|
TASK_EVENTS.TASK_HYDRATE,
|
|
1643
1774
|
expect.any(Function)
|
|
1644
1775
|
);
|
|
1776
|
+
expect(mockTaskManager.off).toHaveBeenCalledWith(
|
|
1777
|
+
TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE,
|
|
1778
|
+
expect.any(Function)
|
|
1779
|
+
);
|
|
1645
1780
|
expect(mockWebSocketManager.off).toHaveBeenCalledWith('message', expect.any(Function));
|
|
1646
1781
|
expect(webex.cc.services.connectionService.off).toHaveBeenCalledWith(
|
|
1647
1782
|
'connectionLost',
|
|
@@ -1687,6 +1822,10 @@ describe('webex.cc', () => {
|
|
|
1687
1822
|
TASK_EVENTS.TASK_HYDRATE,
|
|
1688
1823
|
expect.any(Function)
|
|
1689
1824
|
);
|
|
1825
|
+
expect(mockTaskManager.off).toHaveBeenCalledWith(
|
|
1826
|
+
TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE,
|
|
1827
|
+
expect.any(Function)
|
|
1828
|
+
);
|
|
1690
1829
|
|
|
1691
1830
|
expect(LoggerProxy.error).toHaveBeenCalledWith(`Error during deregister: ${mockError}`, {
|
|
1692
1831
|
module: CC_FILE,
|
|
@@ -92,6 +92,10 @@ describe('TaskManager', () => {
|
|
|
92
92
|
}
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
+
if ([TaskEvent.ASSIGN, TaskEvent.CONSULTING_ACTIVE].includes(event.type as TaskEvent)) {
|
|
96
|
+
task.emit(TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE, task);
|
|
97
|
+
}
|
|
98
|
+
|
|
95
99
|
// Auto-answer is now handled at the Task layer (triggered by state machine actions)
|
|
96
100
|
if (
|
|
97
101
|
[TaskEvent.TASK_OFFERED, TaskEvent.OFFER_CONSULT].includes(event.type as TaskEvent) &&
|
|
@@ -494,6 +498,7 @@ describe('TaskManager', () => {
|
|
|
494
498
|
taskManager.getTask(payload.data.interactionId),
|
|
495
499
|
'emit'
|
|
496
500
|
);
|
|
501
|
+
const taskManagerEmitSpy = jest.spyOn(taskManager, 'emit');
|
|
497
502
|
|
|
498
503
|
webSocketManagerMock.emit('message', JSON.stringify(assignedPayload));
|
|
499
504
|
|
|
@@ -501,6 +506,10 @@ describe('TaskManager', () => {
|
|
|
501
506
|
TASK_EVENTS.TASK_ASSIGNED,
|
|
502
507
|
taskManager.getTask(taskId)
|
|
503
508
|
);
|
|
509
|
+
expect(taskManagerEmitSpy).toHaveBeenCalledWith(
|
|
510
|
+
TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE,
|
|
511
|
+
taskManager.getTask(taskId)
|
|
512
|
+
);
|
|
504
513
|
});
|
|
505
514
|
|
|
506
515
|
it('should handle WebSocket message for AGENT_CONTACT_RESERVED and emit task:incoming for extension case', () => {
|
|
@@ -1837,6 +1846,7 @@ describe('TaskManager', () => {
|
|
|
1837
1846
|
taskManager.getTask(taskId).data.isConsulted = false;
|
|
1838
1847
|
const task = taskManager.getTask(taskId);
|
|
1839
1848
|
const sendStateMachineEventSpy = jest.spyOn(task, 'sendStateMachineEvent');
|
|
1849
|
+
const taskManagerEmitSpy = jest.spyOn(taskManager, 'emit');
|
|
1840
1850
|
const consultingPayload = {
|
|
1841
1851
|
data: {
|
|
1842
1852
|
...initalPayload.data,
|
|
@@ -1846,6 +1856,10 @@ describe('TaskManager', () => {
|
|
|
1846
1856
|
};
|
|
1847
1857
|
webSocketManagerMock.emit('message', JSON.stringify(consultingPayload));
|
|
1848
1858
|
expectLastStateMachineEvent(sendStateMachineEventSpy, TaskEvent.CONSULTING_ACTIVE);
|
|
1859
|
+
expect(taskManagerEmitSpy).toHaveBeenCalledWith(
|
|
1860
|
+
TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE,
|
|
1861
|
+
taskManager.getTask(taskId)
|
|
1862
|
+
);
|
|
1849
1863
|
sendStateMachineEventSpy.mockRestore();
|
|
1850
1864
|
});
|
|
1851
1865
|
|
|
@@ -1784,6 +1784,53 @@ describe('uiControlsComputer outdial accept/decline controls', () => {
|
|
|
1784
1784
|
});
|
|
1785
1785
|
});
|
|
1786
1786
|
|
|
1787
|
+
describe('uiControlsComputer WebRTC call-control end button', () => {
|
|
1788
|
+
function createConnectedContext(voiceVariant: 'webrtc' | 'pstn'): TaskContext {
|
|
1789
|
+
const taskData = createTaskData({
|
|
1790
|
+
interaction: {
|
|
1791
|
+
state: 'connected',
|
|
1792
|
+
} as any,
|
|
1793
|
+
});
|
|
1794
|
+
|
|
1795
|
+
return {
|
|
1796
|
+
taskData,
|
|
1797
|
+
consultInitiator: false,
|
|
1798
|
+
exitingConference: false,
|
|
1799
|
+
consultFromConference: false,
|
|
1800
|
+
transferConferenceRequested: false,
|
|
1801
|
+
consultDestinationType: null,
|
|
1802
|
+
consultDestinationAgentId: null,
|
|
1803
|
+
consultDestinationAgentJoined: false,
|
|
1804
|
+
consultCallHeld: false,
|
|
1805
|
+
recordingControlsAvailable: true,
|
|
1806
|
+
recordingInProgress: true,
|
|
1807
|
+
uiControlConfig: {
|
|
1808
|
+
isEndTaskEnabled: false,
|
|
1809
|
+
isEndConsultEnabled: true,
|
|
1810
|
+
channelType: TASK_CHANNEL_TYPE.VOICE,
|
|
1811
|
+
isRecordingEnabled: true,
|
|
1812
|
+
agentId: taskData.agentId,
|
|
1813
|
+
voiceVariant,
|
|
1814
|
+
},
|
|
1815
|
+
uiControls: getDefaultUIControls(),
|
|
1816
|
+
};
|
|
1817
|
+
}
|
|
1818
|
+
|
|
1819
|
+
it('shows main end for connected WebRTC call-control tasks even when end task is disabled', () => {
|
|
1820
|
+
const context = createConnectedContext('webrtc');
|
|
1821
|
+
const uiControls = computeUIControls(TaskState.CONNECTED, context, context.taskData);
|
|
1822
|
+
|
|
1823
|
+
expect(uiControls.main.end).toEqual({isVisible: true, isEnabled: true});
|
|
1824
|
+
});
|
|
1825
|
+
|
|
1826
|
+
it('keeps main end hidden for non-WebRTC voice tasks when end task is disabled', () => {
|
|
1827
|
+
const context = createConnectedContext('pstn');
|
|
1828
|
+
const uiControls = computeUIControls(TaskState.CONNECTED, context, context.taskData);
|
|
1829
|
+
|
|
1830
|
+
expect(uiControls.main.end).toEqual({isVisible: false, isEnabled: false});
|
|
1831
|
+
});
|
|
1832
|
+
});
|
|
1833
|
+
|
|
1787
1834
|
describe('uiControlsComputer conference controls', () => {
|
|
1788
1835
|
function createConferenceTaskData(participantCount: number) {
|
|
1789
1836
|
const participants: Record<string, any> = {
|