@webex/plugin-meetings 3.0.0-beta.201 → 3.0.0-beta.203

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 (33) hide show
  1. package/dist/breakouts/breakout.js +1 -1
  2. package/dist/breakouts/index.js +1 -1
  3. package/dist/constants.js +10 -4
  4. package/dist/constants.js.map +1 -1
  5. package/dist/interpretation/index.js +1 -1
  6. package/dist/interpretation/siLanguage.js +1 -1
  7. package/dist/locus-info/index.js +5 -8
  8. package/dist/locus-info/index.js.map +1 -1
  9. package/dist/locus-info/infoUtils.js +7 -1
  10. package/dist/locus-info/infoUtils.js.map +1 -1
  11. package/dist/meeting/in-meeting-actions.js +3 -1
  12. package/dist/meeting/in-meeting-actions.js.map +1 -1
  13. package/dist/meeting/index.js +502 -453
  14. package/dist/meeting/index.js.map +1 -1
  15. package/dist/recording-controller/index.js +0 -1
  16. package/dist/recording-controller/index.js.map +1 -1
  17. package/dist/types/constants.d.ts +3 -0
  18. package/dist/types/locus-info/index.d.ts +1 -1
  19. package/dist/types/meeting/in-meeting-actions.d.ts +2 -0
  20. package/dist/types/meeting/index.d.ts +14 -0
  21. package/dist/types/recording-controller/index.d.ts +0 -1
  22. package/package.json +19 -19
  23. package/src/constants.ts +7 -0
  24. package/src/locus-info/index.ts +9 -10
  25. package/src/locus-info/infoUtils.ts +10 -2
  26. package/src/meeting/in-meeting-actions.ts +4 -0
  27. package/src/meeting/index.ts +268 -231
  28. package/src/recording-controller/index.ts +0 -1
  29. package/test/unit/spec/locus-info/index.js +80 -3
  30. package/test/unit/spec/locus-info/infoUtils.js +37 -15
  31. package/test/unit/spec/meeting/in-meeting-actions.ts +2 -0
  32. package/test/unit/spec/meeting/index.js +284 -148
  33. package/test/unit/spec/meetings/index.js +2 -2
@@ -330,19 +330,21 @@ describe('plugin-meetings', () => {
330
330
  });
331
331
 
332
332
  it('uses meeting id as correlation id if not provided in constructor', () => {
333
- const newMeeting = new Meeting({
334
- userId: uuid1,
335
- resource: uuid2,
336
- deviceUrl: uuid3,
337
- locus: {url: url1},
338
- destination: testDestination,
339
- destinationType: _MEETING_ID_,
340
- },
341
- {
342
- parent: webex,
343
- });
333
+ const newMeeting = new Meeting(
334
+ {
335
+ userId: uuid1,
336
+ resource: uuid2,
337
+ deviceUrl: uuid3,
338
+ locus: {url: url1},
339
+ destination: testDestination,
340
+ destinationType: _MEETING_ID_,
341
+ },
342
+ {
343
+ parent: webex,
344
+ }
345
+ );
344
346
  assert.equal(newMeeting.correlationId, newMeeting.id);
345
- })
347
+ });
346
348
 
