@webex/plugin-meetings 3.9.0-next.23 → 3.9.0-next.25
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 +1 -1
- package/dist/constants.js +4 -0
- package/dist/constants.js.map +1 -1
- package/dist/controls-options-manager/index.js +22 -5
- package/dist/controls-options-manager/index.js.map +1 -1
- package/dist/interpretation/index.js +1 -1
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/locus-info/controlsUtils.js +11 -2
- package/dist/locus-info/controlsUtils.js.map +1 -1
- package/dist/locus-info/index.js +17 -3
- package/dist/locus-info/index.js.map +1 -1
- package/dist/meeting/in-meeting-actions.js +2 -0
- package/dist/meeting/in-meeting-actions.js.map +1 -1
- package/dist/meeting/index.js +74 -32
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/request.js +48 -14
- package/dist/meeting/request.js.map +1 -1
- package/dist/meeting/util.js +3 -0
- package/dist/meeting/util.js.map +1 -1
- package/dist/member/index.js +9 -0
- package/dist/member/index.js.map +1 -1
- package/dist/member/util.js +10 -0
- package/dist/member/util.js.map +1 -1
- package/dist/types/constants.d.ts +3 -0
- package/dist/types/controls-options-manager/index.d.ts +9 -1
- package/dist/types/locus-info/index.d.ts +2 -1
- package/dist/types/meeting/in-meeting-actions.d.ts +2 -0
- package/dist/types/meeting/index.d.ts +14 -0
- package/dist/types/meeting/request.d.ts +16 -0
- package/dist/types/meeting/util.d.ts +1 -0
- package/dist/types/member/index.d.ts +1 -0
- package/dist/types/member/util.d.ts +5 -0
- package/dist/webinar/index.js +1 -1
- package/package.json +1 -1
- package/src/constants.ts +6 -0
- package/src/controls-options-manager/index.ts +26 -5
- package/src/locus-info/controlsUtils.ts +18 -0
- package/src/locus-info/index.ts +14 -4
- package/src/meeting/in-meeting-actions.ts +4 -0
- package/src/meeting/index.ts +67 -20
- package/src/meeting/request.ts +38 -0
- package/src/meeting/util.ts +3 -0
- package/src/member/index.ts +10 -0
- package/src/member/util.ts +14 -0
- package/test/unit/spec/controls-options-manager/index.js +47 -0
- package/test/unit/spec/fixture/locus.js +1 -0
- package/test/unit/spec/locus-info/index.js +39 -0
- package/test/unit/spec/meeting/in-meeting-actions.ts +2 -0
- package/test/unit/spec/meeting/index.js +41 -2
- package/test/unit/spec/meeting/utils.js +1 -0
- package/test/unit/spec/member/util.js +24 -0
package/src/meeting/request.ts
CHANGED
@@ -886,6 +886,44 @@ export default class MeetingRequest extends StatelessWebexPlugin {
|
|
886
886
|
});
|
887
887
|
}
|
888
888
|
|
889
|
+
/**
|
890
|
+
* Extend the current meeting duration.
|
891
|
+
*
|
892
|
+
* @param {Object} params - Parameters for extending the meeting.
|
893
|
+
* @param {string} params.meetingInstanceId - The unique ID of the meeting instance.
|
894
|
+
* @param {string} params.participantId - The ID of the participant requesting the extension.
|
895
|
+
* @param {number} params.extensionMinutes - The number of minutes to extend the meeting by.
|
896
|
+
* @param {string} params.meetingPolicyUrl - The base URL for meeting policy service (dynamic, from locus links)
|
897
|
+
* @returns {Promise<any>} A promise that resolves with the server response.
|
898
|
+
*/
|
899
|
+
extendMeeting({
|
900
|
+
meetingInstanceId,
|
901
|
+
participantId,
|
902
|
+
extensionMinutes,
|
903
|
+
meetingPolicyUrl,
|
904
|
+
}: {
|
905
|
+
meetingInstanceId: string;
|
906
|
+
participantId: string;
|
907
|
+
extensionMinutes: number;
|
908
|
+
meetingPolicyUrl: string;
|
909
|
+
}) {
|
910
|
+
if (!meetingPolicyUrl) {
|
911
|
+
return Promise.reject(new Error('meetingPolicyUrl is required'));
|
912
|
+
}
|
913
|
+
const uri = `${meetingPolicyUrl}/continueMeeting`;
|
914
|
+
|
915
|
+
// @ts-ignore
|
916
|
+
return this.request({
|
917
|
+
method: HTTP_VERBS.POST,
|
918
|
+
uri,
|
919
|
+
body: {
|
920
|
+
meetingInstanceId,
|
921
|
+
requestParticipantId: participantId,
|
922
|
+
extensionMinutes,
|
923
|
+
},
|
924
|
+
});
|
925
|
+
}
|
926
|
+
|
889
927
|
/**
|
890
928
|
* Make a network request to enable or disable reactions.
|
891
929
|
* @param {boolean} options.enable - determines if we need to enable or disable.
|
package/src/meeting/util.ts
CHANGED
@@ -654,6 +654,9 @@ const MeetingUtil = {
|
|
654
654
|
|
655
655
|
waitingForOthersToJoin: (displayHints) => displayHints.includes(DISPLAY_HINTS.WAITING_FOR_OTHERS),
|
656
656
|
|
657
|
+
showAutoEndMeetingWarning: (displayHints) =>
|
658
|
+
displayHints.includes(DISPLAY_HINTS.SHOW_AUTO_END_MEETING_WARNING),
|
659
|
+
|
657
660
|
canSendReactions: (originalValue, displayHints) => {
|
658
661
|
if (displayHints.includes(DISPLAY_HINTS.REACTIONS_ACTIVE)) {
|
659
662
|
return true;
|
package/src/member/index.ts
CHANGED
@@ -42,6 +42,7 @@ export default class Member {
|
|
42
42
|
status: any;
|
43
43
|
supportsBreakouts: boolean;
|
44
44
|
supportsInterpretation: boolean;
|
45
|
+
supportsSingleUserAutoEndMeeting: boolean;
|
45
46
|
supportLiveAnnotation: boolean;
|
46
47
|
type: any;
|
47
48
|
namespace = MEETINGS;
|
@@ -130,6 +131,13 @@ export default class Member {
|
|
130
131
|
* @memberof Member
|
131
132
|
*/
|
132
133
|
this.supportsBreakouts = null;
|
134
|
+
/**
|
135
|
+
* @instance
|
136
|
+
* @type {Boolean}
|
137
|
+
* @public
|
138
|
+
* @memberof Member
|
139
|
+
*/
|
140
|
+
this.supportsSingleUserAutoEndMeeting = null;
|
133
141
|
/**
|
134
142
|
* @instance
|
135
143
|
* @type {Boolean}
|
@@ -339,6 +347,8 @@ export default class Member {
|
|
339
347
|
this.isVideoMuted = MemberUtil.isVideoMuted(participant);
|
340
348
|
this.isHandRaised = MemberUtil.isHandRaised(participant);
|
341
349
|
this.supportsBreakouts = MemberUtil.isBreakoutsSupported(participant);
|
350
|
+
this.supportsSingleUserAutoEndMeeting =
|
351
|
+
MemberUtil.isSupportsSingleUserAutoEndMeeting(participant);
|
342
352
|
this.supportsInterpretation = MemberUtil.isInterpretationSupported(participant);
|
343
353
|
this.supportLiveAnnotation = MemberUtil.isLiveAnnotationSupported(participant);
|
344
354
|
this.isGuest = MemberUtil.isGuest(participant);
|
package/src/member/util.ts
CHANGED
@@ -207,6 +207,20 @@ const MemberUtil = {
|
|
207
207
|
return !participant.doesNotSupportBreakouts;
|
208
208
|
},
|
209
209
|
|
210
|
+
/**
|
211
|
+
* @param {Object} participant - The locus participant object.
|
212
|
+
* @returns {Boolean}
|
213
|
+
*/
|
214
|
+
isSupportsSingleUserAutoEndMeeting: (participant) => {
|
215
|
+
if (!participant) {
|
216
|
+
throw new ParameterError(
|
217
|
+
'Single user auto end meeting support could not be processed, participant is undefined.'
|
218
|
+
);
|
219
|
+
}
|
220
|
+
|
221
|
+
return !participant.doesNotSupportSingleUserAutoEndMeeting;
|
222
|
+
},
|
223
|
+
|
210
224
|
/**
|
211
225
|
* @param {Object} participant - The locus participant object.
|
212
226
|
* @returns {Boolean}
|
@@ -133,6 +133,7 @@ describe('plugin-meetings', () => {
|
|
133
133
|
|
134
134
|
manager.set({
|
135
135
|
locusUrl: 'test/id',
|
136
|
+
mainLocusUrl: '',
|
136
137
|
displayHints: [],
|
137
138
|
});
|
138
139
|
});
|
@@ -201,6 +202,38 @@ describe('plugin-meetings', () => {
|
|
201
202
|
Util.canUpdate = restorable;
|
202
203
|
});
|
203
204
|
});
|
205
|
+
|
206
|
+
it('should call request with mainLocusUrl and locusUrl as authorizingLocusUrl if mainLocusUrl is exist and not same with locusUrl', () => {
|
207
|
+
const restorable = Util.canUpdate;
|
208
|
+
Util.canUpdate = sinon.stub().returns(true);
|
209
|
+
manager.mainLocusUrl = 'test/main';
|
210
|
+
|
211
|
+
const audio = {scope: 'audio', properties: {a: 1, b: 2}};
|
212
|
+
const reactions = {scope: 'reactions', properties: {c: 3, d: 4}};
|
213
|
+
|
214
|
+
return manager.update(audio, reactions)
|
215
|
+
.then(() => {
|
216
|
+
assert.calledWith(request.request, {
|
217
|
+
uri: 'test/main/controls',
|
218
|
+
body: {
|
219
|
+
audio: audio.properties,
|
220
|
+
authorizingLocusUrl: 'test/id'
|
221
|
+
},
|
222
|
+
method: HTTP_VERBS.PATCH,
|
223
|
+
});
|
224
|
+
|
225
|
+
assert.calledWith(request.request, {
|
226
|
+
uri: 'test/main/controls',
|
227
|
+
body: {
|
228
|
+
reactions: reactions.properties,
|
229
|
+
authorizingLocusUrl: 'test/id'
|
230
|
+
},
|
231
|
+
method: HTTP_VERBS.PATCH,
|
232
|
+
});
|
233
|
+
|
234
|
+
Util.canUpdate = restorable;
|
235
|
+
});
|
236
|
+
});
|
204
237
|
});
|
205
238
|
|
206
239
|
describe('Mute/Unmute All', () => {
|
@@ -214,6 +247,7 @@ describe('plugin-meetings', () => {
|
|
214
247
|
|
215
248
|
manager.set({
|
216
249
|
locusUrl: 'test/id',
|
250
|
+
mainLocusUrl: '',
|
217
251
|
displayHints: [],
|
218
252
|
})
|
219
253
|
});
|
@@ -305,6 +339,19 @@ describe('plugin-meetings', () => {
|
|
305
339
|
|
306
340
|
assert.deepEqual(result, request.request.firstCall.returnValue);
|
307
341
|
});
|
342
|
+
|
343
|
+
it('request with mainLocusUrl and make locusUrl as authorizingLocusUrl if mainLocusUrl is exist and not same with locusUrl', () => {
|
344
|
+
manager.setDisplayHints(['MUTE_ALL', 'DISABLE_HARD_MUTE', 'DISABLE_MUTE_ON_ENTRY']);
|
345
|
+
manager.mainLocusUrl = `test/main`;
|
346
|
+
|
347
|
+
const result = manager.setMuteAll(true, true, true, ['attendee']);
|
348
|
+
|
349
|
+
assert.calledWith(request.request, { uri: 'test/main/controls',
|
350
|
+
body: { audio: { muted: true, disallowUnmute: true, muteOnEntry: true, roles: ['attendee'] }, authorizingLocusUrl: 'test/id' },
|
351
|
+
method: HTTP_VERBS.PATCH});
|
352
|
+
|
353
|
+
assert.deepEqual(result, request.request.firstCall.returnValue);
|
354
|
+
});
|
308
355
|
});
|
309
356
|
});
|
310
357
|
});
|
@@ -3020,6 +3020,45 @@ describe('plugin-meetings', () => {
|
|
3020
3020
|
});
|
3021
3021
|
});
|
3022
3022
|
|
3023
|
+
describe('#updateLocusUrl', () => {
|
3024
|
+
it('trigger LOCUS_INFO_UPDATE_URL event with isMainLocus is true as default', () => {
|
3025
|
+
const fakeUrl = "https://fake.com/locus";
|
3026
|
+
locusInfo.emitScoped = sinon.stub();
|
3027
|
+
locusInfo.updateLocusUrl(fakeUrl);
|
3028
|
+
|
3029
|
+
assert.calledWith(
|
3030
|
+
locusInfo.emitScoped,
|
3031
|
+
{
|
3032
|
+
file: 'locus-info',
|
3033
|
+
function: 'updateLocusUrl',
|
3034
|
+
},
|
3035
|
+
EVENTS.LOCUS_INFO_UPDATE_URL,
|
3036
|
+
{
|
3037
|
+
url: fakeUrl,
|
3038
|
+
isMainLocus: true
|
3039
|
+
},
|
3040
|
+
);
|
3041
|
+
});
|
3042
|
+
it('trigger LOCUS_INFO_UPDATE_URL event with isMainLocus is false', () => {
|
3043
|
+
const fakeUrl = "https://fake.com/locus";
|
3044
|
+
locusInfo.emitScoped = sinon.stub();
|
3045
|
+
locusInfo.updateLocusUrl(fakeUrl, false);
|
3046
|
+
|
3047
|
+
assert.calledWith(
|
3048
|
+
locusInfo.emitScoped,
|
3049
|
+
{
|
3050
|
+
file: 'locus-info',
|
3051
|
+
function: 'updateLocusUrl',
|
3052
|
+
},
|
3053
|
+
EVENTS.LOCUS_INFO_UPDATE_URL,
|
3054
|
+
{
|
3055
|
+
url: fakeUrl,
|
3056
|
+
isMainLocus: false
|
3057
|
+
},
|
3058
|
+
);
|
3059
|
+
});
|
3060
|
+
});
|
3061
|
+
|
3023
3062
|
// semi-integration tests that use real LocusInfo with real Parser
|
3024
3063
|
// and test various scenarios related to handling out-of-order Locus delta events
|
3025
3064
|
describe('handling of out-of-order Locus delta events', () => {
|
@@ -86,6 +86,7 @@ describe('plugin-meetings', () => {
|
|
86
86
|
canDoVideo: null,
|
87
87
|
canAnnotate: null,
|
88
88
|
canUseVoip: null,
|
89
|
+
showAutoEndMeetingWarning: null,
|
89
90
|
supportHQV: null,
|
90
91
|
supportHDV: null,
|
91
92
|
canShareWhiteBoard: null,
|
@@ -197,6 +198,7 @@ describe('plugin-meetings', () => {
|
|
197
198
|
'canRealtimeCloseCaption',
|
198
199
|
'canRealtimeCloseCaptionManual',
|
199
200
|
'canChat',
|
201
|
+
'showAutoEndMeetingWarning',
|
200
202
|
'canDoVideo',
|
201
203
|
'canAnnotate',
|
202
204
|
'canUseVoip',
|
@@ -10471,6 +10471,24 @@ describe('plugin-meetings', () => {
|
|
10471
10471
|
);
|
10472
10472
|
});
|
10473
10473
|
|
10474
|
+
it('listens to CONTROLS_AUTO_END_MEETING_WARNING_CHANGED', async () => {
|
10475
|
+
const state = {example: 'value'};
|
10476
|
+
|
10477
|
+
await meeting.locusInfo.emitScoped(
|
10478
|
+
{function: 'test', file: 'test'},
|
10479
|
+
LOCUSINFO.EVENTS.CONTROLS_AUTO_END_MEETING_WARNING_CHANGED,
|
10480
|
+
{state}
|
10481
|
+
);
|
10482
|
+
|
10483
|
+
assert.calledWith(
|
10484
|
+
TriggerProxy.trigger,
|
10485
|
+
meeting,
|
10486
|
+
{file: 'meeting/index', function: 'setupLocusControlsListener'},
|
10487
|
+
EVENT_TRIGGERS.MEETING_CONTROLS_AUTO_END_MEETING_WARNING_UPDATED,
|
10488
|
+
{state}
|
10489
|
+
);
|
10490
|
+
});
|
10491
|
+
|
10474
10492
|
it('listens to CONTROLS_REMOTE_DESKTOP_CONTROL_CHANGED', async () => {
|
10475
10493
|
const state = {example: 'value'};
|
10476
10494
|
|
@@ -10550,6 +10568,7 @@ describe('plugin-meetings', () => {
|
|
10550
10568
|
describe('#setUpLocusUrlListener', () => {
|
10551
10569
|
it('listens to the locus url update event', (done) => {
|
10552
10570
|
const newLocusUrl = 'newLocusUrl/12345';
|
10571
|
+
const payload = {url: newLocusUrl}
|
10553
10572
|
|
10554
10573
|
meeting.members = {locusUrlUpdate: sinon.stub().returns(Promise.resolve(test1))};
|
10555
10574
|
meeting.recordingController = {setLocusUrl: sinon.stub().returns(undefined)};
|
@@ -10563,14 +10582,14 @@ describe('plugin-meetings', () => {
|
|
10563
10582
|
meeting.locusInfo.emit(
|
10564
10583
|
{function: 'test', file: 'test'},
|
10565
10584
|
'LOCUS_INFO_UPDATE_URL',
|
10566
|
-
|
10585
|
+
payload
|
10567
10586
|
);
|
10568
10587
|
assert.calledWith(meeting.members.locusUrlUpdate, newLocusUrl);
|
10569
10588
|
assert.calledOnceWithExactly(meeting.breakouts.locusUrlUpdate, newLocusUrl);
|
10570
10589
|
assert.calledOnceWithExactly(meeting.annotation.locusUrlUpdate, newLocusUrl);
|
10571
10590
|
assert.calledWith(meeting.members.locusUrlUpdate, newLocusUrl);
|
10572
10591
|
assert.calledWith(meeting.recordingController.setLocusUrl, newLocusUrl);
|
10573
|
-
assert.calledWith(meeting.controlsOptionsManager.setLocusUrl, newLocusUrl);
|
10592
|
+
assert.calledWith(meeting.controlsOptionsManager.setLocusUrl, newLocusUrl, false);
|
10574
10593
|
assert.calledWith(meeting.simultaneousInterpretation.locusUrlUpdate, newLocusUrl);
|
10575
10594
|
assert.calledWith(meeting.webinar.locusUrlUpdate, newLocusUrl);
|
10576
10595
|
assert.equal(meeting.locusUrl, newLocusUrl);
|
@@ -10588,6 +10607,22 @@ describe('plugin-meetings', () => {
|
|
10588
10607
|
{locusUrl: 'newLocusUrl/12345'}
|
10589
10608
|
);
|
10590
10609
|
|
10610
|
+
done();
|
10611
|
+
});
|
10612
|
+
it('update mainLocusUrl for controlsOptionManager if payload.isMainLocus as true', (done) => {
|
10613
|
+
const newLocusUrl = 'newLocusUrl/12345';
|
10614
|
+
const payload = {url: newLocusUrl, isMainLocus: true}
|
10615
|
+
|
10616
|
+
meeting.controlsOptionsManager = {setLocusUrl: sinon.stub().returns(undefined)};
|
10617
|
+
|
10618
|
+
meeting.locusInfo.emit(
|
10619
|
+
{function: 'test', file: 'test'},
|
10620
|
+
'LOCUS_INFO_UPDATE_URL',
|
10621
|
+
payload
|
10622
|
+
);
|
10623
|
+
|
10624
|
+
assert.calledWith(meeting.controlsOptionsManager.setLocusUrl, newLocusUrl, true);
|
10625
|
+
|
10591
10626
|
done();
|
10592
10627
|
});
|
10593
10628
|
});
|
@@ -11426,6 +11461,7 @@ describe('plugin-meetings', () => {
|
|
11426
11461
|
let canShareWhiteBoardSpy;
|
11427
11462
|
let canMoveToLobbySpy;
|
11428
11463
|
let isSpokenLanguageAutoDetectionEnabledSpy;
|
11464
|
+
let showAutoEndMeetingWarningSpy;
|
11429
11465
|
// Due to import tree issues, hasHints must be stubed within the scope of the `it`.
|
11430
11466
|
|
11431
11467
|
beforeEach(() => {
|
@@ -11457,6 +11493,7 @@ describe('plugin-meetings', () => {
|
|
11457
11493
|
canUserRenameOthersSpy = sinon.spy(MeetingUtil, 'canUserRenameOthers');
|
11458
11494
|
canShareWhiteBoardSpy = sinon.spy(MeetingUtil, 'canShareWhiteBoard');
|
11459
11495
|
canMoveToLobbySpy = sinon.spy(MeetingUtil, 'canMoveToLobby');
|
11496
|
+
showAutoEndMeetingWarningSpy = sinon.spy(MeetingUtil, 'showAutoEndMeetingWarning');
|
11460
11497
|
isSpokenLanguageAutoDetectionEnabledSpy = sinon.spy(MeetingUtil, 'isSpokenLanguageAutoDetectionEnabled');
|
11461
11498
|
|
11462
11499
|
});
|
@@ -11464,6 +11501,7 @@ describe('plugin-meetings', () => {
|
|
11464
11501
|
afterEach(() => {
|
11465
11502
|
inMeetingActionsSetSpy.restore();
|
11466
11503
|
waitingForOthersToJoinSpy.restore();
|
11504
|
+
showAutoEndMeetingWarningSpy.restore();
|
11467
11505
|
});
|
11468
11506
|
|
11469
11507
|
forEach(
|
@@ -12011,6 +12049,7 @@ describe('plugin-meetings', () => {
|
|
12011
12049
|
assert.calledWith(canUserRenameOthersSpy, userDisplayHints);
|
12012
12050
|
assert.calledWith(canShareWhiteBoardSpy, userDisplayHints, selfUserPolicies);
|
12013
12051
|
assert.calledWith(canMoveToLobbySpy, userDisplayHints);
|
12052
|
+
assert.calledWith(showAutoEndMeetingWarningSpy, userDisplayHints);
|
12014
12053
|
assert.calledWith(isSpokenLanguageAutoDetectionEnabledSpy, userDisplayHints);
|
12015
12054
|
|
12016
12055
|
assert.calledWith(ControlsOptionsUtil.hasHints, {
|
@@ -985,6 +985,7 @@ describe('plugin-meetings', () => {
|
|
985
985
|
{functionName: 'isRealTimeTranslationEnabled', displayHint: 'DISPLAY_REAL_TIME_TRANSLATION'},
|
986
986
|
{functionName: 'canSelectSpokenLanguages', displayHint: 'DISPLAY_NON_ENGLISH_ASR'},
|
987
987
|
{functionName: 'waitingForOthersToJoin', displayHint: 'WAITING_FOR_OTHERS'},
|
988
|
+
{functionName: 'showAutoEndMeetingWarning', displayHint: 'SHOW_AUTO_END_MEETING_WARNING'},
|
988
989
|
].forEach(({functionName, displayHint}) => {
|
989
990
|
describe(functionName, () => {
|
990
991
|
it('works as expected', () => {
|
@@ -417,6 +417,30 @@ describe('plugin-meetings', () => {
|
|
417
417
|
});
|
418
418
|
});
|
419
419
|
|
420
|
+
describe('MemberUtil.isSupportsSingleUserAutoEndMeeting', () => {
|
421
|
+
it('throws an error when there is no participant', () => {
|
422
|
+
assert.throws(() => {
|
423
|
+
MemberUtil.isSupportsSingleUserAutoEndMeeting();
|
424
|
+
}, 'Single user auto end meeting support could not be processed, participant is undefined.');
|
425
|
+
});
|
426
|
+
|
427
|
+
it('returns true when single user auto end meeting is supported', () => {
|
428
|
+
const participant = {
|
429
|
+
supportsSingleUserAutoEndMeeting: {},
|
430
|
+
};
|
431
|
+
assert.isTrue(MemberUtil.isSupportsSingleUserAutoEndMeeting(participant));
|
432
|
+
});
|
433
|
+
|
434
|
+
it('returns false when single user auto end meeting is not supported', () => {
|
435
|
+
const participant = {
|
436
|
+
doesNotSupportSingleUserAutoEndMeeting: {},
|
437
|
+
};
|
438
|
+
|
439
|
+
assert.isFalse(MemberUtil.isSupportsSingleUserAutoEndMeeting(participant));
|
440
|
+
});
|
441
|
+
});
|
442
|
+
|
443
|
+
|
420
444
|
describe('MemberUtil.isLiveAnnotationSupported', () => {
|
421
445
|
it('throws an error when there is no participant', () => {
|
422
446
|
assert.throws(() => {
|