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

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
@@ -0,0 +1,122 @@
1
+ export const getSpeaker = (members, csis = []) =>
2
+ Object.values(members).find((member: any) => {
3
+ const memberCSIs = member.participant.status.csis ?? [];
4
+
5
+ return csis.some((csi) => memberCSIs.includes(csi));
6
+ });
7
+
8
+ export const getSpeakerFromProxyOrStore = ({csisKey, meetingMembers, transcriptData}) => {
9
+ let speaker = {
10
+ speakerId: '',
11
+ name: '',
12
+ };
13
+
14
+ let needsCaching = false;
15
+
16
+ if (csisKey && transcriptData.speakerProxy[csisKey]) {
17
+ speaker = transcriptData.speakerProxy[csisKey];
18
+
19
+ return {speaker, needsCaching};
20
+ }
21
+
22
+ const meetingMember: any = getSpeaker(meetingMembers, [csisKey]);
23
+
24
+ speaker = {
25
+ speakerId: meetingMember?.participant.person.id ?? '',
26
+ name: meetingMember?.participant.person.name ?? '',
27
+ };
28
+
29
+ needsCaching = true;
30
+
31
+ return {speaker, needsCaching};
32
+ };
33
+
34
+ export const processNewCaptions = ({data, meeting}) => {
35
+ const {transcriptId} = data;
36
+ const transcriptData = meeting.transcription;
37
+
38
+ if (data.isFinal) {
39
+ transcriptData.interimCaptions[transcriptId].forEach((interimId) => {
40
+ const interimTranscriptIndex = transcriptData.captions.findIndex(
41
+ (transcript) => transcript.id === interimId
42
+ );
43
+
44
+ if (interimTranscriptIndex !== -1) {
45
+ transcriptData.captions.splice(interimTranscriptIndex, 1);
46
+ }
47
+ });
48
+ delete transcriptData.interimCaptions[transcriptId];
49
+ const csisKey = data.transcript?.csis[0];
50
+
51
+ const {needsCaching, speaker} = getSpeakerFromProxyOrStore({
52
+ meetingMembers: meeting.members.membersCollection.members,
53
+ transcriptData,
54
+ csisKey,
55
+ });
56
+
57
+ if (needsCaching) {
58
+ transcriptData.speakerProxy[csisKey] = speaker;
59
+ }
60
+ const captionData = {
61
+ id: transcriptId,
62
+ isFinal: data.isFinal,
63
+ translations: data.translations,
64
+ text: data.transcript?.text,
65
+ currentSpokenLanguage: data.transcript?.transcript_language_code,
66
+ timestamp: data.timestamp,
67
+ speaker,
68
+ };
69
+ transcriptData.captions.push(captionData);
70
+ }
71
+ const {transcripts = []} = data;
72
+ const transcriptsPerCsis = new Map();
73
+
74
+ for (const transcript of transcripts) {
75
+ const {
76
+ text,
77
+ transcript_language_code: currentSpokenLanguage,
78
+ csis: [csisMember],
79
+ } = transcript;
80
+
81
+ const newCaption = `${transcriptsPerCsis.get(csisMember)?.text ?? ''} ${text}`.trim();
82
+
83
+ // eslint-disable-next-line camelcase
84
+ transcriptsPerCsis.set(csisMember, {text: newCaption, currentSpokenLanguage});
85
+ }
86
+ const interimTranscriptionIds = [];
87
+
88
+ for (const [key, value] of transcriptsPerCsis) {
89
+ const {needsCaching, speaker} = getSpeakerFromProxyOrStore({
90
+ meetingMembers: meeting.members.membersCollection.members,
91
+ transcriptData,
92
+ csisKey: key,
93
+ });
94
+
95
+ if (needsCaching) {
96
+ transcriptData.speakerProxy[key] = speaker;
97
+ }
98
+ const {speakerId} = speaker;
99
+ const interimId = `${transcriptId}_${speakerId}`;
100
+ const captionData = {
101
+ id: interimId,
102
+ isFinal: data.isFinal,
103
+ translations: value.translations,
104
+ text: value.text,
105
+ currentCaptionLanguage: value.currentSpokenLanguage,
106
+ timestamp: value?.timestamp,
107
+ speaker,
108
+ };
109
+
110
+ const interimTranscriptIndex = transcriptData.captions.findIndex(
111
+ (transcript) => transcript.id === interimId
112
+ );
113
+
114
+ if (interimTranscriptIndex !== -1) {
115
+ transcriptData.captions.splice(interimTranscriptIndex, 1);
116
+ }
117
+
118
+ interimTranscriptionIds.push(interimId);
119
+ transcriptData.captions.push(captionData);
120
+ }
121
+ transcriptData.interimCaptions[transcriptId] = interimTranscriptionIds;
122
+ };
@@ -204,22 +204,16 @@ export default class MeetingInfoV2 {
204
204
  return invitees;
205
205
  };