347
349
  describe('creates ReceiveSlot manager instance', () => {
348
350
  let mockReceiveSlotManagerCtor;
@@ -3073,6 +3075,7 @@ describe('plugin-meetings', () => {
3073
3075
  meeting.destinationType = FAKE_TYPE;
3074
3076
  meeting.config.installedOrgID = FAKE_INSTALLED_ORG_ID;
3075
3077
  meeting.parseMeetingInfo = sinon.stub().returns(undefined);
3078
+ meeting.updateMeetingActions = sinon.stub().returns(undefined);
3076
3079
 
3077
3080
  await meeting.fetchMeetingInfo({
3078
3081
  password: FAKE_PASSWORD,
@@ -3104,13 +3107,14 @@ describe('plugin-meetings', () => {
3104
3107
  assert.equal(meeting.passwordStatus, PASSWORD_STATUS.NOT_REQUIRED);
3105
3108
  assert.equal(meeting.meetingInfoFailureReason, MEETING_INFO_FAILURE_REASON.NONE);
3106
3109
  assert.equal(meeting.requiredCaptcha, null);
3107
- assert.calledTwice(TriggerProxy.trigger);
3110
+ assert.calledThrice(TriggerProxy.trigger);
3108
3111
  assert.calledWith(
3109
3112
  TriggerProxy.trigger,
3110
3113
  meeting,
3111
3114
  {file: 'meetings', function: 'fetchMeetingInfo'},
3112
3115
  'meeting:meetingInfoAvailable'
3113
3116
  );
3117
+ assert.calledWith(meeting.updateMeetingActions);
3114
3118
  });
3115
3119
 
3116
3120
  it('calls meetingInfoProvider with all the right parameters and parses the result when random delay is applied', async () => {
@@ -3122,6 +3126,7 @@ describe('plugin-meetings', () => {
3122
3126
  meeting.destination = FAKE_DESTINATION;
3123
3127
  meeting.destinationType = FAKE_TYPE;
3124
3128
  meeting.parseMeetingInfo = sinon.stub().returns(undefined);
3129
+ meeting.updateMeetingActions = sinon.stub().returns(undefined);
3125
3130
  meeting.fetchMeetingInfoTimeoutId = FAKE_TIMEOUT_FETCHMEETINGINFO_ID;
3126
3131
 
3127
3132
  const clock = sinon.useFakeTimers();
@@ -3162,13 +3167,14 @@ describe('plugin-meetings', () => {
3162
3167
  assert.equal(meeting.requiredCaptcha, null);
3163
3168
  assert.equal(meeting.passwordStatus, PASSWORD_STATUS.NOT_REQUIRED);
3164
3169
 
3165
- assert.calledTwice(TriggerProxy.trigger);
3170
+ assert.calledThrice(TriggerProxy.trigger);
3166
3171
  assert.calledWith(
3167
3172
  TriggerProxy.trigger,
3168
3173
  meeting,
3169
3174
  {file: 'meetings', function: 'fetchMeetingInfo'},
3170
3175
  'meeting:meetingInfoAvailable'
3171
3176
  );
3177
+ assert.calledWith(meeting.updateMeetingActions);
3172
3178
  });
3173
3179
 
3174
3180
  it('fails if captchaCode is provided when captcha not needed', async () => {
@@ -4327,7 +4333,7 @@ describe('plugin-meetings', () => {
4327
4333
  it('should stop remote tracks, and trigger a media:stopped event when the remote tracks are stopped', async () => {
4328
4334
  await meeting.closeRemoteTracks();
4329
4335
 
4330
- assert.equal(TriggerProxy.trigger.callCount, 4);
4336
+ assert.equal(TriggerProxy.trigger.callCount, 5);
4331
4337
  assert.calledWith(
4332
4338
  TriggerProxy.trigger,
4333
4339
  sinon.match.instanceOf(Meeting),
@@ -4382,8 +4388,8 @@ describe('plugin-meetings', () => {
4382
4388
  track: 'track',
4383
4389
  type: RemoteTrackType.AUDIO,
4384
4390
  });
4385
- assert.equal(TriggerProxy.trigger.getCall(1).args[2], 'media:ready');
4386
- assert.deepEqual(TriggerProxy.trigger.getCall(1).args[3], {
4391
+ assert.equal(TriggerProxy.trigger.getCall(2).args[2], 'media:ready');
4392
+ assert.deepEqual(TriggerProxy.trigger.getCall(2).args[3], {
4387
4393
  type: 'remoteAudio',
4388
4394
  stream: {id: 'stream'},
4389
4395
  });
@@ -4392,8 +4398,8 @@ describe('plugin-meetings', () => {
4392
4398
  track: 'track',
4393
4399
  type: RemoteTrackType.VIDEO,
4394
4400
  });
4395
- assert.equal(TriggerProxy.trigger.getCall(2).args[2], 'media:ready');
4396
- assert.deepEqual(TriggerProxy.trigger.getCall(2).args[3], {
4401
+ assert.equal(TriggerProxy.trigger.getCall(3).args[2], 'media:ready');
4402
+ assert.deepEqual(TriggerProxy.trigger.getCall(3).args[3], {
4397
4403
  type: 'remoteVideo',
4398
4404
  stream: {id: 'stream'},
4399
4405
  });
@@ -4402,8 +4408,8 @@ describe('plugin-meetings', () => {
4402
4408
  track: 'track',
4403
4409
  type: RemoteTrackType.SCREENSHARE_VIDEO,
4404
4410
  });
4405
- assert.equal(TriggerProxy.trigger.getCall(3).args[2], 'media:ready');
4406
- assert.deepEqual(TriggerProxy.trigger.getCall(3).args[3], {
4411
+ assert.equal(TriggerProxy.trigger.getCall(4).args[2], 'media:ready');
4412
+ assert.deepEqual(TriggerProxy.trigger.getCall(4).args[3], {
4407
4413
  type: 'remoteShare',
4408
4414
  stream: {id: 'stream'},
4409
4415
  });
@@ -4830,7 +4836,7 @@ describe('plugin-meetings', () => {
4830
4836
  meeting.startKeepAlive = sinon.stub();
4831
4837
  meeting.locusInfo.emit({function: 'test', file: 'test'}, 'SELF_UNADMITTED_GUEST', test1);
4832
4838
  assert.calledOnceWithExactly(meeting.startKeepAlive);
4833
- assert.calledTwice(TriggerProxy.trigger);
4839
+ assert.calledThrice(TriggerProxy.trigger);
4834
4840
  assert.calledWith(
4835
4841
  TriggerProxy.trigger,
4836
4842
  sinon.match.instanceOf(Meeting),
@@ -4844,7 +4850,7 @@ describe('plugin-meetings', () => {
4844
4850
  meeting.stopKeepAlive = sinon.stub();
4845
4851
  meeting.locusInfo.emit({function: 'test', file: 'test'}, 'SELF_ADMITTED_GUEST', test1);
4846
4852
  assert.calledOnceWithExactly(meeting.stopKeepAlive);
4847
- assert.calledTwice(TriggerProxy.trigger);
4853
+ assert.calledThrice(TriggerProxy.trigger);
4848
4854
  assert.calledWith(
4849
4855
  TriggerProxy.trigger,
4850
4856
  sinon.match.instanceOf(Meeting),
@@ -5251,6 +5257,19 @@ describe('plugin-meetings', () => {
5251
5257
  assert.calledWith(meeting.simultaneousInterpretation.locusUrlUpdate, newLocusUrl);
5252
5258
  assert.equal(meeting.locusUrl, newLocusUrl);
5253
5259
  assert(meeting.locusId, '12345');
5260
+
5261
+ assert.calledThrice(TriggerProxy.trigger);
5262
+ assert.calledWith(
5263
+ TriggerProxy.trigger,
5264
+ sinon.match.instanceOf(Meeting),
5265
+ {
5266
+ file: 'meeting/index',
5267
+ function: 'setUpLocusSelfListener',
5268
+ },
5269
+ EVENT_TRIGGERS.MEETING_LOCUS_URL_UPDATE,
5270
+ {'locusUrl': 'newLocusUrl/12345'}
5271
+ );
5272
+
5254
5273
  done();
5255
5274
  });
5256
5275
  });
@@ -5777,6 +5796,59 @@ describe('plugin-meetings', () => {
5777
5796
 
5778
5797
  describe('#setUpLocusInfoMeetingInfoListener', () => {
5779
5798
  let locusInfoOnSpy;
5799
+ let handleDataChannelUrlChangeSpy;
5800
+ let updateMeetingActionsSpy;
5801
+
5802
+ beforeEach(() => {
5803
+ locusInfoOnSpy = sinon.spy(meeting.locusInfo, 'on');
5804
+ handleDataChannelUrlChangeSpy = sinon.spy(meeting, 'handleDataChannelUrlChange');
5805
+ updateMeetingActionsSpy = sinon.spy(meeting, 'updateMeetingActions');
5806
+ });
5807
+
5808
+ afterEach(() => {
5809
+ locusInfoOnSpy.restore();
5810
+ updateMeetingActionsSpy.restore();
5811
+ });
5812
+
5813
+ it('registers the correct MEETING_INFO_UPDATED event', () => {
5814
+ const userDisplayPolicy = {a: true};
5815
+ const userDisplayHints = ['LOCK_CONTROL_UNLOCK'];
5816
+ const datachannelUrl = 'some url';
5817
+
5818
+ const setUserPolicySpy = sinon.spy(meeting.recordingController, 'setUserPolicy');
5819
+ const setRecordingDisplayHintsSpy = sinon.spy(
5820
+ meeting.recordingController,
5821
+ 'setDisplayHints'
5822
+ );
5823
+ const setControlsDisplayHintsSpy = sinon.spy(
5824
+ meeting.controlsOptionsManager,
5825
+ 'setDisplayHints'
5826
+ );
5827
+
5828
+ meeting.selfUserPolicies = userDisplayPolicy;
5829
+ meeting.userDisplayHints = userDisplayHints;
5830
+ meeting.datachannelUrl = datachannelUrl;
5831
+
5832
+ meeting.setUpLocusInfoMeetingInfoListener();
5833
+
5834
+ assert.calledThrice(locusInfoOnSpy);
5835
+
5836
+ assert.equal(locusInfoOnSpy.firstCall.args[0], 'MEETING_LOCKED');
5837
+ assert.equal(locusInfoOnSpy.secondCall.args[0], 'MEETING_UNLOCKED');
5838
+ assert.equal(locusInfoOnSpy.thirdCall.args[0], 'MEETING_INFO_UPDATED');
5839
+ const callback = locusInfoOnSpy.thirdCall.args[1];
5840
+
5841
+ callback();
5842
+
5843
+ assert.calledWith(updateMeetingActionsSpy);
5844
+ assert.calledWith(setRecordingDisplayHintsSpy, userDisplayHints);
5845
+ assert.calledWith(setUserPolicySpy, userDisplayPolicy);
5846
+ assert.calledWith(setControlsDisplayHintsSpy, userDisplayHints);
5847
+ assert.calledWith(handleDataChannelUrlChangeSpy, datachannelUrl);
5848
+ });
5849
+ });
5850
+
5851
+ describe('#updateMeetingActions', () => {
5780
5852
  let inMeetingActionsSetSpy;
5781
5853
  let canUserLockSpy;
5782
5854
  let canUserUnlockSpy;
@@ -5793,14 +5865,12 @@ describe('plugin-meetings', () => {
5793
5865
  let canUserLowerAllHandsSpy;
5794
5866
  let canUserLowerSomeoneElsesHandSpy;
5795
5867
  let waitingForOthersToJoinSpy;
5796
- let handleDataChannelUrlChangeSpy;
5797
5868
  let canSendReactionsSpy;
5798
5869
  let canUserRenameSelfAndObservedSpy;
5799
5870
  let canUserRenameOthersSpy;
5800
- let hasHintsSpy;
5871
+ // Due to import tree issues, hasHints must be stubed within the scope of the `it`.
5801
5872
 
5802
5873
  beforeEach(() => {
5803
- locusInfoOnSpy = sinon.spy(meeting.locusInfo, 'on');
5804
5874
  canUserLockSpy = sinon.spy(MeetingUtil, 'canUserLock');
5805
5875
  canUserUnlockSpy = sinon.spy(MeetingUtil, 'canUserUnlock');
5806
5876
  canUserStartSpy = sinon.spy(RecordingUtil, 'canUserStart');
@@ -5820,14 +5890,12 @@ describe('plugin-meetings', () => {
5820
5890
  );
5821
5891
  canUserLowerSomeoneElsesHandSpy = sinon.spy(MeetingUtil, 'canUserLowerSomeoneElsesHand');
5822
5892
  waitingForOthersToJoinSpy = sinon.spy(MeetingUtil, 'waitingForOthersToJoin');
5823
- handleDataChannelUrlChangeSpy = sinon.spy(meeting, 'handleDataChannelUrlChange');
5824
5893
  canSendReactionsSpy = sinon.spy(MeetingUtil, 'canSendReactions');
5825
5894
  canUserRenameSelfAndObservedSpy = sinon.spy(MeetingUtil, 'canUserRenameSelfAndObserved');
5826
5895
  canUserRenameOthersSpy = sinon.spy(MeetingUtil, 'canUserRenameOthers');
5827
5896
  });
5828
5897
 
5829
5898
  afterEach(() => {
5830
- locusInfoOnSpy.restore();
5831
5899
  inMeetingActionsSetSpy.restore();
5832
5900
  waitingForOthersToJoinSpy.restore();
5833
5901
  });
@@ -5864,21 +5932,24 @@ describe('plugin-meetings', () => {
5864
5932
  callType: 'MEETING',
5865
5933
  expectedEnabled: false,
5866
5934
  },
5935
+ {
5936
+ actionName: 'canUseVoip',
5937
+ callType: 'CALL',
5938
+ expectedEnabled: true,
5939
+ },
5940
+ {
5941
+ actionName: 'canUseVoip',
5942
+ callType: 'MEETING',
5943
+ expectedEnabled: false,
5944
+ },
5867
5945
  ],
5868
5946
  ({actionName, callType, expectedEnabled}) => {
5869
5947
  it(`${actionName} is ${expectedEnabled} when the call type is ${callType}`, () => {
5870
5948
  meeting.type = callType;
5871
- meeting.setUpLocusInfoMeetingInfoListener();
5872
-
5873
- const callback = locusInfoOnSpy.thirdCall.args[1];
5874
-
5875
- const payload = {
5876
- info: {
5877
- userDisplayHints: [],
5878
- },
5879
- };
5949
+ meeting.userDisplayHints = [];
5950
+ meeting.meetingInfo = {some: 'info'};
5880
5951
 
5881
- callback(payload);
5952
+ meeting.updateMeetingActions();
5882
5953
 
5883
5954
  assert.equal(meeting.inMeetingActions.get()[actionName], expectedEnabled);
5884
5955
  });
@@ -5935,221 +6006,286 @@ describe('plugin-meetings', () => {
5935
6006
  requiredPolicies: [SELF_POLICY.SUPPORT_ANNOTATION],
5936
6007
  },
5937
6008
  ],
5938
- ({actionName, requiredDisplayHints, requiredPolicies, enableUnifiedMeetings}) => {
6009
+ ({actionName, requiredDisplayHints, requiredPolicies, enableUnifiedMeetings, meetingInfo}) => {
5939
6010
  it(`${actionName} is enabled when the conditions are met`, () => {
6011
+ meeting.userDisplayHints = requiredDisplayHints;
5940
6012
  meeting.selfUserPolicies = {};
5941
6013
 
5942
6014
  meeting.config.experimental.enableUnifiedMeetings = isUndefined(enableUnifiedMeetings)
5943
6015
  ? true
5944
6016
  : enableUnifiedMeetings;
5945
6017
 
6018
+ meeting.meetingInfo = isUndefined(meetingInfo) ? {some: 'info'} : meetingInfo;
6019
+
5946
6020
  forEach(requiredPolicies, (policy) => {
5947
6021
  meeting.selfUserPolicies[policy] = true;
5948
6022
  });
5949
6023
 
5950
- meeting.setUpLocusInfoMeetingInfoListener();
5951
-
5952
- const callback = locusInfoOnSpy.thirdCall.args[1];
5953
-
5954
- const payload = {
5955
- info: {
5956
- userDisplayHints: requiredDisplayHints,
5957
- },
5958
- };
5959
-
5960
- callback(payload);
6024
+ meeting.updateMeetingActions();
5961
6025
 
5962
6026
  assert.isTrue(meeting.inMeetingActions.get()[actionName]);
5963
6027
  });
5964
6028
 
5965
6029
  if (requiredDisplayHints.length !== 0) {
5966
6030
  it(`${actionName} is disabled when the required display hints are missing`, () => {
6031
+ meeting.userDisplayHints = [];
5967
6032
  meeting.selfUserPolicies = {};
5968
6033
 
6034
+ meeting.meetingInfo = isUndefined(meetingInfo) ? {some: 'info'} : meetingInfo;
6035
+
5969
6036
  forEach(requiredPolicies, (policy) => {
5970
6037
  meeting.selfUserPolicies[policy] = true;
5971
6038
  });
5972
6039
 
5973
- meeting.setUpLocusInfoMeetingInfoListener();
5974
-
5975
- const callback = locusInfoOnSpy.thirdCall.args[1];
5976
-
5977
- const payload = {
5978
- info: {
5979
- userDisplayHints: [],
5980
- },
5981
- };
5982
-
5983
- callback(payload);
6040
+ meeting.updateMeetingActions();
5984
6041
 
5985
6042
  assert.isFalse(meeting.inMeetingActions.get()[actionName]);
5986
6043
  });
5987
6044
  }
5988
6045
 
5989
6046
  it(`${actionName} is disabled when the required policies are missing`, () => {
6047
+ meeting.userDisplayHints = requiredDisplayHints;
5990
6048
  meeting.selfUserPolicies = {};
5991
6049
 
5992
- meeting.setUpLocusInfoMeetingInfoListener();
5993
-
5994
- const callback = locusInfoOnSpy.thirdCall.args[1];
5995
-
5996
- const payload = {
5997
- info: {
5998
- userDisplayHints: requiredDisplayHints,
5999
- },
6000
- };
6050
+ meeting.meetingInfo = isUndefined(meetingInfo) ? {some: 'info'} : meetingInfo;
6001
6051
 
6002
- callback(payload);
6052
+ meeting.updateMeetingActions();
6003
6053
 
6004
6054
  assert.isFalse(meeting.inMeetingActions.get()[actionName]);
6005
6055
  });
6006
6056
  }
6007
6057
  );
6008
6058
 
6009
- it('registers the correct MEETING_INFO_UPDATED event', () => {
6010
- // Due to import tree issues, hasHints must be stubed within the scope of the `it`.
6011
- const restorableHasHints = ControlsOptionsUtil.hasHints;
6012
- ControlsOptionsUtil.hasHints = sinon.stub().returns(true);
6013
- ControlsOptionsUtil.hasPolicies = sinon.stub().returns(true);
6059
+ it('canUseVoip is enabled based on locus info when the conditions are met', () => {
6060
+ meeting.userDisplayHints = [DISPLAY_HINTS.VOIP_IS_ENABLED];
6061
+ meeting.selfUserPolicies = {[SELF_POLICY.SUPPORT_VOIP]: true};
6062
+ meeting.meetingInfo.supportVoIP = false;
6063
+ meeting.config.experimental.enableUnifiedMeetings = true;
6014
6064
 
6015
- const setUserPolicySpy = sinon.spy(meeting.recordingController, 'setUserPolicy');
6016
- meeting.selfUserPolicies = {a: true};
6065
+ meeting.updateMeetingActions();
6017
6066
 
6018
- meeting.setUpLocusInfoMeetingInfoListener();
6067
+ assert.isTrue(meeting.inMeetingActions.get()['canUseVoip']);
6068
+ });
6019
6069
 
6020
- assert.calledThrice(locusInfoOnSpy);
6070
+ it('canUseVoip is disabled based on locus info when the required display hints are missing', () => {
6071
+ meeting.userDisplayHints = [];
6072
+ meeting.selfUserPolicies = {[SELF_POLICY.SUPPORT_VOIP]: true};
6073
+ meeting.meetingInfo.supportVoIP = true;
6074
+ meeting.config.experimental.enableUnifiedMeetings = true;
6021
6075
 
6022
- assert.equal(locusInfoOnSpy.firstCall.args[0], 'MEETING_LOCKED');
6023
- assert.equal(locusInfoOnSpy.secondCall.args[0], 'MEETING_UNLOCKED');
6024
- assert.equal(locusInfoOnSpy.thirdCall.args[0], 'MEETING_INFO_UPDATED');
6025
- const callback = locusInfoOnSpy.thirdCall.args[1];
6076
+ meeting.updateMeetingActions();
6026
6077
 
6027
- const payload = {
6028
- info: {
6029
- userDisplayHints: ['LOCK_CONTROL_UNLOCK'],
6030
- datachannelUrl: 'some url',
6031
- },
6032
- };
6078
+ assert.isFalse(meeting.inMeetingActions.get()['canUseVoip']);
6079
+ });
6033
6080
 
6034
- callback(payload);
6081
+ it('canUseVoip is disabled based on locus info when the required policies are missing', () => {
6082
+ meeting.userDisplayHints = [DISPLAY_HINTS.VOIP_IS_ENABLED];
6083
+ meeting.selfUserPolicies = {};
6084
+ meeting.meetingInfo.supportVoIP = true;
6085
+ meeting.config.experimental.enableUnifiedMeetings = true;
6086
+
6087
+ meeting.updateMeetingActions();
6088
+
6089
+ assert.isFalse(meeting.inMeetingActions.get()['canUseVoip']);
6090
+ });
6035
6091
 
6036
- assert.calledWith(canUserLockSpy, payload.info.userDisplayHints);
6037
- assert.calledWith(canUserUnlockSpy, payload.info.userDisplayHints);
6038
- assert.calledWith(canUserStartSpy, payload.info.userDisplayHints);
6039
- assert.calledWith(canUserStopSpy, payload.info.userDisplayHints);
6040
- assert.calledWith(canUserPauseSpy, payload.info.userDisplayHints);
6041
- assert.calledWith(canUserResumeSpy, payload.info.userDisplayHints);
6042
- assert.calledWith(canSetMuteOnEntrySpy, payload.info.userDisplayHints);
6043
- assert.calledWith(canUnsetMuteOnEntrySpy, payload.info.userDisplayHints);
6044
- assert.calledWith(canSetDisallowUnmuteSpy, payload.info.userDisplayHints);
6045
- assert.calledWith(canUnsetDisallowUnmuteSpy, payload.info.userDisplayHints);
6046
- assert.calledWith(canUserRaiseHandSpy, payload.info.userDisplayHints);
6047
- assert.calledWith(bothLeaveAndEndMeetingAvailableSpy, payload.info.userDisplayHints);
6048
- assert.calledWith(canUserLowerAllHandsSpy, payload.info.userDisplayHints);
6049
- assert.calledWith(canUserLowerSomeoneElsesHandSpy, payload.info.userDisplayHints);
6050
- assert.calledWith(waitingForOthersToJoinSpy, payload.info.userDisplayHints);
6051
- assert.calledWith(handleDataChannelUrlChangeSpy, payload.info.datachannelUrl);
6052
- assert.calledWith(canSendReactionsSpy, null, payload.info.userDisplayHints);
6053
- assert.calledWith(canUserRenameSelfAndObservedSpy, payload.info.userDisplayHints);
6054
- assert.calledWith(canUserRenameOthersSpy, payload.info.userDisplayHints);
6092
+ it('canUseVoip is enabled based on api info when the conditions are met', () => {
6093
+ meeting.userDisplayHints = undefined;
6094
+ meeting.selfUserPolicies = {[SELF_POLICY.SUPPORT_VOIP]: true};
6095
+ meeting.meetingInfo.supportVoIP = true;
6096
+ meeting.config.experimental.enableUnifiedMeetings = true;
6097
+
6098
+ meeting.updateMeetingActions();
6099
+
6100
+ assert.isTrue(meeting.inMeetingActions.get()['canUseVoip']);
6101
+ });
6102
+
6103
+ it('canUseVoip is enabled when there is no meeting info', () => {
6104
+ meeting.config.experimental.enableUnifiedMeetings = true;
6105
+
6106
+ meeting.updateMeetingActions();
6107
+
6108
+ assert.isTrue(meeting.inMeetingActions.get()['canUseVoip']);
6109
+ });
6110
+
6111
+ it('canUseVoip is enabled when it is a locus call', () => {
6112
+ meeting.config.experimental.enableUnifiedMeetings = true;
6113
+ meeting.meetingInfo = {some: 'info'};
6114
+ meeting.type = 'CALL';
6115
+
6116
+ meeting.updateMeetingActions();
6117
+
6118
+ assert.isTrue(meeting.inMeetingActions.get()['canUseVoip']);
6119
+ });
6120
+
6121
+ it('canUseVoip is disabled based on api info when supportVoip is false', () => {
6122
+ meeting.userDisplayHints = undefined;
6123
+ meeting.selfUserPolicies = {[SELF_POLICY.SUPPORT_VOIP]: true};
6124
+ meeting.meetingInfo.supportVoIP = false;
6125
+ meeting.config.experimental.enableUnifiedMeetings = true;
6126
+
6127
+ meeting.updateMeetingActions();
6128
+
6129
+ assert.isFalse(meeting.inMeetingActions.get()['canUseVoip']);
6130
+ });
6131
+
6132
+ it('canUseVoip is disabled based on api info when the required policies are missing', () => {
6133
+ meeting.userDisplayHints = undefined;
6134
+ meeting.selfUserPolicies = {};
6135
+ meeting.meetingInfo.supportVoIP = true;
6136
+ meeting.config.experimental.enableUnifiedMeetings = true;
6137
+
6138
+ meeting.updateMeetingActions();
6139
+
6140
+ assert.isFalse(meeting.inMeetingActions.get()['canUseVoip']);
6141
+ });
6142
+
6143
+ it('canUseVoip is enabled when enableUnifiedMeetings is false', () => {
6144
+ meeting.userDisplayHints = [];
6145
+ meeting.selfUserPolicies = {};
6146
+ meeting.meetingInfo.supportVoIP = false;
6147
+ meeting.config.experimental.enableUnifiedMeetings = false;
6148
+
6149
+ meeting.updateMeetingActions();
6150
+
6151
+ assert.isTrue(meeting.inMeetingActions.get()['canUseVoip']);
6152
+ });
6153
+
6154
+ it('correctly updates the meeting actions', () => {
6155
+ // Due to import tree issues, hasHints must be stubed within the scope of the `it`.
6156
+ const restorableHasHints = ControlsOptionsUtil.hasHints;
6157
+ ControlsOptionsUtil.hasHints = sinon.stub().returns(true);
6158
+ ControlsOptionsUtil.hasPolicies = sinon.stub().returns(true);
6159
+
6160
+ const selfUserPolicies = {a: true};
6161
+ meeting.selfUserPolicies = {a: true};
6162
+ const userDisplayHints = ['LOCK_CONTROL_UNLOCK'];
6163
+ meeting.userDisplayHints = ['LOCK_CONTROL_UNLOCK'];
6164
+
6165
+ meeting.updateMeetingActions();
6166
+
6167
+ assert.calledWith(canUserLockSpy, userDisplayHints);
6168
+ assert.calledWith(canUserUnlockSpy, userDisplayHints);
6169
+ assert.calledWith(canUserStartSpy, userDisplayHints);
6170
+ assert.calledWith(canUserStopSpy, userDisplayHints);
6171
+ assert.calledWith(canUserPauseSpy, userDisplayHints);
6172
+ assert.calledWith(canUserResumeSpy, userDisplayHints);
6173
+ assert.calledWith(canSetMuteOnEntrySpy, userDisplayHints);
6174
+ assert.calledWith(canUnsetMuteOnEntrySpy, userDisplayHints);
6175
+ assert.calledWith(canSetDisallowUnmuteSpy, userDisplayHints);
6176
+ assert.calledWith(canUnsetDisallowUnmuteSpy, userDisplayHints);
6177
+ assert.calledWith(canUserRaiseHandSpy, userDisplayHints);
6178
+ assert.calledWith(bothLeaveAndEndMeetingAvailableSpy, userDisplayHints);
6179
+ assert.calledWith(canUserLowerAllHandsSpy, userDisplayHints);
6180
+ assert.calledWith(canUserLowerSomeoneElsesHandSpy, userDisplayHints);
6181
+ assert.calledWith(waitingForOthersToJoinSpy, userDisplayHints);
6182
+ assert.calledWith(canSendReactionsSpy, null, userDisplayHints);
6183
+ assert.calledWith(canUserRenameSelfAndObservedSpy, userDisplayHints);
6184
+ assert.calledWith(canUserRenameOthersSpy, userDisplayHints);
6055
6185
 
6056
6186
  assert.calledWith(ControlsOptionsUtil.hasHints, {
6057
6187
  requiredHints: [DISPLAY_HINTS.MUTE_ALL],
6058
- displayHints: payload.info.userDisplayHints,
6188
+ displayHints: userDisplayHints,
6059
6189
  });
6060
6190
  assert.calledWith(ControlsOptionsUtil.hasHints, {
6061
6191
  requiredHints: [DISPLAY_HINTS.UNMUTE_ALL],
6062
- displayHints: payload.info.userDisplayHints,
6192
+ displayHints: userDisplayHints,
6063
6193
  });
6064
6194
  assert.calledWith(ControlsOptionsUtil.hasHints, {
6065
6195
  requiredHints: [DISPLAY_HINTS.ENABLE_HARD_MUTE],
6066
- displayHints: payload.info.userDisplayHints,
6196
+ displayHints: userDisplayHints,
6067
6197
  });
6068
6198
  assert.calledWith(ControlsOptionsUtil.hasHints, {
6069
6199
  requiredHints: [DISPLAY_HINTS.DISABLE_HARD_MUTE],
6070
- displayHints: payload.info.userDisplayHints,
6200
+ displayHints: userDisplayHints,
6071
6201
  });
6072
6202
  assert.calledWith(ControlsOptionsUtil.hasHints, {
6073
6203
  requiredHints: [DISPLAY_HINTS.ENABLE_MUTE_ON_ENTRY],
6074
- displayHints: payload.info.userDisplayHints,
6204
+ displayHints: userDisplayHints,
6075
6205
  });
6076
6206
  assert.calledWith(ControlsOptionsUtil.hasHints, {
6077
6207
  requiredHints: [DISPLAY_HINTS.DISABLE_MUTE_ON_ENTRY],
6078
- displayHints: payload.info.userDisplayHints,
6208
+ displayHints: userDisplayHints,
6079
6209
  });
6080
6210
  assert.calledWith(ControlsOptionsUtil.hasHints, {
6081
6211
  requiredHints: [DISPLAY_HINTS.ENABLE_REACTIONS],
6082
- displayHints: payload.info.userDisplayHints,
6212
+ displayHints: userDisplayHints,
6083
6213
  });
6084
6214
  assert.calledWith(ControlsOptionsUtil.hasHints, {
6085
6215
  requiredHints: [DISPLAY_HINTS.DISABLE_REACTIONS],
6086
- displayHints: payload.info.userDisplayHints,
6216
+ displayHints: userDisplayHints,
6087
6217
  });
6088
6218
  assert.calledWith(ControlsOptionsUtil.hasHints, {
6089
6219
  requiredHints: [DISPLAY_HINTS.ENABLE_SHOW_DISPLAY_NAME],
6090
- displayHints: payload.info.userDisplayHints,
6220
+ displayHints: userDisplayHints,
6091
6221
  });
6092
6222
  assert.calledWith(ControlsOptionsUtil.hasHints, {
6093
6223
  requiredHints: [DISPLAY_HINTS.DISABLE_SHOW_DISPLAY_NAME],
6094
- displayHints: payload.info.userDisplayHints,
6224
+ displayHints: userDisplayHints,
6095
6225
  });
6096
6226
  assert.calledWith(ControlsOptionsUtil.hasHints, {
6097
6227
  requiredHints: [DISPLAY_HINTS.SHARE_CONTROL],
6098
- displayHints: payload.info.userDisplayHints,
6228
+ displayHints: userDisplayHints,
6099
6229
  });
6100
6230
  assert.calledWith(ControlsOptionsUtil.hasHints, {
6101
6231
  requiredHints: [DISPLAY_HINTS.ENABLE_VIEW_THE_PARTICIPANT_LIST],
6102
- displayHints: payload.info.userDisplayHints,
6232
+ displayHints: userDisplayHints,
6103
6233
  });
6104
6234
  assert.calledWith(ControlsOptionsUtil.hasHints, {
6105
6235
  requiredHints: [DISPLAY_HINTS.DISABLE_VIEW_THE_PARTICIPANT_LIST],
6106
- displayHints: payload.info.userDisplayHints,
6236
+ displayHints: userDisplayHints,
6107
6237
  });
6108
6238
  assert.calledWith(ControlsOptionsUtil.hasHints, {
6109
6239
  requiredHints: [DISPLAY_HINTS.SHARE_FILE],
6110
- displayHints: payload.info.userDisplayHints,
6240
+ displayHints: userDisplayHints,
6111
6241
  });
6112
6242
  assert.calledWith(ControlsOptionsUtil.hasPolicies, {
6113
6243
  requiredPolicies: [SELF_POLICY.SUPPORT_FILE_SHARE],
6114
- policies: {a: true},
6244
+ policies: selfUserPolicies,
6115
6245
  });
6116
6246
  assert.calledWith(ControlsOptionsUtil.hasHints, {
6117
6247
  requiredHints: [DISPLAY_HINTS.SHARE_APPLICATION],
6118
- displayHints: payload.info.userDisplayHints,
6248
+ displayHints: userDisplayHints,
6119
6249
  });
6120
6250
  assert.calledWith(ControlsOptionsUtil.hasPolicies, {
6121
6251
  requiredPolicies: [SELF_POLICY.SUPPORT_APP_SHARE],
6122
- policies: {a: true},
6252
+ policies: selfUserPolicies,
6123
6253
  });
6124
6254
  assert.calledWith(ControlsOptionsUtil.hasHints, {
6125
6255
  requiredHints: [DISPLAY_HINTS.SHARE_CAMERA],
6126
- displayHints: payload.info.userDisplayHints,
6256
+ displayHints: userDisplayHints,
6127
6257
  });
6128
6258
  assert.calledWith(ControlsOptionsUtil.hasPolicies, {
6129
6259
  requiredPolicies: [SELF_POLICY.SUPPORT_CAMERA_SHARE],
6130
- policies: {a: true},
6260
+ policies: selfUserPolicies,
6131
6261
  });
6132
6262
  assert.calledWith(ControlsOptionsUtil.hasHints, {
6133
6263
  requiredHints: [DISPLAY_HINTS.SHARE_DESKTOP],
6134
- displayHints: payload.info.userDisplayHints,
6264
+ displayHints: userDisplayHints,
6135
6265
  });
6136
6266
  assert.calledWith(ControlsOptionsUtil.hasPolicies, {
6137
6267
  requiredPolicies: [SELF_POLICY.SUPPORT_DESKTOP_SHARE],
6138
- policies: {a: true},
6268
+ policies: selfUserPolicies,
6139
6269
  });
6140
6270
  assert.calledWith(ControlsOptionsUtil.hasHints, {
6141
6271
  requiredHints: [DISPLAY_HINTS.SHARE_CONTENT],
6142
- displayHints: payload.info.userDisplayHints,
6272
+ displayHints: userDisplayHints,
6273
+ });
6274
+ assert.calledWith(ControlsOptionsUtil.hasHints, {
6275
+ requiredHints: [DISPLAY_HINTS.VOIP_IS_ENABLED],
6276
+ displayHints: userDisplayHints,
6277
+ });
6278
+ assert.calledWith(ControlsOptionsUtil.hasPolicies, {
6279
+ requiredPolicies: [SELF_POLICY.SUPPORT_VOIP],
6280
+ policies: selfUserPolicies,
6143
6281
  });
6144
-
6145
- assert.calledWith(setUserPolicySpy, {a: true});
6146
6282
 
6147
6283
  assert.calledWith(
6148
6284
  TriggerProxy.trigger,
6149
6285
  meeting,
6150
6286
  {
6151
6287
  file: 'meeting/index',
6152
- function: 'setUpLocusInfoMeetingInfoListener',
6288
+ function: 'updateMeetingActions',
6153
6289
  },
6154
6290
  'meeting:actionsUpdate',
6155
6291
  meeting.inMeetingActions.get()
@@ -6157,7 +6293,7 @@ describe('plugin-meetings', () => {
6157
6293
 
6158
6294
  TriggerProxy.trigger.resetHistory();
6159
6295
 
6160
- callback(payload);
6296
+ meeting.updateMeetingActions();
6161
6297
 
6162
6298
  assert.notCalled(TriggerProxy.trigger);
6163
6299
 
@@ -6172,13 +6308,9 @@ describe('plugin-meetings', () => {
6172
6308
  updateLLMConnectionSpy = sinon.spy(meeting, 'updateLLMConnection');
6173
6309
  });
6174
6310
 
6175
- const check = async (url, expectedCalled) => {
6311
+ const check = (url, expectedCalled) => {
6176
6312
  meeting.handleDataChannelUrlChange(url);
6177
6313
 
6178
- assert.notCalled(updateLLMConnectionSpy);
6179
-
6180
- await testUtils.waitUntil(0);
6181
-
6182
6314
  if (expectedCalled) {
6183
6315
  assert.calledWith(updateLLMConnectionSpy);
6184
6316
  } else {
@@ -6186,17 +6318,17 @@ describe('plugin-meetings', () => {
6186
6318
  }
6187
6319
  };
6188
6320
 
6189
- it('calls deferred updateLLMConnection if datachannelURL is set and the enableAutomaticLLM is true', async () => {
6321
+ it('calls deferred updateLLMConnection if datachannelURL is set and the enableAutomaticLLM is true', () => {
6190
6322
  meeting.config.enableAutomaticLLM = true;
6191
6323
  check('some url', true);
6192
6324
  });
6193
6325
 
6194
- it('does not call updateLLMConnection if datachannelURL is undefined', async () => {
6326
+ it('does not call updateLLMConnection if datachannelURL is undefined', () => {
6195
6327
  meeting.config.enableAutomaticLLM = true;
6196
6328
  check(undefined, false);
6197
6329
  });
6198
6330
 
6199
- it('does not call updateLLMConnection if enableAutomaticLLM is false', async () => {
6331
+ it('does not call updateLLMConnection if enableAutomaticLLM is false', () => {
6200
6332
  check('some url', false);
6201
6333
  });
6202
6334
  });
@@ -6520,6 +6652,10 @@ describe('plugin-meetings', () => {
6520
6652
  endedSharingId: null,
6521
6653
  },
6522
6654
  },
6655
+ meeting: {
6656
+ eventName: EVENT_TRIGGERS.MEETING_LOCUS_URL_UPDATE,
6657
+ eventPayload: 'newLocusUrl',
6658
+ },
6523
6659
  };
6524
6660
 
6525
6661
  let shareStatus = null;
@@ -6743,7 +6879,7 @@ describe('plugin-meetings', () => {
6743
6879
  assert.equal(meeting.shareStatus, SHARE_STATUS.NO_SHARE);
6744
6880
 
6745
6881
  // Called once --> members:update (ignore)
6746
- let callCounter = 1;
6882
+ let callCounter = 2;
6747
6883
 
6748
6884
  data.forEach((d, index) => {
6749
6885
  meeting.locusInfo.emit(
@@ -6771,10 +6907,10 @@ describe('plugin-meetings', () => {
6771
6907
 
6772
6908
  assert.callCount(TriggerProxy.trigger, callCounter);
6773
6909
 
6774
- // Start with 1 to ignore members:update trigger
6910
+ // Start with 2 to ignore members:update trigger, and meeting:locus:locusUrl:update
6775
6911
 
6776
- let i = 1;
6777
- let offset = 2;
6912
+ let i = 2;
6913
+ let offset = 3;
6778
6914
 
6779
6915
  while (i < callCounter) {
6780
6916
  const index = Math.floor(i / offset);