@webex/internal-plugin-voicea 3.0.0-beta.4 → 3.0.0-beta.400
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -21
- package/dist/constants.js +13 -3
- package/dist/constants.js.map +1 -1
- package/dist/index.js +13 -11
- package/dist/index.js.map +1 -1
- package/dist/utils.js +0 -4
- package/dist/utils.js.map +1 -1
- package/dist/voicea.js +136 -148
- package/dist/voicea.js.map +1 -1
- package/dist/voicea.types.js +0 -1
- package/dist/voicea.types.js.map +1 -1
- package/package.json +8 -9
- package/src/constants.ts +15 -3
- package/src/index.ts +1 -0
- package/src/utils.ts +1 -2
- package/src/voicea.ts +149 -50
- package/src/voicea.types.ts +3 -3
- package/test/unit/spec/voicea.js +156 -44
package/test/unit/spec/voicea.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import 'jsdom-global/register';
|
|
1
2
|
import MockWebex from '@webex/test-helper-mock-webex';
|
|
2
3
|
import MockWebSocket from '@webex/test-helper-mock-web-socket';
|
|
3
|
-
import {assert} from '@webex/test-helper-chai';
|
|
4
|
+
import {assert, expect} from '@webex/test-helper-chai';
|
|
4
5
|
import sinon from 'sinon';
|
|
5
6
|
import Mercury from '@webex/internal-plugin-mercury';
|
|
6
7
|
import LLMChannel from '@webex/internal-plugin-llm';
|
|
@@ -41,17 +42,26 @@ describe('plugin-voicea', () => {
|
|
|
41
42
|
});
|
|
42
43
|
});
|
|
43
44
|
|
|
45
|
+
describe("#constructor", () => {
|
|
46
|
+
it('should init status', () => {
|
|
47
|
+
assert.equal(voiceaService.announceStatus, 'idle');
|
|
48
|
+
assert.equal(voiceaService.captionStatus, 'idle');
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
|
|
44
52
|
describe('#sendAnnouncement', () => {
|
|
45
53
|
beforeEach(async () => {
|
|
46
54
|
const mockWebSocket = new MockWebSocket();
|
|
47
55
|
|
|
48
56
|
voiceaService.webex.internal.llm.socket = mockWebSocket;
|
|
57
|
+
voiceaService.announceStatus = "idle";
|
|
49
58
|
});
|
|
50
59
|
|
|
51
60
|
it("sends announcement if voicea hasn't joined", () => {
|
|
52
61
|
const spy = sinon.spy(voiceaService, 'listenToEvents');
|
|
53
62
|
|
|
54
63
|
voiceaService.sendAnnouncement();
|
|
64
|
+
assert.equal(voiceaService.announceStatus, 'joining');
|
|
55
65
|
assert.calledOnce(spy);
|
|
56
66
|
|
|
57
67
|
assert.calledOnceWithExactly(voiceaService.webex.internal.llm.socket.send, {
|
|
@@ -98,16 +108,14 @@ describe('plugin-voicea', () => {
|
|
|
98
108
|
data: {relayType: 'voicea.annc', voiceaPayload: {}},
|
|
99
109
|
});
|
|
100
110
|
|
|
101
|
-
assert.equal(voiceaService.hasVoiceaJoined, true);
|
|
102
111
|
assert.equal(voiceaService.areCaptionsEnabled, true);
|
|
103
|
-
assert.equal(voiceaService.isTranscribingEnabled, true);
|
|
104
112
|
assert.equal(voiceaService.vmcDeviceId, 'ws');
|
|
105
113
|
|
|
106
114
|
voiceaService.deregisterEvents();
|
|
107
|
-
assert.equal(voiceaService.hasVoiceaJoined, false);
|
|
108
115
|
assert.equal(voiceaService.areCaptionsEnabled, false);
|
|
109
|
-
assert.equal(voiceaService.isTranscribingEnabled, false);
|
|
110
116
|
assert.equal(voiceaService.vmcDeviceId, undefined);
|
|
117
|
+
assert.equal(voiceaService.announceStatus, 'idle');
|
|
118
|
+
assert.equal(voiceaService.captionStatus, 'idle');
|
|
111
119
|
});
|
|
112
120
|
});
|
|
113
121
|
describe('#processAnnouncementMessage', () => {
|
|
@@ -194,28 +202,38 @@ describe('plugin-voicea', () => {
|
|
|
194
202
|
sinon.match({
|
|
195
203
|
method: 'PUT',
|
|
196
204
|
url: `${locusUrl}/controls/`,
|
|
197
|
-
body: {
|
|
205
|
+
body: {
|
|
206
|
+
transcribe: {
|
|
207
|
+
spokenLanguage: languageCode
|
|
208
|
+
}
|
|
209
|
+
},
|
|
198
210
|
})
|
|
199
211
|
);
|
|
200
212
|
});
|
|
201
213
|
});
|
|
202
214
|
|
|
203
|
-
describe('#
|
|
215
|
+
describe('#requestTurnOnCaptions', () => {
|
|
204
216
|
beforeEach(async () => {
|
|
205
217
|
const mockWebSocket = new MockWebSocket();
|
|
206
218
|
|
|
207
219
|
voiceaService.webex.internal.llm.socket = mockWebSocket;
|
|
220
|
+
voiceaService.captionStatus = 'idle';
|
|
208
221
|
});
|
|
209
222
|
|
|
223
|
+
afterEach( () => {
|
|
224
|
+
voiceaService.captionStatus = 'idle';
|
|
225
|
+
})
|
|
226
|
+
|
|
210
227
|
it('turns on captions', async () => {
|
|
211
|
-
const announcementSpy = sinon.spy(voiceaService, '
|
|
228
|
+
const announcementSpy = sinon.spy(voiceaService, 'announce');
|
|
212
229
|
|
|
213
230
|
const triggerSpy = sinon.spy();
|
|
214
231
|
|
|
215
232
|
voiceaService.on(EVENT_TRIGGERS.CAPTIONS_TURNED_ON, triggerSpy);
|
|
216
233
|
voiceaService.listenToEvents();
|
|
217
234
|
|
|
218
|
-
await voiceaService.
|
|
235
|
+
await voiceaService.requestTurnOnCaptions();
|
|
236
|
+
assert.equal(voiceaService.captionStatus, 'enabled');
|
|
219
237
|
sinon.assert.calledWith(
|
|
220
238
|
voiceaService.request,
|
|
221
239
|
sinon.match({
|
|
@@ -230,18 +248,119 @@ describe('plugin-voicea', () => {
|
|
|
230
248
|
assert.calledOnce(announcementSpy);
|
|
231
249
|
});
|
|
232
250
|
|
|
233
|
-
it("
|
|
234
|
-
|
|
251
|
+
it("should handle request fail", async () => {
|
|
252
|
+
voiceaService.captionStatus = 'sending';
|
|
253
|
+
voiceaService.request = sinon.stub().rejects();
|
|
254
|
+
|
|
255
|
+
try {
|
|
256
|
+
await voiceaService.requestTurnOnCaptions();
|
|
257
|
+
} catch (error) {
|
|
258
|
+
expect(error.message).to.include('turn on captions fail');
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
assert.equal(voiceaService.captionStatus, 'idle');
|
|
262
|
+
});
|
|
263
|
+
});
|
|
235
264
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
265
|
+
describe("#isAnnounceProcessing", () => {
|
|
266
|
+
afterEach(() => {
|
|
267
|
+
voiceaService.announceStatus = 'idle';
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
['joining', 'joined'].forEach((status) => {
|
|
271
|
+
it(`should return true when status is ${status}`, () => {
|
|
272
|
+
voiceaService.announceStatus = status;
|
|
273
|
+
assert.equal(voiceaService.isAnnounceProcessing(), true);
|
|
240
274
|
});
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
it('should return false when status is not processing status', () => {
|
|
278
|
+
voiceaService.announceStatus = 'idle';
|
|
279
|
+
assert.equal(voiceaService.isAnnounceProcessing(), false);
|
|
280
|
+
});
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
describe("#announce", () => {
|
|
284
|
+
let isAnnounceProcessing, sendAnnouncement;
|
|
285
|
+
beforeEach(() => {
|
|
286
|
+
voiceaService.webex.internal.llm.isConnected.returns(true);
|
|
287
|
+
sendAnnouncement = sinon.stub(voiceaService, 'sendAnnouncement');
|
|
288
|
+
isAnnounceProcessing = sinon.stub(voiceaService, 'isAnnounceProcessing').returns(false)
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
afterEach(() => {
|
|
292
|
+
voiceaService.webex.internal.llm.isConnected.returns(true);
|
|
293
|
+
isAnnounceProcessing.restore();
|
|
294
|
+
sendAnnouncement.restore();
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
it('announce to llm data channel', ()=> {
|
|
298
|
+
voiceaService.announce();
|
|
299
|
+
assert.calledOnce(sendAnnouncement);
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
it('announce to llm data channel before llm connected', ()=> {
|
|
303
|
+
voiceaService.webex.internal.llm.isConnected.returns(false);
|
|
304
|
+
assert.throws(() => voiceaService.announce(), "voicea can not announce before llm connected");
|
|
305
|
+
assert.notCalled(sendAnnouncement);
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
it('should not announce duplicate', () => {
|
|
309
|
+
isAnnounceProcessing.returns(true);
|
|
310
|
+
voiceaService.announce();
|
|
311
|
+
assert.notCalled(sendAnnouncement);
|
|
312
|
+
})
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
describe("#isCaptionProcessing", () => {
|
|
316
|
+
afterEach(() => {
|
|
317
|
+
voiceaService.captionStatus = 'idle';
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
['sending', 'enabled'].forEach((status) => {
|
|
321
|
+
it(`should return true when status is ${status}`, () => {
|
|
322
|
+
voiceaService.captionStatus = status;
|
|
323
|
+
assert.equal(voiceaService.isCaptionProcessing(), true);
|
|
324
|
+
});
|
|
325
|
+
});
|
|
241
326
|
|
|
242
|
-
|
|
327
|
+
it('should return false when status is not processing status', () => {
|
|
328
|
+
voiceaService.captionStatus = 'idle';
|
|
329
|
+
assert.equal(voiceaService.isCaptionProcessing(), false);
|
|
330
|
+
});
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
describe('#turnOnCaptions', () => {
|
|
334
|
+
let requestTurnOnCaptions, isCaptionProcessing;
|
|
335
|
+
beforeEach(() => {
|
|
336
|
+
requestTurnOnCaptions = sinon.stub(voiceaService, 'requestTurnOnCaptions');
|
|
337
|
+
isCaptionProcessing = sinon.stub(voiceaService, 'isCaptionProcessing').returns(false);
|
|
338
|
+
voiceaService.webex.internal.llm.isConnected.returns(true);
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
afterEach(() => {
|
|
342
|
+
requestTurnOnCaptions.restore();
|
|
343
|
+
isCaptionProcessing.restore();
|
|
344
|
+
voiceaService.webex.internal.llm.isConnected.returns(true);
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
it('call request turn on captions', () => {
|
|
348
|
+
isCaptionProcessing.returns(false);
|
|
349
|
+
voiceaService.turnOnCaptions();
|
|
350
|
+
assert.calledOnce(requestTurnOnCaptions);
|
|
351
|
+
});
|
|
243
352
|
|
|
244
|
-
|
|
353
|
+
it("turns on captions before llm connected", () => {
|
|
354
|
+
isCaptionProcessing.returns(false);
|
|
355
|
+
voiceaService.webex.internal.llm.isConnected.returns(true);
|
|
356
|
+
// assert.throws(() => voiceaService.turnOnCaptions(), "can not turn on captions before llm connected");
|
|
357
|
+
assert.notCalled(requestTurnOnCaptions);
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
it('should not turn on duplicate when processing', () => {
|
|
361
|
+
isCaptionProcessing.returns(true);
|
|
362
|
+
voiceaService.turnOnCaptions();
|
|
363
|
+
assert.notCalled(voiceaService.requestTurnOnCaptions);
|
|
245
364
|
});
|
|
246
365
|
});
|
|
247
366
|
|
|
@@ -263,9 +382,6 @@ describe('plugin-voicea', () => {
|
|
|
263
382
|
data: {relayType: 'voicea.annc', voiceaPayload: {}},
|
|
264
383
|
});
|
|
265
384
|
|
|
266
|
-
const triggerSpy = sinon.spy();
|
|
267
|
-
|
|
268
|
-
voiceaService.on(EVENT_TRIGGERS.TRANSCRIBING_ON, triggerSpy);
|
|
269
385
|
voiceaService.listenToEvents();
|
|
270
386
|
|
|
271
387
|
await voiceaService.toggleTranscribing(true);
|
|
@@ -278,16 +394,12 @@ describe('plugin-voicea', () => {
|
|
|
278
394
|
})
|
|
279
395
|
);
|
|
280
396
|
|
|
281
|
-
assert.calledOnce(triggerSpy);
|
|
282
397
|
assert.notCalled(announcementSpy);
|
|
283
398
|
});
|
|
284
399
|
|
|
285
400
|
it('turns on transcribing with CC disabled', async () => {
|
|
286
401
|
const announcementSpy = sinon.spy(voiceaService, 'sendAnnouncement');
|
|
287
402
|
|
|
288
|
-
const triggerSpy = sinon.spy();
|
|
289
|
-
|
|
290
|
-
voiceaService.on(EVENT_TRIGGERS.TRANSCRIBING_ON, triggerSpy);
|
|
291
403
|
voiceaService.listenToEvents();
|
|
292
404
|
|
|
293
405
|
await voiceaService.toggleTranscribing(true);
|
|
@@ -300,7 +412,6 @@ describe('plugin-voicea', () => {
|
|
|
300
412
|
})
|
|
301
413
|
);
|
|
302
414
|
|
|
303
|
-
assert.calledOnce(triggerSpy);
|
|
304
415
|
assert.calledOnce(announcementSpy);
|
|
305
416
|
});
|
|
306
417
|
|
|
@@ -309,9 +420,6 @@ describe('plugin-voicea', () => {
|
|
|
309
420
|
|
|
310
421
|
const announcementSpy = sinon.spy(voiceaService, 'sendAnnouncement');
|
|
311
422
|
|
|
312
|
-
const triggerSpy = sinon.spy();
|
|
313
|
-
|
|
314
|
-
voiceaService.on(EVENT_TRIGGERS.TRANSCRIBING_OFF, triggerSpy);
|
|
315
423
|
voiceaService.listenToEvents();
|
|
316
424
|
|
|
317
425
|
await voiceaService.toggleTranscribing(false);
|
|
@@ -324,24 +432,8 @@ describe('plugin-voicea', () => {
|
|
|
324
432
|
})
|
|
325
433
|
);
|
|
326
434
|
|
|
327
|
-
assert.calledOnce(triggerSpy);
|
|
328
435
|
assert.notCalled(announcementSpy);
|
|
329
436
|
});
|
|
330
|
-
|
|
331
|
-
it("doesn't call API on same value", async () => {
|
|
332
|
-
await voiceaService.toggleTranscribing(true);
|
|
333
|
-
const triggerSpy = sinon.spy();
|
|
334
|
-
const announcementSpy = sinon.spy(voiceaService, 'sendAnnouncement');
|
|
335
|
-
|
|
336
|
-
voiceaService.on(EVENT_TRIGGERS.TRANSCRIBING_OFF, triggerSpy);
|
|
337
|
-
|
|
338
|
-
await voiceaService.toggleTranscribing(true);
|
|
339
|
-
|
|
340
|
-
assert.notCalled(triggerSpy);
|
|
341
|
-
assert.notCalled(announcementSpy);
|
|
342
|
-
|
|
343
|
-
assert.calledTwice(voiceaService.request);
|
|
344
|
-
});
|
|
345
437
|
});
|
|
346
438
|
|
|
347
439
|
describe('#processCaptionLanguageResponse', () => {
|
|
@@ -459,6 +551,9 @@ describe('plugin-voicea', () => {
|
|
|
459
551
|
transcript_id: '3ec73890-bffb-f28b-e77f-99dc13caea7e',
|
|
460
552
|
ts: 1611653204.3147924,
|
|
461
553
|
type: 'transcript_final_result',
|
|
554
|
+
translations: {
|
|
555
|
+
en: "Hello?",
|
|
556
|
+
},
|
|
462
557
|
transcript: {
|
|
463
558
|
alignments: [
|
|
464
559
|
{
|
|
@@ -508,6 +603,9 @@ describe('plugin-voicea', () => {
|
|
|
508
603
|
assert.calledOnceWithExactly(triggerSpy, {
|
|
509
604
|
isFinal: true,
|
|
510
605
|
transcriptId: '3ec73890-bffb-f28b-e77f-99dc13caea7e',
|
|
606
|
+
translations: {
|
|
607
|
+
en: "Hello?"
|
|
608
|
+
},
|
|
511
609
|
transcript: {
|
|
512
610
|
csis: [3556942592],
|
|
513
611
|
text: 'Hello?',
|
|
@@ -642,5 +740,19 @@ describe('plugin-voicea', () => {
|
|
|
642
740
|
});
|
|
643
741
|
});
|
|
644
742
|
});
|
|
743
|
+
|
|
744
|
+
describe("#getCaptionStatus", () => {
|
|
745
|
+
it('works correctly', () => {
|
|
746
|
+
voiceaService.captionStatus = "enabled"
|
|
747
|
+
assert.equal(voiceaService.getCaptionStatus(), "enabled");
|
|
748
|
+
});
|
|
749
|
+
});
|
|
750
|
+
|
|
751
|
+
describe("#getAnnounceStatus", () => {
|
|
752
|
+
it('works correctly', () => {
|
|
753
|
+
voiceaService.announceStatus = "joined"
|
|
754
|
+
assert.equal(voiceaService.getAnnounceStatus(), "joined");
|
|
755
|
+
});
|
|
756
|
+
});
|
|
645
757
|
});
|
|
646
758
|
});
|