@webex/plugin-meetings 3.0.0-beta.145 → 3.0.0-beta.147

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 (68) hide show
  1. package/dist/annotation/annotation.types.js.map +1 -1
  2. package/dist/annotation/constants.js +6 -5
  3. package/dist/annotation/constants.js.map +1 -1
  4. package/dist/breakouts/breakout.js +1 -1
  5. package/dist/breakouts/index.js +1 -1
  6. package/dist/common/errors/webex-errors.js +3 -2
  7. package/dist/common/errors/webex-errors.js.map +1 -1
  8. package/dist/config.js +1 -7
  9. package/dist/config.js.map +1 -1
  10. package/dist/constants.js +7 -15
  11. package/dist/constants.js.map +1 -1
  12. package/dist/index.js +6 -0
  13. package/dist/index.js.map +1 -1
  14. package/dist/media/index.js +5 -56
  15. package/dist/media/index.js.map +1 -1
  16. package/dist/media/properties.js +15 -93
  17. package/dist/media/properties.js.map +1 -1
  18. package/dist/meeting/index.js +1106 -1876
  19. package/dist/meeting/index.js.map +1 -1
  20. package/dist/meeting/muteState.js +88 -184
  21. package/dist/meeting/muteState.js.map +1 -1
  22. package/dist/meeting/request.js +2 -2
  23. package/dist/meeting/request.js.map +1 -1
  24. package/dist/meeting/util.js +1 -23
  25. package/dist/meeting/util.js.map +1 -1
  26. package/dist/meetings/index.js +1 -2
  27. package/dist/meetings/index.js.map +1 -1
  28. package/dist/reconnection-manager/index.js +153 -134
  29. package/dist/reconnection-manager/index.js.map +1 -1
  30. package/dist/roap/index.js +8 -7
  31. package/dist/roap/index.js.map +1 -1
  32. package/dist/types/annotation/annotation.types.d.ts +9 -1
  33. package/dist/types/annotation/constants.d.ts +5 -5
  34. package/dist/types/common/errors/webex-errors.d.ts +1 -1
  35. package/dist/types/config.d.ts +0 -6
  36. package/dist/types/constants.d.ts +1 -18
  37. package/dist/types/index.d.ts +1 -1
  38. package/dist/types/media/properties.d.ts +16 -38
  39. package/dist/types/meeting/index.d.ts +92 -352
  40. package/dist/types/meeting/muteState.d.ts +36 -38
  41. package/dist/types/meeting/request.d.ts +2 -1
  42. package/dist/types/meeting/util.d.ts +2 -4
  43. package/package.json +19 -19
  44. package/src/annotation/annotation.types.ts +10 -1
  45. package/src/annotation/constants.ts +5 -5
  46. package/src/common/errors/webex-errors.ts +6 -2
  47. package/src/config.ts +0 -6
  48. package/src/constants.ts +1 -14
  49. package/src/index.ts +1 -0
  50. package/src/media/index.ts +10 -53
  51. package/src/media/properties.ts +32 -92
  52. package/src/meeting/index.ts +532 -1564
  53. package/src/meeting/muteState.ts +87 -178
  54. package/src/meeting/request.ts +4 -3
  55. package/src/meeting/util.ts +3 -24
  56. package/src/meetings/index.ts +0 -1
  57. package/src/reconnection-manager/index.ts +4 -9
  58. package/src/roap/index.ts +13 -14
  59. package/test/integration/spec/converged-space-meetings.js +59 -3
  60. package/test/integration/spec/journey.js +330 -256
  61. package/test/integration/spec/space-meeting.js +75 -3
  62. package/test/unit/spec/meeting/index.js +776 -1344
  63. package/test/unit/spec/meeting/muteState.js +238 -394
  64. package/test/unit/spec/meeting/request.js +4 -4
  65. package/test/unit/spec/meeting/utils.js +2 -9
  66. package/test/unit/spec/multistream/receiveSlot.ts +1 -1
  67. package/test/unit/spec/roap/index.ts +2 -2
  68. package/test/utils/integrationTestUtils.js +5 -23
