@webex/plugin-meetings 3.6.0-next.9 → 3.7.0-next.1

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 (118) hide show
  1. package/README.md +2 -1
  2. package/dist/breakouts/breakout.js +1 -1
  3. package/dist/breakouts/index.js +1 -1
  4. package/dist/config.js +2 -1
  5. package/dist/config.js.map +1 -1
  6. package/dist/constants.js +24 -2
  7. package/dist/constants.js.map +1 -1
  8. package/dist/controls-options-manager/enums.js +1 -0
  9. package/dist/controls-options-manager/enums.js.map +1 -1
  10. package/dist/controls-options-manager/index.js +10 -3
  11. package/dist/controls-options-manager/index.js.map +1 -1
  12. package/dist/controls-options-manager/types.js.map +1 -1
  13. package/dist/controls-options-manager/util.js +12 -0
  14. package/dist/controls-options-manager/util.js.map +1 -1
  15. package/dist/interpretation/index.js +1 -1
  16. package/dist/interpretation/siLanguage.js +1 -1
  17. package/dist/locus-info/controlsUtils.js +28 -4
  18. package/dist/locus-info/controlsUtils.js.map +1 -1
  19. package/dist/locus-info/fullState.js +2 -1
  20. package/dist/locus-info/fullState.js.map +1 -1
  21. package/dist/locus-info/index.js +61 -3
  22. package/dist/locus-info/index.js.map +1 -1
  23. package/dist/meeting/in-meeting-actions.js +19 -1
  24. package/dist/meeting/in-meeting-actions.js.map +1 -1
  25. package/dist/meeting/index.js +564 -441
  26. package/dist/meeting/index.js.map +1 -1
  27. package/dist/meeting/locusMediaRequest.js +2 -6
  28. package/dist/meeting/locusMediaRequest.js.map +1 -1
  29. package/dist/meeting/request.js +21 -29
  30. package/dist/meeting/request.js.map +1 -1
  31. package/dist/meeting/util.js +94 -59
  32. package/dist/meeting/util.js.map +1 -1
  33. package/dist/meetings/index.js +2 -0
  34. package/dist/meetings/index.js.map +1 -1
  35. package/dist/members/index.js +3 -2
  36. package/dist/members/index.js.map +1 -1
  37. package/dist/members/util.js +9 -5
  38. package/dist/members/util.js.map +1 -1
  39. package/dist/reachability/clusterReachability.js +0 -4
  40. package/dist/reachability/clusterReachability.js.map +1 -1
  41. package/dist/reachability/index.js +433 -136
  42. package/dist/reachability/index.js.map +1 -1
  43. package/dist/reachability/reachability.types.js +7 -0
  44. package/dist/reachability/reachability.types.js.map +1 -0
  45. package/dist/reachability/request.js +23 -9
  46. package/dist/reachability/request.js.map +1 -1
  47. package/dist/roap/index.js +5 -7
  48. package/dist/roap/index.js.map +1 -1
  49. package/dist/roap/request.js +45 -79
  50. package/dist/roap/request.js.map +1 -1
  51. package/dist/roap/turnDiscovery.js +3 -6
  52. package/dist/roap/turnDiscovery.js.map +1 -1
  53. package/dist/types/config.d.ts +1 -0
  54. package/dist/types/constants.d.ts +19 -0
  55. package/dist/types/controls-options-manager/enums.d.ts +2 -1
  56. package/dist/types/controls-options-manager/index.d.ts +2 -1
  57. package/dist/types/controls-options-manager/types.d.ts +2 -0
  58. package/dist/types/locus-info/index.d.ts +9 -0
  59. package/dist/types/meeting/in-meeting-actions.d.ts +18 -0
  60. package/dist/types/meeting/index.d.ts +14 -3
  61. package/dist/types/meeting/locusMediaRequest.d.ts +2 -3
  62. package/dist/types/meeting/request.d.ts +2 -2
  63. package/dist/types/meeting/util.d.ts +2 -2
  64. package/dist/types/meetings/index.d.ts +1 -1
  65. package/dist/types/members/index.d.ts +2 -1
  66. package/dist/types/members/util.d.ts +3 -1
  67. package/dist/types/reachability/clusterReachability.d.ts +1 -10
  68. package/dist/types/reachability/index.d.ts +74 -35
  69. package/dist/types/reachability/reachability.types.d.ts +64 -0
  70. package/dist/types/reachability/request.d.ts +5 -1
  71. package/dist/types/roap/request.d.ts +1 -13
  72. package/dist/webinar/index.js +32 -19
  73. package/dist/webinar/index.js.map +1 -1
  74. package/package.json +22 -22
  75. package/src/config.ts +1 -0
  76. package/src/constants.ts +25 -0
  77. package/src/controls-options-manager/enums.ts +1 -0
  78. package/src/controls-options-manager/index.ts +19 -2
  79. package/src/controls-options-manager/types.ts +2 -0
  80. package/src/controls-options-manager/util.ts +12 -0
  81. package/src/locus-info/controlsUtils.ts +46 -2
  82. package/src/locus-info/fullState.ts +1 -0
  83. package/src/locus-info/index.ts +60 -0
  84. package/src/meeting/in-meeting-actions.ts +37 -0
  85. package/src/meeting/index.ts +114 -11
  86. package/src/meeting/locusMediaRequest.ts +4 -8
  87. package/src/meeting/request.ts +4 -11
  88. package/src/meeting/util.ts +24 -4
  89. package/src/meetings/index.ts +46 -39
  90. package/src/members/index.ts +4 -2
  91. package/src/members/util.ts +3 -1
  92. package/src/reachability/clusterReachability.ts +1 -14
  93. package/src/reachability/index.ts +285 -77
  94. package/src/reachability/reachability.types.ts +85 -0
  95. package/src/reachability/request.ts +55 -30
  96. package/src/roap/index.ts +4 -5
  97. package/src/roap/request.ts +30 -44
  98. package/src/roap/turnDiscovery.ts +2 -4
  99. package/src/webinar/index.ts +31 -17
  100. package/test/unit/spec/controls-options-manager/index.js +56 -32
  101. package/test/unit/spec/controls-options-manager/util.js +44 -0
  102. package/test/unit/spec/locus-info/controlsUtils.js +80 -4
  103. package/test/unit/spec/locus-info/index.js +59 -2
  104. package/test/unit/spec/meeting/in-meeting-actions.ts +18 -0
  105. package/test/unit/spec/meeting/index.js +231 -100
  106. package/test/unit/spec/meeting/locusMediaRequest.ts +18 -11
  107. package/test/unit/spec/meeting/request.js +3 -26
  108. package/test/unit/spec/meeting/utils.js +53 -13
  109. package/test/unit/spec/meetings/index.js +16 -1
  110. package/test/unit/spec/members/index.js +25 -2
  111. package/test/unit/spec/members/request.js +37 -3
  112. package/test/unit/spec/members/utils.js +15 -1
  113. package/test/unit/spec/reachability/index.ts +265 -1
  114. package/test/unit/spec/reachability/request.js +56 -15
  115. package/test/unit/spec/roap/index.ts +1 -1
  116. package/test/unit/spec/roap/request.ts +51 -109
  117. package/test/unit/spec/roap/turnDiscovery.ts +202 -147
  118. package/test/unit/spec/webinar/index.ts +82 -16
