@webex/plugin-meetings 3.8.0-web-workers-keepalive.1 → 3.8.1-next.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 +70 -6
- package/dist/breakouts/index.js.map +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 +22 -123
- 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 +30 -10
- package/dist/locus-info/controlsUtils.js.map +1 -1
- package/dist/locus-info/index.js +83 -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 +17 -17
- 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 +9 -2
- 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 +568 -328
- 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 +4 -4
- 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 +9 -1
- package/dist/meeting/util.js.map +1 -1
- package/dist/meeting-info/meeting-info-v2.js +19 -13
- package/dist/meeting-info/meeting-info-v2.js.map +1 -1
- package/dist/meeting-info/utilv2.js +5 -1
- package/dist/meeting-info/utilv2.js.map +1 -1
- package/dist/meetings/index.js +76 -0
- 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 +45 -9
- package/dist/member/index.js.map +1 -1
- package/dist/member/types.js +3 -0
- package/dist/member/types.js.map +1 -1
- package/dist/member/util.js +335 -356
- package/dist/member/util.js.map +1 -1
- package/dist/members/collection.js.map +1 -1
- package/dist/members/index.js +137 -29
- package/dist/members/index.js.map +1 -1
- package/dist/members/request.js +38 -0
- package/dist/members/request.js.map +1 -1
- package/dist/members/util.js +36 -1
- package/dist/members/util.js.map +1 -1
- package/dist/metrics/constants.js +1 -0
- package/dist/metrics/constants.js.map +1 -1
- package/dist/reachability/clusterReachability.js +23 -31
- package/dist/reachability/clusterReachability.js.map +1 -1
- package/dist/reachability/index.js +42 -2
- package/dist/reachability/index.js.map +1 -1
- package/dist/reconnection-manager/index.js +2 -2
- package/dist/reconnection-manager/index.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 +15 -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 +35 -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 +2 -1
- package/dist/types/meetings/index.d.ts +28 -0
- package/dist/types/member/index.d.ts +20 -6
- package/dist/types/member/types.d.ts +73 -14
- package/dist/types/member/util.d.ts +156 -1
- package/dist/types/members/collection.d.ts +6 -5
- package/dist/types/members/index.d.ts +32 -43
- package/dist/types/members/request.d.ts +26 -0
- package/dist/types/members/util.d.ts +27 -0
- package/dist/types/metrics/constants.d.ts +1 -0
- package/dist/types/reachability/clusterReachability.d.ts +2 -6
- package/dist/types/reachability/index.d.ts +8 -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/breakouts/index.ts +69 -0
- package/src/common/errors/webex-errors.ts +8 -1
- package/src/config.ts +2 -0
- package/src/constants.ts +23 -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 +48 -12
- package/src/locus-info/index.ts +88 -13
- package/src/locus-info/selfUtils.ts +496 -442
- package/src/media/index.ts +23 -21
- package/src/media/properties.ts +96 -0
- package/src/meeting/brbState.ts +11 -2
- package/src/meeting/in-meeting-actions.ts +32 -0
- package/src/meeting/index.ts +356 -87
- package/src/meeting/locusMediaRequest.ts +0 -18
- package/src/meeting/muteState.ts +4 -4
- package/src/meeting/request.ts +36 -1
- package/src/meeting/request.type.ts +7 -0
- package/src/meeting/util.ts +9 -1
- package/src/meeting-info/meeting-info-v2.ts +7 -2
- package/src/meeting-info/utilv2.ts +5 -0
- package/src/meetings/index.ts +76 -0
- package/src/meetings/util.ts +18 -0
- package/src/member/index.ts +57 -22
- package/src/member/types.ts +82 -16
- package/src/member/util.ts +357 -353
- package/src/members/collection.ts +4 -3
- package/src/members/index.ts +137 -18
- package/src/members/request.ts +44 -0
- package/src/members/util.ts +43 -1
- package/src/metrics/constants.ts +1 -0
- package/src/reachability/clusterReachability.ts +26 -25
- package/src/reachability/index.ts +55 -1
- package/src/reconnection-manager/index.ts +2 -2
- 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/breakouts/index.ts +167 -95
- package/test/unit/spec/controls-options-manager/util.js +120 -0
- package/test/unit/spec/locus-info/controlsUtils.js +131 -9
- package/test/unit/spec/locus-info/index.js +195 -73
- package/test/unit/spec/locus-info/selfUtils.js +98 -24
- package/test/unit/spec/media/index.ts +150 -18
- package/test/unit/spec/media/properties.ts +130 -0
- package/test/unit/spec/meeting/brbState.ts +40 -2
- package/test/unit/spec/meeting/in-meeting-actions.ts +19 -4
- package/test/unit/spec/meeting/index.js +553 -36
- package/test/unit/spec/meeting/locusMediaRequest.ts +0 -30
- package/test/unit/spec/meeting/muteState.js +73 -2
- package/test/unit/spec/meeting/request.js +32 -1
- package/test/unit/spec/meeting/utils.js +79 -33
- package/test/unit/spec/meeting-info/meetinginfov2.js +41 -0
- package/test/unit/spec/meeting-info/utilv2.js +19 -0
- package/test/unit/spec/meetings/index.js +68 -1
- package/test/unit/spec/members/index.js +304 -78
- package/test/unit/spec/members/request.js +68 -22
- package/test/unit/spec/members/utils.js +75 -0
- package/test/unit/spec/reachability/clusterReachability.ts +41 -55
- package/test/unit/spec/reachability/index.ts +89 -0
- 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,10 @@ describe('createMediaConnection', () => {
|
|
79
80
|
enableRtx: ENABLE_RTX,
|
80
81
|
enableExtmap: ENABLE_EXTMAP,
|
81
82
|
turnServerInfo: {
|
82
|
-
|
83
|
+
urls: [
|
84
|
+
'turns:turn-server-url-1:443?transport=tcp',
|
85
|
+
'turns:turn-server-url-2:443?transport=tcp',
|
86
|
+
],
|
83
87
|
username: 'turn username',
|
84
88
|
password: 'turn password',
|
85
89
|
},
|
@@ -91,12 +95,10 @@ describe('createMediaConnection', () => {
|
|
91
95
|
{
|
92
96
|
iceServers: [
|
93
97
|
{
|
94
|
-
urls:
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
{
|
99
|
-
urls: 'turns:turn-server-url:443?transport=tcp',
|
98
|
+
urls: [
|
99
|
+
'turns:turn-server-url-1:443?transport=tcp',
|
100
|
+
'turns:turn-server-url-2:443?transport=tcp',
|
101
|
+
],
|
100
102
|
username: 'turn username',
|
101
103
|
credential: 'turn password',
|
102
104
|
},
|
@@ -159,11 +161,16 @@ describe('createMediaConnection', () => {
|
|
159
161
|
},
|
160
162
|
rtcMetrics,
|
161
163
|
turnServerInfo: {
|
162
|
-
|
164
|
+
urls: [
|
165
|
+
'turns:turn-server-url-1:443?transport=tcp',
|
166
|
+
'turns:turn-server-url-2:443?transport=tcp',
|
167
|
+
],
|
163
168
|
username: 'turn username',
|
164
169
|
password: 'turn password',
|
165
170
|
},
|
166
171
|
bundlePolicy: 'max-bundle',
|
172
|
+
disableAudioMainDtx: false,
|
173
|
+
enableAudioTwcc: true,
|
167
174
|
});
|
168
175
|
assert.calledOnce(multistreamRoapMediaConnectionConstructorStub);
|
169
176
|
assert.calledWith(
|
@@ -171,25 +178,27 @@ describe('createMediaConnection', () => {
|
|
171
178
|
{
|
172
179
|
iceServers: [
|
173
180
|
{
|
174
|
-
urls:
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
{
|
179
|
-
urls: 'turns:turn-server-url:443?transport=tcp',
|
181
|
+
urls: [
|
182
|
+
'turns:turn-server-url-1:443?transport=tcp',
|
183
|
+
'turns:turn-server-url-2:443?transport=tcp',
|
184
|
+
],
|
180
185
|
username: 'turn username',
|
181
186
|
credential: 'turn password',
|
182
187
|
},
|
183
188
|
],
|
184
189
|
bundlePolicy: 'max-bundle',
|
190
|
+
disableAudioMainDtx: false,
|
191
|
+
disableAudioTwcc: false,
|
185
192
|
},
|
186
193
|
'meeting id'
|
187
194
|
);
|
188
195
|
|
189
196
|
// check if rtcMetrics callbacks are configured correctly
|
190
197
|
const addMetricsCallback = multistreamRoapMediaConnectionConstructorStub.getCalls()[0].args[2];
|
191
|
-
const closeMetricsCallback =
|
192
|
-
|
198
|
+
const closeMetricsCallback =
|
199
|
+
multistreamRoapMediaConnectionConstructorStub.getCalls()[0].args[3];
|
200
|
+
const sendMetricsInQueueCallback =
|
201
|
+
multistreamRoapMediaConnectionConstructorStub.getCalls()[0].args[4];
|
193
202
|
|
194
203
|
assert.isFunction(addMetricsCallback);
|
195
204
|
assert.isFunction(closeMetricsCallback);
|
@@ -205,14 +214,78 @@ describe('createMediaConnection', () => {
|
|
205
214
|
|
206
215
|
sendMetricsInQueueCallback();
|
207
216
|
assert.calledOnce(rtcMetrics.sendMetricsInQueue);
|
217
|
+
});
|
218
|
+
|
219
|
+
it('multistream non-firefox does not care about stopIceGatheringAfterFirstRelayCandidate', () => {
|
220
|
+
const multistreamRoapMediaConnectionConstructorStub = sinon
|
221
|
+
.stub(InternalMediaCoreModule, 'MultistreamRoapMediaConnection')
|
222
|
+
.returns(fakeRoapMediaConnection);
|
223
|
+
|
224
|
+
Media.createMediaConnection(true, 'some debug id', 'meeting id', {
|
225
|
+
stopIceGatheringAfterFirstRelayCandidate: true,
|
226
|
+
});
|
227
|
+
assert.calledOnce(multistreamRoapMediaConnectionConstructorStub);
|
228
|
+
assert.calledWith(
|
229
|
+
multistreamRoapMediaConnectionConstructorStub,
|
230
|
+
{
|
231
|
+
iceServers: [],
|
232
|
+
disableAudioTwcc: true,
|
233
|
+
},
|
234
|
+
'meeting id'
|
235
|
+
);
|
236
|
+
});
|
237
|
+
|
238
|
+
it('multistream firefox stops gathering after first relay if stopIceGatheringAfterFirstRelayCandidate is true', () => {
|
239
|
+
const multistreamRoapMediaConnectionConstructorStub = sinon
|
240
|
+
.stub(InternalMediaCoreModule, 'MultistreamRoapMediaConnection')
|
241
|
+
.returns(fakeRoapMediaConnection);
|
242
|
+
|
243
|
+
sinon.stub(BrowserInfo, 'isFirefox').returns(true);
|
208
244
|
|
245
|
+
Media.createMediaConnection(true, 'some debug id', 'meeting id', {
|
246
|
+
stopIceGatheringAfterFirstRelayCandidate: true,
|
247
|
+
});
|
248
|
+
assert.calledOnce(multistreamRoapMediaConnectionConstructorStub);
|
249
|
+
assert.calledWith(
|
250
|
+
multistreamRoapMediaConnectionConstructorStub,
|
251
|
+
{
|
252
|
+
iceServers: [],
|
253
|
+
doFullIce: true,
|
254
|
+
stopIceGatheringAfterFirstRelayCandidate: true,
|
255
|
+
disableAudioTwcc: true,
|
256
|
+
},
|
257
|
+
'meeting id'
|
258
|
+
);
|
259
|
+
});
|
260
|
+
|
261
|
+
it('multistream firefox continues gathering if stopIceGatheringAfterFirstRelayCandidate is false', () => {
|
262
|
+
const multistreamRoapMediaConnectionConstructorStub = sinon
|
263
|
+
.stub(InternalMediaCoreModule, 'MultistreamRoapMediaConnection')
|
264
|
+
.returns(fakeRoapMediaConnection);
|
265
|
+
|
266
|
+
sinon.stub(BrowserInfo, 'isFirefox').returns(true);
|
267
|
+
|
268
|
+
Media.createMediaConnection(true, 'some debug id', 'meeting id', {
|
269
|
+
stopIceGatheringAfterFirstRelayCandidate: false,
|
270
|
+
});
|
271
|
+
assert.calledOnce(multistreamRoapMediaConnectionConstructorStub);
|
272
|
+
assert.calledWith(
|
273
|
+
multistreamRoapMediaConnectionConstructorStub,
|
274
|
+
{
|
275
|
+
iceServers: [],
|
276
|
+
doFullIce: true,
|
277
|
+
stopIceGatheringAfterFirstRelayCandidate: false,
|
278
|
+
disableAudioTwcc: true,
|
279
|
+
},
|
280
|
+
'meeting id'
|
281
|
+
);
|
209
282
|
});
|
210
283
|
|
211
284
|
[
|
212
285
|
{testCase: 'turnServerInfo is undefined', turnServerInfo: undefined},
|
213
286
|
{
|
214
287
|
testCase: 'turnServerInfo.url is empty string',
|
215
|
-
turnServerInfo: {
|
288
|
+
turnServerInfo: {urls: [], username: 'turn username', password: 'turn password'},
|
216
289
|
},
|
217
290
|
].forEach(({testCase, turnServerInfo}) => {
|
218
291
|
it(`passes empty ICE servers array to MultistreamRoapMediaConnection if ${testCase} (multistream enabled)`, () => {
|
@@ -238,6 +311,7 @@ describe('createMediaConnection', () => {
|
|
238
311
|
multistreamRoapMediaConnectionConstructorStub,
|
239
312
|
{
|
240
313
|
iceServers: [],
|
314
|
+
disableAudioTwcc: true,
|
241
315
|
},
|
242
316
|
'meeting id'
|
243
317
|
);
|
@@ -267,6 +341,64 @@ describe('createMediaConnection', () => {
|
|
267
341
|
multistreamRoapMediaConnectionConstructorStub,
|
268
342
|
{
|
269
343
|
iceServers: [],
|
344
|
+
disableAudioTwcc: true,
|
345
|
+
},
|
346
|
+
'meeting id'
|
347
|
+
);
|
348
|
+
});
|
349
|
+
|
350
|
+
it('does not pass disableAudioMainDtx to MultistreamRoapMediaConnection if disableAudioMainDtx is undefined', () => {
|
351
|
+
const multistreamRoapMediaConnectionConstructorStub = sinon
|
352
|
+
.stub(InternalMediaCoreModule, 'MultistreamRoapMediaConnection')
|
353
|
+
.returns(fakeRoapMediaConnection);
|
354
|
+
|
355
|
+
Media.createMediaConnection(true, 'debug string', 'meeting id', {
|
356
|
+
mediaProperties: {
|
357
|
+
mediaDirection: {
|
358
|
+
sendAudio: true,
|
359
|
+
sendVideo: true,
|
360
|
+
sendShare: false,
|
361
|
+
receiveAudio: true,
|
362
|
+
receiveVideo: true,
|
363
|
+
receiveShare: true,
|
364
|
+
},
|
365
|
+
},
|
366
|
+
disableAudioMainDtx: undefined,
|
367
|
+
});
|
368
|
+
assert.calledOnce(multistreamRoapMediaConnectionConstructorStub);
|
369
|
+
assert.calledWith(
|
370
|
+
multistreamRoapMediaConnectionConstructorStub,
|
371
|
+
{
|
372
|
+
iceServers: [],
|
373
|
+
disableAudioTwcc: true,
|
374
|
+
},
|
375
|
+
'meeting id'
|
376
|
+
);
|
377
|
+
});
|
378
|
+
|
379
|
+
it('MultistreamRoapMediaConnection disable audio twcc by default', () => {
|
380
|
+
const multistreamRoapMediaConnectionConstructorStub = sinon
|
381
|
+
.stub(InternalMediaCoreModule, 'MultistreamRoapMediaConnection')
|
382
|
+
.returns(fakeRoapMediaConnection);
|
383
|
+
|
384
|
+
Media.createMediaConnection(true, 'debug string', 'meeting id', {
|
385
|
+
mediaProperties: {
|
386
|
+
mediaDirection: {
|
387
|
+
sendAudio: true,
|
388
|
+
sendVideo: true,
|
389
|
+
sendShare: false,
|
390
|
+
receiveAudio: true,
|
391
|
+
receiveVideo: true,
|
392
|
+
receiveShare: true,
|
393
|
+
},
|
394
|
+
},
|
395
|
+
});
|
396
|
+
assert.calledOnce(multistreamRoapMediaConnectionConstructorStub);
|
397
|
+
assert.calledWith(
|
398
|
+
multistreamRoapMediaConnectionConstructorStub,
|
399
|
+
{
|
400
|
+
iceServers: [],
|
401
|
+
disableAudioTwcc: true,
|
270
402
|
},
|
271
403
|
'meeting id'
|
272
404
|
);
|
@@ -276,7 +408,7 @@ describe('createMediaConnection', () => {
|
|
276
408
|
{testCase: 'turnServerInfo is undefined', turnServerInfo: undefined},
|
277
409
|
{
|
278
410
|
testCase: 'turnServerInfo.url is empty string',
|
279
|
-
turnServerInfo: {
|
411
|
+
turnServerInfo: {urls: [], username: 'turn username', password: 'turn password'},
|
280
412
|
},
|
281
413
|
].forEach(({testCase, turnServerInfo}) => {
|
282
414
|
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'}]);
|