@webex/plugin-meetings 3.12.0-next.3 → 3.12.0-next.31
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.
- package/AGENTS.md +9 -0
- package/dist/aiEnableRequest/index.js +1 -1
- package/dist/breakouts/breakout.js +1 -1
- package/dist/breakouts/index.js +1 -1
- package/dist/constants.js +3 -1
- package/dist/constants.js.map +1 -1
- package/dist/controls-options-manager/constants.js +11 -1
- package/dist/controls-options-manager/constants.js.map +1 -1
- package/dist/controls-options-manager/index.js +23 -21
- package/dist/controls-options-manager/index.js.map +1 -1
- package/dist/controls-options-manager/util.js +91 -0
- package/dist/controls-options-manager/util.js.map +1 -1
- package/dist/hashTree/constants.js +10 -1
- package/dist/hashTree/constants.js.map +1 -1
- package/dist/hashTree/hashTreeParser.js +550 -346
- package/dist/hashTree/hashTreeParser.js.map +1 -1
- package/dist/hashTree/utils.js +22 -0
- package/dist/hashTree/utils.js.map +1 -1
- package/dist/interceptors/locusRetry.js +23 -8
- package/dist/interceptors/locusRetry.js.map +1 -1
- package/dist/interpretation/index.js +1 -1
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/locus-info/index.js +222 -61
- package/dist/locus-info/index.js.map +1 -1
- package/dist/meeting/index.js +372 -292
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/util.js +1 -0
- package/dist/meeting/util.js.map +1 -1
- package/dist/meetings/index.js +146 -62
- package/dist/meetings/index.js.map +1 -1
- package/dist/meetings/util.js +39 -5
- package/dist/meetings/util.js.map +1 -1
- package/dist/member/index.js +10 -0
- package/dist/member/index.js.map +1 -1
- package/dist/member/types.js.map +1 -1
- package/dist/member/util.js +3 -0
- package/dist/member/util.js.map +1 -1
- package/dist/metrics/constants.js +5 -1
- package/dist/metrics/constants.js.map +1 -1
- package/dist/multistream/sendSlotManager.js +116 -2
- package/dist/multistream/sendSlotManager.js.map +1 -1
- package/dist/types/constants.d.ts +1 -0
- package/dist/types/controls-options-manager/constants.d.ts +6 -1
- package/dist/types/hashTree/constants.d.ts +1 -0
- package/dist/types/hashTree/hashTreeParser.d.ts +53 -15
- package/dist/types/hashTree/utils.d.ts +11 -0
- package/dist/types/interceptors/locusRetry.d.ts +4 -4
- package/dist/types/locus-info/index.d.ts +38 -5
- package/dist/types/meeting/index.d.ts +11 -0
- package/dist/types/member/index.d.ts +1 -0
- package/dist/types/member/types.d.ts +1 -0
- package/dist/types/member/util.d.ts +1 -0
- package/dist/types/metrics/constants.d.ts +4 -0
- package/dist/types/multistream/sendSlotManager.d.ts +23 -1
- package/dist/webinar/index.js +301 -226
- package/dist/webinar/index.js.map +1 -1
- package/package.json +16 -16
- package/src/constants.ts +1 -0
- package/src/controls-options-manager/constants.ts +14 -1
- package/src/controls-options-manager/index.ts +26 -19
- package/src/controls-options-manager/util.ts +81 -1
- package/src/hashTree/constants.ts +9 -0
- package/src/hashTree/hashTreeParser.ts +273 -154
- package/src/hashTree/utils.ts +17 -0
- package/src/interceptors/locusRetry.ts +25 -4
- package/src/locus-info/index.ts +233 -79
- package/src/meeting/index.ts +98 -11
- package/src/meeting/util.ts +1 -0
- package/src/meetings/index.ts +58 -34
- package/src/meetings/util.ts +44 -1
- package/src/member/index.ts +10 -0
- package/src/member/types.ts +1 -0
- package/src/member/util.ts +3 -0
- package/src/metrics/constants.ts +5 -0
- package/src/multistream/sendSlotManager.ts +97 -3
- package/src/webinar/index.ts +75 -1
- package/test/unit/spec/controls-options-manager/index.js +114 -6
- package/test/unit/spec/controls-options-manager/util.js +165 -0
- package/test/unit/spec/hashTree/hashTreeParser.ts +839 -37
- package/test/unit/spec/hashTree/utils.ts +88 -1
- package/test/unit/spec/interceptors/locusRetry.ts +205 -4
- package/test/unit/spec/locus-info/index.js +262 -64
- package/test/unit/spec/meeting/index.js +54 -36
- package/test/unit/spec/meeting/utils.js +4 -0
- package/test/unit/spec/meetings/index.js +190 -8
- package/test/unit/spec/meetings/utils.js +124 -0
- package/test/unit/spec/member/index.js +7 -0
- package/test/unit/spec/member/util.js +24 -0
- package/test/unit/spec/multistream/sendSlotManager.ts +135 -36
- package/test/unit/spec/webinar/index.ts +60 -0
package/src/webinar/index.ts
CHANGED
|
@@ -154,12 +154,63 @@ const Webinar = WebexPlugin.extend({
|
|
|
154
154
|
);
|
|
155
155
|
},
|
|
156
156
|
|
|
157
|
+
/**
|
|
158
|
+
* Ensures practice-session token exists before registering the practice LLM channel.
|
|
159
|
+
* @param {object} meeting
|
|
160
|
+
* @returns {Promise<string|undefined>}
|
|
161
|
+
*/
|
|
162
|
+
async ensurePracticeSessionDatachannelToken(meeting) {
|
|
163
|
+
// @ts-ignore
|
|
164
|
+
const isDataChannelTokenEnabled = await this.webex.internal.llm.isDataChannelTokenEnabled();
|
|
165
|
+
|
|
166
|
+
if (!isDataChannelTokenEnabled) {
|
|
167
|
+
return undefined;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// @ts-ignore
|
|
171
|
+
const cachedToken = this.webex.internal.llm.getDatachannelToken(
|
|
172
|
+
DataChannelTokenType.PracticeSession
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
if (cachedToken) {
|
|
176
|
+
return cachedToken;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
try {
|
|
180
|
+
const refreshResponse = await meeting.refreshDataChannelToken();
|
|
181
|
+
const {datachannelToken, dataChannelTokenType} = refreshResponse?.body ?? {};
|
|
182
|
+
|
|
183
|
+
if (!datachannelToken) {
|
|
184
|
+
return undefined;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// @ts-ignore
|
|
188
|
+
this.webex.internal.llm.setDatachannelToken(
|
|
189
|
+
datachannelToken,
|
|
190
|
+
dataChannelTokenType || DataChannelTokenType.PracticeSession
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
return datachannelToken;
|
|
194
|
+
} catch (error) {
|
|
195
|
+
LoggerProxy.logger.warn(
|
|
196
|
+
`Webinar:index#ensurePracticeSessionDatachannelToken --> failed to proactively refresh practice-session token: ${
|
|
197
|
+
error?.message || String(error)
|
|
198
|
+
}`
|
|
199
|
+
);
|
|
200
|
+
|
|
201
|
+
return undefined;
|
|
202
|
+
}
|
|
203
|
+
},
|
|
204
|
+
|
|
157
205
|
/**
|
|
158
206
|
* Connects to low latency mercury and reconnects if the address has changed
|
|
159
207
|
* It will also disconnect if called when the meeting has ended
|
|
160
208
|
* @returns {Promise}
|
|
161
209
|
*/
|
|
162
210
|
async updatePSDataChannel() {
|
|
211
|
+
this._updatePSDataChannelSequence = (this._updatePSDataChannelSequence || 0) + 1;
|
|
212
|
+
const invocationSequence = this._updatePSDataChannelSequence;
|
|
213
|
+
|
|
163
214
|
const meeting = this.webex.meetings.getMeetingByType(_ID_, this.meetingId);
|
|
164
215
|
const isPracticeSession = meeting?.isJoined() && this.isJoinPracticeSessionDataChannel();
|
|
165
216
|
|
|
@@ -174,7 +225,7 @@ const Webinar = WebexPlugin.extend({
|
|
|
174
225
|
meeting?.locusInfo || {};
|
|
175
226
|
|
|
176
227
|
// @ts-ignore
|
|
177
|
-
|
|
228
|
+
let practiceSessionDatachannelToken = this.webex.internal.llm.getDatachannelToken(
|
|
178
229
|
DataChannelTokenType.PracticeSession
|
|
179
230
|
);
|
|
180
231
|
|
|
@@ -229,6 +280,29 @@ const Webinar = WebexPlugin.extend({
|
|
|
229
280
|
this._pendingOnlineListener = null;
|
|
230
281
|
}
|
|
231
282
|
|
|
283
|
+
const refreshedPracticeSessionToken = await this.ensurePracticeSessionDatachannelToken(meeting);
|
|
284
|
+
|
|
285
|
+
const latestPracticeSessionDatachannelUrl = get(
|
|
286
|
+
meeting,
|
|
287
|
+
'locusInfo.info.practiceSessionDatachannelUrl'
|
|
288
|
+
);
|
|
289
|
+
const isStillPracticeSession = meeting?.isJoined() && this.isJoinPracticeSessionDataChannel();
|
|
290
|
+
|
|
291
|
+
// Skip stale invocations after async refresh to avoid reconnecting a session
|
|
292
|
+
// that was already updated/cleaned by a newer state transition.
|
|
293
|
+
if (
|
|
294
|
+
invocationSequence !== this._updatePSDataChannelSequence ||
|
|
295
|
+
!isStillPracticeSession ||
|
|
296
|
+
!latestPracticeSessionDatachannelUrl ||
|
|
297
|
+
latestPracticeSessionDatachannelUrl !== practiceSessionDatachannelUrl
|
|
298
|
+
) {
|
|
299
|
+
return undefined;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
if (refreshedPracticeSessionToken) {
|
|
303
|
+
practiceSessionDatachannelToken = refreshedPracticeSessionToken;
|
|
304
|
+
}
|
|
305
|
+
|
|
232
306
|
// @ts-ignore - Fix type
|
|
233
307
|
return this.webex.internal.llm
|
|
234
308
|
.registerAndConnect(
|
|
@@ -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';
|
|
@@ -76,6 +77,19 @@ describe('plugin-meetings', () => {
|
|
|
76
77
|
|
|
77
78
|
assert.deepEqual(result, request.request.firstCall.returnValue);
|
|
78
79
|
});
|
|
80
|
+
|
|
81
|
+
it('should send setMuteOnEntry to locusUrl without authorizingLocusUrl when in breakout', () => {
|
|
82
|
+
manager.setDisplayHints(['ENABLE_MUTE_ON_ENTRY']);
|
|
83
|
+
manager.mainLocusUrl = 'test/main';
|
|
84
|
+
|
|
85
|
+
const result = manager.setMuteOnEntry(true);
|
|
86
|
+
|
|
87
|
+
assert.calledWith(request.request, { uri: 'test/id/controls',
|
|
88
|
+
body: { muteOnEntry: { enabled: true } },
|
|
89
|
+
method: HTTP_VERBS.PATCH});
|
|
90
|
+
|
|
91
|
+
assert.deepEqual(result, request.request.firstCall.returnValue);
|
|
92
|
+
});
|
|
79
93
|
});
|
|
80
94
|
|
|
81
95
|
describe('setDisallowUnmute', () => {
|
|
@@ -118,6 +132,19 @@ describe('plugin-meetings', () => {
|
|
|
118
132
|
|
|
119
133
|
assert.deepEqual(result, request.request.firstCall.returnValue);
|
|
120
134
|
});
|
|
135
|
+
|
|
136
|
+
it('should send setDisallowUnmute to locusUrl without authorizingLocusUrl when in breakout', () => {
|
|
137
|
+
manager.setDisplayHints(['ENABLE_HARD_MUTE']);
|
|
138
|
+
manager.mainLocusUrl = 'test/main';
|
|
139
|
+
|
|
140
|
+
const result = manager.setDisallowUnmute(true);
|
|
141
|
+
|
|
142
|
+
assert.calledWith(request.request, { uri: 'test/id/controls',
|
|
143
|
+
body: { disallowUnmute: { enabled: true } },
|
|
144
|
+
method: HTTP_VERBS.PATCH});
|
|
145
|
+
|
|
146
|
+
assert.deepEqual(result, request.request.firstCall.returnValue);
|
|
147
|
+
});
|
|
121
148
|
});
|
|
122
149
|
});
|
|
123
150
|
|
|
@@ -138,6 +165,18 @@ describe('plugin-meetings', () => {
|
|
|
138
165
|
});
|
|
139
166
|
});
|
|
140
167
|
|
|
168
|
+
it('should reject with ParameterError when locusUrl is not set', () => {
|
|
169
|
+
const noLocusManager = new ControlsOptionsManager(request);
|
|
170
|
+
|
|
171
|
+
const result = noLocusManager.update({scope: 'audio', properties: {muted: true}});
|
|
172
|
+
|
|
173
|
+
assert.notCalled(request.request);
|
|
174
|
+
return assert.isRejected(result).then((err) => {
|
|
175
|
+
assert.instanceOf(err, ParameterError);
|
|
176
|
+
assert.match(err.message, /locusUrl.*must be defined/);
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
|
|
141
180
|
it('should throw an error if the scope is not supported', () => {
|
|
142
181
|
const scope = 'invalid';
|
|
143
182
|
|
|
@@ -203,7 +242,7 @@ describe('plugin-meetings', () => {
|
|
|
203
242
|
});
|
|
204
243
|
});
|
|
205
244
|
|
|
206
|
-
it('should
|
|
245
|
+
it('should send audio controls to locusUrl without authorizingLocusUrl and non-audio to mainLocusUrl with authorizingLocusUrl when in breakout', () => {
|
|
207
246
|
const restorable = Util.canUpdate;
|
|
208
247
|
Util.canUpdate = sinon.stub().returns(true);
|
|
209
248
|
manager.mainLocusUrl = 'test/main';
|
|
@@ -213,15 +252,16 @@ describe('plugin-meetings', () => {
|
|
|
213
252
|
|
|
214
253
|
return manager.update(audio, reactions)
|
|
215
254
|
.then(() => {
|
|
255
|
+
// Audio controls go directly to current locusUrl (no cross-locus authorization)
|
|
216
256
|
assert.calledWith(request.request, {
|
|
217
|
-
uri: 'test/
|
|
257
|
+
uri: 'test/id/controls',
|
|
218
258
|
body: {
|
|
219
259
|
audio: audio.properties,
|
|
220
|
-
authorizingLocusUrl: 'test/id'
|
|
221
260
|
},
|
|
222
261
|
method: HTTP_VERBS.PATCH,
|
|
223
262
|
});
|
|
224
263
|
|
|
264
|
+
// Non-audio controls go to mainLocusUrl with authorizingLocusUrl
|
|
225
265
|
assert.calledWith(request.request, {
|
|
226
266
|
uri: 'test/main/controls',
|
|
227
267
|
body: {
|
|
@@ -234,6 +274,49 @@ describe('plugin-meetings', () => {
|
|
|
234
274
|
Util.canUpdate = restorable;
|
|
235
275
|
});
|
|
236
276
|
});
|
|
277
|
+
|
|
278
|
+
it('should send audio controls to locusUrl without authorizingLocusUrl when in breakout', () => {
|
|
279
|
+
const restorable = Util.canUpdate;
|
|
280
|
+
Util.canUpdate = sinon.stub().returns(true);
|
|
281
|
+
manager.mainLocusUrl = 'test/main';
|
|
282
|
+
|
|
283
|
+
const audio = {scope: 'audio', properties: {muted: true, disallowUnmute: false}};
|
|
284
|
+
|
|
285
|
+
return manager.update(audio)
|
|
286
|
+
.then(() => {
|
|
287
|
+
assert.calledWith(request.request, {
|
|
288
|
+
uri: 'test/id/controls',
|
|
289
|
+
body: {
|
|
290
|
+
audio: audio.properties,
|
|
291
|
+
},
|
|
292
|
+
method: HTTP_VERBS.PATCH,
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
Util.canUpdate = restorable;
|
|
296
|
+
});
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
it('should send non-audio controls to mainLocusUrl with authorizingLocusUrl when in breakout', () => {
|
|
300
|
+
const restorable = Util.canUpdate;
|
|
301
|
+
Util.canUpdate = sinon.stub().returns(true);
|
|
302
|
+
manager.mainLocusUrl = 'test/main';
|
|
303
|
+
|
|
304
|
+
const reactions = {scope: 'reactions', properties: {enabled: true}};
|
|
305
|
+
|
|
306
|
+
return manager.update(reactions)
|
|
307
|
+
.then(() => {
|
|
308
|
+
assert.calledWith(request.request, {
|
|
309
|
+
uri: 'test/main/controls',
|
|
310
|
+
body: {
|
|
311
|
+
reactions: reactions.properties,
|
|
312
|
+
authorizingLocusUrl: 'test/id',
|
|
313
|
+
},
|
|
314
|
+
method: HTTP_VERBS.PATCH,
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
Util.canUpdate = restorable;
|
|
318
|
+
});
|
|
319
|
+
});
|
|
237
320
|
});
|
|
238
321
|
|
|
239
322
|
describe('Mute/Unmute All', () => {
|
|
@@ -252,6 +335,18 @@ describe('plugin-meetings', () => {
|
|
|
252
335
|
})
|
|
253
336
|
});
|
|
254
337
|
|
|
338
|
+
it('should reject with ParameterError when locusUrl is not set', () => {
|
|
339
|
+
const noLocusManager = new ControlsOptionsManager(request);
|
|
340
|
+
|
|
341
|
+
const result = noLocusManager.setMuteAll(true, true, true);
|
|
342
|
+
|
|
343
|
+
assert.notCalled(request.request);
|
|
344
|
+
return assert.isRejected(result).then((err) => {
|
|
345
|
+
assert.instanceOf(err, ParameterError);
|
|
346
|
+
assert.match(err.message, /locusUrl.*must be defined/);
|
|
347
|
+
});
|
|
348
|
+
});
|
|
349
|
+
|
|
255
350
|
it('rejects when correct display hint is not present mutedEnabled=false', () => {
|
|
256
351
|
const result = manager.setMuteAll(false, false, false);
|
|
257
352
|
|
|
@@ -340,14 +435,27 @@ describe('plugin-meetings', () => {
|
|
|
340
435
|
assert.deepEqual(result, request.request.firstCall.returnValue);
|
|
341
436
|
});
|
|
342
437
|
|
|
343
|
-
it('
|
|
438
|
+
it('should send setMuteAll to locusUrl without authorizingLocusUrl when in breakout', () => {
|
|
344
439
|
manager.setDisplayHints(['MUTE_ALL', 'DISABLE_HARD_MUTE', 'DISABLE_MUTE_ON_ENTRY']);
|
|
345
440
|
manager.mainLocusUrl = `test/main`;
|
|
346
441
|
|
|
347
442
|
const result = manager.setMuteAll(true, true, true, ['attendee']);
|
|
348
443
|
|
|
349
|
-
assert.calledWith(request.request, { uri: 'test/
|
|
350
|
-
body: { audio: { muted: true, disallowUnmute: true, muteOnEntry: true, roles: ['attendee'] }
|
|
444
|
+
assert.calledWith(request.request, { uri: 'test/id/controls',
|
|
445
|
+
body: { audio: { muted: true, disallowUnmute: true, muteOnEntry: true, roles: ['attendee'] } },
|
|
446
|
+
method: HTTP_VERBS.PATCH});
|
|
447
|
+
|
|
448
|
+
assert.deepEqual(result, request.request.firstCall.returnValue);
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
it('should send setMuteAll with PANELIST role to locusUrl without authorizingLocusUrl when in breakout', () => {
|
|
452
|
+
manager.setDisplayHints(['MUTE_ALL', 'ENABLE_HARD_MUTE', 'ENABLE_MUTE_ON_ENTRY']);
|
|
453
|
+
manager.mainLocusUrl = `test/main`;
|
|
454
|
+
|
|
455
|
+
const result = manager.setMuteAll(true, true, true, ['PANELIST']);
|
|
456
|
+
|
|
457
|
+
assert.calledWith(request.request, { uri: 'test/id/controls',
|
|
458
|
+
body: { audio: { muted: true, disallowUnmute: true, muteOnEntry: true, roles: ['PANELIST'] } },
|
|
351
459
|
method: HTTP_VERBS.PATCH});
|
|
352
460
|
|
|
353
461
|
assert.deepEqual(result, request.request.firstCall.returnValue);
|
|
@@ -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
|
});
|