@webex/plugin-meetings 3.12.0-next.5 → 3.12.0-next.50

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 (136) hide show
  1. package/AGENTS.md +9 -0
  2. package/dist/aiEnableRequest/index.js +15 -2
  3. package/dist/aiEnableRequest/index.js.map +1 -1
  4. package/dist/breakouts/breakout.js +6 -2
  5. package/dist/breakouts/breakout.js.map +1 -1
  6. package/dist/breakouts/index.js +1 -1
  7. package/dist/config.js +1 -0
  8. package/dist/config.js.map +1 -1
  9. package/dist/constants.js +6 -3
  10. package/dist/constants.js.map +1 -1
  11. package/dist/controls-options-manager/constants.js +11 -1
  12. package/dist/controls-options-manager/constants.js.map +1 -1
  13. package/dist/controls-options-manager/index.js +38 -24
  14. package/dist/controls-options-manager/index.js.map +1 -1
  15. package/dist/controls-options-manager/util.js +91 -0
  16. package/dist/controls-options-manager/util.js.map +1 -1
  17. package/dist/hashTree/constants.js +10 -1
  18. package/dist/hashTree/constants.js.map +1 -1
  19. package/dist/hashTree/hashTreeParser.js +593 -358
  20. package/dist/hashTree/hashTreeParser.js.map +1 -1
  21. package/dist/hashTree/utils.js +22 -0
  22. package/dist/hashTree/utils.js.map +1 -1
  23. package/dist/index.js +7 -0
  24. package/dist/index.js.map +1 -1
  25. package/dist/interceptors/locusRetry.js +23 -8
  26. package/dist/interceptors/locusRetry.js.map +1 -1
  27. package/dist/interpretation/index.js +10 -1
  28. package/dist/interpretation/index.js.map +1 -1
  29. package/dist/interpretation/siLanguage.js +1 -1
  30. package/dist/locus-info/controlsUtils.js +4 -1
  31. package/dist/locus-info/controlsUtils.js.map +1 -1
  32. package/dist/locus-info/index.js +277 -86
  33. package/dist/locus-info/index.js.map +1 -1
  34. package/dist/locus-info/types.js +16 -0
  35. package/dist/locus-info/types.js.map +1 -1
  36. package/dist/media/properties.js +1 -0
  37. package/dist/media/properties.js.map +1 -1
  38. package/dist/meeting/in-meeting-actions.js +3 -1
  39. package/dist/meeting/in-meeting-actions.js.map +1 -1
  40. package/dist/meeting/index.js +842 -521
  41. package/dist/meeting/index.js.map +1 -1
  42. package/dist/meeting/util.js +19 -2
  43. package/dist/meeting/util.js.map +1 -1
  44. package/dist/meetings/index.js +199 -77
  45. package/dist/meetings/index.js.map +1 -1
  46. package/dist/meetings/meetings.types.js +6 -1
  47. package/dist/meetings/meetings.types.js.map +1 -1
  48. package/dist/meetings/request.js +39 -0
  49. package/dist/meetings/request.js.map +1 -1
  50. package/dist/meetings/util.js +67 -5
  51. package/dist/meetings/util.js.map +1 -1
  52. package/dist/member/index.js +10 -0
  53. package/dist/member/index.js.map +1 -1
  54. package/dist/member/types.js.map +1 -1
  55. package/dist/member/util.js +3 -0
  56. package/dist/member/util.js.map +1 -1
  57. package/dist/metrics/constants.js +2 -1
  58. package/dist/metrics/constants.js.map +1 -1
  59. package/dist/recording-controller/index.js +1 -3
  60. package/dist/recording-controller/index.js.map +1 -1
  61. package/dist/types/config.d.ts +1 -0
  62. package/dist/types/constants.d.ts +2 -0
  63. package/dist/types/controls-options-manager/constants.d.ts +6 -1
  64. package/dist/types/controls-options-manager/index.d.ts +10 -0
  65. package/dist/types/hashTree/constants.d.ts +1 -0
  66. package/dist/types/hashTree/hashTreeParser.d.ts +61 -15
  67. package/dist/types/hashTree/utils.d.ts +11 -0
  68. package/dist/types/index.d.ts +2 -0
  69. package/dist/types/interceptors/locusRetry.d.ts +4 -4
  70. package/dist/types/locus-info/index.d.ts +46 -6
  71. package/dist/types/locus-info/types.d.ts +17 -1
  72. package/dist/types/media/properties.d.ts +1 -0
  73. package/dist/types/meeting/in-meeting-actions.d.ts +2 -0
  74. package/dist/types/meeting/index.d.ts +70 -1
  75. package/dist/types/meeting/util.d.ts +8 -0
  76. package/dist/types/meetings/index.d.ts +18 -1
  77. package/dist/types/meetings/meetings.types.d.ts +15 -0
  78. package/dist/types/meetings/request.d.ts +14 -0
  79. package/dist/types/member/index.d.ts +1 -0
  80. package/dist/types/member/types.d.ts +1 -0
  81. package/dist/types/member/util.d.ts +1 -0
  82. package/dist/types/metrics/constants.d.ts +1 -0
  83. package/dist/webinar/index.js +361 -235
  84. package/dist/webinar/index.js.map +1 -1
  85. package/package.json +22 -22
  86. package/src/aiEnableRequest/index.ts +16 -0
  87. package/src/breakouts/breakout.ts +2 -1
  88. package/src/config.ts +1 -0
  89. package/src/constants.ts +5 -1
  90. package/src/controls-options-manager/constants.ts +14 -1
  91. package/src/controls-options-manager/index.ts +47 -24
  92. package/src/controls-options-manager/util.ts +81 -1
  93. package/src/hashTree/constants.ts +9 -0
  94. package/src/hashTree/hashTreeParser.ts +306 -160
  95. package/src/hashTree/utils.ts +17 -0
  96. package/src/index.ts +5 -0
  97. package/src/interceptors/locusRetry.ts +25 -4
  98. package/src/interpretation/index.ts +25 -8
  99. package/src/locus-info/controlsUtils.ts +3 -1
  100. package/src/locus-info/index.ts +276 -93
  101. package/src/locus-info/types.ts +19 -1
  102. package/src/media/properties.ts +1 -0
  103. package/src/meeting/in-meeting-actions.ts +4 -0
  104. package/src/meeting/index.ts +315 -26
  105. package/src/meeting/util.ts +20 -2
  106. package/src/meetings/index.ts +104 -43
  107. package/src/meetings/meetings.types.ts +19 -0
  108. package/src/meetings/request.ts +43 -0
  109. package/src/meetings/util.ts +80 -1
  110. package/src/member/index.ts +10 -0
  111. package/src/member/types.ts +1 -0
  112. package/src/member/util.ts +3 -0
  113. package/src/metrics/constants.ts +1 -0
  114. package/src/recording-controller/index.ts +1 -2
  115. package/src/webinar/index.ts +162 -21
  116. package/test/unit/spec/aiEnableRequest/index.ts +86 -0
  117. package/test/unit/spec/breakouts/breakout.ts +7 -3
  118. package/test/unit/spec/controls-options-manager/index.js +140 -29
  119. package/test/unit/spec/controls-options-manager/util.js +165 -0
  120. package/test/unit/spec/hashTree/hashTreeParser.ts +1294 -191
  121. package/test/unit/spec/hashTree/utils.ts +88 -1
  122. package/test/unit/spec/interceptors/locusRetry.ts +205 -4
  123. package/test/unit/spec/interpretation/index.ts +26 -4
  124. package/test/unit/spec/locus-info/controlsUtils.js +172 -57
  125. package/test/unit/spec/locus-info/index.js +443 -81
  126. package/test/unit/spec/meeting/in-meeting-actions.ts +2 -0
  127. package/test/unit/spec/meeting/index.js +836 -41
  128. package/test/unit/spec/meeting/muteState.js +3 -0
  129. package/test/unit/spec/meeting/utils.js +33 -0
  130. package/test/unit/spec/meetings/index.js +275 -10
  131. package/test/unit/spec/meetings/request.js +141 -0
  132. package/test/unit/spec/meetings/utils.js +161 -0
  133. package/test/unit/spec/member/index.js +7 -0
  134. package/test/unit/spec/member/util.js +24 -0
  135. package/test/unit/spec/recording-controller/index.js +9 -8
  136. package/test/unit/spec/webinar/index.ts +141 -16