206
206
 
207
- return this.webex.internal.conversation
208
- .get({url: conversationUrl}, {includeParticipants: true, disableTransform: true})
209
- .then((conversation) => {
210
- const body: {
211
- title: string;
212
- spaceUrl: string;
213
- keyUrl: string;
214
- kroUrl: string;
215
- invitees: any[];
216
- installedOrgID?: string;
217
- } = {
207
+ return this.webex
208
+ .request({uri: conversationUrl, qs: {includeParticipants: true}, disableTransform: true})
209
+ .then(({body: conversation}) => {
210
+ const body = {
218
211
  title: conversation.displayName,
219
212
  spaceUrl: conversation.url,
220
213
  keyUrl: conversation.encryptionKeyUrl,
221
214
  kroUrl: conversation.kmsResourceObjectUrl,
222
215
  invitees: getInvitees(conversation.participants?.items),
216
+ installedOrgID,
223
217
  };
224
218
 
225
219
  if (installedOrgID) {
@@ -1,13 +1,12 @@
1
1
  import url from 'url';
2
2
 
3
3
  import btoa from 'btoa';
4
- import {
5
- // @ts-ignore
6
- deconstructHydraId,
7
- } from '@webex/common';
4
+ // @ts-ignore
5
+ import {deconstructHydraId} from '@webex/common';
8
6
 
9
7
  import ParameterError from '../common/errors/parameter';
10
8
  import LoggerProxy from '../common/logs/logger-proxy';
9
+
11
10
  import {
12
11
  _SIP_URI_,
13
12
  _PERSONAL_ROOM_,
@@ -221,12 +220,16 @@ MeetingInfoUtil.generateOptions = async (from) => {
221
220
  try {
222
221
  await webex.internal.services.waitForCatalog('postauth');
223
222
 
224
- const conversationUrl = webex.internal.conversation.getUrlFromClusterId({
225
- cluster: hydraId.cluster,
226
- id: hydraId.destination,
227
- });
223
+ const serviceUrl = webex.internal.services.getServiceUrlFromClusterId(
224
+ {
225
+ cluster: hydraId.cluster,
226
+ },
227
+ webex
228
+ );
228
229
 
229
- options.destination = conversationUrl;
230
+ options.destination = hydraId.destination
231
+ ? `${serviceUrl}/conversations/${hydraId.destination}`
232
+ : serviceUrl;
230
233
  } catch (e) {
231
234
  LoggerProxy.logger.error(`Meeting-info:util#generateOptions --> ${e}`);
232
235
  throw e;
@@ -1,9 +1,7 @@
1
1
  import url from 'url';
2
2
 
3
- import {
4
- // @ts-ignore
5
- deconstructHydraId,
6
- } from '@webex/common';
3
+ // @ts-ignore
4
+ import {deconstructHydraId} from '@webex/common';
7
5
 
8
6
  import {
9
7
  _SIP_URI_,
@@ -21,6 +19,8 @@ import {
21
19
  JOIN,
22
20
  MEET,
23
21
  MEET_M,
22
+ MEET_CISCO,
23
+ MEET_CO,
24
24
  HTTPS_PROTOCOL,
25
25
  UUID_REG,
26
26
  VALID_EMAIL_ADDRESS,
@@ -68,6 +68,8 @@ MeetingInfoUtil.isMeetingLink = (value: string) => {
68
68
  parsedUrl.pathname &&
69
69
  (parsedUrl.pathname.includes(`/${MEET}`) ||
70
70
  parsedUrl.pathname.includes(`/${MEET_M}`) ||
71
+ parsedUrl.pathname.includes(`/${MEET_CISCO}`) ||
72
+ parsedUrl.pathname.includes(`/${MEET_CO}`) ||
71
73
  parsedUrl.pathname.includes(`/${JOIN}`));
72
74
 
73
75
  return hostNameBool && pathNameBool;
@@ -162,7 +164,13 @@ MeetingInfoUtil.getDestinationType = async (from) => {
162
164
  };
163
165
  }
164
166
  const options: any = {};
165
- const hydraId = MeetingInfoUtil.getHydraId(destination);
167
+ let hydraId;
168
+
169
+ if (webex && webex.config && webex.config.meetings && webex.config.meetings.disableHydraId) {
170
+ hydraId = null;
171
+ } else {
172
+ hydraId = MeetingInfoUtil.getHydraId(destination);
173
+ }
166
174
 
167
175
  if (MeetingInfoUtil.isMeetingLink(destination)) {
168
176
  LoggerProxy.logger.warn(
@@ -180,19 +188,21 @@ MeetingInfoUtil.getDestinationType = async (from) => {
180
188
  } else if (MeetingInfoUtil.isConversationUrl(destination, webex)) {
181
189
  options.type = _CONVERSATION_URL_;
182
190
  options.destination = destination;
183
- } else if (hydraId.people) {
191
+ } else if (hydraId && hydraId.people) {
184
192
  options.type = _SIP_URI_;
185
193
 
186
- return MeetingInfoUtil.getSipUriFromHydraPersonId(hydraId.destination, webex).then((res) => {
187
- options.destination = res;
194
+ return MeetingInfoUtil.getSipUriFromHydraPersonId(hydraId && hydraId.destination, webex).then(
195
+ (res) => {
196
+ options.destination = res;
188
197
 
189
- // Since hydra person ids require a unique case in which they are
190
- // entirely converted to a SIP URI, we need to set a flag for detecting
191
- // this type of destination.
192
- options.wasHydraPerson = true;
198
+ // Since hydra person ids require a unique case in which they are
199
+ // entirely converted to a SIP URI, we need to set a flag for detecting
200
+ // this type of destination.
201
+ options.wasHydraPerson = true;
193
202
 
194
- return Promise.resolve(options);
195
- });
203
+ return Promise.resolve(options);
204
+ }
205
+ );
196
206
  } else if (hydraId.room) {
197
207
  LoggerProxy.logger.error(
198
208
  `Meeting-info:util#getDestinationType --> Using the space ID as a destination is no longer supported. Please refer to the [migration guide](https://github.com/webex/webex-js-sdk/wiki/Migration-to-Unified-Space-Meetings) to migrate to use the meeting ID or SIP address.`
@@ -268,7 +268,6 @@ export default class Member {
268
268
  audio: null,
269
269
  video: null,
270
270
  };
271
-
272
271
  // TODO: more participant types
273
272
  // such as native client, web client, is a device, what type of phone, etc
274
273
  this.processParticipant(participant);
@@ -10,7 +10,7 @@ import {createCameraStream, createDisplayStream, createMicrophoneStream, LocalTr
10
10
 
11
11
  import testUtils from '../../utils/testUtils';
12
12
  import integrationTestUtils from '../../utils/integrationTestUtils';
13
- import {EVENT_TRIGGERS} from '../../../src/constants';
13
+ import {EVENT_TRIGGERS} from '../../../dist/constants';
14
14
 
15
15
  require('dotenv').config();
16
16
 
@@ -239,7 +239,7 @@ skipInNode(describe)('plugin-meetings', () => {
239
239
 
240
240
  // Enabled when config.enableUnifiedMeetings = true
241
241
  xdescribe('Conversation URL', () => {
242
- describe('Successful 1:1 meeting', () => {
242
+ describe('Successful 1:1 meeting', () => {
243
243
  it('Fetch meeting information with a conversation URL for a 1:1 space', async () => {
244
244
  assert.equal(Object.keys(bob.webex.meetings.getAllMeetings()), 0);
245
245
  assert.equal(Object.keys(chris.webex.meetings.getAllMeetings()), 0);
@@ -89,7 +89,7 @@ describe('plugin-meetings', () => {
89
89
  })
90
90
  };
91
91
 
92
- sinon.stub(webex.internal.newMetrics.submitClientEvent, 'bind').returns(webex.internal.newMetrics.submitClientEvent);
92
+ const submitClientEventStub = sinon.stub(webex.internal.newMetrics.submitClientEvent, 'bind').returns(webex.internal.newMetrics.submitClientEvent);
93
93
 
94
94
  let onBreakoutMoveRequestStub = sinon.stub(breakoutEvent, 'onBreakoutMoveRequest');
95
95
  let onBreakoutMoveResponseStub = sinon.stub(breakoutEvent, 'onBreakoutMoveResponse');
@@ -105,6 +105,7 @@ describe('plugin-meetings', () => {
105
105
 
106
106
  onBreakoutMoveRequestStub.restore();
107
107
  onBreakoutMoveResponseStub.restore();
108
+ submitClientEventStub.restore()
108
109
  });
109
110
  });
110
111
 
@@ -381,7 +381,7 @@ describe('plugin-meetings', () => {
381
381
  id: 'meeting-id'
382
382
  })
383
383
  };
384
- breakoutEvent.onBreakoutJoinResponse = sinon.stub();
384
+ const onBreakoutJoinResponseSpy = sinon.stub(breakoutEvent,'onBreakoutJoinResponse')
385
385
  breakouts.currentBreakoutSession.sessionId = "sessionId-old";
386
386
  breakouts.updateBreakout({
387
387
  sessionId: 'sessionId-new',
@@ -398,7 +398,9 @@ describe('plugin-meetings', () => {
398
398
  breakoutMoveId: 'breakoutMoveId',
399
399
  });
400
400
 
401
- assert.calledOnce(breakoutEvent.onBreakoutJoinResponse);
401
+ assert.calledOnce(onBreakoutJoinResponseSpy);
402
+
403
+ onBreakoutJoinResponseSpy.restore()
402
404
 
403
405
  });
404
406
 
@@ -408,7 +410,7 @@ describe('plugin-meetings', () => {
408
410
  id: 'meeting-id'
409
411
  })
410
412
  };
411
- breakoutEvent.onBreakoutJoinResponse = sinon.stub();
413
+ const onBreakoutJoinResponseSpy = sinon.stub(breakoutEvent, 'onBreakoutJoinResponse');
412
414
  breakouts.currentBreakoutSession.sessionId = "sessionId";
413
415
  breakouts.currentBreakoutSession.groupId = "groupId";
414
416
  breakouts.updateBreakout({
@@ -426,7 +428,8 @@ describe('plugin-meetings', () => {
426
428
  breakoutMoveId: 'breakoutMoveId',
427
429
  });
428
430
 
429
- assert.notCalled(breakoutEvent.onBreakoutJoinResponse);
431
+ assert.notCalled(onBreakoutJoinResponseSpy);
432
+ onBreakoutJoinResponseSpy.restore()
430
433
 
431
434
  });
432
435
  });
@@ -1385,7 +1385,7 @@ describe('plugin-meetings', () => {
1385
1385
  function: 'updateMeetingInfo',
1386
1386
  },
1387
1387
  LOCUSINFO.EVENTS.MEETING_INFO_UPDATED,
1388
- payload
1388
+ payload,
1389
1389
  ];
1390
1390
 
1391
1391
  if (expected) {
@@ -1404,7 +1404,7 @@ describe('plugin-meetings', () => {
1404
1404
  function: 'updateMeetingInfo',
1405
1405
  },
1406
1406
  LOCUSINFO.EVENTS.MEETING_INFO_UPDATED,
1407
- payload
1407
+ payload,
1408
1408
  ];
1409
1409
 
1410
1410
  if (expected) {
@@ -1426,8 +1426,7 @@ describe('plugin-meetings', () => {
1426
1426
  */
1427
1427
  sinon.stub(locusInfo, 'emitScoped').callsFake(() => {
1428
1428
  assert.deepEqual(mockMeeting, expectedMeeting);
1429
- })
1430
-
1429
+ });
1431
1430
 
1432
1431
  // set the info initially as locusInfo.info starts as undefined
1433
1432
  expectedMeeting = {
@@ -1907,7 +1906,7 @@ describe('plugin-meetings', () => {
1907
1906
  locusInfo.locusParser.workingCopy = {
1908
1907
  syncUrl: 'current sync url',
1909
1908
  };
1910
-
1909
+
1911
1910
  locusInfo.applyLocusDeltaData(LOCUS_URL_CHANGED, fakeLocus, meeting);
1912
1911
  assert.calledOnceWithExactly(meeting.meetingRequest.getLocusDTO, {url: 'current sync url'});
1913
1912
  });
@@ -1970,8 +1969,12 @@ describe('plugin-meetings', () => {
1970
1969
  }).then(() => {
1971
1970
  assert.calledTwice(meeting.meetingRequest.getLocusDTO);
1972
1971
 
1973
- assert.deepEqual(meeting.meetingRequest.getLocusDTO.getCalls()[0].args, [{url: 'deltaSyncUrl'}]);
1974
- assert.deepEqual(meeting.meetingRequest.getLocusDTO.getCalls()[1].args, [{url: 'fullSyncUrl'}]);
1972
+ assert.deepEqual(meeting.meetingRequest.getLocusDTO.getCalls()[0].args, [
1973
+ {url: 'deltaSyncUrl'},
1974
+ ]);
1975
+ assert.deepEqual(meeting.meetingRequest.getLocusDTO.getCalls()[1].args, [
1976
+ {url: 'fullSyncUrl'},
1977
+ ]);
1975
1978
 
1976
1979
  assert.calledWith(sendBehavioralMetricStub, 'js_sdk_locus_delta_sync_failed', {
1977
1980
  correlationId: meeting.correlationId,
@@ -1999,8 +2002,12 @@ describe('plugin-meetings', () => {
1999
2002
  }).then(() => {
2000
2003
  assert.calledTwice(meeting.meetingRequest.getLocusDTO);
2001
2004
 
2002
- assert.deepEqual(meeting.meetingRequest.getLocusDTO.getCalls()[0].args, [{url: 'deltaSyncUrl'}]);
2003
- assert.deepEqual(meeting.meetingRequest.getLocusDTO.getCalls()[1].args, [{url: 'fullSyncUrl'}]);
2005
+ assert.deepEqual(meeting.meetingRequest.getLocusDTO.getCalls()[0].args, [
2006
+ {url: 'deltaSyncUrl'},
2007
+ ]);
2008
+ assert.deepEqual(meeting.meetingRequest.getLocusDTO.getCalls()[1].args, [
2009
+ {url: 'fullSyncUrl'},
2010
+ ]);
2004
2011
 
2005
2012
  assert.calledWith(sendBehavioralMetricStub, 'js_sdk_locus_delta_sync_failed', {
2006
2013
  correlationId: meeting.correlationId,
@@ -2084,7 +2091,7 @@ describe('plugin-meetings', () => {
2084
2091
 
2085
2092
  locusInfo.clearMainSessionLocusCache = sinon.stub();
2086
2093
  locusInfo.getTheLocusToUpdate(newLocus);
2087
- assert.notCalled(locusInfo.clearMainSessionLocusCache)
2094
+ assert.notCalled(locusInfo.clearMainSessionLocusCache);
2088
2095
  });
2089
2096
 
2090
2097
  it('return the new locus if return to main session but no cache and do not clear main session cache', () => {
@@ -2106,7 +2113,7 @@ describe('plugin-meetings', () => {
2106
2113
 
2107
2114
  locusInfo.clearMainSessionLocusCache = sinon.stub();
2108
2115
  locusInfo.getTheLocusToUpdate(newLocus);
2109
- assert.notCalled(locusInfo.clearMainSessionLocusCache)
2116
+ assert.notCalled(locusInfo.clearMainSessionLocusCache);
2110
2117
  });
2111
2118
 
2112
2119
  it('return the new locus if not return to main session and clear main session cache', () => {
@@ -2116,9 +2123,9 @@ describe('plugin-meetings', () => {
2116
2123
  sessionType: 'MAIN',
2117
2124
  },
2118
2125
  },
2119
- self: {removed: true}
2126
+ self: {removed: true},
2120
2127
  };
2121
- locusInfo.fullState = {state: 'ACTIVE'}
2128
+ locusInfo.fullState = {state: 'ACTIVE'};
2122
2129
  locusInfo.controls = {
2123
2130
  breakout: {
2124
2131
  sessionType: 'MAIN',
@@ -2134,7 +2141,7 @@ describe('plugin-meetings', () => {
2134
2141
 
2135
2142
  locusInfo.clearMainSessionLocusCache = sinon.stub();
2136
2143
  const result = locusInfo.getTheLocusToUpdate(newLocus);
2137
- assert.calledOnce(locusInfo.clearMainSessionLocusCache)
2144
+ assert.calledOnce(locusInfo.clearMainSessionLocusCache);
2138
2145
 
2139
2146
  assert.deepEqual(result, newLocus);
2140
2147
  });
@@ -2146,9 +2153,9 @@ describe('plugin-meetings', () => {
2146
2153
  sessionType: 'MAIN',
2147
2154
  },
2148
2155
  },
2149
- self: {removed: undefined}
2156
+ self: {removed: undefined},
2150
2157
  };
2151
- locusInfo.fullState = {state: 'ACTIVE'}
2158
+ locusInfo.fullState = {state: 'ACTIVE'};
2152
2159
  locusInfo.controls = {
2153
2160
  breakout: {
2154
2161
  sessionType: 'MAIN',
@@ -2164,7 +2171,7 @@ describe('plugin-meetings', () => {
2164
2171
 
2165
2172
  locusInfo.clearMainSessionLocusCache = sinon.stub();
2166
2173
  locusInfo.getTheLocusToUpdate(newLocus);
2167
- assert.notCalled(locusInfo.clearMainSessionLocusCache)
2174
+ assert.notCalled(locusInfo.clearMainSessionLocusCache);
2168
2175
  });
2169
2176
  });
2170
2177
 
@@ -2611,7 +2618,9 @@ describe('plugin-meetings', () => {
2611
2618
  // send an out-of-order delta
2612
2619
  locusInfo.handleLocusDelta(oooDelta, mockMeeting);
2613
2620
 
2614
- assert.calledOnceWithExactly(sendBehavioralMetricStub, 'js_sdk_locus_delta_ooo', { stack: sinon.match.any})
2621
+ assert.calledOnceWithExactly(sendBehavioralMetricStub, 'js_sdk_locus_delta_ooo', {
2622
+ stack: sinon.match.any,
2623
+ });
2615
2624
 
2616
2625
  await clock.tickAsync(12499);
2617
2626
  await testUtils.flushPromises();
@@ -150,8 +150,7 @@ describe('plugin-meetings', () => {
150
150
 
151
151
  it('should return false if no breakouts in current', () => {
152
152
  const current = {
153
- breakoutSessions: {
154
- },
153
+ breakoutSessions: {},
155
154
  };
156
155
  const previous = {
157
156
  breakoutSessions: {
@@ -293,7 +292,7 @@ describe('plugin-meetings', () => {
293
292
  const clonedSelf = cloneDeep(self);
294
293
 
295
294
  clonedSelf.controls.audio.requestedToUnmute = true;
296
- clonedSelf.controls.audio.lastModifiedRequestedToUnmute = '2023-06-16T18:25:04.369Z'
295
+ clonedSelf.controls.audio.lastModifiedRequestedToUnmute = '2023-06-16T18:25:04.369Z';
297
296
 
298
297
  const {updates} = SelfUtils.getSelves(self, clonedSelf);
299
298
 
@@ -371,23 +370,18 @@ describe('plugin-meetings', () => {
371
370
  const clonedSelf = cloneDeep(self);
372
371
 
373
372
  it('get breakoutMoveId works', () => {
374
-
375
373
  assert.deepEqual(SelfUtils.getReplacedBreakoutMoveId(self, deviceId), breakoutMoveId);
376
-
377
374
  });
378
375
 
379
376
  it('replaces is empty', () => {
380
-
381
377
  clonedSelf.devices[0].replaces = undefined;
382
378
  assert.deepEqual(SelfUtils.getReplacedBreakoutMoveId(clonedSelf, deviceId), null);
383
-
384
379
  });
385
380
 
386
381
  it('no self or self.devices is not array', () => {
387
-
388
382
  assert.deepEqual(SelfUtils.getReplacedBreakoutMoveId(undefined, deviceId), null);
389
383
 
390
- clonedSelf.devices = {
384
+ clonedSelf.devices = {
391
385
  url: 'https://wdm-a.wbx2.com/wdm/api/v1/devices/20eabde3-4254-48da-9a24',
392
386
  deviceType: 'WEB',
393
387
  mediaSessionsExternal: false,
@@ -395,10 +389,11 @@ describe('plugin-meetings', () => {
395
389
  {
396
390
  breakoutMoveId: 'e5caeb2c-ffcc-4e06-a08a-1122e7710398',
397
391
  lastActive: '2023-05-04T07:14:32.068Z',
398
- locusUrl: 'https://locus-alpha-apdx.prod.meetapi.webex.com/locus/api/v1/loci/495061ca-7b3c-3b77-85ff-4e1bd58600d1',
392
+ locusUrl:
393
+ 'https://locus-alpha-apdx.prod.meetapi.webex.com/locus/api/v1/loci/495061ca-7b3c-3b77-85ff-4e1bd58600d1',
399
394
  replacedAt: '2023-05-04T07:16:04.905Z',
400
395
  sessionId: 'be3147d4-c318-86d8-7611-8d24beaaca8d',
401
- }
396
+ },
402
397
  ],
403
398
  state: 'JOINED',
404
399
  };
@@ -7,6 +7,10 @@ import {forEach} from 'lodash';
7
7
  import MockWebex from '@webex/test-helper-mock-webex';
8
8
 
9
9
  describe('createMediaConnection', () => {
10
+ let clock;
11
+ beforeEach(() => {
12
+ clock = sinon.useFakeTimers();
13
+ });
10
14
  const webex = MockWebex();
11
15
 
12
16
  const fakeRoapMediaConnection = {
@@ -45,6 +49,7 @@ describe('createMediaConnection', () => {
45
49
  };
46
50
  afterEach(() => {
47
51
  sinon.restore();
52
+ clock.uninstall()
48
53
  });
49
54
 
50
55
  it('creates a RoapMediaConnection when multistream is disabled', () => {