@webex/plugin-meetings 3.0.0-beta.391 → 3.0.0-beta.392

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.
Files changed (51) hide show
  1. package/dist/breakouts/breakout.js +1 -1
  2. package/dist/breakouts/index.js +1 -1
  3. package/dist/config.js +1 -0
  4. package/dist/config.js.map +1 -1
  5. package/dist/constants.js +16 -7
  6. package/dist/constants.js.map +1 -1
  7. package/dist/interpretation/index.js +1 -1
  8. package/dist/interpretation/siLanguage.js +1 -1
  9. package/dist/meeting/index.js +738 -616
  10. package/dist/meeting/index.js.map +1 -1
  11. package/dist/meeting/util.js +4 -1
  12. package/dist/meeting/util.js.map +1 -1
  13. package/dist/meeting/voicea-meeting.js +172 -0
  14. package/dist/meeting/voicea-meeting.js.map +1 -0
  15. package/dist/meeting-info/meeting-info-v2.js +9 -6
  16. package/dist/meeting-info/meeting-info-v2.js.map +1 -1
  17. package/dist/meeting-info/util.js +7 -6
  18. package/dist/meeting-info/util.js.map +1 -1
  19. package/dist/meeting-info/utilv2.js +8 -4
  20. package/dist/meeting-info/utilv2.js.map +1 -1
  21. package/dist/member/index.js +0 -1
  22. package/dist/member/index.js.map +1 -1
  23. package/dist/types/constants.d.ts +8 -2
  24. package/dist/types/meeting/index.d.ts +56 -12
  25. package/dist/types/meeting/voicea-meeting.d.ts +16 -0
  26. package/dist/webinar/index.js +1 -1
  27. package/package.json +20 -20
  28. package/src/config.ts +2 -4
  29. package/src/constants.ts +11 -2
  30. package/src/meeting/index.ts +320 -157
  31. package/src/meeting/util.ts +5 -1
  32. package/src/meeting/voicea-meeting.ts +122 -0
  33. package/src/meeting-info/meeting-info-v2.ts +5 -11
  34. package/src/meeting-info/util.ts +12 -9
  35. package/src/meeting-info/utilv2.ts +24 -14
  36. package/src/member/index.ts +0 -1
  37. package/test/integration/spec/journey.js +2 -2
  38. package/test/unit/spec/breakouts/breakout.ts +2 -1
  39. package/test/unit/spec/breakouts/index.ts +7 -4
  40. package/test/unit/spec/locus-info/index.js +27 -18
  41. package/test/unit/spec/locus-info/selfUtils.js +6 -11
  42. package/test/unit/spec/media/index.ts +5 -0
  43. package/test/unit/spec/meeting/index.js +315 -87
  44. package/test/unit/spec/meeting/utils.js +52 -10
  45. package/test/unit/spec/meeting/voicea-meeting.ts +266 -0
  46. package/test/unit/spec/meeting-info/meetinginfov2.js +20 -15
  47. package/test/unit/spec/meetings/index.js +78 -10
  48. package/test/unit/spec/metrics/index.js +1 -2
  49. package/test/unit/spec/multistream/mediaRequestManager.ts +1 -0
  50. package/test/unit/spec/recording-controller/index.js +0 -1
  51. package/test/unit/spec/roap/turnDiscovery.ts +1 -1
@@ -57,7 +57,8 @@ describe('plugin-meetings', () => {
57
57
  });
58
58
 
