@webex/plugin-meetings 3.0.0-beta.43 → 3.0.0-beta.45
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/dist/breakouts/breakout.js +1 -1
- package/dist/breakouts/index.js +12 -3
- package/dist/breakouts/index.js.map +1 -1
- package/dist/constants.js +15 -3
- package/dist/constants.js.map +1 -1
- package/dist/locus-info/controlsUtils.js +6 -2
- package/dist/locus-info/controlsUtils.js.map +1 -1
- package/dist/locus-info/index.js +35 -1
- package/dist/locus-info/index.js.map +1 -1
- package/dist/locus-info/selfUtils.js +28 -0
- package/dist/locus-info/selfUtils.js.map +1 -1
- package/dist/meeting/in-meeting-actions.js +9 -1
- package/dist/meeting/in-meeting-actions.js.map +1 -1
- package/dist/meeting/index.js +43 -4
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/muteState.js +45 -20
- package/dist/meeting/muteState.js.map +1 -1
- package/dist/meeting/util.js +15 -0
- package/dist/meeting/util.js.map +1 -1
- package/dist/members/index.js +15 -3
- package/dist/members/index.js.map +1 -1
- package/dist/members/util.js +33 -12
- package/dist/members/util.js.map +1 -1
- package/dist/multistream/remoteMediaManager.js +112 -55
- package/dist/multistream/remoteMediaManager.js.map +1 -1
- package/dist/types/constants.d.ts +11 -0
- package/dist/types/locus-info/index.d.ts +7 -0
- package/dist/types/meeting/in-meeting-actions.d.ts +8 -0
- package/dist/types/meeting/index.d.ts +12 -2
- package/dist/types/meeting/muteState.d.ts +16 -0
- package/dist/types/members/index.d.ts +7 -2
- package/dist/types/multistream/remoteMediaManager.d.ts +20 -2
- package/package.json +18 -18
- package/src/breakouts/index.ts +8 -1
- package/src/constants.ts +13 -0
- package/src/locus-info/controlsUtils.ts +8 -0
- package/src/locus-info/index.ts +42 -1
- package/src/locus-info/selfUtils.ts +34 -0
- package/src/meeting/in-meeting-actions.ts +16 -0
- package/src/meeting/index.ts +53 -4
- package/src/meeting/muteState.ts +49 -30
- package/src/meeting/util.ts +15 -0
- package/src/members/index.ts +12 -4
- package/src/members/util.ts +21 -8
- package/src/multistream/remoteMediaManager.ts +57 -26
- package/test/unit/spec/breakouts/index.ts +2 -2
- package/test/unit/spec/locus-info/controlsUtils.js +104 -46
- package/test/unit/spec/locus-info/index.js +131 -16
- package/test/unit/spec/locus-info/selfConstant.js +9 -5
- package/test/unit/spec/locus-info/selfUtils.js +39 -16
- package/test/unit/spec/meeting/in-meeting-actions.ts +9 -1
- package/test/unit/spec/meeting/index.js +208 -79
- package/test/unit/spec/meeting/muteState.js +72 -6
- package/test/unit/spec/meeting/utils.js +35 -0
- package/test/unit/spec/members/index.js +75 -0
- package/test/unit/spec/members/utils.js +112 -0
- package/test/unit/spec/multistream/remoteMediaManager.ts +127 -0
package/src/meeting/muteState.ts
CHANGED
|
@@ -52,9 +52,9 @@ class MuteState {
|
|
|
52
52
|
},
|
|
53
53
|
server: {
|
|
54
54
|
localMute: false,
|
|
55
|
-
//
|
|
56
|
-
remoteMute: type === AUDIO ? meeting.remoteMuted : false,
|
|
57
|
-
unmuteAllowed: type === AUDIO ? meeting.unmuteAllowed : true,
|
|
55
|
+
// because remoteVideoMuted and unmuteVideoAllowed are updated seperately, they might be undefined
|
|
56
|
+
remoteMute: type === AUDIO ? meeting.remoteMuted : meeting.remoteVideoMuted ?? false,
|
|
57
|
+
unmuteAllowed: type === AUDIO ? meeting.unmuteAllowed : meeting.unmuteVideoAllowed ?? true,
|
|
58
58
|
},
|
|
59
59
|
syncToServerInProgress: false,
|
|
60
60
|
};
|
|
@@ -241,35 +241,28 @@ class MuteState {
|
|
|
241
241
|
* @returns {Promise}
|
|
242
242
|
*/
|
|
243
243
|
private sendRemoteMuteRequestToServer(meeting?: any) {
|
|
244
|
-
|
|
245
|
-
const remoteMute = this.state.client.localMute;
|
|
244
|
+
const remoteMute = this.state.client.localMute;
|
|
246
245
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
246
|
+
LoggerProxy.logger.info(
|
|
247
|
+
`Meeting:muteState#sendRemoteMuteRequestToServer --> ${this.type}: sending remote mute:${remoteMute} to server`
|
|
248
|
+
);
|
|
250
249
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
this.state.server.remoteMute = remoteMute;
|
|
259
|
-
})
|
|
260
|
-
.catch((remoteUpdateError) => {
|
|
261
|
-
LoggerProxy.logger.warn(
|
|
262
|
-
`Meeting:muteState#sendRemoteMuteRequestToServer --> ${this.type}: failed to apply remote mute ${remoteMute} to server: ${remoteUpdateError}`
|
|
263
|
-
);
|
|
264
|
-
|
|
265
|
-
return Promise.reject(remoteUpdateError);
|
|
266
|
-
});
|
|
267
|
-
}
|
|
250
|
+
return meeting.members
|
|
251
|
+
.muteMember(meeting.members.selfId, remoteMute, this.type === AUDIO)
|
|
252
|
+
.then(() => {
|
|
253
|
+
LoggerProxy.logger.info(
|
|
254
|
+
`Meeting:muteState#sendRemoteMuteRequestToServer --> ${this.type}: remote mute:${remoteMute} applied to server`
|
|
255
|
+
);
|
|
268
256
|
|
|
269
|
-
|
|
270
|
-
|
|
257
|
+
this.state.server.remoteMute = remoteMute;
|
|
258
|
+
})
|
|
259
|
+
.catch((remoteUpdateError) => {
|
|
260
|
+
LoggerProxy.logger.warn(
|
|
261
|
+
`Meeting:muteState#sendRemoteMuteRequestToServer --> ${this.type}: failed to apply remote mute ${remoteMute} to server: ${remoteUpdateError}`
|
|
262
|
+
);
|
|
271
263
|
|
|
272
|
-
|
|
264
|
+
return Promise.reject(remoteUpdateError);
|
|
265
|
+
});
|
|
273
266
|
}
|
|
274
267
|
|
|
275
268
|
/**
|
|
@@ -285,8 +278,12 @@ class MuteState {
|
|
|
285
278
|
LoggerProxy.logger.info(
|
|
286
279
|
`Meeting:muteState#handleServerRemoteMuteUpdate --> ${this.type}: updating server remoteMute to (${muted})`
|
|
287
280
|
);
|
|
288
|
-
|
|
289
|
-
|
|
281
|
+
if (muted !== undefined) {
|
|
282
|
+
this.state.server.remoteMute = muted;
|
|
283
|
+
}
|
|
284
|
+
if (unmuteAllowed !== undefined) {
|
|
285
|
+
this.state.server.unmuteAllowed = unmuteAllowed;
|
|
286
|
+
}
|
|
290
287
|
}
|
|
291
288
|
|
|
292
289
|
/**
|
|
@@ -330,6 +327,28 @@ class MuteState {
|
|
|
330
327
|
);
|
|
331
328
|
}
|
|
332
329
|
|
|
330
|
+
/**
|
|
331
|
+
* Returns true if the user is remotely muted
|
|
332
|
+
*
|
|
333
|
+
* @public
|
|
334
|
+
* @memberof MuteState
|
|
335
|
+
* @returns {Boolean}
|
|
336
|
+
*/
|
|
337
|
+
public isRemotelyMuted() {
|
|
338
|
+
return this.state.server.remoteMute;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Returns true if unmute is allowed
|
|
343
|
+
*
|
|
344
|
+
* @public
|
|
345
|
+
* @memberof MuteState
|
|
346
|
+
* @returns {Boolean}
|
|
347
|
+
*/
|
|
348
|
+
public isUnmuteAllowed() {
|
|
349
|
+
return this.state.server.unmuteAllowed;
|
|
350
|
+
}
|
|
351
|
+
|
|
333
352
|
/**
|
|
334
353
|
* Returns true if the user is locally muted
|
|
335
354
|
*
|
package/src/meeting/util.ts
CHANGED
|
@@ -375,6 +375,21 @@ MeetingUtil.bothLeaveAndEndMeetingAvailable = (displayHints) =>
|
|
|
375
375
|
displayHints.includes(DISPLAY_HINTS.LEAVE_TRANSFER_HOST_END_MEETING) ||
|
|
376
376
|
displayHints.includes(DISPLAY_HINTS.LEAVE_END_MEETING);
|
|
377
377
|
|
|
378
|
+
MeetingUtil.canManageBreakout = (displayHints) =>
|
|
379
|
+
displayHints.includes(DISPLAY_HINTS.BREAKOUT_MANAGEMENT);
|
|
380
|
+
|
|
381
|
+
MeetingUtil.isSuppressBreakoutSupport = (displayHints) =>
|
|
382
|
+
displayHints.includes(DISPLAY_HINTS.UCF_SUPPRESS_BREAKOUTS_SUPPORT);
|
|
383
|
+
|
|
384
|
+
MeetingUtil.canAdmitLobbyToBreakout = (displayHints) =>
|
|
385
|
+
!displayHints.includes(DISPLAY_HINTS.DISABLE_LOBBY_TO_BREAKOUT);
|
|
386
|
+
|
|
387
|
+
MeetingUtil.isBreakoutPreassignmentsEnabled = (displayHints) =>
|
|
388
|
+
!displayHints.includes(DISPLAY_HINTS.DISABLE_BREAKOUT_PREASSIGNMENTS);
|
|
389
|
+
|
|
390
|
+
MeetingUtil.canUserAskForHelp = (displayHints) =>
|
|
391
|
+
!displayHints.includes(DISPLAY_HINTS.DISABLE_ASK_FOR_HELP);
|
|
392
|
+
|
|
378
393
|
MeetingUtil.lockMeeting = (actions, request, locusUrl) => {
|
|
379
394
|
if (actions && actions.canLock) {
|
|
380
395
|
return request.lockMeeting({locusUrl, lock: true});
|
package/src/members/index.ts
CHANGED
|
@@ -733,15 +733,22 @@ export default class Members extends StatelessWebexPlugin {
|
|
|
733
733
|
/**
|
|
734
734
|
* Admits waiting members (invited guests to meeting)
|
|
735
735
|
* @param {Array} memberIds
|
|
736
|
+
* @param {Object} sessionLocusUrls: {authorizingLocusUrl, mainLocusUrl}
|
|
736
737
|
* @returns {Promise}
|
|
737
738
|
* @public
|
|
738
739
|
* @memberof Members
|
|
739
740
|
*/
|
|
740
|
-
public admitMembers(
|
|
741
|
+
public admitMembers(
|
|
742
|
+
memberIds: Array<any>,
|
|
743
|
+
sessionLocusUrls?: {authorizingLocusUrl: string; mainLocusUrl: string}
|
|
744
|
+
) {
|
|
741
745
|
if (isEmpty(memberIds)) {
|
|
742
746
|
return Promise.reject(new ParameterError('No member ids provided to admit.'));
|
|
743
747
|
}
|
|
744
|
-
const options =
|
|
748
|
+
const options = {
|
|
749
|
+
sessionLocusUrls,
|
|
750
|
+
...MembersUtil.generateAdmitMemberOptions(memberIds, this.locusUrl),
|
|
751
|
+
};
|
|
745
752
|
|
|
746
753
|
return this.membersRequest.admitMember(options);
|
|
747
754
|
}
|
|
@@ -773,11 +780,12 @@ export default class Members extends StatelessWebexPlugin {
|
|
|
773
780
|
* Audio mutes another member in a meeting
|
|
774
781
|
* @param {String} memberId
|
|
775
782
|
* @param {boolean} [mute] default true
|
|
783
|
+
* @param {boolean} [isAudio] default true
|
|
776
784
|
* @returns {Promise}
|
|
777
785
|
* @public
|
|
778
786
|
* @memberof Members
|
|
779
787
|
*/
|
|
780
|
-
public muteMember(memberId: string, mute = true) {
|
|
788
|
+
public muteMember(memberId: string, mute = true, isAudio = true) {
|
|
781
789
|
if (!this.locusUrl) {
|
|
782
790
|
return Promise.reject(
|
|
783
791
|
new ParameterError(
|
|
@@ -790,7 +798,7 @@ export default class Members extends StatelessWebexPlugin {
|
|
|
790
798
|
new ParameterError('The member id must be defined to mute the member.')
|
|
791
799
|
);
|
|
792
800
|
}
|
|
793
|
-
const options = MembersUtil.generateMuteMemberOptions(memberId, mute, this.locusUrl);
|
|
801
|
+
const options = MembersUtil.generateMuteMemberOptions(memberId, mute, this.locusUrl, isAudio);
|
|
794
802
|
|
|
795
803
|
return this.membersRequest.muteMember(options);
|
|
796
804
|
}
|
package/src/members/util.ts
CHANGED
|
@@ -54,20 +54,31 @@ MembersUtil.getAddMemberBody = (options: any) => ({
|
|
|
54
54
|
});
|
|
55
55
|
|
|
56
56
|
/**
|
|
57
|
-
* @param {Object} options with {memberIds}
|
|
57
|
+
* @param {Object} options with {memberIds, authorizingLocusUrl}
|
|
58
58
|
* @returns {Object} admit with {memberIds}
|
|
59
59
|
*/
|
|
60
|
-
MembersUtil.getAdmitMemberRequestBody = (options: any) =>
|
|
61
|
-
|
|
62
|
-
}
|
|
60
|
+
MembersUtil.getAdmitMemberRequestBody = (options: any) => {
|
|
61
|
+
const {memberIds, sessionLocusUrls} = options;
|
|
62
|
+
const body: any = {admit: {participantIds: memberIds}};
|
|
63
|
+
if (sessionLocusUrls) {
|
|
64
|
+
const {authorizingLocusUrl} = sessionLocusUrls;
|
|
65
|
+
|
|
66
|
+
return {authorizingLocusUrl, ...body};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return body;
|
|
70
|
+
};
|
|
63
71
|
|
|
64
72
|
/**
|
|
65
|
-
* @param {Object} format with {memberIds, locusUrl}
|
|
73
|
+
* @param {Object} format with {memberIds, locusUrl, sessionLocusUrls}
|
|
66
74
|
* @returns {Object} the request parameters (method, uri, body) needed to make a admitMember request
|
|
75
|
+
* if a host/cohost is in a breakout session, the locus url should be the main session locus url
|
|
67
76
|
*/
|
|
68
77
|
MembersUtil.getAdmitMemberRequestParams = (format: any) => {
|
|
69
78
|
const body = MembersUtil.getAdmitMemberRequestBody(format);
|
|
70
|
-
const
|
|
79
|
+
const {locusUrl, sessionLocusUrls} = format;
|
|
80
|
+
const baseUrl = sessionLocusUrls?.mainLocusUrl || locusUrl;
|
|
81
|
+
const uri = `${baseUrl}/${CONTROLS}`;
|
|
71
82
|
|
|
72
83
|
return {
|
|
73
84
|
method: HTTP_VERBS.PUT,
|
|
@@ -128,10 +139,11 @@ MembersUtil.generateRemoveMemberOptions = (removal, locusUrl) => ({
|
|
|
128
139
|
locusUrl,
|
|
129
140
|
});
|
|
130
141
|
|
|
131
|
-
MembersUtil.generateMuteMemberOptions = (memberId, status, locusUrl) => ({
|
|
142
|
+
MembersUtil.generateMuteMemberOptions = (memberId, status, locusUrl, isAudio) => ({
|
|
132
143
|
memberId,
|
|
133
144
|
muted: status,
|
|
134
145
|
locusUrl,
|
|
146
|
+
isAudio,
|
|
135
147
|
});
|
|
136
148
|
|
|
137
149
|
MembersUtil.generateRaiseHandMemberOptions = (memberId, status, locusUrl) => ({
|
|
@@ -146,8 +158,9 @@ MembersUtil.generateLowerAllHandsMemberOptions = (requestingParticipantId, locus
|
|
|
146
158
|
});
|
|
147
159
|
|
|
148
160
|
MembersUtil.getMuteMemberRequestParams = (options) => {
|
|
161
|
+
const property = options.isAudio === false ? 'video' : 'audio';
|
|
149
162
|
const body = {
|
|
150
|
-
|
|
163
|
+
[property]: {
|
|
151
164
|
muted: options.muted,
|
|
152
165
|
},
|
|
153
166
|
};
|
|
@@ -601,25 +601,37 @@ export class RemoteMediaManager extends EventsScope {
|
|
|
601
601
|
}
|
|
602
602
|
|
|
603
603
|
/**
|
|
604
|
-
* Allocates receive slots to all video panes
|
|
604
|
+
* Allocates receive slots to all active speaker video panes
|
|
605
|
+
* in the current selected layout.
|
|
606
|
+
*
|
|
607
|
+
* Allocation tries to keep the same order of the slots between the previous
|
|
608
|
+
* layout and the new one. Sorting helps making sure that highest priority slots
|
|
609
|
+
* go in the same order in the new layout.
|
|
605
610
|
*/
|
|
606
|
-
private
|
|
607
|
-
this.
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
611
|
+
private allocateSlotsToActiveSpeakerPaneGroups() {
|
|
612
|
+
this.currentLayout?.activeSpeakerVideoPaneGroups
|
|
613
|
+
// sorting in descending order based on group priority
|
|
614
|
+
?.sort((a, b) => (a.priority < b.priority ? 1 : -1))
|
|
615
|
+
?.forEach((group) => {
|
|
616
|
+
this.receiveSlotAllocations.activeSpeaker[group.id] = {slots: []};
|
|
617
|
+
|
|
618
|
+
for (let paneIndex = 0; paneIndex < group.numPanes; paneIndex += 1) {
|
|
619
|
+
// allocate a slot from the "unused" list, by grabbing in same order (shift) as previous layout
|
|
620
|
+
const freeSlot = this.slots.video.unused.shift();
|
|
621
|
+
|
|
622
|
+
if (freeSlot) {
|
|
623
|
+
this.slots.video.activeSpeaker.push(freeSlot);
|
|
624
|
+
this.receiveSlotAllocations.activeSpeaker[group.id].slots.push(freeSlot);
|
|
625
|
+
}
|
|
619
626
|
}
|
|
620
|
-
}
|
|
621
|
-
|
|
627
|
+
});
|
|
628
|
+
}
|
|
622
629
|
|
|
630
|
+
/**
|
|
631
|
+
* Allocates receive slots to all receiver selected video panes
|
|
632
|
+
* in the current selected layout
|
|
633
|
+
*/
|
|
634
|
+
private allocateSlotsToReceiverSelectedVideoPaneGroups() {
|
|
623
635
|
this.currentLayout?.memberVideoPanes?.forEach((memberPane) => {
|
|
624
636
|
// check if there is existing slot for this csi
|
|
625
637
|
const existingSlot = this.slots.video.receiverSelected.find(
|
|
@@ -646,19 +658,15 @@ export class RemoteMediaManager extends EventsScope {
|
|
|
646
658
|
}
|
|
647
659
|
|
|
648
660
|
/**
|
|
649
|
-
*
|
|
650
|
-
* and allocates them to the right video panes / pane groups
|
|
651
|
-
*
|
|
652
|
-
* @returns {Promise}
|
|
661
|
+
* Ensures that we have enough slots for the current layout.
|
|
653
662
|
*/
|
|
654
|
-
private async
|
|
663
|
+
private async refillRequiredSlotsIfNeeded() {
|
|
655
664
|
const requiredNumSlots = this.getRequiredNumVideoSlotsForLayout(this.currentLayout);
|
|
656
665
|
const totalNumSlots =
|
|
657
666
|
this.slots.video.unused.length +
|
|
658
667
|
this.slots.video.activeSpeaker.length +
|
|
659
668
|
this.slots.video.receiverSelected.length;
|
|
660
669
|
|
|
661
|
-
// ensure we have enough total slots for current layout
|
|
662
670
|
if (totalNumSlots < requiredNumSlots) {
|
|
663
671
|
let numSlotsToCreate = requiredNumSlots - totalNumSlots;
|
|
664
672
|
|
|
@@ -671,16 +679,39 @@ export class RemoteMediaManager extends EventsScope {
|
|
|
671
679
|
numSlotsToCreate -= 1;
|
|
672
680
|
}
|
|
673
681
|
}
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
/**
|
|
685
|
+
* Move all active speaker slots to "unused"
|
|
686
|
+
*/
|
|
687
|
+
private trimActiveSpeakerSlots() {
|
|
688
|
+
this.slots.video.unused.push(...this.slots.video.activeSpeaker);
|
|
689
|
+
this.slots.video.activeSpeaker.length = 0;
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
/**
|
|
693
|
+
* Makes sure we have the right number of receive slots created for the current layout
|
|
694
|
+
* and allocates them to the right video panes / pane groups
|
|
695
|
+
*
|
|
696
|
+
* @returns {Promise}
|
|
697
|
+
*/
|
|
698
|
+
private async updateVideoReceiveSlots() {
|
|
699
|
+
// move all active speaker slots to "unused"
|
|
700
|
+
this.trimActiveSpeakerSlots();
|
|
674
701
|
|
|
675
702
|
// move all no longer needed receiver-selected slots to "unused"
|
|
676
703
|
this.trimReceiverSelectedSlots();
|
|
677
704
|
|
|
678
|
-
//
|
|
679
|
-
this.
|
|
680
|
-
this.slots.video.activeSpeaker.length = 0;
|
|
705
|
+
// ensure we have enough total slots for current layout
|
|
706
|
+
await this.refillRequiredSlotsIfNeeded();
|
|
681
707
|
|
|
682
708
|
// allocate the slots to the right panes / pane groups
|
|
683
|
-
|
|
709
|
+
// reset allocations
|
|
710
|
+
this.receiveSlotAllocations = {activeSpeaker: {}, receiverSelected: {}};
|
|
711
|
+
// allocate active speaker
|
|
712
|
+
this.allocateSlotsToActiveSpeakerPaneGroups();
|
|
713
|
+
// allocate receiver selected
|
|
714
|
+
this.allocateSlotsToReceiverSelectedVideoPaneGroups();
|
|
684
715
|
|
|
685
716
|
LoggerProxy.logger.log(
|
|
686
717
|
`RemoteMediaManager#updateVideoReceiveSlots --> receive slots updated: unused=${this.slots.video.unused.length}, activeSpeaker=${this.slots.video.activeSpeaker.length}, receiverSelected=${this.slots.video.receiverSelected.length}`
|
|
@@ -511,8 +511,8 @@ describe('plugin-meetings', () => {
|
|
|
511
511
|
|
|
512
512
|
assert.equal(arg.uri, 'url');
|
|
513
513
|
assert.equal(arg.method, 'PUT');
|
|
514
|
-
assert.deepEqual(argObj1, {id:'groupId', action: 'START', allowBackToMain: false, allowToJoinLater: false});
|
|
515
|
-
assert.deepEqual(argObj2, {id:'id', action: 'START', allowBackToMain: false, allowToJoinLater: false, someOtherParam: 'someOtherParam'});
|
|
514
|
+
assert.deepEqual(argObj1, {id:'groupId', action: 'START', allowBackToMain: false, allowToJoinLater: false, duration: BREAKOUTS.DEFAULT_DURATION});
|
|
515
|
+
assert.deepEqual(argObj2, {id:'id', action: 'START', allowBackToMain: false, allowToJoinLater: false, someOtherParam: 'someOtherParam', duration: BREAKOUTS.DEFAULT_DURATION});
|
|
516
516
|
assert.deepEqual(result, {body: getBOResponse('OPEN')});
|
|
517
517
|
});
|
|
518
518
|
|
|
@@ -39,64 +39,122 @@ describe('plugin-meetings', () => {
|
|
|
39
39
|
|
|
40
40
|
assert.equal(parsedControls.entryExitTone, null);
|
|
41
41
|
});
|
|
42
|
-
});
|
|
43
|
-
});
|
|
44
42
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
assert.equal(updates.hasEntryExitToneChanged, true);
|
|
56
|
-
});
|
|
43
|
+
describe('videoEnabled', () => {
|
|
44
|
+
it('returns expected', () => {
|
|
45
|
+
const result = ControlsUtils.parse({video: {enabled: true}});
|
|
46
|
+
assert.deepEqual(result, {
|
|
47
|
+
video: {
|
|
48
|
+
enabled: true,
|
|
49
|
+
},
|
|
50
|
+
videoEnabled: true,
|
|
51
|
+
});
|
|
52
|
+
});
|
|
57
53
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
mode: 'foo',
|
|
63
|
-
},
|
|
64
|
-
};
|
|
65
|
-
const {updates} = ControlsUtils.getControls(defaultControls, newControls);
|
|
54
|
+
it('returns expected from undefined', () => {
|
|
55
|
+
const result = ControlsUtils.parse();
|
|
56
|
+
assert.deepEqual(result, {});
|
|
57
|
+
});
|
|
66
58
|
|
|
67
|
-
|
|
59
|
+
it('returns expected from undefined controls', () => {
|
|
60
|
+
const result = ControlsUtils.parse({});
|
|
61
|
+
assert.deepEqual(result, {});
|
|
62
|
+
});
|
|
63
|
+
});
|
|
68
64
|
});
|
|
69
65
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
66
|
+
describe('getControls', () => {
|
|
67
|
+
it('returns hasEntryExitToneChanged = true when mode changed', () => {
|
|
68
|
+
const newControls = {
|
|
69
|
+
entryExitTone: {
|
|
70
|
+
enabled: true,
|
|
71
|
+
mode: 'bar',
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
const {updates} = ControlsUtils.getControls(defaultControls, newControls);
|
|
78
75
|
|
|
79
|
-
|
|
80
|
-
|
|
76
|
+
assert.equal(updates.hasEntryExitToneChanged, true);
|
|
77
|
+
});
|
|
81
78
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
79
|
+
it('returns hasEntryExitToneChanged = true when enabled changed', () => {
|
|
80
|
+
const newControls = {
|
|
81
|
+
entryExitTone: {
|
|
82
|
+
enabled: false,
|
|
83
|
+
mode: 'foo',
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
const {updates} = ControlsUtils.getControls(defaultControls, newControls);
|
|
86
87
|
|
|
87
|
-
|
|
88
|
+
assert.equal(updates.hasEntryExitToneChanged, true);
|
|
89
|
+
});
|
|
88
90
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
+
it('returns hasEntryExitToneChanged = false when nothing changed', () => {
|
|
92
|
+
const newControls = {
|
|
93
|
+
entryExitTone: {
|
|
94
|
+
enabled: true,
|
|
95
|
+
mode: 'foo',
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
const {updates} = ControlsUtils.getControls(defaultControls, newControls);
|
|
99
|
+
|
|
100
|
+
assert.equal(updates.hasEntryExitToneChanged, false);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('returns hasBreakoutChanged = true when it has changed', () => {
|
|
104
|
+
const newControls = {
|
|
105
|
+
breakout: 'breakout',
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const {updates} = ControlsUtils.getControls({breakout: 'old breakout'}, newControls);
|
|
109
|
+
|
|
110
|
+
assert.equal(updates.hasBreakoutChanged, true);
|
|
111
|
+
});
|
|
91
112
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
113
|
+
it('returns hasBreakoutChanged = false when it has not changed', () => {
|
|
114
|
+
const newControls = {
|
|
115
|
+
breakout: 'breakout',
|
|
116
|
+
};
|
|
96
117
|
|
|
97
|
-
|
|
118
|
+
const {updates} = ControlsUtils.getControls({breakout: 'breakout'}, newControls);
|
|
98
119
|
|
|
99
|
-
|
|
120
|
+
assert.equal(updates.hasBreakoutChanged, false);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
describe('videoEnabled', () => {
|
|
124
|
+
const testVideoEnabled = (oldControls, newControls, updatedProperty) => {
|
|
125
|
+
const result = ControlsUtils.getControls(oldControls, newControls);
|
|
126
|
+
|
|
127
|
+
let expectedPrevious = oldControls;
|
|
128
|
+
if (Object.keys(oldControls).length) {
|
|
129
|
+
expectedPrevious = {
|
|
130
|
+
...expectedPrevious,
|
|
131
|
+
...{videoEnabled: oldControls.video.enabled},
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
const expectedCurrent = {...newControls, ...{videoEnabled: newControls.video.enabled}};
|
|
135
|
+
|
|
136
|
+
assert.deepEqual(result.previous, expectedPrevious);
|
|
137
|
+
assert.deepEqual(result.current, expectedCurrent);
|
|
138
|
+
if (updatedProperty !== undefined) {
|
|
139
|
+
assert.deepEqual(
|
|
140
|
+
result.updates.hasVideoEnabledChanged,
|
|
141
|
+
!isEqual(oldControls, newControls)
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
it('returns expected from undefined', () => {
|
|
147
|
+
testVideoEnabled({}, {video: {enabled: true}});
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it('returns expected from defined', () => {
|
|
151
|
+
testVideoEnabled({video: {enabled: false}}, {video: {enabled: true}});
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it('returns expected for unchanged', () => {
|
|
155
|
+
testVideoEnabled({video: {enabled: false}}, {video: {enabled: false}});
|
|
156
|
+
});
|
|
157
|
+
});
|
|
100
158
|
});
|
|
101
159
|
});
|
|
102
160
|
});
|