@webex/plugin-meetings 3.9.0 → 3.10.0-multi-llms.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/common/errors/webex-errors.js +21 -1
- package/dist/common/errors/webex-errors.js.map +1 -1
- package/dist/constants.js +9 -0
- package/dist/constants.js.map +1 -1
- package/dist/controls-options-manager/index.js +22 -5
- package/dist/controls-options-manager/index.js.map +1 -1
- package/dist/index.js +9 -1
- package/dist/index.js.map +1 -1
- package/dist/interceptors/index.js +7 -0
- package/dist/interceptors/index.js.map +1 -1
- package/dist/interceptors/locusRouteToken.js +116 -0
- package/dist/interceptors/locusRouteToken.js.map +1 -0
- package/dist/interpretation/index.js +1 -1
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/locus-info/controlsUtils.js +11 -2
- package/dist/locus-info/controlsUtils.js.map +1 -1
- package/dist/locus-info/index.js +56 -14
- 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/index.js +5 -0
- package/dist/media/index.js.map +1 -1
- package/dist/media/properties.js +53 -5
- package/dist/media/properties.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 +340 -186
- 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 +177 -14
- package/dist/meeting/request.js.map +1 -1
- package/dist/meeting/util.js +39 -11
- 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 +31 -25
- package/dist/meetings/index.js.map +1 -1
- package/dist/member/index.js +9 -0
- package/dist/member/index.js.map +1 -1
- package/dist/member/types.js.map +1 -1
- package/dist/member/util.js +10 -0
- package/dist/member/util.js.map +1 -1
- package/dist/members/collection.js +13 -0
- package/dist/members/collection.js.map +1 -1
- package/dist/members/index.js +42 -20
- package/dist/members/index.js.map +1 -1
- package/dist/members/util.js +7 -2
- 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/reachability/index.js +3 -3
- package/dist/reachability/index.js.map +1 -1
- package/dist/types/common/errors/webex-errors.d.ts +12 -0
- package/dist/types/constants.d.ts +7 -0
- package/dist/types/controls-options-manager/index.d.ts +9 -1
- package/dist/types/index.d.ts +2 -1
- package/dist/types/interceptors/index.d.ts +2 -1
- package/dist/types/interceptors/locusRouteToken.d.ts +38 -0
- package/dist/types/locus-info/index.d.ts +56 -2
- package/dist/types/media/properties.d.ts +21 -0
- package/dist/types/meeting/in-meeting-actions.d.ts +8 -0
- package/dist/types/meeting/index.d.ts +41 -1
- package/dist/types/meeting/request.d.ts +42 -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/index.d.ts +1 -0
- package/dist/types/member/types.d.ts +1 -0
- package/dist/types/member/util.d.ts +5 -0
- package/dist/types/members/collection.d.ts +6 -0
- package/dist/types/members/index.d.ts +12 -2
- package/dist/types/members/util.d.ts +6 -3
- package/dist/types/metrics/constants.d.ts +1 -0
- package/dist/webinar/index.js +1 -1
- package/package.json +24 -24
- package/src/common/errors/webex-errors.ts +19 -0
- package/src/constants.ts +10 -0
- package/src/controls-options-manager/index.ts +26 -5
- package/src/index.ts +4 -1
- package/src/interceptors/index.ts +2 -1
- package/src/interceptors/locusRouteToken.ts +80 -0
- package/src/locus-info/controlsUtils.ts +18 -0
- package/src/locus-info/index.ts +99 -17
- package/src/locus-info/parser.ts +5 -1
- package/src/media/index.ts +6 -0
- package/src/media/properties.ts +43 -0
- package/src/meeting/in-meeting-actions.ts +16 -0
- package/src/meeting/index.ts +207 -25
- package/src/meeting/muteState.ts +2 -6
- package/src/meeting/request.ts +141 -0
- package/src/meeting/util.ts +50 -20
- package/src/meeting-info/meeting-info-v2.ts +24 -5
- package/src/meetings/index.ts +9 -3
- package/src/member/index.ts +10 -0
- package/src/member/types.ts +1 -0
- package/src/member/util.ts +14 -0
- package/src/members/collection.ts +11 -0
- package/src/members/index.ts +38 -5
- package/src/members/util.ts +18 -2
- package/src/metrics/constants.ts +1 -0
- package/src/reachability/index.ts +3 -3
- package/test/unit/spec/common/browser-detection.js +0 -24
- package/test/unit/spec/controls-options-manager/index.js +47 -0
- package/test/unit/spec/fixture/locus.js +1 -0
- package/test/unit/spec/interceptors/locusRouteToken.ts +87 -0
- package/test/unit/spec/locus-info/index.js +91 -15
- package/test/unit/spec/locus-info/parser.js +3 -2
- package/test/unit/spec/media/index.ts +140 -9
- package/test/unit/spec/media/properties.ts +137 -0
- package/test/unit/spec/meeting/in-meeting-actions.ts +8 -0
- package/test/unit/spec/meeting/index.js +469 -88
- package/test/unit/spec/meeting/muteState.js +32 -6
- package/test/unit/spec/meeting/request.js +21 -0
- package/test/unit/spec/meeting/utils.js +48 -16
- package/test/unit/spec/meeting-info/meetinginfov2.js +8 -3
- package/test/unit/spec/meetings/index.js +10 -7
- package/test/unit/spec/member/util.js +24 -0
- package/test/unit/spec/members/collection.js +120 -0
- package/test/unit/spec/members/index.js +72 -3
- package/test/unit/spec/members/request.js +55 -0
- package/test/unit/spec/members/utils.js +116 -14
- package/test/unit/spec/reachability/index.ts +158 -3
- package/test/unit/spec/roap/turnDiscovery.ts +3 -3
|
@@ -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,
|
|
@@ -96,6 +97,7 @@ import PermissionError from '../../../../src/common/errors/permission';
|
|
|
96
97
|
import JoinWebinarError from '../../../../src/common/errors/join-webinar-error';
|
|
97
98
|
import IntentToJoinError from '../../../../src/common/errors/intent-to-join';
|
|
98
99
|
import MultistreamNotSupportedError from '../../../../src/common/errors/multistream-not-supported-error';
|
|
100
|
+
import {SdpResponseTimeoutError} from '@webex/plugin-meetings/src/common/errors/webex-errors';
|
|
99
101
|
import testUtils from '../../../utils/testUtils';
|
|
100
102
|
import {
|
|
101
103
|
MeetingInfoV2CaptchaError,
|
|
@@ -487,7 +489,7 @@ describe('plugin-meetings', () => {
|
|
|
487
489
|
|
|
488
490
|
it('pstnCorrelationId getter/setter should work correctly', () => {
|
|
489
491
|
const testPstnCorrelationId = uuid.v4();
|
|
490
|
-
|
|
492
|
+
|
|
491
493
|
meeting.pstnCorrelationId = testPstnCorrelationId;
|
|
492
494
|
assert.equal(meeting.pstnCorrelationId, testPstnCorrelationId);
|
|
493
495
|
assert.equal(meeting.callStateForMetrics.pstnCorrelationId, testPstnCorrelationId);
|
|
@@ -1992,24 +1994,21 @@ describe('plugin-meetings', () => {
|
|
|
1992
1994
|
it('should handle join failure', async () => {
|
|
1993
1995
|
MeetingUtil.isPinOrGuest = sinon.stub().returns(false);
|
|
1994
1996
|
webex.internal.newMetrics.submitClientEvent = sinon.stub();
|
|
1995
|
-
|
|
1997
|
+
|
|
1996
1998
|
await meeting.join().catch(() => {
|
|
1997
1999
|
assert.calledOnce(MeetingUtil.joinMeeting);
|
|
1998
|
-
|
|
2000
|
+
|
|
1999
2001
|
// Assert that client.locus.join.response error event is not sent from this function, it is now emitted from MeetingUtil.joinMeeting
|
|
2000
2002
|
assert.calledOnce(webex.internal.newMetrics.submitClientEvent);
|
|
2001
|
-
assert.calledWithMatch(
|
|
2002
|
-
|
|
2003
|
-
{
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
options: {meetingId: meeting.id},
|
|
2011
|
-
}
|
|
2012
|
-
);
|
|
2003
|
+
assert.calledWithMatch(webex.internal.newMetrics.submitClientEvent, {
|
|
2004
|
+
name: 'client.call.initiated',
|
|
2005
|
+
payload: {
|
|
2006
|
+
trigger: 'user-interaction',
|
|
2007
|
+
isRoapCallEnabled: true,
|
|
2008
|
+
pstnAudioType: undefined,
|
|
2009
|
+
},
|
|
2010
|
+
options: {meetingId: meeting.id},
|
|
2011
|
+
});
|
|
2013
2012
|
});
|
|
2014
2013
|
});
|
|
2015
2014
|
it('should fail if password is required', async () => {
|
|
@@ -2216,6 +2215,7 @@ describe('plugin-meetings', () => {
|
|
|
2216
2215
|
});
|
|
2217
2216
|
meeting.audio = muteStateStub;
|
|
2218
2217
|
meeting.video = muteStateStub;
|
|
2218
|
+
sinon.stub(MeetingUtil, 'getIpVersion').returns(IP_VERSION.ipv4_and_ipv6);
|
|
2219
2219
|
sinon.stub(Media, 'createMediaConnection').returns(fakeMediaConnection);
|
|
2220
2220
|
sinon.stub(meeting, 'setupMediaConnectionListeners');
|
|
2221
2221
|
sinon.stub(meeting, 'setMercuryListener');
|
|
@@ -2287,13 +2287,24 @@ describe('plugin-meetings', () => {
|
|
|
2287
2287
|
close: sinon.stub(),
|
|
2288
2288
|
forceRtcMetricsSend,
|
|
2289
2289
|
});
|
|
2290
|
-
|
|
2290
|
+
|
|
2291
|
+
const mockStatsMonitor = {removeAllListeners: sinon.stub()};
|
|
2292
|
+
const mockNetworkQualityMonitor = {removeAllListeners: sinon.stub()};
|
|
2293
|
+
|
|
2294
|
+
// set a statsAnalyzer and statsMonitor on the meeting so that we can check that they get reset to null
|
|
2291
2295
|
meeting.statsAnalyzer = {stopAnalyzer: sinon.stub().resolves()};
|
|
2296
|
+
meeting.statsMonitor = mockStatsMonitor;
|
|
2297
|
+
meeting.networkQualityMonitor = mockNetworkQualityMonitor;
|
|
2292
2298
|
const error = await assert.isRejected(meeting.addMedia());
|
|
2293
2299
|
|
|
2294
2300
|
assert.calledOnce(forceRtcMetricsSend);
|
|
2301
|
+
assert.calledOnce(mockStatsMonitor.removeAllListeners);
|
|
2302
|
+
assert.calledOnce(mockNetworkQualityMonitor.removeAllListeners);
|
|
2295
2303
|
|
|
2296
2304
|
assert.isNull(meeting.statsAnalyzer);
|
|
2305
|
+
assert.isNull(meeting.statsMonitor);
|
|
2306
|
+
assert.isNull(meeting.networkQualityMonitor);
|
|
2307
|
+
|
|
2297
2308
|
assert(webex.internal.newMetrics.submitInternalEvent.calledTwice);
|
|
2298
2309
|
assert.calledWith(webex.internal.newMetrics.submitInternalEvent.firstCall, {
|
|
2299
2310
|
name: 'internal.client.add-media.turn-discovery.start',
|
|
@@ -2337,6 +2348,7 @@ describe('plugin-meetings', () => {
|
|
|
2337
2348
|
selected_subnet: null,
|
|
2338
2349
|
numTransports: 1,
|
|
2339
2350
|
iceCandidatesCount: 0,
|
|
2351
|
+
ipver: 1,
|
|
2340
2352
|
}
|
|
2341
2353
|
);
|
|
2342
2354
|
});
|
|
@@ -2384,6 +2396,7 @@ describe('plugin-meetings', () => {
|
|
|
2384
2396
|
subnet_reachable: null,
|
|
2385
2397
|
selected_cluster: null,
|
|
2386
2398
|
selected_subnet: null,
|
|
2399
|
+
ipver: 1,
|
|
2387
2400
|
})
|
|
2388
2401
|
);
|
|
2389
2402
|
|
|
@@ -2403,12 +2416,23 @@ describe('plugin-meetings', () => {
|
|
|
2403
2416
|
|
|
2404
2417
|
meeting.waitForRemoteSDPAnswer = sinon.stub().rejects();
|
|
2405
2418
|
|
|
2406
|
-
|
|
2419
|
+
const mockStatsMonitor = {removeAllListeners: sinon.stub()};
|
|
2420
|
+
const mockNetworkQualityMonitor = {removeAllListeners: sinon.stub()};
|
|
2421
|
+
|
|
2422
|
+
// set a statsAnalyzer and statsMonitor on the meeting so that we can check that they get reset to null
|
|
2407
2423
|
meeting.statsAnalyzer = {stopAnalyzer: sinon.stub().resolves()};
|
|
2424
|
+
meeting.statsMonitor = mockStatsMonitor;
|
|
2425
|
+
meeting.networkQualityMonitor = mockNetworkQualityMonitor;
|
|
2408
2426
|
|
|
2409
2427
|
const error = await assert.isRejected(meeting.addMedia());
|
|
2410
2428
|
|
|
2411
2429
|
assert.isNull(meeting.statsAnalyzer);
|
|
2430
|
+
assert.isNull(meeting.statsMonitor);
|
|
2431
|
+
assert.isNull(meeting.networkQualityMonitor);
|
|
2432
|
+
|
|
2433
|
+
assert.calledOnce(mockStatsMonitor.removeAllListeners);
|
|
2434
|
+
assert.calledOnce(mockNetworkQualityMonitor.removeAllListeners);
|
|
2435
|
+
|
|
2412
2436
|
assert(webex.internal.newMetrics.submitInternalEvent.calledTwice);
|
|
2413
2437
|
assert.calledWith(webex.internal.newMetrics.submitInternalEvent.firstCall, {
|
|
2414
2438
|
name: 'internal.client.add-media.turn-discovery.start',
|
|
@@ -2452,6 +2476,7 @@ describe('plugin-meetings', () => {
|
|
|
2452
2476
|
subnet_reachable: null,
|
|
2453
2477
|
selected_cluster: null,
|
|
2454
2478
|
selected_subnet: null,
|
|
2479
|
+
ipver: 1,
|
|
2455
2480
|
}
|
|
2456
2481
|
);
|
|
2457
2482
|
});
|
|
@@ -2472,8 +2497,9 @@ describe('plugin-meetings', () => {
|
|
|
2472
2497
|
},
|
|
2473
2498
|
},
|
|
2474
2499
|
});
|
|
2475
|
-
// set a statsAnalyzer on the meeting so that we can check that
|
|
2500
|
+
// set a statsAnalyzer and statsMonitor on the meeting so that we can check that they get reset to null
|
|
2476
2501
|
meeting.statsAnalyzer = {stopAnalyzer: sinon.stub().resolves()};
|
|
2502
|
+
meeting.statsMonitor = {removeAllListeners: sinon.stub()};
|
|
2477
2503
|
const error = await assert.isRejected(meeting.addMedia());
|
|
2478
2504
|
|
|
2479
2505
|
assert(webex.internal.newMetrics.submitInternalEvent.calledTwice);
|
|
@@ -2512,10 +2538,12 @@ describe('plugin-meetings', () => {
|
|
|
2512
2538
|
subnet_reachable: null,
|
|
2513
2539
|
selected_cluster: null,
|
|
2514
2540
|
selected_subnet: null,
|
|
2541
|
+
ipver: 1,
|
|
2515
2542
|
})
|
|
2516
2543
|
);
|
|
2517
2544
|
|
|
2518
2545
|
assert.isNull(meeting.statsAnalyzer);
|
|
2546
|
+
assert.isNull(meeting.statsMonitor);
|
|
2519
2547
|
});
|
|
2520
2548
|
|
|
2521
2549
|
it('should include the peer connection properties correctly for transcoded', async () => {
|
|
@@ -2532,8 +2560,14 @@ describe('plugin-meetings', () => {
|
|
|
2532
2560
|
},
|
|
2533
2561
|
},
|
|
2534
2562
|
});
|
|
2535
|
-
|
|
2563
|
+
|
|
2564
|
+
const mockStatsMonitor = {removeAllListeners: sinon.stub()};
|
|
2565
|
+
const mockNetworkQualityMonitor = {removeAllListeners: sinon.stub()};
|
|
2566
|
+
|
|
2567
|
+
// set a statsAnalyzer and statsMonitor on the meeting so that we can check that they get reset to null
|
|
2536
2568
|
meeting.statsAnalyzer = {stopAnalyzer: sinon.stub().resolves()};
|
|
2569
|
+
meeting.statsMonitor = mockStatsMonitor;
|
|
2570
|
+
meeting.networkQualityMonitor = mockNetworkQualityMonitor;
|
|
2537
2571
|
const error = await assert.isRejected(meeting.addMedia());
|
|
2538
2572
|
|
|
2539
2573
|
assert(webex.internal.newMetrics.submitInternalEvent.calledTwice);
|
|
@@ -2572,10 +2606,15 @@ describe('plugin-meetings', () => {
|
|
|
2572
2606
|
subnet_reachable: null,
|
|
2573
2607
|
selected_cluster: null,
|
|
2574
2608
|
selected_subnet: null,
|
|
2609
|
+
ipver: 1,
|
|
2575
2610
|
})
|
|
2576
2611
|
);
|
|
2577
2612
|
|
|
2578
2613
|
assert.isNull(meeting.statsAnalyzer);
|
|
2614
|
+
assert.isNull(meeting.statsMonitor);
|
|
2615
|
+
assert.isNull(meeting.networkQualityMonitor);
|
|
2616
|
+
assert.calledOnce(mockStatsMonitor.removeAllListeners);
|
|
2617
|
+
assert.calledOnce(mockNetworkQualityMonitor.removeAllListeners);
|
|
2579
2618
|
});
|
|
2580
2619
|
|
|
2581
2620
|
it('should work the second time addMedia is called in case the first time fails', async () => {
|
|
@@ -2638,7 +2677,11 @@ describe('plugin-meetings', () => {
|
|
|
2638
2677
|
// simulate timeout waiting for the SDP answer that never comes
|
|
2639
2678
|
await clock.tickAsync(ROAP_OFFER_ANSWER_EXCHANGE_TIMEOUT);
|
|
2640
2679
|
|
|
2641
|
-
await assert.isRejected(
|
|
2680
|
+
await assert.isRejected(
|
|
2681
|
+
result,
|
|
2682
|
+
SdpResponseTimeoutError,
|
|
2683
|
+
'Timed out waiting for REMOTE SDP ANSWER'
|
|
2684
|
+
);
|
|
2642
2685
|
|
|
2643
2686
|
assert.calledOnceWithExactly(getErrorPayloadForClientErrorCodeStub, {
|
|
2644
2687
|
clientErrorCode: 2007,
|
|
@@ -3096,6 +3139,7 @@ describe('plugin-meetings', () => {
|
|
|
3096
3139
|
subnet_reachable: null,
|
|
3097
3140
|
selected_cluster: null,
|
|
3098
3141
|
selected_subnet: null,
|
|
3142
|
+
ipver: 1,
|
|
3099
3143
|
},
|
|
3100
3144
|
]);
|
|
3101
3145
|
|
|
@@ -3297,6 +3341,7 @@ describe('plugin-meetings', () => {
|
|
|
3297
3341
|
connectionType: 'udp',
|
|
3298
3342
|
selectedCandidatePairChanges: 2,
|
|
3299
3343
|
ipVersion: 'IPv6',
|
|
3344
|
+
ipver: 1,
|
|
3300
3345
|
numTransports: 1,
|
|
3301
3346
|
isMultistream: false,
|
|
3302
3347
|
retriedWithTurnServer: true,
|
|
@@ -3443,6 +3488,7 @@ describe('plugin-meetings', () => {
|
|
|
3443
3488
|
meeting.iceCandidatesCount = 3;
|
|
3444
3489
|
meeting.iceCandidateErrors.set('701_error', 3);
|
|
3445
3490
|
meeting.iceCandidateErrors.set('701_turn_host_lookup_received_error', 1);
|
|
3491
|
+
MeetingUtil.getIpVersion.returns(IP_VERSION.only_ipv6);
|
|
3446
3492
|
|
|
3447
3493
|
await meeting.addMedia({
|
|
3448
3494
|
mediaSettings: {},
|
|
@@ -3458,6 +3504,7 @@ describe('plugin-meetings', () => {
|
|
|
3458
3504
|
connectionType: 'udp',
|
|
3459
3505
|
selectedCandidatePairChanges: 2,
|
|
3460
3506
|
ipVersion: 'IPv6',
|
|
3507
|
+
ipver: 6,
|
|
3461
3508
|
numTransports: 1,
|
|
3462
3509
|
isMultistream: false,
|
|
3463
3510
|
retriedWithTurnServer: false,
|
|
@@ -3536,6 +3583,7 @@ describe('plugin-meetings', () => {
|
|
|
3536
3583
|
selected_cluster: null,
|
|
3537
3584
|
selected_subnet: null,
|
|
3538
3585
|
iceCandidatesCount: 0,
|
|
3586
|
+
ipver: 1,
|
|
3539
3587
|
}
|
|
3540
3588
|
);
|
|
3541
3589
|
|
|
@@ -3600,6 +3648,7 @@ describe('plugin-meetings', () => {
|
|
|
3600
3648
|
selected_cluster: null,
|
|
3601
3649
|
selected_subnet: null,
|
|
3602
3650
|
iceCandidatesCount: 0,
|
|
3651
|
+
ipver: 1,
|
|
3603
3652
|
}
|
|
3604
3653
|
);
|
|
3605
3654
|
|
|
@@ -3646,6 +3695,7 @@ describe('plugin-meetings', () => {
|
|
|
3646
3695
|
locus_id: meeting.locusUrl.split('/').pop(),
|
|
3647
3696
|
connectionType: 'udp',
|
|
3648
3697
|
ipVersion: 'IPv6',
|
|
3698
|
+
ipver: 1,
|
|
3649
3699
|
selectedCandidatePairChanges: 2,
|
|
3650
3700
|
numTransports: 1,
|
|
3651
3701
|
isMultistream: false,
|
|
@@ -3726,6 +3776,7 @@ describe('plugin-meetings', () => {
|
|
|
3726
3776
|
selected_cluster: 'some.cluster',
|
|
3727
3777
|
selected_subnet: '1.X.X.X',
|
|
3728
3778
|
iceCandidatesCount: 0,
|
|
3779
|
+
ipver: 1,
|
|
3729
3780
|
}
|
|
3730
3781
|
);
|
|
3731
3782
|
|
|
@@ -4031,12 +4082,13 @@ describe('plugin-meetings', () => {
|
|
|
4031
4082
|
});
|
|
4032
4083
|
});
|
|
4033
4084
|
|
|
4034
|
-
it('counts the number of members that are in the meeting for MEDIA_QUALITY event', async () => {
|
|
4085
|
+
it('counts the number of members that are in the meeting or lobby for MEDIA_QUALITY event', async () => {
|
|
4035
4086
|
let fakeMembersCollection = {
|
|
4036
4087
|
members: {
|
|
4037
|
-
member1: {isInMeeting: true},
|
|
4038
|
-
member2: {isInMeeting: true},
|
|
4039
|
-
member3: {isInMeeting: false},
|
|
4088
|
+
member1: {isInMeeting: true, isInLobby: false},
|
|
4089
|
+
member2: {isInMeeting: false, isInLobby: true},
|
|
4090
|
+
member3: {isInMeeting: false, isInLobby: false},
|
|
4091
|
+
member4: {isInMeeting: true, isInLobby: false},
|
|
4040
4092
|
},
|
|
4041
4093
|
};
|
|
4042
4094
|
sinon.stub(meeting, 'getMembers').returns({membersCollection: fakeMembersCollection});
|
|
@@ -4055,11 +4107,12 @@ describe('plugin-meetings', () => {
|
|
|
4055
4107
|
},
|
|
4056
4108
|
payload: {
|
|
4057
4109
|
intervals: [
|
|
4058
|
-
sinon.match.has('intervalMetadata', sinon.match.has('meetingUserCount',
|
|
4110
|
+
sinon.match.has('intervalMetadata', sinon.match.has('meetingUserCount', 3)),
|
|
4059
4111
|
],
|
|
4060
4112
|
},
|
|
4061
4113
|
});
|
|
4062
|
-
|
|
4114
|
+
// Move member2 from lobby to neither in meeting nor lobby
|
|
4115
|
+
fakeMembersCollection.members.member2.isInLobby = false;
|
|
4063
4116
|
|
|
4064
4117
|
statsAnalyzerStub.emit(
|
|
4065
4118
|
{file: 'test', function: 'test'},
|
|
@@ -4074,7 +4127,7 @@ describe('plugin-meetings', () => {
|
|
|
4074
4127
|
},
|
|
4075
4128
|
payload: {
|
|
4076
4129
|
intervals: [
|
|
4077
|
-
sinon.match.has('intervalMetadata', sinon.match.has('meetingUserCount',
|
|
4130
|
+
sinon.match.has('intervalMetadata', sinon.match.has('meetingUserCount', 2)),
|
|
4078
4131
|
],
|
|
4079
4132
|
},
|
|
4080
4133
|
});
|
|
@@ -4101,6 +4154,132 @@ describe('plugin-meetings', () => {
|
|
|
4101
4154
|
});
|
|
4102
4155
|
});
|
|
4103
4156
|
|
|
4157
|
+
describe('handles StatsMonitor events', () => {
|
|
4158
|
+
let statsMonitorStub;
|
|
4159
|
+
let prevConfigValue;
|
|
4160
|
+
let listeners;
|
|
4161
|
+
|
|
4162
|
+
beforeEach(async () => {
|
|
4163
|
+
meeting.meetingState = 'ACTIVE';
|
|
4164
|
+
prevConfigValue = meeting.config.stats.enableStatsAnalyzer;
|
|
4165
|
+
|
|
4166
|
+
meeting.config.stats.enableStatsAnalyzer = true;
|
|
4167
|
+
|
|
4168
|
+
listeners = {};
|
|
4169
|
+
|
|
4170
|
+
statsMonitorStub = {
|
|
4171
|
+
on: sinon.stub().callsFake((event, callback) => {
|
|
4172
|
+
listeners[event] = callback;
|
|
4173
|
+
}),
|
|
4174
|
+
removeAllListeners: sinon.stub(),
|
|
4175
|
+
};
|
|
4176
|
+
|
|
4177
|
+
sinon.stub(meeting.mediaProperties, 'sendMediaIssueMetric');
|
|
4178
|
+
|
|
4179
|
+
// mock the StatsMonitor constructor
|
|
4180
|
+
sinon.stub(InternalMediaCoreModule, 'StatsMonitor').returns(statsMonitorStub);
|
|
4181
|
+
|
|
4182
|
+
await meeting.addMedia({
|
|
4183
|
+
mediaSettings: {},
|
|
4184
|
+
});
|
|
4185
|
+
});
|
|
4186
|
+
|
|
4187
|
+
afterEach(() => {
|
|
4188
|
+
meeting.config.stats.enableStatsAnalyzer = prevConfigValue;
|
|
4189
|
+
sinon.restore();
|
|
4190
|
+
});
|
|
4191
|
+
|
|
4192
|
+
describe('INBOUND_AUDIO_ISSUE event', () => {
|
|
4193
|
+
it('should not trigger event when no unmuted members exist', () => {
|
|
4194
|
+
const fakeEventData = {issueSubType: 'DECODE_RESULTS_IN_ZERO_AUDIO_LEVEL'};
|
|
4195
|
+
|
|
4196
|
+
// Setup members that are either self or muted
|
|
4197
|
+
const mutedMember = {
|
|
4198
|
+
isSelf: false,
|
|
4199
|
+
isPairedWithSelf: false,
|
|
4200
|
+
isAudioMuted: true,
|
|
4201
|
+
};
|
|
4202
|
+
const selfMember = {
|
|
4203
|
+
isSelf: true,
|
|
4204
|
+
isPairedWithSelf: false,
|
|
4205
|
+
isAudioMuted: false,
|
|
4206
|
+
};
|
|
4207
|
+
const pairedMember = {
|
|
4208
|
+
isSelf: false,
|
|
4209
|
+
isPairedWithSelf: true,
|
|
4210
|
+
isAudioMuted: false,
|
|
4211
|
+
};
|
|
4212
|
+
meeting.members.membersCollection.getAll = sinon.stub().returns({
|
|
4213
|
+
member1: mutedMember,
|
|
4214
|
+
member2: selfMember,
|
|
4215
|
+
member3: pairedMember,
|
|
4216
|
+
});
|
|
4217
|
+
|
|
4218
|
+
// Reset the stub to clear any previous calls
|
|
4219
|
+
TriggerProxy.trigger.resetHistory();
|
|
4220
|
+
|
|
4221
|
+
// Emit the event from statsMonitor
|
|
4222
|
+
listeners[StatsMonitorEventNames.INBOUND_AUDIO_ISSUE](fakeEventData);
|
|
4223
|
+
|
|
4224
|
+
assert.neverCalledWith(
|
|
4225
|
+
TriggerProxy.trigger,
|
|
4226
|
+
meeting,
|
|
4227
|
+
sinon.match.object,
|
|
4228
|
+
EVENT_TRIGGERS.MEDIA_INBOUND_AUDIO_ISSUE_DETECTED,
|
|
4229
|
+
fakeEventData
|
|
4230
|
+
);
|
|
4231
|
+
assert.notCalled(meeting.mediaProperties.sendMediaIssueMetric);
|
|
4232
|
+
});
|
|
4233
|
+
|
|
4234
|
+
it('should trigger event and metric when there are multiple members and at least one is unmuted', () => {
|
|
4235
|
+
const fakeEventData = {issueSubType: 'DECODE_RESULTS_IN_ZERO_AUDIO_LEVEL'};
|
|
4236
|
+
|
|
4237
|
+
// Setup mixed members - some muted, one unmuted
|
|
4238
|
+
const mutedMember = {
|
|
4239
|
+
isSelf: false,
|
|
4240
|
+
isPairedWithSelf: false,
|
|
4241
|
+
isAudioMuted: true,
|
|
4242
|
+
};
|
|
4243
|
+
const unmutedMember = {
|
|
4244
|
+
isSelf: false,
|
|
4245
|
+
isPairedWithSelf: false,
|
|
4246
|
+
isAudioMuted: false,
|
|
4247
|
+
};
|
|
4248
|
+
const selfMember = {
|
|
4249
|
+
isSelf: true,
|
|
4250
|
+
isPairedWithSelf: false,
|
|
4251
|
+
isAudioMuted: false,
|
|
4252
|
+
};
|
|
4253
|
+
meeting.members.membersCollection.getAll = sinon.stub().returns({
|
|
4254
|
+
member1: mutedMember,
|
|
4255
|
+
member2: unmutedMember,
|
|
4256
|
+
member3: selfMember,
|
|
4257
|
+
});
|
|
4258
|
+
|
|
4259
|
+
// Reset the stub to clear any previous calls
|
|
4260
|
+
TriggerProxy.trigger.resetHistory();
|
|
4261
|
+
|
|
4262
|
+
// Emit the event from statsMonitor
|
|
4263
|
+
listeners[StatsMonitorEventNames.INBOUND_AUDIO_ISSUE](fakeEventData);
|
|
4264
|
+
|
|
4265
|
+
assert.calledWith(
|
|
4266
|
+
TriggerProxy.trigger,
|
|
4267
|
+
meeting,
|
|
4268
|
+
sinon.match.object,
|
|
4269
|
+
EVENT_TRIGGERS.MEDIA_INBOUND_AUDIO_ISSUE_DETECTED,
|
|
4270
|
+
fakeEventData
|
|
4271
|
+
);
|
|
4272
|
+
|
|
4273
|
+
assert.calledOnceWithExactly(
|
|
4274
|
+
meeting.mediaProperties.sendMediaIssueMetric,
|
|
4275
|
+
'inbound_audio',
|
|
4276
|
+
fakeEventData.issueSubType,
|
|
4277
|
+
meeting.correlationId
|
|
4278
|
+
);
|
|
4279
|
+
});
|
|
4280
|
+
});
|
|
4281
|
+
});
|
|
4282
|
+
|
|
4104
4283
|
describe('bundlePolicy', () => {
|
|
4105
4284
|
const FAKE_TURN_URL = 'turns:webex.com:3478';
|
|
4106
4285
|
const FAKE_TURN_USER = 'some-turn-username';
|
|
@@ -5567,6 +5746,7 @@ describe('plugin-meetings', () => {
|
|
|
5567
5746
|
let multistreamEventListeners;
|
|
5568
5747
|
let transcodedEventListeners;
|
|
5569
5748
|
let mockStatsAnalyzerCtor;
|
|
5749
|
+
let statsMonitorStub;
|
|
5570
5750
|
|
|
5571
5751
|
const setupFakeRoapMediaConnection = (fakeRoapMediaConnection, eventListeners) => {
|
|
5572
5752
|
fakeRoapMediaConnection.on.callsFake((eventName, cb) => {
|
|
@@ -5598,6 +5778,14 @@ describe('plugin-meetings', () => {
|
|
|
5598
5778
|
return {on: sinon.stub(), stopAnalyzer: sinon.stub()};
|
|
5599
5779
|
});
|
|
5600
5780
|
|
|
5781
|
+
statsMonitorStub = {
|
|
5782
|
+
on: sinon.stub(),
|
|
5783
|
+
removeAllListeners: sinon.stub(),
|
|
5784
|
+
};
|
|
5785
|
+
|
|
5786
|
+
// mock the StatsMonitor constructor
|
|
5787
|
+
sinon.stub(InternalMediaCoreModule, 'StatsMonitor').returns(statsMonitorStub);
|
|
5788
|
+
|
|
5601
5789
|
webex.internal.newMetrics.callDiagnosticMetrics.getErrorPayloadForClientErrorCode =
|
|
5602
5790
|
sinon.stub();
|
|
5603
5791
|
|
|
@@ -5660,6 +5848,7 @@ describe('plugin-meetings', () => {
|
|
|
5660
5848
|
mockStatsAnalyzerCtor,
|
|
5661
5849
|
sinon.match({
|
|
5662
5850
|
isMultistream: true,
|
|
5851
|
+
statsMonitor: statsMonitorStub,
|
|
5663
5852
|
})
|
|
5664
5853
|
);
|
|
5665
5854
|
const initialStatsAnalyzer = mockStatsAnalyzerCtor.returnValues[0];
|
|
@@ -6548,11 +6737,11 @@ describe('plugin-meetings', () => {
|
|
|
6548
6737
|
clientUrl: meeting.deviceUrl,
|
|
6549
6738
|
});
|
|
6550
6739
|
assert.notCalled(meeting.meetingRequest.dialOut);
|
|
6551
|
-
|
|
6740
|
+
|
|
6552
6741
|
// Verify pstnCorrelationId was set
|
|
6553
6742
|
assert.exists(meeting.pstnCorrelationId);
|
|
6554
6743
|
assert.notEqual(meeting.pstnCorrelationId, meeting.correlationId);
|
|
6555
|
-
const firstPstnCorrelationId = meeting.pstnCorrelationId
|
|
6744
|
+
const firstPstnCorrelationId = meeting.pstnCorrelationId;
|
|
6556
6745
|
|
|
6557
6746
|
meeting.meetingRequest.dialIn.resetHistory();
|
|
6558
6747
|
|
|
@@ -6625,18 +6814,22 @@ describe('plugin-meetings', () => {
|
|
|
6625
6814
|
throw new Error('Promise resolved when it should have rejected');
|
|
6626
6815
|
} catch (e) {
|
|
6627
6816
|
assert.equal(e, error);
|
|
6628
|
-
|
|
6817
|
+
|
|
6629
6818
|
// Verify behavioral metric was sent with dial_in_correlation_id
|
|
6630
|
-
assert.calledWith(
|
|
6631
|
-
|
|
6632
|
-
|
|
6633
|
-
|
|
6634
|
-
|
|
6635
|
-
|
|
6636
|
-
|
|
6637
|
-
|
|
6638
|
-
|
|
6639
|
-
|
|
6819
|
+
assert.calledWith(
|
|
6820
|
+
Metrics.sendBehavioralMetric,
|
|
6821
|
+
BEHAVIORAL_METRICS.ADD_DIAL_IN_FAILURE,
|
|
6822
|
+
{
|
|
6823
|
+
correlation_id: meeting.correlationId,
|
|
6824
|
+
dial_in_url: meeting.dialInUrl,
|
|
6825
|
+
dial_in_correlation_id: sinon.match.string,
|
|
6826
|
+
locus_id: meeting.locusUrl.split('/').pop(),
|
|
6827
|
+
client_url: meeting.deviceUrl,
|
|
6828
|
+
reason: error.error.message,
|
|
6829
|
+
stack: error.stack,
|
|
6830
|
+
}
|
|
6831
|
+
);
|
|
6832
|
+
|
|
6640
6833
|
// Verify pstnCorrelationId was cleared after error
|
|
6641
6834
|
assert.equal(meeting.pstnCorrelationId, undefined);
|
|
6642
6835
|
}
|
|
@@ -6652,18 +6845,22 @@ describe('plugin-meetings', () => {
|
|
|
6652
6845
|
throw new Error('Promise resolved when it should have rejected');
|
|
6653
6846
|
} catch (e) {
|
|
6654
6847
|
assert.equal(e, error);
|
|
6655
|
-
|
|
6848
|
+
|
|
6656
6849
|
// Verify behavioral metric was sent with dial_out_correlation_id
|
|
6657
|
-
assert.calledWith(
|
|
6658
|
-
|
|
6659
|
-
|
|
6660
|
-
|
|
6661
|
-
|
|
6662
|
-
|
|
6663
|
-
|
|
6664
|
-
|
|
6665
|
-
|
|
6666
|
-
|
|
6850
|
+
assert.calledWith(
|
|
6851
|
+
Metrics.sendBehavioralMetric,
|
|
6852
|
+
BEHAVIORAL_METRICS.ADD_DIAL_OUT_FAILURE,
|
|
6853
|
+
{
|
|
6854
|
+
correlation_id: meeting.correlationId,
|
|
6855
|
+
dial_out_url: meeting.dialOutUrl,
|
|
6856
|
+
dial_out_correlation_id: sinon.match.string,
|
|
6857
|
+
locus_id: meeting.locusUrl.split('/').pop(),
|
|
6858
|
+
client_url: meeting.deviceUrl,
|
|
6859
|
+
reason: error.error.message,
|
|
6860
|
+
stack: error.stack,
|
|
6861
|
+
}
|
|
6862
|
+
);
|
|
6863
|
+
|
|
6667
6864
|
// Verify pstnCorrelationId was cleared after error
|
|
6668
6865
|
assert.equal(meeting.pstnCorrelationId, undefined);
|
|
6669
6866
|
}
|
|
@@ -6686,12 +6883,12 @@ describe('plugin-meetings', () => {
|
|
|
6686
6883
|
|
|
6687
6884
|
it('should disconnect phone audio and clear pstnCorrelationId', async () => {
|
|
6688
6885
|
meeting.pstnCorrelationId = 'test-pstn-correlation-id';
|
|
6689
|
-
|
|
6886
|
+
|
|
6690
6887
|
await meeting.disconnectPhoneAudio();
|
|
6691
|
-
|
|
6888
|
+
|
|
6692
6889
|
// Verify that pstnCorrelationId is cleared
|
|
6693
6890
|
assert.equal(meeting.pstnCorrelationId, undefined);
|
|
6694
|
-
|
|
6891
|
+
|
|
6695
6892
|
// Verify that MeetingUtil.disconnectPhoneAudio was called for both dial-in and dial-out
|
|
6696
6893
|
assert.calledTwice(MeetingUtil.disconnectPhoneAudio);
|
|
6697
6894
|
assert.calledWith(MeetingUtil.disconnectPhoneAudio, meeting, meeting.dialInUrl);
|
|
@@ -6702,12 +6899,12 @@ describe('plugin-meetings', () => {
|
|
|
6702
6899
|
meeting.dialInDeviceStatus = 'IDLE';
|
|
6703
6900
|
meeting.dialOutDeviceStatus = 'IDLE';
|
|
6704
6901
|
meeting.pstnCorrelationId = 'test-pstn-correlation-id';
|
|
6705
|
-
|
|
6902
|
+
|
|
6706
6903
|
await meeting.disconnectPhoneAudio();
|
|
6707
|
-
|
|
6904
|
+
|
|
6708
6905
|
// Verify that pstnCorrelationId is still cleared even when no phone connection is active
|
|
6709
6906
|
assert.equal(meeting.pstnCorrelationId, undefined);
|
|
6710
|
-
|
|
6907
|
+
// And verify no disconnect was attempted
|
|
6711
6908
|
assert.notCalled(MeetingUtil.disconnectPhoneAudio);
|
|
6712
6909
|
});
|
|
6713
6910
|
});
|
|
@@ -7462,6 +7659,8 @@ describe('plugin-meetings', () => {
|
|
|
7462
7659
|
'locus-id',
|
|
7463
7660
|
{extraParam1: 'value1', permissionToken: FAKE_PERMISSION_TOKEN},
|
|
7464
7661
|
{meetingId: meeting.id, sendCAevents: true},
|
|
7662
|
+
null,
|
|
7663
|
+
null,
|
|
7465
7664
|
null
|
|
7466
7665
|
);
|
|
7467
7666
|
assert.deepEqual(meeting.meetingInfo, {
|
|
@@ -7508,6 +7707,8 @@ describe('plugin-meetings', () => {
|
|
|
7508
7707
|
'locus-id',
|
|
7509
7708
|
{extraParam1: 'value1', permissionToken: FAKE_PERMISSION_TOKEN},
|
|
7510
7709
|
{meetingId: meeting.id, sendCAevents: true},
|
|
7710
|
+
null,
|
|
7711
|
+
null,
|
|
7511
7712
|
null
|
|
7512
7713
|
);
|
|
7513
7714
|
assert.deepEqual(meeting.meetingInfo, {
|
|
@@ -7563,6 +7764,8 @@ describe('plugin-meetings', () => {
|
|
|
7563
7764
|
permissionToken: FAKE_PERMISSION_TOKEN,
|
|
7564
7765
|
},
|
|
7565
7766
|
{meetingId: meeting.id, sendCAevents: true},
|
|
7767
|
+
null,
|
|
7768
|
+
null,
|
|
7566
7769
|
null
|
|
7567
7770
|
);
|
|
7568
7771
|
assert.deepEqual(meeting.meetingInfo, {
|
|
@@ -9004,11 +9207,16 @@ describe('plugin-meetings', () => {
|
|
|
9004
9207
|
meeting.hasMediaConnectionConnectedAtLeastOnce = false;
|
|
9005
9208
|
meeting.setupMediaConnectionListeners();
|
|
9006
9209
|
|
|
9210
|
+
sinon.stub(MeetingUtil, 'getCaEventLabelsForIpVersion').returns(['fake labels']);
|
|
9211
|
+
|
|
9007
9212
|
simulateConnectionStateChange(ConnectionState.Connecting);
|
|
9008
9213
|
|
|
9009
9214
|
assert.calledOnce(webex.internal.newMetrics.submitClientEvent);
|
|
9010
9215
|
assert.calledWithMatch(webex.internal.newMetrics.submitClientEvent, {
|
|
9011
9216
|
name: 'client.ice.start',
|
|
9217
|
+
payload: {
|
|
9218
|
+
labels: ['fake labels'],
|
|
9219
|
+
},
|
|
9012
9220
|
options: {
|
|
9013
9221
|
meetingId: meeting.id,
|
|
9014
9222
|
},
|
|
@@ -10273,6 +10481,24 @@ describe('plugin-meetings', () => {
|
|
|
10273
10481
|
);
|
|
10274
10482
|
});
|
|
10275
10483
|
|
|
10484
|
+
it('listens to CONTROLS_AUTO_END_MEETING_WARNING_CHANGED', async () => {
|
|
10485
|
+
const state = {example: 'value'};
|
|
10486
|
+
|
|
10487
|
+
await meeting.locusInfo.emitScoped(
|
|
10488
|
+
{function: 'test', file: 'test'},
|
|
10489
|
+
LOCUSINFO.EVENTS.CONTROLS_AUTO_END_MEETING_WARNING_CHANGED,
|
|
10490
|
+
{state}
|
|
10491
|
+
);
|
|
10492
|
+
|
|
10493
|
+
assert.calledWith(
|
|
10494
|
+
TriggerProxy.trigger,
|
|
10495
|
+
meeting,
|
|
10496
|
+
{file: 'meeting/index', function: 'setupLocusControlsListener'},
|
|
10497
|
+
EVENT_TRIGGERS.MEETING_CONTROLS_AUTO_END_MEETING_WARNING_UPDATED,
|
|
10498
|
+
{state}
|
|
10499
|
+
);
|
|
10500
|
+
});
|
|
10501
|
+
|
|
10276
10502
|
it('listens to CONTROLS_REMOTE_DESKTOP_CONTROL_CHANGED', async () => {
|
|
10277
10503
|
const state = {example: 'value'};
|
|
10278
10504
|
|
|
@@ -10352,6 +10578,7 @@ describe('plugin-meetings', () => {
|
|
|
10352
10578
|
describe('#setUpLocusUrlListener', () => {
|
|
10353
10579
|
it('listens to the locus url update event', (done) => {
|
|
10354
10580
|
const newLocusUrl = 'newLocusUrl/12345';
|
|
10581
|
+
const payload = {url: newLocusUrl};
|
|
10355
10582
|
|
|
10356
10583
|
meeting.members = {locusUrlUpdate: sinon.stub().returns(Promise.resolve(test1))};
|
|
10357
10584
|
meeting.recordingController = {setLocusUrl: sinon.stub().returns(undefined)};
|
|
@@ -10365,14 +10592,14 @@ describe('plugin-meetings', () => {
|
|
|
10365
10592
|
meeting.locusInfo.emit(
|
|
10366
10593
|
{function: 'test', file: 'test'},
|
|
10367
10594
|
'LOCUS_INFO_UPDATE_URL',
|
|
10368
|
-
|
|
10595
|
+
payload
|
|
10369
10596
|
);
|
|
10370
10597
|
assert.calledWith(meeting.members.locusUrlUpdate, newLocusUrl);
|
|
10371
10598
|
assert.calledOnceWithExactly(meeting.breakouts.locusUrlUpdate, newLocusUrl);
|
|
10372
10599
|
assert.calledOnceWithExactly(meeting.annotation.locusUrlUpdate, newLocusUrl);
|
|
10373
10600
|
assert.calledWith(meeting.members.locusUrlUpdate, newLocusUrl);
|
|
10374
10601
|
assert.calledWith(meeting.recordingController.setLocusUrl, newLocusUrl);
|
|
10375
|
-
assert.calledWith(meeting.controlsOptionsManager.setLocusUrl, newLocusUrl);
|
|
10602
|
+
assert.calledWith(meeting.controlsOptionsManager.setLocusUrl, newLocusUrl, false);
|
|
10376
10603
|
assert.calledWith(meeting.simultaneousInterpretation.locusUrlUpdate, newLocusUrl);
|
|
10377
10604
|
assert.calledWith(meeting.webinar.locusUrlUpdate, newLocusUrl);
|
|
10378
10605
|
assert.equal(meeting.locusUrl, newLocusUrl);
|
|
@@ -10390,6 +10617,22 @@ describe('plugin-meetings', () => {
|
|
|
10390
10617
|
{locusUrl: 'newLocusUrl/12345'}
|
|
10391
10618
|
);
|
|
10392
10619
|
|
|
10620
|
+
done();
|
|
10621
|
+
});
|
|
10622
|
+
it('update mainLocusUrl for controlsOptionManager if payload.isMainLocus as true', (done) => {
|
|
10623
|
+
const newLocusUrl = 'newLocusUrl/12345';
|
|
10624
|
+
const payload = {url: newLocusUrl, isMainLocus: true};
|
|
10625
|
+
|
|
10626
|
+
meeting.controlsOptionsManager = {setLocusUrl: sinon.stub().returns(undefined)};
|
|
10627
|
+
|
|
10628
|
+
meeting.locusInfo.emit(
|
|
10629
|
+
{function: 'test', file: 'test'},
|
|
10630
|
+
'LOCUS_INFO_UPDATE_URL',
|
|
10631
|
+
payload
|
|
10632
|
+
);
|
|
10633
|
+
|
|
10634
|
+
assert.calledWith(meeting.controlsOptionsManager.setLocusUrl, newLocusUrl, true);
|
|
10635
|
+
|
|
10393
10636
|
done();
|
|
10394
10637
|
});
|
|
10395
10638
|
});
|
|
@@ -10610,7 +10853,9 @@ describe('plugin-meetings', () => {
|
|
|
10610
10853
|
meeting.meetingRequest.changeMeetingFloor = sinon.stub().returns(Promise.resolve());
|
|
10611
10854
|
(meeting.deviceUrl = 'deviceUrl.com'), (meeting.localShareInstanceId = '1234-5678');
|
|
10612
10855
|
webex.internal.newMetrics.callDiagnosticLatencies.saveTimestamp = sinon.stub();
|
|
10613
|
-
webex.internal.newMetrics.callDiagnosticLatencies.getShareDuration = sinon
|
|
10856
|
+
webex.internal.newMetrics.callDiagnosticLatencies.getShareDuration = sinon
|
|
10857
|
+
.stub()
|
|
10858
|
+
.returns(1000);
|
|
10614
10859
|
});
|
|
10615
10860
|
it('should call changeMeetingFloor()', async () => {
|
|
10616
10861
|
meeting.screenShareFloorState = 'GRANTED';
|
|
@@ -11227,6 +11472,8 @@ describe('plugin-meetings', () => {
|
|
|
11227
11472
|
let canUserRenameOthersSpy;
|
|
11228
11473
|
let canShareWhiteBoardSpy;
|
|
11229
11474
|
let canMoveToLobbySpy;
|
|
11475
|
+
let isSpokenLanguageAutoDetectionEnabledSpy;
|
|
11476
|
+
let showAutoEndMeetingWarningSpy;
|
|
11230
11477
|
// Due to import tree issues, hasHints must be stubed within the scope of the `it`.
|
|
11231
11478
|
|
|
11232
11479
|
beforeEach(() => {
|
|
@@ -11258,11 +11505,17 @@ describe('plugin-meetings', () => {
|
|
|
11258
11505
|
canUserRenameOthersSpy = sinon.spy(MeetingUtil, 'canUserRenameOthers');
|
|
11259
11506
|
canShareWhiteBoardSpy = sinon.spy(MeetingUtil, 'canShareWhiteBoard');
|
|
11260
11507
|
canMoveToLobbySpy = sinon.spy(MeetingUtil, 'canMoveToLobby');
|
|
11508
|
+
showAutoEndMeetingWarningSpy = sinon.spy(MeetingUtil, 'showAutoEndMeetingWarning');
|
|
11509
|
+
isSpokenLanguageAutoDetectionEnabledSpy = sinon.spy(
|
|
11510
|
+
MeetingUtil,
|
|
11511
|
+
'isSpokenLanguageAutoDetectionEnabled'
|
|
11512
|
+
);
|
|
11261
11513
|
});
|
|
11262
11514
|
|
|
11263
11515
|
afterEach(() => {
|
|
11264
11516
|
inMeetingActionsSetSpy.restore();
|
|
11265
11517
|
waitingForOthersToJoinSpy.restore();
|
|
11518
|
+
showAutoEndMeetingWarningSpy.restore();
|
|
11266
11519
|
});
|
|
11267
11520
|
|
|
11268
11521
|
forEach(
|
|
@@ -11810,6 +12063,8 @@ describe('plugin-meetings', () => {
|
|
|
11810
12063
|
assert.calledWith(canUserRenameOthersSpy, userDisplayHints);
|
|
11811
12064
|
assert.calledWith(canShareWhiteBoardSpy, userDisplayHints, selfUserPolicies);
|
|
11812
12065
|
assert.calledWith(canMoveToLobbySpy, userDisplayHints);
|
|
12066
|
+
assert.calledWith(showAutoEndMeetingWarningSpy, userDisplayHints);
|
|
12067
|
+
assert.calledWith(isSpokenLanguageAutoDetectionEnabledSpy, userDisplayHints);
|
|
11813
12068
|
|
|
11814
12069
|
assert.calledWith(ControlsOptionsUtil.hasHints, {
|
|
11815
12070
|
requiredHints: [DISPLAY_HINTS.MUTE_ALL],
|
|
@@ -12261,7 +12516,10 @@ describe('plugin-meetings', () => {
|
|
|
12261
12516
|
meeting.meetingRequest.changeMeetingFloor = sinon.stub().returns(Promise.resolve());
|
|
12262
12517
|
meeting.deviceUrl = 'deviceUrl.com';
|
|
12263
12518
|
webex.internal.newMetrics.callDiagnosticLatencies.saveTimestamp = sinon.stub();
|
|
12264
|
-
webex.internal.newMetrics.callDiagnosticLatencies.getShareDuration = sinon
|
|
12519
|
+
webex.internal.newMetrics.callDiagnosticLatencies.getShareDuration = sinon
|
|
12520
|
+
.stub()
|
|
12521
|
+
.returns(1000);
|
|
12522
|
+
webex.internal.newMetrics.submitClientEvent = sinon.stub();
|
|
12265
12523
|
});
|
|
12266
12524
|
it('should stop the whiteboard share', async () => {
|
|
12267
12525
|
const whiteboardShare = meeting.stopWhiteboardShare();
|
|
@@ -12363,6 +12621,11 @@ describe('plugin-meetings', () => {
|
|
|
12363
12621
|
meeting.selfId = '9528d952-e4de-46cf-8157-fd4823b98377';
|
|
12364
12622
|
meeting.deviceUrl = 'my-web-url';
|
|
12365
12623
|
meeting.locusInfo.info = {isWebinar: false};
|
|
12624
|
+
webex.internal.newMetrics.callDiagnosticLatencies.saveTimestamp = sinon.stub();
|
|
12625
|
+
webex.internal.newMetrics.callDiagnosticLatencies.getShareDuration = sinon
|
|
12626
|
+
.stub()
|
|
12627
|
+
.returns(1500);
|
|
12628
|
+
webex.internal.newMetrics.submitClientEvent = sinon.stub();
|
|
12366
12629
|
});
|
|
12367
12630
|
|
|
12368
12631
|
const USER_IDS = {
|
|
@@ -12594,7 +12857,7 @@ describe('plugin-meetings', () => {
|
|
|
12594
12857
|
eventName: EVENT_TRIGGERS.MEETING_STARTED_SHARING_REMOTE,
|
|
12595
12858
|
functionName: 'remoteShare',
|
|
12596
12859
|
eventPayload: {
|
|
12597
|
-
memberId: null,
|
|
12860
|
+
memberId: meeting.webinar.selfIsAttendee ? beneficiaryId : null,
|
|
12598
12861
|
url,
|
|
12599
12862
|
shareInstanceId,
|
|
12600
12863
|
annotationInfo: undefined,
|
|
@@ -12610,8 +12873,8 @@ describe('plugin-meetings', () => {
|
|
|
12610
12873
|
|
|
12611
12874
|
shareStatus =
|
|
12612
12875
|
meeting.webinar.selfIsAttendee || meeting.guest
|
|
12613
|
-
|
|
12614
|
-
|
|
12876
|
+
? SHARE_STATUS.REMOTE_SHARE_ACTIVE
|
|
12877
|
+
: SHARE_STATUS.WHITEBOARD_SHARE_ACTIVE;
|
|
12615
12878
|
}
|
|
12616
12879
|
|
|
12617
12880
|
if (eventTrigger.member) {
|
|
@@ -12649,7 +12912,7 @@ describe('plugin-meetings', () => {
|
|
|
12649
12912
|
eventName: EVENT_TRIGGERS.MEETING_STARTED_SHARING_REMOTE,
|
|
12650
12913
|
functionName: 'remoteShare',
|
|
12651
12914
|
eventPayload: {
|
|
12652
|
-
memberId:
|
|
12915
|
+
memberId: beneficiaryId,
|
|
12653
12916
|
url,
|
|
12654
12917
|
shareInstanceId,
|
|
12655
12918
|
annotationInfo: undefined,
|
|
@@ -13508,30 +13771,78 @@ describe('plugin-meetings', () => {
|
|
|
13508
13771
|
payloadTestHelper([data1, data2, data3]);
|
|
13509
13772
|
});
|
|
13510
13773
|
});
|
|
13511
|
-
});
|
|
13512
13774
|
|
|
13513
|
-
|
|
13514
|
-
|
|
13515
|
-
|
|
13516
|
-
|
|
13517
|
-
|
|
13518
|
-
|
|
13519
|
-
|
|
13775
|
+
it('should send share stopped metric when whiteboard sharing stops', () => {
|
|
13776
|
+
// Start whiteboard sharing (this won't trigger metrics)
|
|
13777
|
+
const data1 = generateData(
|
|
13778
|
+
blankPayload,
|
|
13779
|
+
true, // isGranting: true
|
|
13780
|
+
false, // isContent: false (whiteboard)
|
|
13781
|
+
USER_IDS.ME,
|
|
13782
|
+
RESOURCE_URLS.WHITEBOARD_A
|
|
13783
|
+
);
|
|
13784
|
+
|
|
13785
|
+
// Stop whiteboard sharing (this should trigger metrics)
|
|
13786
|
+
const data2 = generateData(
|
|
13787
|
+
data1.payload,
|
|
13788
|
+
false, // isGranting: false (stopping share)
|
|
13789
|
+
false, // isContent: false (whiteboard)
|
|
13790
|
+
USER_IDS.ME
|
|
13791
|
+
);
|
|
13520
13792
|
|
|
13521
|
-
|
|
13793
|
+
// Trigger the events
|
|
13794
|
+
meeting.locusInfo.emit(
|
|
13795
|
+
{function: 'test', file: 'test'},
|
|
13796
|
+
EVENTS.LOCUS_INFO_UPDATE_MEDIA_SHARES,
|
|
13797
|
+
data1.payload
|
|
13798
|
+
);
|
|
13522
13799
|
|
|
13523
|
-
|
|
13524
|
-
|
|
13525
|
-
|
|
13526
|
-
|
|
13527
|
-
correlationId: meeting.correlationId,
|
|
13528
|
-
muted: true,
|
|
13529
|
-
encoderImplementation: 'OpenH264',
|
|
13530
|
-
displaySurface: 'monitor',
|
|
13531
|
-
isMultistream: true,
|
|
13532
|
-
frameRate: 30,
|
|
13533
|
-
}
|
|
13800
|
+
meeting.locusInfo.emit(
|
|
13801
|
+
{function: 'test', file: 'test'},
|
|
13802
|
+
EVENTS.LOCUS_INFO_UPDATE_MEDIA_SHARES,
|
|
13803
|
+
data2.payload
|
|
13534
13804
|
);
|
|
13805
|
+
|
|
13806
|
+
// Verify metrics were called when whiteboard sharing stopped
|
|
13807
|
+
assert.calledWith(webex.internal.newMetrics.callDiagnosticLatencies.saveTimestamp, {
|
|
13808
|
+
key: 'internal.client.share.stopped',
|
|
13809
|
+
});
|
|
13810
|
+
|
|
13811
|
+
assert.calledWith(webex.internal.newMetrics.submitClientEvent, {
|
|
13812
|
+
name: 'client.share.stopped',
|
|
13813
|
+
payload: {
|
|
13814
|
+
mediaType: 'whiteboard',
|
|
13815
|
+
shareDuration: 1500, // mocked return value
|
|
13816
|
+
},
|
|
13817
|
+
options: {
|
|
13818
|
+
meetingId: meeting.id,
|
|
13819
|
+
},
|
|
13820
|
+
});
|
|
13821
|
+
});
|
|
13822
|
+
|
|
13823
|
+
describe('handleShareVideoStreamMuteStateChange', () => {
|
|
13824
|
+
it('should emit MEETING_SHARE_VIDEO_MUTE_STATE_CHANGE event with correct fields', () => {
|
|
13825
|
+
meeting.isMultistream = true;
|
|
13826
|
+
meeting.statsAnalyzer = {shareVideoEncoderImplementation: 'OpenH264'};
|
|
13827
|
+
meeting.mediaProperties.shareVideoStream = {
|
|
13828
|
+
getSettings: sinon.stub().returns({displaySurface: 'monitor', frameRate: 30}),
|
|
13829
|
+
};
|
|
13830
|
+
|
|
13831
|
+
meeting.handleShareVideoStreamMuteStateChange(true);
|
|
13832
|
+
|
|
13833
|
+
assert.calledOnceWithExactly(
|
|
13834
|
+
Metrics.sendBehavioralMetric,
|
|
13835
|
+
BEHAVIORAL_METRICS.MEETING_SHARE_VIDEO_MUTE_STATE_CHANGE,
|
|
13836
|
+
{
|
|
13837
|
+
correlationId: meeting.correlationId,
|
|
13838
|
+
muted: true,
|
|
13839
|
+
encoderImplementation: 'OpenH264',
|
|
13840
|
+
displaySurface: 'monitor',
|
|
13841
|
+
isMultistream: true,
|
|
13842
|
+
frameRate: 30,
|
|
13843
|
+
}
|
|
13844
|
+
);
|
|
13845
|
+
});
|
|
13535
13846
|
});
|
|
13536
13847
|
});
|
|
13537
13848
|
});
|
|
@@ -14733,11 +15044,81 @@ describe('plugin-meetings', () => {
|
|
|
14733
15044
|
assert.exists(unsetStagePromise.then);
|
|
14734
15045
|
await unsetStagePromise;
|
|
14735
15046
|
|
|
15047
|
+
assert.calledOnceWithExactly(meeting.meetingRequest.synchronizeStage, locusUrl, {
|
|
15048
|
+
overrideDefault: false,
|
|
15049
|
+
});
|
|
15050
|
+
});
|
|
15051
|
+
});
|
|
15052
|
+
|
|
15053
|
+
describe('#notifyHost', () => {
|
|
15054
|
+
beforeEach(() => {
|
|
15055
|
+
meeting.meetingRequest.notifyHost = sinon.stub().returns(Promise.resolve());
|
|
15056
|
+
});
|
|
15057
|
+
|
|
15058
|
+
it('sends the expected request', async () => {
|
|
15059
|
+
meeting.meetingInfo.siteFullUrl = `convergedats.webex.com`;
|
|
15060
|
+
const meetingUuid = 'meeting-uuid';
|
|
15061
|
+
const displayName = ['Test', 'User'];
|
|
15062
|
+
meeting.locusId = 'locusId';
|
|
15063
|
+
|
|
15064
|
+
const notifyHostPromise = meeting.notifyHost(meetingUuid, displayName);
|
|
15065
|
+
|
|
15066
|
+
assert.exists(notifyHostPromise.then);
|
|
15067
|
+
await notifyHostPromise;
|
|
15068
|
+
|
|
14736
15069
|
assert.calledOnceWithExactly(
|
|
14737
|
-
meeting.meetingRequest.
|
|
14738
|
-
|
|
14739
|
-
|
|
15070
|
+
meeting.meetingRequest.notifyHost,
|
|
15071
|
+
meeting.meetingInfo.siteFullUrl,
|
|
15072
|
+
meeting.locusId,
|
|
15073
|
+
meetingUuid,
|
|
15074
|
+
displayName
|
|
15075
|
+
);
|
|
15076
|
+
});
|
|
15077
|
+
});
|
|
15078
|
+
|
|
15079
|
+
describe('#sipCallOut', () => {
|
|
15080
|
+
beforeEach(() => {
|
|
15081
|
+
meeting.meetingRequest.sipCallOut = sinon.stub().returns(Promise.resolve({body: {}}));
|
|
15082
|
+
});
|
|
15083
|
+
|
|
15084
|
+
it('sends the expected request', async () => {
|
|
15085
|
+
const address = 'sip:user@example.com';
|
|
15086
|
+
const displayName = 'John Doe';
|
|
15087
|
+
const meetingId = 'a643beaa47f04eedac08f1310ca12366';
|
|
15088
|
+
|
|
15089
|
+
meeting.meetingInfo = {
|
|
15090
|
+
meetingId,
|
|
15091
|
+
};
|
|
15092
|
+
|
|
15093
|
+
const sipCallOutPromise = meeting.sipCallOut(address, displayName);
|
|
15094
|
+
|
|
15095
|
+
assert.exists(sipCallOutPromise.then);
|
|
15096
|
+
await sipCallOutPromise;
|
|
15097
|
+
|
|
15098
|
+
assert.calledOnceWithExactly(
|
|
15099
|
+
meeting.meetingRequest.sipCallOut,
|
|
15100
|
+
meetingId,
|
|
15101
|
+
meetingId,
|
|
15102
|
+
address,
|
|
15103
|
+
displayName
|
|
14740
15104
|
);
|
|
14741
15105
|
});
|
|
14742
15106
|
});
|
|
15107
|
+
|
|
15108
|
+
describe('#cancelSipCallOut', () => {
|
|
15109
|
+
beforeEach(() => {
|
|
15110
|
+
meeting.meetingRequest.cancelSipCallOut = sinon.stub().returns(Promise.resolve({body: {}}));
|
|
15111
|
+
});
|
|
15112
|
+
|
|
15113
|
+
it('sends the expected request', async () => {
|
|
15114
|
+
const participantId = '12345-abcde';
|
|
15115
|
+
|
|
15116
|
+
const cancelSipCallOutPromise = meeting.cancelSipCallOut(participantId);
|
|
15117
|
+
|
|
15118
|
+
assert.exists(cancelSipCallOutPromise.then);
|
|
15119
|
+
await cancelSipCallOutPromise;
|
|
15120
|
+
|
|
15121
|
+
assert.calledOnceWithExactly(meeting.meetingRequest.cancelSipCallOut, participantId);
|
|
15122
|
+
});
|
|
15123
|
+
});
|
|
14743
15124
|
});
|