@webex/plugin-meetings 3.0.0-beta.16 → 3.0.0-beta.18

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 (156) hide show
  1. package/dist/breakouts/breakout.js +116 -0
  2. package/dist/breakouts/breakout.js.map +1 -0
  3. package/dist/breakouts/collection.js +23 -0
  4. package/dist/breakouts/collection.js.map +1 -0
  5. package/dist/breakouts/index.js +226 -0
  6. package/dist/breakouts/index.js.map +1 -0
  7. package/dist/config.js +4 -1
  8. package/dist/config.js.map +1 -1
  9. package/dist/constants.js +43 -6
  10. package/dist/constants.js.map +1 -1
  11. package/dist/locus-info/controlsUtils.js +2 -1
  12. package/dist/locus-info/controlsUtils.js.map +1 -1
  13. package/dist/locus-info/index.js +48 -0
  14. package/dist/locus-info/index.js.map +1 -1
  15. package/dist/locus-info/parser.js +1 -0
  16. package/dist/locus-info/parser.js.map +1 -1
  17. package/dist/locus-info/selfUtils.js +19 -11
  18. package/dist/locus-info/selfUtils.js.map +1 -1
  19. package/dist/media/index.js +3 -3
  20. package/dist/media/index.js.map +1 -1
  21. package/dist/media/properties.js +4 -4
  22. package/dist/media/properties.js.map +1 -1
  23. package/dist/meeting/in-meeting-actions.js +5 -1
  24. package/dist/meeting/in-meeting-actions.js.map +1 -1
  25. package/dist/meeting/index.js +652 -459
  26. package/dist/meeting/index.js.map +1 -1
  27. package/dist/meeting/request.js +25 -44
  28. package/dist/meeting/request.js.map +1 -1
  29. package/dist/meeting/request.type.js.map +1 -1
  30. package/dist/meeting/util.js +22 -57
  31. package/dist/meeting/util.js.map +1 -1
  32. package/dist/meeting-info/meeting-info-v2.js +2 -0
  33. package/dist/meeting-info/meeting-info-v2.js.map +1 -1
  34. package/dist/meetings/index.js +28 -18
  35. package/dist/meetings/index.js.map +1 -1
  36. package/dist/meetings/request.js +14 -12
  37. package/dist/meetings/request.js.map +1 -1
  38. package/dist/member/index.js +9 -0
  39. package/dist/member/index.js.map +1 -1
  40. package/dist/member/util.js +14 -1
  41. package/dist/member/util.js.map +1 -1
  42. package/dist/members/index.js +8 -6
  43. package/dist/members/index.js.map +1 -1
  44. package/dist/members/request.js +3 -1
  45. package/dist/members/request.js.map +1 -1
  46. package/dist/multistream/mediaRequestManager.js +46 -6
  47. package/dist/multistream/mediaRequestManager.js.map +1 -1
  48. package/dist/multistream/multistreamMedia.js +4 -0
  49. package/dist/multistream/multistreamMedia.js.map +1 -1
  50. package/dist/multistream/receiveSlot.js +3 -3
  51. package/dist/multistream/receiveSlot.js.map +1 -1
  52. package/dist/multistream/receiveSlotManager.js +8 -6
  53. package/dist/multistream/receiveSlotManager.js.map +1 -1
  54. package/dist/multistream/remoteMedia.js.map +1 -1
  55. package/dist/multistream/remoteMediaGroup.js.map +1 -1
  56. package/dist/multistream/remoteMediaManager.js +168 -63
  57. package/dist/multistream/remoteMediaManager.js.map +1 -1
  58. package/dist/reachability/index.js +63 -51
  59. package/dist/reachability/index.js.map +1 -1
  60. package/dist/reactions/constants.js +13 -0
  61. package/dist/reactions/constants.js.map +1 -0
  62. package/dist/reactions/reactions.type.js.map +1 -1
  63. package/dist/reconnection-manager/index.js +25 -12
  64. package/dist/reconnection-manager/index.js.map +1 -1
  65. package/dist/recording-controller/enums.js +17 -0
  66. package/dist/recording-controller/enums.js.map +1 -0
  67. package/dist/recording-controller/index.js +343 -0
  68. package/dist/recording-controller/index.js.map +1 -0
  69. package/dist/recording-controller/util.js +63 -0
  70. package/dist/recording-controller/util.js.map +1 -0
  71. package/dist/roap/request.js +88 -68
  72. package/dist/roap/request.js.map +1 -1
  73. package/dist/roap/turnDiscovery.js +72 -47
  74. package/dist/roap/turnDiscovery.js.map +1 -1
  75. package/dist/statsAnalyzer/index.js +3 -3
  76. package/dist/statsAnalyzer/index.js.map +1 -1
  77. package/dist/statsAnalyzer/mqaUtil.js +18 -6
  78. package/dist/statsAnalyzer/mqaUtil.js.map +1 -1
  79. package/package.json +24 -19
  80. package/src/breakouts/README.md +190 -0
  81. package/src/breakouts/breakout.ts +110 -0
  82. package/src/breakouts/collection.ts +19 -0
  83. package/src/breakouts/index.ts +225 -0
  84. package/src/config.ts +4 -1
  85. package/src/constants.ts +39 -1
  86. package/src/locus-info/controlsUtils.ts +2 -0
  87. package/src/locus-info/index.ts +59 -1
  88. package/src/locus-info/parser.ts +1 -0
  89. package/src/locus-info/selfUtils.ts +8 -0
  90. package/src/media/index.ts +1 -2
  91. package/src/media/properties.ts +6 -9
  92. package/src/meeting/in-meeting-actions.ts +8 -0
  93. package/src/meeting/index.ts +360 -111
  94. package/src/meeting/request.ts +9 -31
  95. package/src/meeting/request.type.ts +2 -0
  96. package/src/meeting/util.ts +25 -60
  97. package/src/meeting-info/meeting-info-v2.ts +2 -0
  98. package/src/meetings/index.ts +10 -5
  99. package/src/meetings/request.ts +1 -1
  100. package/src/member/index.ts +9 -0
  101. package/src/member/util.ts +14 -1
  102. package/src/members/index.ts +1 -0
  103. package/src/members/request.ts +1 -0
  104. package/src/multistream/mediaRequestManager.ts +79 -15
  105. package/src/multistream/multistreamMedia.ts +4 -0
  106. package/src/multistream/receiveSlot.ts +17 -12
  107. package/src/multistream/receiveSlotManager.ts +22 -21
  108. package/src/multistream/remoteMedia.ts +1 -1
  109. package/src/multistream/remoteMediaGroup.ts +2 -2
  110. package/src/multistream/remoteMediaManager.ts +150 -37
  111. package/src/reachability/index.ts +16 -13
  112. package/src/reactions/constants.ts +4 -0
  113. package/src/reactions/reactions.type.ts +25 -0
  114. package/src/reconnection-manager/index.ts +18 -9
  115. package/src/recording-controller/enums.ts +8 -0
  116. package/src/recording-controller/index.ts +315 -0
  117. package/src/recording-controller/util.ts +58 -0
  118. package/src/roap/request.ts +78 -73
  119. package/src/roap/turnDiscovery.ts +8 -6
  120. package/src/statsAnalyzer/index.ts +4 -4
  121. package/src/statsAnalyzer/mqaUtil.ts +6 -0
  122. package/test/unit/spec/breakouts/breakout.ts +119 -0
  123. package/test/unit/spec/breakouts/collection.ts +15 -0
  124. package/test/unit/spec/breakouts/index.ts +293 -0
  125. package/test/unit/spec/locus-info/controlsUtils.js +20 -0
  126. package/test/unit/spec/locus-info/index.js +103 -0
  127. package/test/unit/spec/locus-info/selfConstant.js +25 -0
  128. package/test/unit/spec/locus-info/selfUtils.js +84 -0
  129. package/test/unit/spec/media/index.ts +1 -1
  130. package/test/unit/spec/media/properties.ts +9 -9
  131. package/test/unit/spec/meeting/effectsState.js +5 -1
  132. package/test/unit/spec/meeting/in-meeting-actions.ts +5 -1
  133. package/test/unit/spec/meeting/index.js +241 -50
  134. package/test/unit/spec/meeting/request.js +17 -0
  135. package/test/unit/spec/meeting/utils.js +28 -122
  136. package/test/unit/spec/meetings/index.js +1 -0
  137. package/test/unit/spec/member/util.js +26 -1
  138. package/test/unit/spec/multistream/mediaRequestManager.ts +312 -50
  139. package/test/unit/spec/multistream/receiveSlot.ts +6 -6
  140. package/test/unit/spec/multistream/receiveSlotManager.ts +13 -13
  141. package/test/unit/spec/multistream/remoteMedia.ts +2 -2
  142. package/test/unit/spec/multistream/remoteMediaGroup.ts +5 -5
  143. package/test/unit/spec/multistream/remoteMediaManager.ts +354 -65
  144. package/test/unit/spec/reachability/index.ts +58 -24
  145. package/test/unit/spec/reconnection-manager/index.js +42 -13
  146. package/test/unit/spec/recording-controller/index.js +231 -0
  147. package/test/unit/spec/recording-controller/util.js +102 -0
  148. package/test/unit/spec/roap/index.ts +2 -1
  149. package/test/unit/spec/roap/request.ts +114 -0
  150. package/test/unit/spec/roap/turnDiscovery.ts +45 -29
  151. package/test/unit/spec/stats-analyzer/index.js +2 -2
  152. package/test/utils/webex-test-users.js +1 -0
  153. package/tsconfig.json +6 -0
  154. package/dist/media/internal-media-core-wrapper.js +0 -18
  155. package/dist/media/internal-media-core-wrapper.js.map +0 -1
  156. package/src/media/internal-media-core-wrapper.ts +0 -9
