@webex/plugin-meetings 3.12.0-next.72 → 3.12.0-next.74

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 (37) hide show
  1. package/dist/aiEnableRequest/index.js +1 -1
  2. package/dist/breakouts/breakout.js +1 -1
  3. package/dist/breakouts/index.js +1 -1
  4. package/dist/constants.js +4 -1
  5. package/dist/constants.js.map +1 -1
  6. package/dist/controls-options-manager/index.js +29 -5
  7. package/dist/controls-options-manager/index.js.map +1 -1
  8. package/dist/interpretation/index.js +1 -1
  9. package/dist/interpretation/siLanguage.js +1 -1
  10. package/dist/locus-info/infoUtils.js +36 -5
  11. package/dist/locus-info/infoUtils.js.map +1 -1
  12. package/dist/meeting/in-meeting-actions.js +3 -1
  13. package/dist/meeting/in-meeting-actions.js.map +1 -1
  14. package/dist/meeting/index.js +21 -8
  15. package/dist/meeting/index.js.map +1 -1
  16. package/dist/meeting/util.js +9 -0
  17. package/dist/meeting/util.js.map +1 -1
  18. package/dist/types/constants.d.ts +3 -0
  19. package/dist/types/controls-options-manager/index.d.ts +6 -0
  20. package/dist/types/locus-info/infoUtils.d.ts +8 -0
  21. package/dist/types/meeting/in-meeting-actions.d.ts +2 -0
  22. package/dist/types/meeting/index.d.ts +1 -0
  23. package/dist/types/meeting/util.d.ts +1 -0
  24. package/dist/webinar/index.js +1 -1
  25. package/package.json +1 -1
  26. package/src/constants.ts +3 -0
  27. package/src/controls-options-manager/index.ts +40 -4
  28. package/src/locus-info/infoUtils.ts +41 -6
  29. package/src/meeting/in-meeting-actions.ts +4 -0
  30. package/src/meeting/index.ts +10 -0
  31. package/src/meeting/util.ts +12 -0
  32. package/test/unit/spec/controls-options-manager/index.js +104 -0
  33. package/test/unit/spec/locus-info/index.js +12 -0
  34. package/test/unit/spec/locus-info/infoUtils.js +87 -0
  35. package/test/unit/spec/meeting/in-meeting-actions.ts +2 -0
  36. package/test/unit/spec/meeting/utils.js +49 -0
  37. package/test/unit/spec/meetings/index.js +3 -0
@@ -4,7 +4,7 @@ import PermissionError from '../common/errors/permission';
4
4
  import MeetingRequest from '../meeting/request';
5
5
  import LoggerProxy from '../common/logs/logger-proxy';
6
6
  import {Control, Setting} from './enums';
7
- import {ControlConfig} from './types';
7
+ import {ControlConfig, ViewTheParticipantListProperties} from './types';
8
8
  import Util from './util';
9
9
  import {CAN_SET, CAN_UNSET, ENABLED} from './constants';
10
10
 