@@ -15,20 +15,21 @@ describe('plugin-meetings', () => {
15
15
 
16
16
  const fakeLocus = {info: 'this is a fake locus'};
17
17
 
18
- const createFakeLocalTrack = (id) => {
18
+ const createFakeLocalTrack = (id, muted) => {
19
19
  return {
20
20
  id,
21
21
  setMuted: sinon.stub(),
22
22
  setServerMuted: sinon.stub(),
23
23
  setUnmuteAllowed: sinon.stub(),
24
- };
24
+ muted,
25
+ };
25
26
  };
26
27
 
27
28
  beforeEach(async () => {
28
29
  meeting = {
29
30
  mediaProperties: {
30
- audioTrack: createFakeLocalTrack('fake audio track'),
31
- videoTrack: createFakeLocalTrack('fake video track'),
31
+ audioTrack: createFakeLocalTrack('fake audio track', false),
32
+ videoTrack: createFakeLocalTrack('fake video track', false),
32
33
  },
33
34
  remoteMuted: false,
34
35
  unmuteAllowed: true,
@@ -48,8 +49,8 @@ describe('plugin-meetings', () => {
48
49
 
49
50
  MeetingUtil.remoteUpdateAudioVideo = sinon.stub().resolves(fakeLocus);
50
51
 
51
- audio = createMuteState(AUDIO, meeting, {sendAudio: true}, true);
52
- video = createMuteState(VIDEO, meeting, {sendVideo: true}, true);
52
+ audio = createMuteState(AUDIO, meeting, true);
53
+ video = createMuteState(VIDEO, meeting, true);
53
54
 
54
55
  await testUtils.flushPromises();
55
56
  });
@@ -59,38 +60,28 @@ describe('plugin-meetings', () => {
59
60
  });
60
61
 
61
62
  describe('mute state library', () => {
62
- it('does not create an audio instance if we are not sending audio', async () => {
63
- assert.isNull(createMuteState(AUDIO, meeting, {sendAudio: false}, true));
64
- assert.isNull(createMuteState(AUDIO, meeting, {}, true));
65
- });
66
-
67
- it('does not create a video instance if we are not sending video', async () => {
68
- assert.isNull(createMuteState(VIDEO, meeting, {sendVideo: false}));
69
- assert.isNull(createMuteState(VIDEO, meeting, {}));
70
- });
71
-
72
63
  it('takes into account current remote mute status when instantiated', async () => {
73
64
  // simulate being already remote muted
74
65
  meeting.remoteMuted = true;
75
66
 
76
67
  // create a new MuteState instance
77
- audio = createMuteState(AUDIO, meeting, {sendAudio: true}, true);
68
+ audio = createMuteState(AUDIO, meeting, true);
78
69
 
79
70
  await testUtils.flushPromises();
80
71
 
81
72
  assert.isTrue(audio.isMuted());
82
- assert.isFalse(audio.isSelf());
73
+ assert.isTrue(audio.isRemotelyMuted());
83
74
 
84
75
  // now check the opposite case
85
76
  meeting.remoteMuted = false;
86
77
 
87
78
  // create a new MuteState instance
88
- audio = createMuteState(AUDIO, meeting, {sendAudio: true}, true);
79
+ audio = createMuteState(AUDIO, meeting, true);
89
80
 
90
81
  await testUtils.flushPromises();
91
82
 
92
- assert.isFalse(audio.isMuted());
93
- assert.isFalse(audio.isSelf());
83
+ assert.isTrue(audio.isMuted()); // because we start with no track
84
+ assert.isFalse(audio.isRemotelyMuted());
94
85
  });
95
86
 
96
87
  it('initialises correctly for video', async () => {
@@ -99,31 +90,31 @@ describe('plugin-meetings', () => {
99
90
  meeting.unmuteVideoAllowed = false;
100
91
 
101
92
  // create a new video MuteState instance
102
- video = createMuteState(VIDEO, meeting, {sendVideo: true}, true);
93
+ video = createMuteState(VIDEO, meeting, true);
103
94
 
104
95
  await testUtils.flushPromises();
105
96
 
106
- assert.isFalse(video.isMuted());
97
+ assert.isTrue(video.isMuted()); // because we start with no track
98
+ assert.isFalse(video.isRemotelyMuted());
107
99
  assert.isFalse(video.state.server.remoteMute);
108
100
  assert.isFalse(video.state.server.unmuteAllowed);
109
101
  });
110
102
 
111
103
  it('takes remote mute into account when reporting current state', async () => {
112
- assert.isFalse(audio.isMuted());
104
+ assert.isFalse(audio.isRemotelyMuted());
113
105
 
114
106
  // simulate remote mute
115
107
  audio.handleServerRemoteMuteUpdate(meeting, true, true);
116
108
 
117
- assert.isTrue(audio.isMuted());
118
- assert.isFalse(audio.isSelf());
109
+ assert.isTrue(audio.isRemotelyMuted());
119
110
  });
120
111
 
121
112
  it('does local unmute if localAudioUnmuteRequired is received', async () => {
122
- // first we need to mute
123
- await audio.handleClientRequest(meeting, true);
113
+ // first we need to mute have the local track muted
114
+ meeting.mediaProperties.audioTrack.muted = true;
115
+ audio.handleLocalTrackChange(meeting);
124
116
 
125
117
  assert.isTrue(audio.isMuted());
126
- assert.isTrue(audio.isSelf());
127
118
 
128
119
  MeetingUtil.remoteUpdateAudioVideo.resetHistory();
129
120
 
@@ -132,69 +123,21 @@ describe('plugin-meetings', () => {
132
123
  await testUtils.flushPromises();
133
124
 
134
125
  // check that local track was unmuted
135
- assert.calledWith(meeting.mediaProperties.audioTrack.setMuted, false);
126
+ assert.calledWith(meeting.mediaProperties.audioTrack.setServerMuted, false, 'localUnmuteRequired');
136
127
 
137
128
  // and local unmute was sent to server
138
129
  assert.calledOnce(MeetingUtil.remoteUpdateAudioVideo);
139
130
  assert.calledWith(MeetingUtil.remoteUpdateAudioVideo, meeting, false, undefined);
140
131
 
141
132
  assert.isFalse(audio.isMuted());
142
- assert.isFalse(audio.isSelf());
143
- });
144
-
145
- it('rejects client request in progress if localAudioUnmuteRequired is received', async () => {
146
- let clientPromiseResolved = false;
147
- let clientPromiseRejected = false;
148
-
149
- // first we need to mute and make that request last forever
150
- let serverResponseResolve;
151
-
152
- MeetingUtil.remoteUpdateAudioVideo = sinon.stub().returns(
153
- new Promise((resolve) => {
154
- serverResponseResolve = resolve;
155
- })
156
- );
157
-
158
- audio
159
- .handleClientRequest(meeting, true)
160
- .then(() => {
161
- clientPromiseResolved = true;
162
- })
163
- .catch(() => {
164
- clientPromiseRejected = true;
165
- });
166
-
167
- MeetingUtil.remoteUpdateAudioVideo.resetHistory();
168
-
169
- // now simulate server requiring us to locally unmute
170
- audio.handleServerLocalUnmuteRequired(meeting);
171
- await testUtils.flushPromises();
172
-
173
- // the original client request should have been rejected by now
174
- assert.isTrue(clientPromiseRejected);
175
- assert.isFalse(clientPromiseResolved);
176
-
177
- // now make the server respond to the original mute request
178
- serverResponseResolve();
179
- await testUtils.flushPromises();
180
-
181
- // local unmute should be sent to server
182
- assert.calledOnce(MeetingUtil.remoteUpdateAudioVideo);
183
- assert.calledWith(MeetingUtil.remoteUpdateAudioVideo, meeting, false, undefined);
184
-
185
- // and local track should be unmuted
186
- assert.calledWith(meeting.mediaProperties.audioTrack.setMuted, false);
187
-
188
- assert.isFalse(audio.isMuted());
189
- assert.isFalse(audio.isSelf());
190
133
  });
191
134
 
192
135
  it('does local video unmute if localVideoUnmuteRequired is received', async () => {
193
136
  // first we need to mute
194
- await video.handleClientRequest(meeting, true);
137
+ meeting.mediaProperties.videoTrack.muted = true;
138
+ video.handleLocalTrackChange(meeting);
195
139
 
196
140
  assert.isTrue(video.isMuted());
197
- assert.isTrue(video.isSelf());
198
141
 
199
142
  MeetingUtil.remoteUpdateAudioVideo.resetHistory();
200
143
 
@@ -203,22 +146,23 @@ describe('plugin-meetings', () => {
203
146
  await testUtils.flushPromises();
204
147
 
205
148
  // check that local track was unmuted
206
- assert.calledWith(meeting.mediaProperties.videoTrack.setMuted, false);
149
+ assert.calledWith(meeting.mediaProperties.videoTrack.setServerMuted, false, 'localUnmuteRequired');
207
150
 
208
151
  // and local unmute was sent to server
209
152
  assert.calledOnce(MeetingUtil.remoteUpdateAudioVideo);
210
153
  assert.calledWith(MeetingUtil.remoteUpdateAudioVideo, meeting, undefined, false);
211
154
 
212
155
  assert.isFalse(video.isMuted());
213
- assert.isFalse(video.isSelf());
214
156
  });
215
157
 
216
158
  describe('#isLocallyMuted()', () => {
217
159
  it('does not consider remote mute status for audio', async () => {
218
160
  // simulate being already remote muted
219
161
  meeting.remoteMuted = true;
162
+
220
163
  // create a new MuteState instance
221
- audio = createMuteState(AUDIO, meeting, {sendAudio: true}, true);
164
+ audio = createMuteState(AUDIO, meeting, true);
165
+ audio.handleLocalTrackChange(meeting);
222
166
 
223
167
  await testUtils.flushPromises();
224
168
 
@@ -228,8 +172,10 @@ describe('plugin-meetings', () => {
228
172
  it('does not consider remote mute status for video', async () => {
229
173
  // simulate being already remote muted
230
174
  meeting.remoteVideoMuted = true;
175
+
231
176
  // create a new MuteState instance
232
- video = createMuteState(VIDEO, meeting, {sendVideo: true}, true);
177
+ video = createMuteState(VIDEO, meeting, true);
178
+ video.handleLocalTrackChange(meeting);
233
179
 
234
180
  await testUtils.flushPromises();
235
181
 
@@ -237,55 +183,37 @@ describe('plugin-meetings', () => {
237
183
  });
238
184
  });
239
185
 
240
- describe('#handleClientRequest', () => {
241
- it('disables/enables the local audio track when audio is muted/unmuted', async () => {
242
- // mute
243
- audio.handleClientRequest(meeting, true);
244
- assert.calledWith(meeting.mediaProperties.audioTrack.setMuted, true);
186
+ describe('handling local track mute events', () => {
245
187
 
246
- // even when calling mute when it's already muted should still call setMuted
247
- audio.handleClientRequest(meeting, true);
248
- assert.calledWith(meeting.mediaProperties.audioTrack.setMuted, true);
249
-
250
- // unmute
251
- audio.handleClientRequest(meeting, false);
252
- assert.calledWith(meeting.mediaProperties.audioTrack.setMuted, false);
188
+ beforeEach(async () => {
189
+ audio.handleLocalTrackChange(meeting);
190
+ video.handleLocalTrackChange(meeting);
253
191
 
254
- // even when calling unmute when it's already unmuted should still call setMuted
255
- audio.handleClientRequest(meeting, false);
256
- assert.calledWith(meeting.mediaProperties.audioTrack.setMuted, false);
192
+ await testUtils.flushPromises();
257
193
  });
258
194
 
259
- it('disables/enables the local video track when video is muted/unmuted', async () => {
260
- // mute
261
- video.handleClientRequest(meeting, true);
262
- assert.calledWith(meeting.mediaProperties.videoTrack.setMuted, true);
195
+ const simulateAudioMuteChange = async (muteValue) => {
196
+ meeting.mediaProperties.audioTrack.muted = muteValue;
197
+ audio.handleLocalTrackMuteStateChange(meeting, muteValue);
263
198
 
264
- // even when calling mute when it's already muted should still call setMuted
265
- video.handleClientRequest(meeting, false);
266
- assert.calledWith(meeting.mediaProperties.videoTrack.setMuted, true);
199
+ await testUtils.flushPromises();
200
+ }
267
201
 
268
- // unmute
269
- video.handleClientRequest(meeting, false);
270
- assert.calledWith(meeting.mediaProperties.videoTrack.setMuted, false);
202
+ const simulateVideoMuteChange = async (muteValue) => {
203
+ meeting.mediaProperties.videoTrack.muted = muteValue;
204
+ video.handleLocalTrackMuteStateChange(meeting, muteValue);
271
205
 
272
- // even when calling unmute when it's already unmuted should still call setMuted
273
- video.handleClientRequest(meeting, false);
274
- assert.calledWith(meeting.mediaProperties.videoTrack.setMuted, false);
275
- });
206
+ await testUtils.flushPromises();
207
+ }
276
208
 
277
- it('returns correct value in isMuted()/isSelf() methods after client mute/unmute requests', async () => {
209
+ it('returns correct value in isMuted() methods after local track is muted/unmuted', async () => {
278
210
  // mute
279
- audio.handleClientRequest(meeting, true);
280
-
211
+ await simulateAudioMuteChange(true);
281
212
  assert.isTrue(audio.isMuted());
282
- assert.isTrue(audio.isSelf());
283
213
 
284
214
  // unmute
285
- audio.handleClientRequest(meeting, false);
286
-
215
+ await simulateAudioMuteChange(false);
287
216
  assert.isFalse(audio.isMuted());
288
- assert.isFalse(audio.isSelf());
289
217
  });
290
218
 
291
219
  it('does remote unmute when unmuting and remote mute is on', async () => {
@@ -293,14 +221,13 @@ describe('plugin-meetings', () => {
293
221
  audio.handleServerRemoteMuteUpdate(meeting, true, true);
294
222
 
295
223
  // unmute
296
- await audio.handleClientRequest(meeting, false);
224
+ await simulateAudioMuteChange(false);
297
225
 
298
226
  // check that remote unmute was sent to server
299
227
  assert.calledOnce(meeting.members.muteMember);
300
228
  assert.calledWith(meeting.members.muteMember, meeting.members.selfId, false, true);
301
229
 
302
230
  assert.isFalse(audio.isMuted());
303
- assert.isFalse(audio.isSelf());
304
231
  });
305
232
 
306
233
  it('does video remote unmute when unmuting and remote mute is on', async () => {
@@ -308,14 +235,13 @@ describe('plugin-meetings', () => {
308
235
  video.handleServerRemoteMuteUpdate(meeting, true, true);
309
236
 
310
237
  // unmute
311
- await video.handleClientRequest(meeting, false);
238
+ await simulateVideoMuteChange(false);
312
239
 
313
240
  // check that remote unmute was sent to server
314
241
  assert.calledOnce(meeting.members.muteMember);
315
242
  assert.calledWith(meeting.members.muteMember, meeting.members.selfId, false, false);
316
243
 
317
244
  assert.isFalse(video.isMuted());
318
- assert.isFalse(video.isSelf());
319
245
  });
320
246
 
321
247
  it('does not video remote unmute when unmuting and remote mute is off', async () => {
@@ -323,63 +249,35 @@ describe('plugin-meetings', () => {
323
249
  video.handleServerRemoteMuteUpdate(meeting, false, true);
324
250
 
325
251
  // unmute
326
- await video.handleClientRequest(meeting, false);
252
+ await simulateVideoMuteChange(false);
327
253
 
328
- // check that remote unmute was sent to server
254
+ // check that remote unmute was not sent to server
329
255
  assert.notCalled(meeting.members.muteMember);
330
256
 
331
257
  assert.isFalse(video.isMuted());
332
- assert.isFalse(video.isSelf());
333
258
  });
334
259
 
335
- it('resolves client request promise once the server is updated', async () => {
336
- let clientPromiseResolved = false;
260
+ it('calls setServerMuted with "clientRequestFailed" when server request for local mute fails', async () => {
261
+ MeetingUtil.remoteUpdateAudioVideo = sinon.stub().rejects(new Error('fake error'));
337
262
 
338
- let serverResponseResolve;
263
+ await simulateAudioMuteChange(true);
339
264
 
340
- MeetingUtil.remoteUpdateAudioVideo = sinon.stub().returns(
341
- new Promise((resolve) => {
342
- serverResponseResolve = resolve;
343
- })
344
- );
345
-
346
- audio.handleClientRequest(meeting, true).then(() => {
347
- clientPromiseResolved = true;
348
- });
349
-
350
- // do a small delay to make sure that the client promise doesn't resolve in that time
351
- await testUtils.waitUntil(200);
352
- assert.isFalse(clientPromiseResolved);
353
-
354
- meeting.locusInfo.onDeltaLocus.resetHistory();
355
-
356
- // now allow the server response to arrive, this should trigger the client promise to get resolved
357
- serverResponseResolve(fakeLocus);
358
- await testUtils.flushPromises();
359
-
360
- assert.isTrue(clientPromiseResolved);
361
- assert.calledOnceWithExactly(meeting.locusInfo.onDeltaLocus, fakeLocus);
265
+ assert.calledOnceWithExactly(meeting.mediaProperties.audioTrack.setServerMuted , false, 'clientRequestFailed');
362
266
  });
363
267
 
364
- it('rejects client request promise if server request for local mute fails', async () => {
365
- MeetingUtil.remoteUpdateAudioVideo = sinon.stub().returns(
366
- new Promise((resolve, reject) => {
367
- reject();
368
- })
369
- );
370
-
371
- assert.isRejected(audio.handleClientRequest(meeting, true));
372
- });
373
-
374
- it('rejects client request promise if server request for remote mute fails', async () => {
268
+ it('calls setServerMuted with "clientRequestFailed" if server request for remote mute fails', async () => {
375
269
  // we only send remote mute requests when we're unmuting, so first we need to do a remote mute
376
270
  audio.handleServerRemoteMuteUpdate(meeting, true, true);
377
271
 
272
+ await testUtils.flushPromises();
273
+
378
274
  // setup the stub to simulate server error response
379
275
  meeting.members.muteMember = sinon.stub().rejects();
276
+ meeting.mediaProperties.audioTrack.setServerMuted.resetHistory();
277
+
278
+ await simulateAudioMuteChange(false);
380
279
 
381
- // try to unmute - it should fail
382
- await assert.isRejected(audio.handleClientRequest(meeting, false));
280
+ assert.calledOnceWithExactly(meeting.mediaProperties.audioTrack.setServerMuted , true, 'clientRequestFailed');
383
281
 
384
282
  // even though remote mute update in the server failed, isMuted() should still return true,
385
283
  // because of local mute
@@ -395,12 +293,13 @@ describe('plugin-meetings', () => {
395
293
  })
396
294
  );
397
295
 
398
- // simulate many client requests, with the last one matching the initial one
399
- audio.handleClientRequest(meeting, true);
400
- audio.handleClientRequest(meeting, false);
401
- audio.handleClientRequest(meeting, true);
402
- audio.handleClientRequest(meeting, false);
403
- audio.handleClientRequest(meeting, true);
296
+ // the track is initially unmuted
297
+ // simulate many mute changes with the last one matching the first one
298
+ await simulateAudioMuteChange(true);
299
+ await simulateAudioMuteChange(false);
300
+ await simulateAudioMuteChange(true);
301
+ await simulateAudioMuteChange(false);
302
+ await simulateAudioMuteChange(true);
404
303
 
405
304
  // so far there should have been only 1 request to server (because our stub hasn't resolved yet
406
305
  // and MuteState sends only 1 server request at a time)
@@ -415,7 +314,7 @@ describe('plugin-meetings', () => {
415
314
  assert.notCalled(MeetingUtil.remoteUpdateAudioVideo);
416
315
  });
417
316
 
418
- it('queues up server requests when multiple client requests are received', async () => {
317
+ it('queues up server requests when multiple mute changes happen to local track', async () => {
419
318
  let serverResponseResolve;
420
319
 
421
320
  MeetingUtil.remoteUpdateAudioVideo = sinon.stub().returns(
@@ -424,18 +323,9 @@ describe('plugin-meetings', () => {
424
323
  })
425
324
  );
426
325
 
427
- let firstClientPromiseResolved = false;
428
- let secondClientPromiseResolved = false;
429
-
430
326
  // 2 client requests, one after another without waiting for first one to resolve
431
- audio.handleClientRequest(meeting, true).then(() => {
432
- firstClientPromiseResolved = true;
433
- });
434
- audio.handleClientRequest(meeting, false).then(() => {
435
- secondClientPromiseResolved = true;
436
- });
437
-
438
- await testUtils.flushPromises();
327
+ await simulateAudioMuteChange(true);
328
+ await simulateAudioMuteChange(false);
439
329
 
440
330
  assert.calledOnce(MeetingUtil.remoteUpdateAudioVideo);
441
331
  assert.calledWith(MeetingUtil.remoteUpdateAudioVideo, meeting, true, undefined);
@@ -443,55 +333,32 @@ describe('plugin-meetings', () => {
443
333
  // now allow the first request to complete
444
334
  serverResponseResolve();
445
335
  await testUtils.flushPromises();
446
- assert.isTrue(firstClientPromiseResolved);
447
336
 
448
337
  // that should trigger the second server request to be sent
449
338
  assert.calledTwice(MeetingUtil.remoteUpdateAudioVideo);
450
339
  assert.deepEqual([meeting, false, undefined], MeetingUtil.remoteUpdateAudioVideo.getCall(1).args);
451
340
 
452
341
  serverResponseResolve();
453
- await testUtils.flushPromises();
454
-
455
- assert.isTrue(secondClientPromiseResolved);
456
- });
457
-
458
- it('rejects client request to unmute if hard mute is used', (done) => {
459
- audio.handleServerRemoteMuteUpdate(meeting, true, false);
460
-
461
- audio
462
- .handleClientRequest(meeting, false)
463
- .then(() => {
464
- done(new Error('expected handleClientRequest to fail, but it did not!'));
465
- })
466
- .catch((e) => {
467
- assert.isTrue(e instanceof PermissionError);
468
- done();
469
- });
470
342
  });
471
343
 
472
344
  it('does not send remote mute for video', async () => {
473
345
  // mute
474
- await video.handleClientRequest(meeting, true);
346
+ await simulateVideoMuteChange(true);
475
347
 
476
348
  assert.isTrue(video.isMuted());
477
- assert.isTrue(video.isSelf());
478
349
 
479
350
  // check local mute is done, but not remote one
480
- assert.calledWith(meeting.mediaProperties.videoTrack.setMuted, true);
481
351
  assert.calledWith(MeetingUtil.remoteUpdateAudioVideo, meeting, undefined, true);
482
352
  assert.notCalled(meeting.members.muteMember);
483
353
 
484
- meeting.mediaProperties.videoTrack.setMuted.resetHistory();
485
354
  MeetingUtil.remoteUpdateAudioVideo.resetHistory();
486
355
  meeting.members.muteMember.resetHistory();
487
356
 
488
357
  // unmute
489
- await video.handleClientRequest(meeting, false);
358
+ await simulateVideoMuteChange(false);
490
359
 
491
360
  assert.isFalse(video.isMuted());
492
- assert.isFalse(video.isSelf());
493
361
 
494
- assert.calledWith(meeting.mediaProperties.videoTrack.setMuted, false);
495
362
  assert.calledWith(MeetingUtil.remoteUpdateAudioVideo, meeting, undefined, false);
496
363
  assert.notCalled(meeting.members.muteMember);
497
364
  });
@@ -502,243 +369,220 @@ describe('plugin-meetings', () => {
502
369
  meeting.video = video;
503
370
 
504
371
  // mute audio -> the call to remoteUpdateAudioVideo should have video undefined
505
- await audio.handleClientRequest(meeting, true);
372
+ await simulateAudioMuteChange(true);
506
373
  assert.calledWith(MeetingUtil.remoteUpdateAudioVideo, meeting, true, undefined);
507
374
  MeetingUtil.remoteUpdateAudioVideo.resetHistory();
508
375
 
509
376
  // now mute video -> the call to remoteUpdateAudioVideo should have mute for video and undefined for audio
510
- await video.handleClientRequest(meeting, true);
377
+ await simulateVideoMuteChange(true);
511
378
  assert.calledWith(MeetingUtil.remoteUpdateAudioVideo, meeting, undefined, true);
512
379
  MeetingUtil.remoteUpdateAudioVideo.resetHistory();
513
380
 
514
381
  // now unmute the audio -> the call to remoteUpdateAudioVideo should have video undefined
515
- await audio.handleClientRequest(meeting, false);
382
+ await simulateAudioMuteChange(false);
516
383
  assert.calledWith(MeetingUtil.remoteUpdateAudioVideo, meeting, false, undefined);
517
384
  MeetingUtil.remoteUpdateAudioVideo.resetHistory();
518
385
 
519
386
  // unmute video -> the call to remoteUpdateAudioVideo should have both audio undefined
520
- await video.handleClientRequest(meeting, false);
387
+ await simulateVideoMuteChange(false);
521
388
  assert.calledWith(MeetingUtil.remoteUpdateAudioVideo, meeting, undefined, false);
522
389
  });
523
390
  });
524
- });
525
- });
526
-
527
- describe('#init, #handleLocalTrackChange', () => {
528
- let meeting;
529
- let muteState;
530
- let setServerMutedSpy;
531
- let setMutedSpy, setUnmuteAllowedSpy;
532
- const fakeLocus = {info: 'this is a fake locus'};
533
-
534
- const createFakeLocalTrack = (id, muted) => {
535
- return {
536
- id,
537
- setMuted: sinon.stub(),
538
- setServerMuted: sinon.stub(),
539
- setUnmuteAllowed: sinon.stub(),
540
- muted,
541
- };
542
- };
543
-
544
- const setupMeeting = (mediaType, remoteMuted = false, muted = false, defineTracks = true) => {
545
- const remoteMuteField = mediaType === AUDIO ? 'remoteMuted' : 'remoteVideoMuted';
546
-
547
- meeting = {
548
- mediaProperties: {
549
- audioTrack: defineTracks ? createFakeLocalTrack('fake audio track', muted) : undefined,
550
- videoTrack: defineTracks ? createFakeLocalTrack('fake video track', muted) : undefined,
551
- },
552
- [remoteMuteField]: remoteMuted,
553
- unmuteAllowed: true,
554
- unmuteVideoAllowed: true,
555
-
556
- locusInfo: {
557
- onFullLocus: sinon.stub(),
558
- },
559
- members: {
560
- selfId: 'fake self id',
561
- muteMember: sinon.stub().resolves(),
562
- },
563
- };
564
- };
565
-
566
- const setup = async (mediaType, remoteMuted = false, muted = false, defineTracks = true) => {
567
-
568
- setupMeeting(mediaType, remoteMuted, muted, defineTracks);
569
-
570
- const direction = mediaType === AUDIO ? {sendAudio: true} : {sendVideo: true};
571
391
 
572
- muteState = createMuteState(mediaType, meeting, direction, false);
392
+ describe('#init, #handleLocalTrackChange', () => {
393
+ let meeting;
394
+ let muteState;
395
+ let setServerMutedSpy;
396
+ let setMutedSpy, setUnmuteAllowedSpy;
397
+
398
+ const setupMeeting = (mediaType, remoteMuted = false, muted = false, defineTracks = true) => {
399
+ const remoteMuteField = mediaType === AUDIO ? 'remoteMuted' : 'remoteVideoMuted';
400
+
401
+ meeting = {
402
+ mediaProperties: {
403
+ audioTrack: defineTracks ? createFakeLocalTrack('fake audio track', muted) : undefined,
404
+ videoTrack: defineTracks ? createFakeLocalTrack('fake video track', muted) : undefined,
405
+ },
406
+ [remoteMuteField]: remoteMuted,
407
+ unmuteAllowed: true,
408
+ unmuteVideoAllowed: true,
409
+
410
+ locusInfo: {
411
+ onFullLocus: sinon.stub(),
412
+ },
413
+ members: {
414
+ selfId: 'fake self id',
415
+ muteMember: sinon.stub().resolves(),
416
+ },
417
+ };
418
+ };
419
+
420
+ const setup = async (mediaType, remoteMuted = false, muted = false, defineTracks = true) => {
421
+
422
+ setupMeeting(mediaType, remoteMuted, muted, defineTracks);
423
+
424
+ muteState = createMuteState(mediaType, meeting, true);
425
+ muteState.handleLocalTrackChange(meeting);
573
426
 
574
- await testUtils.flushPromises();
575
-
576
- MeetingUtil.remoteUpdateAudioVideo.resetHistory();
577
- }
578
-
579
- const setupSpies = (mediaType) => {
580
- setUnmuteAllowedSpy = mediaType === AUDIO ? meeting.mediaProperties.audioTrack?.setUnmuteAllowed : meeting.mediaProperties.videoTrack?.setUnmuteAllowed;
581
- setServerMutedSpy = mediaType === AUDIO ? meeting.mediaProperties.audioTrack?.setServerMuted : meeting.mediaProperties.videoTrack?.setServerMuted;
582
- setMutedSpy = mediaType === AUDIO ? meeting.mediaProperties.audioTrack?.setMuted : meeting.mediaProperties.videoTrack?.setMuted;
583
-
584
- clearSpies();
585
- };
586
-
587
- const clearSpies = () => {
588
- setUnmuteAllowedSpy?.resetHistory();
589
- setServerMutedSpy?.resetHistory();
590
- setMutedSpy?.resetHistory();
591
- };
592
- const tests = [
593
- {mediaType: AUDIO, title: 'audio'},
594
- {mediaType: VIDEO, title: 'video'}
595
- ];
596
-
597
- tests.forEach(({mediaType, title}) =>
598
- describe(title, () => {
599
- let originalRemoteUpdateAudioVideo;
600
-
601
- beforeEach(() => {
602
- originalRemoteUpdateAudioVideo = MeetingUtil.remoteUpdateAudioVideo;
603
- MeetingUtil.remoteUpdateAudioVideo = sinon.stub().resolves({info: 'fake locus'});
604
- });
427
+ await testUtils.flushPromises();
605
428
 
606
- afterEach(() => {
607
- MeetingUtil.remoteUpdateAudioVideo = originalRemoteUpdateAudioVideo;
608
- sinon.restore();
609
- });
429
+ MeetingUtil.remoteUpdateAudioVideo.resetHistory();
430
+ }
431
+
432
+ const setupSpies = (mediaType) => {
433
+ setUnmuteAllowedSpy = mediaType === AUDIO ? meeting.mediaProperties.audioTrack?.setUnmuteAllowed : meeting.mediaProperties.videoTrack?.setUnmuteAllowed;
434
+ setServerMutedSpy = mediaType === AUDIO ? meeting.mediaProperties.audioTrack?.setServerMuted : meeting.mediaProperties.videoTrack?.setServerMuted;
435
+ setMutedSpy = mediaType === AUDIO ? meeting.mediaProperties.audioTrack?.setMuted : meeting.mediaProperties.videoTrack?.setMuted;
436
+
437
+ clearSpies();
438
+ };
439
+
440
+ const clearSpies = () => {
441
+ setUnmuteAllowedSpy?.resetHistory();
442
+ setServerMutedSpy?.resetHistory();
443
+ setMutedSpy?.resetHistory();
444
+ };
445
+ const tests = [
446
+ {mediaType: AUDIO, title: 'audio'},
447
+ {mediaType: VIDEO, title: 'video'}
448
+ ];
449
+
450
+ tests.forEach(({mediaType, title}) =>
451
+ describe(title, () => {
452
+ let originalRemoteUpdateAudioVideo;
453
+
454
+ beforeEach(() => {
455
+ originalRemoteUpdateAudioVideo = MeetingUtil.remoteUpdateAudioVideo;
456
+ MeetingUtil.remoteUpdateAudioVideo = sinon.stub().resolves({info: 'fake locus'});
457
+ });
610
458
 
611
- describe('#handleLocalTrackChange',() => {
459
+ afterEach(() => {
460
+ MeetingUtil.remoteUpdateAudioVideo = originalRemoteUpdateAudioVideo;
461
+ sinon.restore();
462
+ });
612
463
 
613
- it('calls init()', async () => {
614
- await setup(mediaType);
615
- const spy = sinon.spy(muteState, 'init');
616
- muteState.handleLocalTrackChange(meeting);
617
- assert.calledOnceWithExactly(spy, meeting);
618
- });
619
- });
464
+ describe('#handleLocalTrackChange',() => {
620
465
 
621
- describe('#init', () => {
466
+ it('calls init()', async () => {
467
+ await setup(mediaType);
468
+ const spy = sinon.spy(muteState, 'init');
469
+ muteState.handleLocalTrackChange(meeting);
470
+ assert.calledOnceWithExactly(spy, meeting);
471
+ });
472
+ });
622
473
 
623
- // does the setup by calling new MuteState() so that MuteState.init() doesn't get called
624
- const setupWithoutInit = async (mediaType, remoteMuted = false, muted = false, defineTracks = true) => {
474
+ describe('#init', () => {
625
475
 
626
- setupMeeting(mediaType, remoteMuted, muted, defineTracks);
476
+ // does the setup by calling new MuteState() so that MuteState.init() doesn't get called
477
+ const setupWithoutInit = async (mediaType, remoteMuted = false, muted = false, defineTracks = true) => {
627
478
 
628
- muteState = new MuteState(mediaType, meeting, false);
629
- }
479
+ setupMeeting(mediaType, remoteMuted, muted, defineTracks);
630
480
 
631
- it('nothing goes bad when track is undefined', async () => {
632
- await setupWithoutInit(mediaType, false, false, false);
633
- setupSpies(mediaType);
481
+ muteState = new MuteState(mediaType, meeting, true);
482
+ }
634
483
 
635
- muteState.init(meeting);
484
+ it('nothing goes bad when track is undefined', async () => {
485
+ await setupWithoutInit(mediaType, false, false, false);
486
+ setupSpies(mediaType);
636
487
 
637
- assert.isFalse(muteState.state.client.localMute);
638
- });
488
+ muteState.init(meeting);
639
489
 
640
- it('tests when track muted is true and remoteMuted is false', async () => {
641
- await setupWithoutInit(mediaType, false, true);
642
- setupSpies(mediaType);
490
+ assert.isTrue(muteState.state.client.localMute);
491
+ });
643
492
 
644
- muteState.init(meeting);
493
+ it('tests when track muted is true and remoteMuted is false', async () => {
494
+ await setupWithoutInit(mediaType, false, true);
495
+ setupSpies(mediaType);
645
496
 
646
- assert.calledWith(setUnmuteAllowedSpy, muteState.state.server.unmuteAllowed);
647
- assert.notCalled(setServerMutedSpy);
648
- assert.notCalled(MeetingUtil.remoteUpdateAudioVideo);
649
- assert.isTrue(muteState.state.client.localMute);
650
- });
497
+ muteState.init(meeting);
651
498
 
499
+ assert.calledWith(setUnmuteAllowedSpy, muteState.state.server.unmuteAllowed);
500
+ assert.notCalled(setServerMutedSpy);
501
+ assert.notCalled(MeetingUtil.remoteUpdateAudioVideo);
502
+ assert.isTrue(muteState.state.client.localMute);
503
+ });
652
504
 
653
- it('tests when track muted is false and remoteMuted is false', async () => {
654
- await setupWithoutInit(mediaType, false, false);
655
- setupSpies(mediaType);
656
- muteState.state.server.localMute = true;
657
505
 
658
- muteState.init(meeting);
506
+ it('tests when track muted is false and remoteMuted is false', async () => {
507
+ await setupWithoutInit(mediaType, false, false);
508
+ setupSpies(mediaType);
509
+ muteState.state.server.localMute = true;
659
510
 
660
- assert.calledWith(setUnmuteAllowedSpy, muteState.state.server.unmuteAllowed);
661
- assert.notCalled(setServerMutedSpy);
662
- assert.calledOnce(MeetingUtil.remoteUpdateAudioVideo);
663
- assert.isFalse(muteState.state.client.localMute);
664
- });
511
+ muteState.init(meeting);
665
512
 
666
- it('tests when remoteMuted is true', async () => {
667
- // testing that muteLocalTrack is called
668
- await setupWithoutInit(mediaType, true);
669
- setupSpies(mediaType);
513
+ assert.calledWith(setUnmuteAllowedSpy, muteState.state.server.unmuteAllowed);
514
+ assert.notCalled(setServerMutedSpy);
515
+ assert.calledOnce(MeetingUtil.remoteUpdateAudioVideo);
516
+ assert.isFalse(muteState.state.client.localMute);
517
+ });
670
518
 
671
- muteState.init(meeting);
519
+ it('tests when remoteMuted is true', async () => {
520
+ // testing that muteLocalTrack is called
521
+ await setupWithoutInit(mediaType, true);
522
+ setupSpies(mediaType);
672
523
 
673
- assert.calledWith(setUnmuteAllowedSpy, muteState.state.server.unmuteAllowed);
674
- assert.calledOnceWithExactly(setServerMutedSpy, true, 'remotelyMuted');
675
- });
676
- });
524
+ muteState.init(meeting);
677
525
 
678
- describe('#handleLocalTrackMuteStateChange', () => {
526
+ assert.calledWith(setUnmuteAllowedSpy, muteState.state.server.unmuteAllowed);
527
+ assert.calledOnceWithExactly(setServerMutedSpy, true, 'remotelyMuted');
528
+ });
529
+ });
679
530
 
680
- it('checks when ignoreMuteStateChange is true nothing changes', async () => {
681
- await setup(mediaType, false, false);
682
- muteState.ignoreMuteStateChange = true;
531
+ describe('#handleLocalTrackMuteStateChange', () => {
683
532
 
684
- muteState.handleLocalTrackMuteStateChange(meeting, true);
685
- assert.notCalled(MeetingUtil.remoteUpdateAudioVideo);
533
+ it('checks when ignoreMuteStateChange is true nothing changes', async () => {
534
+ await setup(mediaType, false, false);
535
+ muteState.ignoreMuteStateChange = true;
686
536
 
687
- assert.isFalse(muteState.state.client.localMute);
688
- });
537
+ muteState.handleLocalTrackMuteStateChange(meeting, true);
538
+ assert.notCalled(MeetingUtil.remoteUpdateAudioVideo);
689
539
 
690
- it('tests localMute - true to false', async () => {
691
- await setup(mediaType, false, true);
540
+ assert.isFalse(muteState.state.client.localMute);
541
+ });
692
542
 
693
- muteState.handleLocalTrackMuteStateChange(meeting, false);
694
- assert.equal(muteState.state.client.localMute, false);
695
- assert.called(MeetingUtil.remoteUpdateAudioVideo);
696
- });
543
+ it('tests localMute - true to false', async () => {
544
+ await setup(mediaType, false, true);
697
545
 
698
- it('tests localMute - false to true', async () => {
699
- await setup(mediaType, false, false);
546
+ muteState.handleLocalTrackMuteStateChange(meeting, false);
547
+ assert.equal(muteState.state.client.localMute, false);
548
+ assert.called(MeetingUtil.remoteUpdateAudioVideo);
549
+ });
700
550
 
701
- muteState.handleLocalTrackMuteStateChange(meeting, true);
702
- assert.equal(muteState.state.client.localMute, true);
703
- assert.called(MeetingUtil.remoteUpdateAudioVideo);
704
- });
705
- });
551
+ it('tests localMute - false to true', async () => {
552
+ await setup(mediaType, false, false);
706
553
 
707
- describe('#applyClientStateLocally', () => {
554
+ muteState.handleLocalTrackMuteStateChange(meeting, true);
555
+ assert.equal(muteState.state.client.localMute, true);
556
+ assert.called(MeetingUtil.remoteUpdateAudioVideo);
557
+ });
558
+ });
708
559
 
709
- afterEach(() => {
710
- sinon.restore();
711
- });
560
+ describe('#applyClientStateLocally', () => {
712
561
 
713
- it('checks when sdkOwnsLocalTrack is false', async () => {
714
- await setup(mediaType);
715
- setupSpies(mediaType);
716
- muteState.sdkOwnsLocalTrack= false;
562
+ afterEach(() => {
563
+ sinon.restore();
564
+ });
717
565
 
718
- muteState.applyClientStateLocally(meeting, 'somereason');
719
- assert.calledOnceWithExactly(setServerMutedSpy, muteState.state.client.localMute, 'somereason');
720
- assert.notCalled(setMutedSpy);
721
- });
566
+ it('calls setServerMuted on the track', async () => {
567
+ await setup(mediaType);
568
+ setupSpies(mediaType);
722
569
 
723
- it('checks when sdkOwnsLocalTrack is true', async () => {
724
- await setup(mediaType);
725
- setupSpies(mediaType);
726
- muteState.sdkOwnsLocalTrack= true;
570
+ muteState.applyClientStateLocally(meeting, 'somereason');
571
+ assert.calledOnceWithExactly(setServerMutedSpy, muteState.state.client.localMute, 'somereason');
572
+ assert.notCalled(setMutedSpy);
573
+ });
727
574
 
728
- muteState.applyClientStateLocally(meeting, 'somereason');
729
- assert.notCalled(setServerMutedSpy);
730
- assert.calledOnceWithExactly(setMutedSpy, muteState.state.client.localMute);
731
- });
575
+ it('nothing explodes when tracks are undefined', async () => {
576
+ await setup(mediaType, false, false, false);
577
+ setupSpies(mediaType);
732
578
 
733
- it('checks nothing explodes when tracks are undefined', async () => {
734
- await setup(mediaType, false, false, false);
735
- setupSpies(mediaType);
736
- muteState.sdkOwnsLocalTrack= true;
579
+ muteState.applyClientStateLocally(meeting, 'somereason');
580
+ });
581
+ });
737
582
 
738
- muteState.applyClientStateLocally(meeting, 'somereason');
739
- });
740
- });
583
+ })
584
+ );
585
+ });
741
586
 
742
- })
743
- );
744
- });
587
+ });
588
+ });