@@ -22,7 +22,7 @@ import {
22
22
  LOCUSINFO,
23
23
  PC_BAIL_TIMEOUT,
24
24
  } from '@webex/plugin-meetings/src/constants';
25
- import {MediaConnection as MC} from '@webex/internal-media-core';
25
+ import {ConnectionState, Event, Errors, ErrorType, RemoteTrackType} from '@webex/internal-media-core';
26
26
  import * as StatsAnalyzerModule from '@webex/plugin-meetings/src/statsAnalyzer';
27
27
  import EventsScope from '@webex/plugin-meetings/src/common/events/events-scope';
28
28
  import Meetings, {CONSTANTS} from '@webex/plugin-meetings';
@@ -36,6 +36,7 @@ import MeetingUtil from '@webex/plugin-meetings/src/meeting/util';
36
36
  import Media from '@webex/plugin-meetings/src/media/index';
37
37
  import ReconnectionManager from '@webex/plugin-meetings/src/reconnection-manager';
38
38
  import MediaUtil from '@webex/plugin-meetings/src/media/util';
39
+ import RecordingUtil from '@webex/plugin-meetings/src/recording-controller/util';
39
40
  import LoggerProxy from '@webex/plugin-meetings/src/common/logs/logger-proxy';
40
41
  import LoggerConfig from '@webex/plugin-meetings/src/common/logs/logger-config';
41
42
  import TriggerProxy from '@webex/plugin-meetings/src/common/events/trigger-proxy';
