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

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 +2 -5
  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 +214 -178
  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 +9 -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 +2 -3
  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 +247 -224
  28. package/src/recording-controller/index.ts +0 -1
  29. package/test/unit/spec/locus-info/index.js +69 -2
  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 +255 -140
  33. package/test/unit/spec/meetings/index.js +1 -1
@@ -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,23 @@ 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();
5949
+ meeting.userDisplayHints = [];
5872
5950
 
5873
- const callback = locusInfoOnSpy.thirdCall.args[1];
5874
-
5875
- const payload = {
5876
- info: {
5877
- userDisplayHints: [],
5878
- },
5879
- };
5880
-
5881
- callback(payload);
5951
+ meeting.updateMeetingActions();
5882
5952
 
5883
5953
  assert.equal(meeting.inMeetingActions.get()[actionName], expectedEnabled);
5884
5954
  });
@@ -5937,6 +6007,7 @@ describe('plugin-meetings', () => {
5937
6007
  ],
5938
6008
  ({actionName, requiredDisplayHints, requiredPolicies, enableUnifiedMeetings}) => {
5939
6009
  it(`${actionName} is enabled when the conditions are met`, () => {
6010
+ meeting.userDisplayHints = requiredDisplayHints;
5940
6011
  meeting.selfUserPolicies = {};
5941
6012
 
5942
6013
  meeting.config.experimental.enableUnifiedMeetings = isUndefined(enableUnifiedMeetings)
@@ -5947,209 +6018,249 @@ describe('plugin-meetings', () => {
5947
6018
  meeting.selfUserPolicies[policy] = true;
5948
6019
  });
5949
6020
 
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);
6021
+ meeting.updateMeetingActions();
5961
6022
 
5962
6023
  assert.isTrue(meeting.inMeetingActions.get()[actionName]);
5963
6024
  });
5964
6025
 
5965
6026
  if (requiredDisplayHints.length !== 0) {
5966
6027
  it(`${actionName} is disabled when the required display hints are missing`, () => {
6028
+ meeting.userDisplayHints = [];
5967
6029
  meeting.selfUserPolicies = {};
5968
6030
 
5969
6031
  forEach(requiredPolicies, (policy) => {
5970
6032
  meeting.selfUserPolicies[policy] = true;
5971
6033
  });
5972
6034
 
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);
6035
+ meeting.updateMeetingActions();
5984
6036
 
5985
6037
  assert.isFalse(meeting.inMeetingActions.get()[actionName]);
5986
6038
  });
5987
6039
  }
5988
6040
 
5989
6041
  it(`${actionName} is disabled when the required policies are missing`, () => {
6042
+ meeting.userDisplayHints = requiredDisplayHints;
5990
6043
  meeting.selfUserPolicies = {};
5991
6044
 
5992
- meeting.setUpLocusInfoMeetingInfoListener();
5993
-
5994
- const callback = locusInfoOnSpy.thirdCall.args[1];
5995
-
5996
- const payload = {
5997
- info: {
5998
- userDisplayHints: requiredDisplayHints,
5999
- },
6000
- };
6001
-
6002
- callback(payload);
6045
+ meeting.updateMeetingActions();
6003
6046
 
6004
6047
  assert.isFalse(meeting.inMeetingActions.get()[actionName]);
6005
6048
  });
6006
6049
  }
6007
6050
  );
6008
6051
 
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);
6052
+ it('canUseVoip is enabled based on locus info when the conditions are met', () => {
6053
+ meeting.userDisplayHints = [DISPLAY_HINTS.VOIP_IS_ENABLED];
6054
+ meeting.selfUserPolicies = {[SELF_POLICY.SUPPORT_VOIP]: true};
6055
+ meeting.meetingInfo.supportVoIP = false;
6056
+ meeting.config.experimental.enableUnifiedMeetings = true;
6014
6057
 
6015
- const setUserPolicySpy = sinon.spy(meeting.recordingController, 'setUserPolicy');
6016
- meeting.selfUserPolicies = {a: true};
6058
+ meeting.updateMeetingActions();
6017
6059
 
6018
- meeting.setUpLocusInfoMeetingInfoListener();
6060
+ assert.isTrue(meeting.inMeetingActions.get()['canUseVoip']);
6061
+ });
6019
6062
 
