@webex/internal-plugin-voicea 3.11.0 → 3.12.0-next.2
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/constants.js +2 -1
- package/dist/constants.js.map +1 -1
- package/dist/types/constants.d.ts +1 -0
- package/dist/types/voicea.d.ts +47 -0
- package/dist/voicea.js +143 -18
- package/dist/voicea.js.map +1 -1
- package/package.json +9 -9
- package/src/constants.ts +1 -0
- package/src/voicea.ts +132 -18
- package/test/unit/spec/voicea.js +392 -18
package/test/unit/spec/voicea.js
CHANGED
|
@@ -7,7 +7,11 @@ import Mercury from '@webex/internal-plugin-mercury';
|
|
|
7
7
|
import LLMChannel from '@webex/internal-plugin-llm';
|
|
8
8
|
|
|
9
9
|
import VoiceaService from '../../../src/index';
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
EVENT_TRIGGERS,
|
|
12
|
+
LLM_PRACTICE_SESSION,
|
|
13
|
+
TOGGLE_MANUAL_CAPTION_STATUS,
|
|
14
|
+
} from '../../../src/constants';
|
|
11
15
|
|
|
12
16
|
describe('plugin-voicea', () => {
|
|
13
17
|
const locusUrl = 'locusUrl';
|
|
@@ -28,6 +32,7 @@ describe('plugin-voicea', () => {
|
|
|
28
32
|
voiceaService.connect = sinon.stub().resolves(true);
|
|
29
33
|
voiceaService.webex.internal.llm.isConnected = sinon.stub().returns(true);
|
|
30
34
|
voiceaService.webex.internal.llm.getBinding = sinon.stub().returns(undefined);
|
|
35
|
+
voiceaService.webex.internal.llm.getSocket = sinon.stub().returns(undefined);
|
|
31
36
|
voiceaService.webex.internal.llm.getLocusUrl = sinon.stub().returns(locusUrl);
|
|
32
37
|
|
|
33
38
|
voiceaService.request = sinon.stub().resolves({
|
|
@@ -87,7 +92,9 @@ describe('plugin-voicea', () => {
|
|
|
87
92
|
|
|
88
93
|
voiceaService.sendAnnouncement();
|
|
89
94
|
|
|
90
|
-
assert.
|
|
95
|
+
assert.calledTwice(spy);
|
|
96
|
+
assert.calledWith(spy, 'event:relay.event', sinon.match.func);
|
|
97
|
+
assert.calledWith(spy, `event:relay.event:${LLM_PRACTICE_SESSION}`, sinon.match.func);
|
|
91
98
|
});
|
|
92
99
|
|
|
93
100
|
it('includes captionServiceId in headers when set', () => {
|
|
@@ -214,19 +221,17 @@ describe('plugin-voicea', () => {
|
|
|
214
221
|
assert.notCalled(voiceaService.webex.internal.llm.socket.send);
|
|
215
222
|
});
|
|
216
223
|
});
|
|
217
|
-
|
|
218
224
|
describe('#deregisterEvents', () => {
|
|
219
225
|
beforeEach(async () => {
|
|
220
226
|
const mockWebSocket = new MockWebSocket();
|
|
221
|
-
|
|
222
227
|
voiceaService.webex.internal.llm.socket = mockWebSocket;
|
|
228
|
+
voiceaService.isCaptionBoxOn = true;
|
|
223
229
|
});
|
|
224
230
|
|
|
225
|
-
it('deregisters voicea service', async () => {
|
|
231
|
+
it('deregisters voicea service and resets caption state', async () => {
|
|
226
232
|
voiceaService.listenToEvents();
|
|
227
233
|
await voiceaService.toggleTranscribing(true);
|
|
228
234
|
|
|
229
|
-
// eslint-disable-next-line no-underscore-dangle
|
|
230
235
|
voiceaService.webex.internal.llm._emit('event:relay.event', {
|
|
231
236
|
headers: {from: 'ws'},
|
|
232
237
|
data: {relayType: 'voicea.annc', voiceaPayload: {}},
|
|
@@ -234,12 +239,14 @@ describe('plugin-voicea', () => {
|
|
|
234
239
|
|
|
235
240
|
assert.equal(voiceaService.areCaptionsEnabled, true);
|
|
236
241
|
assert.equal(voiceaService.captionServiceId, 'ws');
|
|
242
|
+
assert.equal(voiceaService.isCaptionBoxOn, true);
|
|
237
243
|
|
|
238
244
|
voiceaService.deregisterEvents();
|
|
239
245
|
assert.equal(voiceaService.areCaptionsEnabled, false);
|
|
240
246
|
assert.equal(voiceaService.captionServiceId, undefined);
|
|
241
247
|
assert.equal(voiceaService.announceStatus, 'idle');
|
|
242
248
|
assert.equal(voiceaService.captionStatus, 'idle');
|
|
249
|
+
assert.equal(voiceaService.isCaptionBoxOn, false);
|
|
243
250
|
});
|
|
244
251
|
});
|
|
245
252
|
describe('#processAnnouncementMessage', () => {
|
|
@@ -269,7 +276,7 @@ describe('plugin-voicea', () => {
|
|
|
269
276
|
});
|
|
270
277
|
});
|
|
271
278
|
|
|
272
|
-
it('works on
|
|
279
|
+
it('works on empty payload', async () => {
|
|
273
280
|
const spy = sinon.spy();
|
|
274
281
|
|
|
275
282
|
voiceaService.on(EVENT_TRIGGERS.VOICEA_ANNOUNCEMENT, spy);
|
|
@@ -401,6 +408,7 @@ describe('plugin-voicea', () => {
|
|
|
401
408
|
|
|
402
409
|
it('turns on captions', async () => {
|
|
403
410
|
const announcementSpy = sinon.spy(voiceaService, 'announce');
|
|
411
|
+
const updateSubchannelSubscriptionsAndSyncCaptionStateSpy = sinon.spy(voiceaService, 'updateSubchannelSubscriptionsAndSyncCaptionState');
|
|
404
412
|
|
|
405
413
|
const triggerSpy = sinon.spy();
|
|
406
414
|
|
|
@@ -421,6 +429,11 @@ describe('plugin-voicea', () => {
|
|
|
421
429
|
assert.calledOnceWithExactly(triggerSpy);
|
|
422
430
|
|
|
423
431
|
assert.calledOnce(announcementSpy);
|
|
432
|
+
assert.calledOnceWithExactly(
|
|
433
|
+
updateSubchannelSubscriptionsAndSyncCaptionStateSpy,
|
|
434
|
+
{ subscribe: ['transcription'] },
|
|
435
|
+
true
|
|
436
|
+
);
|
|
424
437
|
});
|
|
425
438
|
|
|
426
439
|
it("should handle request fail", async () => {
|
|
@@ -455,17 +468,61 @@ describe('plugin-voicea', () => {
|
|
|
455
468
|
});
|
|
456
469
|
});
|
|
457
470
|
|
|
471
|
+
describe('#isLLMConnected', () => {
|
|
472
|
+
it('returns true when the default llm connection is connected', () => {
|
|
473
|
+
voiceaService.webex.internal.llm.isConnected.callsFake((channel) =>
|
|
474
|
+
channel === LLM_PRACTICE_SESSION ? false : true
|
|
475
|
+
);
|
|
476
|
+
|
|
477
|
+
assert.equal(voiceaService.isLLMConnected(), true);
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
it('returns true when only the practice session llm connection is connected', () => {
|
|
481
|
+
voiceaService.webex.internal.llm.isConnected.callsFake((channel) =>
|
|
482
|
+
channel === LLM_PRACTICE_SESSION
|
|
483
|
+
);
|
|
484
|
+
|
|
485
|
+
assert.equal(voiceaService.isLLMConnected(), true);
|
|
486
|
+
});
|
|
487
|
+
|
|
488
|
+
it('returns false when neither llm connection is connected', () => {
|
|
489
|
+
voiceaService.webex.internal.llm.isConnected.returns(false);
|
|
490
|
+
|
|
491
|
+
assert.equal(voiceaService.isLLMConnected(), false);
|
|
492
|
+
});
|
|
493
|
+
});
|
|
494
|
+
|
|
495
|
+
describe('#getIsCaptionBoxOn', () => {
|
|
496
|
+
beforeEach(() => {
|
|
497
|
+
voiceaService.isCaptionBoxOn = false;
|
|
498
|
+
});
|
|
499
|
+
|
|
500
|
+
it('returns false when captions are disabled', () => {
|
|
501
|
+
voiceaService.isCaptionBoxOn = false;
|
|
502
|
+
|
|
503
|
+
const result = voiceaService.getIsCaptionBoxOn();
|
|
504
|
+
|
|
505
|
+
assert.equal(result, false);
|
|
506
|
+
});
|
|
507
|
+
|
|
508
|
+
it('returns true when captions are enabled', () => {
|
|
509
|
+
voiceaService.isCaptionBoxOn = true;
|
|
510
|
+
|
|
511
|
+
const result = voiceaService.getIsCaptionBoxOn();
|
|
512
|
+
|
|
513
|
+
assert.equal(result, true);
|
|
514
|
+
});
|
|
515
|
+
});
|
|
516
|
+
|
|
458
517
|
describe("#announce", () => {
|
|
459
|
-
let
|
|
518
|
+
let isAnnounceProcessed, sendAnnouncement;
|
|
460
519
|
beforeEach(() => {
|
|
461
|
-
voiceaService.webex.internal.llm.isConnected.returns(true);
|
|
462
520
|
sendAnnouncement = sinon.stub(voiceaService, 'sendAnnouncement');
|
|
463
|
-
|
|
521
|
+
isAnnounceProcessed = sinon.stub(voiceaService, 'isAnnounceProcessed').returns(false)
|
|
464
522
|
});
|
|
465
523
|
|
|
466
524
|
afterEach(() => {
|
|
467
|
-
|
|
468
|
-
isAnnounceProcessing.restore();
|
|
525
|
+
isAnnounceProcessed.restore();
|
|
469
526
|
sendAnnouncement.restore();
|
|
470
527
|
});
|
|
471
528
|
|
|
@@ -480,8 +537,18 @@ describe('plugin-voicea', () => {
|
|
|
480
537
|
assert.notCalled(sendAnnouncement);
|
|
481
538
|
});
|
|
482
539
|
|
|
540
|
+
it('announce to llm data channel when only practice session is connected', ()=> {
|
|
541
|
+
voiceaService.webex.internal.llm.isConnected.callsFake((channel) =>
|
|
542
|
+
channel === LLM_PRACTICE_SESSION
|
|
543
|
+
);
|
|
544
|
+
|
|
545
|
+
voiceaService.announce();
|
|
546
|
+
|
|
547
|
+
assert.calledOnce(sendAnnouncement);
|
|
548
|
+
});
|
|
549
|
+
|
|
483
550
|
it('should not announce duplicate', () => {
|
|
484
|
-
|
|
551
|
+
isAnnounceProcessed.returns(true);
|
|
485
552
|
voiceaService.announce();
|
|
486
553
|
assert.notCalled(sendAnnouncement);
|
|
487
554
|
})
|
|
@@ -510,13 +577,11 @@ describe('plugin-voicea', () => {
|
|
|
510
577
|
beforeEach(() => {
|
|
511
578
|
requestTurnOnCaptions = sinon.stub(voiceaService, 'requestTurnOnCaptions');
|
|
512
579
|
voiceaService.captionStatus = 'idle';
|
|
513
|
-
voiceaService.webex.internal.llm.isConnected.returns(true);
|
|
514
580
|
});
|
|
515
581
|
|
|
516
582
|
afterEach(() => {
|
|
517
583
|
requestTurnOnCaptions.restore();
|
|
518
584
|
voiceaService.captionStatus = 'idle';
|
|
519
|
-
voiceaService.webex.internal.llm.isConnected.returns(true);
|
|
520
585
|
});
|
|
521
586
|
|
|
522
587
|
it('call request turn on captions', () => {
|
|
@@ -525,13 +590,27 @@ describe('plugin-voicea', () => {
|
|
|
525
590
|
assert.calledOnce(requestTurnOnCaptions);
|
|
526
591
|
});
|
|
527
592
|
|
|
528
|
-
it(
|
|
593
|
+
it('throws before turning on captions when llm is not connected', async () => {
|
|
529
594
|
voiceaService.captionStatus = 'idle';
|
|
530
|
-
voiceaService.webex.internal.llm.isConnected.returns(
|
|
531
|
-
|
|
595
|
+
voiceaService.webex.internal.llm.isConnected.returns(false);
|
|
596
|
+
|
|
597
|
+
await assert.isRejected(
|
|
598
|
+
voiceaService.turnOnCaptions(),
|
|
599
|
+
'can not turn on captions before llm connected'
|
|
600
|
+
);
|
|
532
601
|
assert.notCalled(requestTurnOnCaptions);
|
|
533
602
|
});
|
|
534
603
|
|
|
604
|
+
it('turns on captions when only the practice session llm connection is connected', () => {
|
|
605
|
+
voiceaService.webex.internal.llm.isConnected.callsFake((channel) =>
|
|
606
|
+
channel === LLM_PRACTICE_SESSION
|
|
607
|
+
);
|
|
608
|
+
|
|
609
|
+
voiceaService.turnOnCaptions();
|
|
610
|
+
|
|
611
|
+
assert.calledOnce(requestTurnOnCaptions);
|
|
612
|
+
});
|
|
613
|
+
|
|
535
614
|
it('should not turn on duplicate when processing', () => {
|
|
536
615
|
voiceaService.captionStatus = 'sending';
|
|
537
616
|
voiceaService.turnOnCaptions();
|
|
@@ -1205,5 +1284,300 @@ describe('plugin-voicea', () => {
|
|
|
1205
1284
|
});
|
|
1206
1285
|
});
|
|
1207
1286
|
|
|
1287
|
+
describe('#updateSubchannelSubscriptions', () => {
|
|
1288
|
+
beforeEach(() => {
|
|
1289
|
+
const mockWebSocket = new MockWebSocket();
|
|
1290
|
+
|
|
1291
|
+
sinon.stub(voiceaService, 'getPublishTransport').returns({
|
|
1292
|
+
socket: mockWebSocket,
|
|
1293
|
+
datachannelUrl: 'mock-datachannel-uri',
|
|
1294
|
+
});
|
|
1295
|
+
|
|
1296
|
+
voiceaService.seqNum = 1;
|
|
1297
|
+
|
|
1298
|
+
voiceaService.isLLMConnected = sinon.stub().returns(true);
|
|
1299
|
+
voiceaService.webex.internal.llm.isDataChannelTokenEnabled = sinon.stub().resolves(true);
|
|
1300
|
+
});
|
|
1301
|
+
|
|
1302
|
+
it('sends subchannelSubscriptionRequest with subscribe and unsubscribe lists', async () => {
|
|
1303
|
+
await voiceaService.updateSubchannelSubscriptions({
|
|
1304
|
+
subscribe: ['transcription'],
|
|
1305
|
+
unsubscribe: ['polls'],
|
|
1306
|
+
});
|
|
1307
|
+
|
|
1308
|
+
const socket = voiceaService.getPublishTransport().socket;
|
|
1309
|
+
|
|
1310
|
+
sinon.assert.calledOnceWithExactly(
|
|
1311
|
+
socket.send,
|
|
1312
|
+
{
|
|
1313
|
+
id: '1',
|
|
1314
|
+
type: 'subchannelSubscriptionRequest',
|
|
1315
|
+
data: {
|
|
1316
|
+
datachannelUri: 'mock-datachannel-uri',
|
|
1317
|
+
subscribe: ['transcription'],
|
|
1318
|
+
unsubscribe: ['polls'],
|
|
1319
|
+
},
|
|
1320
|
+
trackingId: sinon.match.string,
|
|
1321
|
+
}
|
|
1322
|
+
);
|
|
1323
|
+
|
|
1324
|
+
sinon.assert.match(voiceaService.seqNum, 2);
|
|
1325
|
+
});
|
|
1326
|
+
|
|
1327
|
+
it('sends empty arrays when no subscribe/unsubscribe provided', async () => {
|
|
1328
|
+
await voiceaService.updateSubchannelSubscriptions({});
|
|
1329
|
+
|
|
1330
|
+
const socket = voiceaService.getPublishTransport().socket;
|
|
1331
|
+
|
|
1332
|
+
sinon.assert.calledOnceWithExactly(
|
|
1333
|
+
socket.send,
|
|
1334
|
+
{
|
|
1335
|
+
id: '1',
|
|
1336
|
+
type: 'subchannelSubscriptionRequest',
|
|
1337
|
+
data: {
|
|
1338
|
+
datachannelUri: 'mock-datachannel-uri',
|
|
1339
|
+
subscribe: [],
|
|
1340
|
+
unsubscribe: [],
|
|
1341
|
+
},
|
|
1342
|
+
trackingId: sinon.match.string,
|
|
1343
|
+
}
|
|
1344
|
+
);
|
|
1345
|
+
|
|
1346
|
+
sinon.assert.match(voiceaService.seqNum, 2);
|
|
1347
|
+
});
|
|
1348
|
+
|
|
1349
|
+
it('does nothing when LLM is not connected', async () => {
|
|
1350
|
+
voiceaService.isLLMConnected = sinon.stub().returns(false);
|
|
1351
|
+
|
|
1352
|
+
await voiceaService.updateSubchannelSubscriptions({
|
|
1353
|
+
subscribe: ['transcription'],
|
|
1354
|
+
});
|
|
1355
|
+
|
|
1356
|
+
const socket = voiceaService.getPublishTransport().socket;
|
|
1357
|
+
|
|
1358
|
+
sinon.assert.notCalled(socket.send);
|
|
1359
|
+
sinon.assert.match(voiceaService.seqNum, 1);
|
|
1360
|
+
});
|
|
1361
|
+
|
|
1362
|
+
it('does nothing when dataChannelToken is not enabled', async () => {
|
|
1363
|
+
voiceaService.webex.internal.llm.isDataChannelTokenEnabled = sinon.stub().resolves(false);
|
|
1364
|
+
|
|
1365
|
+
await voiceaService.updateSubchannelSubscriptions({
|
|
1366
|
+
subscribe: ['transcription'],
|
|
1367
|
+
});
|
|
1368
|
+
|
|
1369
|
+
const socket = voiceaService.getPublishTransport().socket;
|
|
1370
|
+
|
|
1371
|
+
sinon.assert.notCalled(socket.send);
|
|
1372
|
+
sinon.assert.match(voiceaService.seqNum, 1);
|
|
1373
|
+
});
|
|
1374
|
+
});
|
|
1375
|
+
|
|
1376
|
+
|
|
1377
|
+
describe('#updateSubchannelSubscriptionsAndSyncCaptionState', () => {
|
|
1378
|
+
beforeEach(() => {
|
|
1379
|
+
const mockWebSocket = new MockWebSocket();
|
|
1380
|
+
voiceaService.webex.internal.llm.socket = mockWebSocket;
|
|
1381
|
+
|
|
1382
|
+
voiceaService.webex.internal.llm.getDatachannelUrl = sinon.stub().returns('mock-datachannel-uri');
|
|
1383
|
+
|
|
1384
|
+
voiceaService.seqNum = 1;
|
|
1385
|
+
|
|
1386
|
+
voiceaService.isLLMConnected = sinon.stub().returns(true);
|
|
1387
|
+
voiceaService.webex.internal.llm.isDataChannelTokenEnabled = sinon.stub().resolves(true);
|
|
1388
|
+
|
|
1389
|
+
sinon.spy(voiceaService, 'updateSubchannelSubscriptions');
|
|
1390
|
+
});
|
|
1391
|
+
|
|
1392
|
+
afterEach(() => {
|
|
1393
|
+
sinon.restore();
|
|
1394
|
+
});
|
|
1395
|
+
|
|
1396
|
+
it('updates caption intent and forwards subscribe/unsubscribe to updateSubchannelSubscriptions', async () => {
|
|
1397
|
+
await voiceaService.updateSubchannelSubscriptionsAndSyncCaptionState(
|
|
1398
|
+
{
|
|
1399
|
+
subscribe: ['transcription'],
|
|
1400
|
+
unsubscribe: ['polls'],
|
|
1401
|
+
},
|
|
1402
|
+
true
|
|
1403
|
+
);
|
|
1404
|
+
|
|
1405
|
+
assert.equal(voiceaService.isCaptionBoxOn, true);
|
|
1406
|
+
|
|
1407
|
+
assert.calledOnceWithExactly(
|
|
1408
|
+
voiceaService.updateSubchannelSubscriptions,
|
|
1409
|
+
{
|
|
1410
|
+
subscribe: ['transcription'],
|
|
1411
|
+
unsubscribe: ['polls'],
|
|
1412
|
+
}
|
|
1413
|
+
);
|
|
1414
|
+
});
|
|
1415
|
+
|
|
1416
|
+
it('sets caption intent to false when isCCBoxOpen is false', async () => {
|
|
1417
|
+
await voiceaService.updateSubchannelSubscriptionsAndSyncCaptionState(
|
|
1418
|
+
{ subscribe: ['transcription'] },
|
|
1419
|
+
false
|
|
1420
|
+
);
|
|
1421
|
+
|
|
1422
|
+
assert.equal(voiceaService.isCaptionBoxOn, false);
|
|
1423
|
+
|
|
1424
|
+
assert.calledOnceWithExactly(
|
|
1425
|
+
voiceaService.updateSubchannelSubscriptions,
|
|
1426
|
+
{ subscribe: ['transcription'] }
|
|
1427
|
+
);
|
|
1428
|
+
});
|
|
1429
|
+
|
|
1430
|
+
it('defaults subscribe/unsubscribe to empty arrays when options is empty', async () => {
|
|
1431
|
+
await voiceaService.updateSubchannelSubscriptionsAndSyncCaptionState({}, true);
|
|
1432
|
+
|
|
1433
|
+
assert.equal(voiceaService.isCaptionBoxOn, true);
|
|
1434
|
+
|
|
1435
|
+
assert.calledOnceWithExactly(
|
|
1436
|
+
voiceaService.updateSubchannelSubscriptions,
|
|
1437
|
+
{}
|
|
1438
|
+
);
|
|
1439
|
+
});
|
|
1440
|
+
|
|
1441
|
+
it('still updates caption intent even if updateSubchannelSubscriptions does nothing (e.g., LLM not connected)', async () => {
|
|
1442
|
+
voiceaService.isLLMConnected = sinon.stub().returns(false);
|
|
1443
|
+
|
|
1444
|
+
await voiceaService.updateSubchannelSubscriptionsAndSyncCaptionState(
|
|
1445
|
+
{ subscribe: ['transcription'] },
|
|
1446
|
+
true
|
|
1447
|
+
);
|
|
1448
|
+
|
|
1449
|
+
assert.equal(voiceaService.isCaptionBoxOn, true);
|
|
1450
|
+
|
|
1451
|
+
assert.calledOnceWithExactly(
|
|
1452
|
+
voiceaService.updateSubchannelSubscriptions,
|
|
1453
|
+
{ subscribe: ['transcription'] }
|
|
1454
|
+
);
|
|
1455
|
+
});
|
|
1456
|
+
});
|
|
1457
|
+
|
|
1458
|
+
describe('#multiple llm connections', () => {
|
|
1459
|
+
let defaultSocket;
|
|
1460
|
+
let practiceSocket;
|
|
1461
|
+
let isPracticeSessionConnected;
|
|
1462
|
+
|
|
1463
|
+
beforeEach(() => {
|
|
1464
|
+
defaultSocket = new MockWebSocket();
|
|
1465
|
+
practiceSocket = new MockWebSocket();
|
|
1466
|
+
isPracticeSessionConnected = true;
|
|
1467
|
+
|
|
1468
|
+
voiceaService.webex.internal.llm.socket = defaultSocket;
|
|
1469
|
+
voiceaService.webex.internal.llm.isConnected.callsFake((channel) =>
|
|
1470
|
+
channel === LLM_PRACTICE_SESSION ? isPracticeSessionConnected : true
|
|
1471
|
+
);
|
|
1472
|
+
voiceaService.webex.internal.llm.getSocket.callsFake((channel) =>
|
|
1473
|
+
channel === LLM_PRACTICE_SESSION ? practiceSocket : undefined
|
|
1474
|
+
);
|
|
1475
|
+
voiceaService.webex.internal.llm.getBinding.callsFake((channel) =>
|
|
1476
|
+
channel === LLM_PRACTICE_SESSION ? 'practice-binding' : 'default-binding'
|
|
1477
|
+
);
|
|
1478
|
+
voiceaService.seqNum = 1;
|
|
1479
|
+
});
|
|
1480
|
+
|
|
1481
|
+
it('sendAnnouncement uses the practice session socket and binding when available', () => {
|
|
1482
|
+
voiceaService.announceStatus = 'idle';
|
|
1483
|
+
|
|
1484
|
+
voiceaService.sendAnnouncement();
|
|
1485
|
+
|
|
1486
|
+
assert.calledOnce(practiceSocket.send);
|
|
1487
|
+
assert.notCalled(defaultSocket.send);
|
|
1488
|
+
|
|
1489
|
+
const sent = practiceSocket.send.getCall(0).args[0];
|
|
1490
|
+
expect(sent).to.have.nested.property('recipients.route', 'practice-binding');
|
|
1491
|
+
});
|
|
1492
|
+
|
|
1493
|
+
it('sendAnnouncement falls back to the default socket and binding when the practice session is not connected', () => {
|
|
1494
|
+
voiceaService.announceStatus = 'idle';
|
|
1495
|
+
isPracticeSessionConnected = false;
|
|
1496
|
+
|
|
1497
|
+
voiceaService.sendAnnouncement();
|
|
1498
|
+
|
|
1499
|
+
assert.calledOnce(defaultSocket.send);
|
|
1500
|
+
assert.notCalled(practiceSocket.send);
|
|
1501
|
+
|
|
1502
|
+
const sent = defaultSocket.send.getCall(0).args[0];
|
|
1503
|
+
expect(sent).to.have.nested.property('recipients.route', 'default-binding');
|
|
1504
|
+
});
|
|
1505
|
+
|
|
1506
|
+
it('requestLanguage uses the practice session socket and binding when available', () => {
|
|
1507
|
+
voiceaService.requestLanguage('fr');
|
|
1508
|
+
|
|
1509
|
+
assert.calledOnce(practiceSocket.send);
|
|
1510
|
+
assert.notCalled(defaultSocket.send);
|
|
1511
|
+
|
|
1512
|
+
const sent = practiceSocket.send.getCall(0).args[0];
|
|
1513
|
+
expect(sent).to.have.nested.property('recipients.route', 'practice-binding');
|
|
1514
|
+
expect(sent).to.have.nested.property('data.clientPayload.translationLanguage', 'fr');
|
|
1515
|
+
});
|
|
1516
|
+
|
|
1517
|
+
it('requestLanguage falls back to the default socket and binding when the practice session is not connected', () => {
|
|
1518
|
+
isPracticeSessionConnected = false;
|
|
1519
|
+
|
|
1520
|
+
voiceaService.requestLanguage('fr');
|
|
1521
|
+
|
|
1522
|
+
assert.calledOnce(defaultSocket.send);
|
|
1523
|
+
assert.notCalled(practiceSocket.send);
|
|
1524
|
+
|
|
1525
|
+
const sent = defaultSocket.send.getCall(0).args[0];
|
|
1526
|
+
expect(sent).to.have.nested.property('recipients.route', 'default-binding');
|
|
1527
|
+
expect(sent).to.have.nested.property('data.clientPayload.translationLanguage', 'fr');
|
|
1528
|
+
});
|
|
1529
|
+
|
|
1530
|
+
it('sendManualClosedCaption uses the practice session socket and binding when available', () => {
|
|
1531
|
+
voiceaService.sendManualClosedCaption('caption', 123, [456], true);
|
|
1532
|
+
|
|
1533
|
+
assert.calledOnce(practiceSocket.send);
|
|
1534
|
+
assert.notCalled(defaultSocket.send);
|
|
1535
|
+
|
|
1536
|
+
const sent = practiceSocket.send.getCall(0).args[0];
|
|
1537
|
+
expect(sent).to.have.nested.property('recipients.route', 'practice-binding');
|
|
1538
|
+
expect(sent).to.have.nested.property(
|
|
1539
|
+
'data.transcriptPayload.type',
|
|
1540
|
+
'manual_caption_final_result'
|
|
1541
|
+
);
|
|
1542
|
+
});
|
|
1543
|
+
|
|
1544
|
+
it('sendManualClosedCaption falls back to the default socket and binding when the practice session is not connected', () => {
|
|
1545
|
+
isPracticeSessionConnected = false;
|
|
1546
|
+
|
|
1547
|
+
voiceaService.sendManualClosedCaption('caption', 123, [456], false);
|
|
1548
|
+
|
|
1549
|
+
assert.calledOnce(defaultSocket.send);
|
|
1550
|
+
assert.notCalled(practiceSocket.send);
|
|
1551
|
+
|
|
1552
|
+
const sent = defaultSocket.send.getCall(0).args[0];
|
|
1553
|
+
expect(sent).to.have.nested.property('recipients.route', 'default-binding');
|
|
1554
|
+
expect(sent).to.have.nested.property(
|
|
1555
|
+
'data.transcriptPayload.type',
|
|
1556
|
+
'manual_caption_interim_result'
|
|
1557
|
+
);
|
|
1558
|
+
});
|
|
1559
|
+
|
|
1560
|
+
it('processes relay events from the practice session channel', async () => {
|
|
1561
|
+
const announcementSpy = sinon.spy(voiceaService, 'processAnnouncementMessage');
|
|
1562
|
+
|
|
1563
|
+
voiceaService.listenToEvents();
|
|
1564
|
+
|
|
1565
|
+
// eslint-disable-next-line no-underscore-dangle
|
|
1566
|
+
await voiceaService.webex.internal.llm._emit(`event:relay.event:${LLM_PRACTICE_SESSION}`, {
|
|
1567
|
+
headers: {from: 'svc-practice'},
|
|
1568
|
+
data: {
|
|
1569
|
+
relayType: 'voicea.annc',
|
|
1570
|
+
voiceaPayload: {
|
|
1571
|
+
translation: {allowed_languages: ['en'], max_languages: 1},
|
|
1572
|
+
ASR: {spoken_languages: ['en']},
|
|
1573
|
+
},
|
|
1574
|
+
},
|
|
1575
|
+
sequenceNumber: 10,
|
|
1576
|
+
});
|
|
1577
|
+
|
|
1578
|
+
assert.calledOnce(announcementSpy);
|
|
1579
|
+
assert.equal(voiceaService.captionServiceId, 'svc-practice');
|
|
1580
|
+
});
|
|
1581
|
+
});
|
|
1208
1582
|
});
|
|
1209
1583
|
});
|