@webex/plugin-meetings 3.11.0 → 3.12.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 (170) hide show
  1. package/dist/aiEnableRequest/index.js +184 -0
  2. package/dist/aiEnableRequest/index.js.map +1 -0
  3. package/dist/aiEnableRequest/utils.js +36 -0
  4. package/dist/aiEnableRequest/utils.js.map +1 -0
  5. package/dist/annotation/index.js +14 -5
  6. package/dist/annotation/index.js.map +1 -1
  7. package/dist/breakouts/breakout.js +1 -1
  8. package/dist/breakouts/index.js +1 -1
  9. package/dist/config.js +5 -1
  10. package/dist/config.js.map +1 -1
  11. package/dist/constants.js +28 -6
  12. package/dist/constants.js.map +1 -1
  13. package/dist/hashTree/constants.js +3 -1
  14. package/dist/hashTree/constants.js.map +1 -1
  15. package/dist/hashTree/hashTree.js +18 -0
  16. package/dist/hashTree/hashTree.js.map +1 -1
  17. package/dist/hashTree/hashTreeParser.js +709 -380
  18. package/dist/hashTree/hashTreeParser.js.map +1 -1
  19. package/dist/hashTree/types.js +4 -2
  20. package/dist/hashTree/types.js.map +1 -1
  21. package/dist/hashTree/utils.js +10 -0
  22. package/dist/hashTree/utils.js.map +1 -1
  23. package/dist/index.js +11 -2
  24. package/dist/index.js.map +1 -1
  25. package/dist/interceptors/constant.js +12 -0
  26. package/dist/interceptors/constant.js.map +1 -0
  27. package/dist/interceptors/dataChannelAuthToken.js +290 -0
  28. package/dist/interceptors/dataChannelAuthToken.js.map +1 -0
  29. package/dist/interceptors/index.js +7 -0
  30. package/dist/interceptors/index.js.map +1 -1
  31. package/dist/interceptors/utils.js +27 -0
  32. package/dist/interceptors/utils.js.map +1 -0
  33. package/dist/interpretation/index.js +2 -2
  34. package/dist/interpretation/index.js.map +1 -1
  35. package/dist/interpretation/siLanguage.js +1 -1
  36. package/dist/locus-info/controlsUtils.js +5 -3
  37. package/dist/locus-info/controlsUtils.js.map +1 -1
  38. package/dist/locus-info/index.js +217 -79
  39. package/dist/locus-info/index.js.map +1 -1
  40. package/dist/locus-info/selfUtils.js +1 -0
  41. package/dist/locus-info/selfUtils.js.map +1 -1
  42. package/dist/locus-info/types.js.map +1 -1
  43. package/dist/media/MediaConnectionAwaiter.js +57 -1
  44. package/dist/media/MediaConnectionAwaiter.js.map +1 -1
  45. package/dist/media/properties.js +4 -2
  46. package/dist/media/properties.js.map +1 -1
  47. package/dist/meeting/in-meeting-actions.js +7 -1
  48. package/dist/meeting/in-meeting-actions.js.map +1 -1
  49. package/dist/meeting/index.js +1082 -861
  50. package/dist/meeting/index.js.map +1 -1
  51. package/dist/meeting/request.js +50 -0
  52. package/dist/meeting/request.js.map +1 -1
  53. package/dist/meeting/request.type.js.map +1 -1
  54. package/dist/meeting/util.js +133 -3
  55. package/dist/meeting/util.js.map +1 -1
  56. package/dist/meetings/index.js +100 -45
  57. package/dist/meetings/index.js.map +1 -1
  58. package/dist/member/index.js +10 -0
  59. package/dist/member/index.js.map +1 -1
  60. package/dist/member/util.js +10 -0
  61. package/dist/member/util.js.map +1 -1
  62. package/dist/metrics/constants.js +2 -1
  63. package/dist/metrics/constants.js.map +1 -1
  64. package/dist/multistream/mediaRequestManager.js +9 -60
  65. package/dist/multistream/mediaRequestManager.js.map +1 -1
  66. package/dist/multistream/remoteMediaManager.js +11 -0
  67. package/dist/multistream/remoteMediaManager.js.map +1 -1
  68. package/dist/reachability/index.js +18 -10
  69. package/dist/reachability/index.js.map +1 -1
  70. package/dist/reactions/reactions.type.js.map +1 -1
  71. package/dist/reconnection-manager/index.js +0 -1
  72. package/dist/reconnection-manager/index.js.map +1 -1
  73. package/dist/types/aiEnableRequest/index.d.ts +5 -0
  74. package/dist/types/aiEnableRequest/utils.d.ts +2 -0
  75. package/dist/types/config.d.ts +3 -0
  76. package/dist/types/constants.d.ts +23 -1
  77. package/dist/types/hashTree/constants.d.ts +1 -0
  78. package/dist/types/hashTree/hashTree.d.ts +7 -0
  79. package/dist/types/hashTree/hashTreeParser.d.ts +99 -14
  80. package/dist/types/hashTree/types.d.ts +3 -0
  81. package/dist/types/hashTree/utils.d.ts +6 -0
  82. package/dist/types/index.d.ts +1 -0
  83. package/dist/types/interceptors/constant.d.ts +5 -0
  84. package/dist/types/interceptors/dataChannelAuthToken.d.ts +43 -0
  85. package/dist/types/interceptors/index.d.ts +2 -1
  86. package/dist/types/interceptors/utils.d.ts +1 -0
  87. package/dist/types/locus-info/index.d.ts +21 -2
  88. package/dist/types/locus-info/types.d.ts +1 -0
  89. package/dist/types/media/MediaConnectionAwaiter.d.ts +10 -1
  90. package/dist/types/media/properties.d.ts +2 -1
  91. package/dist/types/meeting/in-meeting-actions.d.ts +6 -0
  92. package/dist/types/meeting/index.d.ts +38 -6
  93. package/dist/types/meeting/request.d.ts +16 -1
  94. package/dist/types/meeting/request.type.d.ts +5 -0
  95. package/dist/types/meeting/util.d.ts +31 -0
  96. package/dist/types/meetings/index.d.ts +4 -2
  97. package/dist/types/member/index.d.ts +1 -0
  98. package/dist/types/member/util.d.ts +5 -0
  99. package/dist/types/metrics/constants.d.ts +1 -0
  100. package/dist/types/multistream/mediaRequestManager.d.ts +0 -23
  101. package/dist/types/reactions/reactions.type.d.ts +1 -0
  102. package/dist/types/webinar/utils.d.ts +6 -0
  103. package/dist/webinar/index.js +260 -90
  104. package/dist/webinar/index.js.map +1 -1
  105. package/dist/webinar/utils.js +25 -0
  106. package/dist/webinar/utils.js.map +1 -0
  107. package/package.json +24 -23
  108. package/src/aiEnableRequest/README.md +84 -0
  109. package/src/aiEnableRequest/index.ts +170 -0
  110. package/src/aiEnableRequest/utils.ts +25 -0
  111. package/src/annotation/index.ts +27 -7
  112. package/src/config.ts +3 -0
  113. package/src/constants.ts +29 -1
  114. package/src/hashTree/constants.ts +1 -0
  115. package/src/hashTree/hashTree.ts +17 -0
  116. package/src/hashTree/hashTreeParser.ts +627 -249
  117. package/src/hashTree/types.ts +4 -0
  118. package/src/hashTree/utils.ts +9 -0
  119. package/src/index.ts +8 -1
  120. package/src/interceptors/constant.ts +6 -0
  121. package/src/interceptors/dataChannelAuthToken.ts +170 -0
  122. package/src/interceptors/index.ts +2 -1
  123. package/src/interceptors/utils.ts +16 -0
  124. package/src/interpretation/index.ts +2 -2
  125. package/src/locus-info/controlsUtils.ts +11 -0
  126. package/src/locus-info/index.ts +231 -61
  127. package/src/locus-info/selfUtils.ts +1 -0
  128. package/src/locus-info/types.ts +1 -0
  129. package/src/media/MediaConnectionAwaiter.ts +41 -1
  130. package/src/media/properties.ts +3 -1
  131. package/src/meeting/in-meeting-actions.ts +12 -0
  132. package/src/meeting/index.ts +205 -44
  133. package/src/meeting/request.ts +42 -0
  134. package/src/meeting/request.type.ts +6 -0
  135. package/src/meeting/util.ts +160 -2
  136. package/src/meetings/index.ts +135 -41
  137. package/src/member/index.ts +10 -0
  138. package/src/member/util.ts +12 -0
  139. package/src/metrics/constants.ts +1 -0
  140. package/src/multistream/mediaRequestManager.ts +4 -54
  141. package/src/multistream/remoteMediaManager.ts +13 -0
  142. package/src/reachability/index.ts +9 -0
  143. package/src/reactions/reactions.type.ts +1 -0
  144. package/src/reconnection-manager/index.ts +0 -1
  145. package/src/webinar/index.ts +162 -5
  146. package/src/webinar/utils.ts +16 -0
  147. package/test/unit/spec/aiEnableRequest/index.ts +981 -0
  148. package/test/unit/spec/aiEnableRequest/utils.ts +130 -0
  149. package/test/unit/spec/annotation/index.ts +69 -7
  150. package/test/unit/spec/hashTree/hashTree.ts +66 -0
  151. package/test/unit/spec/hashTree/hashTreeParser.ts +1869 -189
  152. package/test/unit/spec/interceptors/dataChannelAuthToken.ts +210 -0
  153. package/test/unit/spec/interceptors/utils.ts +75 -0
  154. package/test/unit/spec/locus-info/controlsUtils.js +29 -0
  155. package/test/unit/spec/locus-info/index.js +383 -46
  156. package/test/unit/spec/media/MediaConnectionAwaiter.ts +41 -1
  157. package/test/unit/spec/media/properties.ts +12 -3
  158. package/test/unit/spec/meeting/in-meeting-actions.ts +8 -2
  159. package/test/unit/spec/meeting/index.js +716 -115
  160. package/test/unit/spec/meeting/request.js +70 -0
  161. package/test/unit/spec/meeting/utils.js +438 -26
  162. package/test/unit/spec/meetings/index.js +652 -31
  163. package/test/unit/spec/member/index.js +28 -4
  164. package/test/unit/spec/member/util.js +65 -27
  165. package/test/unit/spec/multistream/mediaRequestManager.ts +2 -85
  166. package/test/unit/spec/multistream/remoteMediaManager.ts +30 -0
  167. package/test/unit/spec/reachability/index.ts +23 -0
  168. package/test/unit/spec/reconnection-manager/index.js +4 -8
  169. package/test/unit/spec/webinar/index.ts +348 -36
  170. package/test/unit/spec/webinar/utils.ts +39 -0