6020
- assert.calledThrice(locusInfoOnSpy);
6063
+ it('canUseVoip is disabled based on locus info when the required display hints are missing', () => {
6064
+ meeting.userDisplayHints = [];
6065
+ meeting.selfUserPolicies = {[SELF_POLICY.SUPPORT_VOIP]: true};
6066
+ meeting.meetingInfo.supportVoIP = true;
6067
+ meeting.config.experimental.enableUnifiedMeetings = true;
6021
6068
 
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];
6069
+ meeting.updateMeetingActions();
6026
6070
 
6027
- const payload = {
6028
- info: {
6029
- userDisplayHints: ['LOCK_CONTROL_UNLOCK'],
6030
- datachannelUrl: 'some url',
6031
- },
6032
- };
6071
+ assert.isFalse(meeting.inMeetingActions.get()['canUseVoip']);
6072
+ });
6033
6073
 
6034
- callback(payload);
6074
+ it('canUseVoip is disabled based on locus info when the required policies are missing', () => {
6075
+ meeting.userDisplayHints = [DISPLAY_HINTS.VOIP_IS_ENABLED];
6076
+ meeting.selfUserPolicies = {};
6077
+ meeting.meetingInfo.supportVoIP = true;
6078
+ meeting.config.experimental.enableUnifiedMeetings = true;
6079
+
6080
+ meeting.updateMeetingActions();
6081
+
6082
+ assert.isFalse(meeting.inMeetingActions.get()['canUseVoip']);
6083
+ });
6084
+
6085
+ it('canUseVoip is enabled based on api info when the conditions are met', () => {
6086
+ meeting.userDisplayHints = undefined;
6087
+ meeting.selfUserPolicies = {[SELF_POLICY.SUPPORT_VOIP]: true};
6088
+ meeting.meetingInfo.supportVoIP = true;
6089
+ meeting.config.experimental.enableUnifiedMeetings = true;
6090
+
6091
+ meeting.updateMeetingActions();
6092
+
6093
+ assert.isTrue(meeting.inMeetingActions.get()['canUseVoip']);
6094
+ });
6035
6095
 
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);
6096
+ it('canUseVoip is disabled based on api info when supportVoip is false', () => {
6097
+ meeting.userDisplayHints = undefined;
6098
+ meeting.selfUserPolicies = {[SELF_POLICY.SUPPORT_VOIP]: true};
6099
+ meeting.meetingInfo.supportVoIP = false;
6100
+ meeting.config.experimental.enableUnifiedMeetings = true;
6101
+
6102
+ meeting.updateMeetingActions();
6103
+
6104
+ assert.isFalse(meeting.inMeetingActions.get()['canUseVoip']);
6105
+ });
6106
+
6107
+ it('canUseVoip is disabled based on api info when the required policies are missing', () => {
6108
+ meeting.userDisplayHints = undefined;
6109
+ meeting.selfUserPolicies = {};
6110
+ meeting.meetingInfo.supportVoIP = true;
6111
+ meeting.config.experimental.enableUnifiedMeetings = true;
6112
+
6113
+ meeting.updateMeetingActions();
6114
+
6115
+ assert.isFalse(meeting.inMeetingActions.get()['canUseVoip']);
6116
+ });
6117
+
6118
+ it('canUseVoip is enabled when enableUnifiedMeetings is false', () => {
6119
+ meeting.userDisplayHints = [];
6120
+ meeting.selfUserPolicies = {};
6121
+ meeting.meetingInfo.supportVoIP = false;
6122
+ meeting.config.experimental.enableUnifiedMeetings = false;
6123
+
6124
+ meeting.updateMeetingActions();
6125
+
6126
+ assert.isTrue(meeting.inMeetingActions.get()['canUseVoip']);
6127
+ });
6128
+
6129
+ it('correctly updates the meeting actions', () => {
6130
+ // Due to import tree issues, hasHints must be stubed within the scope of the `it`.
6131
+ const restorableHasHints = ControlsOptionsUtil.hasHints;
6132
+ ControlsOptionsUtil.hasHints = sinon.stub().returns(true);
6133
+ ControlsOptionsUtil.hasPolicies = sinon.stub().returns(true);
6134
+
6135
+ const selfUserPolicies = {a: true};
6136
+ meeting.selfUserPolicies = {a: true};
6137
+ const userDisplayHints = ['LOCK_CONTROL_UNLOCK'];
6138
+ meeting.userDisplayHints = ['LOCK_CONTROL_UNLOCK'];
6139
+
6140
+ meeting.updateMeetingActions();
6141
+
6142
+ assert.calledWith(canUserLockSpy, userDisplayHints);
6143
+ assert.calledWith(canUserUnlockSpy, userDisplayHints);
6144
+ assert.calledWith(canUserStartSpy, userDisplayHints);
6145
+ assert.calledWith(canUserStopSpy, userDisplayHints);
6146
+ assert.calledWith(canUserPauseSpy, userDisplayHints);
6147
+ assert.calledWith(canUserResumeSpy, userDisplayHints);
6148
+ assert.calledWith(canSetMuteOnEntrySpy, userDisplayHints);
6149
+ assert.calledWith(canUnsetMuteOnEntrySpy, userDisplayHints);
6150
+ assert.calledWith(canSetDisallowUnmuteSpy, userDisplayHints);
6151
+ assert.calledWith(canUnsetDisallowUnmuteSpy, userDisplayHints);
6152
+ assert.calledWith(canUserRaiseHandSpy, userDisplayHints);
6153
+ assert.calledWith(bothLeaveAndEndMeetingAvailableSpy, userDisplayHints);
6154
+ assert.calledWith(canUserLowerAllHandsSpy, userDisplayHints);
6155
+ assert.calledWith(canUserLowerSomeoneElsesHandSpy, userDisplayHints);
6156
+ assert.calledWith(waitingForOthersToJoinSpy, userDisplayHints);
6157
+ assert.calledWith(canSendReactionsSpy, null, userDisplayHints);
6158
+ assert.calledWith(canUserRenameSelfAndObservedSpy, userDisplayHints);
6159
+ assert.calledWith(canUserRenameOthersSpy, userDisplayHints);
6055
6160
 
6056
6161
  assert.calledWith(ControlsOptionsUtil.hasHints, {
6057
6162
  requiredHints: [DISPLAY_HINTS.MUTE_ALL],
6058
- displayHints: payload.info.userDisplayHints,
6163
+ displayHints: userDisplayHints,
6059
6164
  });
6060
6165
  assert.calledWith(ControlsOptionsUtil.hasHints, {
6061
6166
  requiredHints: [DISPLAY_HINTS.UNMUTE_ALL],
6062
- displayHints: payload.info.userDisplayHints,
6167
+ displayHints: userDisplayHints,
6063
6168
  });
6064
6169
  assert.calledWith(ControlsOptionsUtil.hasHints, {
6065
6170
  requiredHints: [DISPLAY_HINTS.ENABLE_HARD_MUTE],
6066
- displayHints: payload.info.userDisplayHints,
6171
+ displayHints: userDisplayHints,
6067
6172
  });
6068
6173
  assert.calledWith(ControlsOptionsUtil.hasHints, {
6069
6174
  requiredHints: [DISPLAY_HINTS.DISABLE_HARD_MUTE],
6070
- displayHints: payload.info.userDisplayHints,
6175
+ displayHints: userDisplayHints,
6071
6176
  });
6072
6177
  assert.calledWith(ControlsOptionsUtil.hasHints, {
6073
6178
  requiredHints: [DISPLAY_HINTS.ENABLE_MUTE_ON_ENTRY],
6074
- displayHints: payload.info.userDisplayHints,
6179
+ displayHints: userDisplayHints,
6075
6180
  });
6076
6181
  assert.calledWith(ControlsOptionsUtil.hasHints, {
6077
6182
  requiredHints: [DISPLAY_HINTS.DISABLE_MUTE_ON_ENTRY],
6078
- displayHints: payload.info.userDisplayHints,
6183
+ displayHints: userDisplayHints,
6079
6184
  });
6080
6185
  assert.calledWith(ControlsOptionsUtil.hasHints, {
6081
6186
  requiredHints: [DISPLAY_HINTS.ENABLE_REACTIONS],
6082
- displayHints: payload.info.userDisplayHints,
6187
+ displayHints: userDisplayHints,
6083
6188
  });
6084
6189
  assert.calledWith(ControlsOptionsUtil.hasHints, {
6085
6190
  requiredHints: [DISPLAY_HINTS.DISABLE_REACTIONS],
6086
- displayHints: payload.info.userDisplayHints,
6191
+ displayHints: userDisplayHints,
6087
6192
  });
6088
6193
  assert.calledWith(ControlsOptionsUtil.hasHints, {
6089
6194
  requiredHints: [DISPLAY_HINTS.ENABLE_SHOW_DISPLAY_NAME],
6090
- displayHints: payload.info.userDisplayHints,
6195
+ displayHints: userDisplayHints,
6091
6196
  });
6092
6197
  assert.calledWith(ControlsOptionsUtil.hasHints, {
6093
6198
  requiredHints: [DISPLAY_HINTS.DISABLE_SHOW_DISPLAY_NAME],
6094
- displayHints: payload.info.userDisplayHints,
6199
+ displayHints: userDisplayHints,
6095
6200
  });
6096
6201
  assert.calledWith(ControlsOptionsUtil.hasHints, {
6097
6202
  requiredHints: [DISPLAY_HINTS.SHARE_CONTROL],
6098
- displayHints: payload.info.userDisplayHints,
6203
+ displayHints: userDisplayHints,
6099
6204
  });
6100
6205
  assert.calledWith(ControlsOptionsUtil.hasHints, {
6101
6206
  requiredHints: [DISPLAY_HINTS.ENABLE_VIEW_THE_PARTICIPANT_LIST],
6102
- displayHints: payload.info.userDisplayHints,
6207
+ displayHints: userDisplayHints,
6103
6208
  });
6104
6209
  assert.calledWith(ControlsOptionsUtil.hasHints, {
6105
6210
  requiredHints: [DISPLAY_HINTS.DISABLE_VIEW_THE_PARTICIPANT_LIST],
6106
- displayHints: payload.info.userDisplayHints,
6211
+ displayHints: userDisplayHints,
6107
6212
  });
6108
6213
  assert.calledWith(ControlsOptionsUtil.hasHints, {
6109
6214
  requiredHints: [DISPLAY_HINTS.SHARE_FILE],
6110
- displayHints: payload.info.userDisplayHints,
6215
+ displayHints: userDisplayHints,
6111
6216
  });
6112
6217
  assert.calledWith(ControlsOptionsUtil.hasPolicies, {
6113
6218
  requiredPolicies: [SELF_POLICY.SUPPORT_FILE_SHARE],
6114
- policies: {a: true},
6219
+ policies: selfUserPolicies,
6115
6220
  });
6116
6221
  assert.calledWith(ControlsOptionsUtil.hasHints, {
6117
6222
  requiredHints: [DISPLAY_HINTS.SHARE_APPLICATION],
6118
- displayHints: payload.info.userDisplayHints,
6223
+ displayHints: userDisplayHints,
6119
6224
  });
6120
6225
  assert.calledWith(ControlsOptionsUtil.hasPolicies, {
6121
6226
  requiredPolicies: [SELF_POLICY.SUPPORT_APP_SHARE],
6122
- policies: {a: true},
6227
+ policies: selfUserPolicies,
6123
6228
  });
6124
6229
  assert.calledWith(ControlsOptionsUtil.hasHints, {
6125
6230
  requiredHints: [DISPLAY_HINTS.SHARE_CAMERA],
6126
- displayHints: payload.info.userDisplayHints,
6231
+ displayHints: userDisplayHints,
6127
6232
  });
6128
6233
  assert.calledWith(ControlsOptionsUtil.hasPolicies, {
6129
6234
  requiredPolicies: [SELF_POLICY.SUPPORT_CAMERA_SHARE],
6130
- policies: {a: true},
6235
+ policies: selfUserPolicies,
6131
6236
  });
6132
6237
  assert.calledWith(ControlsOptionsUtil.hasHints, {
6133
6238
  requiredHints: [DISPLAY_HINTS.SHARE_DESKTOP],
6134
- displayHints: payload.info.userDisplayHints,
6239
+ displayHints: userDisplayHints,
6135
6240
  });
6136
6241
  assert.calledWith(ControlsOptionsUtil.hasPolicies, {
6137
6242
  requiredPolicies: [SELF_POLICY.SUPPORT_DESKTOP_SHARE],
6138
- policies: {a: true},
6243
+ policies: selfUserPolicies,
6139
6244
  });
6140
6245
  assert.calledWith(ControlsOptionsUtil.hasHints, {
6141
6246
  requiredHints: [DISPLAY_HINTS.SHARE_CONTENT],
6142
- displayHints: payload.info.userDisplayHints,
6247
+ displayHints: userDisplayHints,
6248
+ });
6249
+ assert.calledWith(ControlsOptionsUtil.hasHints, {
6250
+ requiredHints: [DISPLAY_HINTS.VOIP_IS_ENABLED],
6251
+ displayHints: userDisplayHints,
6252
+ });
6253
+ assert.calledWith(ControlsOptionsUtil.hasPolicies, {
6254
+ requiredPolicies: [SELF_POLICY.SUPPORT_VOIP],
6255
+ policies: selfUserPolicies,
6143
6256
  });
6144
-
6145
- assert.calledWith(setUserPolicySpy, {a: true});
6146
6257
 
6147
6258
  assert.calledWith(
6148
6259
  TriggerProxy.trigger,
6149
6260
  meeting,
6150
6261
  {
6151
6262
  file: 'meeting/index',
6152
- function: 'setUpLocusInfoMeetingInfoListener',
6263
+ function: 'updateMeetingActions',
6153
6264
  },
6154
6265
  'meeting:actionsUpdate',
6155
6266
  meeting.inMeetingActions.get()
@@ -6157,7 +6268,7 @@ describe('plugin-meetings', () => {
6157
6268
 
6158
6269
  TriggerProxy.trigger.resetHistory();
6159
6270
 
6160
- callback(payload);
6271
+ meeting.updateMeetingActions();
6161
6272
 
6162
6273
  assert.notCalled(TriggerProxy.trigger);
6163
6274
 
@@ -6520,6 +6631,10 @@ describe('plugin-meetings', () => {
6520
6631
  endedSharingId: null,
6521
6632
  },
6522
6633
  },
6634
+ meeting: {
6635
+ eventName: EVENT_TRIGGERS.MEETING_LOCUS_URL_UPDATE,
6636
+ eventPayload: 'newLocusUrl',
6637
+ },
6523
6638
  };
6524
6639
 
6525
6640
  let shareStatus = null;
@@ -6743,7 +6858,7 @@ describe('plugin-meetings', () => {
6743
6858
  assert.equal(meeting.shareStatus, SHARE_STATUS.NO_SHARE);
6744
6859
 
6745
6860
  // Called once --> members:update (ignore)
6746
- let callCounter = 1;
6861
+ let callCounter = 2;
6747
6862
 
6748
6863
  data.forEach((d, index) => {
6749
6864
  meeting.locusInfo.emit(
@@ -6771,10 +6886,10 @@ describe('plugin-meetings', () => {
6771
6886
 
6772
6887
  assert.callCount(TriggerProxy.trigger, callCounter);
6773
6888
 
6774
- // Start with 1 to ignore members:update trigger
6889
+ // Start with 2 to ignore members:update trigger, and meeting:locus:locusUrl:update
6775
6890
 
6776
- let i = 1;
6777
- let offset = 2;
6891
+ let i = 2;
6892
+ let offset = 3;
6778
6893
 
6779
6894
  while (i < callCounter) {
6780
6895
  const index = Math.floor(i / offset);