@webex/plugin-meetings 3.9.0-next.2 → 3.9.0-next.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/breakouts/breakout.js +1 -1
- package/dist/breakouts/index.js +1 -1
- package/dist/constants.js +2 -0
- package/dist/constants.js.map +1 -1
- package/dist/interpretation/index.js +1 -1
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/locus-info/index.js +38 -10
- package/dist/locus-info/index.js.map +1 -1
- package/dist/locus-info/parser.js +4 -1
- package/dist/locus-info/parser.js.map +1 -1
- package/dist/media/properties.js +53 -5
- package/dist/media/properties.js.map +1 -1
- package/dist/meeting/in-meeting-actions.js +2 -0
- package/dist/meeting/in-meeting-actions.js.map +1 -1
- package/dist/meeting/index.js +189 -122
- 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 +25 -0
- package/dist/meeting/request.js.map +1 -1
- package/dist/meeting/util.js +30 -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/types.js.map +1 -1
- package/dist/members/collection.js +13 -0
- package/dist/members/collection.js.map +1 -1
- package/dist/members/index.js +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/constants.d.ts +2 -0
- package/dist/types/locus-info/index.d.ts +54 -1
- package/dist/types/media/properties.d.ts +21 -0
- package/dist/types/meeting/in-meeting-actions.d.ts +2 -0
- package/dist/types/meeting/index.d.ts +11 -1
- package/dist/types/meeting/request.d.ts +9 -0
- package/dist/types/meeting/util.d.ts +10 -3
- package/dist/types/meeting-info/meeting-info-v2.d.ts +6 -3
- package/dist/types/meetings/index.d.ts +3 -1
- package/dist/types/member/types.d.ts +1 -0
- package/dist/types/members/collection.d.ts +6 -0
- package/dist/types/members/index.d.ts +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 +16 -16
- package/src/constants.ts +2 -0
- package/src/locus-info/index.ts +84 -9
- package/src/locus-info/parser.ts +5 -1
- package/src/media/properties.ts +43 -0
- package/src/meeting/in-meeting-actions.ts +4 -0
- package/src/meeting/index.ts +91 -4
- package/src/meeting/muteState.ts +2 -6
- package/src/meeting/request.ts +23 -0
- package/src/meeting/util.ts +41 -20
- package/src/meeting-info/meeting-info-v2.ts +24 -5
- package/src/meetings/index.ts +9 -3
- package/src/member/types.ts +1 -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/locus-info/index.js +30 -15
- package/test/unit/spec/locus-info/parser.js +3 -2
- package/test/unit/spec/media/properties.ts +137 -0
- package/test/unit/spec/meeting/in-meeting-actions.ts +2 -0
- package/test/unit/spec/meeting/index.js +255 -27
- 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 +45 -16
- package/test/unit/spec/meeting-info/meetinginfov2.js +8 -3
- package/test/unit/spec/meetings/index.js +10 -1
- 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
@@ -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,
|
@@ -487,7 +488,7 @@ describe('plugin-meetings', () => {
|
|
487
488
|
|
488
489
|
it('pstnCorrelationId getter/setter should work correctly', () => {
|
489
490
|
const testPstnCorrelationId = uuid.v4();
|
490
|
-
|
491
|
+
|
491
492
|
meeting.pstnCorrelationId = testPstnCorrelationId;
|
492
493
|
assert.equal(meeting.pstnCorrelationId, testPstnCorrelationId);
|
493
494
|
assert.equal(meeting.callStateForMetrics.pstnCorrelationId, testPstnCorrelationId);
|
@@ -1992,10 +1993,10 @@ describe('plugin-meetings', () => {
|
|
1992
1993
|
it('should handle join failure', async () => {
|
1993
1994
|
MeetingUtil.isPinOrGuest = sinon.stub().returns(false);
|
1994
1995
|
webex.internal.newMetrics.submitClientEvent = sinon.stub();
|
1995
|
-
|
1996
|
+
|
1996
1997
|
await meeting.join().catch(() => {
|
1997
1998
|
assert.calledOnce(MeetingUtil.joinMeeting);
|
1998
|
-
|
1999
|
+
|
1999
2000
|
// Assert that client.locus.join.response error event is not sent from this function, it is now emitted from MeetingUtil.joinMeeting
|
2000
2001
|
assert.calledOnce(webex.internal.newMetrics.submitClientEvent);
|
2001
2002
|
assert.calledWithMatch(
|
@@ -2216,6 +2217,7 @@ describe('plugin-meetings', () => {
|
|
2216
2217
|
});
|
2217
2218
|
meeting.audio = muteStateStub;
|
2218
2219
|
meeting.video = muteStateStub;
|
2220
|
+
sinon.stub(MeetingUtil, 'getIpVersion').returns(IP_VERSION.ipv4_and_ipv6);
|
2219
2221
|
sinon.stub(Media, 'createMediaConnection').returns(fakeMediaConnection);
|
2220
2222
|
sinon.stub(meeting, 'setupMediaConnectionListeners');
|
2221
2223
|
sinon.stub(meeting, 'setMercuryListener');
|
@@ -2287,13 +2289,24 @@ describe('plugin-meetings', () => {
|
|
2287
2289
|
close: sinon.stub(),
|
2288
2290
|
forceRtcMetricsSend,
|
2289
2291
|
});
|
2290
|
-
|
2292
|
+
|
2293
|
+
const mockStatsMonitor = {removeAllListeners: sinon.stub()};
|
2294
|
+
const mockNetworkQualityMonitor = {removeAllListeners: sinon.stub()};
|
2295
|
+
|
2296
|
+
// set a statsAnalyzer and statsMonitor on the meeting so that we can check that they get reset to null
|
2291
2297
|
meeting.statsAnalyzer = {stopAnalyzer: sinon.stub().resolves()};
|
2298
|
+
meeting.statsMonitor = mockStatsMonitor;
|
2299
|
+
meeting.networkQualityMonitor = mockNetworkQualityMonitor;
|
2292
2300
|
const error = await assert.isRejected(meeting.addMedia());
|
2293
2301
|
|
2294
2302
|
assert.calledOnce(forceRtcMetricsSend);
|
2303
|
+
assert.calledOnce(mockStatsMonitor.removeAllListeners);
|
2304
|
+
assert.calledOnce(mockNetworkQualityMonitor.removeAllListeners);
|
2295
2305
|
|
2296
2306
|
assert.isNull(meeting.statsAnalyzer);
|
2307
|
+
assert.isNull(meeting.statsMonitor);
|
2308
|
+
assert.isNull(meeting.networkQualityMonitor);
|
2309
|
+
|
2297
2310
|
assert(webex.internal.newMetrics.submitInternalEvent.calledTwice);
|
2298
2311
|
assert.calledWith(webex.internal.newMetrics.submitInternalEvent.firstCall, {
|
2299
2312
|
name: 'internal.client.add-media.turn-discovery.start',
|
@@ -2337,6 +2350,7 @@ describe('plugin-meetings', () => {
|
|
2337
2350
|
selected_subnet: null,
|
2338
2351
|
numTransports: 1,
|
2339
2352
|
iceCandidatesCount: 0,
|
2353
|
+
ipver: 1,
|
2340
2354
|
}
|
2341
2355
|
);
|
2342
2356
|
});
|
@@ -2384,6 +2398,7 @@ describe('plugin-meetings', () => {
|
|
2384
2398
|
subnet_reachable: null,
|
2385
2399
|
selected_cluster: null,
|
2386
2400
|
selected_subnet: null,
|
2401
|
+
ipver: 1,
|
2387
2402
|
})
|
2388
2403
|
);
|
2389
2404
|
|
@@ -2403,12 +2418,23 @@ describe('plugin-meetings', () => {
|
|
2403
2418
|
|
2404
2419
|
meeting.waitForRemoteSDPAnswer = sinon.stub().rejects();
|
2405
2420
|
|
2406
|
-
|
2421
|
+
const mockStatsMonitor = {removeAllListeners: sinon.stub()};
|
2422
|
+
const mockNetworkQualityMonitor = {removeAllListeners: sinon.stub()};
|
2423
|
+
|
2424
|
+
// set a statsAnalyzer and statsMonitor on the meeting so that we can check that they get reset to null
|
2407
2425
|
meeting.statsAnalyzer = {stopAnalyzer: sinon.stub().resolves()};
|
2426
|
+
meeting.statsMonitor = mockStatsMonitor;
|
2427
|
+
meeting.networkQualityMonitor = mockNetworkQualityMonitor;
|
2408
2428
|
|
2409
2429
|
const error = await assert.isRejected(meeting.addMedia());
|
2410
2430
|
|
2411
2431
|
assert.isNull(meeting.statsAnalyzer);
|
2432
|
+
assert.isNull(meeting.statsMonitor);
|
2433
|
+
assert.isNull(meeting.networkQualityMonitor);
|
2434
|
+
|
2435
|
+
assert.calledOnce(mockStatsMonitor.removeAllListeners);
|
2436
|
+
assert.calledOnce(mockNetworkQualityMonitor.removeAllListeners);
|
2437
|
+
|
2412
2438
|
assert(webex.internal.newMetrics.submitInternalEvent.calledTwice);
|
2413
2439
|
assert.calledWith(webex.internal.newMetrics.submitInternalEvent.firstCall, {
|
2414
2440
|
name: 'internal.client.add-media.turn-discovery.start',
|
@@ -2452,6 +2478,7 @@ describe('plugin-meetings', () => {
|
|
2452
2478
|
subnet_reachable: null,
|
2453
2479
|
selected_cluster: null,
|
2454
2480
|
selected_subnet: null,
|
2481
|
+
ipver: 1,
|
2455
2482
|
}
|
2456
2483
|
);
|
2457
2484
|
});
|
@@ -2472,8 +2499,9 @@ describe('plugin-meetings', () => {
|
|
2472
2499
|
},
|
2473
2500
|
},
|
2474
2501
|
});
|
2475
|
-
// set a statsAnalyzer on the meeting so that we can check that
|
2502
|
+
// set a statsAnalyzer and statsMonitor on the meeting so that we can check that they get reset to null
|
2476
2503
|
meeting.statsAnalyzer = {stopAnalyzer: sinon.stub().resolves()};
|
2504
|
+
meeting.statsMonitor = {removeAllListeners: sinon.stub()};
|
2477
2505
|
const error = await assert.isRejected(meeting.addMedia());
|
2478
2506
|
|
2479
2507
|
assert(webex.internal.newMetrics.submitInternalEvent.calledTwice);
|
@@ -2512,10 +2540,12 @@ describe('plugin-meetings', () => {
|
|
2512
2540
|
subnet_reachable: null,
|
2513
2541
|
selected_cluster: null,
|
2514
2542
|
selected_subnet: null,
|
2543
|
+
ipver: 1,
|
2515
2544
|
})
|
2516
2545
|
);
|
2517
2546
|
|
2518
2547
|
assert.isNull(meeting.statsAnalyzer);
|
2548
|
+
assert.isNull(meeting.statsMonitor);
|
2519
2549
|
});
|
2520
2550
|
|
2521
2551
|
it('should include the peer connection properties correctly for transcoded', async () => {
|
@@ -2532,8 +2562,14 @@ describe('plugin-meetings', () => {
|
|
2532
2562
|
},
|
2533
2563
|
},
|
2534
2564
|
});
|
2535
|
-
|
2565
|
+
|
2566
|
+
const mockStatsMonitor = {removeAllListeners: sinon.stub()};
|
2567
|
+
const mockNetworkQualityMonitor = {removeAllListeners: sinon.stub()};
|
2568
|
+
|
2569
|
+
// set a statsAnalyzer and statsMonitor on the meeting so that we can check that they get reset to null
|
2536
2570
|
meeting.statsAnalyzer = {stopAnalyzer: sinon.stub().resolves()};
|
2571
|
+
meeting.statsMonitor = mockStatsMonitor;
|
2572
|
+
meeting.networkQualityMonitor = mockNetworkQualityMonitor;
|
2537
2573
|
const error = await assert.isRejected(meeting.addMedia());
|
2538
2574
|
|
2539
2575
|
assert(webex.internal.newMetrics.submitInternalEvent.calledTwice);
|
@@ -2572,10 +2608,15 @@ describe('plugin-meetings', () => {
|
|
2572
2608
|
subnet_reachable: null,
|
2573
2609
|
selected_cluster: null,
|
2574
2610
|
selected_subnet: null,
|
2611
|
+
ipver: 1,
|
2575
2612
|
})
|
2576
2613
|
);
|
2577
2614
|
|
2578
2615
|
assert.isNull(meeting.statsAnalyzer);
|
2616
|
+
assert.isNull(meeting.statsMonitor);
|
2617
|
+
assert.isNull(meeting.networkQualityMonitor);
|
2618
|
+
assert.calledOnce(mockStatsMonitor.removeAllListeners);
|
2619
|
+
assert.calledOnce(mockNetworkQualityMonitor.removeAllListeners);
|
2579
2620
|
});
|
2580
2621
|
|
2581
2622
|
it('should work the second time addMedia is called in case the first time fails', async () => {
|
@@ -3096,6 +3137,7 @@ describe('plugin-meetings', () => {
|
|
3096
3137
|
subnet_reachable: null,
|
3097
3138
|
selected_cluster: null,
|
3098
3139
|
selected_subnet: null,
|
3140
|
+
ipver: 1,
|
3099
3141
|
},
|
3100
3142
|
]);
|
3101
3143
|
|
@@ -3297,6 +3339,7 @@ describe('plugin-meetings', () => {
|
|
3297
3339
|
connectionType: 'udp',
|
3298
3340
|
selectedCandidatePairChanges: 2,
|
3299
3341
|
ipVersion: 'IPv6',
|
3342
|
+
ipver: 1,
|
3300
3343
|
numTransports: 1,
|
3301
3344
|
isMultistream: false,
|
3302
3345
|
retriedWithTurnServer: true,
|
@@ -3443,6 +3486,7 @@ describe('plugin-meetings', () => {
|
|
3443
3486
|
meeting.iceCandidatesCount = 3;
|
3444
3487
|
meeting.iceCandidateErrors.set('701_error', 3);
|
3445
3488
|
meeting.iceCandidateErrors.set('701_turn_host_lookup_received_error', 1);
|
3489
|
+
MeetingUtil.getIpVersion.returns(IP_VERSION.only_ipv6);
|
3446
3490
|
|
3447
3491
|
await meeting.addMedia({
|
3448
3492
|
mediaSettings: {},
|
@@ -3458,6 +3502,7 @@ describe('plugin-meetings', () => {
|
|
3458
3502
|
connectionType: 'udp',
|
3459
3503
|
selectedCandidatePairChanges: 2,
|
3460
3504
|
ipVersion: 'IPv6',
|
3505
|
+
ipver: 6,
|
3461
3506
|
numTransports: 1,
|
3462
3507
|
isMultistream: false,
|
3463
3508
|
retriedWithTurnServer: false,
|
@@ -3536,6 +3581,7 @@ describe('plugin-meetings', () => {
|
|
3536
3581
|
selected_cluster: null,
|
3537
3582
|
selected_subnet: null,
|
3538
3583
|
iceCandidatesCount: 0,
|
3584
|
+
ipver: 1,
|
3539
3585
|
}
|
3540
3586
|
);
|
3541
3587
|
|
@@ -3600,6 +3646,7 @@ describe('plugin-meetings', () => {
|
|
3600
3646
|
selected_cluster: null,
|
3601
3647
|
selected_subnet: null,
|
3602
3648
|
iceCandidatesCount: 0,
|
3649
|
+
ipver: 1,
|
3603
3650
|
}
|
3604
3651
|
);
|
3605
3652
|
|
@@ -3646,6 +3693,7 @@ describe('plugin-meetings', () => {
|
|
3646
3693
|
locus_id: meeting.locusUrl.split('/').pop(),
|
3647
3694
|
connectionType: 'udp',
|
3648
3695
|
ipVersion: 'IPv6',
|
3696
|
+
ipver: 1,
|
3649
3697
|
selectedCandidatePairChanges: 2,
|
3650
3698
|
numTransports: 1,
|
3651
3699
|
isMultistream: false,
|
@@ -3726,6 +3774,7 @@ describe('plugin-meetings', () => {
|
|
3726
3774
|
selected_cluster: 'some.cluster',
|
3727
3775
|
selected_subnet: '1.X.X.X',
|
3728
3776
|
iceCandidatesCount: 0,
|
3777
|
+
ipver: 1,
|
3729
3778
|
}
|
3730
3779
|
);
|
3731
3780
|
|
@@ -4031,13 +4080,14 @@ describe('plugin-meetings', () => {
|
|
4031
4080
|
});
|
4032
4081
|
});
|
4033
4082
|
|
4034
|
-
it('counts the number of members that are in the meeting for MEDIA_QUALITY event', async () => {
|
4083
|
+
it('counts the number of members that are in the meeting or lobby for MEDIA_QUALITY event', async () => {
|
4035
4084
|
let fakeMembersCollection = {
|
4036
4085
|
members: {
|
4037
|
-
member1: {isInMeeting: true},
|
4038
|
-
member2: {isInMeeting: true},
|
4039
|
-
member3: {isInMeeting: false},
|
4040
|
-
|
4086
|
+
member1: {isInMeeting: true, isInLobby: false},
|
4087
|
+
member2: {isInMeeting: false, isInLobby: true},
|
4088
|
+
member3: {isInMeeting: false, isInLobby: false},
|
4089
|
+
member4: {isInMeeting: true, isInLobby: false},
|
4090
|
+
}
|
4041
4091
|
};
|
4042
4092
|
sinon.stub(meeting, 'getMembers').returns({membersCollection: fakeMembersCollection});
|
4043
4093
|
const fakeData = {intervalMetadata: {}};
|
@@ -4055,11 +4105,12 @@ describe('plugin-meetings', () => {
|
|
4055
4105
|
},
|
4056
4106
|
payload: {
|
4057
4107
|
intervals: [
|
4058
|
-
sinon.match.has('intervalMetadata', sinon.match.has('meetingUserCount',
|
4108
|
+
sinon.match.has('intervalMetadata', sinon.match.has('meetingUserCount', 3)),
|
4059
4109
|
],
|
4060
4110
|
},
|
4061
4111
|
});
|
4062
|
-
|
4112
|
+
// Move member2 from lobby to neither in meeting nor lobby
|
4113
|
+
fakeMembersCollection.members.member2.isInLobby = false;
|
4063
4114
|
|
4064
4115
|
statsAnalyzerStub.emit(
|
4065
4116
|
{file: 'test', function: 'test'},
|
@@ -4074,7 +4125,7 @@ describe('plugin-meetings', () => {
|
|
4074
4125
|
},
|
4075
4126
|
payload: {
|
4076
4127
|
intervals: [
|
4077
|
-
sinon.match.has('intervalMetadata', sinon.match.has('meetingUserCount',
|
4128
|
+
sinon.match.has('intervalMetadata', sinon.match.has('meetingUserCount', 2)),
|
4078
4129
|
],
|
4079
4130
|
},
|
4080
4131
|
});
|
@@ -4101,6 +4152,132 @@ describe('plugin-meetings', () => {
|
|
4101
4152
|
});
|
4102
4153
|
});
|
4103
4154
|
|
4155
|
+
describe('handles StatsMonitor events', () => {
|
4156
|
+
let statsMonitorStub;
|
4157
|
+
let prevConfigValue;
|
4158
|
+
let listeners;
|
4159
|
+
|
4160
|
+
beforeEach(async () => {
|
4161
|
+
meeting.meetingState = 'ACTIVE';
|
4162
|
+
prevConfigValue = meeting.config.stats.enableStatsAnalyzer;
|
4163
|
+
|
4164
|
+
meeting.config.stats.enableStatsAnalyzer = true;
|
4165
|
+
|
4166
|
+
listeners = {};
|
4167
|
+
|
4168
|
+
statsMonitorStub = {
|
4169
|
+
on: sinon.stub().callsFake((event, callback) => {
|
4170
|
+
listeners[event] = callback;
|
4171
|
+
}),
|
4172
|
+
removeAllListeners: sinon.stub(),
|
4173
|
+
};
|
4174
|
+
|
4175
|
+
sinon.stub(meeting.mediaProperties, 'sendMediaIssueMetric');
|
4176
|
+
|
4177
|
+
// mock the StatsMonitor constructor
|
4178
|
+
sinon.stub(InternalMediaCoreModule, 'StatsMonitor').returns(statsMonitorStub);
|
4179
|
+
|
4180
|
+
await meeting.addMedia({
|
4181
|
+
mediaSettings: {},
|
4182
|
+
});
|
4183
|
+
});
|
4184
|
+
|
4185
|
+
afterEach(() => {
|
4186
|
+
meeting.config.stats.enableStatsAnalyzer = prevConfigValue;
|
4187
|
+
sinon.restore();
|
4188
|
+
});
|
4189
|
+
|
4190
|
+
describe('INBOUND_AUDIO_ISSUE event', () => {
|
4191
|
+
it('should not trigger event when no unmuted members exist', () => {
|
4192
|
+
const fakeEventData = {issueSubType: 'DECODE_RESULTS_IN_ZERO_AUDIO_LEVEL'};
|
4193
|
+
|
4194
|
+
// Setup members that are either self or muted
|
4195
|
+
const mutedMember = {
|
4196
|
+
isSelf: false,
|
4197
|
+
isPairedWithSelf: false,
|
4198
|
+
isAudioMuted: true,
|
4199
|
+
};
|
4200
|
+
const selfMember = {
|
4201
|
+
isSelf: true,
|
4202
|
+
isPairedWithSelf: false,
|
4203
|
+
isAudioMuted: false,
|
4204
|
+
};
|
4205
|
+
const pairedMember = {
|
4206
|
+
isSelf: false,
|
4207
|
+
isPairedWithSelf: true,
|
4208
|
+
isAudioMuted: false,
|
4209
|
+
};
|
4210
|
+
meeting.members.membersCollection.getAll = sinon.stub().returns({
|
4211
|
+
member1: mutedMember,
|
4212
|
+
member2: selfMember,
|
4213
|
+
member3: pairedMember,
|
4214
|
+
});
|
4215
|
+
|
4216
|
+
// Reset the stub to clear any previous calls
|
4217
|
+
TriggerProxy.trigger.resetHistory();
|
4218
|
+
|
4219
|
+
// Emit the event from statsMonitor
|
4220
|
+
listeners[StatsMonitorEventNames.INBOUND_AUDIO_ISSUE](fakeEventData);
|
4221
|
+
|
4222
|
+
assert.neverCalledWith(
|
4223
|
+
TriggerProxy.trigger,
|
4224
|
+
meeting,
|
4225
|
+
sinon.match.object,
|
4226
|
+
EVENT_TRIGGERS.MEDIA_INBOUND_AUDIO_ISSUE_DETECTED,
|
4227
|
+
fakeEventData
|
4228
|
+
);
|
4229
|
+
assert.notCalled(meeting.mediaProperties.sendMediaIssueMetric);
|
4230
|
+
});
|
4231
|
+
|
4232
|
+
it('should trigger event and metric when there are multiple members and at least one is unmuted', () => {
|
4233
|
+
const fakeEventData = {issueSubType: 'DECODE_RESULTS_IN_ZERO_AUDIO_LEVEL'};
|
4234
|
+
|
4235
|
+
// Setup mixed members - some muted, one unmuted
|
4236
|
+
const mutedMember = {
|
4237
|
+
isSelf: false,
|
4238
|
+
isPairedWithSelf: false,
|
4239
|
+
isAudioMuted: true,
|
4240
|
+
};
|
4241
|
+
const unmutedMember = {
|
4242
|
+
isSelf: false,
|
4243
|
+
isPairedWithSelf: false,
|
4244
|
+
isAudioMuted: false,
|
4245
|
+
};
|
4246
|
+
const selfMember = {
|
4247
|
+
isSelf: true,
|
4248
|
+
isPairedWithSelf: false,
|
4249
|
+
isAudioMuted: false,
|
4250
|
+
};
|
4251
|
+
meeting.members.membersCollection.getAll = sinon.stub().returns({
|
4252
|
+
member1: mutedMember,
|
4253
|
+
member2: unmutedMember,
|
4254
|
+
member3: selfMember,
|
4255
|
+
});
|
4256
|
+
|
4257
|
+
// Reset the stub to clear any previous calls
|
4258
|
+
TriggerProxy.trigger.resetHistory();
|
4259
|
+
|
4260
|
+
// Emit the event from statsMonitor
|
4261
|
+
listeners[StatsMonitorEventNames.INBOUND_AUDIO_ISSUE](fakeEventData);
|
4262
|
+
|
4263
|
+
assert.calledWith(
|
4264
|
+
TriggerProxy.trigger,
|
4265
|
+
meeting,
|
4266
|
+
sinon.match.object,
|
4267
|
+
EVENT_TRIGGERS.MEDIA_INBOUND_AUDIO_ISSUE_DETECTED,
|
4268
|
+
fakeEventData
|
4269
|
+
);
|
4270
|
+
|
4271
|
+
assert.calledOnceWithExactly(
|
4272
|
+
meeting.mediaProperties.sendMediaIssueMetric,
|
4273
|
+
'inbound_audio',
|
4274
|
+
fakeEventData.issueSubType,
|
4275
|
+
meeting.correlationId
|
4276
|
+
);
|
4277
|
+
});
|
4278
|
+
});
|
4279
|
+
});
|
4280
|
+
|
4104
4281
|
describe('bundlePolicy', () => {
|
4105
4282
|
const FAKE_TURN_URL = 'turns:webex.com:3478';
|
4106
4283
|
const FAKE_TURN_USER = 'some-turn-username';
|
@@ -5567,6 +5744,7 @@ describe('plugin-meetings', () => {
|
|
5567
5744
|
let multistreamEventListeners;
|
5568
5745
|
let transcodedEventListeners;
|
5569
5746
|
let mockStatsAnalyzerCtor;
|
5747
|
+
let statsMonitorStub;
|
5570
5748
|
|
5571
5749
|
const setupFakeRoapMediaConnection = (fakeRoapMediaConnection, eventListeners) => {
|
5572
5750
|
fakeRoapMediaConnection.on.callsFake((eventName, cb) => {
|
@@ -5598,6 +5776,14 @@ describe('plugin-meetings', () => {
|
|
5598
5776
|
return {on: sinon.stub(), stopAnalyzer: sinon.stub()};
|
5599
5777
|
});
|
5600
5778
|
|
5779
|
+
statsMonitorStub = {
|
5780
|
+
on: sinon.stub(),
|
5781
|
+
removeAllListeners: sinon.stub(),
|
5782
|
+
};
|
5783
|
+
|
5784
|
+
// mock the StatsMonitor constructor
|
5785
|
+
sinon.stub(InternalMediaCoreModule, 'StatsMonitor').returns(statsMonitorStub);
|
5786
|
+
|
5601
5787
|
webex.internal.newMetrics.callDiagnosticMetrics.getErrorPayloadForClientErrorCode =
|
5602
5788
|
sinon.stub();
|
5603
5789
|
|
@@ -5660,6 +5846,7 @@ describe('plugin-meetings', () => {
|
|
5660
5846
|
mockStatsAnalyzerCtor,
|
5661
5847
|
sinon.match({
|
5662
5848
|
isMultistream: true,
|
5849
|
+
statsMonitor: statsMonitorStub,
|
5663
5850
|
})
|
5664
5851
|
);
|
5665
5852
|
const initialStatsAnalyzer = mockStatsAnalyzerCtor.returnValues[0];
|
@@ -6548,7 +6735,7 @@ describe('plugin-meetings', () => {
|
|
6548
6735
|
clientUrl: meeting.deviceUrl,
|
6549
6736
|
});
|
6550
6737
|
assert.notCalled(meeting.meetingRequest.dialOut);
|
6551
|
-
|
6738
|
+
|
6552
6739
|
// Verify pstnCorrelationId was set
|
6553
6740
|
assert.exists(meeting.pstnCorrelationId);
|
6554
6741
|
assert.notEqual(meeting.pstnCorrelationId, meeting.correlationId);
|
@@ -6625,7 +6812,7 @@ describe('plugin-meetings', () => {
|
|
6625
6812
|
throw new Error('Promise resolved when it should have rejected');
|
6626
6813
|
} catch (e) {
|
6627
6814
|
assert.equal(e, error);
|
6628
|
-
|
6815
|
+
|
6629
6816
|
// Verify behavioral metric was sent with dial_in_correlation_id
|
6630
6817
|
assert.calledWith(Metrics.sendBehavioralMetric, BEHAVIORAL_METRICS.ADD_DIAL_IN_FAILURE, {
|
6631
6818
|
correlation_id: meeting.correlationId,
|
@@ -6636,7 +6823,7 @@ describe('plugin-meetings', () => {
|
|
6636
6823
|
reason: error.error.message,
|
6637
6824
|
stack: error.stack,
|
6638
6825
|
});
|
6639
|
-
|
6826
|
+
|
6640
6827
|
// Verify pstnCorrelationId was cleared after error
|
6641
6828
|
assert.equal(meeting.pstnCorrelationId, undefined);
|
6642
6829
|
}
|
@@ -6652,7 +6839,7 @@ describe('plugin-meetings', () => {
|
|
6652
6839
|
throw new Error('Promise resolved when it should have rejected');
|
6653
6840
|
} catch (e) {
|
6654
6841
|
assert.equal(e, error);
|
6655
|
-
|
6842
|
+
|
6656
6843
|
// Verify behavioral metric was sent with dial_out_correlation_id
|
6657
6844
|
assert.calledWith(Metrics.sendBehavioralMetric, BEHAVIORAL_METRICS.ADD_DIAL_OUT_FAILURE, {
|
6658
6845
|
correlation_id: meeting.correlationId,
|
@@ -6663,7 +6850,7 @@ describe('plugin-meetings', () => {
|
|
6663
6850
|
reason: error.error.message,
|
6664
6851
|
stack: error.stack,
|
6665
6852
|
});
|
6666
|
-
|
6853
|
+
|
6667
6854
|
// Verify pstnCorrelationId was cleared after error
|
6668
6855
|
assert.equal(meeting.pstnCorrelationId, undefined);
|
6669
6856
|
}
|
@@ -6686,12 +6873,12 @@ describe('plugin-meetings', () => {
|
|
6686
6873
|
|
6687
6874
|
it('should disconnect phone audio and clear pstnCorrelationId', async () => {
|
6688
6875
|
meeting.pstnCorrelationId = 'test-pstn-correlation-id';
|
6689
|
-
|
6876
|
+
|
6690
6877
|
await meeting.disconnectPhoneAudio();
|
6691
|
-
|
6878
|
+
|
6692
6879
|
// Verify that pstnCorrelationId is cleared
|
6693
6880
|
assert.equal(meeting.pstnCorrelationId, undefined);
|
6694
|
-
|
6881
|
+
|
6695
6882
|
// Verify that MeetingUtil.disconnectPhoneAudio was called for both dial-in and dial-out
|
6696
6883
|
assert.calledTwice(MeetingUtil.disconnectPhoneAudio);
|
6697
6884
|
assert.calledWith(MeetingUtil.disconnectPhoneAudio, meeting, meeting.dialInUrl);
|
@@ -6702,9 +6889,9 @@ describe('plugin-meetings', () => {
|
|
6702
6889
|
meeting.dialInDeviceStatus = 'IDLE';
|
6703
6890
|
meeting.dialOutDeviceStatus = 'IDLE';
|
6704
6891
|
meeting.pstnCorrelationId = 'test-pstn-correlation-id';
|
6705
|
-
|
6892
|
+
|
6706
6893
|
await meeting.disconnectPhoneAudio();
|
6707
|
-
|
6894
|
+
|
6708
6895
|
// Verify that pstnCorrelationId is still cleared even when no phone connection is active
|
6709
6896
|
assert.equal(meeting.pstnCorrelationId, undefined);
|
6710
6897
|
// And verify no disconnect was attempted
|
@@ -7462,6 +7649,8 @@ describe('plugin-meetings', () => {
|
|
7462
7649
|
'locus-id',
|
7463
7650
|
{extraParam1: 'value1', permissionToken: FAKE_PERMISSION_TOKEN},
|
7464
7651
|
{meetingId: meeting.id, sendCAevents: true},
|
7652
|
+
null,
|
7653
|
+
null,
|
7465
7654
|
null
|
7466
7655
|
);
|
7467
7656
|
assert.deepEqual(meeting.meetingInfo, {
|
@@ -7508,6 +7697,8 @@ describe('plugin-meetings', () => {
|
|
7508
7697
|
'locus-id',
|
7509
7698
|
{extraParam1: 'value1', permissionToken: FAKE_PERMISSION_TOKEN},
|
7510
7699
|
{meetingId: meeting.id, sendCAevents: true},
|
7700
|
+
null,
|
7701
|
+
null,
|
7511
7702
|
null
|
7512
7703
|
);
|
7513
7704
|
assert.deepEqual(meeting.meetingInfo, {
|
@@ -7563,6 +7754,8 @@ describe('plugin-meetings', () => {
|
|
7563
7754
|
permissionToken: FAKE_PERMISSION_TOKEN,
|
7564
7755
|
},
|
7565
7756
|
{meetingId: meeting.id, sendCAevents: true},
|
7757
|
+
null,
|
7758
|
+
null,
|
7566
7759
|
null
|
7567
7760
|
);
|
7568
7761
|
assert.deepEqual(meeting.meetingInfo, {
|
@@ -9004,11 +9197,16 @@ describe('plugin-meetings', () => {
|
|
9004
9197
|
meeting.hasMediaConnectionConnectedAtLeastOnce = false;
|
9005
9198
|
meeting.setupMediaConnectionListeners();
|
9006
9199
|
|
9200
|
+
sinon.stub(MeetingUtil, 'getCaEventLabelsForIpVersion').returns(['fake labels']);
|
9201
|
+
|
9007
9202
|
simulateConnectionStateChange(ConnectionState.Connecting);
|
9008
9203
|
|
9009
9204
|
assert.calledOnce(webex.internal.newMetrics.submitClientEvent);
|
9010
9205
|
assert.calledWithMatch(webex.internal.newMetrics.submitClientEvent, {
|
9011
9206
|
name: 'client.ice.start',
|
9207
|
+
payload: {
|
9208
|
+
labels: ['fake labels'],
|
9209
|
+
},
|
9012
9210
|
options: {
|
9013
9211
|
meetingId: meeting.id,
|
9014
9212
|
},
|
@@ -11227,6 +11425,7 @@ describe('plugin-meetings', () => {
|
|
11227
11425
|
let canUserRenameOthersSpy;
|
11228
11426
|
let canShareWhiteBoardSpy;
|
11229
11427
|
let canMoveToLobbySpy;
|
11428
|
+
let isSpokenLanguageAutoDetectionEnabledSpy;
|
11230
11429
|
// Due to import tree issues, hasHints must be stubed within the scope of the `it`.
|
11231
11430
|
|
11232
11431
|
beforeEach(() => {
|
@@ -11258,6 +11457,8 @@ describe('plugin-meetings', () => {
|
|
11258
11457
|
canUserRenameOthersSpy = sinon.spy(MeetingUtil, 'canUserRenameOthers');
|
11259
11458
|
canShareWhiteBoardSpy = sinon.spy(MeetingUtil, 'canShareWhiteBoard');
|
11260
11459
|
canMoveToLobbySpy = sinon.spy(MeetingUtil, 'canMoveToLobby');
|
11460
|
+
isSpokenLanguageAutoDetectionEnabledSpy = sinon.spy(MeetingUtil, 'isSpokenLanguageAutoDetectionEnabled');
|
11461
|
+
|
11261
11462
|
});
|
11262
11463
|
|
11263
11464
|
afterEach(() => {
|
@@ -11810,6 +12011,7 @@ describe('plugin-meetings', () => {
|
|
11810
12011
|
assert.calledWith(canUserRenameOthersSpy, userDisplayHints);
|
11811
12012
|
assert.calledWith(canShareWhiteBoardSpy, userDisplayHints, selfUserPolicies);
|
11812
12013
|
assert.calledWith(canMoveToLobbySpy, userDisplayHints);
|
12014
|
+
assert.calledWith(isSpokenLanguageAutoDetectionEnabledSpy, userDisplayHints);
|
11813
12015
|
|
11814
12016
|
assert.calledWith(ControlsOptionsUtil.hasHints, {
|
11815
12017
|
requiredHints: [DISPLAY_HINTS.MUTE_ALL],
|
@@ -12598,7 +12800,7 @@ describe('plugin-meetings', () => {
|
|
12598
12800
|
eventName: EVENT_TRIGGERS.MEETING_STARTED_SHARING_REMOTE,
|
12599
12801
|
functionName: 'remoteShare',
|
12600
12802
|
eventPayload: {
|
12601
|
-
memberId: null,
|
12803
|
+
memberId: meeting.webinar.selfIsAttendee ? beneficiaryId : null,
|
12602
12804
|
url,
|
12603
12805
|
shareInstanceId,
|
12604
12806
|
annotationInfo: undefined,
|
@@ -12653,7 +12855,7 @@ describe('plugin-meetings', () => {
|
|
12653
12855
|
eventName: EVENT_TRIGGERS.MEETING_STARTED_SHARING_REMOTE,
|
12654
12856
|
functionName: 'remoteShare',
|
12655
12857
|
eventPayload: {
|
12656
|
-
memberId:
|
12858
|
+
memberId: beneficiaryId,
|
12657
12859
|
url,
|
12658
12860
|
shareInstanceId,
|
12659
12861
|
annotationInfo: undefined,
|
@@ -14792,4 +14994,30 @@ describe('plugin-meetings', () => {
|
|
14792
14994
|
);
|
14793
14995
|
});
|
14794
14996
|
});
|
14997
|
+
|
14998
|
+
describe('#notifyHost', () => {
|
14999
|
+
beforeEach(() => {
|
15000
|
+
meeting.meetingRequest.notifyHost = sinon.stub().returns(Promise.resolve());
|
15001
|
+
});
|
15002
|
+
|
15003
|
+
it('sends the expected request', async () => {
|
15004
|
+
meeting.meetingInfo.siteFullUrl = `convergedats.webex.com`;
|
15005
|
+
const meetingUuid = 'meeting-uuid';
|
15006
|
+
const displayName = ['Test', 'User'];
|
15007
|
+
meeting.locusId = 'locusId';
|
15008
|
+
|
15009
|
+
const notifyHostPromise = meeting.notifyHost(meetingUuid, displayName);
|
15010
|
+
|
15011
|
+
assert.exists(notifyHostPromise.then);
|
15012
|
+
await notifyHostPromise;
|
15013
|
+
|
15014
|
+
assert.calledOnceWithExactly(
|
15015
|
+
meeting.meetingRequest.notifyHost,
|
15016
|
+
meeting.meetingInfo.siteFullUrl,
|
15017
|
+
meeting.locusId,
|
15018
|
+
meetingUuid,
|
15019
|
+
displayName,
|
15020
|
+
);
|
15021
|
+
});
|
15022
|
+
});
|
14795
15023
|
});
|