@@ -18,6 +18,7 @@ describe('member', () => {
18
18
  assert.exists(member.supportsBreakouts);
19
19
  assert.exists(member.supportLiveAnnotation);
20
20
  assert.exists(member.canReclaimHost);
21
+ assert.exists(member.canApproveAIEnablement);
21
22
  });
22
23
 
23
24
  describe('roles', () => {
@@ -47,17 +48,24 @@ describe('member', () => {
47
48
  it('checks that processParticipant calls canReclaimHost', () => {
48
49
  sinon.spy(MemberUtil, 'canReclaimHost');
49
50
  member.processParticipant(participant);
50
-
51
+
51
52
  assert.calledOnceWithExactly(MemberUtil.canReclaimHost, participant);
52
53
  });
53
54
 
54
55
  it('checks that processParticipant calls isPresenterAssignmentProhibited', () => {
55
56
  sinon.spy(MemberUtil, 'isPresenterAssignmentProhibited');
56
57
  member.processParticipant(participant);
57
-
58
+
58
59
  assert.calledOnceWithExactly(MemberUtil.isPresenterAssignmentProhibited, participant);
59
60
  });
60
- })
61
+
62
+ it('checks that processParticipant calls canApproveAIEnablement', () => {
63
+ sinon.spy(MemberUtil, 'canApproveAIEnablement');
64
+ member.processParticipant(participant);
65
+
66
+ assert.calledOnceWithExactly(MemberUtil.canApproveAIEnablement, participant);
67
+ });
68
+ });
61
69
 
