@webex/plugin-meetings 3.8.1-next.2 → 3.8.1-next.20

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 (54) hide show
  1. package/README.md +26 -13
  2. package/dist/breakouts/breakout.js +1 -1
  3. package/dist/breakouts/index.js +1 -1
  4. package/dist/constants.js +2 -1
  5. package/dist/constants.js.map +1 -1
  6. package/dist/interpretation/index.js +1 -1
  7. package/dist/interpretation/siLanguage.js +1 -1
  8. package/dist/locus-info/index.js +35 -16
  9. package/dist/locus-info/index.js.map +1 -1
  10. package/dist/media/index.js +2 -2
  11. package/dist/media/index.js.map +1 -1
  12. package/dist/meeting/brbState.js +12 -12
  13. package/dist/meeting/brbState.js.map +1 -1
  14. package/dist/meeting/index.js +85 -78
  15. package/dist/meeting/index.js.map +1 -1
  16. package/dist/members/index.js +8 -6
  17. package/dist/members/index.js.map +1 -1
  18. package/dist/members/request.js +3 -3
  19. package/dist/members/request.js.map +1 -1
  20. package/dist/members/util.js +18 -6
  21. package/dist/members/util.js.map +1 -1
  22. package/dist/multistream/sendSlotManager.js +32 -2
  23. package/dist/multistream/sendSlotManager.js.map +1 -1
  24. package/dist/reachability/index.js +5 -10
  25. package/dist/reachability/index.js.map +1 -1
  26. package/dist/types/constants.d.ts +1 -0
  27. package/dist/types/meeting/brbState.d.ts +0 -1
  28. package/dist/types/meeting/index.d.ts +12 -3
  29. package/dist/types/members/index.d.ts +8 -3
  30. package/dist/types/members/request.d.ts +1 -1
  31. package/dist/types/members/util.d.ts +5 -2
  32. package/dist/types/multistream/sendSlotManager.d.ts +16 -0
  33. package/dist/types/reachability/index.d.ts +2 -2
  34. package/dist/webinar/index.js +1 -1
  35. package/package.json +24 -24
  36. package/src/constants.ts +1 -0
  37. package/src/locus-info/index.ts +46 -19
  38. package/src/media/index.ts +2 -2
  39. package/src/meeting/brbState.ts +8 -7
  40. package/src/meeting/index.ts +48 -32
  41. package/src/members/index.ts +7 -5
  42. package/src/members/request.ts +2 -2
  43. package/src/members/util.ts +14 -3
  44. package/src/multistream/sendSlotManager.ts +34 -2
  45. package/src/reachability/index.ts +5 -13
  46. package/test/unit/spec/locus-info/index.js +140 -44
  47. package/test/unit/spec/media/index.ts +107 -0
  48. package/test/unit/spec/meeting/brbState.ts +11 -9
  49. package/test/unit/spec/meeting/index.js +131 -39
  50. package/test/unit/spec/members/index.js +32 -9
  51. package/test/unit/spec/members/request.js +2 -2
  52. package/test/unit/spec/members/utils.js +27 -7
  53. package/test/unit/spec/multistream/sendSlotManager.ts +59 -0
  54. package/test/unit/spec/reachability/index.ts +2 -6