@@ -56,6 +56,10 @@ export default class ControlsOptionsManager {
56
56
  */
57
57
  private mainLocusUrl: string;
58
58
 
59
+ private getControls: () => Record<string, any> = () => ({});
60
+
61
+ private isWebinar: () => boolean = () => false;
62
+
59
63
  /**
60
64
  * @param {MeetingRequest} request
61
65
  * @param {Object} options
@@ -67,6 +71,8 @@ export default class ControlsOptionsManager {
67
71
  options?: {
68
72
  locusUrl: string;
69
73
  displayHints?: Array<string>;
74
+ getControls?: () => Record<string, any>;
75
+ isWebinar?: () => boolean;
70
76
  }
71
77
  ) {
72
78
  this.initialize(request);
@@ -89,7 +95,12 @@ export default class ControlsOptionsManager {
89
95
  * @public
90
96
  * @memberof ControlsOptionsManager
91
97
  */
92
- public set(options?: {locusUrl: string; displayHints?: Array<string>}) {
98
+ public set(options?: {
99
+ locusUrl: string;
100
+ displayHints?: Array<string>;
101
+ getControls?: () => Record<string, any>;
102
+ isWebinar?: () => boolean;
103
+ }) {
93
104
  this.extract(options);
94
105
  }
95
106
 
@@ -141,9 +152,20 @@ export default class ControlsOptionsManager {
141
152
  * @private
142
153
  * @memberof ControlsOptionsManager
143
154
  */
144
- private extract(options?: {locusUrl: string; displayHints?: Array<string>}) {
155
+ private extract(options?: {
156
+ locusUrl: string;
157
+ displayHints?: Array<string>;
158
+ getControls?: () => Record<string, any>;
159
+ isWebinar?: () => boolean;
160
+ }) {
145
161
  this.setDisplayHints(options?.displayHints);
146
162
  this.setLocusUrl(options?.locusUrl);
163
+ if (options?.getControls) {
164
+ this.getControls = options.getControls;
165
+ }
166
+ if (options?.isWebinar) {
167
+ this.isWebinar = options.isWebinar;
168
+ }
147
169
  }
148
170
 
149
171
  /**
@@ -172,8 +194,22 @@ export default class ControlsOptionsManager {
172
194
  );
173
195
  }
174
196
 
197
+ let {properties} = control;
198
+
199
+ if (control.scope === Control.viewTheParticipantList) {
200
+ const props = properties as ViewTheParticipantListProperties;
201
+ const current = this.getControls()?.viewTheParticipantList;
202
+ properties = {
203
+ enabled: props.enabled ?? current?.enabled ?? false,
204
+ ...(this.isWebinar() && {
205
+ panelistEnabled: props.panelistEnabled ?? current?.panelistEnabled ?? false,
206
+ attendeeCount: props.attendeeCount ?? Boolean(current?.attendeeCount) ?? false,
207
+ }),
208
+ };
209
+ }
210
+
175
211
  return {
176
- [control.scope]: control.properties,
212
+ [control.scope]: properties,
177
213
  };
178
214
  });
179
215
 
@@ -1,5 +1,15 @@
1
1
  import {SELF_ROLES, DISPLAY_HINTS, INTERSTITIAL_DISPLAY_HINTS} from '../constants';
2
2
 
3
+ // these values have to match what Locus sends us
4
+ export enum DisplayHintSection {
5
+ JOINED = 'joined',
6
+ MODERATOR = 'moderator',
7
+ COHOST = 'coHost',
8
+ PRESENTER = 'presenter',
9
+ PANELIST = 'panelist',
10
+ ATTENDEE = 'attendee',
11
+ }
12
+
3
13
  const InfoUtils: any = {};
4
14
 
5
15
  InfoUtils.parse = (info, roles, isJoined = true) => {
@@ -7,6 +17,9 @@ InfoUtils.parse = (info, roles, isJoined = true) => {
7
17
  policy: InfoUtils.parsePolicy(info),
8
18
  moderator: InfoUtils.parseModerator(info),
9
19
  coHost: InfoUtils.parseCoHost(info),
20
+ presenter: InfoUtils.parsePresenter(info),
21
+ panelist: InfoUtils.parsePanelist(info),
22
+ attendee: InfoUtils.parseAttendee(info),
10
23
  };
11
24
 
12
25
  let userDisplayHints = isJoined
@@ -27,6 +40,18 @@ InfoUtils.parse = (info, roles, isJoined = true) => {
27
40
  userDisplayHints = {...userDisplayHints, ...parsed.moderator};
28
41
  }
29
42
 
43
+ if (roles.includes(SELF_ROLES.PRESENTER)) {
44
+ userDisplayHints = {...userDisplayHints, ...parsed.presenter};
45
+ }
46
+
47
+ if (roles.includes(SELF_ROLES.PANELIST)) {
48
+ userDisplayHints = {...userDisplayHints, ...parsed.panelist};
49
+ }
50
+
51
+ if (roles.includes(SELF_ROLES.ATTENDEE)) {
52
+ userDisplayHints = {...userDisplayHints, ...parsed.attendee};
53
+ }
54
+
30
55
  parsed.userDisplayHints = Object.keys(userDisplayHints);
31
56
 
32
57
  if (info.sipUri) {
@@ -44,8 +69,8 @@ InfoUtils.parse = (info, roles, isJoined = true) => {
44
69
  return parsed;
45
70
  };
46
71
 
47
- InfoUtils.parseDisplayHintSection = (info, displayHintKey) => {
48
- const displayHints = {};
72
+ InfoUtils.parseDisplayHintSection = (info: any, displayHintKey: DisplayHintSection) => {
73
+ const displayHints: Record<string, boolean> = {};
49
74
 
50
75
  if (
51
76
  info &&
@@ -53,7 +78,7 @@ InfoUtils.parseDisplayHintSection = (info, displayHintKey) => {
53
78
  info.displayHints[displayHintKey] &&
54
79
  info.displayHints[displayHintKey].length > 0
55
80
  ) {
56
- info.displayHints[displayHintKey].forEach((key) => {
81
+ info.displayHints[displayHintKey].forEach((key: any) => {
57
82
  displayHints[key] = true;
58
83
  });
59
84
  }
@@ -61,20 +86,30 @@ InfoUtils.parseDisplayHintSection = (info, displayHintKey) => {
61
86
  return displayHints;
62
87
  };
63
88
 
64
- InfoUtils.parsePolicy = (info) => InfoUtils.parseDisplayHintSection(info, 'joined');
89
+ InfoUtils.parsePolicy = (info) =>
90
+ InfoUtils.parseDisplayHintSection(info, DisplayHintSection.JOINED);
65
91
 
66
92
  InfoUtils.parseModerator = (info) => {
67
- const displayHints = InfoUtils.parseDisplayHintSection(info, 'moderator');
93
+ const displayHints = InfoUtils.parseDisplayHintSection(info, DisplayHintSection.MODERATOR);
68
94
 
69
95
  return {...displayHints, [DISPLAY_HINTS.LOWER_SOMEONE_ELSES_HAND]: true};
70
96
  };
71
97
 
72
98
  InfoUtils.parseCoHost = (info) => {
73
- const displayHints = InfoUtils.parseDisplayHintSection(info, 'coHost');
99
+ const displayHints = InfoUtils.parseDisplayHintSection(info, DisplayHintSection.COHOST);
74
100
 
75
101
  return {...displayHints, [DISPLAY_HINTS.LOWER_SOMEONE_ELSES_HAND]: true};
76
102
  };
77
103
 
104
+ InfoUtils.parsePresenter = (info) =>
105
+ InfoUtils.parseDisplayHintSection(info, DisplayHintSection.PRESENTER);
106
+
107
+ InfoUtils.parsePanelist = (info) =>
108
+ InfoUtils.parseDisplayHintSection(info, DisplayHintSection.PANELIST);
109
+
110
+ InfoUtils.parseAttendee = (info) =>
111
+ InfoUtils.parseDisplayHintSection(info, DisplayHintSection.ATTENDEE);
112
+
78
113
  InfoUtils.isLocked = (policy) => policy.LOCK_STATUS_LOCKED || false;
79
114
 
80
115
  InfoUtils.isUnlocked = (policy) => policy.LOCK_STATUS_UNLOCKED || false;
@@ -121,6 +121,7 @@ interface IInMeetingActions {
121
121
  canAttendeeRequestAiAssistantEnabled?: boolean;
122
122
  isAttendeeRequestAiAssistantDeclinedAll?: boolean;
123
123
  isAnonymizeDisplayNamesEnabled?: boolean;
124
+ canViewTheParticipantList?: boolean;
124
125
  }
125
126
 
126
127
  /**
@@ -349,6 +350,8 @@ export default class InMeetingActions implements IInMeetingActions {
349
350
 
350
351
  isAnonymizeDisplayNamesEnabled = null;
351
352
 
353
+ canViewTheParticipantList = null;
354
+
352
355
  /**
353
356
  * Returns all meeting action options
354
357
  * @returns {Object}
@@ -464,6 +467,7 @@ export default class InMeetingActions implements IInMeetingActions {
464
467
  canAttendeeRequestAiAssistantEnabled: this.canAttendeeRequestAiAssistantEnabled,
465
468
  isAttendeeRequestAiAssistantDeclinedAll: this.isAttendeeRequestAiAssistantDeclinedAll,
466
469
  isAnonymizeDisplayNamesEnabled: this.isAnonymizeDisplayNamesEnabled,
470
+ canViewTheParticipantList: this.canViewTheParticipantList,
467
471
  });
468
472
 
469
473
  /**
@@ -665,6 +665,7 @@ export default class Meeting extends StatelessWebexPlugin {
665
665
  joinedWith?: any;
666
666
  selfId?: string;
667
667
  roles: any[];
668
+ canNotViewTheParticipantList?: boolean;
668
669
  // ... there is more ... see SelfUtils.parse()
669
670
  // end of the group
670
671
  locusMediaRequest?: LocusMediaRequest;
@@ -1567,6 +1568,8 @@ export default class Meeting extends StatelessWebexPlugin {
1567
1568
  this.controlsOptionsManager = new ControlsOptionsManager(this.meetingRequest, {
1568
1569
  locusUrl: this.locusInfo?.url,
1569
1570
  displayHints: [],
1571
+ getControls: () => this.locusInfo?.controls,
1572
+ isWebinar: () => this.locusInfo?.info?.isWebinar,
1570
1573
  });
1571
1574
 
1572
1575
  this.setUpLocusInfoListeners();
@@ -3857,6 +3860,9 @@ export default class Meeting extends StatelessWebexPlugin {
3857
3860
  });
3858
3861
 
3859
3862
  this.locusInfo.on(LOCUSINFO.EVENTS.SELF_CANNOT_VIEW_PARTICIPANT_LIST_CHANGE, (payload) => {
3863
+ // canViewTheParticipantList meeting action depends on this value, so we need to update meeting actions
3864
+ this.updateMeetingActions();
3865
+
3860
3866
  Trigger.trigger(
3861
3867
  this,
3862
3868
  {
@@ -4658,6 +4664,10 @@ export default class Meeting extends StatelessWebexPlugin {
4658
4664
  isAnonymizeDisplayNamesEnabled: MeetingUtil.isAnonymizeDisplayNamesEnabled(
4659
4665
  this.userDisplayHints
4660
4666
  ),
4667
+ canViewTheParticipantList: MeetingUtil.canViewTheParticipantList(
4668
+ this.userDisplayHints,
4669
+ this.canNotViewTheParticipantList ?? false
4670
+ ),
4661
4671
  }) || changed;
4662
4672
  }
4663
4673
  if (changed) {
@@ -949,6 +949,18 @@ const MeetingUtil = {
949
949
  isAnonymizeDisplayNamesEnabled: (displayHints) =>
950
950
  displayHints.includes(DISPLAY_HINTS.ANONYMOUS_DISPLAY_NAMES_ENABLED),
951
951
 
952
+ canViewTheParticipantList: (displayHints, canNotViewTheParticipantList: boolean) => {
953
+ if (!displayHints.includes(DISPLAY_HINTS.VIEW_THE_PARTICIPANT_LIST)) {
954
+ return false;
955
+ }
956
+
957
+ if (canNotViewTheParticipantList) {
958
+ return false;
959
+ }
960
+
961
+ return displayHints.includes(DISPLAY_HINTS.CAN_VIEW_THE_PARTICIPANT_LIST);
962
+ },
963
+
952
964
  selfSupportsFeature: (feature: SELF_POLICY, userPolicies: Record<SELF_POLICY, boolean>) => {
953
965
  if (!userPolicies) {
954
966
  return true;
@@ -319,6 +319,110 @@ describe('plugin-meetings', () => {
319
319
  Util.canUpdate = restorable;
320
320
  });
321
321
  });
322
+
323
+ describe('viewTheParticipantList fill-in', () => {
324
+ let restorable;
325
+
326
+ afterEach(() => {
327
+ Util.canUpdate = restorable;
328
+ });
329
+
330
+ describe('when meeting is a webinar', () => {
331
+ beforeEach(() => {
332
+ restorable = Util.canUpdate;
333
+ Util.canUpdate = sinon.stub().returns(true);
334
+ manager.set({
335
+ locusUrl: 'test/id',
336
+ displayHints: [],
337
+ getControls: () => ({viewTheParticipantList: {enabled: true, panelistEnabled: true, attendeeCount: true}}),
338
+ isWebinar: () => true,
339
+ });
340
+ });
341
+
342
+ it('should fill in all undefined properties from current state', () => {
343
+ const control = {scope: 'viewTheParticipantList', properties: {}};
344
+
345
+ return manager.update(control).then(() => {
346
+ assert.calledOnceWithExactly(request.locusDeltaRequest, {
347
+ uri: 'test/id/controls',
348
+ body: {
349
+ viewTheParticipantList: {enabled: true, panelistEnabled: true, attendeeCount: true},
350
+ },
351
+ method: HTTP_VERBS.PATCH,
352
+ });
353
+ });
354
+ });
355
+
356
+ it('should keep explicitly provided properties and fill in the rest', () => {
357
+ const control = {scope: 'viewTheParticipantList', properties: {enabled: false}};
358
+
359
+ return manager.update(control).then(() => {
360
+ assert.calledOnceWithExactly(request.locusDeltaRequest, {
361
+ uri: 'test/id/controls',
362
+ body: {
363
+ viewTheParticipantList: {enabled: false, panelistEnabled: true, attendeeCount: true},
364
+ },
365
+ method: HTTP_VERBS.PATCH,
366
+ });
367
+ });
368
+ });
369
+
370
+ it('should not fill in properties when all are explicitly provided', () => {
371
+ const control = {scope: 'viewTheParticipantList', properties: {enabled: false, panelistEnabled: false, attendeeCount: false}};
372
+
373
+ return manager.update(control).then(() => {
374
+ assert.calledOnceWithExactly(request.locusDeltaRequest, {
375
+ uri: 'test/id/controls',
376
+ body: {
377
+ viewTheParticipantList: {enabled: false, panelistEnabled: false, attendeeCount: false},
378
+ },
379
+ method: HTTP_VERBS.PATCH,
380
+ });
381
+ });
382
+ });
383
+ });
384
+
385
+ describe('when meeting is not a webinar', () => {
386
+ beforeEach(() => {
387
+ restorable = Util.canUpdate;
388
+ Util.canUpdate = sinon.stub().returns(true);
389
+ manager.set({
390
+ locusUrl: 'test/id',
391
+ displayHints: [],
392
+ getControls: () => ({viewTheParticipantList: {enabled: true, panelistEnabled: true, attendeeCount: true}}),
393
+ isWebinar: () => false,
394
+ });
395
+ });
396
+
397
+ it('should only send enabled property', () => {
398
+ const control = {scope: 'viewTheParticipantList', properties: {}};
399
+
400
+ return manager.update(control).then(() => {
401
+ assert.calledOnceWithExactly(request.locusDeltaRequest, {
402
+ uri: 'test/id/controls',
403
+ body: {
404
+ viewTheParticipantList: {enabled: true},
405
+ },
406
+ method: HTTP_VERBS.PATCH,
407
+ });
408
+ });
409
+ });
410
+
411
+ it('should not include panelistEnabled or attendeeCount even if explicitly provided', () => {
412
+ const control = {scope: 'viewTheParticipantList', properties: {enabled: false, panelistEnabled: true, attendeeCount: true}};
413
+
414
+ return manager.update(control).then(() => {
415
+ assert.calledOnceWithExactly(request.locusDeltaRequest, {
416
+ uri: 'test/id/controls',
417
+ body: {
418
+ viewTheParticipantList: {enabled: false},
419
+ },
420
+ method: HTTP_VERBS.PATCH,
421
+ });
422
+ });
423
+ });
424
+ });
425
+ });
322
426
  });
323
427
 
324
428
  describe('Mute/Unmute All', () => {
@@ -2856,6 +2856,7 @@ describe('plugin-meetings', () => {
2856
2856
 
2857
2857
  // set the info initially as locusInfo.info starts as undefined
2858
2858
  expectedMeeting = {
2859
+ attendee: {},
2859
2860
  coHost: {
2860
2861
  LOWER_SOMEONE_ELSES_HAND: true,
2861
2862
  },
@@ -2864,10 +2865,12 @@ describe('plugin-meetings', () => {
2864
2865
  moderator: {
2865
2866
  LOWER_SOMEONE_ELSES_HAND: true,
2866
2867
  },
2868
+ panelist: {},
2867
2869
  policy: {
2868
2870
  LOCK_STATUS_UNLOCKED: true,
2869
2871
  ROSTER_IN_MEETING: true,
2870
2872
  },
2873
+ presenter: {},
2871
2874
  userDisplayHints: ['ROSTER_IN_MEETING', 'LOCK_STATUS_UNLOCKED'],
2872
2875
  };
2873
2876
  locusInfo.updateMeetingInfo(initialInfo, self);
@@ -2882,6 +2885,7 @@ describe('plugin-meetings', () => {
2882
2885
 
2883
2886
  // Updating with different info should trigger the event
2884
2887
  expectedMeeting = {
2888
+ attendee: {},
2885
2889
  coHost: {
2886
2890
  LOWER_SOMEONE_ELSES_HAND: true,
2887
2891
  LOCK_CONTROL_LOCK: true,
@@ -2891,10 +2895,12 @@ describe('plugin-meetings', () => {
2891
2895
  moderator: {
2892
2896
  LOWER_SOMEONE_ELSES_HAND: true,
2893
2897
  },
2898
+ panelist: {},
2894
2899
  policy: {
2895
2900
  LOCK_STATUS_UNLOCKED: true,
2896
2901
  ROSTER_IN_MEETING: true,
2897
2902
  },
2903
+ presenter: {},
2898
2904
  userDisplayHints: ['ROSTER_IN_MEETING', 'LOCK_STATUS_UNLOCKED'],
2899
2905
  };
2900
2906
  locusInfo.updateMeetingInfo(newInfo, self);
@@ -2903,6 +2909,7 @@ describe('plugin-meetings', () => {
2903
2909
 
2904
2910
  // update it with the same info
2905
2911
  expectedMeeting = {
2912
+ attendee: {},
2906
2913
  coHost: {
2907
2914
  LOWER_SOMEONE_ELSES_HAND: true,
2908
2915
  LOCK_CONTROL_LOCK: true,
@@ -2912,10 +2919,12 @@ describe('plugin-meetings', () => {
2912
2919
  moderator: {
2913
2920
  LOWER_SOMEONE_ELSES_HAND: true,
2914
2921
  },
2922
+ panelist: {},
2915
2923
  policy: {
2916
2924
  LOCK_STATUS_UNLOCKED: true,
2917
2925
  ROSTER_IN_MEETING: true,
2918
2926
  },
2927
+ presenter: {},
2919
2928
  userDisplayHints: ['ROSTER_IN_MEETING', 'LOCK_STATUS_UNLOCKED'],
2920
2929
  };
2921
2930
  locusInfo.updateMeetingInfo(newInfo, self);
@@ -2930,6 +2939,7 @@ describe('plugin-meetings', () => {
2930
2939
  hasRole: true,
2931
2940
  });
2932
2941
  expectedMeeting = {
2942
+ attendee: {},
2933
2943
  coHost: {
2934
2944
  LOWER_SOMEONE_ELSES_HAND: true,
2935
2945
  LOCK_CONTROL_LOCK: true,
@@ -2939,10 +2949,12 @@ describe('plugin-meetings', () => {
2939
2949
  moderator: {
2940
2950
  LOWER_SOMEONE_ELSES_HAND: true,
2941
2951
  },
2952
+ panelist: {},
2942
2953
  policy: {
2943
2954
  LOCK_STATUS_UNLOCKED: true,
2944
2955
  ROSTER_IN_MEETING: true,
2945
2956
  },
2957
+ presenter: {},
2946
2958
  userDisplayHints: [
2947
2959
  'ROSTER_IN_MEETING',
2948
2960
  'LOCK_STATUS_UNLOCKED',
@@ -9,6 +9,9 @@ describe('plugin-meetings', () => {
9
9
  moderator: ['HINT_1', 'HINT_2'],
10
10
  joined: ['HINT_3', 'VOIP_IS_ENABLED'],
11
11
  coHost: ['HINT_4'],
12
+ presenter: ['HINT_5'],
13
+ panelist: ['HINT_6'],
14
+ attendee: ['HINT_7'],
12
15
  },
13
16
  };
14
17
 
@@ -30,6 +33,9 @@ describe('plugin-meetings', () => {
30
33
  policy: {HINT_3: true, VOIP_IS_ENABLED: true},
31
34
  moderator: {HINT_1: true, HINT_2: true, LOWER_SOMEONE_ELSES_HAND: true},
32
35
  coHost: {HINT_4: true, LOWER_SOMEONE_ELSES_HAND: true},
36
+ presenter: {HINT_5: true},
37
+ panelist: {HINT_6: true},
38
+ attendee: {HINT_7: true},
33
39
  userDisplayHints: [
34
40
  'HINT_3',
35
41
  'VOIP_IS_ENABLED',
@@ -43,6 +49,9 @@ describe('plugin-meetings', () => {
43
49
  policy: {HINT_3: true, VOIP_IS_ENABLED: true},
44
50
  moderator: {HINT_1: true, HINT_2: true, LOWER_SOMEONE_ELSES_HAND: true},
45
51
  coHost: {HINT_4: true, LOWER_SOMEONE_ELSES_HAND: true},
52
+ presenter: {HINT_5: true},
53
+ panelist: {HINT_6: true},
54
+ attendee: {HINT_7: true},
46
55
  userDisplayHints: [
47
56
  'HINT_3',
48
57
  'VOIP_IS_ENABLED',
@@ -57,6 +66,9 @@ describe('plugin-meetings', () => {
57
66
  policy: {HINT_3: true, VOIP_IS_ENABLED: true},
58
67
  moderator: {HINT_1: true, HINT_2: true, LOWER_SOMEONE_ELSES_HAND: true},
59
68
  coHost: {HINT_4: true, LOWER_SOMEONE_ELSES_HAND: true},
69
+ presenter: {HINT_5: true},
70
+ panelist: {HINT_6: true},
71
+ attendee: {HINT_7: true},
60
72
  userDisplayHints: ['HINT_3', 'VOIP_IS_ENABLED', 'HINT_4', 'LOWER_SOMEONE_ELSES_HAND'],
61
73
  });
62
74
 
@@ -64,6 +76,9 @@ describe('plugin-meetings', () => {
64
76
  policy: {HINT_3: true, VOIP_IS_ENABLED: true},
65
77
  moderator: {HINT_1: true, HINT_2: true, LOWER_SOMEONE_ELSES_HAND: true},
66
78
  coHost: {HINT_4: true, LOWER_SOMEONE_ELSES_HAND: true},
79
+ presenter: {HINT_5: true},
80
+ panelist: {HINT_6: true},
81
+ attendee: {HINT_7: true},
67
82
  userDisplayHints: ['HINT_3', 'VOIP_IS_ENABLED'],
68
83
  });
69
84
  });
@@ -73,6 +88,9 @@ describe('plugin-meetings', () => {
73
88
  policy: {HINT_3: true, VOIP_IS_ENABLED: true},
74
89
  moderator: {HINT_1: true, HINT_2: true, LOWER_SOMEONE_ELSES_HAND: true},
75
90
  coHost: {HINT_4: true, LOWER_SOMEONE_ELSES_HAND: true},
91
+ presenter: {HINT_5: true},
92
+ panelist: {HINT_6: true},
93
+ attendee: {HINT_7: true},
76
94
  userDisplayHints: ['VOIP_IS_ENABLED', 'HINT_1', 'HINT_2', 'LOWER_SOMEONE_ELSES_HAND'],
77
95
  });
78
96
 
@@ -80,6 +98,9 @@ describe('plugin-meetings', () => {
80
98
  policy: {HINT_3: true, VOIP_IS_ENABLED: true},
81
99
  moderator: {HINT_1: true, HINT_2: true, LOWER_SOMEONE_ELSES_HAND: true},
82
100
  coHost: {HINT_4: true, LOWER_SOMEONE_ELSES_HAND: true},
101
+ presenter: {HINT_5: true},
102
+ panelist: {HINT_6: true},
103
+ attendee: {HINT_7: true},
83
104
  userDisplayHints: [
84
105
  'HINT_3',
85
106
  'VOIP_IS_ENABLED',
@@ -90,10 +111,49 @@ describe('plugin-meetings', () => {
90
111
  });
91
112
  });
92
113
 
114
+ it('merges presenter hints when user has PRESENTER role', () => {
115
+ assert.deepEqual(InfoUtils.parse(info, ['PRESENTER']), {
116
+ policy: {HINT_3: true, VOIP_IS_ENABLED: true},
117
+ moderator: {HINT_1: true, HINT_2: true, LOWER_SOMEONE_ELSES_HAND: true},
118
+ coHost: {HINT_4: true, LOWER_SOMEONE_ELSES_HAND: true},
119
+ presenter: {HINT_5: true},
120
+ panelist: {HINT_6: true},
121
+ attendee: {HINT_7: true},
122
+ userDisplayHints: ['HINT_3', 'VOIP_IS_ENABLED', 'HINT_5'],
123
+ });
124
+ });
125
+
126
+ it('merges panelist hints when user has PANELIST role', () => {
127
+ assert.deepEqual(InfoUtils.parse(info, ['PANELIST']), {
128
+ policy: {HINT_3: true, VOIP_IS_ENABLED: true},
129
+ moderator: {HINT_1: true, HINT_2: true, LOWER_SOMEONE_ELSES_HAND: true},
130
+ coHost: {HINT_4: true, LOWER_SOMEONE_ELSES_HAND: true},
131
+ presenter: {HINT_5: true},
132
+ panelist: {HINT_6: true},
133
+ attendee: {HINT_7: true},
134
+ userDisplayHints: ['HINT_3', 'VOIP_IS_ENABLED', 'HINT_6'],
135
+ });
136
+ });
137
+
138
+ it('merges attendee hints when user has ATTENDEE role', () => {
139
+ assert.deepEqual(InfoUtils.parse(info, ['ATTENDEE']), {
140
+ policy: {HINT_3: true, VOIP_IS_ENABLED: true},
141
+ moderator: {HINT_1: true, HINT_2: true, LOWER_SOMEONE_ELSES_HAND: true},
142
+ coHost: {HINT_4: true, LOWER_SOMEONE_ELSES_HAND: true},
143
+ presenter: {HINT_5: true},
144
+ panelist: {HINT_6: true},
145
+ attendee: {HINT_7: true},
146
+ userDisplayHints: ['HINT_3', 'VOIP_IS_ENABLED', 'HINT_7'],
147
+ });
148
+ });
149
+
93
150
  it('only adds datachannel url when present', () => {
94
151
  assert.deepEqual(InfoUtils.parse({datachannelUrl: 'some url'}, []), {
95
152
  coHost: {LOWER_SOMEONE_ELSES_HAND: true},
96
153
  moderator: {LOWER_SOMEONE_ELSES_HAND: true},
154
+ presenter: {},
155
+ panelist: {},
156
+ attendee: {},
97
157
  datachannelUrl: 'some url',
98
158
  policy: {},
99
159
  userDisplayHints: [],
@@ -102,6 +162,9 @@ describe('plugin-meetings', () => {
102
162
  assert.deepEqual(InfoUtils.parse({}, []), {
103
163
  coHost: {LOWER_SOMEONE_ELSES_HAND: true},
104
164
  moderator: {LOWER_SOMEONE_ELSES_HAND: true},
165
+ presenter: {},
166
+ panelist: {},
167
+ attendee: {},
105
168
  policy: {},
106
169
  userDisplayHints: [],
107
170
  });
@@ -171,6 +234,30 @@ describe('plugin-meetings', () => {
171
234
  LOWER_SOMEONE_ELSES_HAND: true,
172
235
  });
173
236
  });
237
+
238
+ it('parsePresenter calls parseDisplayHintSection correctly and returns the result', () => {
239
+ const result = InfoUtils.parsePresenter(info);
240
+
241
+ assert.calledWith(parseDisplayHintSectionSpy, info, 'presenter');
242
+
243
+ assert.deepEqual(result, parseDisplayHintSectionSpy.firstCall.returnValue);
244
+ });
245
+
246
+ it('parsePanelist calls parseDisplayHintSection correctly and returns the result', () => {
247
+ const result = InfoUtils.parsePanelist(info);
248
+
249
+ assert.calledWith(parseDisplayHintSectionSpy, info, 'panelist');
250
+
251
+ assert.deepEqual(result, parseDisplayHintSectionSpy.firstCall.returnValue);
252
+ });
253
+
254
+ it('parseAttendee calls parseDisplayHintSection correctly and returns the result', () => {
255
+ const result = InfoUtils.parseAttendee(info);
256
+
257
+ assert.calledWith(parseDisplayHintSectionSpy, info, 'attendee');
258
+
259
+ assert.deepEqual(result, parseDisplayHintSectionSpy.firstCall.returnValue);
260
+ });
174
261
  });
175
262
  });
176
263
  });
@@ -115,6 +115,7 @@ describe('plugin-meetings', () => {
115
115
  canAttendeeRequestAiAssistantEnabled: null,
116
116
  isAttendeeRequestAiAssistantDeclinedAll: null,
117
117
  isAnonymizeDisplayNamesEnabled: null,
118
+ canViewTheParticipantList: null,
118
119
 
119
120
  ...expected,
120
121
  };
@@ -236,6 +237,7 @@ describe('plugin-meetings', () => {
236
237
  'canAttendeeRequestAiAssistantEnabled',
237
238
  'isAttendeeRequestAiAssistantDeclinedAll',
238
239
  'isAnonymizeDisplayNamesEnabled',
240
+ 'canViewTheParticipantList',
239
241
  ].forEach((key) => {
240
242
  it(`get and set for ${key} work as expected`, () => {
241
243
  const inMeetingActions = new InMeetingActions();