package/src/roap/index.ts CHANGED
@@ -107,7 +107,7 @@ export default class Roap extends StatelessWebexPlugin {
107
107
  roapMessage,
108
108
  locusSelfUrl: meeting.selfUrl,
109
109
  mediaId: options.mediaId,
110
- meetingId: meeting.id,
110
+ isMultistream: meeting.isMultistream,
111
111
  locusMediaRequest: meeting.locusMediaRequest,
112
112
  })
113
113
  .then(() => {
@@ -141,7 +141,7 @@ export default class Roap extends StatelessWebexPlugin {
141
141
  roapMessage,
142
142
  locusSelfUrl: meeting.selfUrl,
143
143
  mediaId: options.mediaId,
144
- meetingId: meeting.id,
144
+ isMultistream: meeting.isMultistream,
145
145
  locusMediaRequest: meeting.locusMediaRequest,
146
146
  });
147
147
  }
@@ -170,7 +170,7 @@ export default class Roap extends StatelessWebexPlugin {
170
170
  roapMessage,
171
171
  locusSelfUrl: meeting.selfUrl,
172
172
  mediaId: options.mediaId,
173
- meetingId: meeting.id,
173
+ isMultistream: meeting.isMultistream,
174
174
  locusMediaRequest: meeting.locusMediaRequest,
175
175
  })