@@ -44,7 +45,12 @@ import Metrics from '@webex/plugin-meetings/src/metrics';
44
45
  import {trigger, eventType} from '@webex/plugin-meetings/src/metrics/config';
45
46
  import BEHAVIORAL_METRICS from '@webex/plugin-meetings/src/metrics/constants';
46
47
  import {IceGatheringFailed} from '@webex/plugin-meetings/src/common/errors/webex-errors';
48
+ import {MediaRequestManager} from '@webex/plugin-meetings/src/multistream/mediaRequestManager';
47
49
 
50
+ import LLM from '@webex/internal-plugin-llm';
51
+ import Mercury from '@webex/internal-plugin-mercury';
52
+ import Breakouts from '@webex/plugin-meetings/src/breakouts';
53
+ import {REACTION_RELAY_TYPES} from '../../../../src/reactions/constants';
48
54
  import locus from '../fixture/locus';
49
55
  import {
50
56
  UserNotJoinedError,
@@ -155,6 +161,8 @@ describe('plugin-meetings', () => {
155
161
  meetings: Meetings,
156
162
  credentials: Credentials,
157
163
  support: Support,
164
+ llm: LLM,
165
+ mercury: Mercury,
158
166
  },
159
167
  config: {
160
168
  credentials: {
@@ -179,6 +187,7 @@ describe('plugin-meetings', () => {
179
187
  webex.credentials.getOrgId = sinon.stub().returns('fake-org-id');
180
188
  webex.internal.metrics.submitClientMetrics = sinon.stub().returns(Promise.resolve());
181
189
  webex.meetings.uploadLogs = sinon.stub().returns(Promise.resolve());
190
+ webex.internal.llm.on = sinon.stub();
182
191
 
183
192
  TriggerProxy.trigger = sinon.stub().returns(true);
184
193
  Metrics.postEvent = sinon.stub();
@@ -250,6 +259,13 @@ describe('plugin-meetings', () => {
250
259
  assert.equal(meeting.meetingInfoFailureReason, undefined);
251
260
  assert.equal(meeting.destination, testDestination);
252
261
  assert.equal(meeting.destinationType, _MEETING_ID_);
262
+ assert.instanceOf(meeting.breakouts, Breakouts);
263
+ });
264
+ it('creates MediaRequestManager instances', () => {
265
+ assert.instanceOf(meeting.mediaRequestManagers.audio, MediaRequestManager);
266
+ assert.instanceOf(meeting.mediaRequestManagers.video, MediaRequestManager);
267
+ assert.instanceOf(meeting.mediaRequestManagers.screenShareAudio, MediaRequestManager);
268
+ assert.instanceOf(meeting.mediaRequestManagers.screenShareVideo, MediaRequestManager);
253
269
  });
254
270
  });
255
271
  describe('#invite', () => {
@@ -779,7 +795,7 @@ describe('plugin-meetings', () => {
779
795
  });
780
796
  });
781
797
 
782
- it('should throw error', async () => {
798
+ it("should throw error if request doesn't work", async () => {
783
799
  meeting.request = sinon.stub().returns(Promise.reject());
784
800
 
785
801
  try {
@@ -799,6 +815,64 @@ describe('plugin-meetings', () => {
799
815
  assert.calledOnce(meeting.transcription.closeSocket);
800
816
  });
801
817
  });
818
+ describe('#isReactionsSupported', () => {
819
+ it('should return false if the feature is not supported for the meeting', () => {
820
+ meeting.locusInfo.controls = {reactions: {enabled: false}};
821
+
822
+ assert.equal(meeting.isReactionsSupported(), false);
823
+ });
824
+ it('should return true if the feature is not supported for the meeting', () => {
825
+ meeting.locusInfo.controls = {reactions: {enabled: true}};
826
+
827
+ assert.equal(meeting.isReactionsSupported(), true);
828
+ });
829
+ });
830
+ describe('#processRelayEvent', () => {
831
+ it('should process a Reaction event type', () => {
832
+ meeting.isReactionsSupported = sinon.stub().returns(true);
833
+ meeting.config.receiveReactions = true;
834
+ const fakeSendersName = 'Fake reactors name';
835
+ meeting.members.membersCollection.get = sinon.stub().returns({name: fakeSendersName});
836
+ const fakeReactionPayload = {
837
+ type: 'fake_type',
838
+ codepoints: 'fake_codepoints',
839
+ shortcodes: 'fake_shortcodes',
840
+ tone: {
841
+ type: 'fake_tone_type',
842
+ codepoints: 'fake_tone_codepoints',
843
+ shortcodes: 'fake_tone_shortcodes',
844
+ },
845
+ };
846
+ const fakeSenderPayload = {
847
+ participantId: 'fake_participant_id',
848
+ };
849
+ const fakeProcessedReaction = {
850
+ reaction: fakeReactionPayload,
851
+ sender: {
852
+ id: fakeSenderPayload.participantId,
853
+ name: fakeSendersName,
854
+ },
855
+ };
856
+ const fakeRelayEvent = {
857
+ data: {
858
+ relayType: REACTION_RELAY_TYPES.REACTION,
859
+ reaction: fakeReactionPayload,
860
+ sender: fakeSenderPayload,
861
+ }
862
+ };
863
+ meeting.processRelayEvent(fakeRelayEvent);
864
+ assert.calledWith(
865
+ TriggerProxy.trigger,
866
+ sinon.match.instanceOf(Meeting),
867
+ {
868
+ file: 'meeting/index',
869
+ function: 'join',
870
+ },
871
+ EVENT_TRIGGERS.MEETING_RECEIVE_REACTIONS,
872
+ fakeProcessedReaction
873
+ );
874
+ })
875
+ })
802
876
  describe('#join', () => {
803
877
  let sandbox = null;
804
878
  const joinMeetingResult = 'JOIN_MEETINGS_OPTION_RESULT';
@@ -844,15 +918,20 @@ describe('plugin-meetings', () => {
844
918
 
845
919
  it('should call updateLLMConnection upon joining if config value is set', async () => {
846
920
  meeting.config.enableAutomaticLLM = true;
921
+ meeting.webex.internal.llm.on = sinon.stub();
922
+ meeting.processRelayEvent = sinon.stub();
847
923
  await meeting.join();
848
924
 
849
925
  assert.calledOnce(meeting.updateLLMConnection);
926
+ assert.calledOnceWithExactly(meeting.webex.internal.llm.on, 'event:relay.event', meeting.processRelayEvent);
850
927
  });
851
928
 
852
929
  it('should not call updateLLMConnection upon joining if config value is not set', async () => {
930
+ meeting.webex.internal.llm.on = sinon.stub();
853
931
  await meeting.join();
854
932
 
855
933
  assert.notCalled(meeting.updateLLMConnection);
934
+ assert.notCalled(meeting.webex.internal.llm.on);
856
935
  });
857
936
 
858
937
  it('should invoke `receiveTranscription()` if receiveTranscription is set to true', async () => {
@@ -952,7 +1031,7 @@ describe('plugin-meetings', () => {
952
1031
  beforeEach(() => {
953
1032
  fakeMediaConnection = {
954
1033
  close: sinon.stub(),
955
- getConnectionState: sinon.stub().returns(MC.ConnectionState.Connected),
1034
+ getConnectionState: sinon.stub().returns(ConnectionState.Connected),
956
1035
  initiateOffer: sinon.stub().resolves({}),
957
1036
  on: sinon.stub(),
958
1037
  };
@@ -1200,7 +1279,7 @@ describe('plugin-meetings', () => {
1200
1279
  meeting.meetingState = 'ACTIVE';
1201
1280
  fakeMediaConnection.getConnectionState = sinon
1202
1281
  .stub()
1203
- .returns(MC.ConnectionState.Connecting);
1282
+ .returns(ConnectionState.Connecting);
1204
1283
  const clock = sinon.useFakeTimers();
1205
1284
  const media = meeting.addMedia({
1206
1285
  mediaSettings: {},
@@ -3531,19 +3610,19 @@ describe('plugin-meetings', () => {
3531
3610
 
3532
3611
  it('should register for all the correct RoapMediaConnection events', () => {
3533
3612
  meeting.setupMediaConnectionListeners();
3534
- assert.isFunction(eventListeners[MC.Event.ROAP_STARTED]);
3535
- assert.isFunction(eventListeners[MC.Event.ROAP_DONE]);
3536
- assert.isFunction(eventListeners[MC.Event.ROAP_FAILURE]);
3537
- assert.isFunction(eventListeners[MC.Event.ROAP_MESSAGE_TO_SEND]);
3538
- assert.isFunction(eventListeners[MC.Event.REMOTE_TRACK_ADDED]);
3539
- assert.isFunction(eventListeners[MC.Event.CONNECTION_STATE_CHANGED]);
3613
+ assert.isFunction(eventListeners[Event.ROAP_STARTED]);
3614
+ assert.isFunction(eventListeners[Event.ROAP_DONE]);
3615
+ assert.isFunction(eventListeners[Event.ROAP_FAILURE]);
3616
+ assert.isFunction(eventListeners[Event.ROAP_MESSAGE_TO_SEND]);
3617
+ assert.isFunction(eventListeners[Event.REMOTE_TRACK_ADDED]);
3618
+ assert.isFunction(eventListeners[Event.CONNECTION_STATE_CHANGED]);
3540
3619
  });
3541
3620
 
3542
3621
  it('should trigger a media:ready event when REMOTE_TRACK_ADDED is fired', () => {
3543
3622
  meeting.setupMediaConnectionListeners();
3544
- eventListeners[MC.Event.REMOTE_TRACK_ADDED]({
3623
+ eventListeners[Event.REMOTE_TRACK_ADDED]({
3545
3624
  track: 'track',
3546
- type: MC.RemoteTrackType.AUDIO,
3625
+ type: RemoteTrackType.AUDIO,
3547
3626
  });
3548
3627
  assert.equal(TriggerProxy.trigger.getCall(1).args[2], 'media:ready');
3549
3628
  assert.deepEqual(TriggerProxy.trigger.getCall(1).args[3], {
@@ -3551,9 +3630,9 @@ describe('plugin-meetings', () => {
3551
3630
  stream: true,
3552
3631
  });
3553
3632
 
3554
- eventListeners[MC.Event.REMOTE_TRACK_ADDED]({
3633
+ eventListeners[Event.REMOTE_TRACK_ADDED]({
3555
3634
  track: 'track',
3556
- type: MC.RemoteTrackType.VIDEO,
3635
+ type: RemoteTrackType.VIDEO,
3557
3636
  });
3558
3637
  assert.equal(TriggerProxy.trigger.getCall(2).args[2], 'media:ready');
3559
3638
  assert.deepEqual(TriggerProxy.trigger.getCall(2).args[3], {
@@ -3561,9 +3640,9 @@ describe('plugin-meetings', () => {
3561
3640
  stream: true,
3562
3641
  });
3563
3642
 
3564
- eventListeners[MC.Event.REMOTE_TRACK_ADDED]({
3643
+ eventListeners[Event.REMOTE_TRACK_ADDED]({
3565
3644
  track: 'track',
3566
- type: MC.RemoteTrackType.SCREENSHARE_VIDEO,
3645
+ type: RemoteTrackType.SCREENSHARE_VIDEO,
3567
3646
  });
3568
3647
  assert.equal(TriggerProxy.trigger.getCall(3).args[2], 'media:ready');
3569
3648
  assert.deepEqual(TriggerProxy.trigger.getCall(3).args[3], {
@@ -3613,51 +3692,51 @@ describe('plugin-meetings', () => {
3613
3692
  };
3614
3693
 
3615
3694
  it('should send metrics for SdpOfferCreationError error', () => {
3616
- const fakeError = new MC.Errors.SdpOfferCreationError(fakeErrorMessage, {
3695
+ const fakeError = new Errors.SdpOfferCreationError(fakeErrorMessage, {
3617
3696
  name: fakeErrorName,
3618
3697
  cause: {name: fakeRootCauseName},
3619
3698
  });
3620
3699
 
3621
- eventListeners[MC.Event.ROAP_FAILURE](fakeError);
3700
+ eventListeners[Event.ROAP_FAILURE](fakeError);
3622
3701
 
3623
3702
  checkMetricSent(eventType.LOCAL_SDP_GENERATED);
3624
3703
  checkBehavioralMetricSent(
3625
3704
  BEHAVIORAL_METRICS.PEERCONNECTION_FAILURE,
3626
- MC.Errors.ErrorCode.SdpOfferCreationError,
3705
+ Errors.ErrorCode.SdpOfferCreationError,
3627
3706
  fakeErrorMessage,
3628
3707
  fakeRootCauseName
3629
3708
  );
3630
3709
  });
3631
3710
 
3632
3711
  it('should send metrics for SdpOfferHandlingError error', () => {
3633
- const fakeError = new MC.Errors.SdpOfferHandlingError(fakeErrorMessage, {
3712
+ const fakeError = new Errors.SdpOfferHandlingError(fakeErrorMessage, {
3634
3713
  name: fakeErrorName,
3635
3714
  cause: {name: fakeRootCauseName},
3636
3715
  });
3637
3716
 
3638
- eventListeners[MC.Event.ROAP_FAILURE](fakeError);
3717
+ eventListeners[Event.ROAP_FAILURE](fakeError);
3639
3718
 
3640
3719
  checkMetricSent(eventType.REMOTE_SDP_RECEIVED);
3641
3720
  checkBehavioralMetricSent(
3642
3721
  BEHAVIORAL_METRICS.PEERCONNECTION_FAILURE,
3643
- MC.Errors.ErrorCode.SdpOfferHandlingError,
3722
+ Errors.ErrorCode.SdpOfferHandlingError,
3644
3723
  fakeErrorMessage,
3645
3724
  fakeRootCauseName
3646
3725
  );
3647
3726
  });
3648
3727
 
3649
3728
  it('should send metrics for SdpAnswerHandlingError error', () => {
3650
- const fakeError = new MC.Errors.SdpAnswerHandlingError(fakeErrorMessage, {
3729
+ const fakeError = new Errors.SdpAnswerHandlingError(fakeErrorMessage, {
3651
3730
  name: fakeErrorName,
3652
3731
  cause: {name: fakeRootCauseName},
3653
3732
  });
3654
3733
 
3655
- eventListeners[MC.Event.ROAP_FAILURE](fakeError);
3734
+ eventListeners[Event.ROAP_FAILURE](fakeError);
3656
3735
 
3657
3736
  checkMetricSent(eventType.REMOTE_SDP_RECEIVED);
3658
3737
  checkBehavioralMetricSent(
3659
3738
  BEHAVIORAL_METRICS.PEERCONNECTION_FAILURE,
3660
- MC.Errors.ErrorCode.SdpAnswerHandlingError,
3739
+ Errors.ErrorCode.SdpAnswerHandlingError,
3661
3740
  fakeErrorMessage,
3662
3741
  fakeRootCauseName
3663
3742
  );
@@ -3665,15 +3744,15 @@ describe('plugin-meetings', () => {
3665
3744
 
3666
3745
  it('should send metrics for SdpError error', () => {
3667
3746
  // SdpError is usually without a cause
3668
- const fakeError = new MC.Errors.SdpError(fakeErrorMessage, {name: fakeErrorName});
3747
+ const fakeError = new Errors.SdpError(fakeErrorMessage, {name: fakeErrorName});
3669
3748
 
3670
- eventListeners[MC.Event.ROAP_FAILURE](fakeError);
3749
+ eventListeners[Event.ROAP_FAILURE](fakeError);
3671
3750
 
3672
3751
  checkMetricSent(eventType.LOCAL_SDP_GENERATED);
3673
3752
  // expectedMetadataType is the error name in this case
3674
3753
  checkBehavioralMetricSent(
3675
3754
  BEHAVIORAL_METRICS.INVALID_ICE_CANDIDATE,
3676
- MC.Errors.ErrorCode.SdpError,
3755
+ Errors.ErrorCode.SdpError,
3677
3756
  fakeErrorMessage,
3678
3757
  fakeErrorName
3679
3758
  );
@@ -3681,24 +3760,24 @@ describe('plugin-meetings', () => {
3681
3760
 
3682
3761
  it('should send metrics for IceGatheringError error', () => {
3683
3762
  // IceGatheringError is usually without a cause
3684
- const fakeError = new MC.Errors.IceGatheringError(fakeErrorMessage, {
3763
+ const fakeError = new Errors.IceGatheringError(fakeErrorMessage, {
3685
3764
  name: fakeErrorName,
3686
3765
  });
3687
3766
 
3688
- eventListeners[MC.Event.ROAP_FAILURE](fakeError);
3767
+ eventListeners[Event.ROAP_FAILURE](fakeError);
3689
3768
 
3690
3769
  checkMetricSent(eventType.LOCAL_SDP_GENERATED);
3691
3770
  // expectedMetadataType is the error name in this case
3692
3771
  checkBehavioralMetricSent(
3693
3772
  BEHAVIORAL_METRICS.INVALID_ICE_CANDIDATE,
3694
- MC.Errors.ErrorCode.IceGatheringError,
3773
+ Errors.ErrorCode.IceGatheringError,
3695
3774
  fakeErrorMessage,
3696
3775
  fakeErrorName
3697
3776
  );
3698
3777
  });
3699
3778
  });
3700
3779
 
3701
- describe('handles MC.Event.ROAP_MESSAGE_TO_SEND correctly', () => {
3780
+ describe('handles Event.ROAP_MESSAGE_TO_SEND correctly', () => {
3702
3781
  let sendRoapOKStub;
3703
3782
  let sendRoapMediaRequestStub;
3704
3783
  let sendRoapAnswerStub;
@@ -3716,7 +3795,7 @@ describe('plugin-meetings', () => {
3716
3795
  });
3717
3796
 
3718
3797
  it('handles OK message correctly', () => {
3719
- eventListeners[MC.Event.ROAP_MESSAGE_TO_SEND]({
3798
+ eventListeners[Event.ROAP_MESSAGE_TO_SEND]({
3720
3799
  roapMessage: {messageType: 'OK', seq: 1},
3721
3800
  });
3722
3801
 
@@ -3735,7 +3814,7 @@ describe('plugin-meetings', () => {
3735
3814
  });
3736
3815
 
3737
3816
  it('handles OFFER message correctly', () => {
3738
- eventListeners[MC.Event.ROAP_MESSAGE_TO_SEND]({
3817
+ eventListeners[Event.ROAP_MESSAGE_TO_SEND]({
3739
3818
  roapMessage: {
3740
3819
  messageType: 'OFFER',
3741
3820
  seq: 1,
@@ -3761,7 +3840,7 @@ describe('plugin-meetings', () => {
3761
3840
  });
3762
3841
 
3763
3842
  it('handles ANSWER message correctly', () => {
3764
- eventListeners[MC.Event.ROAP_MESSAGE_TO_SEND]({
3843
+ eventListeners[Event.ROAP_MESSAGE_TO_SEND]({
3765
3844
  roapMessage: {
3766
3845
  messageType: 'ANSWER',
3767
3846
  seq: 10,
@@ -3788,7 +3867,7 @@ describe('plugin-meetings', () => {
3788
3867
  it('sends metrics if fails to send roap ANSWER message', async () => {
3789
3868
  sendRoapAnswerStub.rejects(new Error('sending answer failed'));
3790
3869
 
3791
- await eventListeners[MC.Event.ROAP_MESSAGE_TO_SEND]({
3870
+ await eventListeners[Event.ROAP_MESSAGE_TO_SEND]({
3792
3871
  roapMessage: {
3793
3872
  messageType: 'ANSWER',
3794
3873
  seq: 10,
@@ -3810,9 +3889,9 @@ describe('plugin-meetings', () => {
3810
3889
  );
3811
3890
  });
3812
3891
 
3813
- [MC.ErrorType.CONFLICT, MC.ErrorType.DOUBLECONFLICT].forEach((errorType) =>
3892
+ [ErrorType.CONFLICT, ErrorType.DOUBLECONFLICT].forEach((errorType) =>
3814
3893
  it(`handles ERROR message indicating glare condition correctly (errorType=${errorType})`, () => {
3815
- eventListeners[MC.Event.ROAP_MESSAGE_TO_SEND]({
3894
+ eventListeners[Event.ROAP_MESSAGE_TO_SEND]({
3816
3895
  roapMessage: {
3817
3896
  messageType: 'ERROR',
3818
3897
  seq: 10,
@@ -3843,11 +3922,11 @@ describe('plugin-meetings', () => {
3843
3922
  );
3844
3923
 
3845
3924
  it('handles ERROR message indicating other errors correctly', () => {
3846
- eventListeners[MC.Event.ROAP_MESSAGE_TO_SEND]({
3925
+ eventListeners[Event.ROAP_MESSAGE_TO_SEND]({
3847
3926
  roapMessage: {
3848
3927
  messageType: 'ERROR',
3849
3928
  seq: 10,
3850
- errorType: MC.ErrorType.FAILED,
3929
+ errorType: ErrorType.FAILED,
3851
3930
  tieBreaker: 12345,
3852
3931
  },
3853
3932
  });
@@ -3857,7 +3936,7 @@ describe('plugin-meetings', () => {
3857
3936
  assert.calledOnce(sendRoapErrorStub);
3858
3937
  assert.calledWith(sendRoapErrorStub, {
3859
3938
  seq: 10,
3860
- errorType: MC.ErrorType.FAILED,
3939
+ errorType: ErrorType.FAILED,
3861
3940
  mediaId: meeting.mediaId,
3862
3941
  correlationId: meeting.correlationId,
3863
3942
  });
@@ -3893,6 +3972,83 @@ describe('plugin-meetings', () => {
3893
3972
  );
3894
3973
  done();
3895
3974
  });
3975
+
3976
+ it('listens to the breakouts changed event', () => {
3977
+ meeting.breakouts.updateBreakoutSessions = sinon.stub();
3978
+
3979
+ const payload = 'payload';
3980
+
3981
+ meeting.locusInfo.emit({function: 'test', file: 'test'}, 'SELF_MEETING_BREAKOUTS_CHANGED', payload);
3982
+
3983
+ assert.calledOnceWithExactly(meeting.breakouts.updateBreakoutSessions, payload);
3984
+ assert.calledWith(
3985
+ TriggerProxy.trigger,
3986
+ meeting,
3987
+ {file: 'meeting/index', function: 'setUpLocusInfoSelfListener'},
3988
+ EVENT_TRIGGERS.MEETING_BREAKOUTS_UPDATE
3989
+ );
3990
+ });
3991
+ });
3992
+
3993
+ describe('#setUpBreakoutsListener', () => {
3994
+ it('listens to the closing event from breakouts and triggers the closing event', () => {
3995
+ TriggerProxy.trigger.reset();
3996
+ meeting.breakouts.trigger('BREAKOUTS_CLOSING');
3997
+
3998
+ assert.calledWith(
3999
+ TriggerProxy.trigger,
4000
+ meeting,
4001
+ {file: 'meeting/index', function: 'setUpBreakoutsListener'},
4002
+ EVENT_TRIGGERS.MEETING_BREAKOUTS_CLOSING
4003
+ );
4004
+ });
4005
+
4006
+ it('listens to the message event from breakouts and triggers the message event', () => {
4007
+ TriggerProxy.trigger.reset();
4008
+
4009
+ const messageEvent = 'message';
4010
+
4011
+ meeting.breakouts.trigger('MESSAGE', messageEvent);
4012
+
4013
+ assert.calledWith(
4014
+ TriggerProxy.trigger,
4015
+ meeting,
4016
+ {file: 'meeting/index', function: 'setUpBreakoutsListener'},
4017
+ EVENT_TRIGGERS.MEETING_BREAKOUTS_MESSAGE,
4018
+ messageEvent
4019
+ );
4020
+ });
4021
+
4022
+ it('listens to the members update event from breakouts and triggers the breakouts update event', () => {
4023
+ TriggerProxy.trigger.reset();
4024
+ meeting.breakouts.trigger('MEMBERS_UPDATE');
4025
+
4026
+ assert.calledWith(
4027
+ TriggerProxy.trigger,
4028
+ meeting,
4029
+ {file: 'meeting/index', function: 'setUpBreakoutsListener'},
4030
+ EVENT_TRIGGERS.MEETING_BREAKOUTS_UPDATE
4031
+ );
4032
+ });
4033
+ });
4034
+
4035
+ describe('#setupLocusControlsListener', () => {
4036
+ it('listens to the locus breakouts update event', () => {
4037
+ const locus = {
4038
+ breakout: 'breakout'
4039
+ };
4040
+
4041
+ meeting.breakouts.updateBreakout = sinon.stub();
4042
+ meeting.locusInfo.emit({function: 'test', file: 'test'}, 'CONTROLS_MEETING_BREAKOUT_UPDATED', locus);
4043
+
4044
+ assert.calledOnceWithExactly(meeting.breakouts.updateBreakout, locus.breakout);
4045
+ assert.calledWith(
4046
+ TriggerProxy.trigger,
4047
+ meeting,
4048
+ {file: 'meeting/index', function: 'setupLocusControlsListener'},
4049
+ EVENT_TRIGGERS.MEETING_BREAKOUTS_UPDATE
4050
+ );
4051
+ });
3896
4052
  });
3897
4053
 
3898
4054
  describe('#setUpLocusUrlListener', () => {
@@ -3900,18 +4056,47 @@ describe('plugin-meetings', () => {
3900
4056
  const newLocusUrl = 'newLocusUrl/12345';
3901
4057
 
3902
4058
  meeting.members = {locusUrlUpdate: sinon.stub().returns(Promise.resolve(test1))};
4059
+ meeting.recordingController = {setLocusUrl: sinon.stub().returns(undefined)};
3903
4060
 
3904
- meeting.locusInfo.emit(
3905
- {function: 'test', file: 'test'},
3906
- 'LOCUS_INFO_UPDATE_URL',
4061
+ meeting.breakouts.locusUrlUpdate = sinon.stub();
4062
+
4063
+ meeting.locusInfo.emit({function: 'test', file: 'test'}, 'LOCUS_INFO_UPDATE_URL', newLocusUrl);
4064
+ assert.calledWith(
4065
+ meeting.members.locusUrlUpdate,
3907
4066
  newLocusUrl
3908
4067
  );
4068
+ assert.calledOnceWithExactly(meeting.breakouts.locusUrlUpdate, newLocusUrl);
3909
4069
  assert.calledWith(meeting.members.locusUrlUpdate, newLocusUrl);
4070
+ assert.calledWith(meeting.recordingController.setLocusUrl, newLocusUrl);
3910
4071
  assert.equal(meeting.locusUrl, newLocusUrl);
3911
4072
  assert(meeting.locusId, '12345');
3912
4073
  done();
3913
4074
  });
3914
4075
  });
4076
+
4077
+ describe('#setUpLocusServicesListener', () => {
4078
+ it('listens to the locus services update event', (done) => {
4079
+ const newLocusServices = {
4080
+ services: {
4081
+ record: {
4082
+ url: 'url',
4083
+ }
4084
+ },
4085
+ };
4086
+
4087
+ meeting.recordingController = {setServiceUrl: sinon.stub().returns(undefined), setSessionId: sinon.stub().returns(undefined)};
4088
+
4089
+ meeting.locusInfo.emit(
4090
+ {function: 'test', file: 'test'},
4091
+ 'LINKS_SERVICES',
4092
+ newLocusServices
4093
+ );
4094
+
4095
+ assert.calledWith(meeting.recordingController.setServiceUrl, newLocusServices.services.record.url);
4096
+ assert.calledOnce(meeting.recordingController.setSessionId);
4097
+ done();
4098
+ });
4099
+ });
3915
4100
  describe('#setUpLocusInfoMediaInactiveListener', () => {
3916
4101
  it('listens to disconnect due to un activity ', (done) => {
3917
4102
  TriggerProxy.trigger.reset();
@@ -4319,7 +4504,7 @@ describe('plugin-meetings', () => {
4319
4504
  let inMeetingActionsSetSpy;
4320
4505
  let canUserLockSpy;
4321
4506
  let canUserUnlockSpy;
4322
- let canUserRecordSpy;
4507
+ let canUserStartSpy;
4323
4508
  let canUserStopSpy;
4324
4509
  let canUserPauseSpy;
4325
4510
  let canUserResumeSpy;
@@ -4329,15 +4514,17 @@ describe('plugin-meetings', () => {
4329
4514
  let canUserLowerSomeoneElsesHandSpy;
4330
4515
  let waitingForOthersToJoinSpy;
4331
4516
  let handleDataChannelUrlChangeSpy;
4517
+ let canEnableReactionsSpy;
4518
+ let canSendReactionsSpy;
4332
4519
 
4333
4520
  beforeEach(() => {
4334
4521
  locusInfoOnSpy = sinon.spy(meeting.locusInfo, 'on');
4335
4522
  canUserLockSpy = sinon.spy(MeetingUtil, 'canUserLock');
4336
4523
  canUserUnlockSpy = sinon.spy(MeetingUtil, 'canUserUnlock');
4337
- canUserRecordSpy = sinon.spy(MeetingUtil, 'canUserRecord');
4338
- canUserStopSpy = sinon.spy(MeetingUtil, 'canUserStop');
4339
- canUserPauseSpy = sinon.spy(MeetingUtil, 'canUserPause');
4340
- canUserResumeSpy = sinon.spy(MeetingUtil, 'canUserResume');
4524
+ canUserStartSpy = sinon.spy(RecordingUtil, 'canUserStart');
4525
+ canUserStopSpy = sinon.spy(RecordingUtil, 'canUserStop');
4526
+ canUserPauseSpy = sinon.spy(RecordingUtil, 'canUserPause');
4527
+ canUserResumeSpy = sinon.spy(RecordingUtil, 'canUserResume');
4341
4528
  inMeetingActionsSetSpy = sinon.spy(meeting.inMeetingActions, 'set');
4342
4529
  canUserRaiseHandSpy = sinon.spy(MeetingUtil, 'canUserRaiseHand');
4343
4530
  canUserLowerAllHandsSpy = sinon.spy(MeetingUtil, 'canUserLowerAllHands');
@@ -4348,6 +4535,8 @@ describe('plugin-meetings', () => {
4348
4535
  canUserLowerSomeoneElsesHandSpy = sinon.spy(MeetingUtil, 'canUserLowerSomeoneElsesHand');
4349
4536
  waitingForOthersToJoinSpy = sinon.spy(MeetingUtil, 'waitingForOthersToJoin');
4350
4537
  handleDataChannelUrlChangeSpy = sinon.spy(meeting, 'handleDataChannelUrlChange');
4538
+ canEnableReactionsSpy = sinon.spy(MeetingUtil, 'canEnableReactions');
4539
+ canSendReactionsSpy = sinon.spy(MeetingUtil, 'canSendReactions');
4351
4540
  });
4352
4541
 
4353
4542
  afterEach(() => {
@@ -4377,7 +4566,7 @@ describe('plugin-meetings', () => {
4377
4566
 
4378
4567
  assert.calledWith(canUserLockSpy, payload.info.userDisplayHints);
4379
4568
  assert.calledWith(canUserUnlockSpy, payload.info.userDisplayHints);
4380
- assert.calledWith(canUserRecordSpy, payload.info.userDisplayHints);
4569
+ assert.calledWith(canUserStartSpy, payload.info.userDisplayHints);
4381
4570
  assert.calledWith(canUserStopSpy, payload.info.userDisplayHints);
4382
4571
  assert.calledWith(canUserPauseSpy, payload.info.userDisplayHints);
4383
4572
  assert.calledWith(canUserResumeSpy, payload.info.userDisplayHints);
@@ -4387,6 +4576,8 @@ describe('plugin-meetings', () => {
4387
4576
  assert.calledWith(canUserLowerSomeoneElsesHandSpy, payload.info.userDisplayHints);
4388
4577
  assert.calledWith(waitingForOthersToJoinSpy, payload.info.userDisplayHints);
4389
4578
  assert.calledWith(handleDataChannelUrlChangeSpy, payload.info.datachannelUrl);
4579
+ assert.calledWith(canEnableReactionsSpy, null, payload.info.userDisplayHints);
4580
+ assert.calledWith(canSendReactionsSpy, null, payload.info.userDisplayHints);
4390
4581
 
4391
4582
  assert.calledWith(
4392
4583
  TriggerProxy.trigger,
@@ -190,6 +190,23 @@ describe('plugin-meetings', () => {
190
190
  assert.equal(requestParams.uri, 'locusUrl/loci/call?alternateRedirect=true');
191
191
  assert.equal(requestParams.body.invitee.address, 'sipUrl');
192
192
  });
193
+
194
+ it('adds deviceCapabilities to request when breakouts are supported', async () => {
195
+ await meetingsRequest.joinMeeting({
196
+ breakoutsSupported: true
197
+ });
198
+ const requestParams = meetingsRequest.request.getCall(0).args[0];
199
+
200
+ assert.deepEqual(requestParams.body.deviceCapabilities, ['BREAKOUTS_SUPPORTED']);
201
+ });
202
+
203
+ it('does not add deviceCapabilities to request when breakouts are not supported', async () => {
204
+ await meetingsRequest.joinMeeting({});
205
+
206
+ const requestParams = meetingsRequest.request.getCall(0).args[0];
207
+
208
+ assert.deepEqual(requestParams.body.deviceCapabilities, undefined);
209
+ });
193
210
  });
194
211
 
195
212
  describe('#pstn', () => {