@@ -2257,7 +2257,7 @@ describe('plugin-meetings', () => {
2257
2257
 
2258
2258
  it('applyLocusDeltaData gets delta locus on DESYNC action if we have a syncUrl', () => {
2259
2259
  const {DESYNC} = LocusDeltaParser.loci;
2260
- const fakeDeltaLocus = {id: 'fake delta locus'};
2260
+ const fakeDeltaLocus = {baseSequence: {}, id: 'fake delta locus'};
2261
2261
  const meeting = {
2262
2262
  meetingRequest: {
2263
2263
  getLocusDTO: sandbox.stub().resolves({body: fakeDeltaLocus}),
@@ -2392,25 +2392,22 @@ describe('plugin-meetings', () => {
2392
2392
  };
2393
2393
  });
2394
2394
 
2395
- it('applyLocusDeltaData gets full locus on DESYNC action if we do not have a syncUrl and destroys the meeting if that fails', () => {
2395
+ it('applyLocusDeltaData gets full locus on DESYNC action if we do not have a syncUrl and destroys the meeting if that fails', async () => {
2396
2396
  meeting.meetingRequest.getLocusDTO.rejects(new Error('fake error'));
2397
2397
 
2398
2398
  locusInfo.locusParser.workingCopy = {}; // no syncUrl
2399
2399
 
2400
- // Since we have a promise inside a function we want to test that's not returned,
2401
- // we will wait and stub it's last function to resolve this waiting promise.
2402
- return new Promise((resolve) => {
2403
- webex.meetings.destroy.callsFake(() => resolve());
2404
- locusInfo.applyLocusDeltaData(DESYNC, fakeLocus, meeting);
2405
- }).then(() => {
2406
- assert.calledOnceWithExactly(meeting.meetingRequest.getLocusDTO, {url: 'fullSyncUrl'});
2400
+ locusInfo.applyLocusDeltaData(DESYNC, fakeLocus, meeting);
2407
2401
 
2408
- assert.notCalled(meeting.locusInfo.handleLocusDelta);
2409
- assert.notCalled(meeting.locusInfo.onFullLocus);
2410
- assert.notCalled(locusInfo.locusParser.resume);
2402
+ await testUtils.flushPromises();
2411
2403
 
2412
- assert.calledOnceWithExactly(webex.meetings.destroy, meeting, 'LOCUS_DTO_SYNC_FAILED');
2413
- });
2404
+ assert.calledOnceWithExactly(meeting.meetingRequest.getLocusDTO, {url: 'fullSyncUrl'});
2405
+
2406
+ assert.notCalled(meeting.locusInfo.handleLocusDelta);
2407
+ assert.notCalled(meeting.locusInfo.onFullLocus);
2408
+ assert.notCalled(locusInfo.locusParser.resume);
2409
+
2410
+ assert.calledOnceWithExactly(webex.meetings.destroy, meeting, 'LOCUS_DTO_SYNC_FAILED');
2414
2411
  });
2415
2412
 
2416
2413
  it('applyLocusDeltaData first tries a delta sync on DESYNC action and if that fails, does a full locus sync', () => {
@@ -2447,39 +2444,62 @@ describe('plugin-meetings', () => {
2447
2444
  });
2448
2445
  });
2449
2446
 
2450
- it('applyLocusDeltaData destroys the meeting if both delta sync and full sync fail', () => {
2447
+ it('applyLocusDeltaData first tries a delta sync on DESYNC action and if that fails with 403, it does not do a full locus sync', async () => {
2448
+ const fake403Error = new Error('fake error');
2449
+ fake403Error.statusCode = 403;
2450
+
2451
+ meeting.meetingRequest.getLocusDTO.onCall(0).rejects(fake403Error);
2452
+
2453
+ locusInfo.applyLocusDeltaData(DESYNC, fakeLocus, meeting);
2454
+
2455
+ await testUtils.flushPromises();
2456
+
2457
+ assert.calledOnceWithExactly(meeting.meetingRequest.getLocusDTO, {url: 'deltaSyncUrl'});
2458
+
2459
+ assert.calledWith(sendBehavioralMetricStub, 'js_sdk_locus_delta_sync_failed', {
2460
+ correlationId: meeting.correlationId,
2461
+ url: 'deltaSyncUrl',
2462
+ reason: 'fake error',
2463
+ errorName: 'Error',
2464
+ stack: sinon.match.any,
2465
+ code: sinon.match.any,
2466
+ });
2467
+
2468
+ assert.notCalled(meeting.locusInfo.handleLocusDelta);
2469
+ assert.notCalled(meeting.locusInfo.onFullLocus);
2470
+ assert.notCalled(locusInfo.locusParser.resume);
2471
+ });
2472
+
2473
+ it('applyLocusDeltaData destroys the meeting if both delta sync and full sync fail', async () => {
2451
2474
  meeting.meetingRequest.getLocusDTO.rejects(new Error('fake error'));
2452
2475
 
2453
- // Since we have a promise inside a function we want to test that's not returned,
2454
- // we will wait and stub it's last function to resolve this waiting promise.
2455
- return new Promise((resolve) => {
2456
- webex.meetings.destroy.callsFake(() => resolve());
2457
- locusInfo.applyLocusDeltaData(DESYNC, fakeLocus, meeting);
2458
- }).then(() => {
2459
- assert.calledTwice(meeting.meetingRequest.getLocusDTO);
2476
+ locusInfo.applyLocusDeltaData(DESYNC, fakeLocus, meeting);
2460
2477
 
2461
- assert.deepEqual(meeting.meetingRequest.getLocusDTO.getCalls()[0].args, [
2462
- {url: 'deltaSyncUrl'},
2463
- ]);
2464
- assert.deepEqual(meeting.meetingRequest.getLocusDTO.getCalls()[1].args, [
2465
- {url: 'fullSyncUrl'},
2466
- ]);
2478
+ await testUtils.flushPromises();
2467
2479
 
2468
- assert.calledWith(sendBehavioralMetricStub, 'js_sdk_locus_delta_sync_failed', {
2469
- correlationId: meeting.correlationId,
2470
- url: 'deltaSyncUrl',
2471
- reason: 'fake error',
2472
- errorName: 'Error',
2473
- stack: sinon.match.any,
2474
- code: sinon.match.any,
2475
- });
2480
+ assert.calledTwice(meeting.meetingRequest.getLocusDTO);
2476
2481
 
2477
- assert.notCalled(meeting.locusInfo.handleLocusDelta);
2478
- assert.notCalled(meeting.locusInfo.onFullLocus);
2479
- assert.notCalled(locusInfo.locusParser.resume);
2482
+ assert.deepEqual(meeting.meetingRequest.getLocusDTO.getCalls()[0].args, [
2483
+ {url: 'deltaSyncUrl'},
2484
+ ]);
2485
+ assert.deepEqual(meeting.meetingRequest.getLocusDTO.getCalls()[1].args, [
2486
+ {url: 'fullSyncUrl'},
2487
+ ]);
2480
2488
 
2481
- assert.calledOnceWithExactly(webex.meetings.destroy, meeting, 'LOCUS_DTO_SYNC_FAILED');
2489
+ assert.calledWith(sendBehavioralMetricStub, 'js_sdk_locus_delta_sync_failed', {
2490
+ correlationId: meeting.correlationId,
2491
+ url: 'deltaSyncUrl',
2492
+ reason: 'fake error',
2493
+ errorName: 'Error',
2494
+ stack: sinon.match.any,
2495
+ code: sinon.match.any,
2482
2496
  });
2497
+
2498
+ assert.notCalled(meeting.locusInfo.handleLocusDelta);
2499
+ assert.notCalled(meeting.locusInfo.onFullLocus);
2500
+ assert.notCalled(locusInfo.locusParser.resume);
2501
+
2502
+ assert.calledOnceWithExactly(webex.meetings.destroy, meeting, 'LOCUS_DTO_SYNC_FAILED');
2483
2503
  });
2484
2504
  });
2485
2505
 
@@ -2509,9 +2529,7 @@ describe('plugin-meetings', () => {
2509
2529
  });
2510
2530
 
2511
2531
  it('onDeltaLocus merges delta participants with existing participants', () => {
2512
- const FAKE_DELTA_PARTICIPANTS = [
2513
- {id: '1111'}, {id: '2222'}
2514
- ]
2532
+ const FAKE_DELTA_PARTICIPANTS = [{id: '1111'}, {id: '2222'}];
2515
2533
  fakeLocus.participants = FAKE_DELTA_PARTICIPANTS;
2516
2534
 
2517
2535
  sinon.spy(locusInfo, 'mergeParticipants');
@@ -2519,9 +2537,87 @@ describe('plugin-meetings', () => {
2519
2537
  const existingParticipants = locusInfo.participants;
2520
2538
 
2521
2539
  locusInfo.onDeltaLocus(fakeLocus);
2522
- assert.calledOnceWithExactly(locusInfo.mergeParticipants, existingParticipants, FAKE_DELTA_PARTICIPANTS);
2540
+ assert.calledOnceWithExactly(
2541
+ locusInfo.mergeParticipants,
2542
+ existingParticipants,
2543
+ FAKE_DELTA_PARTICIPANTS
2544
+ );
2523
2545
  assert.calledWith(locusInfo.updateParticipants, FAKE_DELTA_PARTICIPANTS, false);
2524
2546
  });
2547
+
2548
+ [true, false].forEach((isDelta) =>
2549
+ it(`applyLocusDeltaData - handles empty ${
2550
+ isDelta ? 'delta' : 'full'
2551
+ } DTO in response`, async () => {
2552
+ const {DESYNC} = LocusDeltaParser.loci;
2553
+ const fakeFullLocusDto = {};
2554
+ const meeting = {
2555
+ meetingRequest: {
2556
+ getLocusDTO: sandbox.stub().resolves({body: fakeFullLocusDto}),
2557
+ },
2558
+ locusInfo: {
2559
+ onFullLocus: sandbox.stub(),
2560
+ handleLocusDelta: sandbox.stub(),
2561
+ },
2562
+ locusUrl: 'fake locus FULL url',
2563
+ };
2564
+
2565
+ sinon.stub(locusInfo.locusParser, 'resume').resolves();
2566
+
2567
+ if (isDelta) {
2568
+ locusInfo.locusParser.workingCopy = {syncUrl: 'fake locus DELTA url'};
2569
+ } else {
2570
+ locusInfo.locusParser.workingCopy = {}; // no syncUrl (to trigger FULL DTO request)
2571
+ }
2572
+
2573
+ await locusInfo.applyLocusDeltaData(DESYNC, fakeLocus, meeting);
2574
+
2575
+ await testUtils.flushPromises();
2576
+
2577
+ if (isDelta) {
2578
+ assert.calledOnceWithExactly(meeting.meetingRequest.getLocusDTO, {
2579
+ url: 'fake locus DELTA url',
2580
+ });
2581
+ } else {
2582
+ assert.calledOnceWithExactly(meeting.meetingRequest.getLocusDTO, {
2583
+ url: 'fake locus FULL url',
2584
+ });
2585
+ }
2586
+ assert.notCalled(meeting.locusInfo.handleLocusDelta);
2587
+ assert.notCalled(meeting.locusInfo.onFullLocus);
2588
+ assert.calledOnce(locusInfo.locusParser.resume);
2589
+ })
2590
+ );
2591
+
2592
+ it(`applyLocusDeltaData - handles the case when we get FULL DTO when we asked for DELTA DTO`, async () => {
2593
+ const {DESYNC} = LocusDeltaParser.loci;
2594
+ const fakeFullLocusDto = {someStuff: 'data'}; // non-empty DTO, without baseSequence
2595
+ const meeting = {
2596
+ meetingRequest: {
2597
+ getLocusDTO: sandbox.stub().resolves({body: fakeFullLocusDto}),
2598
+ },
2599
+ locusInfo: {
2600
+ onFullLocus: sandbox.stub(),
2601
+ handleLocusDelta: sandbox.stub(),
2602
+ },
2603
+ locusUrl: 'fake locus FULL url',
2604
+ };
2605
+
2606
+ sinon.stub(locusInfo.locusParser, 'resume').resolves();
2607
+
2608
+ locusInfo.locusParser.workingCopy = {syncUrl: 'fake locus DELTA url'};
2609
+
2610
+ await locusInfo.applyLocusDeltaData(DESYNC, fakeLocus, meeting);
2611
+
2612
+ await testUtils.flushPromises();
2613
+
2614
+ assert.calledOnceWithExactly(meeting.meetingRequest.getLocusDTO, {
2615
+ url: 'fake locus DELTA url',
2616
+ });
2617
+ assert.notCalled(meeting.locusInfo.handleLocusDelta);
2618
+ assert.calledOnceWithExactly(meeting.locusInfo.onFullLocus, fakeFullLocusDto);
2619
+ assert.calledOnce(locusInfo.locusParser.resume);
2620
+ });
2525
2621
  });
2526
2622
 
2527
2623
  describe('#updateLocusCache', () => {
@@ -137,6 +137,113 @@ describe('createMediaConnection', () => {
137
137
  );
138
138
  });
139
139
 
140
+ it('should set direction to sendonly for both audio and video when only send flags are true', () => {
141
+ const roapMediaConnectionConstructorStub = sinon
142
+ .stub(InternalMediaCoreModule, 'RoapMediaConnection')
143
+ .returns(fakeRoapMediaConnection);
144
+
145
+ StaticConfig.set({bandwidth: {audio: 123, video: 456, startBitrate: 999}});
146
+
147
+ const ENABLE_EXTMAP = false;
148
+ const ENABLE_RTX = true;
149
+
150
+ Media.createMediaConnection(false, 'sendonly-debug-id', 'meetingId', {
151
+ mediaProperties: {
152
+ mediaDirection: {
153
+ sendAudio: true,
154
+ receiveAudio: false,
155
+ sendVideo: true,
156
+ receiveVideo: false,
157
+ sendShare: false,
158
+ receiveShare: false,
159
+ },
160
+ audioStream: fakeAudioStream,
161
+ videoStream: fakeVideoStream,
162
+ shareVideoTrack: null,
163
+ shareAudioTrack: null,
164
+ },
165
+ remoteQualityLevel: 'HIGH',
166
+ enableRtx: ENABLE_RTX,
167
+ enableExtmap: ENABLE_EXTMAP,
168
+ turnServerInfo: undefined,
169
+ iceCandidatesTimeout: undefined,
170
+ });
171
+
172
+ assert.calledWith(
173
+ roapMediaConnectionConstructorStub,
174
+ sinon.match.any,
175
+ {
176
+ localTracks: {
177
+ audio: fakeTrack,
178
+ video: fakeTrack,
179
+ screenShareVideo: undefined,
180
+ screenShareAudio: undefined,
181
+ },
182
+ direction: {
183
+ audio: 'sendonly',
184
+ video: 'sendonly',
185
+ screenShareVideo: 'inactive',
186
+ },
187
+ remoteQualityLevel: 'HIGH',
188
+ },
189
+ 'sendonly-debug-id'
190
+ );
191
+ });
192
+
193
+ it('should set direction to recvonly for both audio and video when only receive flags are true', () => {
194
+ const roapMediaConnectionConstructorStub = sinon
195
+ .stub(InternalMediaCoreModule, 'RoapMediaConnection')
196
+ .returns(fakeRoapMediaConnection);
197
+
198
+ StaticConfig.set({bandwidth: {audio: 123, video: 456, startBitrate: 999}});
199
+
200
+ const ENABLE_EXTMAP = true;
201
+ const ENABLE_RTX = false;
202
+
203
+ Media.createMediaConnection(false, 'recvonly-debug-id', 'meetingId', {
204
+ mediaProperties: {
205
+ mediaDirection: {
206
+ sendAudio: false,
207
+ receiveAudio: true,
208
+ sendVideo: false,
209
+ receiveVideo: true,
210
+ sendShare: false,
211
+ receiveShare: false,
212
+ },
213
+ audioStream: fakeAudioStream,
214
+ videoStream: fakeVideoStream,
215
+ shareVideoTrack: null,
216
+ shareAudioTrack: null,
217
+ },
218
+ remoteQualityLevel: 'HIGH',
219
+ enableRtx: ENABLE_RTX,
220
+ enableExtmap: ENABLE_EXTMAP,
221
+ turnServerInfo: undefined,
222
+ iceCandidatesTimeout: undefined,
223
+ });
224
+
225
+ assert.calledWith(
226
+ roapMediaConnectionConstructorStub,
227
+ sinon.match.any,
228
+ {
229
+ localTracks: {
230
+ audio: fakeTrack,
231
+ video: fakeTrack,
232
+ screenShareVideo: undefined,
233
+ screenShareAudio: undefined,
234
+ },
235
+ direction: {
236
+ audio: 'recvonly',
237
+ video: 'recvonly',
238
+ screenShareVideo: 'inactive',
239
+ },
240
+ remoteQualityLevel: 'HIGH',
241
+ },
242
+ 'recvonly-debug-id'
243
+ );
244
+ });
245
+
246
+
140
247
  it('creates a MultistreamRoapMediaConnection when multistream is enabled', () => {
141
248
  const multistreamRoapMediaConnectionConstructorStub = sinon
142
249
  .stub(InternalMediaCoreModule, 'MultistreamRoapMediaConnection')
@@ -3,12 +3,12 @@ import {assert, expect} from '@webex/test-helper-chai';
3
3
 
4
4
  import testUtils from '../../../utils/testUtils';
5
5
  import {BrbState, createBrbState} from '@webex/plugin-meetings/src/meeting/brbState';
6
- import LoggerProxy from '@webex/plugin-meetings/src/common/logs/logger-proxy';
6
+ import {MediaType} from '@webex/internal-media-core';
7
7
 
8
8
  describe('plugin-meetings', () => {
9
9
  let meeting: any;
10
10
  let brbState: BrbState;
11
- let setBrbStub: sinon.SinonStub;
11
+ let setBrbStub: sinon.SinonStub;
12
12
 
13
13
  beforeEach(async () => {
14
14
  meeting = {
@@ -23,7 +23,7 @@ describe('plugin-meetings', () => {
23
23
  setSourceStateOverride: sinon.stub(),
24
24
  },
25
25
  meetingRequest: {
26
- setBrb: () => {}
26
+ setBrb: () => {},
27
27
  },
28
28
  };
29
29
 
@@ -104,12 +104,12 @@ describe('plugin-meetings', () => {
104
104
  assert.isTrue(meeting.meetingRequest.setBrb.calledOnce);
105
105
  });
106
106
 
107
- it('sets source state override when client state does not match server state', async () => {
107
+ it('updates source state override', async () => {
108
108
  brbState.enable(true, meeting.sendSlotManager);
109
109
  brbState.handleServerBrbUpdate(true);
110
110
  await testUtils.flushPromises();
111
111
 
112
- assert.isTrue(meeting.sendSlotManager.setSourceStateOverride.calledOnce);
112
+ assert.isTrue(meeting.sendSlotManager.setSourceStateOverride.called);
113
113
  });
114
114
 
115
115
  it('handles server update', async () => {
@@ -141,12 +141,14 @@ describe('plugin-meetings', () => {
141
141
  it('should reject when sendLocalBrbStateToServer fails', async () => {
142
142
  const error = new Error('send failed');
143
143
  setBrbStub.rejects(error);
144
-
145
- await expect(
146
- brbState.enable(true, meeting.sendSlotManager)
147
- ).to.be.rejectedWith(error);
144
+
145
+ const enablePromise = brbState.enable(true, meeting.sendSlotManager);
146
+ await expect(enablePromise).to.be.rejectedWith(error);
148
147
 
149
148
  assert.isFalse(brbState.state.syncToServerInProgress);
149
+ assert.isTrue(
150
+ meeting.sendSlotManager.setSourceStateOverride.calledWith(MediaType.VideoMain, 'away')
151
+ );
150
152
  });
151
153
  });
152
154
  });