62
70
  describe('#processMember', () => {
63
71
  it('checks that processMember calls isRemovable', () => {
@@ -80,5 +88,21 @@ describe('member', () => {
80
88
 
81
89
  assert.calledOnceWithExactly(MemberUtil.extractMediaStatus, participant);
82
90
  });
83
- })
91
+ });
92
+
93
+ describe('canApproveAIEnablement integration', () => {
94
+ it('sets canApproveAIEnablement to the value returned by MemberUtil.canApproveAIEnablement', () => {
95
+ const testParticipant = {controls: {}, status: {}};
96
+
97
+ sinon.stub(MemberUtil, 'canApproveAIEnablement').returns(true);
98
+ const memberWithTrue = new Member(testParticipant);
99
+ assert.isTrue(memberWithTrue.canApproveAIEnablement);
100
+
101
+ MemberUtil.canApproveAIEnablement.restore();
102
+
103
+ sinon.stub(MemberUtil, 'canApproveAIEnablement').returns(false);
104
+ const memberWithFalse = new Member(testParticipant);
105
+ assert.isFalse(memberWithFalse.canApproveAIEnablement);
106
+ });
107
+ });
84
108
  });
@@ -82,6 +82,46 @@ describe('plugin-meetings', () => {
82
82
  });
83
83
  });
