@webex/plugin-meetings 3.8.1-web-workers-keepalive.1 → 3.9.0-multipleLLM.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/breakouts/breakout.js +1 -1
- package/dist/breakouts/index.js +1 -1
- package/dist/constants.js +26 -2
- package/dist/constants.js.map +1 -1
- package/dist/interpretation/index.js +1 -1
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/locus-info/index.js +77 -95
- package/dist/locus-info/index.js.map +1 -1
- package/dist/locus-info/parser.js +4 -1
- package/dist/locus-info/parser.js.map +1 -1
- package/dist/media/properties.js +53 -5
- package/dist/media/properties.js.map +1 -1
- package/dist/meeting/brbState.js +14 -12
- package/dist/meeting/brbState.js.map +1 -1
- package/dist/meeting/in-meeting-actions.js +8 -0
- package/dist/meeting/in-meeting-actions.js.map +1 -1
- package/dist/meeting/index.js +443 -225
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/muteState.js +2 -5
- package/dist/meeting/muteState.js.map +1 -1
- package/dist/meeting/request.js +44 -0
- package/dist/meeting/request.js.map +1 -1
- package/dist/meeting/request.type.js.map +1 -1
- package/dist/meeting/type.js +7 -0
- package/dist/meeting/type.js.map +1 -0
- package/dist/meeting/util.js +98 -13
- package/dist/meeting/util.js.map +1 -1
- package/dist/meeting-info/meeting-info-v2.js +29 -21
- package/dist/meeting-info/meeting-info-v2.js.map +1 -1
- package/dist/meetings/index.js +18 -10
- package/dist/meetings/index.js.map +1 -1
- package/dist/member/index.js.map +1 -1
- package/dist/member/types.js.map +1 -1
- package/dist/members/collection.js +13 -0
- package/dist/members/collection.js.map +1 -1
- package/dist/members/index.js +53 -29
- package/dist/members/index.js.map +1 -1
- package/dist/members/request.js +3 -3
- package/dist/members/request.js.map +1 -1
- package/dist/members/util.js +25 -8
- package/dist/members/util.js.map +1 -1
- package/dist/metrics/constants.js +2 -1
- package/dist/metrics/constants.js.map +1 -1
- package/dist/multistream/mediaRequestManager.js +1 -1
- package/dist/multistream/mediaRequestManager.js.map +1 -1
- package/dist/multistream/remoteMedia.js +34 -5
- package/dist/multistream/remoteMedia.js.map +1 -1
- package/dist/multistream/remoteMediaGroup.js +42 -2
- package/dist/multistream/remoteMediaGroup.js.map +1 -1
- package/dist/multistream/sendSlotManager.js +32 -2
- package/dist/multistream/sendSlotManager.js.map +1 -1
- package/dist/reachability/index.js +3 -3
- package/dist/reachability/index.js.map +1 -1
- package/dist/types/constants.d.ts +24 -0
- package/dist/types/locus-info/index.d.ts +54 -10
- package/dist/types/media/properties.d.ts +21 -0
- package/dist/types/meeting/brbState.d.ts +0 -1
- package/dist/types/meeting/in-meeting-actions.d.ts +8 -0
- package/dist/types/meeting/index.d.ts +51 -20
- package/dist/types/meeting/request.d.ts +18 -1
- package/dist/types/meeting/request.type.d.ts +74 -0
- package/dist/types/meeting/type.d.ts +9 -0
- package/dist/types/meeting/util.d.ts +13 -3
- package/dist/types/meeting-info/meeting-info-v2.d.ts +6 -3
- package/dist/types/meetings/index.d.ts +3 -1
- package/dist/types/member/types.d.ts +1 -0
- package/dist/types/members/collection.d.ts +6 -0
- package/dist/types/members/index.d.ts +22 -9
- package/dist/types/members/request.d.ts +1 -1
- package/dist/types/members/util.d.ts +13 -6
- package/dist/types/metrics/constants.d.ts +1 -0
- package/dist/types/multistream/remoteMedia.d.ts +20 -1
- package/dist/types/multistream/remoteMediaGroup.d.ts +11 -0
- package/dist/types/multistream/sendSlotManager.d.ts +16 -0
- package/dist/webinar/index.js +1 -1
- package/package.json +23 -24
- package/src/constants.ts +25 -2
- package/src/locus-info/index.ts +133 -96
- package/src/locus-info/parser.ts +5 -1
- package/src/media/properties.ts +43 -0
- package/src/meeting/brbState.ts +9 -7
- package/src/meeting/in-meeting-actions.ts +17 -0
- package/src/meeting/index.ts +273 -42
- package/src/meeting/muteState.ts +2 -6
- package/src/meeting/request.ts +39 -0
- package/src/meeting/request.type.ts +64 -0
- package/src/meeting/type.ts +9 -0
- package/src/meeting/util.ts +114 -22
- package/src/meeting-info/meeting-info-v2.ts +24 -5
- package/src/meetings/index.ts +12 -5
- package/src/member/index.ts +1 -0
- package/src/member/types.ts +1 -0
- package/src/members/collection.ts +11 -0
- package/src/members/index.ts +51 -15
- package/src/members/request.ts +2 -2
- package/src/members/util.ts +34 -6
- package/src/metrics/constants.ts +1 -0
- package/src/multistream/mediaRequestManager.ts +7 -7
- package/src/multistream/remoteMedia.ts +34 -4
- package/src/multistream/remoteMediaGroup.ts +37 -2
- package/src/multistream/sendSlotManager.ts +34 -2
- package/src/reachability/index.ts +3 -3
- package/test/unit/spec/locus-info/index.js +229 -98
- package/test/unit/spec/locus-info/parser.js +3 -2
- package/test/unit/spec/media/properties.ts +137 -0
- package/test/unit/spec/meeting/brbState.ts +9 -9
- package/test/unit/spec/meeting/in-meeting-actions.ts +8 -0
- package/test/unit/spec/meeting/index.js +1022 -93
- package/test/unit/spec/meeting/muteState.js +32 -6
- package/test/unit/spec/meeting/request.js +92 -0
- package/test/unit/spec/meeting/utils.js +167 -17
- package/test/unit/spec/meeting-info/meetinginfov2.js +8 -3
- package/test/unit/spec/meetings/index.js +12 -1
- package/test/unit/spec/members/collection.js +120 -0
- package/test/unit/spec/members/index.js +140 -12
- package/test/unit/spec/members/request.js +57 -2
- package/test/unit/spec/members/utils.js +139 -17
- package/test/unit/spec/multistream/mediaRequestManager.ts +19 -6
- package/test/unit/spec/multistream/remoteMedia.ts +66 -2
- package/test/unit/spec/multistream/sendSlotManager.ts +59 -0
- package/test/unit/spec/reachability/index.ts +158 -1
@@ -39,6 +39,7 @@ import {
|
|
39
39
|
ConnectionState,
|
40
40
|
MediaConnectionEventNames,
|
41
41
|
StatsAnalyzerEventNames,
|
42
|
+
StatsMonitorEventNames,
|
42
43
|
Errors,
|
43
44
|
ErrorType,
|
44
45
|
RemoteTrackType,
|
@@ -56,6 +57,7 @@ import * as MeetingRequestImport from '@webex/plugin-meetings/src/meeting/reques
|
|
56
57
|
import LocusInfo from '@webex/plugin-meetings/src/locus-info';
|
57
58
|
import MediaProperties from '@webex/plugin-meetings/src/media/properties';
|
58
59
|
import MeetingUtil from '@webex/plugin-meetings/src/meeting/util';
|
60
|
+
import MembersUtil from '@webex/plugin-meetings/src/members/util';
|
59
61
|
import MeetingsUtil from '@webex/plugin-meetings/src/meetings/util';
|
60
62
|
import Media from '@webex/plugin-meetings/src/media/index';
|
61
63
|
import ReconnectionManager from '@webex/plugin-meetings/src/reconnection-manager';
|
@@ -244,6 +246,7 @@ describe('plugin-meetings', () => {
|
|
244
246
|
});
|
245
247
|
|
246
248
|
webex.internal.newMetrics.callDiagnosticMetrics.clearErrorCache = sinon.stub();
|
249
|
+
webex.internal.newMetrics.callDiagnosticMetrics.clearEventLimitsForCorrelationId = sinon.stub();
|
247
250
|
webex.internal.support.submitLogs = sinon.stub().returns(Promise.resolve());
|
248
251
|
webex.internal.services = {get: sinon.stub().returns('locus-url')};
|
249
252
|
webex.credentials.getOrgId = sinon.stub().returns('fake-org-id');
|
@@ -368,6 +371,35 @@ describe('plugin-meetings', () => {
|
|
368
371
|
assert.instanceOf(meeting.simultaneousInterpretation, SimultaneousInterpretation);
|
369
372
|
assert.instanceOf(meeting.webinar, Webinar);
|
370
373
|
});
|
374
|
+
|
375
|
+
it('should call the callback with the meeting that has id already set', () => {
|
376
|
+
let meetingIdFromCallback;
|
377
|
+
// check that the meeting id is already set correctly at the time when the callback is called
|
378
|
+
const meetingCreationCallback = sinon.stub().callsFake((meeting) => {
|
379
|
+
meetingIdFromCallback = meeting.id;
|
380
|
+
});
|
381
|
+
|
382
|
+
meeting = new Meeting(
|
383
|
+
{
|
384
|
+
userId: uuid1,
|
385
|
+
resource: uuid2,
|
386
|
+
deviceUrl: uuid3,
|
387
|
+
locus: {url: url1},
|
388
|
+
destination: testDestination,
|
389
|
+
destinationType: DESTINATION_TYPE.MEETING_ID,
|
390
|
+
correlationId,
|
391
|
+
selfId: uuid1,
|
392
|
+
},
|
393
|
+
{
|
394
|
+
parent: webex,
|
395
|
+
},
|
396
|
+
meetingCreationCallback
|
397
|
+
);
|
398
|
+
assert.exists(meeting.id);
|
399
|
+
assert.calledOnceWithExactly(meetingCreationCallback, meeting);
|
400
|
+
assert.equal(meeting.id, meetingIdFromCallback);
|
401
|
+
});
|
402
|
+
|
371
403
|
it('creates MediaRequestManager instances', () => {
|
372
404
|
assert.instanceOf(meeting.mediaRequestManagers.audio, MediaRequestManager);
|
373
405
|
assert.instanceOf(meeting.mediaRequestManagers.video, MediaRequestManager);
|
@@ -454,6 +486,18 @@ describe('plugin-meetings', () => {
|
|
454
486
|
});
|
455
487
|
});
|
456
488
|
|
489
|
+
it('pstnCorrelationId getter/setter should work correctly', () => {
|
490
|
+
const testPstnCorrelationId = uuid.v4();
|
491
|
+
|
492
|
+
meeting.pstnCorrelationId = testPstnCorrelationId;
|
493
|
+
assert.equal(meeting.pstnCorrelationId, testPstnCorrelationId);
|
494
|
+
assert.equal(meeting.callStateForMetrics.pstnCorrelationId, testPstnCorrelationId);
|
495
|
+
|
496
|
+
meeting.pstnCorrelationId = undefined;
|
497
|
+
assert.equal(meeting.pstnCorrelationId, undefined);
|
498
|
+
assert.equal(meeting.callStateForMetrics.pstnCorrelationId, undefined);
|
499
|
+
});
|
500
|
+
|
457
501
|
describe('creates ReceiveSlot manager instance', () => {
|
458
502
|
let mockReceiveSlotManagerCtor;
|
459
503
|
let providedCreateSlotCallback;
|
@@ -581,7 +625,6 @@ describe('plugin-meetings', () => {
|
|
581
625
|
assert.isFalse(meeting.isLocusCall());
|
582
626
|
});
|
583
627
|
});
|
584
|
-
|
585
628
|
describe('#invite', () => {
|
586
629
|
it('should have #invite', () => {
|
587
630
|
assert.exists(meeting.invite);
|
@@ -592,8 +635,6 @@ describe('plugin-meetings', () => {
|
|
592
635
|
it('should proxy members #addMember and return a promise', async () => {
|
593
636
|
const invite = meeting.invite(uuid1, false);
|
594
637
|
|
595
|
-
assert.exists(invite.then);
|
596
|
-
await invite;
|
597
638
|
assert.calledOnce(meeting.members.addMember);
|
598
639
|
assert.calledWith(meeting.members.addMember, uuid1, false);
|
599
640
|
});
|
@@ -614,20 +655,20 @@ describe('plugin-meetings', () => {
|
|
614
655
|
assert.calledWith(meeting.members.cancelPhoneInvite, uuid1);
|
615
656
|
});
|
616
657
|
});
|
617
|
-
describe('#
|
618
|
-
it('should have #
|
619
|
-
assert.exists(meeting.
|
658
|
+
describe('#cancelInviteByMemberId', () => {
|
659
|
+
it('should have #cancelInviteByMemberId', () => {
|
660
|
+
assert.exists(meeting.cancelInviteByMemberId);
|
620
661
|
});
|
621
662
|
beforeEach(() => {
|
622
|
-
meeting.members.
|
663
|
+
meeting.members.cancelInviteByMemberId = sinon.stub().returns(Promise.resolve(test1));
|
623
664
|
});
|
624
|
-
it('should proxy members #
|
625
|
-
const cancel = meeting.
|
665
|
+
it('should proxy members #cancelInviteByMemberId and return a promise', async () => {
|
666
|
+
const cancel = meeting.cancelInviteByMemberId({memberId: uuid1});
|
626
667
|
|
627
668
|
assert.exists(cancel.then);
|
628
669
|
await cancel;
|
629
|
-
assert.calledOnce(meeting.members.
|
630
|
-
assert.calledWith(meeting.members.
|
670
|
+
assert.calledOnce(meeting.members.cancelInviteByMemberId);
|
671
|
+
assert.calledWith(meeting.members.cancelInviteByMemberId, {memberId: uuid1});
|
631
672
|
});
|
632
673
|
});
|
633
674
|
describe('#admit', () => {
|
@@ -1219,14 +1260,13 @@ describe('plugin-meetings', () => {
|
|
1219
1260
|
allowMediaInLobby: true,
|
1220
1261
|
},
|
1221
1262
|
});
|
1222
|
-
|
1263
|
+
|
1223
1264
|
assert.calledWithMatch(
|
1224
1265
|
meeting.addMediaInternal,
|
1225
1266
|
sinon.match.any,
|
1226
1267
|
sinon.match.any,
|
1227
1268
|
sinon.match.any,
|
1228
|
-
sinon.match.has('videoEnabled', false)
|
1229
|
-
.and(sinon.match.has('allowMediaInLobby', true))
|
1269
|
+
sinon.match.has('videoEnabled', false).and(sinon.match.has('allowMediaInLobby', true))
|
1230
1270
|
);
|
1231
1271
|
});
|
1232
1272
|
|
@@ -1235,23 +1275,21 @@ describe('plugin-meetings', () => {
|
|
1235
1275
|
joinOptions,
|
1236
1276
|
mediaOptions: {
|
1237
1277
|
audioEnabled: false,
|
1238
|
-
sendAudio: true,
|
1239
|
-
receiveAudio: false,
|
1278
|
+
sendAudio: true,
|
1279
|
+
receiveAudio: false,
|
1240
1280
|
allowMediaInLobby: true,
|
1241
1281
|
},
|
1242
1282
|
});
|
1243
|
-
|
1283
|
+
|
1244
1284
|
assert.calledWithMatch(
|
1245
1285
|
meeting.addMediaInternal,
|
1246
1286
|
sinon.match.any,
|
1247
1287
|
sinon.match.any,
|
1248
1288
|
sinon.match.any,
|
1249
|
-
sinon.match.has('audioEnabled', false)
|
1250
|
-
.and(sinon.match.has('allowMediaInLobby', true))
|
1289
|
+
sinon.match.has('audioEnabled', false).and(sinon.match.has('allowMediaInLobby', true))
|
1251
1290
|
);
|
1252
|
-
});
|
1291
|
+
});
|
1253
1292
|
|
1254
|
-
|
1255
1293
|
it('should use provided send/receive values when videoEnabled/audioEnabled are true or not set', async () => {
|
1256
1294
|
await meeting.joinWithMedia({
|
1257
1295
|
joinOptions,
|
@@ -1263,7 +1301,7 @@ describe('plugin-meetings', () => {
|
|
1263
1301
|
allowMediaInLobby: true,
|
1264
1302
|
},
|
1265
1303
|
});
|
1266
|
-
|
1304
|
+
|
1267
1305
|
assert.calledWith(
|
1268
1306
|
meeting.addMediaInternal,
|
1269
1307
|
sinon.match.any,
|
@@ -1301,12 +1339,11 @@ describe('plugin-meetings', () => {
|
|
1301
1339
|
sinon.restore();
|
1302
1340
|
});
|
1303
1341
|
it('should call voicea.onSpokenLanguageUpdate when joined', async () => {
|
1304
|
-
|
1305
1342
|
meeting.joinedWith = {state: 'JOINED'};
|
1306
1343
|
await meeting.locusInfo.emitScoped(
|
1307
1344
|
{function: 'test', file: 'test'},
|
1308
1345
|
LOCUSINFO.EVENTS.CONTROLS_MEETING_TRANSCRIPTION_SPOKEN_LANGUAGE_UPDATED,
|
1309
|
-
{spokenLanguage: 'fr'}
|
1346
|
+
{spokenLanguage: 'fr'}
|
1310
1347
|
);
|
1311
1348
|
assert.calledWith(webex.internal.voicea.onSpokenLanguageUpdate, 'fr', meeting.id);
|
1312
1349
|
assert.equal(meeting.transcription.languageOptions.currentSpokenLanguage, 'fr');
|
@@ -1319,12 +1356,11 @@ describe('plugin-meetings', () => {
|
|
1319
1356
|
});
|
1320
1357
|
|
1321
1358
|
it('should also call voicea.onSpokenLanguageUpdate when not joined', async () => {
|
1322
|
-
|
1323
1359
|
meeting.joinedWith = {state: 'NOT_JOINED'};
|
1324
1360
|
await meeting.locusInfo.emitScoped(
|
1325
1361
|
{function: 'test', file: 'test'},
|
1326
1362
|
LOCUSINFO.EVENTS.CONTROLS_MEETING_TRANSCRIPTION_SPOKEN_LANGUAGE_UPDATED,
|
1327
|
-
{spokenLanguage: 'de'}
|
1363
|
+
{spokenLanguage: 'de'}
|
1328
1364
|
);
|
1329
1365
|
assert.calledWith(webex.internal.voicea.onSpokenLanguageUpdate, 'de');
|
1330
1366
|
assert.equal(meeting.transcription.languageOptions.currentSpokenLanguage, 'de');
|
@@ -1954,21 +1990,25 @@ describe('plugin-meetings', () => {
|
|
1954
1990
|
});
|
1955
1991
|
});
|
1956
1992
|
|
1957
|
-
it('should
|
1993
|
+
it('should handle join failure', async () => {
|
1958
1994
|
MeetingUtil.isPinOrGuest = sinon.stub().returns(false);
|
1995
|
+
webex.internal.newMetrics.submitClientEvent = sinon.stub();
|
1996
|
+
|
1959
1997
|
await meeting.join().catch(() => {
|
1960
|
-
assert.
|
1961
|
-
|
1962
|
-
|
1963
|
-
);
|
1964
|
-
assert.
|
1965
|
-
webex.internal.newMetrics.submitClientEvent
|
1998
|
+
assert.calledOnce(MeetingUtil.joinMeeting);
|
1999
|
+
|
2000
|
+
// Assert that client.locus.join.response error event is not sent from this function, it is now emitted from MeetingUtil.joinMeeting
|
2001
|
+
assert.calledOnce(webex.internal.newMetrics.submitClientEvent);
|
2002
|
+
assert.calledWithMatch(
|
2003
|
+
webex.internal.newMetrics.submitClientEvent,
|
1966
2004
|
{
|
1967
|
-
|
1968
|
-
|
1969
|
-
|
1970
|
-
|
1971
|
-
|
2005
|
+
name: 'client.call.initiated',
|
2006
|
+
payload: {
|
2007
|
+
trigger: 'user-interaction',
|
2008
|
+
isRoapCallEnabled: true,
|
2009
|
+
pstnAudioType: undefined
|
2010
|
+
},
|
2011
|
+
options: {meetingId: meeting.id},
|
1972
2012
|
}
|
1973
2013
|
);
|
1974
2014
|
});
|
@@ -2169,16 +2209,15 @@ describe('plugin-meetings', () => {
|
|
2169
2209
|
};
|
2170
2210
|
meeting.mediaProperties.setMediaDirection = sinon.stub().returns(true);
|
2171
2211
|
meeting.mediaProperties.waitForMediaConnectionConnected = sinon.stub().resolves();
|
2172
|
-
meeting.mediaProperties.getCurrentConnectionInfo = sinon
|
2173
|
-
|
2174
|
-
|
2175
|
-
|
2176
|
-
|
2177
|
-
|
2178
|
-
ipVersion: 'IPv6',
|
2179
|
-
});
|
2212
|
+
meeting.mediaProperties.getCurrentConnectionInfo = sinon.stub().resolves({
|
2213
|
+
connectionType: 'udp',
|
2214
|
+
selectedCandidatePairChanges: 2,
|
2215
|
+
numTransports: 1,
|
2216
|
+
ipVersion: 'IPv6',
|
2217
|
+
});
|
2180
2218
|
meeting.audio = muteStateStub;
|
2181
2219
|
meeting.video = muteStateStub;
|
2220
|
+
sinon.stub(MeetingUtil, 'getIpVersion').returns(IP_VERSION.ipv4_and_ipv6);
|
2182
2221
|
sinon.stub(Media, 'createMediaConnection').returns(fakeMediaConnection);
|
2183
2222
|
sinon.stub(meeting, 'setupMediaConnectionListeners');
|
2184
2223
|
sinon.stub(meeting, 'setMercuryListener');
|
@@ -2250,13 +2289,24 @@ describe('plugin-meetings', () => {
|
|
2250
2289
|
close: sinon.stub(),
|
2251
2290
|
forceRtcMetricsSend,
|
2252
2291
|
});
|
2253
|
-
|
2292
|
+
|
2293
|
+
const mockStatsMonitor = {removeAllListeners: sinon.stub()};
|
2294
|
+
const mockNetworkQualityMonitor = {removeAllListeners: sinon.stub()};
|
2295
|
+
|
2296
|
+
// set a statsAnalyzer and statsMonitor on the meeting so that we can check that they get reset to null
|
2254
2297
|
meeting.statsAnalyzer = {stopAnalyzer: sinon.stub().resolves()};
|
2298
|
+
meeting.statsMonitor = mockStatsMonitor;
|
2299
|
+
meeting.networkQualityMonitor = mockNetworkQualityMonitor;
|
2255
2300
|
const error = await assert.isRejected(meeting.addMedia());
|
2256
2301
|
|
2257
2302
|
assert.calledOnce(forceRtcMetricsSend);
|
2303
|
+
assert.calledOnce(mockStatsMonitor.removeAllListeners);
|
2304
|
+
assert.calledOnce(mockNetworkQualityMonitor.removeAllListeners);
|
2258
2305
|
|
2259
2306
|
assert.isNull(meeting.statsAnalyzer);
|
2307
|
+
assert.isNull(meeting.statsMonitor);
|
2308
|
+
assert.isNull(meeting.networkQualityMonitor);
|
2309
|
+
|
2260
2310
|
assert(webex.internal.newMetrics.submitInternalEvent.calledTwice);
|
2261
2311
|
assert.calledWith(webex.internal.newMetrics.submitInternalEvent.firstCall, {
|
2262
2312
|
name: 'internal.client.add-media.turn-discovery.start',
|
@@ -2300,6 +2350,7 @@ describe('plugin-meetings', () => {
|
|
2300
2350
|
selected_subnet: null,
|
2301
2351
|
numTransports: 1,
|
2302
2352
|
iceCandidatesCount: 0,
|
2353
|
+
ipver: 1,
|
2303
2354
|
}
|
2304
2355
|
);
|
2305
2356
|
});
|
@@ -2347,6 +2398,7 @@ describe('plugin-meetings', () => {
|
|
2347
2398
|
subnet_reachable: null,
|
2348
2399
|
selected_cluster: null,
|
2349
2400
|
selected_subnet: null,
|
2401
|
+
ipver: 1,
|
2350
2402
|
})
|
2351
2403
|
);
|
2352
2404
|
|
@@ -2366,12 +2418,23 @@ describe('plugin-meetings', () => {
|
|
2366
2418
|
|
2367
2419
|
meeting.waitForRemoteSDPAnswer = sinon.stub().rejects();
|
2368
2420
|
|
2369
|
-
|
2421
|
+
const mockStatsMonitor = {removeAllListeners: sinon.stub()};
|
2422
|
+
const mockNetworkQualityMonitor = {removeAllListeners: sinon.stub()};
|
2423
|
+
|
2424
|
+
// set a statsAnalyzer and statsMonitor on the meeting so that we can check that they get reset to null
|
2370
2425
|
meeting.statsAnalyzer = {stopAnalyzer: sinon.stub().resolves()};
|
2426
|
+
meeting.statsMonitor = mockStatsMonitor;
|
2427
|
+
meeting.networkQualityMonitor = mockNetworkQualityMonitor;
|
2371
2428
|
|
2372
2429
|
const error = await assert.isRejected(meeting.addMedia());
|
2373
2430
|
|
2374
2431
|
assert.isNull(meeting.statsAnalyzer);
|
2432
|
+
assert.isNull(meeting.statsMonitor);
|
2433
|
+
assert.isNull(meeting.networkQualityMonitor);
|
2434
|
+
|
2435
|
+
assert.calledOnce(mockStatsMonitor.removeAllListeners);
|
2436
|
+
assert.calledOnce(mockNetworkQualityMonitor.removeAllListeners);
|
2437
|
+
|
2375
2438
|
assert(webex.internal.newMetrics.submitInternalEvent.calledTwice);
|
2376
2439
|
assert.calledWith(webex.internal.newMetrics.submitInternalEvent.firstCall, {
|
2377
2440
|
name: 'internal.client.add-media.turn-discovery.start',
|
@@ -2415,6 +2478,7 @@ describe('plugin-meetings', () => {
|
|
2415
2478
|
subnet_reachable: null,
|
2416
2479
|
selected_cluster: null,
|
2417
2480
|
selected_subnet: null,
|
2481
|
+
ipver: 1,
|
2418
2482
|
}
|
2419
2483
|
);
|
2420
2484
|
});
|
@@ -2435,8 +2499,9 @@ describe('plugin-meetings', () => {
|
|
2435
2499
|
},
|
2436
2500
|
},
|
2437
2501
|
});
|
2438
|
-
// set a statsAnalyzer on the meeting so that we can check that
|
2502
|
+
// set a statsAnalyzer and statsMonitor on the meeting so that we can check that they get reset to null
|
2439
2503
|
meeting.statsAnalyzer = {stopAnalyzer: sinon.stub().resolves()};
|
2504
|
+
meeting.statsMonitor = {removeAllListeners: sinon.stub()};
|
2440
2505
|
const error = await assert.isRejected(meeting.addMedia());
|
2441
2506
|
|
2442
2507
|
assert(webex.internal.newMetrics.submitInternalEvent.calledTwice);
|
@@ -2475,10 +2540,12 @@ describe('plugin-meetings', () => {
|
|
2475
2540
|
subnet_reachable: null,
|
2476
2541
|
selected_cluster: null,
|
2477
2542
|
selected_subnet: null,
|
2543
|
+
ipver: 1,
|
2478
2544
|
})
|
2479
2545
|
);
|
2480
2546
|
|
2481
2547
|
assert.isNull(meeting.statsAnalyzer);
|
2548
|
+
assert.isNull(meeting.statsMonitor);
|
2482
2549
|
});
|
2483
2550
|
|
2484
2551
|
it('should include the peer connection properties correctly for transcoded', async () => {
|
@@ -2495,8 +2562,14 @@ describe('plugin-meetings', () => {
|
|
2495
2562
|
},
|
2496
2563
|
},
|
2497
2564
|
});
|
2498
|
-
|
2565
|
+
|
2566
|
+
const mockStatsMonitor = {removeAllListeners: sinon.stub()};
|
2567
|
+
const mockNetworkQualityMonitor = {removeAllListeners: sinon.stub()};
|
2568
|
+
|
2569
|
+
// set a statsAnalyzer and statsMonitor on the meeting so that we can check that they get reset to null
|
2499
2570
|
meeting.statsAnalyzer = {stopAnalyzer: sinon.stub().resolves()};
|
2571
|
+
meeting.statsMonitor = mockStatsMonitor;
|
2572
|
+
meeting.networkQualityMonitor = mockNetworkQualityMonitor;
|
2500
2573
|
const error = await assert.isRejected(meeting.addMedia());
|
2501
2574
|
|
2502
2575
|
assert(webex.internal.newMetrics.submitInternalEvent.calledTwice);
|
@@ -2535,10 +2608,15 @@ describe('plugin-meetings', () => {
|
|
2535
2608
|
subnet_reachable: null,
|
2536
2609
|
selected_cluster: null,
|
2537
2610
|
selected_subnet: null,
|
2611
|
+
ipver: 1,
|
2538
2612
|
})
|
2539
2613
|
);
|
2540
2614
|
|
2541
2615
|
assert.isNull(meeting.statsAnalyzer);
|
2616
|
+
assert.isNull(meeting.statsMonitor);
|
2617
|
+
assert.isNull(meeting.networkQualityMonitor);
|
2618
|
+
assert.calledOnce(mockStatsMonitor.removeAllListeners);
|
2619
|
+
assert.calledOnce(mockNetworkQualityMonitor.removeAllListeners);
|
2542
2620
|
});
|
2543
2621
|
|
2544
2622
|
it('should work the second time addMedia is called in case the first time fails', async () => {
|
@@ -3059,6 +3137,7 @@ describe('plugin-meetings', () => {
|
|
3059
3137
|
subnet_reachable: null,
|
3060
3138
|
selected_cluster: null,
|
3061
3139
|
selected_subnet: null,
|
3140
|
+
ipver: 1,
|
3062
3141
|
},
|
3063
3142
|
]);
|
3064
3143
|
|
@@ -3260,6 +3339,7 @@ describe('plugin-meetings', () => {
|
|
3260
3339
|
connectionType: 'udp',
|
3261
3340
|
selectedCandidatePairChanges: 2,
|
3262
3341
|
ipVersion: 'IPv6',
|
3342
|
+
ipver: 1,
|
3263
3343
|
numTransports: 1,
|
3264
3344
|
isMultistream: false,
|
3265
3345
|
retriedWithTurnServer: true,
|
@@ -3401,11 +3481,12 @@ describe('plugin-meetings', () => {
|
|
3401
3481
|
meeting.mediaConnections = [
|
3402
3482
|
{
|
3403
3483
|
mediaAgentCluster: 'some.cluster',
|
3404
|
-
}
|
3405
|
-
]
|
3484
|
+
},
|
3485
|
+
];
|
3406
3486
|
meeting.iceCandidatesCount = 3;
|
3407
3487
|
meeting.iceCandidateErrors.set('701_error', 3);
|
3408
3488
|
meeting.iceCandidateErrors.set('701_turn_host_lookup_received_error', 1);
|
3489
|
+
MeetingUtil.getIpVersion.returns(IP_VERSION.only_ipv6);
|
3409
3490
|
|
3410
3491
|
await meeting.addMedia({
|
3411
3492
|
mediaSettings: {},
|
@@ -3421,6 +3502,7 @@ describe('plugin-meetings', () => {
|
|
3421
3502
|
connectionType: 'udp',
|
3422
3503
|
selectedCandidatePairChanges: 2,
|
3423
3504
|
ipVersion: 'IPv6',
|
3505
|
+
ipver: 6,
|
3424
3506
|
numTransports: 1,
|
3425
3507
|
isMultistream: false,
|
3426
3508
|
retriedWithTurnServer: false,
|
@@ -3499,6 +3581,7 @@ describe('plugin-meetings', () => {
|
|
3499
3581
|
selected_cluster: null,
|
3500
3582
|
selected_subnet: null,
|
3501
3583
|
iceCandidatesCount: 0,
|
3584
|
+
ipver: 1,
|
3502
3585
|
}
|
3503
3586
|
);
|
3504
3587
|
|
@@ -3563,6 +3646,7 @@ describe('plugin-meetings', () => {
|
|
3563
3646
|
selected_cluster: null,
|
3564
3647
|
selected_subnet: null,
|
3565
3648
|
iceCandidatesCount: 0,
|
3649
|
+
ipver: 1,
|
3566
3650
|
}
|
3567
3651
|
);
|
3568
3652
|
|
@@ -3583,12 +3667,12 @@ describe('plugin-meetings', () => {
|
|
3583
3667
|
stopReachability: sinon.stub(),
|
3584
3668
|
isSubnetReachable: sinon.stub().returns(false),
|
3585
3669
|
};
|
3586
|
-
meeting.mediaServerIp =
|
3670
|
+
meeting.mediaServerIp = '1.2.3.4';
|
3587
3671
|
meeting.mediaConnections = [
|
3588
3672
|
{
|
3589
3673
|
mediaAgentCluster: 'some.cluster',
|
3590
|
-
}
|
3591
|
-
]
|
3674
|
+
},
|
3675
|
+
];
|
3592
3676
|
|
3593
3677
|
const forceRtcMetricsSend = sinon.stub().resolves();
|
3594
3678
|
const closeMediaConnectionStub = sinon.stub();
|
@@ -3609,6 +3693,7 @@ describe('plugin-meetings', () => {
|
|
3609
3693
|
locus_id: meeting.locusUrl.split('/').pop(),
|
3610
3694
|
connectionType: 'udp',
|
3611
3695
|
ipVersion: 'IPv6',
|
3696
|
+
ipver: 1,
|
3612
3697
|
selectedCandidatePairChanges: 2,
|
3613
3698
|
numTransports: 1,
|
3614
3699
|
isMultistream: false,
|
@@ -3638,12 +3723,12 @@ describe('plugin-meetings', () => {
|
|
3638
3723
|
stopReachability: sinon.stub(),
|
3639
3724
|
isSubnetReachable: sinon.stub().returns(true),
|
3640
3725
|
};
|
3641
|
-
meeting.mediaServerIp =
|
3726
|
+
meeting.mediaServerIp = '1.2.3.4';
|
3642
3727
|
meeting.mediaConnections = [
|
3643
3728
|
{
|
3644
3729
|
mediaAgentCluster: 'some.cluster',
|
3645
|
-
}
|
3646
|
-
]
|
3730
|
+
},
|
3731
|
+
];
|
3647
3732
|
|
3648
3733
|
const forceRtcMetricsSend = sinon.stub().resolves();
|
3649
3734
|
const closeMediaConnectionStub = sinon.stub();
|
@@ -3689,6 +3774,7 @@ describe('plugin-meetings', () => {
|
|
3689
3774
|
selected_cluster: 'some.cluster',
|
3690
3775
|
selected_subnet: '1.X.X.X',
|
3691
3776
|
iceCandidatesCount: 0,
|
3777
|
+
ipver: 1,
|
3692
3778
|
}
|
3693
3779
|
);
|
3694
3780
|
|
@@ -3994,13 +4080,14 @@ describe('plugin-meetings', () => {
|
|
3994
4080
|
});
|
3995
4081
|
});
|
3996
4082
|
|
3997
|
-
it('counts the number of members that are in the meeting for MEDIA_QUALITY event', async () => {
|
4083
|
+
it('counts the number of members that are in the meeting or lobby for MEDIA_QUALITY event', async () => {
|
3998
4084
|
let fakeMembersCollection = {
|
3999
4085
|
members: {
|
4000
|
-
member1: {isInMeeting: true},
|
4001
|
-
member2: {isInMeeting: true},
|
4002
|
-
member3: {isInMeeting: false},
|
4003
|
-
|
4086
|
+
member1: {isInMeeting: true, isInLobby: false},
|
4087
|
+
member2: {isInMeeting: false, isInLobby: true},
|
4088
|
+
member3: {isInMeeting: false, isInLobby: false},
|
4089
|
+
member4: {isInMeeting: true, isInLobby: false},
|
4090
|
+
}
|
4004
4091
|
};
|
4005
4092
|
sinon.stub(meeting, 'getMembers').returns({membersCollection: fakeMembersCollection});
|
4006
4093
|
const fakeData = {intervalMetadata: {}};
|
@@ -4018,11 +4105,12 @@ describe('plugin-meetings', () => {
|
|
4018
4105
|
},
|
4019
4106
|
payload: {
|
4020
4107
|
intervals: [
|
4021
|
-
sinon.match.has('intervalMetadata', sinon.match.has('meetingUserCount',
|
4108
|
+
sinon.match.has('intervalMetadata', sinon.match.has('meetingUserCount', 3)),
|
4022
4109
|
],
|
4023
4110
|
},
|
4024
4111
|
});
|
4025
|
-
|
4112
|
+
// Move member2 from lobby to neither in meeting nor lobby
|
4113
|
+
fakeMembersCollection.members.member2.isInLobby = false;
|
4026
4114
|
|
4027
4115
|
statsAnalyzerStub.emit(
|
4028
4116
|
{file: 'test', function: 'test'},
|
@@ -4037,7 +4125,7 @@ describe('plugin-meetings', () => {
|
|
4037
4125
|
},
|
4038
4126
|
payload: {
|
4039
4127
|
intervals: [
|
4040
|
-
sinon.match.has('intervalMetadata', sinon.match.has('meetingUserCount',
|
4128
|
+
sinon.match.has('intervalMetadata', sinon.match.has('meetingUserCount', 2)),
|
4041
4129
|
],
|
4042
4130
|
},
|
4043
4131
|
});
|
@@ -4064,6 +4152,132 @@ describe('plugin-meetings', () => {
|
|
4064
4152
|
});
|
4065
4153
|
});
|
4066
4154
|
|
4155
|
+
describe('handles StatsMonitor events', () => {
|
4156
|
+
let statsMonitorStub;
|
4157
|
+
let prevConfigValue;
|
4158
|
+
let listeners;
|
4159
|
+
|
4160
|
+
beforeEach(async () => {
|
4161
|
+
meeting.meetingState = 'ACTIVE';
|
4162
|
+
prevConfigValue = meeting.config.stats.enableStatsAnalyzer;
|
4163
|
+
|
4164
|
+
meeting.config.stats.enableStatsAnalyzer = true;
|
4165
|
+
|
4166
|
+
listeners = {};
|
4167
|
+
|
4168
|
+
statsMonitorStub = {
|
4169
|
+
on: sinon.stub().callsFake((event, callback) => {
|
4170
|
+
listeners[event] = callback;
|
4171
|
+
}),
|
4172
|
+
removeAllListeners: sinon.stub(),
|
4173
|
+
};
|
4174
|
+
|
4175
|
+
sinon.stub(meeting.mediaProperties, 'sendMediaIssueMetric');
|
4176
|
+
|
4177
|
+
// mock the StatsMonitor constructor
|
4178
|
+
sinon.stub(InternalMediaCoreModule, 'StatsMonitor').returns(statsMonitorStub);
|
4179
|
+
|
4180
|
+
await meeting.addMedia({
|
4181
|
+
mediaSettings: {},
|
4182
|
+
});
|
4183
|
+
});
|
4184
|
+
|
4185
|
+
afterEach(() => {
|
4186
|
+
meeting.config.stats.enableStatsAnalyzer = prevConfigValue;
|
4187
|
+
sinon.restore();
|
4188
|
+
});
|
4189
|
+
|
4190
|
+
describe('INBOUND_AUDIO_ISSUE event', () => {
|
4191
|
+
it('should not trigger event when no unmuted members exist', () => {
|
4192
|
+
const fakeEventData = {issueSubType: 'DECODE_RESULTS_IN_ZERO_AUDIO_LEVEL'};
|
4193
|
+
|
4194
|
+
// Setup members that are either self or muted
|
4195
|
+
const mutedMember = {
|
4196
|
+
isSelf: false,
|
4197
|
+
isPairedWithSelf: false,
|
4198
|
+
isAudioMuted: true,
|
4199
|
+
};
|
4200
|
+
const selfMember = {
|
4201
|
+
isSelf: true,
|
4202
|
+
isPairedWithSelf: false,
|
4203
|
+
isAudioMuted: false,
|
4204
|
+
};
|
4205
|
+
const pairedMember = {
|
4206
|
+
isSelf: false,
|
4207
|
+
isPairedWithSelf: true,
|
4208
|
+
isAudioMuted: false,
|
4209
|
+
};
|
4210
|
+
meeting.members.membersCollection.getAll = sinon.stub().returns({
|
4211
|
+
member1: mutedMember,
|
4212
|
+
member2: selfMember,
|
4213
|
+
member3: pairedMember,
|
4214
|
+
});
|
4215
|
+
|
4216
|
+
// Reset the stub to clear any previous calls
|
4217
|
+
TriggerProxy.trigger.resetHistory();
|
4218
|
+
|
4219
|
+
// Emit the event from statsMonitor
|
4220
|
+
listeners[StatsMonitorEventNames.INBOUND_AUDIO_ISSUE](fakeEventData);
|
4221
|
+
|
4222
|
+
assert.neverCalledWith(
|
4223
|
+
TriggerProxy.trigger,
|
4224
|
+
meeting,
|
4225
|
+
sinon.match.object,
|
4226
|
+
EVENT_TRIGGERS.MEDIA_INBOUND_AUDIO_ISSUE_DETECTED,
|
4227
|
+
fakeEventData
|
4228
|
+
);
|
4229
|
+
assert.notCalled(meeting.mediaProperties.sendMediaIssueMetric);
|
4230
|
+
});
|
4231
|
+
|
4232
|
+
it('should trigger event and metric when there are multiple members and at least one is unmuted', () => {
|
4233
|
+
const fakeEventData = {issueSubType: 'DECODE_RESULTS_IN_ZERO_AUDIO_LEVEL'};
|
4234
|
+
|
4235
|
+
// Setup mixed members - some muted, one unmuted
|
4236
|
+
const mutedMember = {
|
4237
|
+
isSelf: false,
|
4238
|
+
isPairedWithSelf: false,
|
4239
|
+
isAudioMuted: true,
|
4240
|
+
};
|
4241
|
+
const unmutedMember = {
|
4242
|
+
isSelf: false,
|
4243
|
+
isPairedWithSelf: false,
|
4244
|
+
isAudioMuted: false,
|
4245
|
+
};
|
4246
|
+
const selfMember = {
|
4247
|
+
isSelf: true,
|
4248
|
+
isPairedWithSelf: false,
|
4249
|
+
isAudioMuted: false,
|
4250
|
+
};
|
4251
|
+
meeting.members.membersCollection.getAll = sinon.stub().returns({
|
4252
|
+
member1: mutedMember,
|
4253
|
+
member2: unmutedMember,
|
4254
|
+
member3: selfMember,
|
4255
|
+
});
|
4256
|
+
|
4257
|
+
// Reset the stub to clear any previous calls
|
4258
|
+
TriggerProxy.trigger.resetHistory();
|
4259
|
+
|
4260
|
+
// Emit the event from statsMonitor
|
4261
|
+
listeners[StatsMonitorEventNames.INBOUND_AUDIO_ISSUE](fakeEventData);
|
4262
|
+
|
4263
|
+
assert.calledWith(
|
4264
|
+
TriggerProxy.trigger,
|
4265
|
+
meeting,
|
4266
|
+
sinon.match.object,
|
4267
|
+
EVENT_TRIGGERS.MEDIA_INBOUND_AUDIO_ISSUE_DETECTED,
|
4268
|
+
fakeEventData
|
4269
|
+
);
|
4270
|
+
|
4271
|
+
assert.calledOnceWithExactly(
|
4272
|
+
meeting.mediaProperties.sendMediaIssueMetric,
|
4273
|
+
'inbound_audio',
|
4274
|
+
fakeEventData.issueSubType,
|
4275
|
+
meeting.correlationId
|
4276
|
+
);
|
4277
|
+
});
|
4278
|
+
});
|
4279
|
+
});
|
4280
|
+
|
4067
4281
|
describe('bundlePolicy', () => {
|
4068
4282
|
const FAKE_TURN_URL = 'turns:webex.com:3478';
|
4069
4283
|
const FAKE_TURN_USER = 'some-turn-username';
|
@@ -4355,9 +4569,7 @@ describe('plugin-meetings', () => {
|
|
4355
4569
|
const error = new Error();
|
4356
4570
|
meeting.meetingRequest.setBrb = sinon.stub().rejects(error);
|
4357
4571
|
|
4358
|
-
await expect(
|
4359
|
-
meeting.beRightBack(true)
|
4360
|
-
).to.be.rejectedWith(error);
|
4572
|
+
await expect(meeting.beRightBack(true)).to.be.rejectedWith(error);
|
4361
4573
|
|
4362
4574
|
assert.isFalse(meeting.brbState.state.syncToServerInProgress);
|
4363
4575
|
});
|
@@ -5532,6 +5744,7 @@ describe('plugin-meetings', () => {
|
|
5532
5744
|
let multistreamEventListeners;
|
5533
5745
|
let transcodedEventListeners;
|
5534
5746
|
let mockStatsAnalyzerCtor;
|
5747
|
+
let statsMonitorStub;
|
5535
5748
|
|
5536
5749
|
const setupFakeRoapMediaConnection = (fakeRoapMediaConnection, eventListeners) => {
|
5537
5750
|
fakeRoapMediaConnection.on.callsFake((eventName, cb) => {
|
@@ -5563,6 +5776,14 @@ describe('plugin-meetings', () => {
|
|
5563
5776
|
return {on: sinon.stub(), stopAnalyzer: sinon.stub()};
|
5564
5777
|
});
|
5565
5778
|
|
5779
|
+
statsMonitorStub = {
|
5780
|
+
on: sinon.stub(),
|
5781
|
+
removeAllListeners: sinon.stub(),
|
5782
|
+
};
|
5783
|
+
|
5784
|
+
// mock the StatsMonitor constructor
|
5785
|
+
sinon.stub(InternalMediaCoreModule, 'StatsMonitor').returns(statsMonitorStub);
|
5786
|
+
|
5566
5787
|
webex.internal.newMetrics.callDiagnosticMetrics.getErrorPayloadForClientErrorCode =
|
5567
5788
|
sinon.stub();
|
5568
5789
|
|
@@ -5625,6 +5846,7 @@ describe('plugin-meetings', () => {
|
|
5625
5846
|
mockStatsAnalyzerCtor,
|
5626
5847
|
sinon.match({
|
5627
5848
|
isMultistream: true,
|
5849
|
+
statsMonitor: statsMonitorStub,
|
5628
5850
|
})
|
5629
5851
|
);
|
5630
5852
|
const initialStatsAnalyzer = mockStatsAnalyzerCtor.returnValues[0];
|
@@ -6507,25 +6729,36 @@ describe('plugin-meetings', () => {
|
|
6507
6729
|
const DIAL_IN_URL = meeting.dialInUrl;
|
6508
6730
|
|
6509
6731
|
assert.calledWith(meeting.meetingRequest.dialIn, {
|
6510
|
-
correlationId: meeting.
|
6732
|
+
correlationId: meeting.pstnCorrelationId,
|
6511
6733
|
dialInUrl: DIAL_IN_URL,
|
6512
6734
|
locusUrl: meeting.locusUrl,
|
6513
6735
|
clientUrl: meeting.deviceUrl,
|
6514
6736
|
});
|
6515
6737
|
assert.notCalled(meeting.meetingRequest.dialOut);
|
6516
6738
|
|
6739
|
+
// Verify pstnCorrelationId was set
|
6740
|
+
assert.exists(meeting.pstnCorrelationId);
|
6741
|
+
assert.notEqual(meeting.pstnCorrelationId, meeting.correlationId);
|
6742
|
+
const firstPstnCorrelationId = meeting.pstnCorrelationId
|
6743
|
+
|
6517
6744
|
meeting.meetingRequest.dialIn.resetHistory();
|
6518
6745
|
|
6519
6746
|
// try again. the dial in urls should match
|
6520
6747
|
await meeting.usePhoneAudio();
|
6521
6748
|
|
6522
6749
|
assert.calledWith(meeting.meetingRequest.dialIn, {
|
6523
|
-
correlationId: meeting.
|
6750
|
+
correlationId: meeting.pstnCorrelationId,
|
6524
6751
|
dialInUrl: DIAL_IN_URL,
|
6525
6752
|
locusUrl: meeting.locusUrl,
|
6526
6753
|
clientUrl: meeting.deviceUrl,
|
6527
6754
|
});
|
6528
6755
|
assert.notCalled(meeting.meetingRequest.dialOut);
|
6756
|
+
// A new PSTN correlationId should be generated for the second attempt
|
6757
|
+
assert.notEqual(
|
6758
|
+
meeting.pstnCorrelationId,
|
6759
|
+
firstPstnCorrelationId,
|
6760
|
+
'pstnCorrelationId should be regenerated on each dial-in attempt'
|
6761
|
+
);
|
6529
6762
|
});
|
6530
6763
|
|
6531
6764
|
it('given a phone number, triggers dial-out, delegating request to meetingRequest correctly', async () => {
|
@@ -6535,7 +6768,7 @@ describe('plugin-meetings', () => {
|
|
6535
6768
|
const DIAL_OUT_URL = meeting.dialOutUrl;
|
6536
6769
|
|
6537
6770
|
assert.calledWith(meeting.meetingRequest.dialOut, {
|
6538
|
-
correlationId: meeting.
|
6771
|
+
correlationId: meeting.pstnCorrelationId,
|
6539
6772
|
dialOutUrl: DIAL_OUT_URL,
|
6540
6773
|
locusUrl: meeting.locusUrl,
|
6541
6774
|
clientUrl: meeting.deviceUrl,
|
@@ -6543,49 +6776,126 @@ describe('plugin-meetings', () => {
|
|
6543
6776
|
});
|
6544
6777
|
assert.notCalled(meeting.meetingRequest.dialIn);
|
6545
6778
|
|
6779
|
+
// Verify pstnCorrelationId was set
|
6780
|
+
assert.exists(meeting.pstnCorrelationId);
|
6781
|
+
assert.notEqual(meeting.pstnCorrelationId, meeting.correlationId);
|
6782
|
+
const firstPstnCorrelationId = meeting.pstnCorrelationId;
|
6783
|
+
|
6546
6784
|
meeting.meetingRequest.dialOut.resetHistory();
|
6547
6785
|
|
6548
6786
|
// try again. the dial out urls should match
|
6549
6787
|
await meeting.usePhoneAudio(phoneNumber);
|
6550
6788
|
|
6551
6789
|
assert.calledWith(meeting.meetingRequest.dialOut, {
|
6552
|
-
correlationId: meeting.
|
6790
|
+
correlationId: meeting.pstnCorrelationId,
|
6553
6791
|
dialOutUrl: DIAL_OUT_URL,
|
6554
6792
|
locusUrl: meeting.locusUrl,
|
6555
6793
|
clientUrl: meeting.deviceUrl,
|
6556
6794
|
phoneNumber,
|
6557
6795
|
});
|
6558
6796
|
assert.notCalled(meeting.meetingRequest.dialIn);
|
6797
|
+
// A new PSTN correlationId should be generated for the second attempt
|
6798
|
+
assert.notEqual(
|
6799
|
+
meeting.pstnCorrelationId,
|
6800
|
+
firstPstnCorrelationId,
|
6801
|
+
'pstnCorrelationId should be regenerated on each dial-out attempt'
|
6802
|
+
);
|
6559
6803
|
});
|
6560
6804
|
|
6561
|
-
it('rejects if the request failed (dial in)', () => {
|
6562
|
-
const error = '
|
6805
|
+
it('rejects if the request failed (dial in)', async () => {
|
6806
|
+
const error = {error: {message: 'dial in failed'}, stack: 'error stack'};
|
6563
6807
|
|
6564
6808
|
meeting.meetingRequest.dialIn = sinon.stub().returns(Promise.reject(error));
|
6565
6809
|
|
6566
|
-
|
6567
|
-
.usePhoneAudio()
|
6568
|
-
|
6569
|
-
|
6570
|
-
|
6810
|
+
try {
|
6811
|
+
await meeting.usePhoneAudio();
|
6812
|
+
throw new Error('Promise resolved when it should have rejected');
|
6813
|
+
} catch (e) {
|
6814
|
+
assert.equal(e, error);
|
6571
6815
|
|
6572
|
-
|
6816
|
+
// Verify behavioral metric was sent with dial_in_correlation_id
|
6817
|
+
assert.calledWith(Metrics.sendBehavioralMetric, BEHAVIORAL_METRICS.ADD_DIAL_IN_FAILURE, {
|
6818
|
+
correlation_id: meeting.correlationId,
|
6819
|
+
dial_in_url: meeting.dialInUrl,
|
6820
|
+
dial_in_correlation_id: sinon.match.string,
|
6821
|
+
locus_id: meeting.locusUrl.split('/').pop(),
|
6822
|
+
client_url: meeting.deviceUrl,
|
6823
|
+
reason: error.error.message,
|
6824
|
+
stack: error.stack,
|
6573
6825
|
});
|
6826
|
+
|
6827
|
+
// Verify pstnCorrelationId was cleared after error
|
6828
|
+
assert.equal(meeting.pstnCorrelationId, undefined);
|
6829
|
+
}
|
6574
6830
|
});
|
6575
6831
|
|
6576
6832
|
it('rejects if the request failed (dial out)', async () => {
|
6577
|
-
const error = '
|
6833
|
+
const error = {error: {message: 'dial out failed'}, stack: 'error stack'};
|
6578
6834
|
|
6579
6835
|
meeting.meetingRequest.dialOut = sinon.stub().returns(Promise.reject(error));
|
6580
6836
|
|
6581
|
-
|
6582
|
-
.usePhoneAudio('+441234567890')
|
6583
|
-
|
6584
|
-
|
6585
|
-
|
6837
|
+
try {
|
6838
|
+
await meeting.usePhoneAudio('+441234567890');
|
6839
|
+
throw new Error('Promise resolved when it should have rejected');
|
6840
|
+
} catch (e) {
|
6841
|
+
assert.equal(e, error);
|
6586
6842
|
|
6587
|
-
|
6843
|
+
// Verify behavioral metric was sent with dial_out_correlation_id
|
6844
|
+
assert.calledWith(Metrics.sendBehavioralMetric, BEHAVIORAL_METRICS.ADD_DIAL_OUT_FAILURE, {
|
6845
|
+
correlation_id: meeting.correlationId,
|
6846
|
+
dial_out_url: meeting.dialOutUrl,
|
6847
|
+
dial_out_correlation_id: sinon.match.string,
|
6848
|
+
locus_id: meeting.locusUrl.split('/').pop(),
|
6849
|
+
client_url: meeting.deviceUrl,
|
6850
|
+
reason: error.error.message,
|
6851
|
+
stack: error.stack,
|
6588
6852
|
});
|
6853
|
+
|
6854
|
+
// Verify pstnCorrelationId was cleared after error
|
6855
|
+
assert.equal(meeting.pstnCorrelationId, undefined);
|
6856
|
+
}
|
6857
|
+
});
|
6858
|
+
});
|
6859
|
+
|
6860
|
+
describe('#disconnectPhoneAudio', () => {
|
6861
|
+
beforeEach(() => {
|
6862
|
+
// Mock the MeetingUtil.disconnectPhoneAudio method
|
6863
|
+
sinon.stub(MeetingUtil, 'disconnectPhoneAudio').resolves();
|
6864
|
+
meeting.dialInUrl = 'dialin:///test-dial-in-url';
|
6865
|
+
meeting.dialOutUrl = 'dialout:///test-dial-out-url';
|
6866
|
+
meeting.dialInDeviceStatus = 'JOINED';
|
6867
|
+
meeting.dialOutDeviceStatus = 'JOINED';
|
6868
|
+
});
|
6869
|
+
|
6870
|
+
afterEach(() => {
|
6871
|
+
MeetingUtil.disconnectPhoneAudio.restore();
|
6872
|
+
});
|
6873
|
+
|
6874
|
+
it('should disconnect phone audio and clear pstnCorrelationId', async () => {
|
6875
|
+
meeting.pstnCorrelationId = 'test-pstn-correlation-id';
|
6876
|
+
|
6877
|
+
await meeting.disconnectPhoneAudio();
|
6878
|
+
|
6879
|
+
// Verify that pstnCorrelationId is cleared
|
6880
|
+
assert.equal(meeting.pstnCorrelationId, undefined);
|
6881
|
+
|
6882
|
+
// Verify that MeetingUtil.disconnectPhoneAudio was called for both dial-in and dial-out
|
6883
|
+
assert.calledTwice(MeetingUtil.disconnectPhoneAudio);
|
6884
|
+
assert.calledWith(MeetingUtil.disconnectPhoneAudio, meeting, meeting.dialInUrl);
|
6885
|
+
assert.calledWith(MeetingUtil.disconnectPhoneAudio, meeting, meeting.dialOutUrl);
|
6886
|
+
});
|
6887
|
+
|
6888
|
+
it('should handle case when no PSTN connection is active', async () => {
|
6889
|
+
meeting.dialInDeviceStatus = 'IDLE';
|
6890
|
+
meeting.dialOutDeviceStatus = 'IDLE';
|
6891
|
+
meeting.pstnCorrelationId = 'test-pstn-correlation-id';
|
6892
|
+
|
6893
|
+
await meeting.disconnectPhoneAudio();
|
6894
|
+
|
6895
|
+
// Verify that pstnCorrelationId is still cleared even when no phone connection is active
|
6896
|
+
assert.equal(meeting.pstnCorrelationId, undefined);
|
6897
|
+
// And verify no disconnect was attempted
|
6898
|
+
assert.notCalled(MeetingUtil.disconnectPhoneAudio);
|
6589
6899
|
});
|
6590
6900
|
});
|
6591
6901
|
|
@@ -7339,6 +7649,8 @@ describe('plugin-meetings', () => {
|
|
7339
7649
|
'locus-id',
|
7340
7650
|
{extraParam1: 'value1', permissionToken: FAKE_PERMISSION_TOKEN},
|
7341
7651
|
{meetingId: meeting.id, sendCAevents: true},
|
7652
|
+
null,
|
7653
|
+
null,
|
7342
7654
|
null
|
7343
7655
|
);
|
7344
7656
|
assert.deepEqual(meeting.meetingInfo, {
|
@@ -7385,6 +7697,8 @@ describe('plugin-meetings', () => {
|
|
7385
7697
|
'locus-id',
|
7386
7698
|
{extraParam1: 'value1', permissionToken: FAKE_PERMISSION_TOKEN},
|
7387
7699
|
{meetingId: meeting.id, sendCAevents: true},
|
7700
|
+
null,
|
7701
|
+
null,
|
7388
7702
|
null
|
7389
7703
|
);
|
7390
7704
|
assert.deepEqual(meeting.meetingInfo, {
|
@@ -7440,6 +7754,8 @@ describe('plugin-meetings', () => {
|
|
7440
7754
|
permissionToken: FAKE_PERMISSION_TOKEN,
|
7441
7755
|
},
|
7442
7756
|
{meetingId: meeting.id, sendCAevents: true},
|
7757
|
+
null,
|
7758
|
+
null,
|
7443
7759
|
null
|
7444
7760
|
);
|
7445
7761
|
assert.deepEqual(meeting.meetingInfo, {
|
@@ -8016,11 +8332,13 @@ describe('plugin-meetings', () => {
|
|
8016
8332
|
meeting.isoLocalClientMeetingJoinTime = undefined;
|
8017
8333
|
assert.equal(meeting.isoLocalClientMeetingJoinTime, currentSystemTime);
|
8018
8334
|
});
|
8335
|
+
|
8019
8336
|
it('should fallback to system clock ISO string when given an invalid value', () => {
|
8020
8337
|
const currentSystemTime = new Date().toISOString();
|
8021
8338
|
meeting.isoLocalClientMeetingJoinTime = 'invalid-date';
|
8022
8339
|
assert.equal(meeting.isoLocalClientMeetingJoinTime, currentSystemTime);
|
8023
8340
|
});
|
8341
|
+
|
8024
8342
|
it('should set the isoLocalClientMeetingJoinTime correctly for a valid date string', () => {
|
8025
8343
|
const validDateString = 'Tue, 01 Apr 2025 13:00:36 GMT';
|
8026
8344
|
const expectedISOString = new Date(validDateString).toISOString();
|
@@ -8100,6 +8418,7 @@ describe('plugin-meetings', () => {
|
|
8100
8418
|
|
8101
8419
|
meeting.requestScreenShareFloor = sinon.stub().resolves({});
|
8102
8420
|
meeting.releaseScreenShareFloor = sinon.stub().resolves({});
|
8421
|
+
webex.internal.newMetrics.callDiagnosticLatencies.saveTimestamp = sinon.stub();
|
8103
8422
|
meeting.mediaProperties.mediaDirection = {
|
8104
8423
|
sendAudio: 'fake value', // using non-boolean here so that we can check that these values are untouched in tests
|
8105
8424
|
sendVideo: 'fake value',
|
@@ -8181,6 +8500,12 @@ describe('plugin-meetings', () => {
|
|
8181
8500
|
payload: {mediaType: 'share', shareInstanceId: meeting.localShareInstanceId},
|
8182
8501
|
options: {meetingId: meeting.id},
|
8183
8502
|
});
|
8503
|
+
|
8504
|
+
// ensure the share start timestamp is saved
|
8505
|
+
assert.calledWith(webex.internal.newMetrics.callDiagnosticLatencies.saveTimestamp, {
|
8506
|
+
key: 'internal.client.share.initiated',
|
8507
|
+
});
|
8508
|
+
|
8184
8509
|
assert.equal(meeting.mediaProperties.mediaDirection.sendShare, true);
|
8185
8510
|
|
8186
8511
|
assert.equal(meeting.shareCAEventSentStatus.transmitStart, false);
|
@@ -8199,6 +8524,11 @@ describe('plugin-meetings', () => {
|
|
8199
8524
|
options: {meetingId: meeting.id},
|
8200
8525
|
});
|
8201
8526
|
|
8527
|
+
// ensure the share start timestamp is saved
|
8528
|
+
assert.calledWith(webex.internal.newMetrics.callDiagnosticLatencies.saveTimestamp, {
|
8529
|
+
key: 'internal.client.share.initiated',
|
8530
|
+
});
|
8531
|
+
|
8202
8532
|
assert.calledWith(
|
8203
8533
|
meeting.sendSlotManager.getSlot(MediaType.AudioSlides).publishStream,
|
8204
8534
|
stream
|
@@ -8867,11 +9197,16 @@ describe('plugin-meetings', () => {
|
|
8867
9197
|
meeting.hasMediaConnectionConnectedAtLeastOnce = false;
|
8868
9198
|
meeting.setupMediaConnectionListeners();
|
8869
9199
|
|
9200
|
+
sinon.stub(MeetingUtil, 'getCaEventLabelsForIpVersion').returns(['fake labels']);
|
9201
|
+
|
8870
9202
|
simulateConnectionStateChange(ConnectionState.Connecting);
|
8871
9203
|
|
8872
9204
|
assert.calledOnce(webex.internal.newMetrics.submitClientEvent);
|
8873
9205
|
assert.calledWithMatch(webex.internal.newMetrics.submitClientEvent, {
|
8874
9206
|
name: 'client.ice.start',
|
9207
|
+
payload: {
|
9208
|
+
labels: ['fake labels'],
|
9209
|
+
},
|
8875
9210
|
options: {
|
8876
9211
|
meetingId: meeting.id,
|
8877
9212
|
},
|
@@ -9194,6 +9529,7 @@ describe('plugin-meetings', () => {
|
|
9194
9529
|
meeting.deferSDPAnswer = {
|
9195
9530
|
reject: sinon.stub(),
|
9196
9531
|
};
|
9532
|
+
|
9197
9533
|
const clearTimeoutSpy = sinon.spy(clock, 'clearTimeout');
|
9198
9534
|
|
9199
9535
|
const fakeError = new Errors.SdpAnswerHandlingError(fakeErrorMessage, {
|
@@ -10471,6 +10807,8 @@ describe('plugin-meetings', () => {
|
|
10471
10807
|
meeting.mediaProperties = {mediaDirection: {sendShare: true}};
|
10472
10808
|
meeting.meetingRequest.changeMeetingFloor = sinon.stub().returns(Promise.resolve());
|
10473
10809
|
(meeting.deviceUrl = 'deviceUrl.com'), (meeting.localShareInstanceId = '1234-5678');
|
10810
|
+
webex.internal.newMetrics.callDiagnosticLatencies.saveTimestamp = sinon.stub();
|
10811
|
+
webex.internal.newMetrics.callDiagnosticLatencies.getShareDuration = sinon.stub().returns(1000);
|
10474
10812
|
});
|
10475
10813
|
it('should call changeMeetingFloor()', async () => {
|
10476
10814
|
meeting.screenShareFloorState = 'GRANTED';
|
@@ -10488,6 +10826,22 @@ describe('plugin-meetings', () => {
|
|
10488
10826
|
assert.exists(share.then);
|
10489
10827
|
await share;
|
10490
10828
|
assert.calledOnce(meeting.meetingRequest.changeMeetingFloor);
|
10829
|
+
|
10830
|
+
// ensure the share stop timestamp is saved
|
10831
|
+
assert.calledWith(webex.internal.newMetrics.callDiagnosticLatencies.saveTimestamp, {
|
10832
|
+
key: 'internal.client.share.stopped',
|
10833
|
+
});
|
10834
|
+
|
10835
|
+
// ensure the CA share stopped metric is submitted with duration
|
10836
|
+
assert.calledWith(webex.internal.newMetrics.submitClientEvent, {
|
10837
|
+
name: 'client.share.stopped',
|
10838
|
+
payload: {
|
10839
|
+
mediaType: 'share',
|
10840
|
+
shareInstanceId: meeting.localShareInstanceId,
|
10841
|
+
shareDuration: 1000,
|
10842
|
+
},
|
10843
|
+
options: {meetingId: meeting.id},
|
10844
|
+
});
|
10491
10845
|
});
|
10492
10846
|
it('should not call changeMeetingFloor() if someone else already has the floor', async () => {
|
10493
10847
|
// change selfId so that it doesn't match the beneficiary id from meeting.locusInfo.mediaShares
|
@@ -11071,6 +11425,7 @@ describe('plugin-meetings', () => {
|
|
11071
11425
|
let canUserRenameOthersSpy;
|
11072
11426
|
let canShareWhiteBoardSpy;
|
11073
11427
|
let canMoveToLobbySpy;
|
11428
|
+
let isSpokenLanguageAutoDetectionEnabledSpy;
|
11074
11429
|
// Due to import tree issues, hasHints must be stubed within the scope of the `it`.
|
11075
11430
|
|
11076
11431
|
beforeEach(() => {
|
@@ -11102,6 +11457,8 @@ describe('plugin-meetings', () => {
|
|
11102
11457
|
canUserRenameOthersSpy = sinon.spy(MeetingUtil, 'canUserRenameOthers');
|
11103
11458
|
canShareWhiteBoardSpy = sinon.spy(MeetingUtil, 'canShareWhiteBoard');
|
11104
11459
|
canMoveToLobbySpy = sinon.spy(MeetingUtil, 'canMoveToLobby');
|
11460
|
+
isSpokenLanguageAutoDetectionEnabledSpy = sinon.spy(MeetingUtil, 'isSpokenLanguageAutoDetectionEnabled');
|
11461
|
+
|
11105
11462
|
});
|
11106
11463
|
|
11107
11464
|
afterEach(() => {
|
@@ -11654,6 +12011,7 @@ describe('plugin-meetings', () => {
|
|
11654
12011
|
assert.calledWith(canUserRenameOthersSpy, userDisplayHints);
|
11655
12012
|
assert.calledWith(canShareWhiteBoardSpy, userDisplayHints, selfUserPolicies);
|
11656
12013
|
assert.calledWith(canMoveToLobbySpy, userDisplayHints);
|
12014
|
+
assert.calledWith(isSpokenLanguageAutoDetectionEnabledSpy, userDisplayHints);
|
11657
12015
|
|
11658
12016
|
assert.calledWith(ControlsOptionsUtil.hasHints, {
|
11659
12017
|
requiredHints: [DISPLAY_HINTS.MUTE_ALL],
|
@@ -12060,6 +12418,7 @@ describe('plugin-meetings', () => {
|
|
12060
12418
|
meeting.locusInfo.self = {url: url1};
|
12061
12419
|
meeting.meetingRequest.changeMeetingFloor = sinon.stub().returns(Promise.resolve());
|
12062
12420
|
meeting.deviceUrl = 'deviceUrl.com';
|
12421
|
+
webex.internal.newMetrics.callDiagnosticLatencies.saveTimestamp = sinon.stub();
|
12063
12422
|
});
|
12064
12423
|
it('should have #startWhiteboardShare', () => {
|
12065
12424
|
assert.exists(meeting.startWhiteboardShare);
|
@@ -12087,6 +12446,11 @@ describe('plugin-meetings', () => {
|
|
12087
12446
|
payload: {mediaType: 'whiteboard'},
|
12088
12447
|
options: {meetingId: meeting.id},
|
12089
12448
|
});
|
12449
|
+
|
12450
|
+
// ensure the share start timestamp is saved
|
12451
|
+
assert.calledWith(webex.internal.newMetrics.callDiagnosticLatencies.saveTimestamp, {
|
12452
|
+
key: 'internal.client.share.initiated',
|
12453
|
+
});
|
12090
12454
|
});
|
12091
12455
|
});
|
12092
12456
|
describe('#stopWhiteboardShare', () => {
|
@@ -12098,6 +12462,9 @@ describe('plugin-meetings', () => {
|
|
12098
12462
|
meeting.locusInfo.self = {url: url1};
|
12099
12463
|
meeting.meetingRequest.changeMeetingFloor = sinon.stub().returns(Promise.resolve());
|
12100
12464
|
meeting.deviceUrl = 'deviceUrl.com';
|
12465
|
+
webex.internal.newMetrics.callDiagnosticLatencies.saveTimestamp = sinon.stub();
|
12466
|
+
webex.internal.newMetrics.callDiagnosticLatencies.getShareDuration = sinon.stub().returns(1000);
|
12467
|
+
webex.internal.newMetrics.submitClientEvent = sinon.stub();
|
12101
12468
|
});
|
12102
12469
|
it('should stop the whiteboard share', async () => {
|
12103
12470
|
const whiteboardShare = meeting.stopWhiteboardShare();
|
@@ -12112,6 +12479,21 @@ describe('plugin-meetings', () => {
|
|
12112
12479
|
uri: url1,
|
12113
12480
|
});
|
12114
12481
|
assert.calledOnce(meeting.meetingRequest.changeMeetingFloor);
|
12482
|
+
|
12483
|
+
// ensure the share stop timestamp is saved
|
12484
|
+
assert.calledWith(webex.internal.newMetrics.callDiagnosticLatencies.saveTimestamp, {
|
12485
|
+
key: 'internal.client.share.stopped',
|
12486
|
+
});
|
12487
|
+
|
12488
|
+
// ensure the CA share stopped metric is submitted with duration
|
12489
|
+
assert.calledWith(webex.internal.newMetrics.submitClientEvent, {
|
12490
|
+
name: 'client.share.stopped',
|
12491
|
+
payload: {
|
12492
|
+
mediaType: 'whiteboard',
|
12493
|
+
shareDuration: 1000,
|
12494
|
+
},
|
12495
|
+
options: {meetingId: meeting.id},
|
12496
|
+
});
|
12115
12497
|
});
|
12116
12498
|
});
|
12117
12499
|
});
|
@@ -12184,6 +12566,9 @@ describe('plugin-meetings', () => {
|
|
12184
12566
|
meeting.selfId = '9528d952-e4de-46cf-8157-fd4823b98377';
|
12185
12567
|
meeting.deviceUrl = 'my-web-url';
|
12186
12568
|
meeting.locusInfo.info = {isWebinar: false};
|
12569
|
+
webex.internal.newMetrics.callDiagnosticLatencies.saveTimestamp = sinon.stub();
|
12570
|
+
webex.internal.newMetrics.callDiagnosticLatencies.getShareDuration = sinon.stub().returns(1500);
|
12571
|
+
webex.internal.newMetrics.submitClientEvent = sinon.stub();
|
12187
12572
|
});
|
12188
12573
|
|
12189
12574
|
const USER_IDS = {
|
@@ -12410,12 +12795,12 @@ describe('plugin-meetings', () => {
|
|
12410
12795
|
activeSharingId.whiteboard = beneficiaryId;
|
12411
12796
|
|
12412
12797
|
eventTrigger.share.push(
|
12413
|
-
meeting.webinar.selfIsAttendee
|
12798
|
+
meeting.webinar.selfIsAttendee || meeting.guest
|
12414
12799
|
? {
|
12415
12800
|
eventName: EVENT_TRIGGERS.MEETING_STARTED_SHARING_REMOTE,
|
12416
12801
|
functionName: 'remoteShare',
|
12417
12802
|
eventPayload: {
|
12418
|
-
memberId: null,
|
12803
|
+
memberId: meeting.webinar.selfIsAttendee ? beneficiaryId : null,
|
12419
12804
|
url,
|
12420
12805
|
shareInstanceId,
|
12421
12806
|
annotationInfo: undefined,
|
@@ -12429,7 +12814,8 @@ describe('plugin-meetings', () => {
|
|
12429
12814
|
}
|
12430
12815
|
);
|
12431
12816
|
|
12432
|
-
shareStatus =
|
12817
|
+
shareStatus =
|
12818
|
+
meeting.webinar.selfIsAttendee || meeting.guest
|
12433
12819
|
? SHARE_STATUS.REMOTE_SHARE_ACTIVE
|
12434
12820
|
: SHARE_STATUS.WHITEBOARD_SHARE_ACTIVE;
|
12435
12821
|
}
|
@@ -12469,7 +12855,7 @@ describe('plugin-meetings', () => {
|
|
12469
12855
|
eventName: EVENT_TRIGGERS.MEETING_STARTED_SHARING_REMOTE,
|
12470
12856
|
functionName: 'remoteShare',
|
12471
12857
|
eventPayload: {
|
12472
|
-
memberId:
|
12858
|
+
memberId: beneficiaryId,
|
12473
12859
|
url,
|
12474
12860
|
shareInstanceId,
|
12475
12861
|
annotationInfo: undefined,
|
@@ -12647,6 +13033,36 @@ describe('plugin-meetings', () => {
|
|
12647
13033
|
});
|
12648
13034
|
});
|
12649
13035
|
|
13036
|
+
describe('Whiteboard Share - User is guest', () => {
|
13037
|
+
it('User receives a remote share instead of whiteboard share', () => {
|
13038
|
+
// Set the guest flag
|
13039
|
+
meeting.guest = true;
|
13040
|
+
|
13041
|
+
// Step 1: Start sharing whiteboard A
|
13042
|
+
const data1 = generateData(
|
13043
|
+
blankPayload, // Initial payload
|
13044
|
+
true, // isGranting: Granting share
|
13045
|
+
false, // isContent: Whiteboard (not content)
|
13046
|
+
USER_IDS.REMOTE_A, // Beneficiary ID: Remote user A
|
13047
|
+
RESOURCE_URLS.WHITEBOARD_A // Resource URL: Whiteboard A
|
13048
|
+
);
|
13049
|
+
|
13050
|
+
// Step 2: Stop sharing whiteboard A
|
13051
|
+
const data2 = generateData(
|
13052
|
+
data1.payload, // Updated payload from Step 1
|
13053
|
+
false, // isGranting: Stopping share
|
13054
|
+
false, // isContent: Whiteboard
|
13055
|
+
USER_IDS.REMOTE_A // Beneficiary ID: Remote user A
|
13056
|
+
);
|
13057
|
+
|
13058
|
+
// Validate the payload changes and status updates
|
13059
|
+
payloadTestHelper([data1]);
|
13060
|
+
|
13061
|
+
// Specific assertions for guest
|
13062
|
+
assert.equal(meeting.shareStatus, SHARE_STATUS.REMOTE_SHARE_ACTIVE);
|
13063
|
+
});
|
13064
|
+
});
|
13065
|
+
|
12650
13066
|
describe('Whiteboard A --> Whiteboard B', () => {
|
12651
13067
|
it('Scenario #1: you share both whiteboards', () => {
|
12652
13068
|
const data1 = generateData(
|
@@ -13298,7 +13714,54 @@ describe('plugin-meetings', () => {
|
|
13298
13714
|
payloadTestHelper([data1, data2, data3]);
|
13299
13715
|
});
|
13300
13716
|
});
|
13301
|
-
|
13717
|
+
|
13718
|
+
it('should send share stopped metric when whiteboard sharing stops', () => {
|
13719
|
+
// Start whiteboard sharing (this won't trigger metrics)
|
13720
|
+
const data1 = generateData(
|
13721
|
+
blankPayload,
|
13722
|
+
true, // isGranting: true
|
13723
|
+
false, // isContent: false (whiteboard)
|
13724
|
+
USER_IDS.ME,
|
13725
|
+
RESOURCE_URLS.WHITEBOARD_A
|
13726
|
+
);
|
13727
|
+
|
13728
|
+
// Stop whiteboard sharing (this should trigger metrics)
|
13729
|
+
const data2 = generateData(
|
13730
|
+
data1.payload,
|
13731
|
+
false, // isGranting: false (stopping share)
|
13732
|
+
false, // isContent: false (whiteboard)
|
13733
|
+
USER_IDS.ME
|
13734
|
+
);
|
13735
|
+
|
13736
|
+
// Trigger the events
|
13737
|
+
meeting.locusInfo.emit(
|
13738
|
+
{function: 'test', file: 'test'},
|
13739
|
+
EVENTS.LOCUS_INFO_UPDATE_MEDIA_SHARES,
|
13740
|
+
data1.payload
|
13741
|
+
);
|
13742
|
+
|
13743
|
+
meeting.locusInfo.emit(
|
13744
|
+
{function: 'test', file: 'test'},
|
13745
|
+
EVENTS.LOCUS_INFO_UPDATE_MEDIA_SHARES,
|
13746
|
+
data2.payload
|
13747
|
+
);
|
13748
|
+
|
13749
|
+
// Verify metrics were called when whiteboard sharing stopped
|
13750
|
+
assert.calledWith(webex.internal.newMetrics.callDiagnosticLatencies.saveTimestamp, {
|
13751
|
+
key: 'internal.client.share.stopped',
|
13752
|
+
});
|
13753
|
+
|
13754
|
+
assert.calledWith(webex.internal.newMetrics.submitClientEvent, {
|
13755
|
+
name: 'client.share.stopped',
|
13756
|
+
payload: {
|
13757
|
+
mediaType: 'whiteboard',
|
13758
|
+
shareDuration: 1500, // mocked return value
|
13759
|
+
},
|
13760
|
+
options: {
|
13761
|
+
meetingId: meeting.id,
|
13762
|
+
},
|
13763
|
+
});
|
13764
|
+
});
|
13302
13765
|
|
13303
13766
|
describe('handleShareVideoStreamMuteStateChange', () => {
|
13304
13767
|
it('should emit MEETING_SHARE_VIDEO_MUTE_STATE_CHANGE event with correct fields', () => {
|
@@ -13325,6 +13788,7 @@ describe('plugin-meetings', () => {
|
|
13325
13788
|
});
|
13326
13789
|
});
|
13327
13790
|
});
|
13791
|
+
});
|
13328
13792
|
|
13329
13793
|
describe('#startKeepAlive', () => {
|
13330
13794
|
let clock;
|
@@ -14091,4 +14555,469 @@ describe('plugin-meetings', () => {
|
|
14091
14555
|
assert.equal(result.failureReason, MEETING_INFO_FAILURE_REASON.WRONG_CAPTCHA);
|
14092
14556
|
});
|
14093
14557
|
});
|
14558
|
+
|
14559
|
+
describe('#setStage', () => {
|
14560
|
+
const check = async (options, expectedVideoLayout) => {
|
14561
|
+
const locusUrl = `https://locus-test.wbx2.com/locus/api/v1/loci/${uuidv4()}`;
|
14562
|
+
meeting.locusUrl = locusUrl;
|
14563
|
+
|
14564
|
+
const setStagePromise = meeting.setStage(options);
|
14565
|
+
|
14566
|
+
assert.exists(setStagePromise.then);
|
14567
|
+
await setStagePromise;
|
14568
|
+
|
14569
|
+
assert.calledOnceWithExactly(
|
14570
|
+
meeting.meetingRequest.synchronizeStage,
|
14571
|
+
locusUrl,
|
14572
|
+
expectedVideoLayout
|
14573
|
+
);
|
14574
|
+
};
|
14575
|
+
|
14576
|
+
beforeEach(() => {
|
14577
|
+
meeting.meetingRequest.synchronizeStage = sinon.stub().returns(Promise.resolve());
|
14578
|
+
});
|
14579
|
+
|
14580
|
+
it('sends the expected request when no options are provided', async () => {
|
14581
|
+
await check(undefined, {
|
14582
|
+
overrideDefault: true,
|
14583
|
+
lockAttendeeViewOnStageOnly: false,
|
14584
|
+
stageParameters: {
|
14585
|
+
activeSpeakerProportion: 0.5,
|
14586
|
+
showActiveSpeaker: {show: false, order: 0},
|
14587
|
+
stageManagerType: 0,
|
14588
|
+
},
|
14589
|
+
});
|
14590
|
+
});
|
14591
|
+
|
14592
|
+
it('sends the expected request when empty options are provided', async () => {
|
14593
|
+
await check(
|
14594
|
+
{},
|
14595
|
+
{
|
14596
|
+
overrideDefault: true,
|
14597
|
+
lockAttendeeViewOnStageOnly: false,
|
14598
|
+
stageParameters: {
|
14599
|
+
activeSpeakerProportion: 0.5,
|
14600
|
+
showActiveSpeaker: {show: false, order: 0},
|
14601
|
+
stageManagerType: 0,
|
14602
|
+
},
|
14603
|
+
}
|
14604
|
+
);
|
14605
|
+
});
|
14606
|
+
|
14607
|
+
[0.25, 0.5, 0.75].forEach((activeSpeakerProportion) => {
|
14608
|
+
it(`sends the expected request when only the active speaker proportion option is provided as ${activeSpeakerProportion}`, async () => {
|
14609
|
+
await check(
|
14610
|
+
{activeSpeakerProportion},
|
14611
|
+
{
|
14612
|
+
overrideDefault: true,
|
14613
|
+
lockAttendeeViewOnStageOnly: false,
|
14614
|
+
stageParameters: {
|
14615
|
+
activeSpeakerProportion,
|
14616
|
+
showActiveSpeaker: {show: false, order: 0},
|
14617
|
+
stageManagerType: 0,
|
14618
|
+
},
|
14619
|
+
}
|
14620
|
+
);
|
14621
|
+
});
|
14622
|
+
});
|
14623
|
+
|
14624
|
+
it('sends the expected request when only the custom background option is provided', async () => {
|
14625
|
+
const customBackground = {
|
14626
|
+
url: `https://test.wbx2.com/background/${uuidv4()}.jpg`,
|
14627
|
+
};
|
14628
|
+
|
14629
|
+
await check(
|
14630
|
+
{customBackground},
|
14631
|
+
{
|
14632
|
+
overrideDefault: true,
|
14633
|
+
lockAttendeeViewOnStageOnly: false,
|
14634
|
+
stageParameters: {
|
14635
|
+
activeSpeakerProportion: 0.5,
|
14636
|
+
showActiveSpeaker: {show: false, order: 0},
|
14637
|
+
stageManagerType: 2,
|
14638
|
+
},
|
14639
|
+
customLayouts: {background: customBackground},
|
14640
|
+
}
|
14641
|
+
);
|
14642
|
+
});
|
14643
|
+
|
14644
|
+
it('sends the expected request when only the custom logo option is provided', async () => {
|
14645
|
+
const customLogo = {
|
14646
|
+
url: `https://test.wbx2.com/logo/${uuidv4()}.png`,
|
14647
|
+
position: 'LowerRight',
|
14648
|
+
};
|
14649
|
+
|
14650
|
+
await check(
|
14651
|
+
{customLogo},
|
14652
|
+
{
|
14653
|
+
overrideDefault: true,
|
14654
|
+
lockAttendeeViewOnStageOnly: false,
|
14655
|
+
stageParameters: {
|
14656
|
+
activeSpeakerProportion: 0.5,
|
14657
|
+
showActiveSpeaker: {show: false, order: 0},
|
14658
|
+
stageManagerType: 1,
|
14659
|
+
},
|
14660
|
+
customLayouts: {logo: customLogo},
|
14661
|
+
}
|
14662
|
+
);
|
14663
|
+
});
|
14664
|
+
|
14665
|
+
it('sends the expected request when only the custom name label option is provided', async () => {
|
14666
|
+
const customNameLabel = {
|
14667
|
+
accentColor: '#0A7806',
|
14668
|
+
background: {color: 'rgba(255, 255, 255, 1)'},
|
14669
|
+
border: {color: 'rgba(255, 255, 255, 1)'},
|
14670
|
+
content: {
|
14671
|
+
displayName: {color: 'rgba(0, 0, 0, 0.95)'},
|
14672
|
+
subtitle: {color: 'rgba(0, 0, 0, 0.6)'},
|
14673
|
+
},
|
14674
|
+
decoration: {color: 'rgba(10, 120, 6, 1)'},
|
14675
|
+
fadeOut: {delay: 15},
|
14676
|
+
type: 'Primary',
|
14677
|
+
};
|
14678
|
+
|
14679
|
+
await check(
|
14680
|
+
{customNameLabel},
|
14681
|
+
{
|
14682
|
+
overrideDefault: true,
|
14683
|
+
lockAttendeeViewOnStageOnly: false,
|
14684
|
+
stageParameters: {
|
14685
|
+
activeSpeakerProportion: 0.5,
|
14686
|
+
showActiveSpeaker: {show: false, order: 0},
|
14687
|
+
stageManagerType: 4,
|
14688
|
+
},
|
14689
|
+
nameLabelStyle: customNameLabel,
|
14690
|
+
}
|
14691
|
+
);
|
14692
|
+
});
|
14693
|
+
|
14694
|
+
it('sends the expected request when only the custom background and logo options are provided', async () => {
|
14695
|
+
const customBackground = {
|
14696
|
+
url: `https://test.wbx2.com/background/${uuidv4()}.jpg`,
|
14697
|
+
};
|
14698
|
+
const customLogo = {
|
14699
|
+
url: `https://test.wbx2.com/logo/${uuidv4()}.png`,
|
14700
|
+
position: 'UpperRight',
|
14701
|
+
};
|
14702
|
+
|
14703
|
+
await check(
|
14704
|
+
{customBackground, customLogo},
|
14705
|
+
{
|
14706
|
+
overrideDefault: true,
|
14707
|
+
lockAttendeeViewOnStageOnly: false,
|
14708
|
+
stageParameters: {
|
14709
|
+
activeSpeakerProportion: 0.5,
|
14710
|
+
showActiveSpeaker: {show: false, order: 0},
|
14711
|
+
stageManagerType: 3,
|
14712
|
+
},
|
14713
|
+
customLayouts: {background: customBackground, logo: customLogo},
|
14714
|
+
}
|
14715
|
+
);
|
14716
|
+
});
|
14717
|
+
|
14718
|
+
it('sends the expected request when only the custom background and name label options are provided', async () => {
|
14719
|
+
const customBackground = {
|
14720
|
+
url: `https://test.wbx2.com/background/${uuidv4()}.jpg`,
|
14721
|
+
};
|
14722
|
+
const customNameLabel = {
|
14723
|
+
accentColor: '#00A3FF',
|
14724
|
+
background: {color: 'rgba(0, 163, 255, 1)'},
|
14725
|
+
border: {color: 'rgba(0, 163, 255, 1)'},
|
14726
|
+
content: {
|
14727
|
+
displayName: {color: 'rgba(255, 255, 255, 0.95)'},
|
14728
|
+
subtitle: {color: 'rgba(255, 255, 255, 0.7)'},
|
14729
|
+
},
|
14730
|
+
decoration: {color: 'rgba(255, 255, 255, 0.95)'},
|
14731
|
+
fadeOut: {delay: 15},
|
14732
|
+
type: 'PrimaryInverted',
|
14733
|
+
};
|
14734
|
+
|
14735
|
+
await check(
|
14736
|
+
{customBackground, customNameLabel},
|
14737
|
+
{
|
14738
|
+
overrideDefault: true,
|
14739
|
+
lockAttendeeViewOnStageOnly: false,
|
14740
|
+
stageParameters: {
|
14741
|
+
activeSpeakerProportion: 0.5,
|
14742
|
+
showActiveSpeaker: {show: false, order: 0},
|
14743
|
+
stageManagerType: 6,
|
14744
|
+
},
|
14745
|
+
customLayouts: {background: customBackground},
|
14746
|
+
nameLabelStyle: customNameLabel,
|
14747
|
+
}
|
14748
|
+
);
|
14749
|
+
});
|
14750
|
+
|
14751
|
+
it('sends the expected request when only the custom logo and name label options are provided', async () => {
|
14752
|
+
const customLogo = {
|
14753
|
+
url: `https://test.wbx2.com/logo/${uuidv4()}.png`,
|
14754
|
+
position: 'UpperLeft',
|
14755
|
+
};
|
14756
|
+
const customNameLabel = {
|
14757
|
+
accentColor: '#942B2B',
|
14758
|
+
background: {color: 'rgba(255, 255, 255, 1)'},
|
14759
|
+
border: {color: 'rgba(148, 43, 43, 1)'},
|
14760
|
+
content: {
|
14761
|
+
displayName: {color: 'rgba(0, 0, 0, 0.95)'},
|
14762
|
+
subtitle: {color: 'rgba(0, 0, 0, 0.6)'},
|
14763
|
+
},
|
14764
|
+
decoration: {color: 'rgba(0, 0, 0, 0)'},
|
14765
|
+
fadeOut: {delay: 15},
|
14766
|
+
type: 'Secondary',
|
14767
|
+
};
|
14768
|
+
|
14769
|
+
await check(
|
14770
|
+
{customLogo, customNameLabel},
|
14771
|
+
{
|
14772
|
+
overrideDefault: true,
|
14773
|
+
lockAttendeeViewOnStageOnly: false,
|
14774
|
+
stageParameters: {
|
14775
|
+
activeSpeakerProportion: 0.5,
|
14776
|
+
showActiveSpeaker: {show: false, order: 0},
|
14777
|
+
stageManagerType: 5,
|
14778
|
+
},
|
14779
|
+
customLayouts: {logo: customLogo},
|
14780
|
+
nameLabelStyle: customNameLabel,
|
14781
|
+
}
|
14782
|
+
);
|
14783
|
+
});
|
14784
|
+
|
14785
|
+
it('sends the expected request when only the custom background, logo, name label options are provided', async () => {
|
14786
|
+
const customBackground = {
|
14787
|
+
url: `https://test.wbx2.com/background/${uuidv4()}.jpg`,
|
14788
|
+
};
|
14789
|
+
const customLogo = {
|
14790
|
+
url: `https://test.wbx2.com/logo/${uuidv4()}.png`,
|
14791
|
+
position: 'LowerLeft',
|
14792
|
+
};
|
14793
|
+
const customNameLabel = {
|
14794
|
+
accentColor: '#EBD960',
|
14795
|
+
background: {color: 'rgba(235, 217, 96, 0.55)'},
|
14796
|
+
border: {color: 'rgba(235, 217, 96, 0.55)'},
|
14797
|
+
content: {
|
14798
|
+
displayName: {color: 'rgba(255, 255, 255, 0.95)'},
|
14799
|
+
subtitle: {color: 'rgba(255, 255, 255, 0.7)'},
|
14800
|
+
},
|
14801
|
+
decoration: {color: 'rgba(0, 0, 0, 0)'},
|
14802
|
+
fadeOut: {delay: 15},
|
14803
|
+
type: 'SecondaryInverted',
|
14804
|
+
};
|
14805
|
+
|
14806
|
+
await check(
|
14807
|
+
{customBackground, customLogo, customNameLabel},
|
14808
|
+
{
|
14809
|
+
overrideDefault: true,
|
14810
|
+
lockAttendeeViewOnStageOnly: false,
|
14811
|
+
stageParameters: {
|
14812
|
+
activeSpeakerProportion: 0.5,
|
14813
|
+
showActiveSpeaker: {show: false, order: 0},
|
14814
|
+
stageManagerType: 7,
|
14815
|
+
},
|
14816
|
+
customLayouts: {background: customBackground, logo: customLogo},
|
14817
|
+
nameLabelStyle: customNameLabel,
|
14818
|
+
}
|
14819
|
+
);
|
14820
|
+
});
|
14821
|
+
|
14822
|
+
it('sends the expected request when only the important participants option is provided as empty', async () => {
|
14823
|
+
await check(
|
14824
|
+
{importantParticipants: []},
|
14825
|
+
{
|
14826
|
+
overrideDefault: true,
|
14827
|
+
lockAttendeeViewOnStageOnly: false,
|
14828
|
+
stageParameters: {
|
14829
|
+
activeSpeakerProportion: 0.5,
|
14830
|
+
showActiveSpeaker: {show: false, order: 0},
|
14831
|
+
stageManagerType: 0,
|
14832
|
+
},
|
14833
|
+
}
|
14834
|
+
);
|
14835
|
+
});
|
14836
|
+
|
14837
|
+
it('sends the expected request when only the important participants option is provided as populated', async () => {
|
14838
|
+
const importantParticipants = [
|
14839
|
+
{mainCsi: 11111111, participantId: uuidv4()},
|
14840
|
+
{mainCsi: 22222222, participantId: uuidv4()},
|
14841
|
+
{mainCsi: 33333333, participantId: uuidv4()},
|
14842
|
+
{mainCsi: 44444444, participantId: uuidv4()},
|
14843
|
+
{mainCsi: 55555555, participantId: uuidv4()},
|
14844
|
+
{mainCsi: 66666666, participantId: uuidv4()},
|
14845
|
+
{mainCsi: 77777777, participantId: uuidv4()},
|
14846
|
+
{mainCsi: 88888888, participantId: uuidv4()},
|
14847
|
+
];
|
14848
|
+
|
14849
|
+
await check(
|
14850
|
+
{importantParticipants},
|
14851
|
+
{
|
14852
|
+
overrideDefault: true,
|
14853
|
+
lockAttendeeViewOnStageOnly: false,
|
14854
|
+
stageParameters: {
|
14855
|
+
activeSpeakerProportion: 0.5,
|
14856
|
+
importantParticipants: [
|
14857
|
+
{...importantParticipants[0], order: 1},
|
14858
|
+
{...importantParticipants[1], order: 2},
|
14859
|
+
{...importantParticipants[2], order: 3},
|
14860
|
+
{...importantParticipants[3], order: 4},
|
14861
|
+
{...importantParticipants[4], order: 5},
|
14862
|
+
{...importantParticipants[5], order: 6},
|
14863
|
+
{...importantParticipants[6], order: 7},
|
14864
|
+
{...importantParticipants[7], order: 8},
|
14865
|
+
],
|
14866
|
+
showActiveSpeaker: {show: false, order: 0},
|
14867
|
+
stageManagerType: 0,
|
14868
|
+
},
|
14869
|
+
}
|
14870
|
+
);
|
14871
|
+
});
|
14872
|
+
|
14873
|
+
[false, true].forEach((lockAttendeeViewOnStage) => {
|
14874
|
+
it(`sends the expected request when only the lock attendee view on stage option is provided as ${lockAttendeeViewOnStage}`, async () => {
|
14875
|
+
await check(
|
14876
|
+
{lockAttendeeViewOnStage},
|
14877
|
+
{
|
14878
|
+
overrideDefault: true,
|
14879
|
+
lockAttendeeViewOnStageOnly: lockAttendeeViewOnStage,
|
14880
|
+
stageParameters: {
|
14881
|
+
activeSpeakerProportion: 0.5,
|
14882
|
+
showActiveSpeaker: {show: false, order: 0},
|
14883
|
+
stageManagerType: 0,
|
14884
|
+
},
|
14885
|
+
}
|
14886
|
+
);
|
14887
|
+
});
|
14888
|
+
});
|
14889
|
+
|
14890
|
+
[false, true].forEach((showActiveSpeaker) => {
|
14891
|
+
it(`sends the expected request when only the show active speaker option is provided as ${showActiveSpeaker}`, async () => {
|
14892
|
+
await check(
|
14893
|
+
{showActiveSpeaker},
|
14894
|
+
{
|
14895
|
+
overrideDefault: true,
|
14896
|
+
lockAttendeeViewOnStageOnly: false,
|
14897
|
+
stageParameters: {
|
14898
|
+
activeSpeakerProportion: 0.5,
|
14899
|
+
showActiveSpeaker: {show: showActiveSpeaker, order: 0},
|
14900
|
+
stageManagerType: 0,
|
14901
|
+
},
|
14902
|
+
}
|
14903
|
+
);
|
14904
|
+
});
|
14905
|
+
});
|
14906
|
+
|
14907
|
+
it('sends the expected request when all options are provided', async () => {
|
14908
|
+
const activeSpeakerProportion = 0.6;
|
14909
|
+
const customBackground = {
|
14910
|
+
url: `https://test.wbx2.com/background/${uuidv4()}.jpg`,
|
14911
|
+
};
|
14912
|
+
const customLogo = {
|
14913
|
+
url: `https://test.wbx2.com/logo/${uuidv4()}.png`,
|
14914
|
+
position: 'UpperMiddle',
|
14915
|
+
};
|
14916
|
+
const customNameLabel = {
|
14917
|
+
accentColor: '#0A7806',
|
14918
|
+
background: {color: 'rgba(255, 255, 255, 1)'},
|
14919
|
+
border: {color: 'rgba(255, 255, 255, 1)'},
|
14920
|
+
content: {
|
14921
|
+
displayName: {color: 'rgba(0, 0, 0, 0.95)'},
|
14922
|
+
subtitle: {color: 'rgba(0, 0, 0, 0.6)'},
|
14923
|
+
},
|
14924
|
+
decoration: {color: 'rgba(10, 120, 6, 1)'},
|
14925
|
+
fadeOut: {delay: 15},
|
14926
|
+
type: 'Primary',
|
14927
|
+
};
|
14928
|
+
const importantParticipants = [
|
14929
|
+
{mainCsi: 11111111, participantId: uuidv4()},
|
14930
|
+
{mainCsi: 22222222, participantId: uuidv4()},
|
14931
|
+
{mainCsi: 33333333, participantId: uuidv4()},
|
14932
|
+
{mainCsi: 44444444, participantId: uuidv4()},
|
14933
|
+
{mainCsi: 55555555, participantId: uuidv4()},
|
14934
|
+
{mainCsi: 66666666, participantId: uuidv4()},
|
14935
|
+
{mainCsi: 77777777, participantId: uuidv4()},
|
14936
|
+
{mainCsi: 88888888, participantId: uuidv4()},
|
14937
|
+
];
|
14938
|
+
const lockAttendeeViewOnStage = true;
|
14939
|
+
const showActiveSpeaker = true;
|
14940
|
+
|
14941
|
+
await check(
|
14942
|
+
{
|
14943
|
+
activeSpeakerProportion,
|
14944
|
+
customBackground,
|
14945
|
+
customLogo,
|
14946
|
+
customNameLabel,
|
14947
|
+
importantParticipants,
|
14948
|
+
lockAttendeeViewOnStage,
|
14949
|
+
showActiveSpeaker,
|
14950
|
+
},
|
14951
|
+
{
|
14952
|
+
overrideDefault: true,
|
14953
|
+
lockAttendeeViewOnStageOnly: lockAttendeeViewOnStage,
|
14954
|
+
stageParameters: {
|
14955
|
+
activeSpeakerProportion,
|
14956
|
+
importantParticipants: [
|
14957
|
+
{...importantParticipants[0], order: 1},
|
14958
|
+
{...importantParticipants[1], order: 2},
|
14959
|
+
{...importantParticipants[2], order: 3},
|
14960
|
+
{...importantParticipants[3], order: 4},
|
14961
|
+
{...importantParticipants[4], order: 5},
|
14962
|
+
{...importantParticipants[5], order: 6},
|
14963
|
+
{...importantParticipants[6], order: 7},
|
14964
|
+
{...importantParticipants[7], order: 8},
|
14965
|
+
],
|
14966
|
+
showActiveSpeaker: {show: showActiveSpeaker, order: 0},
|
14967
|
+
stageManagerType: 7,
|
14968
|
+
},
|
14969
|
+
customLayouts: {background: customBackground, logo: customLogo},
|
14970
|
+
nameLabelStyle: customNameLabel,
|
14971
|
+
}
|
14972
|
+
);
|
14973
|
+
});
|
14974
|
+
});
|
14975
|
+
|
14976
|
+
describe('#unsetStage', () => {
|
14977
|
+
beforeEach(() => {
|
14978
|
+
meeting.meetingRequest.synchronizeStage = sinon.stub().returns(Promise.resolve());
|
14979
|
+
});
|
14980
|
+
|
14981
|
+
it('sends the expected request', async () => {
|
14982
|
+
const locusUrl = `https://locus-test.wbx2.com/locus/api/v1/loci/${uuidv4()}`;
|
14983
|
+
meeting.locusUrl = locusUrl;
|
14984
|
+
|
14985
|
+
const unsetStagePromise = meeting.unsetStage();
|
14986
|
+
|
14987
|
+
assert.exists(unsetStagePromise.then);
|
14988
|
+
await unsetStagePromise;
|
14989
|
+
|
14990
|
+
assert.calledOnceWithExactly(
|
14991
|
+
meeting.meetingRequest.synchronizeStage,
|
14992
|
+
locusUrl,
|
14993
|
+
{overrideDefault: false}
|
14994
|
+
);
|
14995
|
+
});
|
14996
|
+
});
|
14997
|
+
|
14998
|
+
describe('#notifyHost', () => {
|
14999
|
+
beforeEach(() => {
|
15000
|
+
meeting.meetingRequest.notifyHost = sinon.stub().returns(Promise.resolve());
|
15001
|
+
});
|
15002
|
+
|
15003
|
+
it('sends the expected request', async () => {
|
15004
|
+
meeting.meetingInfo.siteFullUrl = `convergedats.webex.com`;
|
15005
|
+
const meetingUuid = 'meeting-uuid';
|
15006
|
+
const displayName = ['Test', 'User'];
|
15007
|
+
meeting.locusId = 'locusId';
|
15008
|
+
|
15009
|
+
const notifyHostPromise = meeting.notifyHost(meetingUuid, displayName);
|
15010
|
+
|
15011
|
+
assert.exists(notifyHostPromise.then);
|
15012
|
+
await notifyHostPromise;
|
15013
|
+
|
15014
|
+
assert.calledOnceWithExactly(
|
15015
|
+
meeting.meetingRequest.notifyHost,
|
15016
|
+
meeting.meetingInfo.siteFullUrl,
|
15017
|
+
meeting.locusId,
|
15018
|
+
meetingUuid,
|
15019
|
+
displayName,
|
15020
|
+
);
|
15021
|
+
});
|
15022
|
+
});
|
14094
15023
|
});
|