@webex/plugin-meetings 3.9.0-multi-llms.4 → 3.9.0-multi-llms.5

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 (64) hide show
  1. package/dist/breakouts/breakout.js +1 -1
  2. package/dist/breakouts/index.js +1 -1
  3. package/dist/constants.js +4 -0
  4. package/dist/constants.js.map +1 -1
  5. package/dist/controls-options-manager/index.js +22 -5
  6. package/dist/controls-options-manager/index.js.map +1 -1
  7. package/dist/index.js +2 -1
  8. package/dist/index.js.map +1 -1
  9. package/dist/interceptors/index.js +7 -0
  10. package/dist/interceptors/index.js.map +1 -1
  11. package/dist/interceptors/locusRouteToken.js +116 -0
  12. package/dist/interceptors/locusRouteToken.js.map +1 -0
  13. package/dist/interpretation/index.js +1 -1
  14. package/dist/interpretation/siLanguage.js +1 -1
  15. package/dist/locus-info/controlsUtils.js +11 -2
  16. package/dist/locus-info/controlsUtils.js.map +1 -1
  17. package/dist/locus-info/index.js +17 -3
  18. package/dist/locus-info/index.js.map +1 -1
  19. package/dist/meeting/in-meeting-actions.js +2 -0
  20. package/dist/meeting/in-meeting-actions.js.map +1 -1
  21. package/dist/meeting/index.js +74 -32
  22. package/dist/meeting/index.js.map +1 -1
  23. package/dist/meeting/request.js +48 -14
  24. package/dist/meeting/request.js.map +1 -1
  25. package/dist/meeting/util.js +3 -0
  26. package/dist/meeting/util.js.map +1 -1
  27. package/dist/member/index.js +9 -0
  28. package/dist/member/index.js.map +1 -1
  29. package/dist/member/util.js +10 -0
  30. package/dist/member/util.js.map +1 -1
  31. package/dist/types/constants.d.ts +3 -0
  32. package/dist/types/controls-options-manager/index.d.ts +9 -1
  33. package/dist/types/interceptors/index.d.ts +2 -1
  34. package/dist/types/interceptors/locusRouteToken.d.ts +38 -0
  35. package/dist/types/locus-info/index.d.ts +2 -1
  36. package/dist/types/meeting/in-meeting-actions.d.ts +2 -0
  37. package/dist/types/meeting/index.d.ts +14 -0
  38. package/dist/types/meeting/request.d.ts +16 -0
  39. package/dist/types/meeting/util.d.ts +1 -0
  40. package/dist/types/member/index.d.ts +1 -0
  41. package/dist/types/member/util.d.ts +5 -0
  42. package/dist/webinar/index.js +1 -1
  43. package/package.json +13 -13
  44. package/src/constants.ts +6 -0
  45. package/src/controls-options-manager/index.ts +26 -5
  46. package/src/index.ts +2 -1
  47. package/src/interceptors/index.ts +2 -1
  48. package/src/interceptors/locusRouteToken.ts +80 -0
  49. package/src/locus-info/controlsUtils.ts +18 -0
  50. package/src/locus-info/index.ts +14 -4
  51. package/src/meeting/in-meeting-actions.ts +4 -0
  52. package/src/meeting/index.ts +67 -20
  53. package/src/meeting/request.ts +38 -0
  54. package/src/meeting/util.ts +3 -0
  55. package/src/member/index.ts +10 -0
  56. package/src/member/util.ts +14 -0
  57. package/test/unit/spec/controls-options-manager/index.js +47 -0
  58. package/test/unit/spec/fixture/locus.js +1 -0
  59. package/test/unit/spec/interceptors/locusRouteToken.ts +87 -0
  60. package/test/unit/spec/locus-info/index.js +39 -0
  61. package/test/unit/spec/meeting/in-meeting-actions.ts +2 -0
  62. package/test/unit/spec/meeting/index.js +41 -2
  63. package/test/unit/spec/meeting/utils.js +1 -0
  64. package/test/unit/spec/member/util.js +24 -0
