@webex/plugin-meetings 3.8.0-next.38 → 3.8.0-next.39

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 (38) hide show
  1. package/dist/breakouts/breakout.js +1 -1
  2. package/dist/breakouts/index.js +1 -1
  3. package/dist/constants.js +11 -1
  4. package/dist/constants.js.map +1 -1
  5. package/dist/controls-options-manager/enums.js +2 -0
  6. package/dist/controls-options-manager/enums.js.map +1 -1
  7. package/dist/controls-options-manager/types.js.map +1 -1
  8. package/dist/controls-options-manager/util.js +52 -0
  9. package/dist/controls-options-manager/util.js.map +1 -1
  10. package/dist/interpretation/index.js +1 -1
  11. package/dist/interpretation/siLanguage.js +1 -1
  12. package/dist/locus-info/controlsUtils.js +14 -2
  13. package/dist/locus-info/controlsUtils.js.map +1 -1
  14. package/dist/locus-info/index.js +18 -0
  15. package/dist/locus-info/index.js.map +1 -1
  16. package/dist/meeting/in-meeting-actions.js +9 -1
  17. package/dist/meeting/in-meeting-actions.js.map +1 -1
  18. package/dist/meeting/index.js +42 -8
  19. package/dist/meeting/index.js.map +1 -1
  20. package/dist/types/constants.d.ts +8 -0
  21. package/dist/types/controls-options-manager/enums.d.ts +3 -1
  22. package/dist/types/controls-options-manager/types.d.ts +7 -1
  23. package/dist/types/meeting/in-meeting-actions.d.ts +8 -0
  24. package/dist/webinar/index.js +1 -1
  25. package/package.json +3 -3
  26. package/src/constants.ts +13 -0
  27. package/src/controls-options-manager/enums.ts +2 -0
  28. package/src/controls-options-manager/types.ts +11 -1
  29. package/src/controls-options-manager/util.ts +62 -0
  30. package/src/locus-info/controlsUtils.ts +18 -0
  31. package/src/locus-info/index.ts +18 -0
  32. package/src/meeting/in-meeting-actions.ts +16 -0
  33. package/src/meeting/index.ts +34 -0
  34. package/test/unit/spec/controls-options-manager/util.js +120 -0
  35. package/test/unit/spec/locus-info/controlsUtils.js +46 -12
  36. package/test/unit/spec/locus-info/index.js +28 -0
  37. package/test/unit/spec/meeting/in-meeting-actions.ts +8 -1
  38. package/test/unit/spec/meeting/index.js +52 -0
