@webex/plugin-meetings 3.8.0-next.7 → 3.8.0-next.71
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/common/errors/webex-errors.js +12 -2
- package/dist/common/errors/webex-errors.js.map +1 -1
- package/dist/config.js +4 -1
- package/dist/config.js.map +1 -1
- package/dist/constants.js +17 -121
- package/dist/constants.js.map +1 -1
- package/dist/controls-options-manager/enums.js +2 -0
- package/dist/controls-options-manager/enums.js.map +1 -1
- package/dist/controls-options-manager/types.js.map +1 -1
- package/dist/controls-options-manager/util.js +52 -0
- package/dist/controls-options-manager/util.js.map +1 -1
- package/dist/interpretation/index.js +1 -1
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/locus-info/controlsUtils.js +28 -10
- package/dist/locus-info/controlsUtils.js.map +1 -1
- package/dist/locus-info/index.js +32 -12
- package/dist/locus-info/index.js.map +1 -1
- package/dist/locus-info/selfUtils.js +432 -418
- package/dist/locus-info/selfUtils.js.map +1 -1
- package/dist/media/index.js +14 -16
- package/dist/media/index.js.map +1 -1
- package/dist/media/properties.js +94 -6
- package/dist/media/properties.js.map +1 -1
- package/dist/meeting/brbState.js +6 -0
- package/dist/meeting/brbState.js.map +1 -1
- package/dist/meeting/in-meeting-actions.js +17 -1
- package/dist/meeting/in-meeting-actions.js.map +1 -1
- package/dist/meeting/index.js +541 -302
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/locusMediaRequest.js +0 -17
- package/dist/meeting/locusMediaRequest.js.map +1 -1
- package/dist/meeting/muteState.js +0 -2
- package/dist/meeting/muteState.js.map +1 -1
- package/dist/meeting/request.js +30 -0
- package/dist/meeting/request.js.map +1 -1
- package/dist/meeting/request.type.js.map +1 -1
- package/dist/meeting/util.js +13 -2
- package/dist/meeting/util.js.map +1 -1
- package/dist/meeting-info/meeting-info-v2.js +359 -60
- package/dist/meeting-info/meeting-info-v2.js.map +1 -1
- package/dist/meetings/index.js +114 -1
- package/dist/meetings/index.js.map +1 -1
- package/dist/meetings/util.js +14 -0
- package/dist/meetings/util.js.map +1 -1
- package/dist/member/index.js +10 -0
- package/dist/member/index.js.map +1 -1
- package/dist/member/util.js +330 -353
- package/dist/member/util.js.map +1 -1
- package/dist/members/index.js +23 -0
- package/dist/members/index.js.map +1 -1
- package/dist/members/request.js +21 -0
- package/dist/members/request.js.map +1 -1
- package/dist/members/util.js +15 -0
- package/dist/members/util.js.map +1 -1
- package/dist/metrics/constants.js +9 -0
- package/dist/metrics/constants.js.map +1 -1
- package/dist/reachability/clusterReachability.js +63 -27
- package/dist/reachability/clusterReachability.js.map +1 -1
- package/dist/reachability/index.js +112 -47
- package/dist/reachability/index.js.map +1 -1
- package/dist/reachability/reachability.types.js +14 -0
- package/dist/reachability/reachability.types.js.map +1 -1
- package/dist/reachability/request.js +19 -3
- package/dist/reachability/request.js.map +1 -1
- package/dist/reconnection-manager/index.js +2 -2
- package/dist/reconnection-manager/index.js.map +1 -1
- package/dist/recording-controller/util.js +5 -5
- package/dist/recording-controller/util.js.map +1 -1
- package/dist/roap/index.js.map +1 -1
- package/dist/roap/turnDiscovery.js +45 -27
- package/dist/roap/turnDiscovery.js.map +1 -1
- package/dist/roap/types.js +17 -0
- package/dist/roap/types.js.map +1 -0
- package/dist/types/common/errors/webex-errors.d.ts +7 -1
- package/dist/types/config.d.ts +2 -0
- package/dist/types/constants.d.ts +12 -85
- package/dist/types/controls-options-manager/enums.d.ts +3 -1
- package/dist/types/controls-options-manager/types.d.ts +7 -1
- package/dist/types/locus-info/index.d.ts +3 -3
- package/dist/types/locus-info/selfUtils.d.ts +216 -1
- package/dist/types/media/properties.d.ts +15 -0
- package/dist/types/meeting/in-meeting-actions.d.ts +16 -0
- package/dist/types/meeting/index.d.ts +32 -1
- package/dist/types/meeting/muteState.d.ts +0 -1
- package/dist/types/meeting/request.d.ts +12 -1
- package/dist/types/meeting/request.type.d.ts +6 -0
- package/dist/types/meeting/util.d.ts +3 -1
- package/dist/types/meeting-info/meeting-info-v2.d.ts +80 -0
- package/dist/types/meetings/index.d.ts +48 -0
- package/dist/types/member/index.d.ts +1 -0
- package/dist/types/member/util.d.ts +159 -1
- package/dist/types/members/index.d.ts +8 -0
- package/dist/types/members/request.d.ts +19 -0
- package/dist/types/members/util.d.ts +13 -0
- package/dist/types/metrics/constants.d.ts +9 -0
- package/dist/types/reachability/clusterReachability.d.ts +15 -7
- package/dist/types/reachability/index.d.ts +10 -1
- package/dist/types/reachability/reachability.types.d.ts +5 -0
- package/dist/types/roap/index.d.ts +3 -2
- package/dist/types/roap/turnDiscovery.d.ts +5 -17
- package/dist/types/roap/types.d.ts +16 -0
- package/dist/webinar/index.js +1 -1
- package/package.json +24 -23
- package/src/common/errors/webex-errors.ts +8 -1
- package/src/config.ts +2 -0
- package/src/constants.ts +19 -90
- package/src/controls-options-manager/enums.ts +2 -0
- package/src/controls-options-manager/types.ts +11 -1
- package/src/controls-options-manager/util.ts +62 -0
- package/src/locus-info/controlsUtils.ts +44 -14
- package/src/locus-info/index.ts +38 -12
- package/src/locus-info/selfUtils.ts +496 -442
- package/src/media/index.ts +20 -21
- package/src/media/properties.ts +96 -0
- package/src/meeting/brbState.ts +7 -0
- package/src/meeting/in-meeting-actions.ts +32 -0
- package/src/meeting/index.ts +346 -93
- package/src/meeting/locusMediaRequest.ts +0 -18
- package/src/meeting/muteState.ts +0 -2
- package/src/meeting/request.ts +36 -1
- package/src/meeting/request.type.ts +7 -0
- package/src/meeting/util.ts +11 -2
- package/src/meeting-info/meeting-info-v2.ts +247 -6
- package/src/meetings/index.ts +128 -1
- package/src/meetings/util.ts +18 -0
- package/src/member/index.ts +13 -2
- package/src/member/util.ts +351 -348
- package/src/members/index.ts +25 -0
- package/src/members/request.ts +26 -0
- package/src/members/util.ts +16 -0
- package/src/metrics/constants.ts +9 -0
- package/src/reachability/clusterReachability.ts +73 -26
- package/src/reachability/index.ts +70 -1
- package/src/reachability/reachability.types.ts +6 -0
- package/src/reachability/request.ts +7 -0
- package/src/reconnection-manager/index.ts +2 -2
- package/src/recording-controller/util.ts +17 -13
- package/src/roap/index.ts +3 -7
- package/src/roap/turnDiscovery.ts +34 -39
- package/src/roap/types.ts +23 -0
- package/test/unit/spec/controls-options-manager/util.js +120 -0
- package/test/unit/spec/locus-info/controlsUtils.js +103 -9
- package/test/unit/spec/locus-info/index.js +141 -73
- package/test/unit/spec/locus-info/selfUtils.js +98 -24
- package/test/unit/spec/media/index.ts +98 -16
- package/test/unit/spec/media/properties.ts +130 -0
- package/test/unit/spec/meeting/brbState.ts +19 -0
- package/test/unit/spec/meeting/in-meeting-actions.ts +19 -4
- package/test/unit/spec/meeting/index.js +524 -35
- package/test/unit/spec/meeting/locusMediaRequest.ts +0 -30
- package/test/unit/spec/meeting/muteState.js +0 -2
- package/test/unit/spec/meeting/request.js +32 -1
- package/test/unit/spec/meeting/utils.js +119 -18
- package/test/unit/spec/meeting-info/meetinginfov2.js +443 -114
- package/test/unit/spec/meetings/index.js +133 -2
- package/test/unit/spec/member/index.js +7 -0
- package/test/unit/spec/member/util.js +24 -0
- package/test/unit/spec/members/index.js +103 -26
- package/test/unit/spec/members/request.js +45 -22
- package/test/unit/spec/members/utils.js +33 -0
- package/test/unit/spec/reachability/clusterReachability.ts +88 -56
- package/test/unit/spec/reachability/index.ts +101 -0
- package/test/unit/spec/reachability/request.js +47 -2
- package/test/unit/spec/reconnection-manager/index.js +4 -4
- package/test/unit/spec/roap/turnDiscovery.ts +110 -28
@@ -4,7 +4,7 @@ import {cloneDeep, defaultsDeep} from 'lodash';
|
|
4
4
|
import SelfUtils from '@webex/plugin-meetings/src/locus-info/selfUtils';
|
5
5
|
|
6
6
|
import {self} from './selfConstant';
|
7
|
-
import {_IDLE_, _WAIT_} from '@webex/plugin-meetings/src/constants';
|
7
|
+
import {_IDLE_, _WAIT_, _OBSERVE_, _NONE_} from '@webex/plugin-meetings/src/constants';
|
8
8
|
|
9
9
|
describe('plugin-meetings', () => {
|
10
10
|
describe('selfUtils', () => {
|
@@ -269,13 +269,18 @@ describe('plugin-meetings', () => {
|
|
269
269
|
});
|
270
270
|
|
271
271
|
describe('getSelves', () => {
|
272
|
+
let parsedSelf;
|
273
|
+
|
274
|
+
beforeEach(() => {
|
275
|
+
parsedSelf = SelfUtils.parse(self);
|
276
|
+
});
|
272
277
|
describe('canNotViewTheParticipantListChanged', () => {
|
273
278
|
it('should return canNotViewTheParticipantListChanged = true when changed', () => {
|
274
279
|
const clonedSelf = cloneDeep(self);
|
275
280
|
|
276
281
|
clonedSelf.canNotViewTheParticipantList = true; // different
|
277
282
|
|
278
|
-
const {updates} = SelfUtils.getSelves(
|
283
|
+
const {updates} = SelfUtils.getSelves(parsedSelf, clonedSelf);
|
279
284
|
|
280
285
|
assert.equal(updates.canNotViewTheParticipantListChanged, true);
|
281
286
|
});
|
@@ -285,7 +290,7 @@ describe('plugin-meetings', () => {
|
|
285
290
|
|
286
291
|
clonedSelf.canNotViewTheParticipantList = false; // same
|
287
292
|
|
288
|
-
const {updates} = SelfUtils.getSelves(
|
293
|
+
const {updates} = SelfUtils.getSelves(parsedSelf, clonedSelf);
|
289
294
|
|
290
295
|
assert.equal(updates.canNotViewTheParticipantListChanged, false);
|
291
296
|
});
|
@@ -295,7 +300,7 @@ describe('plugin-meetings', () => {
|
|
295
300
|
it('should return localAudioUnmuteRequestedByServer = false when requestedToUnmute = false', () => {
|
296
301
|
const clonedSelf = cloneDeep(self);
|
297
302
|
|
298
|
-
const {updates} = SelfUtils.getSelves(
|
303
|
+
const {updates} = SelfUtils.getSelves(parsedSelf, clonedSelf);
|
299
304
|
|
300
305
|
assert.equal(updates.localAudioUnmuteRequestedByServer, false);
|
301
306
|
});
|
@@ -307,7 +312,7 @@ describe('plugin-meetings', () => {
|
|
307
312
|
clonedSelf.controls.audio.requestedToUnmute = true;
|
308
313
|
clonedSelf.controls.audio.lastModifiedRequestedToUnmute = '2023-06-16T18:25:04.369Z';
|
309
314
|
|
310
|
-
const {updates} = SelfUtils.getSelves(
|
315
|
+
const {updates} = SelfUtils.getSelves(parsedSelf, clonedSelf);
|
311
316
|
|
312
317
|
assert.equal(updates.localAudioUnmuteRequestedByServer, true);
|
313
318
|
});
|
@@ -321,7 +326,7 @@ describe('plugin-meetings', () => {
|
|
321
326
|
clonedSelf.controls.audio.requestedToUnmute = true;
|
322
327
|
clonedSelf.controls.audio.lastModifiedRequestedToUnmute = '2023-06-16T19:25:04.369Z';
|
323
328
|
|
324
|
-
const {updates} = SelfUtils.getSelves(
|
329
|
+
const {updates} = SelfUtils.getSelves(parsedSelf, clonedSelf);
|
325
330
|
|
326
331
|
assert.equal(updates.localAudioUnmuteRequestedByServer, true);
|
327
332
|
});
|
@@ -334,70 +339,139 @@ describe('plugin-meetings', () => {
|
|
334
339
|
clonedSelf.controls.audio.requestedToUnmute = true;
|
335
340
|
clonedSelf.controls.audio.lastModifiedRequestedToUnmute = '2023-06-16T18:25:04.369Z';
|
336
341
|
|
337
|
-
const {updates} = SelfUtils.getSelves(
|
342
|
+
const {updates} = SelfUtils.getSelves(parsedSelf, clonedSelf);
|
338
343
|
|
339
344
|
assert.equal(updates.localAudioUnmuteRequestedByServer, false);
|
340
345
|
});
|
341
346
|
});
|
342
347
|
|
343
|
-
describe('updates.
|
344
|
-
const testIsUserUnadmitted = (
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
348
|
+
describe('updates.hasUserEnteredLobby', () => {
|
349
|
+
const testIsUserUnadmitted = (
|
350
|
+
previousParsedSelves,
|
351
|
+
currentSelfDelta,
|
352
|
+
participants,
|
353
|
+
expected
|
354
|
+
) =>
|
355
|
+
function () {
|
356
|
+
const currentSelf = defaultsDeep(currentSelfDelta, self);
|
357
|
+
|
358
|
+
if (previousParsedSelves === undefined) {
|
359
|
+
parsedSelf.state = undefined;
|
360
|
+
} else {
|
361
|
+
parsedSelf = defaultsDeep(previousParsedSelves, parsedSelf);
|
362
|
+
}
|
363
|
+
const {updates} = SelfUtils.getSelves(
|
364
|
+
parsedSelf,
|
365
|
+
currentSelf,
|
366
|
+
currentSelf.devices[0].url,
|
367
|
+
participants
|
368
|
+
);
|
369
|
+
|
370
|
+
assert.equal(updates.hasUserEnteredLobby, expected);
|
371
|
+
};
|
353
372
|
|
354
373
|
it(
|
355
374
|
'should return true when previous is undefined and current is in lobby',
|
356
375
|
testIsUserUnadmitted(
|
357
376
|
undefined,
|
358
377
|
{devices: [{intent: {type: _WAIT_}}], state: _IDLE_},
|
378
|
+
[],
|
359
379
|
true
|
360
380
|
)
|
361
381
|
);
|
362
382
|
|
363
383
|
it(
|
364
384
|
'should return false when previous is undefined and user is not in meeting',
|
365
|
-
testIsUserUnadmitted(undefined, {devices: [], state: _IDLE_}, false)
|
385
|
+
testIsUserUnadmitted(undefined, {devices: [], state: _IDLE_}, [], false)
|
366
386
|
);
|
367
387
|
|
368
388
|
it(
|
369
389
|
'should return false when previous is undefined and current is in meeting',
|
370
|
-
testIsUserUnadmitted(undefined, {}, false)
|
390
|
+
testIsUserUnadmitted(undefined, {}, [], false)
|
391
|
+
);
|
392
|
+
|
393
|
+
it(
|
394
|
+
'should return true when previous is undefined and current is in lobby with paired device',
|
395
|
+
testIsUserUnadmitted(
|
396
|
+
undefined,
|
397
|
+
{
|
398
|
+
devices: [{intent: {type: _OBSERVE_, associatedWith: 'pairedDeviceUrl'}}],
|
399
|
+
state: _IDLE_,
|
400
|
+
},
|
401
|
+
[{url: 'pairedDeviceUrl', devices: [{intent: {type: _WAIT_}}]}],
|
402
|
+
true
|
403
|
+
)
|
404
|
+
);
|
405
|
+
|
406
|
+
it(
|
407
|
+
'should return false when previous is in lobby with paired device and current is the same',
|
408
|
+
testIsUserUnadmitted(
|
409
|
+
{
|
410
|
+
pairedWith: {intent: {type: _WAIT_}},
|
411
|
+
joinedWith: {intent: {type: _OBSERVE_}},
|
412
|
+
state: _IDLE_,
|
413
|
+
},
|
414
|
+
{
|
415
|
+
devices: [{intent: {type: _OBSERVE_, associatedWith: 'pairedDeviceUrl'}}],
|
416
|
+
state: _IDLE_,
|
417
|
+
},
|
418
|
+
[{url: 'pairedDeviceUrl', devices: [{intent: {type: _WAIT_}}]}],
|
419
|
+
false
|
420
|
+
)
|
371
421
|
);
|
372
422
|
|
373
423
|
it(
|
374
424
|
'should return false when previous is in lobby and current is in lobby',
|
375
425
|
testIsUserUnadmitted(
|
426
|
+
{joinedWith: {intent: {type: _WAIT_}}, state: _IDLE_},
|
376
427
|
{devices: [{intent: {type: _WAIT_}}], state: _IDLE_},
|
377
|
-
|
428
|
+
[],
|
429
|
+
false
|
430
|
+
)
|
431
|
+
);
|
432
|
+
|
433
|
+
it(
|
434
|
+
'should return false when previous is in lobby with paired device and current is in the meeting',
|
435
|
+
testIsUserUnadmitted(
|
436
|
+
{
|
437
|
+
pairedWith: {intent: {type: _WAIT_}},
|
438
|
+
joinedWith: {intent: {type: _OBSERVE_}},
|
439
|
+
state: _IDLE_,
|
440
|
+
},
|
441
|
+
{
|
442
|
+
devices: [{intent: {type: _OBSERVE_, associatedWith: 'pairedDeviceUrl'}}],
|
443
|
+
state: _IDLE_,
|
444
|
+
},
|
445
|
+
[{url: 'pairedDeviceUrl', devices: [{intent: {type: _NONE_}}]}],
|
378
446
|
false
|
379
447
|
)
|
380
448
|
);
|
381
449
|
|
382
450
|
it(
|
383
451
|
'should return false when previous is in lobby and current is in meeting',
|
384
|
-
testIsUserUnadmitted({
|
452
|
+
testIsUserUnadmitted({joinedWith: {intent: {type: _WAIT_}}, state: _IDLE_}, {}, [], false)
|
385
453
|
);
|
386
454
|
|
387
455
|
it(
|
388
456
|
'should return true when previous is in meeting and current is in lobby',
|
389
|
-
testIsUserUnadmitted({}, {devices: [{intent: {type: _WAIT_}}], state: _IDLE_}, true)
|
457
|
+
testIsUserUnadmitted({}, {devices: [{intent: {type: _WAIT_}}], state: _IDLE_}, [], true)
|
390
458
|
);
|
391
459
|
});
|
392
460
|
});
|
393
461
|
|
394
462
|
describe('isSharingBlocked', () => {
|
463
|
+
let parsedSelf;
|
464
|
+
|
465
|
+
beforeEach(() => {
|
466
|
+
parsedSelf = SelfUtils.parse(self);
|
467
|
+
});
|
468
|
+
|
395
469
|
it('should return isSharingBlockedChanged = true when changed', () => {
|
396
470
|
const clonedSelf = cloneDeep(self);
|
397
471
|
|
398
472
|
clonedSelf.isSharingBlocked = true; // different
|
399
473
|
|
400
|
-
const {updates} = SelfUtils.getSelves(
|
474
|
+
const {updates} = SelfUtils.getSelves(parsedSelf, clonedSelf);
|
401
475
|
|
402
476
|
assert.equal(updates.isSharingBlockedChanged, true);
|
403
477
|
});
|
@@ -407,7 +481,7 @@ describe('plugin-meetings', () => {
|
|
407
481
|
|
408
482
|
clonedSelf.isSharingBlocked = false; // same
|
409
483
|
|
410
|
-
const {updates} = SelfUtils.getSelves(
|
484
|
+
const {updates} = SelfUtils.getSelves(parsedSelf, clonedSelf);
|
411
485
|
|
412
486
|
assert.equal(updates.isSharingBlockedChanged, false);
|
413
487
|
});
|
@@ -4,6 +4,7 @@ import Media from '@webex/plugin-meetings/src/media/index';
|
|
4
4
|
import {assert} from '@webex/test-helper-chai';
|
5
5
|
import sinon from 'sinon';
|
6
6
|
import StaticConfig from '@webex/plugin-meetings/src/common/config';
|
7
|
+
import { BrowserInfo } from '@webex/web-capabilities';
|
7
8
|
|
8
9
|
describe('createMediaConnection', () => {
|
9
10
|
let clock;
|
@@ -79,7 +80,7 @@ describe('createMediaConnection', () => {
|
|
79
80
|
enableRtx: ENABLE_RTX,
|
80
81
|
enableExtmap: ENABLE_EXTMAP,
|
81
82
|
turnServerInfo: {
|
82
|
-
url: 'turns:turn-server-url:443?transport=tcp',
|
83
|
+
urls: ['turns:turn-server-url-1:443?transport=tcp', 'turns:turn-server-url-2:443?transport=tcp'],
|
83
84
|
username: 'turn username',
|
84
85
|
password: 'turn password',
|
85
86
|
},
|
@@ -91,12 +92,7 @@ describe('createMediaConnection', () => {
|
|
91
92
|
{
|
92
93
|
iceServers: [
|
93
94
|
{
|
94
|
-
urls: 'turn:turn-server-url:
|
95
|
-
username: 'turn username',
|
96
|
-
credential: 'turn password',
|
97
|
-
},
|
98
|
-
{
|
99
|
-
urls: 'turns:turn-server-url:443?transport=tcp',
|
95
|
+
urls: ['turns:turn-server-url-1:443?transport=tcp', 'turns:turn-server-url-2:443?transport=tcp'],
|
100
96
|
username: 'turn username',
|
101
97
|
credential: 'turn password',
|
102
98
|
},
|
@@ -159,11 +155,12 @@ describe('createMediaConnection', () => {
|
|
159
155
|
},
|
160
156
|
rtcMetrics,
|
161
157
|
turnServerInfo: {
|
162
|
-
url: 'turns:turn-server-url:443?transport=tcp',
|
158
|
+
urls: ['turns:turn-server-url-1:443?transport=tcp', 'turns:turn-server-url-2:443?transport=tcp'],
|
163
159
|
username: 'turn username',
|
164
160
|
password: 'turn password',
|
165
161
|
},
|
166
162
|
bundlePolicy: 'max-bundle',
|
163
|
+
disableAudioMainDtx: false,
|
167
164
|
});
|
168
165
|
assert.calledOnce(multistreamRoapMediaConnectionConstructorStub);
|
169
166
|
assert.calledWith(
|
@@ -171,17 +168,13 @@ describe('createMediaConnection', () => {
|
|
171
168
|
{
|
172
169
|
iceServers: [
|
173
170
|
{
|
174
|
-
urls: 'turn:turn-server-url:
|
175
|
-
username: 'turn username',
|
176
|
-
credential: 'turn password',
|
177
|
-
},
|
178
|
-
{
|
179
|
-
urls: 'turns:turn-server-url:443?transport=tcp',
|
171
|
+
urls: ['turns:turn-server-url-1:443?transport=tcp', 'turns:turn-server-url-2:443?transport=tcp'],
|
180
172
|
username: 'turn username',
|
181
173
|
credential: 'turn password',
|
182
174
|
},
|
183
175
|
],
|
184
176
|
bundlePolicy: 'max-bundle',
|
177
|
+
disableAudioMainDtx: false,
|
185
178
|
},
|
186
179
|
'meeting id'
|
187
180
|
);
|
@@ -205,14 +198,75 @@ describe('createMediaConnection', () => {
|
|
205
198
|
|
206
199
|
sendMetricsInQueueCallback();
|
207
200
|
assert.calledOnce(rtcMetrics.sendMetricsInQueue);
|
201
|
+
});
|
202
|
+
|
203
|
+
it('multistream non-firefox does not care about stopIceGatheringAfterFirstRelayCandidate', () => {
|
204
|
+
const multistreamRoapMediaConnectionConstructorStub = sinon
|
205
|
+
.stub(InternalMediaCoreModule, 'MultistreamRoapMediaConnection')
|
206
|
+
.returns(fakeRoapMediaConnection);
|
207
|
+
|
208
|
+
Media.createMediaConnection(true, 'some debug id', 'meeting id', {
|
209
|
+
stopIceGatheringAfterFirstRelayCandidate: true,
|
210
|
+
});
|
211
|
+
assert.calledOnce(multistreamRoapMediaConnectionConstructorStub);
|
212
|
+
assert.calledWith(
|
213
|
+
multistreamRoapMediaConnectionConstructorStub,
|
214
|
+
{
|
215
|
+
iceServers: []
|
216
|
+
},
|
217
|
+
'meeting id'
|
218
|
+
);
|
219
|
+
});
|
220
|
+
|
221
|
+
it('multistream firefox stops gathering after first relay if stopIceGatheringAfterFirstRelayCandidate is true', () => {
|
222
|
+
const multistreamRoapMediaConnectionConstructorStub = sinon
|
223
|
+
.stub(InternalMediaCoreModule, 'MultistreamRoapMediaConnection')
|
224
|
+
.returns(fakeRoapMediaConnection);
|
225
|
+
|
226
|
+
sinon.stub(BrowserInfo, 'isFirefox').returns(true);
|
227
|
+
|
228
|
+
Media.createMediaConnection(true, 'some debug id', 'meeting id', {
|
229
|
+
stopIceGatheringAfterFirstRelayCandidate: true,
|
230
|
+
});
|
231
|
+
assert.calledOnce(multistreamRoapMediaConnectionConstructorStub);
|
232
|
+
assert.calledWith(
|
233
|
+
multistreamRoapMediaConnectionConstructorStub,
|
234
|
+
{
|
235
|
+
iceServers: [],
|
236
|
+
doFullIce: true,
|
237
|
+
stopIceGatheringAfterFirstRelayCandidate: true,
|
238
|
+
},
|
239
|
+
'meeting id'
|
240
|
+
);
|
241
|
+
});
|
208
242
|
|
243
|
+
it('multistream firefox continues gathering if stopIceGatheringAfterFirstRelayCandidate is false', () => {
|
244
|
+
const multistreamRoapMediaConnectionConstructorStub = sinon
|
245
|
+
.stub(InternalMediaCoreModule, 'MultistreamRoapMediaConnection')
|
246
|
+
.returns(fakeRoapMediaConnection);
|
247
|
+
|
248
|
+
sinon.stub(BrowserInfo, 'isFirefox').returns(true);
|
249
|
+
|
250
|
+
Media.createMediaConnection(true, 'some debug id', 'meeting id', {
|
251
|
+
stopIceGatheringAfterFirstRelayCandidate: false,
|
252
|
+
});
|
253
|
+
assert.calledOnce(multistreamRoapMediaConnectionConstructorStub);
|
254
|
+
assert.calledWith(
|
255
|
+
multistreamRoapMediaConnectionConstructorStub,
|
256
|
+
{
|
257
|
+
iceServers: [],
|
258
|
+
doFullIce: true,
|
259
|
+
stopIceGatheringAfterFirstRelayCandidate: false,
|
260
|
+
},
|
261
|
+
'meeting id'
|
262
|
+
);
|
209
263
|
});
|
210
264
|
|
211
265
|
[
|
212
266
|
{testCase: 'turnServerInfo is undefined', turnServerInfo: undefined},
|
213
267
|
{
|
214
268
|
testCase: 'turnServerInfo.url is empty string',
|
215
|
-
turnServerInfo: {
|
269
|
+
turnServerInfo: {urls: [], username: 'turn username', password: 'turn password'},
|
216
270
|
},
|
217
271
|
].forEach(({testCase, turnServerInfo}) => {
|
218
272
|
it(`passes empty ICE servers array to MultistreamRoapMediaConnection if ${testCase} (multistream enabled)`, () => {
|
@@ -272,11 +326,39 @@ describe('createMediaConnection', () => {
|
|
272
326
|
);
|
273
327
|
});
|
274
328
|
|
329
|
+
it('does not pass disableAudioMainDtx to MultistreamRoapMediaConnection if disableAudioMainDtx is undefined', () => {
|
330
|
+
const multistreamRoapMediaConnectionConstructorStub = sinon
|
331
|
+
.stub(InternalMediaCoreModule, 'MultistreamRoapMediaConnection')
|
332
|
+
.returns(fakeRoapMediaConnection);
|
333
|
+
|
334
|
+
Media.createMediaConnection(true, 'debug string', 'meeting id', {
|
335
|
+
mediaProperties: {
|
336
|
+
mediaDirection: {
|
337
|
+
sendAudio: true,
|
338
|
+
sendVideo: true,
|
339
|
+
sendShare: false,
|
340
|
+
receiveAudio: true,
|
341
|
+
receiveVideo: true,
|
342
|
+
receiveShare: true,
|
343
|
+
},
|
344
|
+
},
|
345
|
+
disableAudioMainDtx: undefined,
|
346
|
+
});
|
347
|
+
assert.calledOnce(multistreamRoapMediaConnectionConstructorStub);
|
348
|
+
assert.calledWith(
|
349
|
+
multistreamRoapMediaConnectionConstructorStub,
|
350
|
+
{
|
351
|
+
iceServers: [],
|
352
|
+
},
|
353
|
+
'meeting id'
|
354
|
+
);
|
355
|
+
});
|
356
|
+
|
275
357
|
[
|
276
358
|
{testCase: 'turnServerInfo is undefined', turnServerInfo: undefined},
|
277
359
|
{
|
278
360
|
testCase: 'turnServerInfo.url is empty string',
|
279
|
-
turnServerInfo: {
|
361
|
+
turnServerInfo: {urls: [], username: 'turn username', password: 'turn password'},
|
280
362
|
},
|
281
363
|
].forEach(({testCase, turnServerInfo}) => {
|
282
364
|
it(`passes empty ICE servers array to RoapMediaConnection if ${testCase} (multistream disabled)`, () => {
|
@@ -2,6 +2,7 @@ import 'jsdom-global/register';
|
|
2
2
|
import {assert} from '@webex/test-helper-chai';
|
3
3
|
import sinon from 'sinon';
|
4
4
|
import {ConnectionState} from '@webex/internal-media-core';
|
5
|
+
import * as tsSdpModule from '@webex/ts-sdp';
|
5
6
|
import MediaProperties from '@webex/plugin-meetings/src/media/properties';
|
6
7
|
import {Defer} from '@webex/common';
|
7
8
|
import MediaConnectionAwaiter from '../../../../src/media/MediaConnectionAwaiter';
|
@@ -10,15 +11,21 @@ describe('MediaProperties', () => {
|
|
10
11
|
let mediaProperties;
|
11
12
|
let mockMC;
|
12
13
|
let clock;
|
14
|
+
let rtcPeerConnection;
|
13
15
|
|
14
16
|
beforeEach(() => {
|
15
17
|
clock = sinon.useFakeTimers();
|
16
18
|
|
19
|
+
rtcPeerConnection = {
|
20
|
+
localDescription: {sdp: ''},
|
21
|
+
};
|
22
|
+
|
17
23
|
mockMC = {
|
18
24
|
getStats: sinon.stub().resolves([]),
|
19
25
|
on: sinon.stub(),
|
20
26
|
off: sinon.stub(),
|
21
27
|
getConnectionState: sinon.stub().returns(ConnectionState.Connected),
|
28
|
+
multistreamConnection: {pc: {pc: rtcPeerConnection}},
|
22
29
|
};
|
23
30
|
|
24
31
|
mediaProperties = new MediaProperties();
|
@@ -81,6 +88,129 @@ describe('MediaProperties', () => {
|
|
81
88
|
assert.equal(numTransports, 0);
|
82
89
|
});
|
83
90
|
|
91
|
+
describe('ipVersion', () => {
|
92
|
+
it('returns ipVersion=undefined if getStats() returns no candidate pairs', async () => {
|
93
|
+
mockMC.getStats.resolves([{type: 'something', id: '1234'}]);
|
94
|
+
const info = await mediaProperties.getCurrentConnectionInfo();
|
95
|
+
assert.equal(info.ipVersion, undefined);
|
96
|
+
});
|
97
|
+
|
98
|
+
it('returns ipVersion=undefined if getStats() returns no selected candidate pair', async () => {
|
99
|
+
mockMC.getStats.resolves([{type: 'candidate-pair', id: '1234', selected: false}]);
|
100
|
+
const info = await mediaProperties.getCurrentConnectionInfo();
|
101
|
+
assert.equal(info.ipVersion, undefined);
|
102
|
+
});
|
103
|
+
|
104
|
+
it('returns ipVersion="IPv4" if transport has selectedCandidatePairId and local candidate has IPv4 address', async () => {
|
105
|
+
mockMC.getStats.resolves([
|
106
|
+
{type: 'transport', id: 't1', selectedCandidatePairId: 'cp1'},
|
107
|
+
{type: 'candidate-pair', id: 'cp1', localCandidateId: 'lc1'},
|
108
|
+
{type: 'local-candidate', id: 'lc1', address: '192.168.1.1'},
|
109
|
+
]);
|
110
|
+
const info = await mediaProperties.getCurrentConnectionInfo();
|
111
|
+
assert.equal(info.ipVersion, 'IPv4');
|
112
|
+
});
|
113
|
+
|
114
|
+
it('returns ipVersion="IPv6" if transport has selectedCandidatePairId and local candidate has IPv6 address', async () => {
|
115
|
+
mockMC.getStats.resolves([
|
116
|
+
{type: 'transport', id: 't1', selectedCandidatePairId: 'cp1'},
|
117
|
+
{type: 'candidate-pair', id: 'cp1', localCandidateId: 'lc1'},
|
118
|
+
{type: 'local-candidate', id: 'lc1', address: 'fd8f:12e6:5e53:784f:a0ba:f8d5:b906:1acc'},
|
119
|
+
]);
|
120
|
+
const info = await mediaProperties.getCurrentConnectionInfo();
|
121
|
+
assert.equal(info.ipVersion, 'IPv6');
|
122
|
+
});
|
123
|
+
|
124
|
+
it('returns ipVersion="IPv4" if transport has no selectedCandidatePairId but finds selected candidate pair and local candidate has IPv4 address', async () => {
|
125
|
+
mockMC.getStats.resolves([
|
126
|
+
{type: 'transport', id: 't1'},
|
127
|
+
{type: 'candidate-pair', id: 'cp2', localCandidateId: 'lc2', selected: true},
|
128
|
+
{type: 'local-candidate', id: 'lc2', address: '10.0.0.1'},
|
129
|
+
]);
|
130
|
+
const info = await mediaProperties.getCurrentConnectionInfo();
|
131
|
+
assert.equal(info.ipVersion, 'IPv4');
|
132
|
+
});
|
133
|
+
|
134
|
+
it('returns ipVersion="IPv6" if transport has no selectedCandidatePairId but finds selected candidate pair and local candidate has IPv6 address', async () => {
|
135
|
+
mockMC.getStats.resolves([
|
136
|
+
{type: 'transport', id: 't1'},
|
137
|
+
{type: 'candidate-pair', id: 'cp2', localCandidateId: 'lc2', selected: true},
|
138
|
+
{type: 'local-candidate', id: 'lc2', address: 'fe80::1ff:fe23:4567:890a'},
|
139
|
+
]);
|
140
|
+
const info = await mediaProperties.getCurrentConnectionInfo();
|
141
|
+
assert.equal(info.ipVersion, 'IPv6');
|
142
|
+
});
|
143
|
+
|
144
|
+
describe('local candidate without address', () => {
|
145
|
+
it('return="IPv4" if candidate from SDP with matching port number has IPv4 address', async () => {
|
146
|
+
sinon.stub(tsSdpModule, 'parse').returns({
|
147
|
+
avMedia: [
|
148
|
+
{
|
149
|
+
iceInfo: {
|
150
|
+
candidates: [
|
151
|
+
{
|
152
|
+
port: 1234,
|
153
|
+
connectionAddress: '192.168.0.1',
|
154
|
+
},
|
155
|
+
],
|
156
|
+
},
|
157
|
+
},
|
158
|
+
],
|
159
|
+
});
|
160
|
+
|
161
|
+
mockMC.getStats.resolves([
|
162
|
+
{type: 'transport', id: 't1'},
|
163
|
+
{type: 'candidate-pair', id: 'cp2', localCandidateId: 'lc2', selected: true},
|
164
|
+
{type: 'local-candidate', id: 'lc2', port: 1234},
|
165
|
+
]);
|
166
|
+
const info = await mediaProperties.getCurrentConnectionInfo();
|
167
|
+
assert.equal(info.ipVersion, 'IPv4');
|
168
|
+
|
169
|
+
assert.calledWith(tsSdpModule.parse, rtcPeerConnection.localDescription.sdp);
|
170
|
+
});
|
171
|
+
|
172
|
+
it('returns ipVersion="IPv6" if candidate from SDP with matching port number has IPv6 address', async () => {
|
173
|
+
sinon.stub(tsSdpModule, 'parse').returns({
|
174
|
+
avMedia: [
|
175
|
+
{
|
176
|
+
iceInfo: {
|
177
|
+
candidates: [
|
178
|
+
{
|
179
|
+
port: 5000,
|
180
|
+
connectionAddress: 'fe80::1ff:fe23:4567:890a',
|
181
|
+
},
|
182
|
+
],
|
183
|
+
},
|
184
|
+
},
|
185
|
+
],
|
186
|
+
});
|
187
|
+
|
188
|
+
mockMC.getStats.resolves([
|
189
|
+
{type: 'transport', id: 't1'},
|
190
|
+
{type: 'candidate-pair', id: 'cp2', localCandidateId: 'lc2', selected: true},
|
191
|
+
{type: 'local-candidate', id: 'lc2', port: 5000},
|
192
|
+
]);
|
193
|
+
const info = await mediaProperties.getCurrentConnectionInfo();
|
194
|
+
assert.equal(info.ipVersion, 'IPv6');
|
195
|
+
|
196
|
+
assert.calledWith(tsSdpModule.parse, rtcPeerConnection.localDescription.sdp);
|
197
|
+
});
|
198
|
+
|
199
|
+
it('returns ipVersion=undefined if parsing of the SDP fails', async () => {
|
200
|
+
sinon.stub(tsSdpModule, 'parse').throws(new Error('fake error'));
|
201
|
+
|
202
|
+
mockMC.getStats.resolves([
|
203
|
+
{type: 'candidate-pair', id: 'cp2', localCandidateId: 'lc2', selected: true},
|
204
|
+
{type: 'local-candidate', id: 'lc2', port: 5000},
|
205
|
+
]);
|
206
|
+
const info = await mediaProperties.getCurrentConnectionInfo();
|
207
|
+
assert.equal(info.ipVersion, undefined);
|
208
|
+
|
209
|
+
assert.calledWith(tsSdpModule.parse, rtcPeerConnection.localDescription.sdp);
|
210
|
+
});
|
211
|
+
});
|
212
|
+
});
|
213
|
+
|
84
214
|
describe('selectedCandidatePairChanges and numTransports', () => {
|
85
215
|
it('returns correct values when getStats() returns no transport stats at all', async () => {
|
86
216
|
mockMC.getStats.resolves([{type: 'something', id: '1234'}]);
|
@@ -110,5 +110,24 @@ describe('plugin-meetings', () => {
|
|
110
110
|
|
111
111
|
assert.isTrue(brbState.state.server.enabled);
|
112
112
|
});
|
113
|
+
|
114
|
+
it('invokes handleServerBrbUpdate with correct client state after syncing with server', async () => {
|
115
|
+
const sendLocalBrbStateToServerStub = sinon
|
116
|
+
.stub(brbState, 'sendLocalBrbStateToServer')
|
117
|
+
.resolves();
|
118
|
+
|
119
|
+
const handleServerBrbUpdateSpy = sinon.spy(brbState, 'handleServerBrbUpdate');
|
120
|
+
|
121
|
+
await brbState.enable(true, meeting.sendSlotManager);
|
122
|
+
|
123
|
+
assert.isTrue(sendLocalBrbStateToServerStub.calledOnce);
|
124
|
+
|
125
|
+
assert.isTrue(handleServerBrbUpdateSpy.calledOnceWith(brbState.state.client.enabled));
|
126
|
+
|
127
|
+
assert.isFalse(brbState.state.syncToServerInProgress);
|
128
|
+
|
129
|
+
sendLocalBrbStateToServerStub.restore();
|
130
|
+
handleServerBrbUpdateSpy.restore();
|
131
|
+
});
|
113
132
|
});
|
114
133
|
});
|
@@ -76,6 +76,8 @@ describe('plugin-meetings', () => {
|
|
76
76
|
canShareDesktop: null,
|
77
77
|
canShareContent: null,
|
78
78
|
canTransferFile: null,
|
79
|
+
canRealtimeCloseCaption: null,
|
80
|
+
canRealtimeCloseCaptionManual: null,
|
79
81
|
canChat: null,
|
80
82
|
canDoVideo: null,
|
81
83
|
canAnnotate: null,
|
@@ -90,10 +92,16 @@ describe('plugin-meetings', () => {
|
|
90
92
|
canShowStageView: null,
|
91
93
|
canEnableStageView: null,
|
92
94
|
canDisableStageView: null,
|
93
|
-
isPracticeSessionOn
|
94
|
-
isPracticeSessionOff
|
95
|
+
isPracticeSessionOn: null,
|
96
|
+
isPracticeSessionOff: null,
|
95
97
|
canStartPracticeSession: null,
|
96
98
|
canStopPracticeSession: null,
|
99
|
+
requiresPostMeetingDataConsentPrompt: null,
|
100
|
+
canEnableAnnotation: null,
|
101
|
+
canDisableAnnotation: null,
|
102
|
+
canEnableRemoteDesktopControl: null,
|
103
|
+
canDisableRemoteDesktopControl: null,
|
104
|
+
canMoveToLobby: null,
|
97
105
|
|
98
106
|
...expected,
|
99
107
|
};
|
@@ -176,6 +184,8 @@ describe('plugin-meetings', () => {
|
|
176
184
|
'canShareDesktop',
|
177
185
|
'canShareContent',
|
178
186
|
'canTransferFile',
|
187
|
+
'canRealtimeCloseCaption',
|
188
|
+
'canRealtimeCloseCaptionManual',
|
179
189
|
'canChat',
|
180
190
|
'canDoVideo',
|
181
191
|
'canAnnotate',
|
@@ -194,8 +204,13 @@ describe('plugin-meetings', () => {
|
|
194
204
|
'isPracticeSessionOff',
|
195
205
|
'canStartPracticeSession',
|
196
206
|
'canStopPracticeSession',
|
197
|
-
|
198
|
-
|
207
|
+
'requiresPostMeetingDataConsentPrompt',
|
208
|
+
'canEnableAnnotation',
|
209
|
+
'canDisableAnnotation',
|
210
|
+
'canEnableRemoteDesktopControl',
|
211
|
+
'canDisableRemoteDesktopControl',
|
212
|
+
'canMoveToLobby',
|
213
|
+
].forEach((key) => {
|
199
214
|
it(`get and set for ${key} work as expected`, () => {
|
200
215
|
const inMeetingActions = new InMeetingActions();
|
201
216
|
|