@webex/plugin-meetings 3.8.0-next.4 → 3.8.0-next.40
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/config.js +1 -0
- package/dist/config.js.map +1 -1
- package/dist/constants.js +14 -1
- package/dist/constants.js.map +1 -1
- package/dist/controls-options-manager/enums.js +2 -0
- package/dist/controls-options-manager/enums.js.map +1 -1
- package/dist/controls-options-manager/types.js.map +1 -1
- package/dist/controls-options-manager/util.js +52 -0
- package/dist/controls-options-manager/util.js.map +1 -1
- package/dist/interpretation/index.js +1 -1
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/locus-info/controlsUtils.js +28 -10
- package/dist/locus-info/controlsUtils.js.map +1 -1
- package/dist/locus-info/index.js +20 -1
- package/dist/locus-info/index.js.map +1 -1
- package/dist/media/index.js +3 -15
- package/dist/media/index.js.map +1 -1
- package/dist/meeting/in-meeting-actions.js +11 -1
- package/dist/meeting/in-meeting-actions.js.map +1 -1
- package/dist/meeting/index.js +443 -256
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/locusMediaRequest.js +21 -22
- package/dist/meeting/locusMediaRequest.js.map +1 -1
- package/dist/meeting/muteState.js +0 -2
- package/dist/meeting/muteState.js.map +1 -1
- package/dist/meeting/request.js +30 -0
- package/dist/meeting/request.js.map +1 -1
- package/dist/meeting/request.type.js.map +1 -1
- package/dist/meeting/util.js +10 -2
- package/dist/meeting/util.js.map +1 -1
- package/dist/meeting-info/meeting-info-v2.js +359 -60
- package/dist/meeting-info/meeting-info-v2.js.map +1 -1
- package/dist/meetings/index.js +60 -1
- package/dist/meetings/index.js.map +1 -1
- package/dist/member/index.js +10 -0
- package/dist/member/index.js.map +1 -1
- package/dist/member/util.js +3 -0
- package/dist/member/util.js.map +1 -1
- package/dist/metrics/constants.js +9 -0
- package/dist/metrics/constants.js.map +1 -1
- package/dist/reachability/clusterReachability.js +52 -8
- package/dist/reachability/clusterReachability.js.map +1 -1
- package/dist/reachability/index.js +70 -45
- package/dist/reachability/index.js.map +1 -1
- package/dist/reachability/reachability.types.js +14 -0
- package/dist/reachability/reachability.types.js.map +1 -1
- package/dist/reachability/request.js +19 -3
- package/dist/reachability/request.js.map +1 -1
- package/dist/reconnection-manager/index.js +2 -2
- package/dist/reconnection-manager/index.js.map +1 -1
- package/dist/recording-controller/util.js +5 -5
- package/dist/recording-controller/util.js.map +1 -1
- package/dist/roap/index.js.map +1 -1
- package/dist/roap/turnDiscovery.js +45 -27
- package/dist/roap/turnDiscovery.js.map +1 -1
- package/dist/roap/types.js +17 -0
- package/dist/roap/types.js.map +1 -0
- package/dist/types/config.d.ts +1 -0
- package/dist/types/constants.d.ts +10 -0
- 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 +1 -0
- package/dist/types/meeting/in-meeting-actions.d.ts +10 -0
- package/dist/types/meeting/index.d.ts +47 -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 +2 -1
- package/dist/types/meeting-info/meeting-info-v2.d.ts +80 -0
- package/dist/types/meetings/index.d.ts +29 -0
- package/dist/types/member/index.d.ts +1 -0
- package/dist/types/metrics/constants.d.ts +9 -0
- package/dist/types/reachability/clusterReachability.d.ts +13 -1
- package/dist/types/reachability/index.d.ts +2 -1
- package/dist/types/reachability/reachability.types.d.ts +5 -0
- package/dist/types/roap/index.d.ts +3 -2
- package/dist/types/roap/turnDiscovery.d.ts +5 -17
- package/dist/types/roap/types.d.ts +16 -0
- package/dist/webinar/index.js +1 -1
- package/package.json +22 -22
- package/src/config.ts +1 -0
- package/src/constants.ts +17 -0
- package/src/controls-options-manager/enums.ts +2 -0
- package/src/controls-options-manager/types.ts +11 -1
- package/src/controls-options-manager/util.ts +62 -0
- package/src/locus-info/controlsUtils.ts +44 -14
- package/src/locus-info/index.ts +23 -1
- package/src/media/index.ts +5 -21
- package/src/meeting/in-meeting-actions.ts +20 -0
- package/src/meeting/index.ts +263 -69
- package/src/meeting/locusMediaRequest.ts +27 -22
- package/src/meeting/muteState.ts +0 -2
- package/src/meeting/request.ts +36 -1
- package/src/meeting/request.type.ts +7 -0
- package/src/meeting/util.ts +9 -2
- package/src/meeting-info/meeting-info-v2.ts +247 -6
- package/src/meetings/index.ts +72 -1
- package/src/member/index.ts +11 -0
- package/src/member/util.ts +3 -0
- package/src/metrics/constants.ts +9 -0
- package/src/reachability/clusterReachability.ts +47 -1
- package/src/reachability/index.ts +15 -0
- package/src/reachability/reachability.types.ts +6 -0
- package/src/reachability/request.ts +7 -0
- package/src/reconnection-manager/index.ts +2 -2
- package/src/recording-controller/util.ts +17 -13
- package/src/roap/index.ts +3 -7
- package/src/roap/turnDiscovery.ts +34 -39
- package/src/roap/types.ts +23 -0
- package/test/unit/spec/controls-options-manager/util.js +120 -0
- package/test/unit/spec/locus-info/controlsUtils.js +103 -9
- package/test/unit/spec/locus-info/index.js +28 -0
- package/test/unit/spec/media/index.ts +6 -16
- package/test/unit/spec/meeting/in-meeting-actions.ts +13 -4
- package/test/unit/spec/meeting/index.js +490 -130
- package/test/unit/spec/meeting/locusMediaRequest.ts +95 -87
- package/test/unit/spec/meeting/muteState.js +0 -2
- package/test/unit/spec/meeting/request.js +32 -1
- package/test/unit/spec/meeting/utils.js +115 -18
- package/test/unit/spec/meeting-info/meetinginfov2.js +443 -114
- package/test/unit/spec/meetings/index.js +78 -1
- package/test/unit/spec/member/index.js +7 -0
- package/test/unit/spec/member/util.js +24 -0
- package/test/unit/spec/reachability/clusterReachability.ts +47 -1
- package/test/unit/spec/reachability/index.ts +12 -0
- package/test/unit/spec/reachability/request.js +47 -2
- package/test/unit/spec/reconnection-manager/index.js +4 -4
- package/test/unit/spec/roap/turnDiscovery.ts +110 -28
@@ -1,6 +1,7 @@
|
|
1
1
|
/*!
|
2
2
|
* Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.
|
3
3
|
*/
|
4
|
+
import {v4 as uuidv4} from 'uuid';
|
4
5
|
import 'jsdom-global/register';
|
5
6
|
import {cloneDeep, forEach, isEqual, isUndefined} from 'lodash';
|
6
7
|
import sinon from 'sinon';
|
@@ -93,13 +94,14 @@ import CaptchaError from '../../../../src/common/errors/captcha-error';
|
|
93
94
|
import PermissionError from '../../../../src/common/errors/permission';
|
94
95
|
import JoinWebinarError from '../../../../src/common/errors/join-webinar-error';
|
95
96
|
import IntentToJoinError from '../../../../src/common/errors/intent-to-join';
|
96
|
-
import MultistreamNotSupportedError from '../../../../src/common/errors/multistream-not-supported-error'
|
97
|
+
import MultistreamNotSupportedError from '../../../../src/common/errors/multistream-not-supported-error';
|
97
98
|
import testUtils from '../../../utils/testUtils';
|
98
99
|
import {
|
99
100
|
MeetingInfoV2CaptchaError,
|
100
101
|
MeetingInfoV2PasswordError,
|
101
102
|
MeetingInfoV2PolicyError,
|
102
|
-
MeetingInfoV2JoinWebinarError,
|
103
|
+
MeetingInfoV2JoinWebinarError,
|
104
|
+
MeetingInfoV2JoinForbiddenError,
|
103
105
|
} from '../../../../src/meeting-info/meeting-info-v2';
|
104
106
|
import {
|
105
107
|
DTLS_HANDSHAKE_FAILED_CLIENT_CODE,
|
@@ -114,8 +116,9 @@ import {ERROR_DESCRIPTIONS} from '@webex/internal-plugin-metrics/src/call-diagno
|
|
114
116
|
import MeetingCollection from '@webex/plugin-meetings/src/meetings/collection';
|
115
117
|
|
116
118
|
import {EVENT_TRIGGERS as VOICEAEVENTS} from '@webex/internal-plugin-voicea';
|
117
|
-
import {
|
118
|
-
import JoinForbiddenError
|
119
|
+
import {createBrbState} from '@webex/plugin-meetings/src/meeting/brbState';
|
120
|
+
import JoinForbiddenError from '../../../../src/common/errors/join-forbidden-error';
|
121
|
+
import {EventEmitter} from 'stream';
|
119
122
|
|
120
123
|
describe('plugin-meetings', () => {
|
121
124
|
const logger = {
|
@@ -208,6 +211,8 @@ describe('plugin-meetings', () => {
|
|
208
211
|
let membersSpy;
|
209
212
|
let meetingRequestSpy;
|
210
213
|
let correlationId;
|
214
|
+
let isoLocalClientMeetingJoinTime;
|
215
|
+
let uploadEvent;
|
211
216
|
|
212
217
|
beforeEach(() => {
|
213
218
|
webex = new MockWebex({
|
@@ -277,6 +282,8 @@ describe('plugin-meetings', () => {
|
|
277
282
|
test4 = `test4-${uuid.v4()}`;
|
278
283
|
testDestination = `testDestination-${uuid.v4()}`;
|
279
284
|
correlationId = uuid.v4();
|
285
|
+
uploadEvent = new EventEmitter();
|
286
|
+
uploadEvent.addListener('progress', () => {});
|
280
287
|
|
281
288
|
meeting = new Meeting(
|
282
289
|
{
|
@@ -667,7 +674,7 @@ describe('plugin-meetings', () => {
|
|
667
674
|
beforeEach(() => {
|
668
675
|
meeting.join = sinon.stub().callsFake((joinOptions) => {
|
669
676
|
meeting.isMultistream = joinOptions.enableMultistream;
|
670
|
-
return Promise.resolve(fakeJoinResult)
|
677
|
+
return Promise.resolve(fakeJoinResult);
|
671
678
|
});
|
672
679
|
addMediaInternalStub = sinon
|
673
680
|
.stub(meeting, 'addMediaInternal')
|
@@ -1070,7 +1077,11 @@ describe('plugin-meetings', () => {
|
|
1070
1077
|
mediaOptions,
|
1071
1078
|
});
|
1072
1079
|
|
1073
|
-
assert.deepEqual(result, {
|
1080
|
+
assert.deepEqual(result, {
|
1081
|
+
join: fakeJoinResult,
|
1082
|
+
media: undefined,
|
1083
|
+
multistreamEnabled: false,
|
1084
|
+
});
|
1074
1085
|
|
1075
1086
|
assert.calledOnce(meeting.join);
|
1076
1087
|
|
@@ -1174,7 +1185,10 @@ describe('plugin-meetings', () => {
|
|
1174
1185
|
type: addMediaError.name,
|
1175
1186
|
}
|
1176
1187
|
);
|
1177
|
-
assert.calledOnceWithExactly(meeting.leave, {
|
1188
|
+
assert.calledOnceWithExactly(meeting.leave, {
|
1189
|
+
resourceId: undefined,
|
1190
|
+
reason: 'joinWithMedia failure',
|
1191
|
+
});
|
1178
1192
|
});
|
1179
1193
|
});
|
1180
1194
|
|
@@ -1680,10 +1694,6 @@ describe('plugin-meetings', () => {
|
|
1680
1694
|
sandbox.stub(MeetingUtil, 'joinMeeting').returns(Promise.resolve(joinMeetingResult));
|
1681
1695
|
});
|
1682
1696
|
|
1683
|
-
afterEach(() => {
|
1684
|
-
assert.exists(meeting.isoLocalClientMeetingJoinTime);
|
1685
|
-
});
|
1686
|
-
|
1687
1697
|
it('should join the meeting and return promise', async () => {
|
1688
1698
|
const join = meeting.join({pstnAudioType: 'dial-in'});
|
1689
1699
|
meeting.config.enableAutomaticLLM = true;
|
@@ -2655,7 +2665,7 @@ describe('plugin-meetings', () => {
|
|
2655
2665
|
|
2656
2666
|
meeting.roap.doTurnDiscovery = sinon.stub().resolves({
|
2657
2667
|
turnServerInfo: {
|
2658
|
-
|
2668
|
+
urls: [FAKE_TURN_URL],
|
2659
2669
|
username: FAKE_TURN_USER,
|
2660
2670
|
password: FAKE_TURN_PASSWORD,
|
2661
2671
|
},
|
@@ -2677,7 +2687,7 @@ describe('plugin-meetings', () => {
|
|
2677
2687
|
meeting.id,
|
2678
2688
|
sinon.match({
|
2679
2689
|
turnServerInfo: {
|
2680
|
-
|
2690
|
+
urls: [FAKE_TURN_URL],
|
2681
2691
|
username: FAKE_TURN_USER,
|
2682
2692
|
password: FAKE_TURN_PASSWORD,
|
2683
2693
|
},
|
@@ -2735,7 +2745,7 @@ describe('plugin-meetings', () => {
|
|
2735
2745
|
.onSecondCall()
|
2736
2746
|
.returns({
|
2737
2747
|
turnServerInfo: {
|
2738
|
-
|
2748
|
+
urls: [FAKE_TURN_URL],
|
2739
2749
|
username: FAKE_TURN_USER,
|
2740
2750
|
password: FAKE_TURN_PASSWORD,
|
2741
2751
|
},
|
@@ -2947,7 +2957,7 @@ describe('plugin-meetings', () => {
|
|
2947
2957
|
.onSecondCall()
|
2948
2958
|
.returns({
|
2949
2959
|
turnServerInfo: {
|
2950
|
-
|
2960
|
+
urls: [FAKE_TURN_URL],
|
2951
2961
|
username: FAKE_TURN_USER,
|
2952
2962
|
password: FAKE_TURN_PASSWORD,
|
2953
2963
|
},
|
@@ -3124,7 +3134,7 @@ describe('plugin-meetings', () => {
|
|
3124
3134
|
.onSecondCall()
|
3125
3135
|
.returns({
|
3126
3136
|
turnServerInfo: {
|
3127
|
-
|
3137
|
+
urls: [FAKE_TURN_URL],
|
3128
3138
|
username: FAKE_TURN_USER,
|
3129
3139
|
password: FAKE_TURN_PASSWORD,
|
3130
3140
|
},
|
@@ -3176,7 +3186,7 @@ describe('plugin-meetings', () => {
|
|
3176
3186
|
.onSecondCall()
|
3177
3187
|
.returns({
|
3178
3188
|
turnServerInfo: {
|
3179
|
-
|
3189
|
+
urls: [FAKE_TURN_URL],
|
3180
3190
|
username: FAKE_TURN_USER,
|
3181
3191
|
password: FAKE_TURN_PASSWORD,
|
3182
3192
|
},
|
@@ -3427,6 +3437,40 @@ describe('plugin-meetings', () => {
|
|
3427
3437
|
});
|
3428
3438
|
});
|
3429
3439
|
|
3440
|
+
it('LOCAL_MEDIA_STARTED triggers "meeting:media:local:start" event and does not send metric because we already have', async () => {
|
3441
|
+
meeting.shareCAEventSentStatus = {
|
3442
|
+
transmitStart: true,
|
3443
|
+
transmitStop: false,
|
3444
|
+
receiveStart: false,
|
3445
|
+
receiveStop: false,
|
3446
|
+
};
|
3447
|
+
statsAnalyzerStub.emit(
|
3448
|
+
{file: 'test', function: 'test'},
|
3449
|
+
StatsAnalyzerEventNames.LOCAL_MEDIA_STARTED,
|
3450
|
+
{mediaType: 'share'}
|
3451
|
+
);
|
3452
|
+
|
3453
|
+
assert.calledWith(
|
3454
|
+
TriggerProxy.trigger,
|
3455
|
+
sinon.match.instanceOf(Meeting),
|
3456
|
+
{
|
3457
|
+
file: 'meeting/index',
|
3458
|
+
function: 'addMedia',
|
3459
|
+
},
|
3460
|
+
EVENT_TRIGGERS.MEETING_MEDIA_LOCAL_STARTED,
|
3461
|
+
{
|
3462
|
+
mediaType: 'share',
|
3463
|
+
}
|
3464
|
+
);
|
3465
|
+
assert.neverCalledWith(webex.internal.newMetrics.submitClientEvent, {
|
3466
|
+
name: 'client.media.tx.start',
|
3467
|
+
payload: {mediaType: 'share', shareInstanceId: meeting.remoteShareInstanceId},
|
3468
|
+
options: {
|
3469
|
+
meetingId: meeting.id,
|
3470
|
+
},
|
3471
|
+
});
|
3472
|
+
});
|
3473
|
+
|
3430
3474
|
it('LOCAL_MEDIA_STOPPED triggers the right metrics', async () => {
|
3431
3475
|
statsAnalyzerStub.emit(
|
3432
3476
|
{file: 'test', function: 'test'},
|
@@ -3443,6 +3487,28 @@ describe('plugin-meetings', () => {
|
|
3443
3487
|
});
|
3444
3488
|
});
|
3445
3489
|
|
3490
|
+
it('LOCAL_MEDIA_STOPPED does not send metric because we already have', async () => {
|
3491
|
+
meeting.shareCAEventSentStatus = {
|
3492
|
+
transmitStart: false,
|
3493
|
+
transmitStop: true,
|
3494
|
+
receiveStart: false,
|
3495
|
+
receiveStop: false,
|
3496
|
+
};
|
3497
|
+
statsAnalyzerStub.emit(
|
3498
|
+
{file: 'test', function: 'test'},
|
3499
|
+
StatsAnalyzerEventNames.LOCAL_MEDIA_STOPPED,
|
3500
|
+
{mediaType: 'share'}
|
3501
|
+
);
|
3502
|
+
|
3503
|
+
assert.neverCalledWith(webex.internal.newMetrics.submitClientEvent, {
|
3504
|
+
name: 'client.media.tx.stop',
|
3505
|
+
payload: {mediaType: 'share', shareInstanceId: meeting.remoteShareInstanceId},
|
3506
|
+
options: {
|
3507
|
+
meetingId: meeting.id,
|
3508
|
+
},
|
3509
|
+
});
|
3510
|
+
});
|
3511
|
+
|
3446
3512
|
it('REMOTE_MEDIA_STARTED triggers "meeting:media:remote:start" event and sends metrics', async () => {
|
3447
3513
|
statsAnalyzerStub.emit(
|
3448
3514
|
{file: 'test', function: 'test'},
|
@@ -3523,6 +3589,47 @@ describe('plugin-meetings', () => {
|
|
3523
3589
|
});
|
3524
3590
|
});
|
3525
3591
|
|
3592
|
+
it('REMOTE_MEDIA_STARTED triggers "meeting:media:remote:start" event and does not send metric because we already have', async () => {
|
3593
|
+
meeting.shareCAEventSentStatus = {
|
3594
|
+
transmitStart: false,
|
3595
|
+
transmitStop: false,
|
3596
|
+
receiveStart: true,
|
3597
|
+
receiveStop: false,
|
3598
|
+
};
|
3599
|
+
statsAnalyzerStub.emit(
|
3600
|
+
{file: 'test', function: 'test'},
|
3601
|
+
StatsAnalyzerEventNames.REMOTE_MEDIA_STARTED,
|
3602
|
+
{mediaType: 'share'}
|
3603
|
+
);
|
3604
|
+
|
3605
|
+
assert.calledWith(
|
3606
|
+
TriggerProxy.trigger,
|
3607
|
+
sinon.match.instanceOf(Meeting),
|
3608
|
+
{
|
3609
|
+
file: 'meeting/index',
|
3610
|
+
function: 'addMedia',
|
3611
|
+
},
|
3612
|
+
EVENT_TRIGGERS.MEETING_MEDIA_REMOTE_STARTED,
|
3613
|
+
{
|
3614
|
+
mediaType: 'share',
|
3615
|
+
}
|
3616
|
+
);
|
3617
|
+
assert.neverCalledWith(webex.internal.newMetrics.submitClientEvent, {
|
3618
|
+
name: 'client.media.render.start',
|
3619
|
+
payload: {mediaType: 'share', shareInstanceId: meeting.remoteShareInstanceId},
|
3620
|
+
options: {
|
3621
|
+
meetingId: meeting.id,
|
3622
|
+
},
|
3623
|
+
});
|
3624
|
+
assert.neverCalledWith(webex.internal.newMetrics.submitClientEvent, {
|
3625
|
+
name: 'client.media.rx.start',
|
3626
|
+
payload: {mediaType: 'share', shareInstanceId: meeting.remoteShareInstanceId},
|
3627
|
+
options: {
|
3628
|
+
meetingId: meeting.id,
|
3629
|
+
},
|
3630
|
+
});
|
3631
|
+
});
|
3632
|
+
|
3526
3633
|
it('REMOTE_MEDIA_STOPPED triggers the right metrics for share', async () => {
|
3527
3634
|
statsAnalyzerStub.emit(
|
3528
3635
|
{file: 'test', function: 'test'},
|
@@ -3547,21 +3654,49 @@ describe('plugin-meetings', () => {
|
|
3547
3654
|
});
|
3548
3655
|
});
|
3549
3656
|
|
3657
|
+
it('REMOTE_MEDIA_STOPPED does not send metric because we already have', async () => {
|
3658
|
+
meeting.shareCAEventSentStatus = {
|
3659
|
+
transmitStart: false,
|
3660
|
+
transmitStop: false,
|
3661
|
+
receiveStart: true,
|
3662
|
+
receiveStop: true,
|
3663
|
+
};
|
3664
|
+
statsAnalyzerStub.emit(
|
3665
|
+
{file: 'test', function: 'test'},
|
3666
|
+
StatsAnalyzerEventNames.REMOTE_MEDIA_STOPPED,
|
3667
|
+
{mediaType: 'share'}
|
3668
|
+
);
|
3669
|
+
assert.neverCalledWith(webex.internal.newMetrics.submitClientEvent, {
|
3670
|
+
name: 'client.media.render.stop',
|
3671
|
+
payload: {mediaType: 'share', shareInstanceId: meeting.remoteShareInstanceId},
|
3672
|
+
options: {
|
3673
|
+
meetingId: meeting.id,
|
3674
|
+
},
|
3675
|
+
});
|
3676
|
+
assert.neverCalledWith(webex.internal.newMetrics.submitClientEvent, {
|
3677
|
+
name: 'client.media.rx.stop',
|
3678
|
+
payload: {mediaType: 'share', shareInstanceId: meeting.remoteShareInstanceId},
|
3679
|
+
options: {
|
3680
|
+
meetingId: meeting.id,
|
3681
|
+
},
|
3682
|
+
});
|
3683
|
+
});
|
3684
|
+
|
3550
3685
|
it('counts the number of members that are in the meeting for MEDIA_QUALITY event', async () => {
|
3551
3686
|
let fakeMembersCollection = {
|
3552
3687
|
members: {
|
3553
|
-
member1: {
|
3554
|
-
member2: {
|
3555
|
-
member3: {
|
3688
|
+
member1: {isInMeeting: true},
|
3689
|
+
member2: {isInMeeting: true},
|
3690
|
+
member3: {isInMeeting: false},
|
3556
3691
|
},
|
3557
3692
|
};
|
3558
|
-
sinon.stub(meeting, 'getMembers').returns({
|
3559
|
-
const fakeData = {
|
3693
|
+
sinon.stub(meeting, 'getMembers').returns({membersCollection: fakeMembersCollection});
|
3694
|
+
const fakeData = {intervalMetadata: {}, networkType: 'wifi'};
|
3560
3695
|
|
3561
3696
|
statsAnalyzerStub.emit(
|
3562
|
-
{
|
3697
|
+
{file: 'test', function: 'test'},
|
3563
3698
|
StatsAnalyzerEventNames.MEDIA_QUALITY,
|
3564
|
-
{
|
3699
|
+
{data: fakeData}
|
3565
3700
|
);
|
3566
3701
|
|
3567
3702
|
assert.calledWithMatch(webex.internal.newMetrics.submitMQE, {
|
@@ -3570,15 +3705,17 @@ describe('plugin-meetings', () => {
|
|
3570
3705
|
meetingId: meeting.id,
|
3571
3706
|
},
|
3572
3707
|
payload: {
|
3573
|
-
intervals: [
|
3708
|
+
intervals: [
|
3709
|
+
sinon.match.has('intervalMetadata', sinon.match.has('meetingUserCount', 2)),
|
3710
|
+
],
|
3574
3711
|
},
|
3575
3712
|
});
|
3576
3713
|
fakeMembersCollection.members.member2.isInMeeting = false;
|
3577
3714
|
|
3578
3715
|
statsAnalyzerStub.emit(
|
3579
|
-
{
|
3716
|
+
{file: 'test', function: 'test'},
|
3580
3717
|
StatsAnalyzerEventNames.MEDIA_QUALITY,
|
3581
|
-
{
|
3718
|
+
{data: fakeData}
|
3582
3719
|
);
|
3583
3720
|
|
3584
3721
|
assert.calledWithMatch(webex.internal.newMetrics.submitMQE, {
|
@@ -3587,7 +3724,9 @@ describe('plugin-meetings', () => {
|
|
3587
3724
|
meetingId: meeting.id,
|
3588
3725
|
},
|
3589
3726
|
payload: {
|
3590
|
-
intervals: [
|
3727
|
+
intervals: [
|
3728
|
+
sinon.match.has('intervalMetadata', sinon.match.has('meetingUserCount', 1)),
|
3729
|
+
],
|
3591
3730
|
},
|
3592
3731
|
});
|
3593
3732
|
});
|
@@ -3624,7 +3763,7 @@ describe('plugin-meetings', () => {
|
|
3624
3763
|
|
3625
3764
|
meeting.roap.doTurnDiscovery = sinon.stub().resolves({
|
3626
3765
|
turnServerInfo: {
|
3627
|
-
|
3766
|
+
urls: [FAKE_TURN_URL],
|
3628
3767
|
username: FAKE_TURN_USER,
|
3629
3768
|
password: FAKE_TURN_PASSWORD,
|
3630
3769
|
},
|
@@ -3650,7 +3789,7 @@ describe('plugin-meetings', () => {
|
|
3650
3789
|
meeting.id,
|
3651
3790
|
sinon.match({
|
3652
3791
|
turnServerInfo: {
|
3653
|
-
|
3792
|
+
urls: [FAKE_TURN_URL],
|
3654
3793
|
username: FAKE_TURN_USER,
|
3655
3794
|
password: FAKE_TURN_PASSWORD,
|
3656
3795
|
},
|
@@ -3842,7 +3981,6 @@ describe('plugin-meetings', () => {
|
|
3842
3981
|
});
|
3843
3982
|
|
3844
3983
|
describe('when in a multistream meeting', () => {
|
3845
|
-
|
3846
3984
|
beforeEach(() => {
|
3847
3985
|
meeting.isMultistream = true;
|
3848
3986
|
});
|
@@ -3853,7 +3991,7 @@ describe('plugin-meetings', () => {
|
|
3853
3991
|
await brbResult;
|
3854
3992
|
assert.exists(brbResult.then);
|
3855
3993
|
assert.calledOnce(meeting.brbState.enable);
|
3856
|
-
})
|
3994
|
+
});
|
3857
3995
|
|
3858
3996
|
it('should disable #beRightBack and return a promise', async () => {
|
3859
3997
|
const brbResult = meeting.beRightBack(false);
|
@@ -3861,7 +3999,7 @@ describe('plugin-meetings', () => {
|
|
3861
3999
|
await brbResult;
|
3862
4000
|
assert.exists(brbResult.then);
|
3863
4001
|
assert.calledOnce(meeting.brbState.enable);
|
3864
|
-
})
|
4002
|
+
});
|
3865
4003
|
|
3866
4004
|
it('should throw an error and reject the promise if setBrb fails', async () => {
|
3867
4005
|
const error = new Error('setBrb failed');
|
@@ -3872,9 +4010,29 @@ describe('plugin-meetings', () => {
|
|
3872
4010
|
} catch (err) {
|
3873
4011
|
assert.instanceOf(err, Error);
|
3874
4012
|
assert.equal(err.message, 'setBrb failed');
|
3875
|
-
assert.isRejected(
|
4013
|
+
assert.isRejected(Promise.reject());
|
3876
4014
|
}
|
3877
|
-
})
|
4015
|
+
});
|
4016
|
+
|
4017
|
+
it('updates remote mute state when brb is enabled', async () => {
|
4018
|
+
meeting.audio = {handleServerRemoteMuteUpdate: sinon.stub()};
|
4019
|
+
|
4020
|
+
await meeting.beRightBack(true);
|
4021
|
+
|
4022
|
+
sinon.assert.calledOnceWithExactly(
|
4023
|
+
meeting.audio.handleServerRemoteMuteUpdate,
|
4024
|
+
meeting,
|
4025
|
+
true
|
4026
|
+
);
|
4027
|
+
});
|
4028
|
+
|
4029
|
+
it('does not update remote mute state when brb is disabled', async () => {
|
4030
|
+
meeting.audio = {handleServerRemoteMuteUpdate: sinon.stub()};
|
4031
|
+
|
4032
|
+
await meeting.beRightBack(false);
|
4033
|
+
|
4034
|
+
assert.notCalled(meeting.audio.handleServerRemoteMuteUpdate);
|
4035
|
+
});
|
3878
4036
|
});
|
3879
4037
|
});
|
3880
4038
|
|
@@ -3928,7 +4086,10 @@ describe('plugin-meetings', () => {
|
|
3928
4086
|
.resolves({id: 'fake clientMediaPreferences'});
|
3929
4087
|
meeting.roap.doTurnDiscovery = sinon.stub().resolves({
|
3930
4088
|
turnServerInfo: {
|
3931
|
-
|
4089
|
+
urls: [
|
4090
|
+
'turns:turn-server-url1:443?transport=tcp',
|
4091
|
+
'turns:turn-server-url2:443?transport=tcp',
|
4092
|
+
],
|
3932
4093
|
username: 'turn user',
|
3933
4094
|
password: 'turn password',
|
3934
4095
|
},
|
@@ -3946,12 +4107,10 @@ describe('plugin-meetings', () => {
|
|
3946
4107
|
expectedMediaConnectionConfig = {
|
3947
4108
|
iceServers: [
|
3948
4109
|
{
|
3949
|
-
urls:
|
3950
|
-
|
3951
|
-
|
3952
|
-
|
3953
|
-
{
|
3954
|
-
urls: 'turns:turn-server-url:443?transport=tcp',
|
4110
|
+
urls: [
|
4111
|
+
'turns:turn-server-url1:443?transport=tcp',
|
4112
|
+
'turns:turn-server-url2:443?transport=tcp',
|
4113
|
+
],
|
3955
4114
|
username: 'turn user',
|
3956
4115
|
credential: 'turn password',
|
3957
4116
|
},
|
@@ -4006,7 +4165,7 @@ describe('plugin-meetings', () => {
|
|
4006
4165
|
initiateOffer: sinon.stub().resolves({}),
|
4007
4166
|
update: sinon.stub().resolves({}),
|
4008
4167
|
on: sinon.stub(),
|
4009
|
-
roapMessageReceived: sinon.stub()
|
4168
|
+
roapMessageReceived: sinon.stub(),
|
4010
4169
|
};
|
4011
4170
|
|
4012
4171
|
fakeMultistreamRoapMediaConnection = {
|
@@ -4033,9 +4192,11 @@ describe('plugin-meetings', () => {
|
|
4033
4192
|
.stub(InternalMediaCoreModule, 'MultistreamRoapMediaConnection')
|
4034
4193
|
.returns(fakeMultistreamRoapMediaConnection);
|
4035
4194
|
|
4036
|
-
locusMediaRequestStub = sinon
|
4037
|
-
|
4038
|
-
.
|
4195
|
+
locusMediaRequestStub = sinon.stub(WebexPlugin.prototype, 'request').resolves({
|
4196
|
+
body: {locus: {fullState: {}}},
|
4197
|
+
upload: sinon.match.instanceOf(EventEmitter),
|
4198
|
+
download: sinon.match.instanceOf(EventEmitter),
|
4199
|
+
});
|
4039
4200
|
|
4040
4201
|
// setup some things and mocks so that the call to join() works
|
4041
4202
|
// (we need to call join() because it creates the LocusMediaRequest instance
|
@@ -4144,6 +4305,8 @@ describe('plugin-meetings', () => {
|
|
4144
4305
|
id: 'fake clientMediaPreferences',
|
4145
4306
|
},
|
4146
4307
|
},
|
4308
|
+
upload: sinon.match.instanceOf(EventEmitter),
|
4309
|
+
download: sinon.match.instanceOf(EventEmitter),
|
4147
4310
|
});
|
4148
4311
|
};
|
4149
4312
|
|
@@ -4171,6 +4334,8 @@ describe('plugin-meetings', () => {
|
|
4171
4334
|
},
|
4172
4335
|
],
|
4173
4336
|
},
|
4337
|
+
upload: sinon.match.instanceOf(EventEmitter),
|
4338
|
+
download: sinon.match.instanceOf(EventEmitter),
|
4174
4339
|
});
|
4175
4340
|
};
|
4176
4341
|
|
@@ -4195,6 +4360,8 @@ describe('plugin-meetings', () => {
|
|
4195
4360
|
respOnlySdp: true,
|
4196
4361
|
usingResource: null,
|
4197
4362
|
},
|
4363
|
+
upload: sinon.match.instanceOf(EventEmitter),
|
4364
|
+
download: sinon.match.instanceOf(EventEmitter),
|
4198
4365
|
});
|
4199
4366
|
};
|
4200
4367
|
|
@@ -5213,7 +5380,10 @@ describe('plugin-meetings', () => {
|
|
5213
5380
|
// and check that when we fallback to transcoded we still do another TURN discovery
|
5214
5381
|
await runCheck(
|
5215
5382
|
{
|
5216
|
-
|
5383
|
+
urls: [
|
5384
|
+
'turns:turn-server-url1:443?transport=tcp',
|
5385
|
+
'turns:turn-server-url2:443?transport=tcp',
|
5386
|
+
],
|
5217
5387
|
username: 'turn user',
|
5218
5388
|
password: 'turn password',
|
5219
5389
|
},
|
@@ -5227,7 +5397,10 @@ describe('plugin-meetings', () => {
|
|
5227
5397
|
// but doing it just for completeness
|
5228
5398
|
await runCheck(
|
5229
5399
|
{
|
5230
|
-
|
5400
|
+
urls: [
|
5401
|
+
'turns:turn-server-url1:443?transport=tcp',
|
5402
|
+
'turns:turn-server-url2:443?transport=tcp',
|
5403
|
+
],
|
5231
5404
|
username: 'turn user',
|
5232
5405
|
password: 'turn password',
|
5233
5406
|
},
|
@@ -6337,7 +6510,10 @@ describe('plugin-meetings', () => {
|
|
6337
6510
|
.throws(new MeetingInfoV2JoinForbiddenError(403003, FAKE_MEETING_INFO)),
|
6338
6511
|
};
|
6339
6512
|
|
6340
|
-
await assert.isRejected(
|
6513
|
+
await assert.isRejected(
|
6514
|
+
meeting.fetchMeetingInfo({sendCAevents: true}),
|
6515
|
+
JoinForbiddenError
|
6516
|
+
);
|
6341
6517
|
|
6342
6518
|
assert.calledWith(
|
6343
6519
|
meeting.attrs.meetingInfoProvider.fetchMeetingInfo,
|
@@ -6353,10 +6529,7 @@ describe('plugin-meetings', () => {
|
|
6353
6529
|
|
6354
6530
|
assert.deepEqual(meeting.meetingInfo, FAKE_MEETING_INFO);
|
6355
6531
|
assert.equal(meeting.meetingInfoFailureCode, 403003);
|
6356
|
-
assert.equal(
|
6357
|
-
meeting.meetingInfoFailureReason,
|
6358
|
-
MEETING_INFO_FAILURE_REASON.NOT_REACH_JBH
|
6359
|
-
);
|
6532
|
+
assert.equal(meeting.meetingInfoFailureReason, MEETING_INFO_FAILURE_REASON.NOT_REACH_JBH);
|
6360
6533
|
assert.equal(meeting.requiredCaptcha, null);
|
6361
6534
|
});
|
6362
6535
|
|
@@ -6733,15 +6906,10 @@ describe('plugin-meetings', () => {
|
|
6733
6906
|
meeting.attrs.meetingInfoProvider = {
|
6734
6907
|
fetchMeetingInfo: sinon
|
6735
6908
|
.stub()
|
6736
|
-
.throws(
|
6737
|
-
new MeetingInfoV2JoinWebinarError(403021, FAKE_MEETING_INFO, 'a message')
|
6738
|
-
),
|
6909
|
+
.throws(new MeetingInfoV2JoinWebinarError(403021, FAKE_MEETING_INFO, 'a message')),
|
6739
6910
|
};
|
6740
6911
|
|
6741
|
-
await assert.isRejected(
|
6742
|
-
meeting.fetchMeetingInfo({sendCAevents: true}),
|
6743
|
-
JoinWebinarError
|
6744
|
-
);
|
6912
|
+
await assert.isRejected(meeting.fetchMeetingInfo({sendCAevents: true}), JoinWebinarError);
|
6745
6913
|
|
6746
6914
|
assert.deepEqual(meeting.meetingInfo, FAKE_MEETING_INFO);
|
6747
6915
|
assert.equal(
|
@@ -6756,15 +6924,10 @@ describe('plugin-meetings', () => {
|
|
6756
6924
|
meeting.attrs.meetingInfoProvider = {
|
6757
6925
|
fetchMeetingInfo: sinon
|
6758
6926
|
.stub()
|
6759
|
-
.throws(
|
6760
|
-
new MeetingInfoV2JoinWebinarError(403026, FAKE_MEETING_INFO, 'a message')
|
6761
|
-
),
|
6927
|
+
.throws(new MeetingInfoV2JoinWebinarError(403026, FAKE_MEETING_INFO, 'a message')),
|
6762
6928
|
};
|
6763
6929
|
|
6764
|
-
await assert.isRejected(
|
6765
|
-
meeting.fetchMeetingInfo({sendCAevents: true}),
|
6766
|
-
JoinWebinarError
|
6767
|
-
);
|
6930
|
+
await assert.isRejected(meeting.fetchMeetingInfo({sendCAevents: true}), JoinWebinarError);
|
6768
6931
|
|
6769
6932
|
assert.deepEqual(meeting.meetingInfo, FAKE_MEETING_INFO);
|
6770
6933
|
assert.equal(
|
@@ -6779,15 +6942,10 @@ describe('plugin-meetings', () => {
|
|
6779
6942
|
meeting.attrs.meetingInfoProvider = {
|
6780
6943
|
fetchMeetingInfo: sinon
|
6781
6944
|
.stub()
|
6782
|
-
.throws(
|
6783
|
-
new MeetingInfoV2JoinWebinarError(403037, FAKE_MEETING_INFO, 'a message')
|
6784
|
-
),
|
6945
|
+
.throws(new MeetingInfoV2JoinWebinarError(403037, FAKE_MEETING_INFO, 'a message')),
|
6785
6946
|
};
|
6786
6947
|
|
6787
|
-
await assert.isRejected(
|
6788
|
-
meeting.fetchMeetingInfo({sendCAevents: true}),
|
6789
|
-
JoinWebinarError
|
6790
|
-
);
|
6948
|
+
await assert.isRejected(meeting.fetchMeetingInfo({sendCAevents: true}), JoinWebinarError);
|
6791
6949
|
|
6792
6950
|
assert.deepEqual(meeting.meetingInfo, FAKE_MEETING_INFO);
|
6793
6951
|
assert.equal(
|
@@ -7524,6 +7682,27 @@ describe('plugin-meetings', () => {
|
|
7524
7682
|
});
|
7525
7683
|
});
|
7526
7684
|
|
7685
|
+
describe('#setIsoLocalClientMeetingJoinTime', () => {
|
7686
|
+
it('should fallback to system clock ISO string when given an undefined value', () => {
|
7687
|
+
const currentSystemTime = new Date().toISOString();
|
7688
|
+
meeting.isoLocalClientMeetingJoinTime = undefined;
|
7689
|
+
assert.equal(meeting.isoLocalClientMeetingJoinTime, currentSystemTime);
|
7690
|
+
});
|
7691
|
+
|
7692
|
+
it('should fallback to system clock ISO string when given an invalid value', () => {
|
7693
|
+
const currentSystemTime = new Date().toISOString();
|
7694
|
+
meeting.isoLocalClientMeetingJoinTime = 'invalid-date';
|
7695
|
+
assert.equal(meeting.isoLocalClientMeetingJoinTime, currentSystemTime);
|
7696
|
+
});
|
7697
|
+
|
7698
|
+
it('should set the isoLocalClientMeetingJoinTime correctly for a valid date string', () => {
|
7699
|
+
const validDateString = 'Tue, 01 Apr 2025 13:00:36 GMT';
|
7700
|
+
const expectedISOString = new Date(validDateString).toISOString();
|
7701
|
+
meeting.isoLocalClientMeetingJoinTime = validDateString;
|
7702
|
+
assert.equal(meeting.isoLocalClientMeetingJoinTime, expectedISOString);
|
7703
|
+
});
|
7704
|
+
});
|
7705
|
+
|
7527
7706
|
describe('#updateCallStateForMetrics', () => {
|
7528
7707
|
it('should update the callState, overriding existing values', () => {
|
7529
7708
|
assert.deepEqual(meeting.callStateForMetrics, {correlationId, sessionCorrelationId: ''});
|
@@ -7605,6 +7784,12 @@ describe('plugin-meetings', () => {
|
|
7605
7784
|
meeting.audio = {handleLocalStreamChange: sinon.stub()};
|
7606
7785
|
meeting.video = {handleLocalStreamChange: sinon.stub()};
|
7607
7786
|
meeting.statsAnalyzer = {updateMediaStatus: sinon.stub()};
|
7787
|
+
meeting.shareCAEventSentStatus = {
|
7788
|
+
transmitStart: false,
|
7789
|
+
transmitStop: false,
|
7790
|
+
receiveStart: false,
|
7791
|
+
receiveStop: false,
|
7792
|
+
};
|
7608
7793
|
fakeMultistreamRoapMediaConnection = {
|
7609
7794
|
createSendSlot: () => {
|
7610
7795
|
return {
|
@@ -7672,6 +7857,9 @@ describe('plugin-meetings', () => {
|
|
7672
7857
|
});
|
7673
7858
|
assert.equal(meeting.mediaProperties.mediaDirection.sendShare, true);
|
7674
7859
|
|
7860
|
+
assert.equal(meeting.shareCAEventSentStatus.transmitStart, false);
|
7861
|
+
assert.equal(meeting.shareCAEventSentStatus.transmitStop, false);
|
7862
|
+
|
7675
7863
|
assert.calledWith(meeting.statsAnalyzer.updateMediaStatus, {
|
7676
7864
|
expected: {sendShare: true},
|
7677
7865
|
});
|
@@ -7692,18 +7880,23 @@ describe('plugin-meetings', () => {
|
|
7692
7880
|
assert.equal(meeting.mediaProperties.shareAudioStream, stream);
|
7693
7881
|
assert.equal(meeting.mediaProperties.mediaDirection.sendShare, true);
|
7694
7882
|
|
7883
|
+
assert.equal(meeting.shareCAEventSentStatus.transmitStart, false);
|
7884
|
+
assert.equal(meeting.shareCAEventSentStatus.transmitStop, false);
|
7885
|
+
|
7695
7886
|
assert.calledWith(meeting.statsAnalyzer.updateMediaStatus, {
|
7696
7887
|
expected: {sendShare: true},
|
7697
7888
|
});
|
7698
7889
|
};
|
7699
7890
|
|
7700
7891
|
it('requests screen share floor and publishes the screen share video stream', async () => {
|
7892
|
+
meeting.shareCAEventSentStatus.transmitStart = true;
|
7701
7893
|
await meeting.publishStreams({screenShare: {video: videoShareStream}});
|
7702
7894
|
|
7703
7895
|
checkScreenShareVideoPublished(videoShareStream);
|
7704
7896
|
});
|
7705
7897
|
|
7706
7898
|
it('requests screen share floor and publishes the screen share audio stream', async () => {
|
7899
|
+
meeting.shareCAEventSentStatus.transmitStart = true;
|
7707
7900
|
await meeting.publishStreams({screenShare: {audio: audioShareStream}});
|
7708
7901
|
|
7709
7902
|
checkScreenShareAudioPublished(audioShareStream);
|
@@ -8590,13 +8783,19 @@ describe('plugin-meetings', () => {
|
|
8590
8783
|
const fakeErrorMessage = 'test error';
|
8591
8784
|
const fakeRootCauseName = 'root cause name';
|
8592
8785
|
const fakeErrorName = 'test error name';
|
8786
|
+
let clock;
|
8593
8787
|
|
8594
8788
|
beforeEach(() => {
|
8789
|
+
clock = sinon.useFakeTimers();
|
8595
8790
|
meeting.setupMediaConnectionListeners();
|
8596
8791
|
webex.internal.newMetrics.submitClientEvent.resetHistory();
|
8597
8792
|
Metrics.sendBehavioralMetric.resetHistory();
|
8598
8793
|
});
|
8599
8794
|
|
8795
|
+
afterEach(() => {
|
8796
|
+
clock.restore();
|
8797
|
+
});
|
8798
|
+
|
8600
8799
|
const checkMetricSent = (event, error) => {
|
8601
8800
|
assert.calledOnce(webex.internal.newMetrics.submitClientEvent);
|
8602
8801
|
assert.calledWithMatch(webex.internal.newMetrics.submitClientEvent, {
|
@@ -8665,6 +8864,13 @@ describe('plugin-meetings', () => {
|
|
8665
8864
|
});
|
8666
8865
|
|
8667
8866
|
it('should send metrics for SdpAnswerHandlingError error', () => {
|
8867
|
+
meeting.sdpResponseTimer = '1234';
|
8868
|
+
meeting.deferSDPAnswer = {
|
8869
|
+
reject: sinon.stub(),
|
8870
|
+
};
|
8871
|
+
|
8872
|
+
const clearTimeoutSpy = sinon.spy(clock, 'clearTimeout');
|
8873
|
+
|
8668
8874
|
const fakeError = new Errors.SdpAnswerHandlingError(fakeErrorMessage, {
|
8669
8875
|
name: fakeErrorName,
|
8670
8876
|
cause: {name: fakeRootCauseName},
|
@@ -8679,6 +8885,8 @@ describe('plugin-meetings', () => {
|
|
8679
8885
|
fakeErrorMessage,
|
8680
8886
|
fakeRootCauseName
|
8681
8887
|
);
|
8888
|
+
assert.calledOnce(meeting.deferSDPAnswer.reject);
|
8889
|
+
assert.calledOnce(clearTimeoutSpy);
|
8682
8890
|
});
|
8683
8891
|
|
8684
8892
|
it('should send metrics for SdpError error', () => {
|
@@ -9223,22 +9431,22 @@ describe('plugin-meetings', () => {
|
|
9223
9431
|
const assertBrb = (enabled) => {
|
9224
9432
|
meeting.brbState = createBrbState(meeting, false);
|
9225
9433
|
meeting.locusInfo.emit(
|
9226
|
-
{
|
9434
|
+
{function: 'test', file: 'test'},
|
9227
9435
|
LOCUSINFO.EVENTS.SELF_MEETING_BRB_CHANGED,
|
9228
|
-
{
|
9229
|
-
)
|
9436
|
+
{brb: {enabled}}
|
9437
|
+
);
|
9230
9438
|
assert.calledWithExactly(
|
9231
9439
|
TriggerProxy.trigger,
|
9232
9440
|
meeting,
|
9233
9441
|
{file: 'meeting/index', function: 'setUpLocusInfoSelfListener'},
|
9234
9442
|
EVENT_TRIGGERS.MEETING_SELF_BRB_UPDATE,
|
9235
|
-
{
|
9443
|
+
{payload: {brb: {enabled}}}
|
9236
9444
|
);
|
9237
|
-
}
|
9445
|
+
};
|
9238
9446
|
|
9239
9447
|
assertBrb(true);
|
9240
9448
|
assertBrb(false);
|
9241
|
-
})
|
9449
|
+
});
|
9242
9450
|
|
9243
9451
|
it('listens to the interpretation changed event', () => {
|
9244
9452
|
meeting.simultaneousInterpretation.updateSelfInterpretation = sinon.stub();
|
@@ -9584,6 +9792,42 @@ describe('plugin-meetings', () => {
|
|
9584
9792
|
);
|
9585
9793
|
});
|
9586
9794
|
|
9795
|
+
it('listens to CONTROLS_ANNOTATION_CHANGED', async () => {
|
9796
|
+
const state = {example: 'value'};
|
9797
|
+
|
9798
|
+
await meeting.locusInfo.emitScoped(
|
9799
|
+
{function: 'test', file: 'test'},
|
9800
|
+
LOCUSINFO.EVENTS.CONTROLS_ANNOTATION_CHANGED,
|
9801
|
+
{state}
|
9802
|
+
);
|
9803
|
+
|
9804
|
+
assert.calledWith(
|
9805
|
+
TriggerProxy.trigger,
|
9806
|
+
meeting,
|
9807
|
+
{file: 'meeting/index', function: 'setupLocusControlsListener'},
|
9808
|
+
EVENT_TRIGGERS.MEETING_CONTROLS_ANNOTATION_UPDATED,
|
9809
|
+
{state}
|
9810
|
+
);
|
9811
|
+
});
|
9812
|
+
|
9813
|
+
it('listens to CONTROLS_REMOTE_DESKTOP_CONTROL_CHANGED', async () => {
|
9814
|
+
const state = {example: 'value'};
|
9815
|
+
|
9816
|
+
await meeting.locusInfo.emitScoped(
|
9817
|
+
{function: 'test', file: 'test'},
|
9818
|
+
LOCUSINFO.EVENTS.CONTROLS_REMOTE_DESKTOP_CONTROL_CHANGED,
|
9819
|
+
{state}
|
9820
|
+
);
|
9821
|
+
|
9822
|
+
assert.calledWith(
|
9823
|
+
TriggerProxy.trigger,
|
9824
|
+
meeting,
|
9825
|
+
{file: 'meeting/index', function: 'setupLocusControlsListener'},
|
9826
|
+
EVENT_TRIGGERS.MEETING_CONTROLS_REMOTE_DESKTOP_CONTROL_UPDATED,
|
9827
|
+
{state}
|
9828
|
+
);
|
9829
|
+
});
|
9830
|
+
|
9587
9831
|
it('listens to the locus interpretation update event', () => {
|
9588
9832
|
const interpretation = {
|
9589
9833
|
siLanguages: [{languageCode: 20, languageName: 'en'}],
|
@@ -9922,6 +10166,22 @@ describe('plugin-meetings', () => {
|
|
9922
10166
|
});
|
9923
10167
|
});
|
9924
10168
|
|
10169
|
+
describe('#emailInput', () => {
|
10170
|
+
it('should set the email input', () => {
|
10171
|
+
assert.notOk(meeting.emailInput);
|
10172
|
+
meeting.emailInput = 'current';
|
10173
|
+
assert.equal(meeting.emailInput, 'current');
|
10174
|
+
});
|
10175
|
+
});
|
10176
|
+
|
10177
|
+
describe('#userNameInput', () => {
|
10178
|
+
it('should set the user name input', () => {
|
10179
|
+
assert.notOk(meeting.userNameInput);
|
10180
|
+
meeting.userNameInput = 'current';
|
10181
|
+
assert.equal(meeting.userNameInput, 'current');
|
10182
|
+
});
|
10183
|
+
});
|
10184
|
+
|
9925
10185
|
describe('#setPermissionTokenPayload', () => {
|
9926
10186
|
let now;
|
9927
10187
|
let clock;
|
@@ -10463,6 +10723,7 @@ describe('plugin-meetings', () => {
|
|
10463
10723
|
let canUserLowerSomeoneElsesHandSpy;
|
10464
10724
|
let waitingForOthersToJoinSpy;
|
10465
10725
|
let canSendReactionsSpy;
|
10726
|
+
let requiresPostMeetingDataConsentPromptSpy;
|
10466
10727
|
let canUserRenameSelfAndObservedSpy;
|
10467
10728
|
let canUserRenameOthersSpy;
|
10468
10729
|
let canShareWhiteBoardSpy;
|
@@ -10490,6 +10751,10 @@ describe('plugin-meetings', () => {
|
|
10490
10751
|
waitingForOthersToJoinSpy = sinon.spy(MeetingUtil, 'waitingForOthersToJoin');
|
10491
10752
|
canSendReactionsSpy = sinon.spy(MeetingUtil, 'canSendReactions');
|
10492
10753
|
canUserRenameSelfAndObservedSpy = sinon.spy(MeetingUtil, 'canUserRenameSelfAndObserved');
|
10754
|
+
requiresPostMeetingDataConsentPromptSpy = sinon.spy(
|
10755
|
+
MeetingUtil,
|
10756
|
+
'requiresPostMeetingDataConsentPrompt'
|
10757
|
+
);
|
10493
10758
|
canUserRenameOthersSpy = sinon.spy(MeetingUtil, 'canUserRenameOthers');
|
10494
10759
|
canShareWhiteBoardSpy = sinon.spy(MeetingUtil, 'canShareWhiteBoard');
|
10495
10760
|
});
|
@@ -10618,6 +10883,11 @@ describe('plugin-meetings', () => {
|
|
10618
10883
|
requiredDisplayHints: [],
|
10619
10884
|
requiredPolicies: [SELF_POLICY.SUPPORT_POLLING_AND_QA],
|
10620
10885
|
},
|
10886
|
+
{
|
10887
|
+
actionName: 'canShareWhiteBoard',
|
10888
|
+
requiredDisplayHints: [DISPLAY_HINTS.SHARE_WHITEBOARD],
|
10889
|
+
requiredPolicies: [SELF_POLICY.SUPPORT_WHITEBOARD],
|
10890
|
+
},
|
10621
10891
|
],
|
10622
10892
|
({
|
10623
10893
|
actionName,
|
@@ -11025,8 +11295,9 @@ describe('plugin-meetings', () => {
|
|
11025
11295
|
assert.calledWith(waitingForOthersToJoinSpy, userDisplayHints);
|
11026
11296
|
assert.calledWith(canSendReactionsSpy, null, userDisplayHints);
|
11027
11297
|
assert.calledWith(canUserRenameSelfAndObservedSpy, userDisplayHints);
|
11298
|
+
assert.calledWith(requiresPostMeetingDataConsentPromptSpy, userDisplayHints);
|
11028
11299
|
assert.calledWith(canUserRenameOthersSpy, userDisplayHints);
|
11029
|
-
assert.calledWith(canShareWhiteBoardSpy, userDisplayHints);
|
11300
|
+
assert.calledWith(canShareWhiteBoardSpy, userDisplayHints, selfUserPolicies);
|
11030
11301
|
|
11031
11302
|
assert.calledWith(ControlsOptionsUtil.hasHints, {
|
11032
11303
|
requiredHints: [DISPLAY_HINTS.MUTE_ALL],
|
@@ -11120,6 +11391,22 @@ describe('plugin-meetings', () => {
|
|
11120
11391
|
requiredPolicies: [SELF_POLICY.SUPPORT_VOIP],
|
11121
11392
|
policies: selfUserPolicies,
|
11122
11393
|
});
|
11394
|
+
assert.calledWith(ControlsOptionsUtil.hasHints, {
|
11395
|
+
requiredHints: [DISPLAY_HINTS.ENABLE_ANNOTATION_MEETING_OPTION],
|
11396
|
+
displayHints: userDisplayHints,
|
11397
|
+
});
|
11398
|
+
assert.calledWith(ControlsOptionsUtil.hasHints, {
|
11399
|
+
requiredHints: [DISPLAY_HINTS.DISABLE_ANNOTATION_MEETING_OPTION],
|
11400
|
+
displayHints: userDisplayHints,
|
11401
|
+
});
|
11402
|
+
assert.calledWith(ControlsOptionsUtil.hasHints, {
|
11403
|
+
requiredHints: [DISPLAY_HINTS.ENABLE_RDC_MEETING_OPTION],
|
11404
|
+
displayHints: userDisplayHints,
|
11405
|
+
});
|
11406
|
+
assert.calledWith(ControlsOptionsUtil.hasHints, {
|
11407
|
+
requiredHints: [DISPLAY_HINTS.DISABLE_RDC_MEETING_OPTION],
|
11408
|
+
displayHints: userDisplayHints,
|
11409
|
+
});
|
11123
11410
|
|
11124
11411
|
assert.calledWith(
|
11125
11412
|
TriggerProxy.trigger,
|
@@ -11326,18 +11613,21 @@ describe('plugin-meetings', () => {
|
|
11326
11613
|
);
|
11327
11614
|
});
|
11328
11615
|
|
11329
|
-
|
11330
11616
|
it('connect ps data channel if ps started in webinar', async () => {
|
11331
11617
|
meeting.joinedWith = {state: 'JOINED'};
|
11332
|
-
meeting.locusInfo = {
|
11618
|
+
meeting.locusInfo = {
|
11619
|
+
url: 'a url',
|
11620
|
+
info: {
|
11621
|
+
datachannelUrl: 'a datachannel url',
|
11622
|
+
practiceSessionDatachannelUrl: 'a ps datachannel url',
|
11623
|
+
},
|
11624
|
+
};
|
11333
11625
|
meeting.webinar.isJoinPracticeSessionDataChannel = sinon.stub().returns(true);
|
11334
11626
|
await meeting.updateLLMConnection();
|
11335
11627
|
|
11336
11628
|
assert.notCalled(webex.internal.llm.disconnectLLM);
|
11337
11629
|
assert.calledWith(webex.internal.llm.registerAndConnect, 'a url', 'a ps datachannel url');
|
11338
|
-
|
11339
11630
|
});
|
11340
|
-
|
11341
11631
|
});
|
11342
11632
|
|
11343
11633
|
describe('#setLocus', () => {
|
@@ -11755,24 +12045,29 @@ describe('plugin-meetings', () => {
|
|
11755
12045
|
|
11756
12046
|
activeSharingId.whiteboard = beneficiaryId;
|
11757
12047
|
|
11758
|
-
eventTrigger.share.push(
|
11759
|
-
|
11760
|
-
|
11761
|
-
|
11762
|
-
|
11763
|
-
|
11764
|
-
|
11765
|
-
|
11766
|
-
|
11767
|
-
|
11768
|
-
|
11769
|
-
|
11770
|
-
|
11771
|
-
|
11772
|
-
|
11773
|
-
|
11774
|
-
|
12048
|
+
eventTrigger.share.push(
|
12049
|
+
meeting.webinar.selfIsAttendee
|
12050
|
+
? {
|
12051
|
+
eventName: EVENT_TRIGGERS.MEETING_STARTED_SHARING_REMOTE,
|
12052
|
+
functionName: 'remoteShare',
|
12053
|
+
eventPayload: {
|
12054
|
+
memberId: null,
|
12055
|
+
url,
|
12056
|
+
shareInstanceId,
|
12057
|
+
annotationInfo: undefined,
|
12058
|
+
resourceType: undefined,
|
12059
|
+
},
|
12060
|
+
}
|
12061
|
+
: {
|
12062
|
+
eventName: EVENT_TRIGGERS.MEETING_STARTED_SHARING_WHITEBOARD,
|
12063
|
+
functionName: 'startWhiteboardShare',
|
12064
|
+
eventPayload: {resourceUrl, memberId: beneficiaryId},
|
12065
|
+
}
|
12066
|
+
);
|
11775
12067
|
|
12068
|
+
shareStatus = meeting.webinar.selfIsAttendee
|
12069
|
+
? SHARE_STATUS.REMOTE_SHARE_ACTIVE
|
12070
|
+
: SHARE_STATUS.WHITEBOARD_SHARE_ACTIVE;
|
11776
12071
|
}
|
11777
12072
|
|
11778
12073
|
if (eventTrigger.member) {
|
@@ -11804,24 +12099,29 @@ describe('plugin-meetings', () => {
|
|
11804
12099
|
newPayload.current.content.disposition = FLOOR_ACTION.ACCEPTED;
|
11805
12100
|
newPayload.current.content.beneficiaryId = otherBeneficiaryId;
|
11806
12101
|
|
11807
|
-
eventTrigger.share.push(
|
11808
|
-
|
11809
|
-
|
11810
|
-
|
11811
|
-
|
11812
|
-
|
11813
|
-
|
11814
|
-
|
11815
|
-
|
11816
|
-
|
11817
|
-
|
11818
|
-
|
11819
|
-
|
11820
|
-
|
11821
|
-
|
11822
|
-
|
11823
|
-
|
12102
|
+
eventTrigger.share.push(
|
12103
|
+
meeting.webinar.selfIsAttendee
|
12104
|
+
? {
|
12105
|
+
eventName: EVENT_TRIGGERS.MEETING_STARTED_SHARING_REMOTE,
|
12106
|
+
functionName: 'remoteShare',
|
12107
|
+
eventPayload: {
|
12108
|
+
memberId: null,
|
12109
|
+
url,
|
12110
|
+
shareInstanceId,
|
12111
|
+
annotationInfo: undefined,
|
12112
|
+
resourceType: undefined,
|
12113
|
+
},
|
12114
|
+
}
|
12115
|
+
: {
|
12116
|
+
eventName: EVENT_TRIGGERS.MEETING_STARTED_SHARING_WHITEBOARD,
|
12117
|
+
functionName: 'startWhiteboardShare',
|
12118
|
+
eventPayload: {resourceUrl, memberId: beneficiaryId},
|
12119
|
+
}
|
12120
|
+
);
|
11824
12121
|
|
12122
|
+
shareStatus = meeting.webinar.selfIsAttendee
|
12123
|
+
? SHARE_STATUS.REMOTE_SHARE_ACTIVE
|
12124
|
+
: SHARE_STATUS.WHITEBOARD_SHARE_ACTIVE;
|
11825
12125
|
} else {
|
11826
12126
|
eventTrigger.share.push({
|
11827
12127
|
eventName: EVENT_TRIGGERS.MEETING_STOPPED_SHARING_WHITEBOARD,
|
@@ -11951,24 +12251,26 @@ describe('plugin-meetings', () => {
|
|
11951
12251
|
describe('Whiteboard Share - Webinar Attendee', () => {
|
11952
12252
|
it('Scenario #1: Whiteboard sharing as a webinar attendee', () => {
|
11953
12253
|
// Set the webinar attendee flag
|
11954
|
-
meeting.webinar = {
|
12254
|
+
meeting.webinar = {selfIsAttendee: true};
|
11955
12255
|
meeting.locusInfo.info.isWebinar = true;
|
12256
|
+
meeting.shareCAEventSentStatus.receiveStart = true;
|
12257
|
+
meeting.shareCAEventSentStatus.receiveStop = true;
|
11956
12258
|
|
11957
12259
|
// Step 1: Start sharing whiteboard A
|
11958
12260
|
const data1 = generateData(
|
11959
|
-
blankPayload,
|
11960
|
-
true,
|
11961
|
-
false,
|
11962
|
-
USER_IDS.REMOTE_A,
|
12261
|
+
blankPayload, // Initial payload
|
12262
|
+
true, // isGranting: Granting share
|
12263
|
+
false, // isContent: Whiteboard (not content)
|
12264
|
+
USER_IDS.REMOTE_A, // Beneficiary ID: Remote user A
|
11963
12265
|
RESOURCE_URLS.WHITEBOARD_A // Resource URL: Whiteboard A
|
11964
12266
|
);
|
11965
12267
|
|
11966
12268
|
// Step 2: Stop sharing whiteboard A
|
11967
12269
|
const data2 = generateData(
|
11968
|
-
data1.payload,
|
11969
|
-
false,
|
11970
|
-
false,
|
11971
|
-
USER_IDS.REMOTE_A
|
12270
|
+
data1.payload, // Updated payload from Step 1
|
12271
|
+
false, // isGranting: Stopping share
|
12272
|
+
false, // isContent: Whiteboard
|
12273
|
+
USER_IDS.REMOTE_A // Beneficiary ID: Remote user A
|
11972
12274
|
);
|
11973
12275
|
|
11974
12276
|
// Validate the payload changes and status updates
|
@@ -11976,10 +12278,11 @@ describe('plugin-meetings', () => {
|
|
11976
12278
|
|
11977
12279
|
// Specific assertions for webinar attendee status
|
11978
12280
|
assert.equal(meeting.shareStatus, SHARE_STATUS.REMOTE_SHARE_ACTIVE);
|
12281
|
+
assert.equal(meeting.shareCAEventSentStatus.receiveStart, false);
|
12282
|
+
assert.equal(meeting.shareCAEventSentStatus.receiveStop, false);
|
11979
12283
|
});
|
11980
12284
|
});
|
11981
12285
|
|
11982
|
-
|
11983
12286
|
describe('Whiteboard A --> Whiteboard B', () => {
|
11984
12287
|
it('Scenario #1: you share both whiteboards', () => {
|
11985
12288
|
const data1 = generateData(
|
@@ -12632,6 +12935,31 @@ describe('plugin-meetings', () => {
|
|
12632
12935
|
});
|
12633
12936
|
});
|
12634
12937
|
});
|
12938
|
+
|
12939
|
+
describe('handleShareVideoStreamMuteStateChange', () => {
|
12940
|
+
it('should emit MEETING_SHARE_VIDEO_MUTE_STATE_CHANGE event with correct fields', () => {
|
12941
|
+
meeting.isMultistream = true;
|
12942
|
+
meeting.statsAnalyzer = {shareVideoEncoderImplementation: 'OpenH264'};
|
12943
|
+
meeting.mediaProperties.shareVideoStream = {
|
12944
|
+
getSettings: sinon.stub().returns({displaySurface: 'monitor', frameRate: 30}),
|
12945
|
+
};
|
12946
|
+
|
12947
|
+
meeting.handleShareVideoStreamMuteStateChange(true);
|
12948
|
+
|
12949
|
+
assert.calledOnceWithExactly(
|
12950
|
+
Metrics.sendBehavioralMetric,
|
12951
|
+
BEHAVIORAL_METRICS.MEETING_SHARE_VIDEO_MUTE_STATE_CHANGE,
|
12952
|
+
{
|
12953
|
+
correlationId: meeting.correlationId,
|
12954
|
+
muted: true,
|
12955
|
+
encoderImplementation: 'OpenH264',
|
12956
|
+
displaySurface: 'monitor',
|
12957
|
+
isMultistream: true,
|
12958
|
+
frameRate: 30,
|
12959
|
+
}
|
12960
|
+
);
|
12961
|
+
});
|
12962
|
+
});
|
12635
12963
|
});
|
12636
12964
|
|
12637
12965
|
describe('#startKeepAlive', () => {
|
@@ -12799,6 +13127,38 @@ describe('plugin-meetings', () => {
|
|
12799
13127
|
});
|
12800
13128
|
});
|
12801
13129
|
|
13130
|
+
describe('#setPostMeetingDataConsent', () => {
|
13131
|
+
it('should have #setPostMeetingDataConsent', () => {
|
13132
|
+
assert.exists(meeting.setPostMeetingDataConsent);
|
13133
|
+
});
|
13134
|
+
|
13135
|
+
beforeEach(() => {
|
13136
|
+
meeting.meetingRequest.setPostMeetingDataConsent = sinon
|
13137
|
+
.stub()
|
13138
|
+
.returns(Promise.resolve());
|
13139
|
+
});
|
13140
|
+
|
13141
|
+
[true, false].forEach((accept) => {
|
13142
|
+
it(`should send consent with ${accept}`, async () => {
|
13143
|
+
const id = uuidv4();
|
13144
|
+
meeting.locusUrl = `https://locus-test.wbx2.com/locus/api/v1/loci/${accept}`;
|
13145
|
+
meeting.deviceUrl = `https://wdm-test.wbx2.com/wdm/api/v1/devices/${accept}`;
|
13146
|
+
meeting.members.selfId = id;
|
13147
|
+
|
13148
|
+
const consentPromise = meeting.setPostMeetingDataConsent(accept);
|
13149
|
+
|
13150
|
+
assert.exists(consentPromise.then);
|
13151
|
+
await consentPromise;
|
13152
|
+
assert.calledOnceWithExactly(meeting.meetingRequest.setPostMeetingDataConsent, {
|
13153
|
+
locusUrl: `https://locus-test.wbx2.com/locus/api/v1/loci/${accept}`,
|
13154
|
+
postMeetingDataConsent: accept,
|
13155
|
+
selfId: id,
|
13156
|
+
deviceUrl: `https://wdm-test.wbx2.com/wdm/api/v1/devices/${accept}`,
|
13157
|
+
});
|
13158
|
+
});
|
13159
|
+
});
|
13160
|
+
});
|
13161
|
+
|
12802
13162
|
describe('#sendReaction', () => {
|
12803
13163
|
it('should have #sendReaction', () => {
|
12804
13164
|
assert.exists(meeting.sendReaction);
|
@@ -13290,7 +13650,7 @@ describe('plugin-meetings', () => {
|
|
13290
13650
|
await meeting.roapMessageReceived(fakeMessage);
|
13291
13651
|
|
13292
13652
|
assert.fail('Expected MultistreamNotSupportedError to be thrown');
|
13293
|
-
} catch(e) {
|
13653
|
+
} catch (e) {
|
13294
13654
|
assert.isTrue(e instanceof MultistreamNotSupportedError);
|
13295
13655
|
}
|
13296
13656
|
|