@@ -275,6 +275,8 @@ export declare const EVENT_TRIGGERS: {
275
275
  MEETING_CONTROLS_WEBCAST_UPDATED: string;
276
276
  MEETING_CONTROLS_MEETING_FULL_UPDATED: string;
277
277
  MEETING_CONTROLS_PRACTICE_SESSION_STATUS_UPDATED: string;
278
+ MEETING_CONTROLS_ANNOTATION_UPDATED: string;
279
+ MEETING_CONTROLS_REMOTE_DESKTOP_CONTROL_UPDATED: string;
278
280
  MEETING_LOCUS_URL_UPDATE: string;
279
281
  MEETING_STREAM_PUBLISH_STATE_CHANGED: string;
280
282
  MEETING_TRANSCRIPTION_CONNECTED: string;
@@ -578,6 +580,8 @@ export declare const LOCUSINFO: {
578
580
  CONTROLS_PRACTICE_SESSION_STATUS_UPDATED: string;
579
581
  CONTROLS_VIDEO_CHANGED: string;
580
582
  CONTROLS_STAGE_VIEW_UPDATED: string;
583
+ CONTROLS_ANNOTATION_CHANGED: string;
584
+ CONTROLS_REMOTE_DESKTOP_CONTROL_CHANGED: string;
581
585
  SELF_UNADMITTED_GUEST: string;
582
586
  SELF_ADMITTED_GUEST: string;
583
587
  SELF_REMOTE_VIDEO_MUTE_STATUS_UPDATED: string;
@@ -839,6 +843,10 @@ export declare const DISPLAY_HINTS: {
839
843
  PRACTICE_SESSION_OFF: string;
840
844
  SHOW_PRACTICE_SESSION_START: string;
841
845
  SHOW_PRACTICE_SESSION_STOP: string;
846
+ ENABLE_ANNOTATION_MEETING_OPTION: string;
847
+ DISABLE_ANNOTATION_MEETING_OPTION: string;
848
+ ENABLE_RDC_MEETING_OPTION: string;
849
+ DISABLE_RDC_MEETING_OPTION: string;
842
850
  };
843
851
  export declare const INTERSTITIAL_DISPLAY_HINTS: string[];
844
852
  export declare const SELF_ROLES: {
@@ -10,7 +10,9 @@ declare enum Control {
10
10
  reactions = "reactions",
11
11
  shareControl = "shareControl",
12
12
  video = "video",
13
- viewTheParticipantList = "viewTheParticipantList"
13
+ viewTheParticipantList = "viewTheParticipantList",
14
+ annotation = "annotation",
15
+ rdc = "rdc"
14
16
  }
15
17
  export { Control, Setting };
16
18
  export default Setting;
@@ -32,7 +32,13 @@ export interface ViewTheParticipantListProperties {
32
32
  panelistEnabled?: boolean;
33
33
  attendeeCount?: boolean;
34
34
  }
35
- export type Properties = AudioProperties | RaiseHandProperties | ReactionsProperties | ShareControlProperties | VideoProperties | ViewTheParticipantListProperties;
35
+ export interface AnnotationProperties {
36
+ enabled?: boolean;
37
+ }
38
+ export interface RemoteDesktopControlProperties {
39
+ enabled?: boolean;
40
+ }
41
+ export type Properties = AudioProperties | RaiseHandProperties | ReactionsProperties | ShareControlProperties | VideoProperties | ViewTheParticipantListProperties | AnnotationProperties | RemoteDesktopControlProperties;
36
42
  export interface ControlConfig<Props = Properties> {
37
43
  /**
38
44
  * The scope of the control within this object.
@@ -95,6 +95,10 @@ interface IInMeetingActions {
95
95
  isPracticeSessionOff?: boolean;
96
96
  canStartPracticeSession?: boolean;
97
97
  canStopPracticeSession?: boolean;
98
+ canEnableAnnotation?: boolean;
99
+ canDisableAnnotation?: boolean;
100
+ canEnableRemoteDesktopControl?: boolean;
101
+ canDisableRemoteDesktopControl?: boolean;
98
102
  }
99
103
  /**
100
104
  * @class InMeetingActions
@@ -190,6 +194,10 @@ export default class InMeetingActions implements IInMeetingActions {
190
194
  isPracticeSessionOff: any;
191
195
  canStartPracticeSession: any;
192
196
  canStopPracticeSession: any;
197
+ canEnableAnnotation: any;
198
+ canDisableAnnotation: any;
199
+ canEnableRemoteDesktopControl: any;
200
+ canDisableRemoteDesktopControl: any;
193
201
  /**
194
202
  * Returns all meeting action options
195
203
  * @returns {Object}
@@ -458,7 +458,7 @@ var Webinar = _webexCore.WebexPlugin.extend({
458
458
  }, _callee7);
459
459
  }))();
460
460
  },
461
- version: "3.8.0-next.38"
461
+ version: "3.8.0-next.39"
462
462
  });
463
463
  var _default = exports.default = Webinar;
464
464
  //# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -43,7 +43,7 @@
43
43
  "@webex/eslint-config-legacy": "0.0.0",
44
44
  "@webex/jest-config-legacy": "0.0.0",
45
45
  "@webex/legacy-tools": "0.0.0",
46
- "@webex/plugin-meetings": "3.8.0-next.38",
46
+ "@webex/plugin-meetings": "3.8.0-next.39",
47
47
  "@webex/plugin-rooms": "3.8.0-next.13",
48
48
  "@webex/test-helper-chai": "3.8.0-next.11",
49
49
  "@webex/test-helper-mocha": "3.8.0-next.11",
@@ -71,7 +71,7 @@
71
71
  "@webex/internal-plugin-metrics": "3.8.0-next.11",
72
72
  "@webex/internal-plugin-support": "3.8.0-next.13",
73
73
  "@webex/internal-plugin-user": "3.8.0-next.11",
74
- "@webex/internal-plugin-voicea": "3.8.0-next.38",
74
+ "@webex/internal-plugin-voicea": "3.8.0-next.39",
75
75
  "@webex/media-helpers": "3.8.0-next.12",
76
76
  "@webex/plugin-people": "3.8.0-next.13",
77
77
  "@webex/plugin-rooms": "3.8.0-next.13",
@@ -92,5 +92,5 @@
92
92
  "//": [
93
93
  "TODO: upgrade jwt-decode when moving to node 18"
94
94
  ],
95
- "version": "3.8.0-next.38"
95
+ "version": "3.8.0-next.39"
96
96
  }
package/src/constants.ts CHANGED
@@ -369,6 +369,9 @@ export const EVENT_TRIGGERS = {
369
369
  MEETING_CONTROLS_MEETING_FULL_UPDATED: 'meeting:controls:meeting-full:updated',
370
370
  MEETING_CONTROLS_PRACTICE_SESSION_STATUS_UPDATED:
371
371
  'meeting:controls:practice-session-status:updated',
372
+ MEETING_CONTROLS_ANNOTATION_UPDATED: 'meeting:controls:annotation:updated',
373
+ MEETING_CONTROLS_REMOTE_DESKTOP_CONTROL_UPDATED:
374
+ 'meeting:controls:remote-desktop-control:updated',
372
375
  // Locus URL changed
373
376
  MEETING_LOCUS_URL_UPDATE: 'meeting:locus:locusUrl:update',
374
377
  MEETING_STREAM_PUBLISH_STATE_CHANGED: 'meeting:streamPublishStateChanged',
@@ -711,6 +714,8 @@ export const LOCUSINFO = {
711
714
  CONTROLS_PRACTICE_SESSION_STATUS_UPDATED: 'CONTROLS_PRACTICE_SESSION_STATUS_UPDATED',
712
715
  CONTROLS_VIDEO_CHANGED: 'CONTROLS_VIDEO_CHANGED',
713
716
  CONTROLS_STAGE_VIEW_UPDATED: 'CONTROLS_STAGE_VIEW_UPDATED',
717
+ CONTROLS_ANNOTATION_CHANGED: 'CONTROLS_ANNOTATION_CHANGED',
718
+ CONTROLS_REMOTE_DESKTOP_CONTROL_CHANGED: 'CONTROLS_REMOTE_DESKTOP_CONTROL_CHANGED',
714
719
  SELF_UNADMITTED_GUEST: 'SELF_UNADMITTED_GUEST',
715
720
  SELF_ADMITTED_GUEST: 'SELF_ADMITTED_GUEST',
716
721
  SELF_REMOTE_VIDEO_MUTE_STATUS_UPDATED: 'SELF_REMOTE_VIDEO_MUTE_STATUS_UPDATED',
@@ -1026,6 +1031,14 @@ export const DISPLAY_HINTS = {
1026
1031
  PRACTICE_SESSION_OFF: 'PRACTICE_SESSION_OFF',
1027
1032
  SHOW_PRACTICE_SESSION_START: 'SHOW_PRACTICE_SESSION_START',
1028
1033
  SHOW_PRACTICE_SESSION_STOP: 'SHOW_PRACTICE_SESSION_STOP',
1034
+
1035
+ // Annotations
1036
+ ENABLE_ANNOTATION_MEETING_OPTION: 'ENABLE_ANNOTATION_MEETING_OPTION',
1037
+ DISABLE_ANNOTATION_MEETING_OPTION: 'DISABLE_ANNOTATION_MEETING_OPTION',
1038
+
1039
+ // Remote Desktop Control
1040
+ ENABLE_RDC_MEETING_OPTION: 'ENABLE_RDC_MEETING_OPTION',
1041
+ DISABLE_RDC_MEETING_OPTION: 'DISABLE_RDC_MEETING_OPTION',
1029
1042
  };
1030
1043
 
1031
1044
  export const INTERSTITIAL_DISPLAY_HINTS = [DISPLAY_HINTS.VOIP_IS_ENABLED];
@@ -12,6 +12,8 @@ enum Control {
12
12
  shareControl = 'shareControl',
13
13
  video = 'video',
14
14
  viewTheParticipantList = 'viewTheParticipantList',
15
+ annotation = 'annotation',
16
+ rdc = 'rdc',
15
17
  }
16
18
 
17
19
  export {Control, Setting};
@@ -40,13 +40,23 @@ export interface ViewTheParticipantListProperties {
40
40
  attendeeCount?: boolean;
41
41
  }
42
42
 
43
+ export interface AnnotationProperties {
44
+ enabled?: boolean;
45
+ }
46
+
47
+ export interface RemoteDesktopControlProperties {
48
+ enabled?: boolean;
49
+ }
50
+
43
51
  export type Properties =
44
52
  | AudioProperties
45
53
  | RaiseHandProperties
46
54
  | ReactionsProperties
47
55
  | ShareControlProperties
48
56
  | VideoProperties
49
- | ViewTheParticipantListProperties;
57
+ | ViewTheParticipantListProperties
58
+ | AnnotationProperties
59
+ | RemoteDesktopControlProperties;
50
60
 
51
61
  export interface ControlConfig<Props = Properties> {
52
62
  /**
@@ -7,6 +7,8 @@ import {
7
7
  ReactionsProperties,
8
8
  ViewTheParticipantListProperties,
9
9
  VideoProperties,
10
+ type RemoteDesktopControlProperties,
11
+ type AnnotationProperties,
10
12
  } from './types';
11
13
 
12
14
  /**
@@ -256,6 +258,52 @@ class Utils {
256
258
  return Utils.hasHints({requiredHints, displayHints});
257
259
  }
258
260
 
261
+ /**
262
+ * Validate if a annotation-scoped control is allowed to be sent to the service.
263
+ *
264
+ * @param {ControlConfig<AnnotationProperties>} control - Annotation control config to validate
265
+ * @param {Array<string>} displayHints - All available hints
266
+ * @returns {boolean} - True if all of the actions are allowed.
267
+ */
268
+ public static canUpdateAnnotation(
269
+ control: ControlConfig<AnnotationProperties>,
270
+ displayHints: Array<string>
271
+ ): boolean {
272
+ const requiredHints = [];
273
+
274
+ if (control.properties.enabled === true) {
275
+ requiredHints.push(DISPLAY_HINTS.ENABLE_ANNOTATION_MEETING_OPTION);
276
+ }
277
+ if (control.properties.enabled === false) {
278
+ requiredHints.push(DISPLAY_HINTS.DISABLE_ANNOTATION_MEETING_OPTION);
279
+ }
280
+
281
+ return Utils.hasHints({requiredHints, displayHints});
282
+ }
283
+
284
+ /**
285
+ * Validate if a rdc-scoped control is allowed to be sent to the service.
286
+ *
287
+ * @param {ControlConfig<RemoteDesktopControlProperties>} control - Remote Desktop Control config to validate
288
+ * @param {Array<string>} displayHints - All available hints
289
+ * @returns {boolean} - True if all of the actions are allowed.
290
+ */
291
+ public static canUpdateRemoteDesktopControl(
292
+ control: ControlConfig<RemoteDesktopControlProperties>,
293
+ displayHints: Array<string>
294
+ ): boolean {
295
+ const requiredHints = [];
296
+
297
+ if (control.properties.enabled === true) {
298
+ requiredHints.push(DISPLAY_HINTS.ENABLE_RDC_MEETING_OPTION);
299
+ }
300
+ if (control.properties.enabled === false) {
301
+ requiredHints.push(DISPLAY_HINTS.DISABLE_RDC_MEETING_OPTION);
302
+ }
303
+
304
+ return Utils.hasHints({requiredHints, displayHints});
305
+ }
306
+
259
307
  /**
260
308
  * Validate that a control can be sent to the service based on the provided
261
309
  * display hints.
@@ -301,6 +349,20 @@ class Utils {
301
349
  );
302
350
  break;
303
351
 
352
+ case Control.annotation:
353
+ determinant = Utils.canUpdateAnnotation(
354
+ control as ControlConfig<AnnotationProperties>,
355
+ displayHints
356
+ );
357
+ break;
358
+
359
+ case Control.rdc:
360
+ determinant = Utils.canUpdateRemoteDesktopControl(
361
+ control as ControlConfig<RemoteDesktopControlProperties>,
362
+ displayHints
363
+ );
364
+ break;
365
+
304
366
  default:
305
367
  determinant = false;
306
368
  }
@@ -111,6 +111,18 @@ ControlsUtils.parse = (controls: any) => {
111
111
  };
112
112
  }
113
113
 
114
+ if (controls?.annotationControl) {
115
+ parsedControls.annotationControl = {
116
+ enabled: controls.annotationControl.enabled,
117
+ };
118
+ }
119
+
120
+ if (controls?.rdcControl) {
121
+ parsedControls.rdcControl = {
122
+ enabled: controls.rdcControl.enabled,
123
+ };
124
+ }
125
+
114
126
  return parsedControls;
115
127
  };
116
128
 
@@ -211,6 +223,12 @@ ControlsUtils.getControls = (oldControls: any, newControls: any) => {
211
223
  ),
212
224
 
213
225
  hasStageViewChanged: !isEqual(previous?.videoLayout, current?.videoLayout),
226
+
227
+ hasAnnotationControlChanged:
228
+ current?.annotationControl?.enabled !== previous?.annotationControl?.enabled,
229
+
230
+ hasRemoteDesktopControlChanged:
231
+ current?.rdcControl?.enabled !== previous?.rdcControl?.enabled,
214
232
  },
215
233
  };
216
234
  };
@@ -817,6 +817,8 @@ export default class LocusInfo extends EventsScope {
817
817
  hasMeetingFullChanged,
818
818
  hasPracticeSessionEnabledChanged,
819
819
  hasStageViewChanged,
820
+ hasAnnotationControlChanged,
821
+ hasRemoteDesktopControlChanged,
820
822
  },
821
823
  current,
822
824
  } = ControlsUtils.getControls(this.controls, controls);
@@ -1052,6 +1054,22 @@ export default class LocusInfo extends EventsScope {
1052
1054
  );
1053
1055
  }
1054
1056
 
1057
+ if (hasAnnotationControlChanged) {
1058
+ this.emitScoped(
1059
+ {file: 'locus-info', function: 'updateControls'},
1060
+ LOCUSINFO.EVENTS.CONTROLS_ANNOTATION_CHANGED,
1061
+ {state: current.annotationControl}
1062
+ );
1063
+ }
1064
+
1065
+ if (hasRemoteDesktopControlChanged) {
1066
+ this.emitScoped(
1067
+ {file: 'locus-info', function: 'updateControls'},
1068
+ LOCUSINFO.EVENTS.CONTROLS_REMOTE_DESKTOP_CONTROL_CHANGED,
1069
+ {state: current.rdcControl}
1070
+ );
1071
+ }
1072
+
1055
1073
  this.controls = controls;
1056
1074
  }
1057
1075
  }
@@ -99,6 +99,10 @@ interface IInMeetingActions {
99
99
  isPracticeSessionOff?: boolean;
100
100
  canStartPracticeSession?: boolean;
101
101
  canStopPracticeSession?: boolean;
102
+ canEnableAnnotation?: boolean;
103
+ canDisableAnnotation?: boolean;
104
+ canEnableRemoteDesktopControl?: boolean;
105
+ canDisableRemoteDesktopControl?: boolean;
102
106
  }
103
107
 
104
108
  /**
@@ -285,6 +289,14 @@ export default class InMeetingActions implements IInMeetingActions {
285
289
 
286
290
  canStopPracticeSession = null;
287
291
 
292
+ canEnableAnnotation = null;
293
+
294
+ canDisableAnnotation = null;
295
+
296
+ canEnableRemoteDesktopControl = null;
297
+
298
+ canDisableRemoteDesktopControl = null;
299
+
288
300
  /**
289
301
  * Returns all meeting action options
290
302
  * @returns {Object}
@@ -379,6 +391,10 @@ export default class InMeetingActions implements IInMeetingActions {
379
391
  isPracticeSessionOff: this.isPracticeSessionOff,
380
392
  canStartPracticeSession: this.canStartPracticeSession,
381
393
  canStopPracticeSession: this.canStopPracticeSession,
394
+ canEnableAnnotation: this.canEnableAnnotation,
395
+ canDisableAnnotation: this.canDisableAnnotation,
396
+ canEnableRemoteDesktopControl: this.canEnableRemoteDesktopControl,
397
+ canDisableRemoteDesktopControl: this.canDisableRemoteDesktopControl,
382
398
  });
383
399
 
384
400
  /**
@@ -2871,6 +2871,24 @@ export default class Meeting extends StatelessWebexPlugin {
2871
2871
  {state}
2872
2872
  );
2873
2873
  });
2874
+
2875
+ this.locusInfo.on(LOCUSINFO.EVENTS.CONTROLS_ANNOTATION_CHANGED, ({state}) => {
2876
+ Trigger.trigger(
2877
+ this,
2878
+ {file: 'meeting/index', function: 'setupLocusControlsListener'},
2879
+ EVENT_TRIGGERS.MEETING_CONTROLS_ANNOTATION_UPDATED,
2880
+ {state}
2881
+ );
2882
+ });
2883
+
2884
+ this.locusInfo.on(LOCUSINFO.EVENTS.CONTROLS_REMOTE_DESKTOP_CONTROL_CHANGED, ({state}) => {
2885
+ Trigger.trigger(
2886
+ this,
2887
+ {file: 'meeting/index', function: 'setupLocusControlsListener'},
2888
+ EVENT_TRIGGERS.MEETING_CONTROLS_REMOTE_DESKTOP_CONTROL_UPDATED,
2889
+ {state}
2890
+ );
2891
+ });
2874
2892
  }
2875
2893
 
2876
2894
  /**
@@ -4269,6 +4287,22 @@ export default class Meeting extends StatelessWebexPlugin {
4269
4287
  requiredPolicies: [SELF_POLICY.SUPPORT_ANNOTATION],
4270
4288
  policies: this.selfUserPolicies,
4271
4289
  }),
4290
+ canEnableAnnotation: ControlsOptionsUtil.hasHints({
4291
+ requiredHints: [DISPLAY_HINTS.ENABLE_ANNOTATION_MEETING_OPTION],
4292
+ displayHints: this.userDisplayHints,
4293
+ }),
4294
+ canDisableAnnotation: ControlsOptionsUtil.hasHints({
4295
+ requiredHints: [DISPLAY_HINTS.DISABLE_ANNOTATION_MEETING_OPTION],
4296
+ displayHints: this.userDisplayHints,
4297
+ }),
4298
+ canEnableRemoteDesktopControl: ControlsOptionsUtil.hasHints({
4299
+ requiredHints: [DISPLAY_HINTS.ENABLE_RDC_MEETING_OPTION],
4300
+ displayHints: this.userDisplayHints,
4301
+ }),
4302
+ canDisableRemoteDesktopControl: ControlsOptionsUtil.hasHints({
4303
+ requiredHints: [DISPLAY_HINTS.DISABLE_RDC_MEETING_OPTION],
4304
+ displayHints: this.userDisplayHints,
4305
+ }),
4272
4306
  }) || changed;
4273
4307
  }
4274
4308
  if (changed) {
@@ -406,6 +406,74 @@ describe('plugin-meetings', () => {
406
406
  });
407
407
  });
408
408
 
409
+ describe('canUpdateAnnotation()', () => {
410
+ beforeEach(() => {
411
+ sinon.stub(ControlsOptionsUtil, 'hasHints').returns(true);
412
+ });
413
+
414
+ it('should call hasHints() with proper hints when `enabled` is true', () => {
415
+ ControlsOptionsUtil.canUpdateAnnotation({properties: {enabled: true}}, []);
416
+
417
+ assert.calledWith(ControlsOptionsUtil.hasHints, {
418
+ requiredHints: [DISPLAY_HINTS.ENABLE_ANNOTATION_MEETING_OPTION],
419
+ displayHints: [],
420
+ });
421
+ });
422
+
423
+ it('should call hasHints() with proper hints when `enabled` is false', () => {
424
+ ControlsOptionsUtil.canUpdateAnnotation({properties: {enabled: false}}, []);
425
+
426
+ assert.calledWith(ControlsOptionsUtil.hasHints, {
427
+ requiredHints: [DISPLAY_HINTS.DISABLE_ANNOTATION_MEETING_OPTION],
428
+ displayHints: [],
429
+ });
430
+ });
431
+
432
+ it('should return the resolution of hasHints()', () => {
433
+ const expected = 'example-return-value';
434
+ ControlsOptionsUtil.hasHints.returns(expected);
435
+
436
+ const results = ControlsOptionsUtil.canUpdateAnnotation({properties: {}}, []);
437
+
438
+ assert.calledOnce(ControlsOptionsUtil.hasHints);
439
+ assert.equal(results, expected);
440
+ });
441
+ });
442
+
443
+ describe('canUpdateRemoteDesktopControl()', () => {
444
+ beforeEach(() => {
445
+ sinon.stub(ControlsOptionsUtil, 'hasHints').returns(true);
446
+ });
447
+
448
+ it('should call hasHints() with proper hints when `enabled` is true', () => {
449
+ ControlsOptionsUtil.canUpdateRemoteDesktopControl({properties: {enabled: true}}, []);
450
+
451
+ assert.calledWith(ControlsOptionsUtil.hasHints, {
452
+ requiredHints: [DISPLAY_HINTS.ENABLE_RDC_MEETING_OPTION],
453
+ displayHints: [],
454
+ });
455
+ });
456
+
457
+ it('should call hasHints() with proper hints when `enabled` is false', () => {
458
+ ControlsOptionsUtil.canUpdateRemoteDesktopControl({properties: {enabled: false}}, []);
459
+
460
+ assert.calledWith(ControlsOptionsUtil.hasHints, {
461
+ requiredHints: [DISPLAY_HINTS.DISABLE_RDC_MEETING_OPTION],
462
+ displayHints: [],
463
+ });
464
+ });
465
+
466
+ it('should return the resolution of hasHints()', () => {
467
+ const expected = 'example-return-value';
468
+ ControlsOptionsUtil.hasHints.returns(expected);
469
+
470
+ const results = ControlsOptionsUtil.canUpdateRemoteDesktopControl({properties: {}}, []);
471
+
472
+ assert.calledOnce(ControlsOptionsUtil.hasHints);
473
+ assert.equal(results, expected);
474
+ });
475
+ });
476
+
409
477
  describe('canUpdate()', () => {
410
478
  const displayHints = [];
411
479
 
@@ -416,6 +484,8 @@ describe('plugin-meetings', () => {
416
484
  ControlsOptionsUtil.canUpdateShareControl = sinon.stub().returns(true);
417
485
  ControlsOptionsUtil.canUpdateVideo = sinon.stub().returns(true);
418
486
  ControlsOptionsUtil.canUpdateViewTheParticipantsList = sinon.stub().returns(true);
487
+ ControlsOptionsUtil.canUpdateAnnotation = sinon.stub().returns(true);
488
+ ControlsOptionsUtil.canUpdateRemoteDesktopControl = sinon.stub().returns(true);
419
489
  });
420
490
 
421
491
  it('should only call canUpdateAudio() if the scope is audio', () => {
@@ -429,6 +499,8 @@ describe('plugin-meetings', () => {
429
499
  assert.callCount(ControlsOptionsUtil.canUpdateShareControl, 0);
430
500
  assert.callCount(ControlsOptionsUtil.canUpdateVideo, 0);
431
501
  assert.callCount(ControlsOptionsUtil.canUpdateViewTheParticipantsList, 0);
502
+ assert.callCount(ControlsOptionsUtil.canUpdateAnnotation, 0);
503
+ assert.callCount(ControlsOptionsUtil.canUpdateRemoteDesktopControl, 0);
432
504
  assert.isTrue(results);
433
505
  });
434
506
 
@@ -443,6 +515,8 @@ describe('plugin-meetings', () => {
443
515
  assert.callCount(ControlsOptionsUtil.canUpdateShareControl, 0);
444
516
  assert.callCount(ControlsOptionsUtil.canUpdateVideo, 0);
445
517
  assert.callCount(ControlsOptionsUtil.canUpdateViewTheParticipantsList, 0);
518
+ assert.callCount(ControlsOptionsUtil.canUpdateAnnotation, 0);
519
+ assert.callCount(ControlsOptionsUtil.canUpdateRemoteDesktopControl, 0);
446
520
  assert.isTrue(results);
447
521
  });
448
522
 
@@ -457,6 +531,8 @@ describe('plugin-meetings', () => {
457
531
  assert.callCount(ControlsOptionsUtil.canUpdateShareControl, 0);
458
532
  assert.callCount(ControlsOptionsUtil.canUpdateVideo, 0);
459
533
  assert.callCount(ControlsOptionsUtil.canUpdateViewTheParticipantsList, 0);
534
+ assert.callCount(ControlsOptionsUtil.canUpdateAnnotation, 0);
535
+ assert.callCount(ControlsOptionsUtil.canUpdateRemoteDesktopControl, 0);
460
536
  assert.isTrue(results);
461
537
  });
462
538
 
@@ -471,6 +547,8 @@ describe('plugin-meetings', () => {
471
547
  assert.calledWith(ControlsOptionsUtil.canUpdateShareControl, displayHints);
472
548
  assert.callCount(ControlsOptionsUtil.canUpdateVideo, 0);
473
549
  assert.callCount(ControlsOptionsUtil.canUpdateViewTheParticipantsList, 0);
550
+ assert.callCount(ControlsOptionsUtil.canUpdateAnnotation, 0);
551
+ assert.callCount(ControlsOptionsUtil.canUpdateRemoteDesktopControl, 0);
474
552
  assert.isTrue(results);
475
553
  });
476
554
 
@@ -485,6 +563,8 @@ describe('plugin-meetings', () => {
485
563
  assert.callCount(ControlsOptionsUtil.canUpdateShareControl, 0);
486
564
  assert.calledWith(ControlsOptionsUtil.canUpdateVideo, control, displayHints);
487
565
  assert.callCount(ControlsOptionsUtil.canUpdateViewTheParticipantsList, 0);
566
+ assert.callCount(ControlsOptionsUtil.canUpdateAnnotation, 0);
567
+ assert.callCount(ControlsOptionsUtil.canUpdateRemoteDesktopControl, 0);
488
568
  assert.isTrue(results);
489
569
  });
490
570
 
@@ -503,6 +583,44 @@ describe('plugin-meetings', () => {
503
583
  control,
504
584
  displayHints
505
585
  );
586
+ assert.callCount(ControlsOptionsUtil.canUpdateAnnotation, 0);
587
+ assert.callCount(ControlsOptionsUtil.canUpdateRemoteDesktopControl, 0);
588
+ assert.isTrue(results);
589
+ });
590
+
591
+ it('should only call canUpdateAnnotation() if the scope is annotation', () => {
592
+ const control = {scope: 'annotation'};
593
+
594
+ const results = ControlsOptionsUtil.canUpdate(control, displayHints);
595
+
596
+ assert.callCount(ControlsOptionsUtil.canUpdateAudio, 0);
597
+ assert.callCount(ControlsOptionsUtil.canUpdateRaiseHand, 0);
598
+ assert.callCount(ControlsOptionsUtil.canUpdateReactions, 0);
599
+ assert.callCount(ControlsOptionsUtil.canUpdateShareControl, 0);
600
+ assert.callCount(ControlsOptionsUtil.canUpdateVideo, 0);
601
+ assert.callCount(ControlsOptionsUtil.canUpdateViewTheParticipantsList, 0);
602
+ assert.calledWith(ControlsOptionsUtil.canUpdateAnnotation, control, displayHints);
603
+ assert.callCount(ControlsOptionsUtil.canUpdateRemoteDesktopControl, 0);
604
+ assert.isTrue(results);
605
+ });
606
+
607
+ it('should only call canUpdateRemoteDesktopControl() if the scope is rdc', () => {
608
+ const control = {scope: 'rdc'};
609
+
610
+ const results = ControlsOptionsUtil.canUpdate(control, displayHints);
611
+
612
+ assert.callCount(ControlsOptionsUtil.canUpdateAudio, 0);
613
+ assert.callCount(ControlsOptionsUtil.canUpdateRaiseHand, 0);
614
+ assert.callCount(ControlsOptionsUtil.canUpdateReactions, 0);
615
+ assert.callCount(ControlsOptionsUtil.canUpdateShareControl, 0);
616
+ assert.callCount(ControlsOptionsUtil.canUpdateVideo, 0);
617
+ assert.callCount(ControlsOptionsUtil.canUpdateViewTheParticipantsList, 0);
618
+ assert.callCount(ControlsOptionsUtil.canUpdateAnnotation, 0);
619
+ assert.calledWith(
620
+ ControlsOptionsUtil.canUpdateRemoteDesktopControl,
621
+ control,
622
+ displayHints
623
+ );
506
624
  assert.isTrue(results);
507
625
  });
508
626
 
@@ -517,6 +635,8 @@ describe('plugin-meetings', () => {
517
635
  assert.callCount(ControlsOptionsUtil.canUpdateShareControl, 0);
518
636
  assert.callCount(ControlsOptionsUtil.canUpdateVideo, 0);
519
637
  assert.callCount(ControlsOptionsUtil.canUpdateViewTheParticipantsList, 0);
638
+ assert.callCount(ControlsOptionsUtil.canUpdateAnnotation, 0);
639
+ assert.callCount(ControlsOptionsUtil.canUpdateRemoteDesktopControl, 0);
520
640
  assert.isFalse(results);
521
641
  });
522
642
  });