59
59
  describe('#cleanup', () => {
60
- it('do clean up on meeting object', async () => {
60
+ it('do clean up on meeting object with LLM enabled', async () => {
61
+ meeting.config = {enableAutomaticLLM : true};
61
62
  await MeetingUtil.cleanUp(meeting);
62
63
  assert.calledOnce(meeting.cleanupLocalStreams);
63
64
  assert.calledOnce(meeting.closeRemoteStreams);
@@ -71,6 +72,37 @@ describe('plugin-meetings', () => {
71
72
  assert.calledOnce(meeting.breakouts.cleanUp);
72
73
  assert.calledOnce(meeting.simultaneousInterpretation.cleanUp);
73
74
  });
75
+
76
+ it('do clean up on meeting object with LLM disabled', async () => {
77
+ meeting.config = {enableAutomaticLLM : false};
78
+ await MeetingUtil.cleanUp(meeting);
79
+ assert.calledOnce(meeting.cleanupLocalStreams);
80
+ assert.calledOnce(meeting.closeRemoteStreams);
81
+ assert.calledOnce(meeting.closePeerConnections);
82
+
83
+ assert.calledOnce(meeting.unsetRemoteStreams);
84
+ assert.calledOnce(meeting.unsetPeerConnections);
85
+ assert.calledOnce(meeting.reconnectionManager.cleanUp);
86
+ assert.calledOnce(meeting.stopKeepAlive);
87
+ assert.notCalled(meeting.updateLLMConnection);
88
+ assert.calledOnce(meeting.breakouts.cleanUp);
89
+ assert.calledOnce(meeting.simultaneousInterpretation.cleanUp);
90
+ });
91
+
92
+ it('do clean up on meeting object with no config', async () => {
93
+ await MeetingUtil.cleanUp(meeting);
94
+ assert.calledOnce(meeting.cleanupLocalStreams);
95
+ assert.calledOnce(meeting.closeRemoteStreams);
96
+ assert.calledOnce(meeting.closePeerConnections);
97
+
98
+ assert.calledOnce(meeting.unsetRemoteStreams);
99
+ assert.calledOnce(meeting.unsetPeerConnections);
100
+ assert.calledOnce(meeting.reconnectionManager.cleanUp);
101
+ assert.calledOnce(meeting.stopKeepAlive);
102
+ assert.notCalled(meeting.updateLLMConnection);
103
+ assert.calledOnce(meeting.breakouts.cleanUp);
104
+ assert.calledOnce(meeting.simultaneousInterpretation.cleanUp);
105
+ });
74
106
  });
75
107
 
76
108
  describe('logging', () => {
@@ -369,7 +401,7 @@ describe('plugin-meetings', () => {
369
401
  getWebexObject: sinon.stub().returns(webex),
370
402
  };
371
403
 
372
- MeetingUtil.parseLocusJoin = sinon.stub();
404
+ const parseLocusJoinSpy = sinon.stub(MeetingUtil, 'parseLocusJoin');
373
405
  await MeetingUtil.joinMeeting(meeting, {});
374
406
 
375
407
  assert.calledOnce(meeting.meetingRequest.joinMeeting);
@@ -396,6 +428,7 @@ describe('plugin-meetings', () => {
396
428
  mediaConnections: 'mediaConnections',
397
429
  },
398
430
  });
431
+ parseLocusJoinSpy.restore();
399
432
  });
400
433
 
401
434
  it('#Should call meetingRequest.joinMeeting with breakoutsSupported=true when passed in as true', async () => {
@@ -406,7 +439,7 @@ describe('plugin-meetings', () => {
406
439
  getWebexObject: sinon.stub().returns(webex),
407
440
  };
408
441
 
409
- MeetingUtil.parseLocusJoin = sinon.stub();
442
+ const parseLocusJoinSpy = sinon.stub(MeetingUtil, 'parseLocusJoin');
410
443
  await MeetingUtil.joinMeeting(meeting, {
411
444
  breakoutsSupported: true,
412
445
  });
@@ -415,6 +448,7 @@ describe('plugin-meetings', () => {
415
448
  const parameter = meeting.meetingRequest.joinMeeting.getCall(0).args[0];
416
449
 
417
450
  assert.equal(parameter.breakoutsSupported, true);
451
+ parseLocusJoinSpy.restore();
418
452
  });
419
453
 
420
454
  it('#Should call meetingRequest.joinMeeting with liveAnnotationSupported=true when passed in as true', async () => {
@@ -425,7 +459,7 @@ describe('plugin-meetings', () => {
425
459
  getWebexObject: sinon.stub().returns(webex),
426
460
  };
427
461
 
428
- MeetingUtil.parseLocusJoin = sinon.stub();
462
+ const parseLocusJoinSpy = sinon.stub(MeetingUtil, 'parseLocusJoin');
429
463
  await MeetingUtil.joinMeeting(meeting, {
430
464
  liveAnnotationSupported: true,
431
465
  });
@@ -434,6 +468,7 @@ describe('plugin-meetings', () => {
434
468
  const parameter = meeting.meetingRequest.joinMeeting.getCall(0).args[0];
435
469
 
436
470
  assert.equal(parameter.liveAnnotationSupported, true);
471
+ parseLocusJoinSpy.restore();
437
472
  });
438
473
 
439
474
  it('#Should call meetingRequest.joinMeeting with locale=en_UK, deviceCapabilities=["TEST"] when they are passed in as those values', async () => {
@@ -444,7 +479,7 @@ describe('plugin-meetings', () => {
444
479
  getWebexObject: sinon.stub().returns(webex),
445
480
  };
446
481
 
447
- MeetingUtil.parseLocusJoin = sinon.stub();
482
+ const parseLocusJoinSpy = sinon.stub(MeetingUtil, 'parseLocusJoin');
448
483
  await MeetingUtil.joinMeeting(meeting, {
449
484
  locale: 'en_UK',
450
485
  deviceCapabilities: ['TEST'],
@@ -455,6 +490,7 @@ describe('plugin-meetings', () => {
455
490
 
456
491
  assert.equal(parameter.locale, 'en_UK');
457
492
  assert.deepEqual(parameter.deviceCapabilities, ['TEST']);
493
+ parseLocusJoinSpy.restore();
458
494
  });
459
495
 
460
496
  it('#Should call meetingRequest.joinMeeting with preferTranscoding=false when multistream is enabled', async () => {
@@ -468,7 +504,7 @@ describe('plugin-meetings', () => {
468
504
  getWebexObject: sinon.stub().returns(webex),
469
505
  };
470
506
 
471
- MeetingUtil.parseLocusJoin = sinon.stub();
507
+ const parseLocusJoinSpy = sinon.stub(MeetingUtil, 'parseLocusJoin');
472
508
  await MeetingUtil.joinMeeting(meeting, {});
473
509
 
474
510
  assert.calledOnce(meeting.meetingRequest.joinMeeting);
@@ -476,6 +512,7 @@ describe('plugin-meetings', () => {
476
512
 
477
513
  assert.equal(parameter.inviteeAddress, 'meetingJoinUrl');
478
514
  assert.equal(parameter.preferTranscoding, false);
515
+ parseLocusJoinSpy.restore();
479
516
  });
480
517
 
481
518
  it('#Should fallback sipUrl if meetingJoinUrl does not exists', async () => {
@@ -488,13 +525,14 @@ describe('plugin-meetings', () => {
488
525
  getWebexObject: sinon.stub().returns(webex),
489
526
  };
490
527
 
491
- MeetingUtil.parseLocusJoin = sinon.stub();
528
+ const parseLocusJoinSpy = sinon.stub(MeetingUtil, 'parseLocusJoin');
492
529
  await MeetingUtil.joinMeeting(meeting, {});
493
530
 
494
531
  assert.calledOnce(meeting.meetingRequest.joinMeeting);
495
532
  const parameter = meeting.meetingRequest.joinMeeting.getCall(0).args[0];
496
533
 
497
534
  assert.equal(parameter.inviteeAddress, 'sipUri');
535
+ parseLocusJoinSpy.restore();
498
536
  });
499
537
 
500
538
  it('#Should fallback to meetingNumber if meetingJoinUrl/sipUrl does not exists', async () => {
@@ -507,7 +545,7 @@ describe('plugin-meetings', () => {
507
545
  getWebexObject: sinon.stub().returns(webex),
508
546
  };
509
547
 
510
- MeetingUtil.parseLocusJoin = sinon.stub();
548
+ const parseLocusJoinSpy = sinon.stub(MeetingUtil, 'parseLocusJoin');
511
549
  await MeetingUtil.joinMeeting(meeting, {});
512
550
 
513
551
  assert.calledOnce(meeting.meetingRequest.joinMeeting);
@@ -515,6 +553,7 @@ describe('plugin-meetings', () => {
515
553
 
516
554
  assert.isUndefined(parameter.inviteeAddress);
517
555
  assert.equal(parameter.meetingNumber, 'meetingNumber');
556
+ parseLocusJoinSpy.restore();
518
557
  });
519
558
 
520
559
  it('should pass in the locusClusterUrl from meetingInfo', async () => {
@@ -528,19 +567,20 @@ describe('plugin-meetings', () => {
528
567
  getWebexObject: sinon.stub().returns(webex),
529
568
  };
530
569
 
531
- MeetingUtil.parseLocusJoin = sinon.stub();
570
+ const parseLocusJoinSpy = sinon.stub(MeetingUtil, 'parseLocusJoin');
532
571
  await MeetingUtil.joinMeeting(meeting, {});
533
572
 
534
573
  assert.calledOnce(meeting.meetingRequest.joinMeeting);
535
574
  const parameter = meeting.meetingRequest.joinMeeting.getCall(0).args[0];
536
575
 
537
576
  assert.equal(parameter.locusClusterUrl, 'locusClusterUrl');
577
+ parseLocusJoinSpy.restore();
538
578
  });
539
579
  });
540
580
 
541
581
  describe('joinMeetingOptions', () => {
542
582
  it('sends client events correctly', async () => {
543
- MeetingUtil.joinMeeting = sinon.stub().rejects({});
583
+ const joinMeetingSpy = sinon.stub(MeetingUtil, 'joinMeeting').rejects({});
544
584
  MeetingUtil.isPinOrGuest = sinon.stub().returns(true);
545
585
  const meeting = {
546
586
  id: 'meeting-id',
@@ -571,6 +611,8 @@ describe('plugin-meetings', () => {
571
611
  meetingId: meeting.id,
572
612
  },
573
613
  });
614
+ } finally {
615
+ joinMeetingSpy.restore();
574
616
  }
575
617
  });
576
618
  });
@@ -0,0 +1,266 @@
1
+ import {
2
+ getSpeakerFromProxyOrStore,
3
+ processNewCaptions,
4
+ } from '@webex/plugin-meetings/src/meeting/voicea-meeting';
5
+ import {assert} from '@webex/test-helper-chai';
6
+ import { expect } from 'chai';
7
+
8
+ describe('plugin-meetings', () => {
9
+ let fakeMeeting, fakeVoiceaPayload;
10
+ const fakeMemberId = "4f35a5ab-f750-3ba7-b309-dea62a512257";
11
+ beforeEach(() => {
12
+ const fakeMemberList = {
13
+ [fakeMemberId]: {
14
+ participant: {
15
+ person: {
16
+ id: "8093d335-9b96-4f9d-a6b2-7293423be88a",
17
+ name: "John Doe",
18
+ isExternal: false,
19
+ orgId: "1eb65fdf-9643-417f-9974-ad72cae0e10f",
20
+ incomingCallProtocols: []
21
+ },
22
+ status: {
23
+ audioStatus: "SENDRECV",
24
+ videoStatus: "INACTIVE",
25
+ videoSlidesStatus: "RECVONLY",
26
+ audioSlidesStatus: "INACTIVE",
27
+ csis: [
28
+ 3060099329,
29
+ 3060099328,
30
+ 1234867712,
31
+ 1234867713
32
+ ]
33
+ },
34
+ },
35
+ id: fakeMemberId
36
+ }
37
+ };
38
+
39
+ const fakeCaptions = {
40
+ captions: [
41
+ {
42
+ id: "6bece1b9-4e50-fafe-fb63-d27d5fb27280",
43
+ isFinal: true,
44
+ text: "Oh it's not update.",
45
+ currentSpokenLanguage: "en",
46
+ timestamp: "1:34",
47
+ speaker: {
48
+ speakerId: "8093d335-9b96-4f9d-a6b2-7293423be88a",
49
+ name: "John Doe"
50
+ }
51
+ },
52
+ {
53
+ id: "c34400a9-cb2b-20c3-d20c-bd7981cc62a9",
54
+ isFinal: true,
55
+ text: "Nice.",
56
+ currentSpokenLanguage: "en",
57
+ timestamp: "1:60",
58
+ speaker: {
59
+ speakerId: "8093d335-9b96-4f9d-a6b2-7293423be88a",
60
+ name: "John Doe"
61
+ }
62
+ },
63
+ {
64
+ id: "311cc182-e657-c077-c078-795f866c4e9b_8093d335-9b96-4f9d-a6b2-7293423be88a",
65
+ isFinal: false,
66
+ text: " Don't bother me talking I'm just going to get the transcript data that is interim and I",
67
+ currentCaptionLanguage: "en",
68
+ speaker: {
69
+ speakerId: "8093d335-9b96-4f9d-a6b2-7293423be88a",
70
+ name: "John Doe"
71
+ }
72
+ }
73
+ ],
74
+ interimCaptions: {
75
+ "6bece1b9-4e50-fafe-fb63-d27d5fb27280": [],
76
+ "c34400a9-cb2b-20c3-d20c-bd7981cc62a9": [],
77
+ "311cc182-e657-c077-c078-795f866c4e9b": [
78
+ "311cc182-e657-c077-c078-795f866c4e9b_8093d335-9b96-4f9d-a6b2-7293423be88a"
79
+ ]
80
+ },
81
+ speakerProxy: {},
82
+ };
83
+
84
+ fakeMeeting = {
85
+ members: {
86
+ membersCollection: {
87
+ members: fakeMemberList
88
+ }
89
+ },
90
+ transcription: fakeCaptions
91
+ }
92
+
93
+ fakeVoiceaPayload = {
94
+ isFinal: true,
95
+ transcriptId: "311cc182-e657-c077-c078-795f866c4e9b",
96
+ transcripts: [
97
+ {
98
+ text: "Don't bother me talking I'm just going to get the transcript data that is interim and I needed if I keep talking, I get the interim data",
99
+ csis: [
100
+ 1234867712
101
+ ],
102
+ transcript_language_code: "en"
103
+ }
104
+ ],
105
+ transcript: {
106
+ text: "Don't bother me talking I'm just going to get the transcript data that is interim and I needed if I keep talking, I get the interim data",
107
+ csis: [
108
+ 1234867712
109
+ ],
110
+ transcript_language_code: "en"
111
+ }
112
+ };
113
+ });
114
+
115
+ describe('voicea-meeting', () => {
116
+ it('should export the correct members', () => {
117
+ assert.isFunction(getSpeakerFromProxyOrStore);
118
+ assert.isFunction(processNewCaptions);
119
+ });
120
+
121
+ describe('getSpeakerFromProxyOrStore', () => {
122
+ it('should return a cached speaker if csisKey is in speakerProxy', () => {
123
+ // Add a speaker to the speakerProxy
124
+ const csisKey = 1234867712;
125
+ const cachedSpeaker = {
126
+ speakerId: 'cached-speaker-id',
127
+ name: 'Cached Speaker'
128
+ };
129
+ fakeMeeting.transcription.speakerProxy[csisKey] = cachedSpeaker;
130
+
131
+ const { speaker, needsCaching } = getSpeakerFromProxyOrStore({
132
+ csisKey,
133
+ meetingMembers: fakeMeeting.members.membersCollection.members,
134
+ transcriptData: fakeMeeting.transcription
135
+ });
136
+
137
+ expect(speaker).to.deep.equal(cachedSpeaker);
138
+ expect(needsCaching).to.be.false;
139
+ });
140
+
141
+ it('should find and cache a speaker if not already in speakerProxy', () => {
142
+ const csisKey = 1234867712; // This csis exists in the fakeMemberList
143
+
144
+ // Ensure speakerProxy is empty
145
+ fakeMeeting.transcription.speakerProxy = {};
146
+
147
+ const { speaker, needsCaching } = getSpeakerFromProxyOrStore({
148
+ csisKey,
149
+ meetingMembers: fakeMeeting.members.membersCollection.members,
150
+ transcriptData: fakeMeeting.transcription
151
+ });
152
+
153
+ expect(speaker.speakerId).to.equal(fakeMeeting.members.membersCollection.members[fakeMemberId].participant.person.id);
154
+ expect(speaker.name).to.equal(fakeMeeting.members.membersCollection.members[fakeMemberId].participant.person.name);
155
+ expect(needsCaching).to.be.true;
156
+ });
157
+ });
158
+
159
+ describe('processNewCaptions', () => {
160
+ it('should process new final captions correctly', () => {
161
+ let transcriptData = fakeMeeting.transcription;
162
+ let transcriptId = fakeVoiceaPayload.transcriptId;
163
+ delete fakeVoiceaPayload.transcripts;
164
+
165
+ // Assuming that processNewCaptions is a pure function that doesn't mutate the input but returns a new state
166
+ processNewCaptions({
167
+ data: fakeVoiceaPayload,
168
+ meeting: fakeMeeting
169
+ });
170
+
171
+ // Check if speaker details are cached if needed
172
+ const csisKey = fakeVoiceaPayload.transcript.csis[0];
173
+ const speaker = transcriptData.speakerProxy[csisKey];
174
+ expect(speaker).to.exist;
175
+
176
+ // Check if interim captions are removed
177
+ expect(transcriptData.interimCaptions[transcriptId]).to.deep.equal([]);
178
+
179
+ //check if the interim caption is removed
180
+ const oldInterimCaption = transcriptData.captions.find(caption => caption.id === `${transcriptId}_${speaker.speakerId}`);
181
+ expect(oldInterimCaption).to.not.exist;
182
+
183
+ // Check the final caption data
184
+ const newCaption = transcriptData.captions.find(caption => caption.id === transcriptId);
185
+ expect(newCaption).to.exist;
186
+ expect(newCaption).to.include({
187
+ id: transcriptId,
188
+ isFinal: fakeVoiceaPayload.isFinal,
189
+ text: fakeVoiceaPayload.transcript.text,
190
+ currentSpokenLanguage: fakeVoiceaPayload.transcript.transcript_language_code,
191
+ });
192
+
193
+ // Check the speaker data in the new caption
194
+ expect(newCaption.speaker).to.deep.equal(speaker);
195
+ });
196
+
197
+ it('should process new interim captions correctly', () => {
198
+ let transcriptData = fakeMeeting.transcription;
199
+ let transcriptId = fakeVoiceaPayload.transcriptId;
200
+ delete fakeVoiceaPayload.transcript;
201
+
202
+ transcriptData.captions.splice(transcriptData.length - 1, 1);
203
+ fakeVoiceaPayload.isFinal = false;
204
+
205
+ processNewCaptions({
206
+ data: fakeVoiceaPayload,
207
+ meeting: fakeMeeting
208
+ });
209
+
210
+ // Check if speaker details are cached if needed
211
+ const csisKey = fakeVoiceaPayload.transcripts[0].csis[0];
212
+ const speaker = transcriptData.speakerProxy[csisKey];
213
+ expect(speaker).to.exist;
214
+
215
+ // Check the final caption data
216
+ const newCaption = transcriptData.captions.find(caption => caption.id === `${transcriptId}_${speaker.speakerId}`);
217
+ expect(newCaption).to.exist;
218
+ expect(newCaption).to.include({
219
+ id: `${transcriptId}_${speaker.speakerId}`,
220
+ isFinal: fakeVoiceaPayload.isFinal,
221
+ text: fakeVoiceaPayload.transcripts[0].text,
222
+ currentCaptionLanguage: fakeVoiceaPayload.transcripts[0].transcript_language_code,
223
+ });
224
+
225
+ // Check if interim captions has the right caption id
226
+ expect(transcriptData.interimCaptions[transcriptId]).to.deep.equal([newCaption.id]);
227
+
228
+ // Check the speaker data in the new caption
229
+ expect(newCaption.speaker).to.deep.equal(speaker);
230
+ });
231
+
232
+ it('should process interim captions with an existing one correctly', () => {
233
+ let transcriptData = fakeMeeting.transcription;
234
+ let transcriptId = fakeVoiceaPayload.transcriptId;
235
+ delete fakeVoiceaPayload.transcript;
236
+ fakeVoiceaPayload.isFinal = false;
237
+
238
+ processNewCaptions({
239
+ data: fakeVoiceaPayload,
240
+ meeting: fakeMeeting
241
+ });
242
+
243
+ // Check if speaker details are cached if needed
244
+ const csisKey = fakeVoiceaPayload.transcripts[0].csis[0];
245
+ const speaker = transcriptData.speakerProxy[csisKey];
246
+ expect(speaker).to.exist;
247
+
248
+ // Check the final caption data
249
+ const newCaption = transcriptData.captions.find(caption => caption.id === `${transcriptId}_${speaker.speakerId}`);
250
+ expect(newCaption).to.exist;
251
+ expect(newCaption).to.include({
252
+ id: `${transcriptId}_${speaker.speakerId}`,
253
+ isFinal: fakeVoiceaPayload.isFinal,
254
+ text: fakeVoiceaPayload.transcripts[0].text,
255
+ currentCaptionLanguage: fakeVoiceaPayload.transcripts[0].transcript_language_code,
256
+ });
257
+
258
+ // Check if interim captions has the right caption id
259
+ expect(transcriptData.interimCaptions[transcriptId]).to.deep.equal([newCaption.id]);
260
+
261
+ // Check the speaker data in the new caption
262
+ expect(newCaption.speaker).to.deep.equal(speaker);
263
+ });
264
+ });
265
+ });
266
+ });
@@ -795,13 +795,17 @@ describe('plugin-meetings', () => {
795
795
  it('Make a request to /spaceInstant when conversationUrl', async () => {
796
796
  const {invitee} = setup();
797
797
 
798
- const result = await meetingInfo.createAdhocSpaceMeeting(conversationUrl);
798
+ webex.request.resolves({
799
+ statusCode: 200,
800
+ body: conversation
801
+ });
802
+
803
+ const result = await meetingInfo.createAdhocSpaceMeeting(conversationUrl,installedOrgID);
799
804
 
800
805
  assert.calledWith(
801
- webex.internal.conversation.get,
802
- {url: conversationUrl},
803
- {includeParticipants: true, disableTransform: true}
804
- );
806
+ webex.request,
807
+ {uri:conversationUrl, qs: {includeParticipants: true}, disableTransform: true}
808
+ )
805
809
 
806
810
  assert.calledWith(webex.request, {
807
811
  method: 'POST',
@@ -812,27 +816,28 @@ describe('plugin-meetings', () => {
812
816
  keyUrl: conversation.encryptionKeyUrl,
813
817
  kroUrl: conversation.kmsResourceObjectUrl,
814
818
  invitees: invitee,
819
+ installedOrgID: installedOrgID
815
820
  },
816
821
  });
817
- assert(Metrics.sendBehavioralMetric.calledOnce);
822
+ assert.calledOnce(Metrics.sendBehavioralMetric);
818
823
  assert.calledWith(Metrics.sendBehavioralMetric, BEHAVIORAL_METRICS.ADHOC_MEETING_SUCCESS);
819
824
  assert.deepEqual(result, {
820
- body: {},
825
+ body: conversation,
821
826
  statusCode: 200
822
827
  });
823
828
  });
824
-
825
829
  it('Make a request to /spaceInstant when conversationUrl with installed org ID', async () => {
826
830
  const {invitee} = setup();
827
-
831
+ webex.request = sinon.stub().resolves({
832
+ body: conversation,
833
+ });
828
834
  await meetingInfo.createAdhocSpaceMeeting(conversationUrl, installedOrgID);
829
835
 
830
- assert.calledWith(
831
- webex.internal.conversation.get,
832
- {url: conversationUrl},
833
- {includeParticipants: true, disableTransform: true}
834
- );
835
-
836
+ assert.calledWith(webex.request, {
837
+ uri: conversationUrl,
838
+ qs: {includeParticipants: true},
839
+ disableTransform: true,
840
+ });
836
841
  assert.calledWith(webex.request, {
837
842
  method: 'POST',
838
843
  uri: 'https://go.webex.com/wbxappapi/v2/meetings/spaceInstant',
@@ -595,6 +595,7 @@ describe('plugin-meetings', () => {
595
595
  });
596
596
  describe('when destroying meeting is needed', () => {
597
597
  let destroySpy;
598
+ let cleanUpSpy;
598
599
 
599
600
  const meetingCollectionMeetings = {
600
601
  stillValidLocusMeeting: {
@@ -625,7 +626,11 @@ describe('plugin-meetings', () => {
625
626
  loci: [{url: 'still-valid-locus-url'}],
626
627
  })
627
628
  );
628
- MeetingUtil.cleanUp = sinon.stub().returns(Promise.resolve());
629
+ cleanUpSpy = sinon.stub(MeetingUtil, 'cleanUp').returns(Promise.resolve());
630
+ });
631
+
632
+ afterEach(() => {
633
+ cleanUpSpy.restore();
629
634
  });
630
635
 
631
636
  it('destroy any meeting that has no active locus url if keepOnlyLocusMeetings is not defined', async () => {
@@ -1205,7 +1210,8 @@ describe('plugin-meetings', () => {
1205
1210
  webex.meetings.meetingInfo.fetchMeetingInfo = sinon.stub().returns(
1206
1211
  Promise.resolve({
1207
1212
  body: {
1208
- permissionToken: 'PT',
1213
+ permissionToken:
1214
+ 'eyJhbGciOiJIUzI1NiJ9.eyJleHAiOiIxMjM0NTYiLCJwZXJtaXNzaW9uIjp7InVzZXJQb2xpY2llcyI6eyJhIjp0cnVlfX19.wkTk0Hp8sUlq2wi2nP4-Ym4Xb7aEUHzyXA1kzk6f0V0',
1209
1215
  meetingJoinUrl: 'meetingJoinUrl',
1210
1216
  },
1211
1217
  })
@@ -1308,7 +1314,8 @@ describe('plugin-meetings', () => {
1308
1314
  const meeting = await webex.meetings.createMeeting('test destination', 'test type');
1309
1315
 
1310
1316
  const expectedMeetingData = {
1311
- permissionToken: 'PT',
1317
+ permissionToken:
1318
+ 'eyJhbGciOiJIUzI1NiJ9.eyJleHAiOiIxMjM0NTYiLCJwZXJtaXNzaW9uIjp7InVzZXJQb2xpY2llcyI6eyJhIjp0cnVlfX19.wkTk0Hp8sUlq2wi2nP4-Ym4Xb7aEUHzyXA1kzk6f0V0',
1312
1319
  meetingJoinUrl: 'meetingJoinUrl',
1313
1320
  correlationId: meeting.id,
1314
1321
  };
@@ -1324,7 +1331,8 @@ describe('plugin-meetings', () => {
1324
1331
 
1325
1332
  it('accepts injected meeting info', async () => {
1326
1333
  const meetingInfo = {
1327
- permissionToken: 'PT',
1334
+ permissionToken:
1335
+ 'eyJhbGciOiJIUzI1NiJ9.eyJleHAiOiIxMjM0NTYiLCJwZXJtaXNzaW9uIjp7InVzZXJQb2xpY2llcyI6eyJhIjp0cnVlfX19.wkTk0Hp8sUlq2wi2nP4-Ym4Xb7aEUHzyXA1kzk6f0V0',
1328
1336
  meetingJoinUrl: 'meetingJoinUrl',
1329
1337
  };
1330
1338
 
@@ -1356,7 +1364,8 @@ describe('plugin-meetings', () => {
1356
1364
 
1357
1365
  it('accepts injected meeting info with meeting lookup url', async () => {
1358
1366
  const meetingInfo = {
1359
- permissionToken: 'PT',
1367
+ permissionToken:
1368
+ 'eyJhbGciOiJIUzI1NiJ9.eyJleHAiOiIxMjM0NTYiLCJwZXJtaXNzaW9uIjp7InVzZXJQb2xpY2llcyI6eyJhIjp0cnVlfX19.wkTk0Hp8sUlq2wi2nP4-Ym4Xb7aEUHzyXA1kzk6f0V0',
1360
1369
  meetingJoinUrl: 'meetingJoinUrl',
1361
1370
  };
1362
1371
 
@@ -1401,7 +1410,8 @@ describe('plugin-meetings', () => {
1401
1410
  infoExtraParams
1402
1411
  );
1403
1412
  const expectedMeetingData = {
1404
- permissionToken: 'PT',
1413
+ permissionToken:
1414
+ 'eyJhbGciOiJIUzI1NiJ9.eyJleHAiOiIxMjM0NTYiLCJwZXJtaXNzaW9uIjp7InVzZXJQb2xpY2llcyI6eyJhIjp0cnVlfX19.wkTk0Hp8sUlq2wi2nP4-Ym4Xb7aEUHzyXA1kzk6f0V0',
1405
1415
  meetingJoinUrl: 'meetingJoinUrl',
1406
1416
  };
1407
1417
 
@@ -1502,8 +1512,10 @@ describe('plugin-meetings', () => {
1502
1512
  assert.equal(meeting.meetingNumber, 'locusMeetingId');
1503
1513
  assert.equal(meeting.meetingJoinUrl, 'meetingJoinUrl');
1504
1514
  assert.equal(meeting.owner, 'locusOwner');
1505
- assert.equal(meeting.permissionToken, 'PT');
1506
-
1515
+ assert.equal(
1516
+ meeting.permissionToken,
1517
+ 'eyJhbGciOiJIUzI1NiJ9.eyJleHAiOiIxMjM0NTYiLCJwZXJtaXNzaW9uIjp7InVzZXJQb2xpY2llcyI6eyJhIjp0cnVlfX19.wkTk0Hp8sUlq2wi2nP4-Ym4Xb7aEUHzyXA1kzk6f0V0'
1518
+ );
1507
1519
  assert.calledWith(
1508
1520
  TriggerProxy.trigger,
1509
1521
  meeting,
@@ -1802,9 +1814,13 @@ describe('plugin-meetings', () => {
1802
1814
  });
1803
1815
  });
1804
1816
  describe('Public Event Triggers', () => {
1817
+ let cleanUpSpy;
1805
1818
  describe('#destroy', () => {
1806
1819
  beforeEach(() => {
1807
- MeetingUtil.cleanUp = sinon.stub();
1820
+ cleanUpSpy = sinon.stub(MeetingUtil, 'cleanUp');
1821
+ });
1822
+ afterEach(() => {
1823
+ cleanUpSpy.restore();
1808
1824
  });
1809
1825
  it('should have #destroy', () => {
1810
1826
  assert.exists(webex.meetings.destroy);
@@ -1969,6 +1985,57 @@ describe('plugin-meetings', () => {
1969
1985
  'Failed to fetch preferred site from user - no site will be set'
1970
1986
  );
1971
1987
  });
1988
+
1989
+ it('should fall back to fetching the site from the user', async () => {
1990
+ setup({
1991
+ user: {
1992
+ userPreferences: {
1993
+ userPreferencesItems: {
1994
+ preferredWebExSite: 'site.webex.com',
1995
+ },
1996
+ },
1997
+ },
1998
+ });
1999
+
2000
+ await webex.meetings.fetchUserPreferredWebexSite();
2001
+
2002
+ assert.equal(webex.meetings.preferredWebexSite, 'site.webex.com');
2003
+ assert.notCalled(loggerProxySpy);
2004
+ });
2005
+
2006
+ forEach([
2007
+ {user: undefined},
2008
+ {user: {userPreferences: {}}},
2009
+ {user: {userPreferences: {userPreferencesItems: {}}}},
2010
+ {user: {userPreferences: {userPreferencesItems: {preferredWebExSite: undefined}}}},
2011
+ ], ({user}) => {
2012
+ it(`should handle invalid user data ${user}`, async () => {
2013
+ setup({user});
2014
+
2015
+ await webex.meetings.fetchUserPreferredWebexSite();
2016
+
2017
+ assert.equal(webex.meetings.preferredWebexSite, '');
2018
+ assert.calledOnceWithExactly(
2019
+ loggerProxySpy,
2020
+ 'Failed to fetch preferred site from user - no site will be set'
2021
+ );
2022
+ });
2023
+ });
2024
+
2025
+ it('should handle a get user failure', async () => {
2026
+ setup();
2027
+
2028
+ webex.internal.user.get.rejects(new Error());
2029
+
2030
+ await webex.meetings.fetchUserPreferredWebexSite();
2031
+
2032
+ assert.equal(webex.meetings.preferredWebexSite, '');
2033
+ assert.calledOnceWithExactly(
2034
+ loggerProxySpy,
2035
+ 'Failed to fetch preferred site from user - no site will be set'
2036
+ );
2037
+ });
2038
+
1972
2039
  });
1973
2040
  });
1974
2041
 
@@ -1986,7 +2053,8 @@ describe('plugin-meetings', () => {
1986
2053
  webex.meetings.meetingInfo.fetchMeetingInfo = sinon.stub().returns(
1987
2054
  Promise.resolve({
1988
2055
  body: {
1989
- permissionToken: 'PT',
2056
+ permissionToken:
2057
+ 'eyJhbGciOiJIUzI1NiJ9.eyJleHAiOiIxMjM0NTYiLCJwZXJtaXNzaW9uIjp7InVzZXJQb2xpY2llcyI6eyJhIjp0cnVlfX19.wkTk0Hp8sUlq2wi2nP4-Ym4Xb7aEUHzyXA1kzk6f0V0',
1990
2058
  meetingJoinUrl: 'meetingJoinUrl',
1991
2059
  },
1992
2060
  })