@@ -1,5 +1,6 @@
1
1
  import ControlsOptionsManager from '@webex/plugin-meetings/src/controls-options-manager';
2
2
  import Util from '@webex/plugin-meetings/src/controls-options-manager/util';
3
+ import ParameterError from '@webex/plugin-meetings/src/common/errors/parameter';
3
4
  import sinon from 'sinon';
4
5
  import {assert} from '@webex/test-helper-chai';
5
6
  import { HTTP_VERBS } from '@webex/plugin-meetings/src/constants';
@@ -26,6 +27,7 @@ describe('plugin-meetings', () => {
26
27
  beforeEach(() => {
27
28
  request = {
28
29
  request: sinon.stub().returns(Promise.resolve()),
30
+ locusDeltaRequest: sinon.stub().returns(Promise.resolve()),
29
31
  };
30
32
 
31
33
  manager = new ControlsOptionsManager(request);
@@ -58,11 +60,11 @@ describe('plugin-meetings', () => {
58
60
 
59
61
  const result = manager.setMuteOnEntry(true);
60
62
 
61
- assert.calledWith(request.request, { uri: 'test/id/controls',
63
+ assert.calledWith(request.locusDeltaRequest, { uri: 'test/id/controls',
62
64
  body: { muteOnEntry: { enabled: true } },
63
65
  method: HTTP_VERBS.PATCH});
64
66
 
65
- assert.deepEqual(result, request.request.firstCall.returnValue);
67
+ assert.deepEqual(result, request.locusDeltaRequest.firstCall.returnValue);
66
68
  });
67
69
 
68
70
  it('can set mute on entry when the display hint is available enabled=false', () => {
@@ -70,11 +72,24 @@ describe('plugin-meetings', () => {
70
72
 
71
73
  const result = manager.setMuteOnEntry(false);
72
74
 
73
- assert.calledWith(request.request, { uri: 'test/id/controls',
75
+ assert.calledWith(request.locusDeltaRequest, { uri: 'test/id/controls',
74
76
  body: { muteOnEntry: { enabled: false } },
75
77
  method: HTTP_VERBS.PATCH});
76
78
 
77
- assert.deepEqual(result, request.request.firstCall.returnValue);
79
+ assert.deepEqual(result, request.locusDeltaRequest.firstCall.returnValue);
80
+ });
81
+
82
+ it('should send setMuteOnEntry to locusUrl without authorizingLocusUrl when in breakout', () => {
83
+ manager.setDisplayHints(['ENABLE_MUTE_ON_ENTRY']);
84
+ manager.mainLocusUrl = 'test/main';
85
+
86
+ const result = manager.setMuteOnEntry(true);
87
+
88
+ assert.calledWith(request.locusDeltaRequest, { uri: 'test/id/controls',
89
+ body: { muteOnEntry: { enabled: true } },
90
+ method: HTTP_VERBS.PATCH});
91
+
92
+ assert.deepEqual(result, request.locusDeltaRequest.firstCall.returnValue);
78
93
  });
79
94
  });
80
95
 
@@ -100,11 +115,11 @@ describe('plugin-meetings', () => {
100
115
 
101
116
  const result = manager.setDisallowUnmute(true);
102
117
 
103
- assert.calledWith(request.request, { uri: 'test/id/controls',
118
+ assert.calledWith(request.locusDeltaRequest, { uri: 'test/id/controls',
104
119
  body: { disallowUnmute: { enabled: true } },
105
120
  method: HTTP_VERBS.PATCH});
106
121
 
107
- assert.deepEqual(result, request.request.firstCall.returnValue);
122
+ assert.deepEqual(result, request.locusDeltaRequest.firstCall.returnValue);
108
123
  });
109
124
 
110
125
  it('can set allow unmute when DISABLE_HARD_MUTE display hint is available', () => {
@@ -112,11 +127,24 @@ describe('plugin-meetings', () => {
112
127
 
113
128
  const result = manager.setDisallowUnmute(false);
114
129
 
115
- assert.calledWith(request.request, { uri: 'test/id/controls',
130
+ assert.calledWith(request.locusDeltaRequest, { uri: 'test/id/controls',
116
131
  body: { disallowUnmute: { enabled: false } },
117
132
  method: HTTP_VERBS.PATCH});
118
133
 
119
- assert.deepEqual(result, request.request.firstCall.returnValue);
134
+ assert.deepEqual(result, request.locusDeltaRequest.firstCall.returnValue);
135
+ });
136
+
137
+ it('should send setDisallowUnmute to locusUrl without authorizingLocusUrl when in breakout', () => {
138
+ manager.setDisplayHints(['ENABLE_HARD_MUTE']);
139
+ manager.mainLocusUrl = 'test/main';
140
+
141
+ const result = manager.setDisallowUnmute(true);
142
+
143
+ assert.calledWith(request.locusDeltaRequest, { uri: 'test/id/controls',
144
+ body: { disallowUnmute: { enabled: true } },
145
+ method: HTTP_VERBS.PATCH});
146
+
147
+ assert.deepEqual(result, request.locusDeltaRequest.firstCall.returnValue);
120
148
  });
121
149
  });
122
150
  });
@@ -127,6 +155,7 @@ describe('plugin-meetings', () => {
127
155
  beforeEach(() => {
128
156
  request = {
129
157
  request: sinon.stub().resolves(),
158
+ locusDeltaRequest: sinon.stub().resolves(),
130
159
  };
131
160
 
132
161
  manager = new ControlsOptionsManager(request);
@@ -138,6 +167,18 @@ describe('plugin-meetings', () => {
138
167
  });
139
168
  });
140
169
 
170
+ it('should reject with ParameterError when locusUrl is not set', () => {
171
+ const noLocusManager = new ControlsOptionsManager(request);
172
+
173
+ const result = noLocusManager.update({scope: 'audio', properties: {muted: true}});
174
+
175
+ assert.notCalled(request.request);
176
+ return assert.isRejected(result).then((err) => {
177
+ assert.instanceOf(err, ParameterError);
178
+ assert.match(err.message, /locusUrl.*must be defined/);
179
+ });
180
+ });
181
+
141
182
  it('should throw an error if the scope is not supported', () => {
142
183
  const scope = 'invalid';
143
184
 
@@ -163,7 +204,7 @@ describe('plugin-meetings', () => {
163
204
 
164
205
  return manager.update(audio, reactions)
165
206
  .then(() => {
166
- assert.calledWith(request.request, {
207
+ assert.calledWith(request.locusDeltaRequest, {
167
208
  uri: 'test/id/controls',
168
209
  body: {
169
210
  audio: audio.properties,
@@ -171,7 +212,7 @@ describe('plugin-meetings', () => {
171
212
  method: HTTP_VERBS.PATCH,
172
213
  });
173
214
 
174
- assert.calledWith(request.request, {
215
+ assert.calledWith(request.locusDeltaRequest, {
175
216
  uri: 'test/id/controls',
176
217
  body: {
177
218
  reactions: reactions.properties,
@@ -203,7 +244,7 @@ describe('plugin-meetings', () => {
203
244
  });
204
245
  });
205
246
 
206
- it('should call request with mainLocusUrl and locusUrl as authorizingLocusUrl if mainLocusUrl is exist and not same with locusUrl', () => {
247
+ it('should send audio controls to locusUrl without authorizingLocusUrl and non-audio to mainLocusUrl with authorizingLocusUrl when in breakout', () => {
207
248
  const restorable = Util.canUpdate;
208
249
  Util.canUpdate = sinon.stub().returns(true);
209
250
  manager.mainLocusUrl = 'test/main';
@@ -213,20 +254,64 @@ describe('plugin-meetings', () => {
213
254
 
214
255
  return manager.update(audio, reactions)
215
256
  .then(() => {
257
+ // Audio controls go directly to current locusUrl (no cross-locus authorization)
258
+ assert.calledWith(request.locusDeltaRequest, {
259
+ uri: 'test/id/controls',
260
+ body: {
261
+ audio: audio.properties,
262
+ },
263
+ method: HTTP_VERBS.PATCH,
264
+ });
265
+
266
+ // Non-audio controls go to mainLocusUrl with authorizingLocusUrl
216
267
  assert.calledWith(request.request, {
217
268
  uri: 'test/main/controls',
218
269
  body: {
219
- audio: audio.properties,
270
+ reactions: reactions.properties,
220
271
  authorizingLocusUrl: 'test/id'
221
272
  },
222
273
  method: HTTP_VERBS.PATCH,
223
274
  });
224
275
 
276
+ Util.canUpdate = restorable;
277
+ });
278
+ });
279
+
280
+ it('should send audio controls to locusUrl without authorizingLocusUrl when in breakout', () => {
281
+ const restorable = Util.canUpdate;
282
+ Util.canUpdate = sinon.stub().returns(true);
283
+ manager.mainLocusUrl = 'test/main';
284
+
285
+ const audio = {scope: 'audio', properties: {muted: true, disallowUnmute: false}};
286
+
287
+ return manager.update(audio)
288
+ .then(() => {
289
+ assert.calledWith(request.locusDeltaRequest, {
290
+ uri: 'test/id/controls',
291
+ body: {
292
+ audio: audio.properties,
293
+ },
294
+ method: HTTP_VERBS.PATCH,
295
+ });
296
+
297
+ Util.canUpdate = restorable;
298
+ });
299
+ });
300
+
301
+ it('should send non-audio controls to mainLocusUrl with authorizingLocusUrl when in breakout', () => {
302
+ const restorable = Util.canUpdate;
303
+ Util.canUpdate = sinon.stub().returns(true);
304
+ manager.mainLocusUrl = 'test/main';
305
+
306
+ const reactions = {scope: 'reactions', properties: {enabled: true}};
307
+
308
+ return manager.update(reactions)
309
+ .then(() => {
225
310
  assert.calledWith(request.request, {
226
311
  uri: 'test/main/controls',
227
312
  body: {
228
313
  reactions: reactions.properties,
229
- authorizingLocusUrl: 'test/id'
314
+ authorizingLocusUrl: 'test/id',
230
315
  },
231
316
  method: HTTP_VERBS.PATCH,
232
317
  });
@@ -241,6 +326,7 @@ describe('plugin-meetings', () => {
241
326
  beforeEach(() => {
242
327
  request = {
243
328
  request: sinon.stub().returns(Promise.resolve()),
329
+ locusDeltaRequest: sinon.stub().returns(Promise.resolve()),
244
330
  };
245
331
 
246
332
  manager = new ControlsOptionsManager(request);
@@ -252,6 +338,18 @@ describe('plugin-meetings', () => {
252
338
  })
253
339
  });
254
340
 
341
+ it('should reject with ParameterError when locusUrl is not set', () => {
342
+ const noLocusManager = new ControlsOptionsManager(request);
343
+
344
+ const result = noLocusManager.setMuteAll(true, true, true);
345
+
346
+ assert.notCalled(request.request);
347
+ return assert.isRejected(result).then((err) => {
348
+ assert.instanceOf(err, ParameterError);
349
+ assert.match(err.message, /locusUrl.*must be defined/);
350
+ });
351
+ });
352
+
255
353
  it('rejects when correct display hint is not present mutedEnabled=false', () => {
256
354
  const result = manager.setMuteAll(false, false, false);
257
355
 
@@ -273,11 +371,11 @@ describe('plugin-meetings', () => {
273
371
 
274
372
  const result = manager.setMuteAll(true, true, true);
275
373
 
276
- assert.calledWith(request.request, { uri: 'test/id/controls',
374
+ assert.calledWith(request.locusDeltaRequest, { uri: 'test/id/controls',
277
375
  body: { audio: { muted: true, disallowUnmute: true, muteOnEntry: true } },
278
376
  method: HTTP_VERBS.PATCH});
279
377
 
280
- assert.deepEqual(result, request.request.firstCall.returnValue);
378
+ assert.deepEqual(result, request.locusDeltaRequest.firstCall.returnValue);
281
379
  });
282
380
 
283
381
  it('can set mute all when the display hint is available mutedEnabled=true', () => {
@@ -285,11 +383,11 @@ describe('plugin-meetings', () => {
285
383
 
286
384
  const result = manager.setMuteAll(true, true, true);
287
385
 
288
- assert.calledWith(request.request, { uri: 'test/id/controls',
386
+ assert.calledWith(request.locusDeltaRequest, { uri: 'test/id/controls',
289
387
  body: { audio: { muted: true, disallowUnmute: true, muteOnEntry: true } },
290
388
  method: HTTP_VERBS.PATCH});
291
389
 
292
- assert.deepEqual(result, request.request.firstCall.returnValue);
390
+ assert.deepEqual(result, request.locusDeltaRequest.firstCall.returnValue);
293
391
  });
294
392
 
295
393
  it('can set mute all when the display hint is available mutedEnabled=true', () => {
@@ -297,11 +395,11 @@ describe('plugin-meetings', () => {
297
395
 
298
396
  const result = manager.setMuteAll(true, true, true);
299
397
 
300
- assert.calledWith(request.request, { uri: 'test/id/controls',
398
+ assert.calledWith(request.locusDeltaRequest, { uri: 'test/id/controls',
301
399
  body: { audio: { muted: true, disallowUnmute: true, muteOnEntry: true } },
302
400
  method: HTTP_VERBS.PATCH});
303
401
 
304
- assert.deepEqual(result, request.request.firstCall.returnValue);
402
+ assert.deepEqual(result, request.locusDeltaRequest.firstCall.returnValue);
305
403
  });
306
404
 
307
405
  it('can set mute all when the display hint is available mutedEnabled=false', () => {
@@ -309,11 +407,11 @@ describe('plugin-meetings', () => {
309
407
 
310
408
  const result = manager.setMuteAll(false, false, false);
311
409
 
312
- assert.calledWith(request.request, { uri: 'test/id/controls',
410
+ assert.calledWith(request.locusDeltaRequest, { uri: 'test/id/controls',
313
411
  body: { audio: { muted: false, disallowUnmute: false, muteOnEntry: false } },
314
412
  method: HTTP_VERBS.PATCH});
315
413
 
316
- assert.deepEqual(result, request.request.firstCall.returnValue);
414
+ assert.deepEqual(result, request.locusDeltaRequest.firstCall.returnValue);
317
415
  });
318
416
 
319
417
  it('can set mute all panelists when the display hint is available mutedEnabled=true', () => {
@@ -321,11 +419,11 @@ describe('plugin-meetings', () => {
321
419
 
322
420
  const result = manager.setMuteAll(true, true, true, ['panelist']);
323
421
 
324
- assert.calledWith(request.request, { uri: 'test/id/controls',
422
+ assert.calledWith(request.locusDeltaRequest, { uri: 'test/id/controls',
325
423
  body: { audio: { muted: true, disallowUnmute: true, muteOnEntry: true, roles: ['panelist'] } },
326
424
  method: HTTP_VERBS.PATCH});
327
425
 
328
- assert.deepEqual(result, request.request.firstCall.returnValue);
426
+ assert.deepEqual(result, request.locusDeltaRequest.firstCall.returnValue);
329
427
  });
330
428
 
331
429
  it('can set mute all attendees when the display hint is available mutedEnabled=true', () => {
@@ -333,24 +431,37 @@ describe('plugin-meetings', () => {
333
431
 
334
432
  const result = manager.setMuteAll(true, true, true, ['attendee']);
335
433
 
336
- assert.calledWith(request.request, { uri: 'test/id/controls',
434
+ assert.calledWith(request.locusDeltaRequest, { uri: 'test/id/controls',
337
435
  body: { audio: { muted: true, disallowUnmute: true, muteOnEntry: true, roles: ['attendee'] } },
338
436
  method: HTTP_VERBS.PATCH});
339
437
 
340
- assert.deepEqual(result, request.request.firstCall.returnValue);
438
+ assert.deepEqual(result, request.locusDeltaRequest.firstCall.returnValue);
341
439
  });
342
440
 
343
- it('request with mainLocusUrl and make locusUrl as authorizingLocusUrl if mainLocusUrl is exist and not same with locusUrl', () => {
441
+ it('should send setMuteAll to locusUrl without authorizingLocusUrl when in breakout', () => {
344
442
  manager.setDisplayHints(['MUTE_ALL', 'DISABLE_HARD_MUTE', 'DISABLE_MUTE_ON_ENTRY']);
345
443
  manager.mainLocusUrl = `test/main`;
346
444
 
347
445
  const result = manager.setMuteAll(true, true, true, ['attendee']);
348
446
 
349
- assert.calledWith(request.request, { uri: 'test/main/controls',
350
- body: { audio: { muted: true, disallowUnmute: true, muteOnEntry: true, roles: ['attendee'] }, authorizingLocusUrl: 'test/id' },
447
+ assert.calledWith(request.locusDeltaRequest, { uri: 'test/id/controls',
448
+ body: { audio: { muted: true, disallowUnmute: true, muteOnEntry: true, roles: ['attendee'] } },
449
+ method: HTTP_VERBS.PATCH});
450
+
451
+ assert.deepEqual(result, request.locusDeltaRequest.firstCall.returnValue);
452
+ });
453
+
454
+ it('should send setMuteAll with PANELIST role to locusUrl without authorizingLocusUrl when in breakout', () => {
455
+ manager.setDisplayHints(['MUTE_ALL', 'ENABLE_HARD_MUTE', 'ENABLE_MUTE_ON_ENTRY']);
456
+ manager.mainLocusUrl = `test/main`;
457
+
458
+ const result = manager.setMuteAll(true, true, true, ['PANELIST']);
459
+
460
+ assert.calledWith(request.locusDeltaRequest, { uri: 'test/id/controls',
461
+ body: { audio: { muted: true, disallowUnmute: true, muteOnEntry: true, roles: ['PANELIST'] } },
351
462
  method: HTTP_VERBS.PATCH});
352
463
 
353
- assert.deepEqual(result, request.request.firstCall.returnValue);
464
+ assert.deepEqual(result, request.locusDeltaRequest.firstCall.returnValue);
354
465
  });
355
466
  });
356
467
  });
@@ -799,6 +799,171 @@ describe('plugin-meetings', () => {
799
799
  );
800
800
  });
801
801
  });
802
+
803
+ describe('isAudioControl()', () => {
804
+ it('should return true when all body keys are audio control keys', () => {
805
+ assert.isTrue(ControlsOptionsUtil.isAudioControl({audio: {muted: true}}));
806
+ });
807
+
808
+ it('should return true when body has muteOnEntry key', () => {
809
+ assert.isTrue(ControlsOptionsUtil.isAudioControl({muteOnEntry: {enabled: true}}));
810
+ });
811
+
812
+ it('should return true when body has disallowUnmute key', () => {
813
+ assert.isTrue(ControlsOptionsUtil.isAudioControl({disallowUnmute: {enabled: true}}));
814
+ });
815
+
816
+ it('should return true when body has multiple audio control keys', () => {
817
+ assert.isTrue(ControlsOptionsUtil.isAudioControl({audio: {muted: true}, muteOnEntry: {enabled: true}, disallowUnmute: {enabled: true}}));
818
+ });
819
+
820
+ it('should return false when body has a non-audio control key', () => {
821
+ assert.isFalse(ControlsOptionsUtil.isAudioControl({raiseHand: {enabled: true}}));
822
+ });
823
+
824
+ it('should return false when body has a mix of audio and non-audio keys', () => {
825
+ assert.isFalse(ControlsOptionsUtil.isAudioControl({audio: {muted: true}, raiseHand: {enabled: true}}));
826
+ });
827
+
828
+ it('should return true for an empty body', () => {
829
+ assert.isTrue(ControlsOptionsUtil.isAudioControl({}));
830
+ });
831
+ });
832
+
833
+ describe('isBreakoutLocusUrl()', () => {
834
+ it('should return true when mainLocusUrl differs from locusUrl', () => {
835
+ assert.isTrue(ControlsOptionsUtil.isBreakoutLocusUrl('locus/breakout', 'locus/main'));
836
+ });
837
+
838
+ it('should return false when mainLocusUrl equals locusUrl', () => {
839
+ assert.isFalse(ControlsOptionsUtil.isBreakoutLocusUrl('locus/main', 'locus/main'));
840
+ });
841
+
842
+ it('should return false when mainLocusUrl is undefined', () => {
843
+ assert.isFalse(ControlsOptionsUtil.isBreakoutLocusUrl('locus/breakout', undefined));
844
+ });
845
+
846
+ it('should return false when mainLocusUrl is null', () => {
847
+ assert.isFalse(ControlsOptionsUtil.isBreakoutLocusUrl('locus/breakout', null));
848
+ });
849
+
850
+ it('should return false when mainLocusUrl is empty string', () => {
851
+ assert.isFalse(ControlsOptionsUtil.isBreakoutLocusUrl('locus/breakout', ''));
852
+ });
853
+ });
854
+
855
+ describe('getControlsRequestParams()', () => {
856
+ const locusUrl = 'locus/breakout';
857
+ const mainLocusUrl = 'locus/main';
858
+
859
+ it('should return full request params targeting locusUrl when not in a breakout', () => {
860
+ const result = ControlsOptionsUtil.getControlsRequestParams({
861
+ body: {raiseHand: {enabled: true}},
862
+ locusUrl: 'locus/main',
863
+ mainLocusUrl: 'locus/main',
864
+ });
865
+
866
+ assert.equal(result.uri, 'locus/main/controls');
867
+ assert.deepEqual(result.body, {raiseHand: {enabled: true}});
868
+ assert.equal(result.method, 'PATCH');
869
+ });
870
+
871
+ it('should return mainLocusUrl with authorizingLocusUrl in body for non-audio controls in a breakout', () => {
872
+ const result = ControlsOptionsUtil.getControlsRequestParams({
873
+ body: {raiseHand: {enabled: true}},
874
+ locusUrl,
875
+ mainLocusUrl,
876
+ });
877
+
878
+ assert.equal(result.uri, 'locus/main/controls');
879
+ assert.deepEqual(result.body, {raiseHand: {enabled: true}, authorizingLocusUrl: locusUrl});
880
+ assert.equal(result.method, 'PATCH');
881
+ });
882
+
883
+ it('should return locusUrl without authorizingLocusUrl for audio controls in a breakout', () => {
884
+ const result = ControlsOptionsUtil.getControlsRequestParams({
885
+ body: {audio: {muted: true}},
886
+ locusUrl,
887
+ mainLocusUrl,
888
+ });
889
+
890
+ assert.equal(result.uri, 'locus/breakout/controls');
891
+ assert.deepEqual(result.body, {audio: {muted: true}});
892
+ assert.equal(result.method, 'PATCH');
893
+ });
894
+
895
+ it('should return locusUrl without authorizingLocusUrl for muteOnEntry in a breakout', () => {
896
+ const result = ControlsOptionsUtil.getControlsRequestParams({
897
+ body: {muteOnEntry: {enabled: true}},
898
+ locusUrl,
899
+ mainLocusUrl,
900
+ });
901
+
902
+ assert.equal(result.uri, 'locus/breakout/controls');
903
+ assert.deepEqual(result.body, {muteOnEntry: {enabled: true}});
904
+ assert.equal(result.method, 'PATCH');
905
+ });
906
+
907
+ it('should return locusUrl without authorizingLocusUrl for disallowUnmute in a breakout', () => {
908
+ const result = ControlsOptionsUtil.getControlsRequestParams({
909
+ body: {disallowUnmute: {enabled: true}},
910
+ locusUrl,
911
+ mainLocusUrl,
912
+ });
913
+
914
+ assert.equal(result.uri, 'locus/breakout/controls');
915
+ assert.deepEqual(result.body, {disallowUnmute: {enabled: true}});
916
+ assert.equal(result.method, 'PATCH');
917
+ });
918
+
919
+ it('should return locusUrl when mainLocusUrl is undefined', () => {
920
+ const result = ControlsOptionsUtil.getControlsRequestParams({
921
+ body: {raiseHand: {enabled: true}},
922
+ locusUrl,
923
+ mainLocusUrl: undefined,
924
+ });
925
+
926
+ assert.equal(result.uri, 'locus/breakout/controls');
927
+ assert.deepEqual(result.body, {raiseHand: {enabled: true}});
928
+ assert.equal(result.method, 'PATCH');
929
+ });
930
+
931
+ it('should return locusUrl when mainLocusUrl is null', () => {
932
+ const result = ControlsOptionsUtil.getControlsRequestParams({
933
+ body: {raiseHand: {enabled: true}},
934
+ locusUrl,
935
+ mainLocusUrl: null,
936
+ });
937
+
938
+ assert.equal(result.uri, 'locus/breakout/controls');
939
+ assert.deepEqual(result.body, {raiseHand: {enabled: true}});
940
+ assert.equal(result.method, 'PATCH');
941
+ });
942
+
943
+ it('should return locusUrl when mainLocusUrl is empty string', () => {
944
+ const result = ControlsOptionsUtil.getControlsRequestParams({
945
+ body: {raiseHand: {enabled: true}},
946
+ locusUrl,
947
+ mainLocusUrl: '',
948
+ });
949
+
950
+ assert.equal(result.uri, 'locus/breakout/controls');
951
+ assert.deepEqual(result.body, {raiseHand: {enabled: true}});
952
+ assert.equal(result.method, 'PATCH');
953
+ });
954
+
955
+ it('should return locusUrl for audio controls when not in a breakout', () => {
956
+ const result = ControlsOptionsUtil.getControlsRequestParams({
957
+ body: {audio: {muted: true}},
958
+ locusUrl: 'locus/main',
959
+ mainLocusUrl: 'locus/main',
960
+ });
961
+
962
+ assert.equal(result.uri, 'locus/main/controls');
963
+ assert.deepEqual(result.body, {audio: {muted: true}});
964
+ assert.equal(result.method, 'PATCH');
965
+ });
966
+ });
802
967
  });
803
968
  });
804
969
  });