@@ -343,7 +343,7 @@ export default class LocusInfo extends EventsScope {
343
343
  // For 1:1 space meeting the conversation Url does not exist in locus.conversation
344
344
  this.updateConversationUrl(locus.conversationUrl, locus.info);
345
345
  this.updateControls(locus.controls, locus.self);
346
- this.updateLocusUrl(locus.url);
346
+ this.updateLocusUrl(locus.url, ControlsUtils.isMainSessionDTO(locus));
347
347
  this.updateFullState(locus.fullState);
348
348
  this.updateMeetingInfo(locus.info);
349
349
  this.updateEmbeddedApps(locus.embeddedApps);
@@ -549,7 +549,7 @@ export default class LocusInfo extends EventsScope {
549
549
  this.updateCreated(locus.created);
550
550
  this.updateFullState(locus.fullState);
551
551
  this.updateHostInfo(locus.host);
552
- this.updateLocusUrl(locus.url);
552
+ this.updateLocusUrl(locus.url, ControlsUtils.isMainSessionDTO(locus));
553
553
  this.updateMeetingInfo(locus.info, locus.self);
554
554
  this.updateMediaShares(locus.mediaShares);
555
555
  this.updateParticipantsUrl(locus.participantsUrl);
@@ -895,6 +895,7 @@ export default class LocusInfo extends EventsScope {
895
895
  hasAnnotationControlChanged,
896
896
  hasRemoteDesktopControlChanged,
897
897
  hasPollingQAControlChanged,
898
+ hasAutoEndMeetingChanged,
898
899
  },
899
900
  current,
900
901
  } = ControlsUtils.getControls(this.controls, controls);
@@ -1169,6 +1170,14 @@ export default class LocusInfo extends EventsScope {
1169
1170
  );
1170
1171
  }
1171
1172
 
1173
+ if (hasAutoEndMeetingChanged) {
1174
+ this.emitScoped(
1175
+ {file: 'locus-info', function: 'updateControls'},
1176
+ LOCUSINFO.EVENTS.CONTROLS_AUTO_END_MEETING_WARNING_CHANGED,
1177
+ {state: current.autoEndMeetingWarning}
1178
+ );
1179
+ }
1180
+
1172
1181
  this.controls = controls;
1173
1182
  }
1174
1183
  }
