@webex/plugin-meetings 3.1.0 → 3.3.0

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 (206) hide show
  1. package/dist/breakouts/breakout.js +1 -1
  2. package/dist/breakouts/index.js +1 -1
  3. package/dist/common/errors/{reconnection-in-progress.js → reconnection-not-started.js} +27 -15
  4. package/dist/common/errors/reconnection-not-started.js.map +1 -0
  5. package/dist/constants.js +12 -3
  6. package/dist/constants.js.map +1 -1
  7. package/dist/index.js +80 -0
  8. package/dist/index.js.map +1 -1
  9. package/dist/interpretation/index.js +1 -1
  10. package/dist/interpretation/siLanguage.js +1 -1
  11. package/dist/locus-info/controlsUtils.js +7 -1
  12. package/dist/locus-info/controlsUtils.js.map +1 -1
  13. package/dist/locus-info/index.js +10 -0
  14. package/dist/locus-info/index.js.map +1 -1
  15. package/dist/media/properties.js +102 -57
  16. package/dist/media/properties.js.map +1 -1
  17. package/dist/meeting/in-meeting-actions.js +6 -0
  18. package/dist/meeting/in-meeting-actions.js.map +1 -1
  19. package/dist/meeting/index.js +543 -467
  20. package/dist/meeting/index.js.map +1 -1
  21. package/dist/meeting/locusMediaRequest.js +27 -0
  22. package/dist/meeting/locusMediaRequest.js.map +1 -1
  23. package/dist/meeting/util.js +9 -16
  24. package/dist/meeting/util.js.map +1 -1
  25. package/dist/meeting/voicea-meeting.js +37 -49
  26. package/dist/meeting/voicea-meeting.js.map +1 -1
  27. package/dist/meeting-info/util.js +304 -267
  28. package/dist/meeting-info/util.js.map +1 -1
  29. package/dist/meeting-info/utilv2.js +334 -298
  30. package/dist/meeting-info/utilv2.js.map +1 -1
  31. package/dist/meetings/index.js +6 -27
  32. package/dist/meetings/index.js.map +1 -1
  33. package/dist/reachability/index.js +6 -0
  34. package/dist/reachability/index.js.map +1 -1
  35. package/dist/reconnection-manager/index.js +138 -109
  36. package/dist/reconnection-manager/index.js.map +1 -1
  37. package/dist/roap/request.js +3 -27
  38. package/dist/roap/request.js.map +1 -1
  39. package/dist/statsAnalyzer/index.js +4 -0
  40. package/dist/statsAnalyzer/index.js.map +1 -1
  41. package/dist/statsAnalyzer/mqaUtil.js +3 -0
  42. package/dist/statsAnalyzer/mqaUtil.js.map +1 -1
  43. package/dist/types/common/errors/reconnection-not-started.d.ts +13 -0
  44. package/dist/{constants.d.ts → types/constants.d.ts} +11 -2
  45. package/dist/types/index.d.ts +19 -0
  46. package/dist/{media → types/media}/properties.d.ts +26 -2
  47. package/dist/{meeting → types/meeting}/in-meeting-actions.d.ts +6 -0
  48. package/dist/{meeting → types/meeting}/index.d.ts +5 -6
  49. package/dist/{meeting → types/meeting}/locusMediaRequest.d.ts +1 -0
  50. package/dist/{meeting → types/meeting}/util.d.ts +3 -0
  51. package/dist/{meeting → types/meeting}/voicea-meeting.d.ts +3 -2
  52. package/dist/{meeting-info → types/meeting-info}/index.d.ts +1 -1
  53. package/dist/{meeting-info → types/meeting-info}/meeting-info-v2.d.ts +1 -1
  54. package/dist/types/meeting-info/util.d.ts +49 -0
  55. package/dist/types/meeting-info/utilv2.d.ts +65 -0
  56. package/dist/{meetings → types/meetings}/index.d.ts +1 -16
  57. package/dist/{reconnection-manager → types/reconnection-manager}/index.d.ts +4 -14
  58. package/dist/webinar/index.js +1 -1
  59. package/package.json +22 -22
  60. package/src/common/errors/reconnection-not-started.ts +25 -0
  61. package/src/constants.ts +12 -4
  62. package/src/index.ts +30 -0
  63. package/src/locus-info/controlsUtils.ts +11 -0
  64. package/src/locus-info/index.ts +16 -0
  65. package/src/media/properties.ts +67 -15
  66. package/src/meeting/in-meeting-actions.ts +12 -0
  67. package/src/meeting/index.ts +121 -98
  68. package/src/meeting/locusMediaRequest.ts +31 -0
  69. package/src/meeting/util.ts +9 -16
  70. package/src/meeting/voicea-meeting.ts +44 -46
  71. package/src/meeting-info/util.ts +241 -233
  72. package/src/meeting-info/utilv2.ts +250 -244
  73. package/src/meetings/index.ts +8 -25
  74. package/src/reachability/index.ts +3 -0
  75. package/src/reconnection-manager/index.ts +128 -105
  76. package/src/roap/request.ts +1 -24
  77. package/src/statsAnalyzer/index.ts +4 -0
  78. package/src/statsAnalyzer/mqaUtil.ts +5 -0
  79. package/test/unit/spec/locus-info/controlsUtils.js +20 -0
  80. package/test/unit/spec/locus-info/index.js +21 -0
  81. package/test/unit/spec/media/properties.ts +145 -140
  82. package/test/unit/spec/meeting/in-meeting-actions.ts +6 -0
  83. package/test/unit/spec/meeting/index.js +243 -97
  84. package/test/unit/spec/meeting/locusMediaRequest.ts +49 -0
  85. package/test/unit/spec/meeting/utils.js +3 -10
  86. package/test/unit/spec/meeting/voicea-meeting.ts +5 -14
  87. package/test/unit/spec/meetings/index.js +27 -8
  88. package/test/unit/spec/reconnection-manager/index.js +127 -39
  89. package/test/unit/spec/roap/request.ts +0 -37
  90. package/test/unit/spec/stats-analyzer/index.js +11 -0
  91. package/dist/common/errors/reconnection-in-progress.d.ts +0 -9
  92. package/dist/common/errors/reconnection-in-progress.js.map +0 -1
  93. package/dist/index.d.ts +0 -7
  94. package/dist/meeting-info/util.d.ts +0 -2
  95. package/dist/meeting-info/utilv2.d.ts +0 -2
  96. package/src/common/errors/reconnection-in-progress.ts +0 -8
  97. /package/dist/{annotation → types/annotation}/annotation.types.d.ts +0 -0
  98. /package/dist/{annotation → types/annotation}/constants.d.ts +0 -0
  99. /package/dist/{annotation → types/annotation}/index.d.ts +0 -0
  100. /package/dist/{breakouts → types/breakouts}/breakout.d.ts +0 -0
  101. /package/dist/{breakouts → types/breakouts}/collection.d.ts +0 -0
  102. /package/dist/{breakouts → types/breakouts}/edit-lock-error.d.ts +0 -0
  103. /package/dist/{breakouts → types/breakouts}/events.d.ts +0 -0
  104. /package/dist/{breakouts → types/breakouts}/index.d.ts +0 -0
  105. /package/dist/{breakouts → types/breakouts}/request.d.ts +0 -0
  106. /package/dist/{breakouts → types/breakouts}/utils.d.ts +0 -0
  107. /package/dist/{common → types/common}/browser-detection.d.ts +0 -0
  108. /package/dist/{common → types/common}/collection.d.ts +0 -0
  109. /package/dist/{common → types/common}/config.d.ts +0 -0
  110. /package/dist/{common → types/common}/errors/captcha-error.d.ts +0 -0
  111. /package/dist/{common → types/common}/errors/intent-to-join.d.ts +0 -0
  112. /package/dist/{common → types/common}/errors/join-meeting.d.ts +0 -0
  113. /package/dist/{common → types/common}/errors/media.d.ts +0 -0
  114. /package/dist/{common → types/common}/errors/no-meeting-info.d.ts +0 -0
  115. /package/dist/{common → types/common}/errors/parameter.d.ts +0 -0
  116. /package/dist/{common → types/common}/errors/password-error.d.ts +0 -0
  117. /package/dist/{common → types/common}/errors/permission.d.ts +0 -0
  118. /package/dist/{common → types/common}/errors/reclaim-host-role-errors.d.ts +0 -0
  119. /package/dist/{common → types/common}/errors/reconnection.d.ts +0 -0
  120. /package/dist/{common → types/common}/errors/stats.d.ts +0 -0
  121. /package/dist/{common → types/common}/errors/webex-errors.d.ts +0 -0
  122. /package/dist/{common → types/common}/errors/webex-meetings-error.d.ts +0 -0
  123. /package/dist/{common → types/common}/events/events-scope.d.ts +0 -0
  124. /package/dist/{common → types/common}/events/events.d.ts +0 -0
  125. /package/dist/{common → types/common}/events/trigger-proxy.d.ts +0 -0
  126. /package/dist/{common → types/common}/events/util.d.ts +0 -0
  127. /package/dist/{common → types/common}/logs/logger-config.d.ts +0 -0
  128. /package/dist/{common → types/common}/logs/logger-proxy.d.ts +0 -0
  129. /package/dist/{common → types/common}/logs/request.d.ts +0 -0
  130. /package/dist/{common → types/common}/queue.d.ts +0 -0
  131. /package/dist/{config.d.ts → types/config.d.ts} +0 -0
  132. /package/dist/{controls-options-manager → types/controls-options-manager}/constants.d.ts +0 -0
  133. /package/dist/{controls-options-manager → types/controls-options-manager}/enums.d.ts +0 -0
  134. /package/dist/{controls-options-manager → types/controls-options-manager}/index.d.ts +0 -0
  135. /package/dist/{controls-options-manager → types/controls-options-manager}/types.d.ts +0 -0
  136. /package/dist/{controls-options-manager → types/controls-options-manager}/util.d.ts +0 -0
  137. /package/dist/{interceptors → types/interceptors}/index.d.ts +0 -0
  138. /package/dist/{interceptors → types/interceptors}/locusRetry.d.ts +0 -0
  139. /package/dist/{interpretation → types/interpretation}/collection.d.ts +0 -0
  140. /package/dist/{interpretation → types/interpretation}/index.d.ts +0 -0
  141. /package/dist/{interpretation → types/interpretation}/siLanguage.d.ts +0 -0
  142. /package/dist/{locus-info → types/locus-info}/controlsUtils.d.ts +0 -0
  143. /package/dist/{locus-info → types/locus-info}/embeddedAppsUtils.d.ts +0 -0
  144. /package/dist/{locus-info → types/locus-info}/fullState.d.ts +0 -0
  145. /package/dist/{locus-info → types/locus-info}/hostUtils.d.ts +0 -0
  146. /package/dist/{locus-info → types/locus-info}/index.d.ts +0 -0
  147. /package/dist/{locus-info → types/locus-info}/infoUtils.d.ts +0 -0
  148. /package/dist/{locus-info → types/locus-info}/mediaSharesUtils.d.ts +0 -0
  149. /package/dist/{locus-info → types/locus-info}/parser.d.ts +0 -0
  150. /package/dist/{locus-info → types/locus-info}/selfUtils.d.ts +0 -0
  151. /package/dist/{media → types/media}/MediaConnectionAwaiter.d.ts +0 -0
  152. /package/dist/{media → types/media}/index.d.ts +0 -0
  153. /package/dist/{media → types/media}/util.d.ts +0 -0
  154. /package/dist/{mediaQualityMetrics → types/mediaQualityMetrics}/config.d.ts +0 -0
  155. /package/dist/{meeting → types/meeting}/muteState.d.ts +0 -0
  156. /package/dist/{meeting → types/meeting}/request.d.ts +0 -0
  157. /package/dist/{meeting → types/meeting}/request.type.d.ts +0 -0
  158. /package/dist/{meeting → types/meeting}/state.d.ts +0 -0
  159. /package/dist/{meeting-info → types/meeting-info}/collection.d.ts +0 -0
  160. /package/dist/{meeting-info → types/meeting-info}/request.d.ts +0 -0
  161. /package/dist/{meetings → types/meetings}/collection.d.ts +0 -0
  162. /package/dist/{meetings → types/meetings}/meetings.types.d.ts +0 -0
  163. /package/dist/{meetings → types/meetings}/request.d.ts +0 -0
  164. /package/dist/{meetings → types/meetings}/util.d.ts +0 -0
  165. /package/dist/{member → types/member}/index.d.ts +0 -0
  166. /package/dist/{member → types/member}/types.d.ts +0 -0
  167. /package/dist/{member → types/member}/util.d.ts +0 -0
  168. /package/dist/{members → types/members}/collection.d.ts +0 -0
  169. /package/dist/{members → types/members}/index.d.ts +0 -0
  170. /package/dist/{members → types/members}/request.d.ts +0 -0
  171. /package/dist/{members → types/members}/types.d.ts +0 -0
  172. /package/dist/{members → types/members}/util.d.ts +0 -0
  173. /package/dist/{metrics → types/metrics}/constants.d.ts +0 -0
  174. /package/dist/{metrics → types/metrics}/index.d.ts +0 -0
  175. /package/dist/{multistream → types/multistream}/mediaRequestManager.d.ts +0 -0
  176. /package/dist/{multistream → types/multistream}/receiveSlot.d.ts +0 -0
  177. /package/dist/{multistream → types/multistream}/receiveSlotManager.d.ts +0 -0
  178. /package/dist/{multistream → types/multistream}/remoteMedia.d.ts +0 -0
  179. /package/dist/{multistream → types/multistream}/remoteMediaGroup.d.ts +0 -0
  180. /package/dist/{multistream → types/multistream}/remoteMediaManager.d.ts +0 -0
  181. /package/dist/{multistream → types/multistream}/sendSlotManager.d.ts +0 -0
  182. /package/dist/{networkQualityMonitor → types/networkQualityMonitor}/index.d.ts +0 -0
  183. /package/dist/{personal-meeting-room → types/personal-meeting-room}/index.d.ts +0 -0
  184. /package/dist/{personal-meeting-room → types/personal-meeting-room}/request.d.ts +0 -0
  185. /package/dist/{personal-meeting-room → types/personal-meeting-room}/util.d.ts +0 -0
  186. /package/dist/{reachability → types/reachability}/clusterReachability.d.ts +0 -0
  187. /package/dist/{reachability → types/reachability}/index.d.ts +0 -0
  188. /package/dist/{reachability → types/reachability}/request.d.ts +0 -0
  189. /package/dist/{reachability → types/reachability}/util.d.ts +0 -0
  190. /package/dist/{reactions → types/reactions}/constants.d.ts +0 -0
  191. /package/dist/{reactions → types/reactions}/reactions.d.ts +0 -0
  192. /package/dist/{reactions → types/reactions}/reactions.type.d.ts +0 -0
  193. /package/dist/{recording-controller → types/recording-controller}/enums.d.ts +0 -0
  194. /package/dist/{recording-controller → types/recording-controller}/index.d.ts +0 -0
  195. /package/dist/{recording-controller → types/recording-controller}/util.d.ts +0 -0
  196. /package/dist/{roap → types/roap}/index.d.ts +0 -0
  197. /package/dist/{roap → types/roap}/request.d.ts +0 -0
  198. /package/dist/{roap → types/roap}/turnDiscovery.d.ts +0 -0
  199. /package/dist/{rtcMetrics → types/rtcMetrics}/constants.d.ts +0 -0
  200. /package/dist/{rtcMetrics → types/rtcMetrics}/index.d.ts +0 -0
  201. /package/dist/{statsAnalyzer → types/statsAnalyzer}/global.d.ts +0 -0
  202. /package/dist/{statsAnalyzer → types/statsAnalyzer}/index.d.ts +0 -0
  203. /package/dist/{statsAnalyzer → types/statsAnalyzer}/mqaUtil.d.ts +0 -0
  204. /package/dist/{transcription → types/transcription}/index.d.ts +0 -0
  205. /package/dist/{webinar → types/webinar}/collection.d.ts +0 -0
  206. /package/dist/{webinar → types/webinar}/index.d.ts +0 -0