176
176
  .then(() => {
@@ -207,10 +207,9 @@ export default class Roap extends StatelessWebexPlugin {
207
207
  roapMessage,
208
208
  locusSelfUrl: meeting.selfUrl,
209
209
  mediaId: sendEmptyMediaId ? '' : meeting.mediaId,
210
- meetingId: meeting.id,
210
+ isMultistream: meeting.isMultistream,
211
211
  preferTranscoding: !meeting.isMultistream,
212
212
  locusMediaRequest: meeting.locusMediaRequest,
213
- ipVersion: MeetingUtil.getIpVersion(meeting.webex),
214
213
  })
215
214
  .then(({locus, mediaConnections}) => {
216
215
  if (mediaConnections) {
@@ -4,44 +4,13 @@ import {StatelessWebexPlugin} from '@webex/webex-core';
4
4
  import LoggerProxy from '../common/logs/logger-proxy';
5
5
  import {IP_VERSION, REACHABILITY} from '../constants';
6
6
  import {LocusMediaRequest} from '../meeting/locusMediaRequest';
7
+ import MeetingUtil from '../meeting/util';
8
+ import {ClientMediaPreferences} from '../reachability/reachability.types';
7
9
 
8
10
  /**
9
11
  * @class RoapRequest
10
12
  */
11
13
  export default class RoapRequest extends StatelessWebexPlugin {
12
- /**
13
- * Returns reachability data.
14
- * @param {Object} localSdp
15
- * @returns {Object}
16
- */
17
- async attachReachabilityData(localSdp) {
18
- let joinCookie;
19
-
20
- // @ts-ignore
21
- const reachabilityResult = await this.webex.meetings.reachability.getReachabilityResults();
22
-
23
- if (reachabilityResult && Object.keys(reachabilityResult).length) {
24
- localSdp.reachability = reachabilityResult;
25
- }
26
-
27
- // @ts-ignore
28
- const joinCookieRaw = await this.webex.boundedStorage
29
- .get(REACHABILITY.namespace, REACHABILITY.localStorageJoinCookie)
30
- .catch(() => {});
31
-
32
- if (joinCookieRaw) {
33
- try {
34
- joinCookie = JSON.parse(joinCookieRaw);
35
- } catch (e) {
36
- LoggerProxy.logger.error(
37
- `MeetingRequest#constructor --> Error in parsing join cookie data: ${e}`
38
- );
39
- }
40
- }
41
-
42
- return {localSdp, joinCookie};
43
- }
44
-
45
14
  /**
46
15
  * Sends a ROAP message
47
16
  * @param {Object} options
@@ -50,18 +19,16 @@ export default class RoapRequest extends StatelessWebexPlugin {
50
19
  * @param {String} options.mediaId
51
20
  * @param {String} options.correlationId
52
21
  * @param {String} options.meetingId
53
- * @param {IP_VERSION} options.ipVersion only required for offers
54
22
  * @returns {Promise} returns the response/failure of the request
55
23
  */
56
24
  async sendRoap(options: {
57
25
  roapMessage: any;
58
26
  locusSelfUrl: string;
59
27
  mediaId: string;
60
- meetingId: string;
61
- ipVersion?: IP_VERSION;
28
+ isMultistream: boolean;
62
29
  locusMediaRequest?: LocusMediaRequest;
63
30
  }) {
64
- const {roapMessage, locusSelfUrl, mediaId, locusMediaRequest, ipVersion} = options;
31
+ const {roapMessage, locusSelfUrl, isMultistream, mediaId, locusMediaRequest} = options;
65
32
 
66
33
  if (!mediaId) {
67
34
  LoggerProxy.logger.info('Roap:request#sendRoap --> sending empty mediaID');
@@ -74,13 +41,33 @@ export default class RoapRequest extends StatelessWebexPlugin {
74
41
 
75
42
  return Promise.reject(new Error('sendRoap called when locusMediaRequest is undefined'));
76
43
  }
77
- const {localSdp: localSdpWithReachabilityData, joinCookie} = await this.attachReachabilityData({
78
- roapMessage,
79
- });
44
+
45
+ let reachability;
46
+ let clientMediaPreferences: ClientMediaPreferences = {
47
+ // bare minimum fallback value that should allow us to join;
48
+ joinCookie: undefined,
49
+ ipver: IP_VERSION.unknown,
50
+ preferTranscoding: !isMultistream,
51
+ };
52
+
53
+ try {
54
+ clientMediaPreferences =
55
+ // @ts-ignore
56
+ await this.webex.meetings.reachability.getClientMediaPreferences(
57
+ isMultistream,
58
+ // @ts-ignore
59
+ MeetingUtil.getIpVersion(this.webex)
60
+ );
61
+ reachability =
62
+ // @ts-ignore
63
+ await this.webex.meetings.reachability.getReachabilityReportToAttachToRoap();
64
+ } catch (error) {
65
+ LoggerProxy.logger.error('Roap:request#sendRoap --> reachability error:', error);
66
+ }
80
67
 
81
68
  LoggerProxy.logger.info(
82
69
  `Roap:request#sendRoap --> ${roapMessage.messageType} seq:${roapMessage.seq} ${
83
- ipVersion ? `ipver=${ipVersion} ` : ''
70
+ clientMediaPreferences?.ipver ? `ipver=${clientMediaPreferences?.ipver} ` : ''
84
71
  } ${locusSelfUrl}`
85
72
  );
86
73
 
@@ -88,11 +75,10 @@ export default class RoapRequest extends StatelessWebexPlugin {
88
75
  .send({
89
76
  type: 'RoapMessage',
90
77
  selfUrl: locusSelfUrl,
91
- joinCookie,
92
78
  mediaId,
93
79
  roapMessage,
94
- reachability: localSdpWithReachabilityData.reachability,
95
- ipVersion,
80
+ reachability,
81
+ clientMediaPreferences,
96
82
  })
97
83
  .then((res) => {
98
84
  // always it will be the first mediaConnection Object
@@ -408,10 +408,8 @@ export default class TurnDiscovery {
408
408
  locusSelfUrl: meeting.selfUrl,
409
409
  // @ts-ignore - Fix missing type
410
410
  mediaId: isReconnecting ? '' : meeting.mediaId,
411
- meetingId: meeting.id,
411
+ isMultistream: meeting.isMultistream,
412
412
  locusMediaRequest: meeting.locusMediaRequest,
413
- // @ts-ignore - because of meeting.webex
414
- ipVersion: MeetingUtil.getIpVersion(meeting.webex),
415
413
  })
416
414
  .then(async (response) => {
417
415
  const {mediaConnections} = response;
@@ -451,7 +449,7 @@ export default class TurnDiscovery {
451
449
  locusSelfUrl: meeting.selfUrl,
452
450
  // @ts-ignore - fix type
453
451
  mediaId: meeting.mediaId,
454
- meetingId: meeting.id,
452
+ isMultistream: meeting.isMultistream,
455
453
  locusMediaRequest: meeting.locusMediaRequest,
456
454
  });
457
455
  }
@@ -2,7 +2,8 @@
2
2
  * Copyright (c) 2015-2023 Cisco Systems, Inc. See LICENSE file.
3
3
  */
4
4
  import {WebexPlugin} from '@webex/webex-core';
5
- import {MEETINGS} from '../constants';
5
+ import {get} from 'lodash';
6
+ import {MEETINGS, SELF_ROLES} from '../constants';
6
7
 
7
8
  import WebinarCollection from './collection';
8
9
 
@@ -17,14 +18,15 @@ const Webinar = WebexPlugin.extend({
17
18
 
18
19
  props: {
19
20
  locusUrl: 'string', // appears current webinar's locus url
20
- webcastUrl: 'string', // current webinar's webcast url
21
- webinarAttendeesSearchingUrl: 'string', // current webinarAttendeesSearching url
21
+ webcastInstanceUrl: 'string', // current webinar's webcast instance url
22
22
  canManageWebcast: 'boolean', // appears the ability to manage webcast
23
+ selfIsPanelist: 'boolean', // self is panelist
24
+ selfIsAttendee: 'boolean', // self is attendee
23
25
  },
24
26
 
25
27
  /**
26
28
  * Update the current locus url of the webinar
27
- * @param {string} locusUrl // locus url
29
+ * @param {string} locusUrl
28
30
  * @returns {void}
29
31
  */
30
32
  locusUrlUpdate(locusUrl) {
@@ -32,21 +34,12 @@ const Webinar = WebexPlugin.extend({
32
34
  },
33
35
 
34
36
  /**
35
- * Update the current webcast url of the meeting
36
- * @param {string} webcastUrl // webcast url
37
+ * Update the current webcast instance url of the meeting
38
+ * @param {object} payload
37
39
  * @returns {void}
38
40
  */
39
- webcastUrlUpdate(webcastUrl) {
40
- this.set('webcastUrl', webcastUrl);
41
- },
42
-
43
- /**
44
- * Update the current webinarAttendeesSearching url of the meeting
45
- * @param {string} webinarAttendeesSearchingUrl // webinarAttendeesSearching url
46
- * @returns {void}
47
- */
48
- webinarAttendeesSearchingUrlUpdate(webinarAttendeesSearchingUrl) {
49
- this.set('webinarAttendeesSearchingUrl', webinarAttendeesSearchingUrl);
41
+ updateWebcastUrl(payload) {
42
+ this.set('webcastInstanceUrl', get(payload, 'resources.webcastInstance.url'));
50
43
  },
51
44
 
52
45
  /**
@@ -57,6 +50,27 @@ const Webinar = WebexPlugin.extend({
57
50
  updateCanManageWebcast(canManageWebcast) {
58
51
  this.set('canManageWebcast', canManageWebcast);
59
52
  },
53
+
54
+ /**
55
+ * Updates user roles and manages associated state transitions
56
+ * @param {object} payload
57
+ * @param {string[]} payload.oldRoles - Previous roles of the user
58
+ * @param {string[]} payload.newRoles - New roles of the user
59
+ * @returns {{isPromoted: boolean, isDemoted: boolean}} Role transition states
60
+ */
61
+ updateRoleChanged(payload) {
62
+ const isPromoted =
63
+ get(payload, 'oldRoles', []).includes(SELF_ROLES.ATTENDEE) &&
64
+ get(payload, 'newRoles', []).includes(SELF_ROLES.PANELIST);
65
+ const isDemoted =
66
+ get(payload, 'oldRoles', []).includes(SELF_ROLES.PANELIST) &&
67
+ get(payload, 'newRoles', []).includes(SELF_ROLES.ATTENDEE);
68
+ this.set('selfIsPanelist', get(payload, 'newRoles', []).includes(SELF_ROLES.PANELIST));
69
+ this.set('selfIsAttendee', get(payload, 'newRoles', []).includes(SELF_ROLES.ATTENDEE));
70
+ this.updateCanManageWebcast(get(payload, 'newRoles', []).includes(SELF_ROLES.MODERATOR));
71
+
72
+ return {isPromoted, isDemoted};
73
+ },
60
74
  });
61
75
 
62
76
  export default Webinar;
@@ -22,7 +22,7 @@ describe('plugin-meetings', () => {
22
22
 
23
23
  describe('Mute On Entry', () => {
24
24
  let manager;
25
-
25
+
26
26
  beforeEach(() => {
27
27
  request = {
28
28
  request: sinon.stub().returns(Promise.resolve()),
@@ -37,85 +37,85 @@ describe('plugin-meetings', () => {
37
37
  });
38
38
 
39
39
  describe('setMuteOnEntry', () => {
40
- it('rejects when correct display hint is not present enabled=false', () => {
40
+ it('rejects when correct display hint is not present enabled=false', () => {
41
41
  const result = manager.setMuteOnEntry(false);
42
-
42
+
43
43
  assert.notCalled(request.request);
44
-
44
+
45
45
  assert.isRejected(result);
46
46
  });
47
47
 
48
- it('rejects when correct display hint is not present enabled=true', () => {
48
+ it('rejects when correct display hint is not present enabled=true', () => {
49
49
  const result = manager.setMuteOnEntry(true);
50
-
50
+
51
51
  assert.notCalled(request.request);
52
-
52
+
53
53
  assert.isRejected(result);
54
54
  });
55
-
55
+
56
56
  it('can set mute on entry when the display hint is available enabled=true', () => {
57
57
  manager.setDisplayHints(['ENABLE_MUTE_ON_ENTRY']);
58
-
58
+
59
59
  const result = manager.setMuteOnEntry(true);
60
-
60
+
61
61
  assert.calledWith(request.request, { uri: 'test/id/controls',
62
62
  body: { muteOnEntry: { enabled: true } },
63
63
  method: HTTP_VERBS.PATCH});
64
-
64
+
65
65
  assert.deepEqual(result, request.request.firstCall.returnValue);
66
66
  });
67
67
 
68
68
  it('can set mute on entry when the display hint is available enabled=false', () => {
69
69
  manager.setDisplayHints(['DISABLE_MUTE_ON_ENTRY']);
70
-
70
+
71
71
  const result = manager.setMuteOnEntry(false);
72
-
72
+
73
73
  assert.calledWith(request.request, { uri: 'test/id/controls',
74
74
  body: { muteOnEntry: { enabled: false } },
75
75
  method: HTTP_VERBS.PATCH});
76
-
76
+
77
77
  assert.deepEqual(result, request.request.firstCall.returnValue);
78
78
  });
79
79
  });
80
80
 
81
81
  describe('setDisallowUnmute', () => {
82
- it('rejects when correct display hint is not present enabled=false', () => {
82
+ it('rejects when correct display hint is not present enabled=false', () => {
83
83
  const result = manager.setDisallowUnmute(false);
84
-
84
+
85
85
  assert.notCalled(request.request);
86
-
86
+
87
87
  assert.isRejected(result);
88
88
  });
89
89
 
90
- it('rejects when correct display hint is not present enabled=true', () => {
90
+ it('rejects when correct display hint is not present enabled=true', () => {
91
91
  const result = manager.setDisallowUnmute(true);
92
-
92
+
93
93
  assert.notCalled(request.request);
94
-
94
+
95
95
  assert.isRejected(result);
96
96
  });
97
-
98
- it('can set mute on entry when the display hint is available enabled=true', () => {
97
+
98
+ it('can set disallow unmute when ENABLE_HARD_MUTE display hint is available', () => {
99
99
  manager.setDisplayHints(['ENABLE_HARD_MUTE']);
100
-
100
+
101
101
  const result = manager.setDisallowUnmute(true);
102
-
102
+
103
103
  assert.calledWith(request.request, { uri: 'test/id/controls',
104
104
  body: { disallowUnmute: { enabled: true } },
105
105
  method: HTTP_VERBS.PATCH});
106
-
106
+
107
107
  assert.deepEqual(result, request.request.firstCall.returnValue);
108
108
  });
109
109
 
110
- it('can set mute on entry when the display hint is available enabled=false', () => {
110
+ it('can set allow unmute when DISABLE_HARD_MUTE display hint is available', () => {
111
111
  manager.setDisplayHints(['DISABLE_HARD_MUTE']);
112
-
112
+
113
113
  const result = manager.setDisallowUnmute(false);
114
-
114
+
115
115
  assert.calledWith(request.request, { uri: 'test/id/controls',
116
116
  body: { disallowUnmute: { enabled: false } },
117
117
  method: HTTP_VERBS.PATCH});
118
-
118
+
119
119
  assert.deepEqual(result, request.request.firstCall.returnValue);
120
120
  });
121
121
  });
@@ -218,7 +218,7 @@ describe('plugin-meetings', () => {
218
218
  })
219
219
  });
220
220
 
221
- it('rejects when correct display hint is not present mutedEnabled=false', () => {
221
+ it('rejects when correct display hint is not present mutedEnabled=false', () => {
222
222
  const result = manager.setMuteAll(false, false, false);
223
223
 
224
224
  assert.notCalled(request.request);
@@ -226,7 +226,7 @@ describe('plugin-meetings', () => {
226
226
  assert.isRejected(result);
227
227
  });
228
228
 
229
- it('rejects when correct display hint is not present mutedEnabled=true', () => {
229
+ it('rejects when correct display hint is not present mutedEnabled=true', () => {
230
230
  const result = manager.setMuteAll(true, false, false);
231
231
 
232
232
  assert.notCalled(request.request);
@@ -281,7 +281,31 @@ describe('plugin-meetings', () => {
281
281
 
282
282
  assert.deepEqual(result, request.request.firstCall.returnValue);
283
283
  });
284
+
285
+ it('can set mute all panelists when the display hint is available mutedEnabled=true', () => {
286
+ manager.setDisplayHints(['MUTE_ALL', 'DISABLE_HARD_MUTE', 'DISABLE_MUTE_ON_ENTRY']);
287
+
288
+ const result = manager.setMuteAll(true, true, true, ['panelist']);
289
+
290
+ assert.calledWith(request.request, { uri: 'test/id/controls',
291
+ body: { audio: { muted: true, disallowUnmute: true, muteOnEntry: true, roles: ['panelist'] } },
292
+ method: HTTP_VERBS.PATCH});
293
+
294
+ assert.deepEqual(result, request.request.firstCall.returnValue);
295
+ });
296
+
297
+ it('can set mute all attendees when the display hint is available mutedEnabled=true', () => {
298
+ manager.setDisplayHints(['MUTE_ALL', 'DISABLE_HARD_MUTE', 'DISABLE_MUTE_ON_ENTRY']);
299
+
300
+ const result = manager.setMuteAll(true, true, true, ['attendee']);
301
+
302
+ assert.calledWith(request.request, { uri: 'test/id/controls',
303
+ body: { audio: { muted: true, disallowUnmute: true, muteOnEntry: true, roles: ['attendee'] } },
304
+ method: HTTP_VERBS.PATCH});
305
+
306
+ assert.deepEqual(result, request.request.firstCall.returnValue);
307
+ });
284
308
  });
285
309
  });
286
310
  });
287
- });
311
+ });
@@ -348,6 +348,50 @@ describe('plugin-meetings', () => {
348
348
  });
349
349
  });
350
350
 
351
+ it('should call hasHints() with proper hints when `panelistEnabled` is true, attendeeCount is false', () => {
352
+ ControlsOptionsUtil.canUpdateViewTheParticipantsList({properties: {enabled: true, panelistEnabled: true, attendeeCount: false}}, []);
353
+
354
+ assert.calledWith(ControlsOptionsUtil.hasHints, {
355
+ requiredHints: [DISPLAY_HINTS.ENABLE_VIEW_THE_PARTICIPANT_LIST,
356
+ DISPLAY_HINTS.ENABLE_VIEW_THE_PARTICIPANT_LIST_PANELIST,
357
+ DISPLAY_HINTS.DISABLE_SHOW_ATTENDEE_COUNT],
358
+ displayHints: [],
359
+ });
360
+ });
361
+
362
+ it('should call hasHints() with proper hints when `panelistEnabled` is true, attendeeCount is true', () => {
363
+ ControlsOptionsUtil.canUpdateViewTheParticipantsList({properties: {enabled: true, panelistEnabled: true, attendeeCount: true}}, []);
364
+
365
+ assert.calledWith(ControlsOptionsUtil.hasHints, {
366
+ requiredHints: [DISPLAY_HINTS.ENABLE_VIEW_THE_PARTICIPANT_LIST,
367
+ DISPLAY_HINTS.ENABLE_VIEW_THE_PARTICIPANT_LIST_PANELIST,
368
+ DISPLAY_HINTS.ENABLE_SHOW_ATTENDEE_COUNT],
369
+ displayHints: [],
370
+ });
371
+ });
372
+
373
+ it('should call hasHints() with proper hints when `panelistEnabled` is false, attendeeCount is false', () => {
374
+ ControlsOptionsUtil.canUpdateViewTheParticipantsList({properties: {enabled: true, panelistEnabled: false, attendeeCount: false}}, []);
375
+
376
+ assert.calledWith(ControlsOptionsUtil.hasHints, {
377
+ requiredHints: [DISPLAY_HINTS.ENABLE_VIEW_THE_PARTICIPANT_LIST,
378
+ DISPLAY_HINTS.DISABLE_VIEW_THE_PARTICIPANT_LIST_PANELIST,
379
+ DISPLAY_HINTS.DISABLE_SHOW_ATTENDEE_COUNT],
380
+ displayHints: [],
381
+ });
382
+ });
383
+
384
+ it('should call hasHints() with proper hints when `panelistEnabled` is false, attendeeCount is true', () => {
385
+ ControlsOptionsUtil.canUpdateViewTheParticipantsList({properties: {enabled: true, panelistEnabled: false, attendeeCount: true}}, []);
386
+
387
+ assert.calledWith(ControlsOptionsUtil.hasHints, {
388
+ requiredHints: [DISPLAY_HINTS.ENABLE_VIEW_THE_PARTICIPANT_LIST,
389
+ DISPLAY_HINTS.DISABLE_VIEW_THE_PARTICIPANT_LIST_PANELIST,
390
+ DISPLAY_HINTS.ENABLE_SHOW_ATTENDEE_COUNT],
391
+ displayHints: [],
392
+ });
393
+ });
394
+
351
395
  it('should return the resolution of hasHints()', () => {
352
396
  const expected = 'example-return-value';
353
397
  ControlsOptionsUtil.hasHints.returns(expected);
@@ -82,11 +82,13 @@ describe('plugin-meetings', () => {
82
82
  });
83
83
 
84
84
  it('should parse the viewTheParticipantList control', () => {
85
- const newControls = {viewTheParticipantList: {enabled: true}};
85
+ const newControls = {viewTheParticipantList: {enabled: true, panelistEnabled: true, attendeeCount: false}};
86
86
 
87
87
  const parsedControls = ControlsUtils.parse(newControls);
88
88
 
89
89
  assert.equal(parsedControls.viewTheParticipantList.enabled, newControls.viewTheParticipantList.enabled);
90
+ assert.equal(parsedControls.viewTheParticipantList.panelistEnabled, newControls.viewTheParticipantList.panelistEnabled);
91
+ assert.equal(parsedControls.viewTheParticipantList.attendeeCount, newControls.viewTheParticipantList.attendeeCount);
90
92
  });
91
93
 
92
94
  it('should parse the raiseHand control', () => {
@@ -105,6 +107,42 @@ describe('plugin-meetings', () => {
105
107
  assert.equal(parsedControls.video.enabled, newControls.video.enabled);
106
108
  });
107
109
 
110
+ it('should parse the webcast control', () => {
111
+ const newControls = {webcastControl: {streaming: true}};
112
+
113
+ const parsedControls = ControlsUtils.parse(newControls);
114
+
115
+ assert.equal(parsedControls.webcastControl.streaming, newControls.webcastControl.streaming);
116
+ });
117
+
118
+ it('should parse the meeting full control', () => {
119
+ const newControls = {meetingFull: {meetingFull: true, meetingPanelistFull: false}};
120
+
121
+ const parsedControls = ControlsUtils.parse(newControls);
122
+
123
+ assert.equal(parsedControls.meetingFull.meetingFull, newControls.meetingFull.meetingFull);
124
+ assert.equal(parsedControls.meetingFull.meetingPanelistFull, newControls.meetingFull.meetingPanelistFull);
125
+ });
126
+
127
+ it('should parse the practiceSession control', () => {
128
+ const newControls = {practiceSession: {enabled: true}};
129
+
130
+ const parsedControls = ControlsUtils.parse(newControls);
131
+
132
+ assert.equal(parsedControls.practiceSession.enabled, newControls.practiceSession.enabled);
133
+ });
134
+
135
+ it('should parse the videoLayout control', () => {
136
+ const newControls = {videoLayout: {overrideDefault: true, lockAttendeeViewOnStageOnly:false, stageParameters: {}}};
137
+
138
+ const parsedControls = ControlsUtils.parse(newControls);
139
+
140
+ assert.equal(parsedControls.videoLayout.overrideDefault, newControls.videoLayout.overrideDefault);
141
+ assert.equal(parsedControls.videoLayout.lockAttendeeViewOnStageOnly, newControls.videoLayout.lockAttendeeViewOnStageOnly);
142
+ assert.equal(parsedControls.videoLayout.stageParameters, newControls.videoLayout.stageParameters);
143
+
144
+ });
145
+
108
146
  describe('videoEnabled', () => {
109
147
  it('returns expected', () => {
110
148
  const result = ControlsUtils.parse({video: {enabled: true}});
@@ -170,11 +208,21 @@ describe('plugin-meetings', () => {
170
208
  });
171
209
 
172
210
  it('returns hasViewTheParticipantListChanged = true when changed', () => {
173
- const newControls = {viewTheParticipantList: {enabled: true}};
211
+ const oldControls = {viewTheParticipantList: {enabled: true, panelistEnabled: true, attendeeCount: false}};
174
212
 
175
- const {updates} = ControlsUtils.getControls(defaultControls, newControls);
213
+ let result = ControlsUtils.getControls(oldControls, {viewTheParticipantList: {enabled: false, panelistEnabled: true, attendeeCount: false}});
214
+
215
+ assert.equal(result.updates.hasViewTheParticipantListChanged, true);
216
+
217
+ result = ControlsUtils.getControls(oldControls, {viewTheParticipantList: {enabled: true, panelistEnabled: false, attendeeCount: false}});
176
218
 
177
- assert.equal(updates.hasViewTheParticipantListChanged, true);
219
+ assert.equal(result.updates.hasViewTheParticipantListChanged, true);
220
+ result = ControlsUtils.getControls(oldControls, {viewTheParticipantList: {enabled: true, panelistEnabled: true, attendeeCount: true}});
221
+
222
+ assert.equal(result.updates.hasViewTheParticipantListChanged, true);
223
+ result = ControlsUtils.getControls(oldControls, {viewTheParticipantList: {enabled: true, panelistEnabled: true, attendeeCount: false}});
224
+
225
+ assert.equal(result.updates.hasViewTheParticipantListChanged, false);
178
226
  });
179
227
 
180
228
  it('returns hasRaiseHandChanged = true when changed', () => {
@@ -193,6 +241,34 @@ describe('plugin-meetings', () => {
193
241
  assert.equal(updates.hasVideoChanged, true);
194
242
  });
195
243
 
244
+ it('returns hasWebcastChanged = true when changed', () => {
245
+ const newControls = {webcastControl: {streaming: true}};
246
+
247
+ const {updates} = ControlsUtils.getControls(defaultControls, newControls);
248
+
249
+ assert.equal(updates.hasWebcastChanged, true);
250
+ });
251
+
252
+ it('returns hasMeetingFullChanged = true when changed', () => {
253
+ const newControls = {meetingFull: {meetingFull: true, meetingPanelistFull: false}};
254
+
255
+ let result = ControlsUtils.getControls(defaultControls, newControls);
256
+
257
+ assert.equal(result.updates.hasMeetingFullChanged, true);
258
+
259
+ result = ControlsUtils.getControls(newControls, {meetingFull: {meetingFull: true, meetingPanelistFull: true}});
260
+
261
+ assert.equal(result.updates.hasMeetingFullChanged, true);
262
+ });
263
+
264
+ it('returns hasPracticeSessionEnabledChanged = true when changed', () => {
265
+ const newControls = {practiceSession: {enabled: true}};
266
+
267
+ const {updates} = ControlsUtils.getControls(defaultControls, newControls);
268
+
269
+ assert.equal(updates.hasPracticeSessionEnabledChanged, true);
270
+ });
271
+
196
272
  it('returns hasEntryExitToneChanged = true when mode changed', () => {
197
273
  const newControls = {
198
274
  entryExitTone: {