@@ -1732,10 +1741,11 @@ export default class LocusInfo extends EventsScope {
1732
1741
  /**
1733
1742
  * handles when the locus.url is updated
1734
1743
  * @param {String} url
1744
+ * @param {Boolean} isMainLocus
1735
1745
  * @returns {undefined}
1736
1746
  * emits internal event locus_info_update_url
1737
1747
  */
1738
- updateLocusUrl(url: string) {
1748
+ updateLocusUrl(url: string, isMainLocus = true) {
1739
1749
  if (url && this.url !== url) {
1740
1750
  this.url = url;
1741
1751
  this.updateMeeting({locusUrl: url});
@@ -1745,7 +1755,7 @@ export default class LocusInfo extends EventsScope {
1745
1755
  function: 'updateLocusUrl',
1746
1756
  },
1747
1757
  EVENTS.LOCUS_INFO_UPDATE_URL,
1748
- url
1758
+ {url, isMainLocus}
1749
1759
  );
1750
1760
  }
1751
1761
  }
@@ -92,6 +92,7 @@ interface IInMeetingActions {
92
92
  canDoVideo?: boolean;
93
93
  canAnnotate?: boolean;
94
94
  canUseVoip?: boolean;
95
+ showAutoEndMeetingWarning?: boolean;
95
96
  supportHQV?: boolean;
96
97
  supportHDV?: boolean;
97
98
  canShareWhiteBoard?: boolean;
@@ -284,6 +285,8 @@ export default class InMeetingActions implements IInMeetingActions {
284
285
 
285
286
  canUseVoip = null;
286
287
 
288
+ showAutoEndMeetingWarning = null;
289
+
287
290
  supportHQV = null;
288
291
 
289
292
  enforceVirtualBackground = null;
@@ -405,6 +408,7 @@ export default class InMeetingActions implements IInMeetingActions {
405
408
  canShareFile: this.canShareFile,
406
409
  canShareApplication: this.canShareApplication,
407
410
  canShareCamera: this.canShareCamera,
411
+ showAutoEndMeetingWarning: this.showAutoEndMeetingWarning,
408
412
  canShareDesktop: this.canShareDesktop,
409
413
  canShareContent: this.canShareContent,
410
414
  canTransferFile: this.canTransferFile,
@@ -2976,6 +2976,18 @@ export default class Meeting extends StatelessWebexPlugin {
2976
2976
  );
2977
2977
  });
2978
2978
 
2979
+ this.locusInfo.on(LOCUSINFO.EVENTS.CONTROLS_AUTO_END_MEETING_WARNING_CHANGED, ({state}) => {
2980
+ Trigger.trigger(
2981
+ this,
2982
+ {
2983
+ file: 'meeting/index',
2984
+ function: 'setupLocusControlsListener',
2985
+ },
2986
+ EVENT_TRIGGERS.MEETING_CONTROLS_AUTO_END_MEETING_WARNING_UPDATED,
2987
+ {state}
2988
+ );
2989
+ });
2990
+
2979
2991
  this.locusInfo.on(LOCUSINFO.EVENTS.CONTROLS_ANNOTATION_CHANGED, ({state}) => {
2980
2992
  Trigger.trigger(
2981
2993
  this,
@@ -3356,27 +3368,31 @@ export default class Meeting extends StatelessWebexPlugin {
3356
3368
  * @memberof Meeting
3357
3369
  */
3358
3370
  private setUpLocusUrlListener() {
3359
- this.locusInfo.on(EVENTS.LOCUS_INFO_UPDATE_URL, (payload) => {
3360
- this.members.locusUrlUpdate(payload);
3361
- this.breakouts.locusUrlUpdate(payload);
3362
- this.simultaneousInterpretation.locusUrlUpdate(payload);
3363
- this.annotation.locusUrlUpdate(payload);
3364
- this.locusUrl = payload;
3365
- this.locusId = this.locusUrl?.split('/').pop();
3366
- this.recordingController.setLocusUrl(this.locusUrl);
3367
- this.controlsOptionsManager.setLocusUrl(this.locusUrl);
3368
- this.webinar.locusUrlUpdate(payload);
3371
+ this.locusInfo.on(
3372
+ EVENTS.LOCUS_INFO_UPDATE_URL,
3373
+ (payload: {url: string; isMainLocus?: boolean}) => {
3374
+ const {url, isMainLocus} = payload;
3375
+ this.members.locusUrlUpdate(url);
3376
+ this.breakouts.locusUrlUpdate(url);
3377
+ this.simultaneousInterpretation.locusUrlUpdate(url);
3378
+ this.annotation.locusUrlUpdate(url);
3379
+ this.locusUrl = url;
3380
+ this.locusId = this.locusUrl?.split('/').pop();
3381
+ this.recordingController.setLocusUrl(this.locusUrl);
3382
+ this.controlsOptionsManager.setLocusUrl(this.locusUrl, !!isMainLocus);
3383
+ this.webinar.locusUrlUpdate(url);
3369
3384
 
3370
- Trigger.trigger(
3371
- this,
3372
- {
3373
- file: 'meeting/index',
3374
- function: 'setUpLocusSelfListener',
3375
- },
3376
- EVENT_TRIGGERS.MEETING_LOCUS_URL_UPDATE,
3377
- {locusUrl: payload}
3378
- );
3379
- });
3385
+ Trigger.trigger(
3386
+ this,
3387
+ {
3388
+ file: 'meeting/index',
3389
+ function: 'setUpLocusSelfListener',
3390
+ },
3391
+ EVENT_TRIGGERS.MEETING_LOCUS_URL_UPDATE,
3392
+ {locusUrl: url}
3393
+ );
3394
+ }
3395
+ );
3380
3396
  }
3381
3397
 
3382
3398
  /**
@@ -4219,6 +4235,7 @@ export default class Meeting extends StatelessWebexPlugin {
4219
4235
  this.userDisplayHints,
4220
4236
  this.selfUserPolicies
4221
4237
  ),
4238
+ showAutoEndMeetingWarning: MeetingUtil.showAutoEndMeetingWarning(this.userDisplayHints),
4222
4239
  canRaiseHand: MeetingUtil.canUserRaiseHand(this.userDisplayHints),
4223
4240
  canLowerAllHands: MeetingUtil.canUserLowerAllHands(this.userDisplayHints),
4224
4241
  canLowerSomeoneElsesHand: MeetingUtil.canUserLowerSomeoneElsesHand(this.userDisplayHints),
@@ -9449,6 +9466,36 @@ export default class Meeting extends StatelessWebexPlugin {
9449
9466
  return Promise.reject(new Error('Error sending reaction, service url not found.'));
9450
9467
  }
9451
9468
 
9469
+ /**
9470
+ * Extend the current meeting duration.
9471
+ *
9472
+ * @param {number} extensionMinutes - how many minutes to extend
9473
+ * @returns {Promise}
9474
+ * @public
9475
+ * @memberof Meeting
9476
+ */
9477
+ public extendMeeting({
9478
+ meetingPolicyUrl,
9479
+ meetingInstanceId,
9480
+ participantId,
9481
+ extensionMinutes = 30,
9482
+ }) {
9483
+ if (!meetingInstanceId || !participantId) {
9484
+ return Promise.reject(new Error('Missing meetingInstanceId or participantId'));
9485
+ }
9486
+
9487
+ if (!meetingPolicyUrl) {
9488
+ return Promise.reject(new Error('Missing meetingPolicyUrl'));
9489
+ }
9490
+
9491
+ return this.meetingRequest.extendMeeting({
9492
+ meetingInstanceId,
9493
+ participantId,
9494
+ extensionMinutes,
9495
+ meetingPolicyUrl,
9496
+ });
9497
+ }
9498
+
9452
9499
  /**
9453
9500
  * Method to enable or disable reactions inside the meeting.
9454
9501
  *
@@ -886,6 +886,44 @@ export default class MeetingRequest extends StatelessWebexPlugin {
886
886
  });
887
887
  }
888
888
 
889
+ /**
890
+ * Extend the current meeting duration.
891
+ *
892
+ * @param {Object} params - Parameters for extending the meeting.
893
+ * @param {string} params.meetingInstanceId - The unique ID of the meeting instance.
894
+ * @param {string} params.participantId - The ID of the participant requesting the extension.
895
+ * @param {number} params.extensionMinutes - The number of minutes to extend the meeting by.
896
+ * @param {string} params.meetingPolicyUrl - The base URL for meeting policy service (dynamic, from locus links)
897
+ * @returns {Promise<any>} A promise that resolves with the server response.
898
+ */
899
+ extendMeeting({
900
+ meetingInstanceId,
901
+ participantId,
902
+ extensionMinutes,
903
+ meetingPolicyUrl,
904
+ }: {
905
+ meetingInstanceId: string;
906
+ participantId: string;
907
+ extensionMinutes: number;
908
+ meetingPolicyUrl: string;
909
+ }) {
910
+ if (!meetingPolicyUrl) {
911
+ return Promise.reject(new Error('meetingPolicyUrl is required'));
912
+ }
913
+ const uri = `${meetingPolicyUrl}/continueMeeting`;
914
+
915
+ // @ts-ignore
916
+ return this.request({
917
+ method: HTTP_VERBS.POST,
918
+ uri,
919
+ body: {
920
+ meetingInstanceId,
921
+ requestParticipantId: participantId,
922
+ extensionMinutes,
923
+ },
924
+ });
925
+ }
926
+
889
927
  /**
890
928
  * Make a network request to enable or disable reactions.
891
929
  * @param {boolean} options.enable - determines if we need to enable or disable.
@@ -654,6 +654,9 @@ const MeetingUtil = {
654
654
 
655
655
  waitingForOthersToJoin: (displayHints) => displayHints.includes(DISPLAY_HINTS.WAITING_FOR_OTHERS),
656
656
 
657
+ showAutoEndMeetingWarning: (displayHints) =>
658
+ displayHints.includes(DISPLAY_HINTS.SHOW_AUTO_END_MEETING_WARNING),
659
+
657
660
  canSendReactions: (originalValue, displayHints) => {
658
661
  if (displayHints.includes(DISPLAY_HINTS.REACTIONS_ACTIVE)) {
659
662
  return true;
@@ -42,6 +42,7 @@ export default class Member {
42
42
  status: any;
43
43
  supportsBreakouts: boolean;
44
44
  supportsInterpretation: boolean;
45
+ supportsSingleUserAutoEndMeeting: boolean;
45
46
  supportLiveAnnotation: boolean;
46
47
  type: any;
47
48
  namespace = MEETINGS;
@@ -130,6 +131,13 @@ export default class Member {
130
131
  * @memberof Member
131
132
  */
132
133
  this.supportsBreakouts = null;
134
+ /**
135
+ * @instance
136
+ * @type {Boolean}
137
+ * @public
138
+ * @memberof Member
139
+ */
140
+ this.supportsSingleUserAutoEndMeeting = null;
133
141
  /**
134
142
  * @instance
135
143
  * @type {Boolean}
@@ -339,6 +347,8 @@ export default class Member {
339
347
  this.isVideoMuted = MemberUtil.isVideoMuted(participant);
340
348
  this.isHandRaised = MemberUtil.isHandRaised(participant);
341
349
  this.supportsBreakouts = MemberUtil.isBreakoutsSupported(participant);
350
+ this.supportsSingleUserAutoEndMeeting =
351
+ MemberUtil.isSupportsSingleUserAutoEndMeeting(participant);
342
352
  this.supportsInterpretation = MemberUtil.isInterpretationSupported(participant);
343
353
  this.supportLiveAnnotation = MemberUtil.isLiveAnnotationSupported(participant);
344
354
  this.isGuest = MemberUtil.isGuest(participant);
@@ -207,6 +207,20 @@ const MemberUtil = {
207
207
  return !participant.doesNotSupportBreakouts;
208
208
  },
209
209
 
210
+ /**
211
+ * @param {Object} participant - The locus participant object.
212
+ * @returns {Boolean}
213
+ */
214
+ isSupportsSingleUserAutoEndMeeting: (participant) => {
215
+ if (!participant) {
216
+ throw new ParameterError(
217
+ 'Single user auto end meeting support could not be processed, participant is undefined.'
218
+ );
219
+ }
220
+
221
+ return !participant.doesNotSupportSingleUserAutoEndMeeting;
222
+ },
223
+
210
224
  /**
211
225
  * @param {Object} participant - The locus participant object.
212
226
  * @returns {Boolean}
@@ -133,6 +133,7 @@ describe('plugin-meetings', () => {
133
133
 
134
134
  manager.set({
135
135
  locusUrl: 'test/id',
136
+ mainLocusUrl: '',
136
137
  displayHints: [],
137
138
  });
138
139
  });
@@ -201,6 +202,38 @@ describe('plugin-meetings', () => {
201
202
  Util.canUpdate = restorable;
202
203
  });
203
204
  });
205
+
206
+ it('should call request with mainLocusUrl and locusUrl as authorizingLocusUrl if mainLocusUrl is exist and not same with locusUrl', () => {
207
+ const restorable = Util.canUpdate;
208
+ Util.canUpdate = sinon.stub().returns(true);
209
+ manager.mainLocusUrl = 'test/main';
210
+
211
+ const audio = {scope: 'audio', properties: {a: 1, b: 2}};
212
+ const reactions = {scope: 'reactions', properties: {c: 3, d: 4}};
213
+
214
+ return manager.update(audio, reactions)
215
+ .then(() => {
216
+ assert.calledWith(request.request, {
217
+ uri: 'test/main/controls',
218
+ body: {
219
+ audio: audio.properties,
220
+ authorizingLocusUrl: 'test/id'
221
+ },
222
+ method: HTTP_VERBS.PATCH,
223
+ });
224
+
225
+ assert.calledWith(request.request, {
226
+ uri: 'test/main/controls',
227
+ body: {
228
+ reactions: reactions.properties,
229
+ authorizingLocusUrl: 'test/id'
230
+ },
231
+ method: HTTP_VERBS.PATCH,
232
+ });
233
+
234
+ Util.canUpdate = restorable;
235
+ });
236
+ });
204
237
  });
205
238
 
206
239
  describe('Mute/Unmute All', () => {
@@ -214,6 +247,7 @@ describe('plugin-meetings', () => {
214
247
 
215
248
  manager.set({
216
249
  locusUrl: 'test/id',
250
+ mainLocusUrl: '',
217
251
  displayHints: [],
218
252
  })
219
253
  });
@@ -305,6 +339,19 @@ describe('plugin-meetings', () => {
305
339
 
306
340
  assert.deepEqual(result, request.request.firstCall.returnValue);
307
341
  });
342
+
343
+ it('request with mainLocusUrl and make locusUrl as authorizingLocusUrl if mainLocusUrl is exist and not same with locusUrl', () => {
344
+ manager.setDisplayHints(['MUTE_ALL', 'DISABLE_HARD_MUTE', 'DISABLE_MUTE_ON_ENTRY']);
345
+ manager.mainLocusUrl = `test/main`;
346
+
347
+ const result = manager.setMuteAll(true, true, true, ['attendee']);
348
+
349
+ assert.calledWith(request.request, { uri: 'test/main/controls',
350
+ body: { audio: { muted: true, disallowUnmute: true, muteOnEntry: true, roles: ['attendee'] }, authorizingLocusUrl: 'test/id' },
351
+ method: HTTP_VERBS.PATCH});
352
+
353
+ assert.deepEqual(result, request.request.firstCall.returnValue);
354
+ });
308
355
  });
309
356
  });
310
357
  });
@@ -36,6 +36,7 @@ export default {
36
36
  'LOCK_STATUS_UNLOCKED',
37
37
  'ROSTER_IN_MEETING',
38
38
  'SHARE_WHITEBOARD',
39
+ 'SHOW_AUTO_END_MEETING_WARNING',
39
40
  'SHARE_WHITEBOARD_POLICY',
40
41
  'WEBEX_ASSISTANT_STATUS_INACTIVE',
41
42
  'CAPTION_START',
@@ -0,0 +1,87 @@
1
+ /*!
2
+ * Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.
3
+ */
4
+
5
+ /* eslint-disable camelcase */
6
+ import 'jsdom-global/register';
7
+ import {assert} from '@webex/test-helper-chai';
8
+ import MockWebex from '@webex/test-helper-mock-webex';
9
+ import {LocusRouteTokenInterceptor} from '@webex/plugin-meetings/src/interceptors';
10
+ import Meetings from '@webex/plugin-meetings';
11
+
12
+ const X_CISCO_PART_ROUTE_TOKEN = 'X-Cisco-Part-Route-Token';
13
+
14
+ describe('LocusRouteTokenInterceptor', () => {
15
+ let interceptor, webex;
16
+ const TEST_LOCUS_ID = '0f1eba56-91e2-2a11-9b2b-1e2da077f066';
17
+ beforeEach(() => {
18
+ webex = new MockWebex({
19
+ children: {
20
+ meetings: Meetings,
21
+ },
22
+ });
23
+ interceptor = Reflect.apply(LocusRouteTokenInterceptor.create, webex, []);
24
+ });
25
+
26
+ it('getLocusIdByRequestUrl should return locusId from url', () => {
27
+ const url = `https://locus-test.webex.com/locus/api/v1/loci/${TEST_LOCUS_ID}/foo`;
28
+ assert.equal(interceptor.getLocusIdByRequestUrl(url), TEST_LOCUS_ID);
29
+ });
30
+
31
+ it('getLocusIdByRequestUrl should return undefined when no locusId in url', () => {
32
+ const url = 'https://locus-test.webex.com/locus/api/v1/foo';
33
+ assert.isUndefined(interceptor.getLocusIdByRequestUrl(url));
34
+ });
35
+
36
+ it('getLocusIdByRequestUrl should return undefined when url is undefined', () => {
37
+ assert.isUndefined(interceptor.getLocusIdByRequestUrl(undefined));
38
+ });
39
+
40
+ it('onResponse should store route token when header exists', async () => {
41
+ const response = {
42
+ headers: {
43
+ [X_CISCO_PART_ROUTE_TOKEN]: 'test-token',
44
+ },
45
+ };
46
+
47
+ const result = await interceptor.onResponse(
48
+ {
49
+ uri: `https://locus-test.webex.com/locus/api/v1/loci/${TEST_LOCUS_ID}/foo`,
50
+ },
51
+ response
52
+ );
53
+ assert.equal(result, response);
54
+ assert.equal(interceptor.getToken(TEST_LOCUS_ID), 'test-token');
55
+ });
56
+
57
+ it('onResponse should not store token when header missing', async () => {
58
+ interceptor.updateToken(TEST_LOCUS_ID);
59
+ const response = {headers: {}};
60
+
61
+ await interceptor.onResponse({}, response);
62
+ assert.isUndefined(interceptor.getToken(TEST_LOCUS_ID));
63
+ });
64
+
65
+ it('onRequest should attach token to headers when token exists', async () => {
66
+ interceptor.updateToken(TEST_LOCUS_ID, 'abc123');
67
+
68
+ const options = {
69
+ headers: {},
70
+ uri: `https://locus-test.webex.com/locus/api/v1/loci/${TEST_LOCUS_ID}/foo`,
71
+ };
72
+ const result = await interceptor.onRequest(options);
73
+ assert.equal(result.headers[X_CISCO_PART_ROUTE_TOKEN], 'abc123');
74
+ });
75
+
76
+ it('onRequest should not attach token if none is stored', async () => {
77
+ interceptor.updateToken(TEST_LOCUS_ID);
78
+ const options = {headers: {}};
79
+ const result = await interceptor.onRequest(options);
80
+ assert.isUndefined(result.headers[X_CISCO_PART_ROUTE_TOKEN]);
81
+ });
82
+
83
+ it('updateToken & getToken should work as pair', () => {
84
+ interceptor.updateToken(TEST_LOCUS_ID, 'abc456');
85
+ assert.equal(interceptor.getToken(TEST_LOCUS_ID), 'abc456');
86
+ });
87
+ });
@@ -3020,6 +3020,45 @@ describe('plugin-meetings', () => {
3020
3020
  });
3021
3021
  });
3022
3022
 
3023
+ describe('#updateLocusUrl', () => {
3024
+ it('trigger LOCUS_INFO_UPDATE_URL event with isMainLocus is true as default', () => {
3025
+ const fakeUrl = "https://fake.com/locus";
3026
+ locusInfo.emitScoped = sinon.stub();
3027
+ locusInfo.updateLocusUrl(fakeUrl);
3028
+
3029
+ assert.calledWith(
3030
+ locusInfo.emitScoped,
3031
+ {
3032
+ file: 'locus-info',
3033
+ function: 'updateLocusUrl',
3034
+ },
3035
+ EVENTS.LOCUS_INFO_UPDATE_URL,
3036
+ {
3037
+ url: fakeUrl,
3038
+ isMainLocus: true
3039
+ },
3040
+ );
3041
+ });
3042
+ it('trigger LOCUS_INFO_UPDATE_URL event with isMainLocus is false', () => {
3043
+ const fakeUrl = "https://fake.com/locus";
3044
+ locusInfo.emitScoped = sinon.stub();
3045
+ locusInfo.updateLocusUrl(fakeUrl, false);
3046
+
3047
+ assert.calledWith(
3048
+ locusInfo.emitScoped,
3049
+ {
3050
+ file: 'locus-info',
3051
+ function: 'updateLocusUrl',
3052
+ },
3053
+ EVENTS.LOCUS_INFO_UPDATE_URL,
3054
+ {
3055
+ url: fakeUrl,
3056
+ isMainLocus: false
3057
+ },
3058
+ );
3059
+ });
3060
+ });
3061
+
3023
3062
  // semi-integration tests that use real LocusInfo with real Parser
3024
3063
  // and test various scenarios related to handling out-of-order Locus delta events
3025
3064
  describe('handling of out-of-order Locus delta events', () => {
@@ -86,6 +86,7 @@ describe('plugin-meetings', () => {
86
86
  canDoVideo: null,
87
87
  canAnnotate: null,
88
88
  canUseVoip: null,
89
+ showAutoEndMeetingWarning: null,
89
90
  supportHQV: null,
90
91
  supportHDV: null,
91
92
  canShareWhiteBoard: null,
@@ -197,6 +198,7 @@ describe('plugin-meetings', () => {
197
198
  'canRealtimeCloseCaption',
198
199
  'canRealtimeCloseCaptionManual',
199
200
  'canChat',
201
+ 'showAutoEndMeetingWarning',
200
202
  'canDoVideo',
201
203
  'canAnnotate',
202
204
  'canUseVoip',
@@ -10471,6 +10471,24 @@ describe('plugin-meetings', () => {
10471
10471
  );
10472
10472
  });
10473
10473
 
10474
+ it('listens to CONTROLS_AUTO_END_MEETING_WARNING_CHANGED', async () => {
10475
+ const state = {example: 'value'};
10476
+
10477
+ await meeting.locusInfo.emitScoped(
10478
+ {function: 'test', file: 'test'},
10479
+ LOCUSINFO.EVENTS.CONTROLS_AUTO_END_MEETING_WARNING_CHANGED,
10480
+ {state}
10481
+ );
10482
+
10483
+ assert.calledWith(
10484
+ TriggerProxy.trigger,
10485
+ meeting,
10486
+ {file: 'meeting/index', function: 'setupLocusControlsListener'},
10487
+ EVENT_TRIGGERS.MEETING_CONTROLS_AUTO_END_MEETING_WARNING_UPDATED,
10488
+ {state}
10489
+ );
10490
+ });
10491
+
10474
10492
  it('listens to CONTROLS_REMOTE_DESKTOP_CONTROL_CHANGED', async () => {
10475
10493
  const state = {example: 'value'};
10476
10494
 
@@ -10550,6 +10568,7 @@ describe('plugin-meetings', () => {
10550
10568
  describe('#setUpLocusUrlListener', () => {
10551
10569
  it('listens to the locus url update event', (done) => {
10552
10570
  const newLocusUrl = 'newLocusUrl/12345';
10571
+ const payload = {url: newLocusUrl}
10553
10572
 
10554
10573
  meeting.members = {locusUrlUpdate: sinon.stub().returns(Promise.resolve(test1))};
10555
10574
  meeting.recordingController = {setLocusUrl: sinon.stub().returns(undefined)};
@@ -10563,14 +10582,14 @@ describe('plugin-meetings', () => {
10563
10582
  meeting.locusInfo.emit(
10564
10583
  {function: 'test', file: 'test'},
10565
10584
  'LOCUS_INFO_UPDATE_URL',
10566
- newLocusUrl
10585
+ payload
10567
10586
  );
10568
10587
  assert.calledWith(meeting.members.locusUrlUpdate, newLocusUrl);
10569
10588
  assert.calledOnceWithExactly(meeting.breakouts.locusUrlUpdate, newLocusUrl);
10570
10589
  assert.calledOnceWithExactly(meeting.annotation.locusUrlUpdate, newLocusUrl);
10571
10590
  assert.calledWith(meeting.members.locusUrlUpdate, newLocusUrl);
10572
10591
  assert.calledWith(meeting.recordingController.setLocusUrl, newLocusUrl);
10573
- assert.calledWith(meeting.controlsOptionsManager.setLocusUrl, newLocusUrl);
10592
+ assert.calledWith(meeting.controlsOptionsManager.setLocusUrl, newLocusUrl, false);
10574
10593
  assert.calledWith(meeting.simultaneousInterpretation.locusUrlUpdate, newLocusUrl);
10575
10594
  assert.calledWith(meeting.webinar.locusUrlUpdate, newLocusUrl);
10576
10595
  assert.equal(meeting.locusUrl, newLocusUrl);
@@ -10588,6 +10607,22 @@ describe('plugin-meetings', () => {
10588
10607
  {locusUrl: 'newLocusUrl/12345'}
10589
10608
  );
10590
10609
 
10610
+ done();
10611
+ });
10612
+ it('update mainLocusUrl for controlsOptionManager if payload.isMainLocus as true', (done) => {
10613
+ const newLocusUrl = 'newLocusUrl/12345';
10614
+ const payload = {url: newLocusUrl, isMainLocus: true}
10615
+
10616
+ meeting.controlsOptionsManager = {setLocusUrl: sinon.stub().returns(undefined)};
10617
+
10618
+ meeting.locusInfo.emit(
10619
+ {function: 'test', file: 'test'},
10620
+ 'LOCUS_INFO_UPDATE_URL',
10621
+ payload
10622
+ );
10623
+
10624
+ assert.calledWith(meeting.controlsOptionsManager.setLocusUrl, newLocusUrl, true);
10625
+
10591
10626
  done();
10592
10627
  });
10593
10628
  });
@@ -11426,6 +11461,7 @@ describe('plugin-meetings', () => {
11426
11461
  let canShareWhiteBoardSpy;
11427
11462
  let canMoveToLobbySpy;
11428
11463
  let isSpokenLanguageAutoDetectionEnabledSpy;
11464
+ let showAutoEndMeetingWarningSpy;
11429
11465
  // Due to import tree issues, hasHints must be stubed within the scope of the `it`.
11430
11466
 
11431
11467
  beforeEach(() => {
@@ -11457,6 +11493,7 @@ describe('plugin-meetings', () => {
11457
11493
  canUserRenameOthersSpy = sinon.spy(MeetingUtil, 'canUserRenameOthers');
11458
11494
  canShareWhiteBoardSpy = sinon.spy(MeetingUtil, 'canShareWhiteBoard');
11459
11495
  canMoveToLobbySpy = sinon.spy(MeetingUtil, 'canMoveToLobby');
11496
+ showAutoEndMeetingWarningSpy = sinon.spy(MeetingUtil, 'showAutoEndMeetingWarning');
11460
11497
  isSpokenLanguageAutoDetectionEnabledSpy = sinon.spy(MeetingUtil, 'isSpokenLanguageAutoDetectionEnabled');
11461
11498
 
11462
11499
  });
@@ -11464,6 +11501,7 @@ describe('plugin-meetings', () => {
11464
11501
  afterEach(() => {
11465
11502
  inMeetingActionsSetSpy.restore();
11466
11503
  waitingForOthersToJoinSpy.restore();
11504
+ showAutoEndMeetingWarningSpy.restore();
11467
11505
  });
11468
11506
 
11469
11507
  forEach(
@@ -12011,6 +12049,7 @@ describe('plugin-meetings', () => {
12011
12049
  assert.calledWith(canUserRenameOthersSpy, userDisplayHints);
12012
12050
  assert.calledWith(canShareWhiteBoardSpy, userDisplayHints, selfUserPolicies);
12013
12051
  assert.calledWith(canMoveToLobbySpy, userDisplayHints);
12052
+ assert.calledWith(showAutoEndMeetingWarningSpy, userDisplayHints);
12014
12053
  assert.calledWith(isSpokenLanguageAutoDetectionEnabledSpy, userDisplayHints);
12015
12054
 
12016
12055
  assert.calledWith(ControlsOptionsUtil.hasHints, {
@@ -985,6 +985,7 @@ describe('plugin-meetings', () => {
985
985
  {functionName: 'isRealTimeTranslationEnabled', displayHint: 'DISPLAY_REAL_TIME_TRANSLATION'},
986
986
  {functionName: 'canSelectSpokenLanguages', displayHint: 'DISPLAY_NON_ENGLISH_ASR'},
987
987
  {functionName: 'waitingForOthersToJoin', displayHint: 'WAITING_FOR_OTHERS'},
988
+ {functionName: 'showAutoEndMeetingWarning', displayHint: 'SHOW_AUTO_END_MEETING_WARNING'},
988
989
  ].forEach(({functionName, displayHint}) => {
989
990
  describe(functionName, () => {
990
991
  it('works as expected', () => {