@@ -106,6 +106,12 @@ describe('LocusMediaRequest.send()', () => {
106
106
  },
107
107
  });
108
108
 
109
+ mockWebex.internal = {
110
+ newMetrics: {
111
+ submitClientEvent: sinon.stub()
112
+ },
113
+ };
114
+
109
115
  locusMediaRequest = new LocusMediaRequest({
110
116
  device: {
111
117
  url: 'deviceUrl',
@@ -113,6 +119,7 @@ describe('LocusMediaRequest.send()', () => {
113
119
  regionCode: 'regionCode',
114
120
  },
115
121
  correlationId: 'correlationId',
122
+ meetingId: 'meetingId',
116
123
  preferTranscoding: true,
117
124
  }, {
118
125
  parent: mockWebex,
@@ -134,6 +141,27 @@ describe('LocusMediaRequest.send()', () => {
134
141
  await sendRoapMessage('OFFER');
135
142
 
136
143
  webexRequestStub.resetHistory();
144
+ mockWebex.internal.newMetrics.submitClientEvent.resetHistory();
145
+ }
146
+
147
+ const checkMetrics = (expectedMetrics: boolean = true) => {
148
+ if (expectedMetrics) {
149
+ assert.calledWith(mockWebex.internal.newMetrics.submitClientEvent, {
150
+ name: 'client.locus.media.request',
151
+ options: {
152
+ meetingId: 'meetingId',
153
+ },
154
+ });
155
+
156
+ assert.calledWith(mockWebex.internal.newMetrics.submitClientEvent, {
157
+ name: 'client.locus.media.response',
158
+ options: {
159
+ meetingId: 'meetingId',
160
+ },
161
+ });
162
+ } else {
163
+ assert.notCalled(mockWebex.internal.newMetrics.submitClientEvent);
164
+ }
137
165
  }
138
166
 
139
167
  it('sends a roap message', async () => {
@@ -146,6 +174,21 @@ describe('LocusMediaRequest.send()', () => {
146
174
  uri: 'fakeMeetingSelfUrl/media',
147
175
  body: createExpectedRoapBody('OFFER', {audioMuted: true, videoMuted: true}),
148
176
  });
177
+
178
+ checkMetrics();
179
+ });
180
+
181
+ it('sends correct metric event when roap message fails', async () => {
182
+ webexRequestStub.rejects({code: 300, message: 'fake error'});
183
+ await assert.isRejected(sendRoapMessage('OFFER'));
184
+
185
+ assert.calledWith(mockWebex.internal.newMetrics.submitClientEvent, {
186
+ name: 'client.locus.media.response',
187
+ options: {
188
+ meetingId: 'meetingId',
189
+ rawError: {code: 300, message: 'fake error'},
190
+ },
191
+ });
149
192
  });
150
193
 
151
194
  it('sends a local mute request', async () => {
@@ -160,6 +203,8 @@ describe('LocusMediaRequest.send()', () => {
160
203
  uri: 'fakeMeetingSelfUrl/media',
161
204
  body: createExpectedLocalMuteBody({audioMuted: false, videoMuted: false}),
162
205
  });
206
+
207
+ checkMetrics(false);
163
208
  });
164
209
 
165
210
  it('sends a local mute request with sequence', async () => {
@@ -207,6 +252,7 @@ describe('LocusMediaRequest.send()', () => {
207
252
  body: createExpectedLocalMuteBody({audioMuted: false, videoMuted: true}),
208
253
  });
209
254
 
255
+ checkMetrics(false);
210
256
  });
211
257
 
212
258
  it('sends a local mute request with the last audio/video mute values', async () => {
@@ -225,6 +271,7 @@ describe('LocusMediaRequest.send()', () => {
225
271
  body: createExpectedLocalMuteBody({audioMuted: true, videoMuted: false}),
226
272
  });
227
273
 
274
+ checkMetrics(false);
228
275
  });
229
276
 
230
277
  it('sends only roap when roap and local mute are requested', async () => {
@@ -242,6 +289,8 @@ describe('LocusMediaRequest.send()', () => {
242
289
  uri: 'fakeMeetingSelfUrl/media',
243
290
  body: createExpectedRoapBody('OFFER', {audioMuted: true, videoMuted: false}),
244
291
  });
292
+
293
+ checkMetrics();
245
294
  });
246
295
 
247
296
  describe('queueing', () => {
@@ -370,16 +370,6 @@ describe('plugin-meetings', () => {
370
370
  sequence: {},
371
371
  type: 'LocalMute',
372
372
  });
373
-
374
- assert.calledWith(webex.internal.newMetrics.submitClientEvent, {
375
- name: 'client.locus.media.request',
376
- options: {meetingId: meeting.id},
377
- });
378
-
379
- assert.calledWith(webex.internal.newMetrics.submitClientEvent, {
380
- name: 'client.locus.media.response',
381
- options: {meetingId: meeting.id},
382
- });
383
373
  });
384
374
  });
385
375
 
@@ -765,6 +755,9 @@ describe('plugin-meetings', () => {
765
755
  {functionName: 'canStartTranscribing', displayHint: 'TRANSCRIPTION_CONTROL_START'},
766
756
  {functionName: 'canStopTranscribing', displayHint: 'TRANSCRIPTION_CONTROL_STOP'},
767
757
  {functionName: 'isClosedCaptionActive', displayHint: 'CAPTION_STATUS_ACTIVE'},
758
+ {functionName: 'canStartManualCaption', displayHint: 'MANUAL_CAPTION_START'},
759
+ {functionName: 'canStopManualCaption', displayHint: 'MANUAL_CAPTION_STOP'},
760
+ {functionName: 'isManualCaptionActive', displayHint: 'MANUAL_CAPTION_STATUS_ACTIVE'},
768
761
  {functionName: 'isWebexAssistantActive', displayHint: 'WEBEX_ASSISTANT_STATUS_ACTIVE'},
769
762
  {functionName: 'canViewCaptionPanel', displayHint: 'ENABLE_CAPTION_PANEL'},
770
763
  {functionName: 'isRealTimeTranslationEnabled', displayHint: 'DISPLAY_REAL_TIME_TRANSLATION'},
@@ -101,14 +101,7 @@ describe('plugin-meetings', () => {
101
101
  ],
102
102
  transcript_language_code: "en"
103
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
- }
104
+ ]
112
105
  };
113
106
  });
114
107
 
@@ -160,7 +153,6 @@ describe('plugin-meetings', () => {
160
153
  it('should process new final captions correctly', () => {
161
154
  let transcriptData = fakeMeeting.transcription;
162
155
  let transcriptId = fakeVoiceaPayload.transcriptId;
163
- delete fakeVoiceaPayload.transcripts;
164
156
 
165
157
  // Assuming that processNewCaptions is a pure function that doesn't mutate the input but returns a new state
166
158
  processNewCaptions({
@@ -169,7 +161,7 @@ describe('plugin-meetings', () => {
169
161
  });
170
162
 
171
163
  // Check if speaker details are cached if needed
172
- const csisKey = fakeVoiceaPayload.transcript.csis[0];
164
+ const csisKey = fakeVoiceaPayload.transcripts[0].csis[0];
173
165
  const speaker = transcriptData.speakerProxy[csisKey];
174
166
  expect(speaker).to.exist;
175
167
 
@@ -178,6 +170,7 @@ describe('plugin-meetings', () => {
178
170
 
179
171
  //check if the interim caption is removed
180
172
  const oldInterimCaption = transcriptData.captions.find(caption => caption.id === `${transcriptId}_${speaker.speakerId}`);
173
+ console.log(oldInterimCaption);
181
174
  expect(oldInterimCaption).to.not.exist;
182
175
 
183
176
  // Check the final caption data
@@ -186,8 +179,8 @@ describe('plugin-meetings', () => {
186
179
  expect(newCaption).to.include({
187
180
  id: transcriptId,
188
181
  isFinal: fakeVoiceaPayload.isFinal,
189
- text: fakeVoiceaPayload.transcript.text,
190
- currentSpokenLanguage: fakeVoiceaPayload.transcript.transcript_language_code,
182
+ text: fakeVoiceaPayload.transcripts[0].text,
183
+ currentSpokenLanguage: fakeVoiceaPayload.transcripts[0].transcript_language_code,
191
184
  });
192
185
 
193
186
  // Check the speaker data in the new caption
@@ -197,7 +190,6 @@ describe('plugin-meetings', () => {
197
190
  it('should process new interim captions correctly', () => {
198
191
  let transcriptData = fakeMeeting.transcription;
199
192
  let transcriptId = fakeVoiceaPayload.transcriptId;
200
- delete fakeVoiceaPayload.transcript;
201
193
 
202
194
  transcriptData.captions.splice(transcriptData.length - 1, 1);
203
195
  fakeVoiceaPayload.isFinal = false;
@@ -232,7 +224,6 @@ describe('plugin-meetings', () => {
232
224
  it('should process interim captions with an existing one correctly', () => {
233
225
  let transcriptData = fakeMeeting.transcription;
234
226
  let transcriptId = fakeVoiceaPayload.transcriptId;
235
- delete fakeVoiceaPayload.transcript;
236
227
  fakeVoiceaPayload.isFinal = false;
237
228
 
238
229
  processNewCaptions({
@@ -513,13 +513,8 @@ describe('plugin-meetings', () => {
513
513
  });
514
514
  describe('#getAllMeetings', () => {
515
515
  it('calls MeetingCollection to get all meetings with supplied options', () => {
516
- webex.meetings.getAllMeetings({
517
- test: test1,
518
- });
516
+ webex.meetings.getAllMeetings();
519
517
  assert.calledOnce(webex.meetings.meetingCollection.getAll);
520
- assert.calledWith(webex.meetings.meetingCollection.getAll, {
521
- test: test1,
522
- });
523
518
  });
524
519
  });
525
520
  });
@@ -1011,6 +1006,7 @@ describe('plugin-meetings', () => {
1011
1006
  callBackInfo: {
1012
1007
  callbackAddress: uri1,
1013
1008
  },
1009
+ devices: [],
1014
1010
  },
1015
1011
  info: {
1016
1012
  webExMeetingId,
@@ -1038,6 +1034,7 @@ describe('plugin-meetings', () => {
1038
1034
  callBackInfo: {
1039
1035
  callbackAddress: uri1,
1040
1036
  },
1037
+ devices: [],
1041
1038
  },
1042
1039
  info: {
1043
1040
  webExMeetingId,
@@ -1052,6 +1049,7 @@ describe('plugin-meetings', () => {
1052
1049
  callBackInfo: {
1053
1050
  callbackAddress: uri1,
1054
1051
  },
1052
+ devices: [],
1055
1053
  },
1056
1054
  info: {
1057
1055
  webExMeetingId,
@@ -1074,6 +1072,7 @@ describe('plugin-meetings', () => {
1074
1072
  callBackInfo: {
1075
1073
  callbackAddress: uri1,
1076
1074
  },
1075
+ devices: [],
1077
1076
  },
1078
1077
  info: {
1079
1078
  webExMeetingId,
@@ -1091,6 +1090,7 @@ describe('plugin-meetings', () => {
1091
1090
  callBackInfo: {
1092
1091
  callbackAddress: uri1,
1093
1092
  },
1093
+ devices: [],
1094
1094
  },
1095
1095
  info: {
1096
1096
  webExMeetingId,
@@ -1121,6 +1121,7 @@ describe('plugin-meetings', () => {
1121
1121
  callBackInfo: {
1122
1122
  callbackAddress: uri1,
1123
1123
  },
1124
+ devices: [],
1124
1125
  },
1125
1126
  info: {
1126
1127
  webExMeetingId,
@@ -1143,6 +1144,7 @@ describe('plugin-meetings', () => {
1143
1144
  callBackInfo: {
1144
1145
  callbackAddress: uri1,
1145
1146
  },
1147
+ devices: [],
1146
1148
  },
1147
1149
  info: {
1148
1150
  webExMeetingId,
@@ -1157,6 +1159,7 @@ describe('plugin-meetings', () => {
1157
1159
  callbackInfo: {
1158
1160
  callbackAddress: uri1,
1159
1161
  },
1162
+ devices: [],
1160
1163
  },
1161
1164
  info: {
1162
1165
  isUnifiedSpaceMeeting,
@@ -2299,6 +2302,7 @@ describe('plugin-meetings', () => {
2299
2302
  sessionType: 'BREAKOUT',
2300
2303
  };
2301
2304
  newLocus.self.state = 'JOINED';
2305
+ newLocus.self.devices = [];
2302
2306
  newLocus.fullState = {
2303
2307
  active: true,
2304
2308
  };
@@ -2315,7 +2319,7 @@ describe('plugin-meetings', () => {
2315
2319
  sessionType: 'MAIN',
2316
2320
  };
2317
2321
  newLocus.self.state = 'JOINED';
2318
-
2322
+ newLocus.self.devices = [];
2319
2323
  LoggerProxy.logger.log = sinon.stub();
2320
2324
  const result = webex.meetings.isNeedHandleLocusDTO(meeting, newLocus);
2321
2325
  assert.equal(result, true);
@@ -2330,11 +2334,26 @@ describe('plugin-meetings', () => {
2330
2334
  };
2331
2335
  newLocus.self.state = 'LEFT';
2332
2336
  newLocus.self.reason = 'MOVED';
2333
-
2337
+ newLocus.self.devices = [];
2334
2338
  LoggerProxy.logger.log = sinon.stub();
2335
2339
  const result = webex.meetings.isNeedHandleLocusDTO(meeting, newLocus);
2336
2340
  assert.equal(result, false);
2337
2341
  });
2342
+ it('moved to lobby, return true', () => {
2343
+ newLocus.controls.breakout = {
2344
+ sessionType: 'MAIN',
2345
+ };
2346
+ newLocus.self.state = 'JOINED';
2347
+ newLocus.self.devices = [{
2348
+ intent: {
2349
+ reason: 'ON_HOLD_LOBBY',
2350
+ type: 'WAIT',
2351
+ }
2352
+ }];
2353
+ LoggerProxy.logger.log = sinon.stub();
2354
+ const result = webex.meetings.isNeedHandleLocusDTO(meeting, newLocus);
2355
+ assert.equal(result, true);
2356
+ });
2338
2357
  });
2339
2358
 
2340
2359
  describe('#getCorrespondingMeetingByLocus', () => {
@@ -2,11 +2,14 @@ import 'jsdom-global/register';
2
2
  import chai from 'chai';
3
3
  import chaiAsPromised from 'chai-as-promised';
4
4
  import sinon from 'sinon';
5
+ import {Defer} from '@webex/common';
5
6
  import ReconnectionManager from '@webex/plugin-meetings/src/reconnection-manager';
7
+ import ReconnectionNotStartedError from '@webex/plugin-meetings/src/common/errors/reconnection-not-started';
8
+ import TriggerProxy from '@webex/plugin-meetings/src/common/events/trigger-proxy';
9
+ import Metrics from '@webex/plugin-meetings/src/metrics';
6
10
  import { RECONNECTION } from '../../../../src/constants';
7
11
  import LoggerProxy from '../../../../src/common/logs/logger-proxy';
8
12
  import LoggerConfig from '../../../../src/common/logs/logger-config';
9
-
10
13
  const {assert} = chai;
11
14
 
12
15
  chai.use(chaiAsPromised);
@@ -14,18 +17,15 @@ sinon.assert.expose(chai.assert, {prefix: ''});
14
17
 
15
18
  describe('plugin-meetings', () => {
16
19
  describe('ReconnectionManager.reconnect', () => {
17
- const sandbox = sinon.createSandbox();
18
20
  let fakeMediaConnection;
19
21
  let fakeMeeting;
20
22
  let loggerSpy;
21
23
 
22
- before(() => {
24
+ beforeEach(() => {
23
25
  LoggerConfig.set({ enable: false });
24
26
  LoggerProxy.set();
25
- loggerSpy = sandbox.spy(LoggerProxy.logger, 'info');
26
- });
27
+ loggerSpy = sinon.spy(LoggerProxy.logger, 'info');
27
28
 
28
- beforeEach(() => {
29
29
  fakeMediaConnection = {
30
30
  initiateOffer: sinon.stub().resolves({}),
31
31
  reconnect: sinon.stub().resolves({}),
@@ -33,6 +33,7 @@ describe('plugin-meetings', () => {
33
33
  fakeMeeting = {
34
34
  closePeerConnections: sinon.stub().resolves({}),
35
35
  createMediaConnection: sinon.stub().returns(fakeMediaConnection),
36
+ correlationId: 'correlationId',
36
37
  config: {
37
38
  reconnection: {
38
39
  enabled: true,
@@ -47,6 +48,7 @@ describe('plugin-meetings', () => {
47
48
  },
48
49
  },
49
50
  },
51
+ locusUrl: 'test/id',
50
52
  mediaProperties: {
51
53
  unsetPeerConnection: sinon.stub(),
52
54
  webrtcMediaConnection: fakeMediaConnection,
@@ -83,11 +85,16 @@ describe('plugin-meetings', () => {
83
85
  }
84
86
  }
85
87
  },
88
+ trigger: sinon.stub(),
86
89
  };
90
+
91
+ sinon.stub(TriggerProxy, 'trigger').returns(true);
92
+ sinon.stub(Metrics, 'sendBehavioralMetric');
87
93
  });
88
94
 
89
95
  afterEach(() => {
90
- sandbox.reset();
96
+ sinon.reset();
97
+ sinon.restore();
91
98
  });
92
99
 
93
100
  it('calls syncMeetings', async () => {
@@ -223,6 +230,99 @@ describe('plugin-meetings', () => {
223
230
  });
224
231
  }
225
232
  });
233
+
234
+ it('sends the right metrics and events when succeeds', async () => {
235
+ const rm = new ReconnectionManager(fakeMeeting);
236
+
237
+ await rm.reconnect();
238
+
239
+ assert.calledWith(
240
+ TriggerProxy.trigger,
241
+ fakeMeeting,
242
+ {file: 'reconnection-manager/index', function: 'reconnect'},
243
+ 'meeting:reconnectionSuccess'
244
+ );
245
+ assert.calledWithMatch(fakeMeeting.webex.internal.newMetrics.submitClientEvent, {
246
+ name: 'client.media.recovered',
247
+ payload: {
248
+ recoveredBy: 'new',
249
+ },
250
+ options: {
251
+ meetingId: fakeMeeting.id,
252
+ },
253
+ });
254
+ assert.equal(rm.status, RECONNECTION.STATE.DEFAULT_STATUS);
255
+ });
256
+
257
+ it('sends the right metrics and events when fails', async () => {
258
+ const rm = new ReconnectionManager(fakeMeeting);
259
+
260
+ sinon.stub(rm, 'executeReconnection').rejects(new Error('fake error'));
261
+
262
+ await assert.isRejected(rm.reconnect());
263
+
264
+ assert.calledWith(
265
+ TriggerProxy.trigger,
266
+ fakeMeeting,
267
+ {file: 'reconnection-manager/index', function: 'reconnect'},
268
+ 'meeting:reconnectionFailure'
269
+ );
270
+ assert.calledWithMatch(fakeMeeting.webex.internal.newMetrics.submitClientEvent, {
271
+ name: 'client.call.aborted',
272
+ payload: {
273
+ errors: [
274
+ {
275
+ category: 'expected',
276
+ errorCode: 2008,
277
+ fatal: true,
278
+ name: 'media-engine',
279
+ shownToUser: false,
280
+ },
281
+ ],
282
+ },
283
+ options: {
284
+ meetingId: fakeMeeting.id,
285
+ },
286
+ });
287
+ assert.calledWith(Metrics.sendBehavioralMetric, 'js_sdk_meeting_reconnect_failures', {
288
+ correlation_id: fakeMeeting.correlationId,
289
+ locus_id: 'id',
290
+ reason: 'fake error',
291
+ stack: sinon.match.any,
292
+ });
293
+ assert.equal(rm.status, RECONNECTION.STATE.DEFAULT_STATUS);
294
+ });
295
+
296
+ it('throws ReconnectionNotStartedError if reconnection is already in progress', async () => {
297
+ const rm = new ReconnectionManager(fakeMeeting);
298
+ const defer = new Defer();
299
+
300
+ sinon.stub(rm, 'executeReconnection').returns(defer.promise);
301
+
302
+ rm.reconnect();
303
+
304
+ try {
305
+ await rm.reconnect();
306
+
307
+ fail("rm.reconnect() should have thrown, but it hasn't");
308
+ } catch (e) {
309
+ assert.instanceOf(e, ReconnectionNotStartedError);
310
+ }
311
+ });
312
+
313
+ it('throws ReconnectionNotStartedError if reconnection is disabled in config', async () => {
314
+ fakeMeeting.config.reconnection.enabled = false;
315
+
316
+ const rm = new ReconnectionManager(fakeMeeting);
317
+
318
+ try {
319
+ await rm.reconnect();
320
+
321
+ fail("rm.reconnect() should have thrown, but it hasn't");
322
+ } catch (e) {
323
+ assert.instanceOf(e, ReconnectionNotStartedError);
324
+ }
325
+ });
226
326
  });
227
327
 
228
328
  /**
@@ -316,29 +416,41 @@ describe('plugin-meetings', () => {
316
416
 
317
417
  describe('waitForIceReconnect()', () => {
318
418
  describe('when ice is marked as not disconnected', () => {
419
+ let clock;
420
+
319
421
  beforeEach(() => {
422
+ clock = sinon.useFakeTimers();
320
423
  reconnectionManager.iceState.disconnected = false;
321
424
  });
322
425
 
426
+ afterEach(() => {
427
+ clock.restore();
428
+ });
429
+
323
430
  it('should set the disconnected state to true', () => {
324
- reconnectionManager.waitForIceReconnect();
431
+ const promise = reconnectionManager.waitForIceReconnect();
325
432
 
326
433
  assert.isTrue(reconnectionManager.iceState.disconnected);
434
+
435
+ // we let the timer expire
436
+ clock.tick(reconnectionManager.iceState.timeoutDuration);
437
+ assert.isRejected(promise);
327
438
  });
328
439
 
329
- it('should return a promise that rejects after a duration', (done) => {
330
- reconnectionManager.iceState.timeoutDuration = 100;
440
+ it('should return a promise that rejects after a duration', async () => {
441
+ const promise = reconnectionManager.waitForIceReconnect();
331
442
 
332
- assert.isRejected(reconnectionManager.waitForIceReconnect());
333
- done();
443
+ // we let the timer expire
444
+ clock.tick(reconnectionManager.iceState.timeoutDuration);
445
+ assert.isRejected(promise);
334
446
  });
335
447
 
336
- it('should resolve return a resolved promise when triggered', () => {
448
+ it('should resolve when ICE is reconnected', async () => {
337
449
  const promise = reconnectionManager.waitForIceReconnect();
338
450
 
339
- reconnectionManager.iceState.resolve();
451
+ reconnectionManager.iceReconnected();
340
452
 
341
- assert.isFulfilled(promise);
453
+ await promise;
342
454
  });
343
455
  });
344
456
 
@@ -359,30 +471,6 @@ describe('plugin-meetings', () => {
359
471
  });
360
472
  });
361
473
 
362
- describe('setStatus()', () => {
363
- beforeEach(() => {
364
- reconnectionManager.status = RECONNECTION.STATE.DEFAULT_STATUS;
365
- });
366
-
367
- it('should correctly change status to in progress', () => {
368
- reconnectionManager.setStatus(RECONNECTION.STATE.IN_PROGRESS);
369
-
370
- assert.equal(reconnectionManager.status, RECONNECTION.STATE.IN_PROGRESS);
371
- });
372
-
373
- it('should correctly change status to complete', () => {
374
- reconnectionManager.setStatus(RECONNECTION.STATE.COMPLETE);
375
-
376
- assert.equal(reconnectionManager.status, RECONNECTION.STATE.COMPLETE);
377
- });
378
-
379
- it('should correctly change status to failure', () => {
380
- reconnectionManager.setStatus(RECONNECTION.STATE.FAILURE);
381
-
382
- assert.equal(reconnectionManager.status, RECONNECTION.STATE.FAILURE);
383
- });
384
- });
385
-
386
474
  describe('cleanUp()', () => {
387
475
  it('should call reset and keep reference to meeting object', () => {
388
476
  const resetSpy = sinon.spy(reconnectionManager, 'reset');
@@ -133,20 +133,6 @@ describe('plugin-meetings/roap', () => {
133
133
  locusMediaRequest,
134
134
  });
135
135
 
136
- assert.calledWith(webex.internal.newMetrics.submitClientEvent, {
137
- name: 'client.locus.media.request',
138
- options: {
139
- meetingId: 'meeting-id',
140
- },
141
- });
142
-
143
- assert.calledWith(webex.internal.newMetrics.submitClientEvent, {
144
- name: 'client.locus.media.response',
145
- options: {
146
- meetingId: 'meeting-id',
147
- },
148
- });
149
-
150
136
  const requestParams = locusMediaRequest.send.getCall(0).args[0];
151
137
  assert.deepEqual(requestParams, {
152
138
  type: 'RoapMessage',
@@ -175,29 +161,6 @@ describe('plugin-meetings/roap', () => {
175
161
  },
176
162
  });
177
163
  });
178
-
179
- it('sends correct client event when fails', async () => {
180
- const locusMediaRequest = {send: sinon.stub().rejects({code: 300, message: 'error'})};
181
- try {
182
- await roapRequest.sendRoap({
183
- locusSelfUrl: locusUrl,
184
- mediaId: 'mediaId',
185
- roapMessage: {
186
- seq: 'seq',
187
- },
188
- meetingId: 'meeting-id',
189
- locusMediaRequest,
190
- });
191
- } catch (err) {
192
- assert.calledWith(webex.internal.newMetrics.submitClientEvent, {
193
- name: 'client.locus.media.response',
194
- options: {
195
- meetingId: 'meeting-id',
196
- rawError: {code: 300, message: 'error'},
197
- },
198
- });
199
- }
200
- });
201
164
  });
202
165
 
203
166
  it('calls attachReachabilityData when sendRoap', async () => {