84
84
 
85
+ describe('MemberUtil.canApproveAIEnablement', () => {
86
+ it('returns false when there is no participant', () => {
87
+ assert.isFalse(MemberUtil.canApproveAIEnablement());
88
+ });
89
+
90
+ it('returns false when there is null participant', () => {
91
+ assert.isFalse(MemberUtil.canApproveAIEnablement(null));
92
+ });
93
+
94
+ it('returns true when attendeeRequestAiAssistantNotAllowed is false', () => {
95
+ const participant = {
96
+ attendeeRequestAiAssistantNotAllowed: false,
97
+ };
98
+
99
+ assert.isTrue(MemberUtil.canApproveAIEnablement(participant));
100
+ });
101
+
102
+ it('returns false when attendeeRequestAiAssistantNotAllowed is true', () => {
103
+ const participant = {
104
+ attendeeRequestAiAssistantNotAllowed: true,
105
+ };
106
+
107
+ assert.isFalse(MemberUtil.canApproveAIEnablement(participant));
108
+ });
109
+
110
+ it('returns true when attendeeRequestAiAssistantNotAllowed is undefined', () => {
111
+ const participant = {
112
+ attendeeRequestAiAssistantNotAllowed: undefined,
113
+ };
114
+
115
+ assert.isTrue(MemberUtil.canApproveAIEnablement(participant));
116
+ });
117
+
118
+ it('returns true when attendeeRequestAiAssistantNotAllowed is not present', () => {
119
+ const participant = {};
120
+
121
+ assert.isTrue(MemberUtil.canApproveAIEnablement(participant));
122
+ });
123
+ });
124
+
85
125
  describe('MemberUtil.extractControlRoles', () => {
86
126
  it('happy path extract control roles', () => {
87
127
  const participant = {
@@ -377,7 +417,6 @@ describe('plugin-meetings', () => {
377
417
  assert.isFalse(MemberUtil.isBrb(participant));
378
418
  });
379
419
 
380
-
381
420
  it('returns false when brb is not present', () => {
382
421
  const participant = {
383
422
  controls: {},
@@ -417,29 +456,28 @@ describe('plugin-meetings', () => {
417
456
  });
418
457
  });
419
458
 
420
- describe('MemberUtil.isSupportsSingleUserAutoEndMeeting', () => {
421
- it('throws an error when there is no participant', () => {
422
- assert.throws(() => {
423
- MemberUtil.isSupportsSingleUserAutoEndMeeting();
424
- }, 'Single user auto end meeting support could not be processed, participant is undefined.');
425
- });
459
+ describe('MemberUtil.isSupportsSingleUserAutoEndMeeting', () => {
460
+ it('throws an error when there is no participant', () => {
461
+ assert.throws(() => {
462
+ MemberUtil.isSupportsSingleUserAutoEndMeeting();
463
+ }, 'Single user auto end meeting support could not be processed, participant is undefined.');
464
+ });
426
465
 
427
- it('returns true when single user auto end meeting is supported', () => {
428
- const participant = {
429
- supportsSingleUserAutoEndMeeting: {},
430
- };
431
- assert.isTrue(MemberUtil.isSupportsSingleUserAutoEndMeeting(participant));
432
- });
466
+ it('returns true when single user auto end meeting is supported', () => {
467
+ const participant = {
468
+ supportsSingleUserAutoEndMeeting: {},
469
+ };
470
+ assert.isTrue(MemberUtil.isSupportsSingleUserAutoEndMeeting(participant));
471
+ });
433
472
 
434
- it('returns false when single user auto end meeting is not supported', () => {
435
- const participant = {
436
- doesNotSupportSingleUserAutoEndMeeting: {},
437
- };
473
+ it('returns false when single user auto end meeting is not supported', () => {
474
+ const participant = {
475
+ doesNotSupportSingleUserAutoEndMeeting: {},
476
+ };
438
477
 
439
- assert.isFalse(MemberUtil.isSupportsSingleUserAutoEndMeeting(participant));
478
+ assert.isFalse(MemberUtil.isSupportsSingleUserAutoEndMeeting(participant));
479
+ });
440
480
  });
441
- });
442
-
443
481
 
444
482
  describe('MemberUtil.isLiveAnnotationSupported', () => {
445
483
  it('throws an error when there is no participant', () => {
@@ -585,7 +623,7 @@ describe('MemberUtil.isSupportsSingleUserAutoEndMeeting', () => {
585
623
  describe('MemberUtil.isPresenterAssignmentProhibited', () => {
586
624
  it('returns true when isPresenterAssignmentProhibited is true', () => {
587
625
  const participant = {
588
- presenterAssignmentNotAllowed: true
626
+ presenterAssignmentNotAllowed: true,
589
627
  };
590
628
 
591
629
  assert.isTrue(MemberUtil.isPresenterAssignmentProhibited(participant));
@@ -610,16 +648,16 @@ describe('MemberUtil.isSupportsSingleUserAutoEndMeeting', () => {
610
648
  describe('extractMediaStatus', () => {
611
649
  it('throws an error when there is no participant', () => {
612
650
  assert.throws(() => {
613
- MemberUtil.extractMediaStatus()
651
+ MemberUtil.extractMediaStatus();
614
652
  }, 'Media status could not be extracted, participant is undefined.');
615
653
  });
616
654
 
617
655
  it('returns undefined media status when participant audio/video status is not present', () => {
618
656
  const participant = {
619
- status: {}
657
+ status: {},
620
658
  };
621
659
 
622
- const mediaStatus = MemberUtil.extractMediaStatus(participant)
660
+ const mediaStatus = MemberUtil.extractMediaStatus(participant);
623
661
 
624
662
  assert.deepEqual(mediaStatus, {audio: undefined, video: undefined});
625
663
  });
@@ -628,11 +666,11 @@ describe('extractMediaStatus', () => {
628
666
  const participant = {
629
667
  status: {
630
668
  audioStatus: 'RECVONLY',
631
- videoStatus: 'SENDRECV'
632
- }
669
+ videoStatus: 'SENDRECV',
670
+ },
633
671
  };
634
672
 
635
- const mediaStatus = MemberUtil.extractMediaStatus(participant)
673
+ const mediaStatus = MemberUtil.extractMediaStatus(participant);
636
674
 
637
675
  assert.deepEqual(mediaStatus, {audio: 'RECVONLY', video: 'SENDRECV'});
638
676
  });
@@ -666,8 +666,8 @@ describe('MediaRequestManager', () => {
666
666
  ]);
667
667
  });
668
668
 
669
- it('avoids sending duplicate requests and clears all the requests on reset()', () => {
670
- // send some requests and commit them one by one
669
+ it('clears all the requests on reset()', () => {
670
+ // send some requests and commit them
671
671
  addReceiverSelectedRequest(1500, fakeReceiveSlots[0], MAX_FS_1080p, false);
672
672
  addReceiverSelectedRequest(1501, fakeReceiveSlots[1], MAX_FS_1080p, false);
673
673
  addActiveSpeakerRequest(
@@ -722,95 +722,12 @@ describe('MediaRequestManager', () => {
722
722
  },
723
723
  ]);
724
724
 
725
- // check that when calling commit()
726
- // all requests are not re-sent again (avoid duplicate requests)
727
- mediaRequestManager.commit();
728
-
729
- assert.notCalled(sendMediaRequestsCallback);
730
-
731
- // now reset everything
732
- mediaRequestManager.reset();
733
-
734
- // calling commit now should not cause any requests to be sent out
735
- mediaRequestManager.commit();
736
- checkMediaRequestsSent([]);
737
- });
738
-
739
- it('makes sure to call requests correctly after reset was called and another request was added', () => {
740
- addReceiverSelectedRequest(1500, fakeReceiveSlots[0], MAX_FS_1080p, false);
741
-
742
- assert.notCalled(sendMediaRequestsCallback);
743
-
744
- mediaRequestManager.commit();
745
- checkMediaRequestsSent([
746
- {
747
- policy: 'receiver-selected',
748
- csi: 1500,
749
- receiveSlot: fakeWcmeSlots[0],
750
- maxPayloadBitsPerSecond: MAX_PAYLOADBITSPS_1080p,
751
- maxFs: MAX_FS_1080p,
752
- maxMbps: MAX_MBPS_1080p,
753
- },
754
- ]);
755
-
756
725
  // now reset everything
757
726
  mediaRequestManager.reset();
758
727
 
759
728
  // calling commit now should not cause any requests to be sent out
760
729
  mediaRequestManager.commit();
761
730
  checkMediaRequestsSent([]);
762
-
763
- //add new request
764
- addReceiverSelectedRequest(1501, fakeReceiveSlots[1], MAX_FS_1080p, false);
765
-
766
- // commit
767
- mediaRequestManager.commit();
768
-
769
- // check the new request was sent
770
- checkMediaRequestsSent([
771
- {
772
- policy: 'receiver-selected',
773
- csi: 1501,
774
- receiveSlot: fakeWcmeSlots[1],
775
- maxPayloadBitsPerSecond: MAX_PAYLOADBITSPS_1080p,
776
- maxFs: MAX_FS_1080p,
777
- maxMbps: MAX_MBPS_1080p,
778
- },
779
- ]);
780
- });
781
-
782
- it('can send same media request after previous requests have been cleared', () => {
783
- // add a request and commit
784
- addReceiverSelectedRequest(1500, fakeReceiveSlots[0], MAX_FS_1080p, false);
785
- mediaRequestManager.commit();
786
- checkMediaRequestsSent([
787
- {
788
- policy: 'receiver-selected',
789
- csi: 1500,
790
- maxPayloadBitsPerSecond: MAX_PAYLOADBITSPS_1080p,
791
- receiveSlot: fakeWcmeSlots[0],
792
- maxFs: MAX_FS_1080p,
793
- maxMbps: MAX_MBPS_1080p,
794
- },
795
- ]);
796
-
797
- // clear previous requests
798
- mediaRequestManager.clearPreviousRequests();
799
-
800
- // commit same request
801
- mediaRequestManager.commit();
802
-
803
- // check the request was sent
804
- checkMediaRequestsSent([
805
- {
806
- policy: 'receiver-selected',
807
- csi: 1500,
808
- maxPayloadBitsPerSecond: MAX_PAYLOADBITSPS_1080p,
809
- receiveSlot: fakeWcmeSlots[0],
810
- maxFs: MAX_FS_1080p,
811
- maxMbps: MAX_MBPS_1080p,
812
- },
813
- ]);
814
731
  });
815
732
 
816
733
  it('re-sends media requests after degradation preferences are set', () => {
@@ -965,6 +965,36 @@ describe('RemoteMediaManager', () => {
965
965
  );
966
966
  });
967
967
 
968
+ it('allocates 25 video slots for AllEqual25 layout', async () => {
969
+ const config = cloneDeep(DefaultTestConfiguration);
970
+ config.video.layouts['AllEqual25'] = {
971
+ activeSpeakerVideoPaneGroups: [
972
+ {id: 'main', numPanes: 25, size: 'best', priority: 255},
973
+ ],
974
+ };
975
+ config.video.initialLayoutId = 'AllEqual25';
976
+
977
+ let slotCount = 0;
978
+ fakeReceiveSlotManager.allocateSlot.callsFake((mediaType: MediaType) => {
979
+ if (mediaType === MediaType.VideoMain) {
980
+ slotCount += 1;
981
+ return Promise.resolve(new FakeSlot(mediaType, `fake video ${slotCount}`));
982
+ }
983
+ return Promise.resolve(fakeAudioSlot);
984
+ });
985
+
986
+ remoteMediaManager = new RemoteMediaManager(
987
+ fakeReceiveSlotManager,
988
+ fakeMediaRequestManagers,
989
+ config
990
+ );
991
+
992
+ await remoteMediaManager.start();
993
+
994
+ assert.strictEqual(remoteMediaManager.getLayoutId(), 'AllEqual25');
995
+ assert.strictEqual(remoteMediaManager.slots.video.activeSpeaker.length, 25);
996
+ });
997
+
968
998
  it('releases slots when switching to layout that requires less active speaker slots', async () => {
969
999
  // start with "AllEqual" layout that needs just 9 video slots
970
1000
  const config = cloneDeep(DefaultTestConfiguration);
@@ -1,5 +1,6 @@
1
1
  import {assert} from '@webex/test-helper-chai';
2
2
  import MockWebex from '@webex/test-helper-mock-webex';
3
+ import { CapabilityState, WebCapabilities } from '@webex/web-capabilities';
3
4
  import sinon from 'sinon';
4
5
  import EventEmitter from 'events';
5
6
  import testUtils from '../../../utils/testUtils';
@@ -8,6 +9,7 @@ import {ClusterNode} from '../../../../src/reachability/request';
8
9
  import MeetingUtil from '@webex/plugin-meetings/src/meeting/util';
9
10
  import * as ClusterReachabilityModule from '@webex/plugin-meetings/src/reachability/clusterReachability';
10
11
  import Metrics from '@webex/plugin-meetings/src/metrics';
12
+ import * as InternalMediaCore from '@webex/internal-media-core';
11
13
 
12
14
  import {IP_VERSION} from '@webex/plugin-meetings/src/constants';
13
15
  import { ReachabilityResultsForBackend } from '@webex/plugin-meetings/src/reachability/reachability.types';
@@ -466,11 +468,13 @@ describe('gatherReachability', () => {
466
468
  let clock;
467
469
  let clusterReachabilityCtorStub;
468
470
  let mockClusterReachabilityInstances: Record<string, MockClusterReachability>;
471
+ let supportsRTCPeerConnectionStub;
469
472
 
470
473
  beforeEach(async () => {
471
474
  webex = new MockWebex();
472
475
 
473
476
  sinon.stub(Metrics, 'sendBehavioralMetric');
477
+ supportsRTCPeerConnectionStub = sinon.stub(WebCapabilities, 'supportsRTCPeerConnection').returns(CapabilityState.CAPABLE);
474
478
 
475
479
  await webex.boundedStorage.put(
476
480
  'Reachability',
@@ -545,6 +549,25 @@ describe('gatherReachability', () => {
545
549
  await assert.isRejected(reachability.gatherReachability('test'), 'enableReachabilityChecks is disabled in config');
546
550
  });
547
551
 
552
+ [CapabilityState.NOT_CAPABLE, CapabilityState.UNKNOWN].forEach((capabilityState) =>
553
+ it(`returns empty object if WebRTC API is not available (capabilityState=${capabilityState}`, async () => {
554
+ supportsRTCPeerConnectionStub.returns(capabilityState);
555
+
556
+ const reachability = new Reachability(webex);
557
+
558
+ const result = await reachability.gatherReachability('test');
559
+
560
+ assert.deepEqual(result, {});
561
+
562
+ // Verify that no new reachability result was stored - old results should remain unchanged
563
+ // This check is mainly to ensure that we don't put any "unreachable" results into storage
564
+ const storedResults = await webex.boundedStorage.get('Reachability', 'reachability.result');
565
+ assert.equal(storedResults, JSON.stringify({old: 'results'}));
566
+
567
+ assert.equal(await reachability.isWebexMediaBackendUnreachable(), false);
568
+ })
569
+ );
570
+
548
571
  [
549
572
  // ========================================================================
550
573
  {
@@ -54,8 +54,8 @@ describe('plugin-meetings', () => {
54
54
  webrtcMediaConnection: fakeMediaConnection,
55
55
  },
56
56
  mediaRequestManagers: {
57
- audio: {commit: sinon.stub(), clearPreviousRequests: sinon.stub()},
58
- video: {commit: sinon.stub(), clearPreviousRequests: sinon.stub()},
57
+ audio: {commit: sinon.stub()},
58
+ video: {commit: sinon.stub()},
59
59
  },
60
60
  roap: {
61
61
  doTurnDiscovery: sinon.stub().resolves({
@@ -179,26 +179,22 @@ describe('plugin-meetings', () => {
179
179
  });
180
180
  });
181
181
 
182
- it('does not clear previous requests and re-request media for non-multistream meetings', async () => {
182
+ it('does not re-request media for non-multistream meetings', async () => {
183
183
  fakeMeeting.isMultistream = false;
184
184
  const rm = new ReconnectionManager(fakeMeeting);
185
185
 
186
186
  await rm.reconnect();
187
187
 
188
- assert.notCalled(fakeMeeting.mediaRequestManagers.audio.clearPreviousRequests);
189
- assert.notCalled(fakeMeeting.mediaRequestManagers.video.clearPreviousRequests);
190
188
  assert.notCalled(fakeMeeting.mediaRequestManagers.audio.commit);
191
189
  assert.notCalled(fakeMeeting.mediaRequestManagers.video.commit);
192
190
  });
193
191
 
194
- it('does clear previous requests and re-request media for multistream meetings', async () => {
192
+ it('does re-request media for multistream meetings', async () => {
195
193
  fakeMeeting.isMultistream = true;
196
194
  const rm = new ReconnectionManager(fakeMeeting);
197
195
 
198
196
  await rm.reconnect();
199
197
 
200
- assert.calledOnce(fakeMeeting.mediaRequestManagers.audio.clearPreviousRequests);
201
- assert.calledOnce(fakeMeeting.mediaRequestManagers.video.clearPreviousRequests);
202
198
  assert.calledOnce(fakeMeeting.mediaRequestManagers.audio.commit);
203
199
  assert.calledOnce(fakeMeeting.mediaRequestManagers.video.commit);
204
200
  });