@webex/plugin-meetings 3.8.0-next.5 → 3.8.0-next.51
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 +544 -324
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/locusMediaRequest.js +26 -23
- 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 +27 -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 +69 -1
- package/dist/meetings/index.js.map +1 -1
- package/dist/meetings/util.js +14 -0
- package/dist/meetings/util.js.map +1 -1
- package/dist/member/index.js +10 -0
- package/dist/member/index.js.map +1 -1
- package/dist/member/util.js +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 +63 -27
- package/dist/reachability/clusterReachability.js.map +1 -1
- package/dist/reachability/index.js +112 -47
- package/dist/reachability/index.js.map +1 -1
- package/dist/reachability/reachability.types.js +14 -0
- package/dist/reachability/reachability.types.js.map +1 -1
- package/dist/reachability/request.js +19 -3
- package/dist/reachability/request.js.map +1 -1
- package/dist/reconnection-manager/index.js +2 -2
- package/dist/reconnection-manager/index.js.map +1 -1
- package/dist/recording-controller/util.js +5 -5
- package/dist/recording-controller/util.js.map +1 -1
- package/dist/roap/index.js.map +1 -1
- package/dist/roap/turnDiscovery.js +45 -27
- package/dist/roap/turnDiscovery.js.map +1 -1
- package/dist/roap/types.js +17 -0
- package/dist/roap/types.js.map +1 -0
- package/dist/types/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 +50 -3
- 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 +8 -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 +15 -7
- package/dist/types/reachability/index.d.ts +10 -1
- package/dist/types/reachability/reachability.types.d.ts +5 -0
- package/dist/types/roap/index.d.ts +3 -2
- package/dist/types/roap/turnDiscovery.d.ts +5 -17
- package/dist/types/roap/types.d.ts +16 -0
- package/dist/webinar/index.js +1 -1
- package/package.json +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 +351 -99
- package/src/meeting/locusMediaRequest.ts +33 -23
- 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 +27 -2
- package/src/meeting-info/meeting-info-v2.ts +247 -6
- package/src/meetings/index.ts +87 -1
- package/src/meetings/util.ts +18 -0
- 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 +73 -26
- package/src/reachability/index.ts +70 -1
- package/src/reachability/reachability.types.ts +6 -0
- package/src/reachability/request.ts +7 -0
- package/src/reconnection-manager/index.ts +2 -2
- package/src/recording-controller/util.ts +17 -13
- package/src/roap/index.ts +3 -7
- package/src/roap/turnDiscovery.ts +34 -39
- package/src/roap/types.ts +23 -0
- package/test/unit/spec/controls-options-manager/util.js +120 -0
- package/test/unit/spec/locus-info/controlsUtils.js +103 -9
- package/test/unit/spec/locus-info/index.js +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 +558 -145
- package/test/unit/spec/meeting/locusMediaRequest.ts +101 -88
- 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 +123 -18
- package/test/unit/spec/meeting-info/meetinginfov2.js +443 -114
- package/test/unit/spec/meetings/index.js +96 -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 +88 -56
- package/test/unit/spec/reachability/index.ts +101 -0
- package/test/unit/spec/reachability/request.js +47 -2
- package/test/unit/spec/reconnection-manager/index.js +4 -4
- package/test/unit/spec/roap/turnDiscovery.ts +110 -28
@@ -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({
|
@@ -248,6 +253,7 @@ describe('plugin-meetings', () => {
|
|
248
253
|
getReachabilityResults: sinon.stub().resolves(undefined),
|
249
254
|
getReachabilityMetrics: sinon.stub().resolves({}),
|
250
255
|
stopReachability: sinon.stub(),
|
256
|
+
isSubnetReachable: sinon.stub().returns(true),
|
251
257
|
};
|
252
258
|
webex.internal.llm.on = sinon.stub();
|
253
259
|
webex.internal.newMetrics.callDiagnosticLatencies = new CallDiagnosticLatencies(
|
@@ -277,6 +283,8 @@ describe('plugin-meetings', () => {
|
|
277
283
|
test4 = `test4-${uuid.v4()}`;
|
278
284
|
testDestination = `testDestination-${uuid.v4()}`;
|
279
285
|
correlationId = uuid.v4();
|
286
|
+
uploadEvent = new EventEmitter();
|
287
|
+
uploadEvent.addListener('progress', () => {});
|
280
288
|
|
281
289
|
meeting = new Meeting(
|
282
290
|
{
|
@@ -667,7 +675,7 @@ describe('plugin-meetings', () => {
|
|
667
675
|
beforeEach(() => {
|
668
676
|
meeting.join = sinon.stub().callsFake((joinOptions) => {
|
669
677
|
meeting.isMultistream = joinOptions.enableMultistream;
|
670
|
-
return Promise.resolve(fakeJoinResult)
|
678
|
+
return Promise.resolve(fakeJoinResult);
|
671
679
|
});
|
672
680
|
addMediaInternalStub = sinon
|
673
681
|
.stub(meeting, 'addMediaInternal')
|
@@ -1004,13 +1012,19 @@ describe('plugin-meetings', () => {
|
|
1004
1012
|
.stub()
|
1005
1013
|
.returns(fakeClientError);
|
1006
1014
|
|
1007
|
-
|
1008
|
-
await assert.isRejected(
|
1009
|
-
meeting.joinWithMedia({
|
1015
|
+
const promise = meeting.joinWithMedia({
|
1010
1016
|
joinOptions,
|
1011
1017
|
mediaOptions,
|
1012
1018
|
})
|
1013
|
-
|
1019
|
+
|
1020
|
+
// call joinWithMedia() - it should fail
|
1021
|
+
await assert.isRejected(promise);
|
1022
|
+
|
1023
|
+
const rejectedError = await promise.catch((error) => error);
|
1024
|
+
|
1025
|
+
// Since the SDK has sent the CA events, we need to mark this error as handled
|
1026
|
+
// so the client doesn't try and send CA events again
|
1027
|
+
assert.isTrue(rejectedError.handledBySdk);
|
1014
1028
|
|
1015
1029
|
// check the right CA events have been sent:
|
1016
1030
|
// calls at index 0 and 2 to submitClientEvent are for "client.media.capabilities" which we don't care about in this test
|
@@ -1070,7 +1084,11 @@ describe('plugin-meetings', () => {
|
|
1070
1084
|
mediaOptions,
|
1071
1085
|
});
|
1072
1086
|
|
1073
|
-
assert.deepEqual(result, {
|
1087
|
+
assert.deepEqual(result, {
|
1088
|
+
join: fakeJoinResult,
|
1089
|
+
media: undefined,
|
1090
|
+
multistreamEnabled: false,
|
1091
|
+
});
|
1074
1092
|
|
1075
1093
|
assert.calledOnce(meeting.join);
|
1076
1094
|
|
@@ -1174,7 +1192,10 @@ describe('plugin-meetings', () => {
|
|
1174
1192
|
type: addMediaError.name,
|
1175
1193
|
}
|
1176
1194
|
);
|
1177
|
-
assert.calledOnceWithExactly(meeting.leave, {
|
1195
|
+
assert.calledOnceWithExactly(meeting.leave, {
|
1196
|
+
resourceId: undefined,
|
1197
|
+
reason: 'joinWithMedia failure',
|
1198
|
+
});
|
1178
1199
|
});
|
1179
1200
|
});
|
1180
1201
|
|
@@ -1680,10 +1701,6 @@ describe('plugin-meetings', () => {
|
|
1680
1701
|
sandbox.stub(MeetingUtil, 'joinMeeting').returns(Promise.resolve(joinMeetingResult));
|
1681
1702
|
});
|
1682
1703
|
|
1683
|
-
afterEach(() => {
|
1684
|
-
assert.exists(meeting.isoLocalClientMeetingJoinTime);
|
1685
|
-
});
|
1686
|
-
|
1687
1704
|
it('should join the meeting and return promise', async () => {
|
1688
1705
|
const join = meeting.join({pstnAudioType: 'dial-in'});
|
1689
1706
|
meeting.config.enableAutomaticLLM = true;
|
@@ -1799,6 +1816,7 @@ describe('plugin-meetings', () => {
|
|
1799
1816
|
await meeting.join();
|
1800
1817
|
joinSucceeded = true;
|
1801
1818
|
} catch (e) {
|
1819
|
+
assert.isTrue(e.handledBySdk)
|
1802
1820
|
assert.instanceOf(e, IntentToJoinError);
|
1803
1821
|
}
|
1804
1822
|
assert.isFalse(joinSucceeded);
|
@@ -1851,7 +1869,20 @@ describe('plugin-meetings', () => {
|
|
1851
1869
|
});
|
1852
1870
|
});
|
1853
1871
|
it('should try to join the meeting and return promise reject', async () => {
|
1854
|
-
await meeting.join().catch(() => {
|
1872
|
+
await meeting.join().catch((e) => {
|
1873
|
+
assert.isTrue(e.handledBySdk);
|
1874
|
+
assert.calledOnce(MeetingUtil.joinMeeting);
|
1875
|
+
});
|
1876
|
+
});
|
1877
|
+
|
1878
|
+
it('should try to join the meeting and return deferred promise reject', async () => {
|
1879
|
+
|
1880
|
+
// call first
|
1881
|
+
meeting.join();
|
1882
|
+
|
1883
|
+
// call 2nd time will get the deferred promise
|
1884
|
+
await meeting.join().catch((e) => {
|
1885
|
+
assert.isTrue(e.handledBySdk);
|
1855
1886
|
assert.calledOnce(MeetingUtil.joinMeeting);
|
1856
1887
|
});
|
1857
1888
|
});
|
@@ -2098,6 +2129,7 @@ describe('plugin-meetings', () => {
|
|
2098
2129
|
someReachabilityMetric2: 'some value2',
|
2099
2130
|
}),
|
2100
2131
|
stopReachability: sinon.stub(),
|
2132
|
+
isSubnetReachable: sinon.stub().returns(false),
|
2101
2133
|
};
|
2102
2134
|
|
2103
2135
|
const forceRtcMetricsSend = sinon.stub().resolves();
|
@@ -2153,6 +2185,7 @@ describe('plugin-meetings', () => {
|
|
2153
2185
|
someReachabilityMetric1: 'some value1',
|
2154
2186
|
someReachabilityMetric2: 'some value2',
|
2155
2187
|
selectedCandidatePairChanges: 2,
|
2188
|
+
isSubnetReachable: false,
|
2156
2189
|
numTransports: 1,
|
2157
2190
|
iceCandidatesCount: 0,
|
2158
2191
|
}
|
@@ -2199,6 +2232,7 @@ describe('plugin-meetings', () => {
|
|
2199
2232
|
signalingState: 'unknown',
|
2200
2233
|
connectionState: 'unknown',
|
2201
2234
|
iceConnectionState: 'unknown',
|
2235
|
+
isSubnetReachable: true,
|
2202
2236
|
})
|
2203
2237
|
);
|
2204
2238
|
|
@@ -2213,6 +2247,7 @@ describe('plugin-meetings', () => {
|
|
2213
2247
|
someReachabilityMetric1: 'some value1',
|
2214
2248
|
someReachabilityMetric2: 'some value2',
|
2215
2249
|
}),
|
2250
|
+
isSubnetReachable: sinon.stub().returns(true),
|
2216
2251
|
};
|
2217
2252
|
|
2218
2253
|
meeting.waitForRemoteSDPAnswer = sinon.stub().rejects();
|
@@ -2263,6 +2298,7 @@ describe('plugin-meetings', () => {
|
|
2263
2298
|
selectedCandidatePairChanges: 2,
|
2264
2299
|
numTransports: 1,
|
2265
2300
|
iceCandidatesCount: 0,
|
2301
|
+
isSubnetReachable: true,
|
2266
2302
|
}
|
2267
2303
|
);
|
2268
2304
|
});
|
@@ -2320,6 +2356,7 @@ describe('plugin-meetings', () => {
|
|
2320
2356
|
signalingState: 'have-local-offer',
|
2321
2357
|
connectionState: 'connecting',
|
2322
2358
|
iceConnectionState: 'checking',
|
2359
|
+
isSubnetReachable: true,
|
2323
2360
|
})
|
2324
2361
|
);
|
2325
2362
|
|
@@ -2377,6 +2414,7 @@ describe('plugin-meetings', () => {
|
|
2377
2414
|
signalingState: 'have-local-offer',
|
2378
2415
|
connectionState: 'connecting',
|
2379
2416
|
iceConnectionState: 'checking',
|
2417
|
+
isSubnetReachable: true,
|
2380
2418
|
})
|
2381
2419
|
);
|
2382
2420
|
|
@@ -2655,7 +2693,7 @@ describe('plugin-meetings', () => {
|
|
2655
2693
|
|
2656
2694
|
meeting.roap.doTurnDiscovery = sinon.stub().resolves({
|
2657
2695
|
turnServerInfo: {
|
2658
|
-
|
2696
|
+
urls: [FAKE_TURN_URL],
|
2659
2697
|
username: FAKE_TURN_USER,
|
2660
2698
|
password: FAKE_TURN_PASSWORD,
|
2661
2699
|
},
|
@@ -2677,7 +2715,7 @@ describe('plugin-meetings', () => {
|
|
2677
2715
|
meeting.id,
|
2678
2716
|
sinon.match({
|
2679
2717
|
turnServerInfo: {
|
2680
|
-
|
2718
|
+
urls: [FAKE_TURN_URL],
|
2681
2719
|
username: FAKE_TURN_USER,
|
2682
2720
|
password: FAKE_TURN_PASSWORD,
|
2683
2721
|
},
|
@@ -2714,6 +2752,7 @@ describe('plugin-meetings', () => {
|
|
2714
2752
|
isWebexMediaBackendUnreachable: sinon.stub().resolves(false),
|
2715
2753
|
getReachabilityMetrics: sinon.stub().resolves(),
|
2716
2754
|
stopReachability: sinon.stub(),
|
2755
|
+
isSubnetReachable: sinon.stub().returns(true),
|
2717
2756
|
};
|
2718
2757
|
const MOCK_CLIENT_ERROR_CODE = 2004;
|
2719
2758
|
const generateClientErrorCodeForIceFailureStub = sinon
|
@@ -2735,7 +2774,7 @@ describe('plugin-meetings', () => {
|
|
2735
2774
|
.onSecondCall()
|
2736
2775
|
.returns({
|
2737
2776
|
turnServerInfo: {
|
2738
|
-
|
2777
|
+
urls: [FAKE_TURN_URL],
|
2739
2778
|
username: FAKE_TURN_USER,
|
2740
2779
|
password: FAKE_TURN_PASSWORD,
|
2741
2780
|
},
|
@@ -2893,6 +2932,7 @@ describe('plugin-meetings', () => {
|
|
2893
2932
|
selectedCandidatePairChanges: 2,
|
2894
2933
|
numTransports: 1,
|
2895
2934
|
iceCandidatesCount: 0,
|
2935
|
+
isSubnetReachable: true,
|
2896
2936
|
},
|
2897
2937
|
]);
|
2898
2938
|
|
@@ -2923,6 +2963,7 @@ describe('plugin-meetings', () => {
|
|
2923
2963
|
.resolves(false),
|
2924
2964
|
getReachabilityMetrics: sinon.stub().resolves({}),
|
2925
2965
|
stopReachability: sinon.stub(),
|
2966
|
+
isSubnetReachable: sinon.stub().returns(true),
|
2926
2967
|
};
|
2927
2968
|
const getErrorPayloadForClientErrorCodeStub =
|
2928
2969
|
(webex.internal.newMetrics.callDiagnosticMetrics.getErrorPayloadForClientErrorCode =
|
@@ -2947,7 +2988,7 @@ describe('plugin-meetings', () => {
|
|
2947
2988
|
.onSecondCall()
|
2948
2989
|
.returns({
|
2949
2990
|
turnServerInfo: {
|
2950
|
-
|
2991
|
+
urls: [FAKE_TURN_URL],
|
2951
2992
|
username: FAKE_TURN_USER,
|
2952
2993
|
password: FAKE_TURN_PASSWORD,
|
2953
2994
|
},
|
@@ -3090,6 +3131,7 @@ describe('plugin-meetings', () => {
|
|
3090
3131
|
retriedWithTurnServer: true,
|
3091
3132
|
isJoinWithMediaRetry: false,
|
3092
3133
|
iceCandidatesCount: 0,
|
3134
|
+
isSubnetReachable: true,
|
3093
3135
|
},
|
3094
3136
|
]);
|
3095
3137
|
meeting.roap.doTurnDiscovery;
|
@@ -3124,7 +3166,7 @@ describe('plugin-meetings', () => {
|
|
3124
3166
|
.onSecondCall()
|
3125
3167
|
.returns({
|
3126
3168
|
turnServerInfo: {
|
3127
|
-
|
3169
|
+
urls: [FAKE_TURN_URL],
|
3128
3170
|
username: FAKE_TURN_USER,
|
3129
3171
|
password: FAKE_TURN_PASSWORD,
|
3130
3172
|
},
|
@@ -3176,7 +3218,7 @@ describe('plugin-meetings', () => {
|
|
3176
3218
|
.onSecondCall()
|
3177
3219
|
.returns({
|
3178
3220
|
turnServerInfo: {
|
3179
|
-
|
3221
|
+
urls: [FAKE_TURN_URL],
|
3180
3222
|
username: FAKE_TURN_USER,
|
3181
3223
|
password: FAKE_TURN_PASSWORD,
|
3182
3224
|
},
|
@@ -3218,6 +3260,7 @@ describe('plugin-meetings', () => {
|
|
3218
3260
|
someReachabilityMetric2: 'some value2',
|
3219
3261
|
}),
|
3220
3262
|
stopReachability: sinon.stub(),
|
3263
|
+
isSubnetReachable: sinon.stub().returns(true),
|
3221
3264
|
};
|
3222
3265
|
meeting.iceCandidatesCount = 3;
|
3223
3266
|
meeting.iceCandidateErrors.set('701_error', 3);
|
@@ -3245,6 +3288,7 @@ describe('plugin-meetings', () => {
|
|
3245
3288
|
iceCandidatesCount: 3,
|
3246
3289
|
'701_error': 3,
|
3247
3290
|
'701_turn_host_lookup_received_error': 1,
|
3291
|
+
isSubnetReachable: true,
|
3248
3292
|
}
|
3249
3293
|
);
|
3250
3294
|
|
@@ -3307,6 +3351,7 @@ describe('plugin-meetings', () => {
|
|
3307
3351
|
iceConnectionState: 'unknown',
|
3308
3352
|
selectedCandidatePairChanges: 2,
|
3309
3353
|
numTransports: 1,
|
3354
|
+
isSubnetReachable: true,
|
3310
3355
|
iceCandidatesCount: 0,
|
3311
3356
|
}
|
3312
3357
|
);
|
@@ -3368,6 +3413,7 @@ describe('plugin-meetings', () => {
|
|
3368
3413
|
numTransports: 1,
|
3369
3414
|
'701_error': 2,
|
3370
3415
|
'701_turn_host_lookup_received_error': 1,
|
3416
|
+
isSubnetReachable: true,
|
3371
3417
|
iceCandidatesCount: 0,
|
3372
3418
|
}
|
3373
3419
|
);
|
@@ -3427,6 +3473,40 @@ describe('plugin-meetings', () => {
|
|
3427
3473
|
});
|
3428
3474
|
});
|
3429
3475
|
|
3476
|
+
it('LOCAL_MEDIA_STARTED triggers "meeting:media:local:start" event and does not send metric because we already have', async () => {
|
3477
|
+
meeting.shareCAEventSentStatus = {
|
3478
|
+
transmitStart: true,
|
3479
|
+
transmitStop: false,
|
3480
|
+
receiveStart: false,
|
3481
|
+
receiveStop: false,
|
3482
|
+
};
|
3483
|
+
statsAnalyzerStub.emit(
|
3484
|
+
{file: 'test', function: 'test'},
|
3485
|
+
StatsAnalyzerEventNames.LOCAL_MEDIA_STARTED,
|
3486
|
+
{mediaType: 'share'}
|
3487
|
+
);
|
3488
|
+
|
3489
|
+
assert.calledWith(
|
3490
|
+
TriggerProxy.trigger,
|
3491
|
+
sinon.match.instanceOf(Meeting),
|
3492
|
+
{
|
3493
|
+
file: 'meeting/index',
|
3494
|
+
function: 'addMedia',
|
3495
|
+
},
|
3496
|
+
EVENT_TRIGGERS.MEETING_MEDIA_LOCAL_STARTED,
|
3497
|
+
{
|
3498
|
+
mediaType: 'share',
|
3499
|
+
}
|
3500
|
+
);
|
3501
|
+
assert.neverCalledWith(webex.internal.newMetrics.submitClientEvent, {
|
3502
|
+
name: 'client.media.tx.start',
|
3503
|
+
payload: {mediaType: 'share', shareInstanceId: meeting.remoteShareInstanceId},
|
3504
|
+
options: {
|
3505
|
+
meetingId: meeting.id,
|
3506
|
+
},
|
3507
|
+
});
|
3508
|
+
});
|
3509
|
+
|
3430
3510
|
it('LOCAL_MEDIA_STOPPED triggers the right metrics', async () => {
|
3431
3511
|
statsAnalyzerStub.emit(
|
3432
3512
|
{file: 'test', function: 'test'},
|
@@ -3443,6 +3523,28 @@ describe('plugin-meetings', () => {
|
|
3443
3523
|
});
|
3444
3524
|
});
|
3445
3525
|
|
3526
|
+
it('LOCAL_MEDIA_STOPPED does not send metric because we already have', async () => {
|
3527
|
+
meeting.shareCAEventSentStatus = {
|
3528
|
+
transmitStart: false,
|
3529
|
+
transmitStop: true,
|
3530
|
+
receiveStart: false,
|
3531
|
+
receiveStop: false,
|
3532
|
+
};
|
3533
|
+
statsAnalyzerStub.emit(
|
3534
|
+
{file: 'test', function: 'test'},
|
3535
|
+
StatsAnalyzerEventNames.LOCAL_MEDIA_STOPPED,
|
3536
|
+
{mediaType: 'share'}
|
3537
|
+
);
|
3538
|
+
|
3539
|
+
assert.neverCalledWith(webex.internal.newMetrics.submitClientEvent, {
|
3540
|
+
name: 'client.media.tx.stop',
|
3541
|
+
payload: {mediaType: 'share', shareInstanceId: meeting.remoteShareInstanceId},
|
3542
|
+
options: {
|
3543
|
+
meetingId: meeting.id,
|
3544
|
+
},
|
3545
|
+
});
|
3546
|
+
});
|
3547
|
+
|
3446
3548
|
it('REMOTE_MEDIA_STARTED triggers "meeting:media:remote:start" event and sends metrics', async () => {
|
3447
3549
|
statsAnalyzerStub.emit(
|
3448
3550
|
{file: 'test', function: 'test'},
|
@@ -3523,6 +3625,47 @@ describe('plugin-meetings', () => {
|
|
3523
3625
|
});
|
3524
3626
|
});
|
3525
3627
|
|
3628
|
+
it('REMOTE_MEDIA_STARTED triggers "meeting:media:remote:start" event and does not send metric because we already have', async () => {
|
3629
|
+
meeting.shareCAEventSentStatus = {
|
3630
|
+
transmitStart: false,
|
3631
|
+
transmitStop: false,
|
3632
|
+
receiveStart: true,
|
3633
|
+
receiveStop: false,
|
3634
|
+
};
|
3635
|
+
statsAnalyzerStub.emit(
|
3636
|
+
{file: 'test', function: 'test'},
|
3637
|
+
StatsAnalyzerEventNames.REMOTE_MEDIA_STARTED,
|
3638
|
+
{mediaType: 'share'}
|
3639
|
+
);
|
3640
|
+
|
3641
|
+
assert.calledWith(
|
3642
|
+
TriggerProxy.trigger,
|
3643
|
+
sinon.match.instanceOf(Meeting),
|
3644
|
+
{
|
3645
|
+
file: 'meeting/index',
|
3646
|
+
function: 'addMedia',
|
3647
|
+
},
|
3648
|
+
EVENT_TRIGGERS.MEETING_MEDIA_REMOTE_STARTED,
|
3649
|
+
{
|
3650
|
+
mediaType: 'share',
|
3651
|
+
}
|
3652
|
+
);
|
3653
|
+
assert.neverCalledWith(webex.internal.newMetrics.submitClientEvent, {
|
3654
|
+
name: 'client.media.render.start',
|
3655
|
+
payload: {mediaType: 'share', shareInstanceId: meeting.remoteShareInstanceId},
|
3656
|
+
options: {
|
3657
|
+
meetingId: meeting.id,
|
3658
|
+
},
|
3659
|
+
});
|
3660
|
+
assert.neverCalledWith(webex.internal.newMetrics.submitClientEvent, {
|
3661
|
+
name: 'client.media.rx.start',
|
3662
|
+
payload: {mediaType: 'share', shareInstanceId: meeting.remoteShareInstanceId},
|
3663
|
+
options: {
|
3664
|
+
meetingId: meeting.id,
|
3665
|
+
},
|
3666
|
+
});
|
3667
|
+
});
|
3668
|
+
|
3526
3669
|
it('REMOTE_MEDIA_STOPPED triggers the right metrics for share', async () => {
|
3527
3670
|
statsAnalyzerStub.emit(
|
3528
3671
|
{file: 'test', function: 'test'},
|
@@ -3547,21 +3690,49 @@ describe('plugin-meetings', () => {
|
|
3547
3690
|
});
|
3548
3691
|
});
|
3549
3692
|
|
3693
|
+
it('REMOTE_MEDIA_STOPPED does not send metric because we already have', async () => {
|
3694
|
+
meeting.shareCAEventSentStatus = {
|
3695
|
+
transmitStart: false,
|
3696
|
+
transmitStop: false,
|
3697
|
+
receiveStart: true,
|
3698
|
+
receiveStop: true,
|
3699
|
+
};
|
3700
|
+
statsAnalyzerStub.emit(
|
3701
|
+
{file: 'test', function: 'test'},
|
3702
|
+
StatsAnalyzerEventNames.REMOTE_MEDIA_STOPPED,
|
3703
|
+
{mediaType: 'share'}
|
3704
|
+
);
|
3705
|
+
assert.neverCalledWith(webex.internal.newMetrics.submitClientEvent, {
|
3706
|
+
name: 'client.media.render.stop',
|
3707
|
+
payload: {mediaType: 'share', shareInstanceId: meeting.remoteShareInstanceId},
|
3708
|
+
options: {
|
3709
|
+
meetingId: meeting.id,
|
3710
|
+
},
|
3711
|
+
});
|
3712
|
+
assert.neverCalledWith(webex.internal.newMetrics.submitClientEvent, {
|
3713
|
+
name: 'client.media.rx.stop',
|
3714
|
+
payload: {mediaType: 'share', shareInstanceId: meeting.remoteShareInstanceId},
|
3715
|
+
options: {
|
3716
|
+
meetingId: meeting.id,
|
3717
|
+
},
|
3718
|
+
});
|
3719
|
+
});
|
3720
|
+
|
3550
3721
|
it('counts the number of members that are in the meeting for MEDIA_QUALITY event', async () => {
|
3551
3722
|
let fakeMembersCollection = {
|
3552
3723
|
members: {
|
3553
|
-
member1: {
|
3554
|
-
member2: {
|
3555
|
-
member3: {
|
3724
|
+
member1: {isInMeeting: true},
|
3725
|
+
member2: {isInMeeting: true},
|
3726
|
+
member3: {isInMeeting: false},
|
3556
3727
|
},
|
3557
3728
|
};
|
3558
|
-
sinon.stub(meeting, 'getMembers').returns({
|
3559
|
-
const fakeData = {
|
3729
|
+
sinon.stub(meeting, 'getMembers').returns({membersCollection: fakeMembersCollection});
|
3730
|
+
const fakeData = {intervalMetadata: {}, networkType: 'wifi'};
|
3560
3731
|
|
3561
3732
|
statsAnalyzerStub.emit(
|
3562
|
-
{
|
3733
|
+
{file: 'test', function: 'test'},
|
3563
3734
|
StatsAnalyzerEventNames.MEDIA_QUALITY,
|
3564
|
-
{
|
3735
|
+
{data: fakeData}
|
3565
3736
|
);
|
3566
3737
|
|
3567
3738
|
assert.calledWithMatch(webex.internal.newMetrics.submitMQE, {
|
@@ -3570,15 +3741,17 @@ describe('plugin-meetings', () => {
|
|
3570
3741
|
meetingId: meeting.id,
|
3571
3742
|
},
|
3572
3743
|
payload: {
|
3573
|
-
intervals: [
|
3744
|
+
intervals: [
|
3745
|
+
sinon.match.has('intervalMetadata', sinon.match.has('meetingUserCount', 2)),
|
3746
|
+
],
|
3574
3747
|
},
|
3575
3748
|
});
|
3576
3749
|
fakeMembersCollection.members.member2.isInMeeting = false;
|
3577
3750
|
|
3578
3751
|
statsAnalyzerStub.emit(
|
3579
|
-
{
|
3752
|
+
{file: 'test', function: 'test'},
|
3580
3753
|
StatsAnalyzerEventNames.MEDIA_QUALITY,
|
3581
|
-
{
|
3754
|
+
{data: fakeData}
|
3582
3755
|
);
|
3583
3756
|
|
3584
3757
|
assert.calledWithMatch(webex.internal.newMetrics.submitMQE, {
|
@@ -3587,7 +3760,9 @@ describe('plugin-meetings', () => {
|
|
3587
3760
|
meetingId: meeting.id,
|
3588
3761
|
},
|
3589
3762
|
payload: {
|
3590
|
-
intervals: [
|
3763
|
+
intervals: [
|
3764
|
+
sinon.match.has('intervalMetadata', sinon.match.has('meetingUserCount', 1)),
|
3765
|
+
],
|
3591
3766
|
},
|
3592
3767
|
});
|
3593
3768
|
});
|
@@ -3624,7 +3799,7 @@ describe('plugin-meetings', () => {
|
|
3624
3799
|
|
3625
3800
|
meeting.roap.doTurnDiscovery = sinon.stub().resolves({
|
3626
3801
|
turnServerInfo: {
|
3627
|
-
|
3802
|
+
urls: [FAKE_TURN_URL],
|
3628
3803
|
username: FAKE_TURN_USER,
|
3629
3804
|
password: FAKE_TURN_PASSWORD,
|
3630
3805
|
},
|
@@ -3650,7 +3825,7 @@ describe('plugin-meetings', () => {
|
|
3650
3825
|
meeting.id,
|
3651
3826
|
sinon.match({
|
3652
3827
|
turnServerInfo: {
|
3653
|
-
|
3828
|
+
urls: [FAKE_TURN_URL],
|
3654
3829
|
username: FAKE_TURN_USER,
|
3655
3830
|
password: FAKE_TURN_PASSWORD,
|
3656
3831
|
},
|
@@ -3842,7 +4017,6 @@ describe('plugin-meetings', () => {
|
|
3842
4017
|
});
|
3843
4018
|
|
3844
4019
|
describe('when in a multistream meeting', () => {
|
3845
|
-
|
3846
4020
|
beforeEach(() => {
|
3847
4021
|
meeting.isMultistream = true;
|
3848
4022
|
});
|
@@ -3853,7 +4027,7 @@ describe('plugin-meetings', () => {
|
|
3853
4027
|
await brbResult;
|
3854
4028
|
assert.exists(brbResult.then);
|
3855
4029
|
assert.calledOnce(meeting.brbState.enable);
|
3856
|
-
})
|
4030
|
+
});
|
3857
4031
|
|
3858
4032
|
it('should disable #beRightBack and return a promise', async () => {
|
3859
4033
|
const brbResult = meeting.beRightBack(false);
|
@@ -3861,7 +4035,7 @@ describe('plugin-meetings', () => {
|
|
3861
4035
|
await brbResult;
|
3862
4036
|
assert.exists(brbResult.then);
|
3863
4037
|
assert.calledOnce(meeting.brbState.enable);
|
3864
|
-
})
|
4038
|
+
});
|
3865
4039
|
|
3866
4040
|
it('should throw an error and reject the promise if setBrb fails', async () => {
|
3867
4041
|
const error = new Error('setBrb failed');
|
@@ -3872,9 +4046,29 @@ describe('plugin-meetings', () => {
|
|
3872
4046
|
} catch (err) {
|
3873
4047
|
assert.instanceOf(err, Error);
|
3874
4048
|
assert.equal(err.message, 'setBrb failed');
|
3875
|
-
assert.isRejected(
|
4049
|
+
assert.isRejected(Promise.reject());
|
3876
4050
|
}
|
3877
|
-
})
|
4051
|
+
});
|
4052
|
+
|
4053
|
+
it('updates remote mute state when brb is enabled', async () => {
|
4054
|
+
meeting.audio = {handleServerRemoteMuteUpdate: sinon.stub()};
|
4055
|
+
|
4056
|
+
await meeting.beRightBack(true);
|
4057
|
+
|
4058
|
+
sinon.assert.calledOnceWithExactly(
|
4059
|
+
meeting.audio.handleServerRemoteMuteUpdate,
|
4060
|
+
meeting,
|
4061
|
+
true
|
4062
|
+
);
|
4063
|
+
});
|
4064
|
+
|
4065
|
+
it('does not update remote mute state when brb is disabled', async () => {
|
4066
|
+
meeting.audio = {handleServerRemoteMuteUpdate: sinon.stub()};
|
4067
|
+
|
4068
|
+
await meeting.beRightBack(false);
|
4069
|
+
|
4070
|
+
assert.notCalled(meeting.audio.handleServerRemoteMuteUpdate);
|
4071
|
+
});
|
3878
4072
|
});
|
3879
4073
|
});
|
3880
4074
|
|
@@ -3928,7 +4122,10 @@ describe('plugin-meetings', () => {
|
|
3928
4122
|
.resolves({id: 'fake clientMediaPreferences'});
|
3929
4123
|
meeting.roap.doTurnDiscovery = sinon.stub().resolves({
|
3930
4124
|
turnServerInfo: {
|
3931
|
-
|
4125
|
+
urls: [
|
4126
|
+
'turns:turn-server-url1:443?transport=tcp',
|
4127
|
+
'turns:turn-server-url2:443?transport=tcp',
|
4128
|
+
],
|
3932
4129
|
username: 'turn user',
|
3933
4130
|
password: 'turn password',
|
3934
4131
|
},
|
@@ -3946,12 +4143,10 @@ describe('plugin-meetings', () => {
|
|
3946
4143
|
expectedMediaConnectionConfig = {
|
3947
4144
|
iceServers: [
|
3948
4145
|
{
|
3949
|
-
urls:
|
3950
|
-
|
3951
|
-
|
3952
|
-
|
3953
|
-
{
|
3954
|
-
urls: 'turns:turn-server-url:443?transport=tcp',
|
4146
|
+
urls: [
|
4147
|
+
'turns:turn-server-url1:443?transport=tcp',
|
4148
|
+
'turns:turn-server-url2:443?transport=tcp',
|
4149
|
+
],
|
3955
4150
|
username: 'turn user',
|
3956
4151
|
credential: 'turn password',
|
3957
4152
|
},
|
@@ -4006,7 +4201,7 @@ describe('plugin-meetings', () => {
|
|
4006
4201
|
initiateOffer: sinon.stub().resolves({}),
|
4007
4202
|
update: sinon.stub().resolves({}),
|
4008
4203
|
on: sinon.stub(),
|
4009
|
-
roapMessageReceived: sinon.stub()
|
4204
|
+
roapMessageReceived: sinon.stub(),
|
4010
4205
|
};
|
4011
4206
|
|
4012
4207
|
fakeMultistreamRoapMediaConnection = {
|
@@ -4033,9 +4228,11 @@ describe('plugin-meetings', () => {
|
|
4033
4228
|
.stub(InternalMediaCoreModule, 'MultistreamRoapMediaConnection')
|
4034
4229
|
.returns(fakeMultistreamRoapMediaConnection);
|
4035
4230
|
|
4036
|
-
locusMediaRequestStub = sinon
|
4037
|
-
|
4038
|
-
.
|
4231
|
+
locusMediaRequestStub = sinon.stub(WebexPlugin.prototype, 'request').resolves({
|
4232
|
+
body: {locus: {fullState: {}}},
|
4233
|
+
upload: sinon.match.instanceOf(EventEmitter),
|
4234
|
+
download: sinon.match.instanceOf(EventEmitter),
|
4235
|
+
});
|
4039
4236
|
|
4040
4237
|
// setup some things and mocks so that the call to join() works
|
4041
4238
|
// (we need to call join() because it creates the LocusMediaRequest instance
|
@@ -4144,6 +4341,8 @@ describe('plugin-meetings', () => {
|
|
4144
4341
|
id: 'fake clientMediaPreferences',
|
4145
4342
|
},
|
4146
4343
|
},
|
4344
|
+
upload: sinon.match.instanceOf(EventEmitter),
|
4345
|
+
download: sinon.match.instanceOf(EventEmitter),
|
4147
4346
|
});
|
4148
4347
|
};
|
4149
4348
|
|
@@ -4171,6 +4370,8 @@ describe('plugin-meetings', () => {
|
|
4171
4370
|
},
|
4172
4371
|
],
|
4173
4372
|
},
|
4373
|
+
upload: sinon.match.instanceOf(EventEmitter),
|
4374
|
+
download: sinon.match.instanceOf(EventEmitter),
|
4174
4375
|
});
|
4175
4376
|
};
|
4176
4377
|
|
@@ -4195,6 +4396,8 @@ describe('plugin-meetings', () => {
|
|
4195
4396
|
respOnlySdp: true,
|
4196
4397
|
usingResource: null,
|
4197
4398
|
},
|
4399
|
+
upload: sinon.match.instanceOf(EventEmitter),
|
4400
|
+
download: sinon.match.instanceOf(EventEmitter),
|
4198
4401
|
});
|
4199
4402
|
};
|
4200
4403
|
|
@@ -5213,7 +5416,10 @@ describe('plugin-meetings', () => {
|
|
5213
5416
|
// and check that when we fallback to transcoded we still do another TURN discovery
|
5214
5417
|
await runCheck(
|
5215
5418
|
{
|
5216
|
-
|
5419
|
+
urls: [
|
5420
|
+
'turns:turn-server-url1:443?transport=tcp',
|
5421
|
+
'turns:turn-server-url2:443?transport=tcp',
|
5422
|
+
],
|
5217
5423
|
username: 'turn user',
|
5218
5424
|
password: 'turn password',
|
5219
5425
|
},
|
@@ -5227,7 +5433,10 @@ describe('plugin-meetings', () => {
|
|
5227
5433
|
// but doing it just for completeness
|
5228
5434
|
await runCheck(
|
5229
5435
|
{
|
5230
|
-
|
5436
|
+
urls: [
|
5437
|
+
'turns:turn-server-url1:443?transport=tcp',
|
5438
|
+
'turns:turn-server-url2:443?transport=tcp',
|
5439
|
+
],
|
5231
5440
|
username: 'turn user',
|
5232
5441
|
password: 'turn password',
|
5233
5442
|
},
|
@@ -6337,7 +6546,10 @@ describe('plugin-meetings', () => {
|
|
6337
6546
|
.throws(new MeetingInfoV2JoinForbiddenError(403003, FAKE_MEETING_INFO)),
|
6338
6547
|
};
|
6339
6548
|
|
6340
|
-
await assert.isRejected(
|
6549
|
+
await assert.isRejected(
|
6550
|
+
meeting.fetchMeetingInfo({sendCAevents: true}),
|
6551
|
+
JoinForbiddenError
|
6552
|
+
);
|
6341
6553
|
|
6342
6554
|
assert.calledWith(
|
6343
6555
|
meeting.attrs.meetingInfoProvider.fetchMeetingInfo,
|
@@ -6353,10 +6565,7 @@ describe('plugin-meetings', () => {
|
|
6353
6565
|
|
6354
6566
|
assert.deepEqual(meeting.meetingInfo, FAKE_MEETING_INFO);
|
6355
6567
|
assert.equal(meeting.meetingInfoFailureCode, 403003);
|
6356
|
-
assert.equal(
|
6357
|
-
meeting.meetingInfoFailureReason,
|
6358
|
-
MEETING_INFO_FAILURE_REASON.NOT_REACH_JBH
|
6359
|
-
);
|
6568
|
+
assert.equal(meeting.meetingInfoFailureReason, MEETING_INFO_FAILURE_REASON.NOT_REACH_JBH);
|
6360
6569
|
assert.equal(meeting.requiredCaptcha, null);
|
6361
6570
|
});
|
6362
6571
|
|
@@ -6733,15 +6942,10 @@ describe('plugin-meetings', () => {
|
|
6733
6942
|
meeting.attrs.meetingInfoProvider = {
|
6734
6943
|
fetchMeetingInfo: sinon
|
6735
6944
|
.stub()
|
6736
|
-
.throws(
|
6737
|
-
new MeetingInfoV2JoinWebinarError(403021, FAKE_MEETING_INFO, 'a message')
|
6738
|
-
),
|
6945
|
+
.throws(new MeetingInfoV2JoinWebinarError(403021, FAKE_MEETING_INFO, 'a message')),
|
6739
6946
|
};
|
6740
6947
|
|
6741
|
-
await assert.isRejected(
|
6742
|
-
meeting.fetchMeetingInfo({sendCAevents: true}),
|
6743
|
-
JoinWebinarError
|
6744
|
-
);
|
6948
|
+
await assert.isRejected(meeting.fetchMeetingInfo({sendCAevents: true}), JoinWebinarError);
|
6745
6949
|
|
6746
6950
|
assert.deepEqual(meeting.meetingInfo, FAKE_MEETING_INFO);
|
6747
6951
|
assert.equal(
|
@@ -6756,15 +6960,10 @@ describe('plugin-meetings', () => {
|
|
6756
6960
|
meeting.attrs.meetingInfoProvider = {
|
6757
6961
|
fetchMeetingInfo: sinon
|
6758
6962
|
.stub()
|
6759
|
-
.throws(
|
6760
|
-
new MeetingInfoV2JoinWebinarError(403026, FAKE_MEETING_INFO, 'a message')
|
6761
|
-
),
|
6963
|
+
.throws(new MeetingInfoV2JoinWebinarError(403026, FAKE_MEETING_INFO, 'a message')),
|
6762
6964
|
};
|
6763
6965
|
|
6764
|
-
await assert.isRejected(
|
6765
|
-
meeting.fetchMeetingInfo({sendCAevents: true}),
|
6766
|
-
JoinWebinarError
|
6767
|
-
);
|
6966
|
+
await assert.isRejected(meeting.fetchMeetingInfo({sendCAevents: true}), JoinWebinarError);
|
6768
6967
|
|
6769
6968
|
assert.deepEqual(meeting.meetingInfo, FAKE_MEETING_INFO);
|
6770
6969
|
assert.equal(
|
@@ -6779,15 +6978,10 @@ describe('plugin-meetings', () => {
|
|
6779
6978
|
meeting.attrs.meetingInfoProvider = {
|
6780
6979
|
fetchMeetingInfo: sinon
|
6781
6980
|
.stub()
|
6782
|
-
.throws(
|
6783
|
-
new MeetingInfoV2JoinWebinarError(403037, FAKE_MEETING_INFO, 'a message')
|
6784
|
-
),
|
6981
|
+
.throws(new MeetingInfoV2JoinWebinarError(403037, FAKE_MEETING_INFO, 'a message')),
|
6785
6982
|
};
|
6786
6983
|
|
6787
|
-
await assert.isRejected(
|
6788
|
-
meeting.fetchMeetingInfo({sendCAevents: true}),
|
6789
|
-
JoinWebinarError
|
6790
|
-
);
|
6984
|
+
await assert.isRejected(meeting.fetchMeetingInfo({sendCAevents: true}), JoinWebinarError);
|
6791
6985
|
|
6792
6986
|
assert.deepEqual(meeting.meetingInfo, FAKE_MEETING_INFO);
|
6793
6987
|
assert.equal(
|
@@ -7524,6 +7718,27 @@ describe('plugin-meetings', () => {
|
|
7524
7718
|
});
|
7525
7719
|
});
|
7526
7720
|
|
7721
|
+
describe('#setIsoLocalClientMeetingJoinTime', () => {
|
7722
|
+
it('should fallback to system clock ISO string when given an undefined value', () => {
|
7723
|
+
const currentSystemTime = new Date().toISOString();
|
7724
|
+
meeting.isoLocalClientMeetingJoinTime = undefined;
|
7725
|
+
assert.equal(meeting.isoLocalClientMeetingJoinTime, currentSystemTime);
|
7726
|
+
});
|
7727
|
+
|
7728
|
+
it('should fallback to system clock ISO string when given an invalid value', () => {
|
7729
|
+
const currentSystemTime = new Date().toISOString();
|
7730
|
+
meeting.isoLocalClientMeetingJoinTime = 'invalid-date';
|
7731
|
+
assert.equal(meeting.isoLocalClientMeetingJoinTime, currentSystemTime);
|
7732
|
+
});
|
7733
|
+
|
7734
|
+
it('should set the isoLocalClientMeetingJoinTime correctly for a valid date string', () => {
|
7735
|
+
const validDateString = 'Tue, 01 Apr 2025 13:00:36 GMT';
|
7736
|
+
const expectedISOString = new Date(validDateString).toISOString();
|
7737
|
+
meeting.isoLocalClientMeetingJoinTime = validDateString;
|
7738
|
+
assert.equal(meeting.isoLocalClientMeetingJoinTime, expectedISOString);
|
7739
|
+
});
|
7740
|
+
});
|
7741
|
+
|
7527
7742
|
describe('#updateCallStateForMetrics', () => {
|
7528
7743
|
it('should update the callState, overriding existing values', () => {
|
7529
7744
|
assert.deepEqual(meeting.callStateForMetrics, {correlationId, sessionCorrelationId: ''});
|
@@ -7605,6 +7820,12 @@ describe('plugin-meetings', () => {
|
|
7605
7820
|
meeting.audio = {handleLocalStreamChange: sinon.stub()};
|
7606
7821
|
meeting.video = {handleLocalStreamChange: sinon.stub()};
|
7607
7822
|
meeting.statsAnalyzer = {updateMediaStatus: sinon.stub()};
|
7823
|
+
meeting.shareCAEventSentStatus = {
|
7824
|
+
transmitStart: false,
|
7825
|
+
transmitStop: false,
|
7826
|
+
receiveStart: false,
|
7827
|
+
receiveStop: false,
|
7828
|
+
};
|
7608
7829
|
fakeMultistreamRoapMediaConnection = {
|
7609
7830
|
createSendSlot: () => {
|
7610
7831
|
return {
|
@@ -7672,6 +7893,9 @@ describe('plugin-meetings', () => {
|
|
7672
7893
|
});
|
7673
7894
|
assert.equal(meeting.mediaProperties.mediaDirection.sendShare, true);
|
7674
7895
|
|
7896
|
+
assert.equal(meeting.shareCAEventSentStatus.transmitStart, false);
|
7897
|
+
assert.equal(meeting.shareCAEventSentStatus.transmitStop, false);
|
7898
|
+
|
7675
7899
|
assert.calledWith(meeting.statsAnalyzer.updateMediaStatus, {
|
7676
7900
|
expected: {sendShare: true},
|
7677
7901
|
});
|
@@ -7692,18 +7916,23 @@ describe('plugin-meetings', () => {
|
|
7692
7916
|
assert.equal(meeting.mediaProperties.shareAudioStream, stream);
|
7693
7917
|
assert.equal(meeting.mediaProperties.mediaDirection.sendShare, true);
|
7694
7918
|
|
7919
|
+
assert.equal(meeting.shareCAEventSentStatus.transmitStart, false);
|
7920
|
+
assert.equal(meeting.shareCAEventSentStatus.transmitStop, false);
|
7921
|
+
|
7695
7922
|
assert.calledWith(meeting.statsAnalyzer.updateMediaStatus, {
|
7696
7923
|
expected: {sendShare: true},
|
7697
7924
|
});
|
7698
7925
|
};
|
7699
7926
|
|
7700
7927
|
it('requests screen share floor and publishes the screen share video stream', async () => {
|
7928
|
+
meeting.shareCAEventSentStatus.transmitStart = true;
|
7701
7929
|
await meeting.publishStreams({screenShare: {video: videoShareStream}});
|
7702
7930
|
|
7703
7931
|
checkScreenShareVideoPublished(videoShareStream);
|
7704
7932
|
});
|
7705
7933
|
|
7706
7934
|
it('requests screen share floor and publishes the screen share audio stream', async () => {
|
7935
|
+
meeting.shareCAEventSentStatus.transmitStart = true;
|
7707
7936
|
await meeting.publishStreams({screenShare: {audio: audioShareStream}});
|
7708
7937
|
|
7709
7938
|
checkScreenShareAudioPublished(audioShareStream);
|
@@ -8590,21 +8819,34 @@ describe('plugin-meetings', () => {
|
|
8590
8819
|
const fakeErrorMessage = 'test error';
|
8591
8820
|
const fakeRootCauseName = 'root cause name';
|
8592
8821
|
const fakeErrorName = 'test error name';
|
8822
|
+
let clock;
|
8593
8823
|
|
8594
8824
|
beforeEach(() => {
|
8825
|
+
clock = sinon.useFakeTimers();
|
8595
8826
|
meeting.setupMediaConnectionListeners();
|
8596
8827
|
webex.internal.newMetrics.submitClientEvent.resetHistory();
|
8597
8828
|
Metrics.sendBehavioralMetric.resetHistory();
|
8598
8829
|
});
|
8599
8830
|
|
8600
|
-
|
8831
|
+
afterEach(() => {
|
8832
|
+
clock.restore();
|
8833
|
+
});
|
8834
|
+
|
8835
|
+
const checkMetricSent = (event, error, expectedErrorCode) => {
|
8601
8836
|
assert.calledOnce(webex.internal.newMetrics.submitClientEvent);
|
8602
|
-
assert.
|
8837
|
+
assert.deepEqual(webex.internal.newMetrics.submitClientEvent.getCall(0).args[0], {
|
8603
8838
|
name: event,
|
8604
8839
|
payload: {
|
8605
8840
|
canProceed: false,
|
8606
8841
|
},
|
8607
|
-
options: {
|
8842
|
+
options: {
|
8843
|
+
rawError: {
|
8844
|
+
...(error.cause ? {cause: {name: error.cause.name}} : {cause: undefined}),
|
8845
|
+
code: expectedErrorCode,
|
8846
|
+
name: error.name,
|
8847
|
+
},
|
8848
|
+
meetingId: meeting.id,
|
8849
|
+
},
|
8608
8850
|
});
|
8609
8851
|
};
|
8610
8852
|
|
@@ -8638,7 +8880,7 @@ describe('plugin-meetings', () => {
|
|
8638
8880
|
|
8639
8881
|
eventListeners[MediaConnectionEventNames.ROAP_FAILURE](fakeError);
|
8640
8882
|
|
8641
|
-
checkMetricSent('client.media-engine.local-sdp-generated', fakeError);
|
8883
|
+
checkMetricSent('client.media-engine.local-sdp-generated', fakeError, 30005);
|
8642
8884
|
checkBehavioralMetricSent(
|
8643
8885
|
BEHAVIORAL_METRICS.PEERCONNECTION_FAILURE,
|
8644
8886
|
Errors.ErrorCode.SdpOfferCreationError,
|
@@ -8655,7 +8897,7 @@ describe('plugin-meetings', () => {
|
|
8655
8897
|
|
8656
8898
|
eventListeners[MediaConnectionEventNames.ROAP_FAILURE](fakeError);
|
8657
8899
|
|
8658
|
-
checkMetricSent('client.media-engine.remote-sdp-received', fakeError);
|
8900
|
+
checkMetricSent('client.media-engine.remote-sdp-received', fakeError, 30006);
|
8659
8901
|
checkBehavioralMetricSent(
|
8660
8902
|
BEHAVIORAL_METRICS.PEERCONNECTION_FAILURE,
|
8661
8903
|
Errors.ErrorCode.SdpOfferHandlingError,
|
@@ -8665,6 +8907,13 @@ describe('plugin-meetings', () => {
|
|
8665
8907
|
});
|
8666
8908
|
|
8667
8909
|
it('should send metrics for SdpAnswerHandlingError error', () => {
|
8910
|
+
meeting.sdpResponseTimer = '1234';
|
8911
|
+
meeting.deferSDPAnswer = {
|
8912
|
+
reject: sinon.stub(),
|
8913
|
+
};
|
8914
|
+
|
8915
|
+
const clearTimeoutSpy = sinon.spy(clock, 'clearTimeout');
|
8916
|
+
|
8668
8917
|
const fakeError = new Errors.SdpAnswerHandlingError(fakeErrorMessage, {
|
8669
8918
|
name: fakeErrorName,
|
8670
8919
|
cause: {name: fakeRootCauseName},
|
@@ -8672,13 +8921,16 @@ describe('plugin-meetings', () => {
|
|
8672
8921
|
|
8673
8922
|
eventListeners[MediaConnectionEventNames.ROAP_FAILURE](fakeError);
|
8674
8923
|
|
8675
|
-
checkMetricSent('client.media-engine.remote-sdp-received', fakeError);
|
8924
|
+
checkMetricSent('client.media-engine.remote-sdp-received', fakeError, 30004);
|
8676
8925
|
checkBehavioralMetricSent(
|
8677
8926
|
BEHAVIORAL_METRICS.PEERCONNECTION_FAILURE,
|
8678
8927
|
Errors.ErrorCode.SdpAnswerHandlingError,
|
8679
8928
|
fakeErrorMessage,
|
8680
8929
|
fakeRootCauseName
|
8681
8930
|
);
|
8931
|
+
assert.calledOnce(meeting.deferSDPAnswer.reject);
|
8932
|
+
assert.isTrue(meeting.deferSDPAnswer.reject.getCall(0).args[0].handledBySdk);
|
8933
|
+
assert.calledOnce(clearTimeoutSpy);
|
8682
8934
|
});
|
8683
8935
|
|
8684
8936
|
it('should send metrics for SdpError error', () => {
|
@@ -8687,7 +8939,7 @@ describe('plugin-meetings', () => {
|
|
8687
8939
|
|
8688
8940
|
eventListeners[MediaConnectionEventNames.ROAP_FAILURE](fakeError);
|
8689
8941
|
|
8690
|
-
checkMetricSent('client.media-engine.local-sdp-generated', fakeError);
|
8942
|
+
checkMetricSent('client.media-engine.local-sdp-generated', fakeError, 30002);
|
8691
8943
|
// expectedMetadataType is the error name in this case
|
8692
8944
|
checkBehavioralMetricSent(
|
8693
8945
|
BEHAVIORAL_METRICS.INVALID_ICE_CANDIDATE,
|
@@ -8705,7 +8957,7 @@ describe('plugin-meetings', () => {
|
|
8705
8957
|
|
8706
8958
|
eventListeners[MediaConnectionEventNames.ROAP_FAILURE](fakeError);
|
8707
8959
|
|
8708
|
-
checkMetricSent('client.media-engine.local-sdp-generated', fakeError);
|
8960
|
+
checkMetricSent('client.media-engine.local-sdp-generated', fakeError, 30003);
|
8709
8961
|
// expectedMetadataType is the error name in this case
|
8710
8962
|
checkBehavioralMetricSent(
|
8711
8963
|
BEHAVIORAL_METRICS.INVALID_ICE_CANDIDATE,
|
@@ -8899,7 +9151,7 @@ describe('plugin-meetings', () => {
|
|
8899
9151
|
assert.calledOnceWithExactly(getErrorPayloadForClientErrorCodeStub, {
|
8900
9152
|
clientErrorCode: expectedErrorCode,
|
8901
9153
|
});
|
8902
|
-
assert.
|
9154
|
+
assert.deepEqual(webex.internal.newMetrics.submitClientEvent.getCall(0).args[0], {
|
8903
9155
|
name: 'client.media-engine.remote-sdp-received',
|
8904
9156
|
payload: {
|
8905
9157
|
canProceed,
|
@@ -8907,9 +9159,18 @@ describe('plugin-meetings', () => {
|
|
8907
9159
|
},
|
8908
9160
|
options: {
|
8909
9161
|
meetingId: meeting.id,
|
8910
|
-
rawError: fakeError
|
9162
|
+
rawError: fakeError instanceof MultistreamNotSupportedError ? {
|
9163
|
+
code: fakeError.code,
|
9164
|
+
name: fakeError.name,
|
9165
|
+
sdkMessage: fakeError.sdkMessage,
|
9166
|
+
error: fakeError.error,
|
9167
|
+
} : {},
|
8911
9168
|
},
|
8912
9169
|
});
|
9170
|
+
const actualError = webex.internal.newMetrics.submitClientEvent.getCall(0).args[0].options.rawError;
|
9171
|
+
|
9172
|
+
assert.isTrue(actualError.handledBySdk);
|
9173
|
+
assert.equal(actualError.message, fakeError.message);
|
8913
9174
|
};
|
8914
9175
|
|
8915
9176
|
it('handles OFFER message correctly when request fails', async () => {
|
@@ -9223,22 +9484,22 @@ describe('plugin-meetings', () => {
|
|
9223
9484
|
const assertBrb = (enabled) => {
|
9224
9485
|
meeting.brbState = createBrbState(meeting, false);
|
9225
9486
|
meeting.locusInfo.emit(
|
9226
|
-
{
|
9487
|
+
{function: 'test', file: 'test'},
|
9227
9488
|
LOCUSINFO.EVENTS.SELF_MEETING_BRB_CHANGED,
|
9228
|
-
{
|
9229
|
-
)
|
9489
|
+
{brb: {enabled}}
|
9490
|
+
);
|
9230
9491
|
assert.calledWithExactly(
|
9231
9492
|
TriggerProxy.trigger,
|
9232
9493
|
meeting,
|
9233
9494
|
{file: 'meeting/index', function: 'setUpLocusInfoSelfListener'},
|
9234
9495
|
EVENT_TRIGGERS.MEETING_SELF_BRB_UPDATE,
|
9235
|
-
{
|
9496
|
+
{payload: {brb: {enabled}}}
|
9236
9497
|
);
|
9237
|
-
}
|
9498
|
+
};
|
9238
9499
|
|
9239
9500
|
assertBrb(true);
|
9240
9501
|
assertBrb(false);
|
9241
|
-
})
|
9502
|
+
});
|
9242
9503
|
|
9243
9504
|
it('listens to the interpretation changed event', () => {
|
9244
9505
|
meeting.simultaneousInterpretation.updateSelfInterpretation = sinon.stub();
|
@@ -9584,6 +9845,42 @@ describe('plugin-meetings', () => {
|
|
9584
9845
|
);
|
9585
9846
|
});
|
9586
9847
|
|
9848
|
+
it('listens to CONTROLS_ANNOTATION_CHANGED', async () => {
|
9849
|
+
const state = {example: 'value'};
|
9850
|
+
|
9851
|
+
await meeting.locusInfo.emitScoped(
|
9852
|
+
{function: 'test', file: 'test'},
|
9853
|
+
LOCUSINFO.EVENTS.CONTROLS_ANNOTATION_CHANGED,
|
9854
|
+
{state}
|
9855
|
+
);
|
9856
|
+
|
9857
|
+
assert.calledWith(
|
9858
|
+
TriggerProxy.trigger,
|
9859
|
+
meeting,
|
9860
|
+
{file: 'meeting/index', function: 'setupLocusControlsListener'},
|
9861
|
+
EVENT_TRIGGERS.MEETING_CONTROLS_ANNOTATION_UPDATED,
|
9862
|
+
{state}
|
9863
|
+
);
|
9864
|
+
});
|
9865
|
+
|
9866
|
+
it('listens to CONTROLS_REMOTE_DESKTOP_CONTROL_CHANGED', async () => {
|
9867
|
+
const state = {example: 'value'};
|
9868
|
+
|
9869
|
+
await meeting.locusInfo.emitScoped(
|
9870
|
+
{function: 'test', file: 'test'},
|
9871
|
+
LOCUSINFO.EVENTS.CONTROLS_REMOTE_DESKTOP_CONTROL_CHANGED,
|
9872
|
+
{state}
|
9873
|
+
);
|
9874
|
+
|
9875
|
+
assert.calledWith(
|
9876
|
+
TriggerProxy.trigger,
|
9877
|
+
meeting,
|
9878
|
+
{file: 'meeting/index', function: 'setupLocusControlsListener'},
|
9879
|
+
EVENT_TRIGGERS.MEETING_CONTROLS_REMOTE_DESKTOP_CONTROL_UPDATED,
|
9880
|
+
{state}
|
9881
|
+
);
|
9882
|
+
});
|
9883
|
+
|
9587
9884
|
it('listens to the locus interpretation update event', () => {
|
9588
9885
|
const interpretation = {
|
9589
9886
|
siLanguages: [{languageCode: 20, languageName: 'en'}],
|
@@ -9922,6 +10219,22 @@ describe('plugin-meetings', () => {
|
|
9922
10219
|
});
|
9923
10220
|
});
|
9924
10221
|
|
10222
|
+
describe('#emailInput', () => {
|
10223
|
+
it('should set the email input', () => {
|
10224
|
+
assert.notOk(meeting.emailInput);
|
10225
|
+
meeting.emailInput = 'current';
|
10226
|
+
assert.equal(meeting.emailInput, 'current');
|
10227
|
+
});
|
10228
|
+
});
|
10229
|
+
|
10230
|
+
describe('#userNameInput', () => {
|
10231
|
+
it('should set the user name input', () => {
|
10232
|
+
assert.notOk(meeting.userNameInput);
|
10233
|
+
meeting.userNameInput = 'current';
|
10234
|
+
assert.equal(meeting.userNameInput, 'current');
|
10235
|
+
});
|
10236
|
+
});
|
10237
|
+
|
9925
10238
|
describe('#setPermissionTokenPayload', () => {
|
9926
10239
|
let now;
|
9927
10240
|
let clock;
|
@@ -10463,6 +10776,7 @@ describe('plugin-meetings', () => {
|
|
10463
10776
|
let canUserLowerSomeoneElsesHandSpy;
|
10464
10777
|
let waitingForOthersToJoinSpy;
|
10465
10778
|
let canSendReactionsSpy;
|
10779
|
+
let requiresPostMeetingDataConsentPromptSpy;
|
10466
10780
|
let canUserRenameSelfAndObservedSpy;
|
10467
10781
|
let canUserRenameOthersSpy;
|
10468
10782
|
let canShareWhiteBoardSpy;
|
@@ -10490,6 +10804,10 @@ describe('plugin-meetings', () => {
|
|
10490
10804
|
waitingForOthersToJoinSpy = sinon.spy(MeetingUtil, 'waitingForOthersToJoin');
|
10491
10805
|
canSendReactionsSpy = sinon.spy(MeetingUtil, 'canSendReactions');
|
10492
10806
|
canUserRenameSelfAndObservedSpy = sinon.spy(MeetingUtil, 'canUserRenameSelfAndObserved');
|
10807
|
+
requiresPostMeetingDataConsentPromptSpy = sinon.spy(
|
10808
|
+
MeetingUtil,
|
10809
|
+
'requiresPostMeetingDataConsentPrompt'
|
10810
|
+
);
|
10493
10811
|
canUserRenameOthersSpy = sinon.spy(MeetingUtil, 'canUserRenameOthers');
|
10494
10812
|
canShareWhiteBoardSpy = sinon.spy(MeetingUtil, 'canShareWhiteBoard');
|
10495
10813
|
});
|
@@ -10618,6 +10936,11 @@ describe('plugin-meetings', () => {
|
|
10618
10936
|
requiredDisplayHints: [],
|
10619
10937
|
requiredPolicies: [SELF_POLICY.SUPPORT_POLLING_AND_QA],
|
10620
10938
|
},
|
10939
|
+
{
|
10940
|
+
actionName: 'canShareWhiteBoard',
|
10941
|
+
requiredDisplayHints: [DISPLAY_HINTS.SHARE_WHITEBOARD],
|
10942
|
+
requiredPolicies: [SELF_POLICY.SUPPORT_WHITEBOARD],
|
10943
|
+
},
|
10621
10944
|
],
|
10622
10945
|
({
|
10623
10946
|
actionName,
|
@@ -11025,8 +11348,9 @@ describe('plugin-meetings', () => {
|
|
11025
11348
|
assert.calledWith(waitingForOthersToJoinSpy, userDisplayHints);
|
11026
11349
|
assert.calledWith(canSendReactionsSpy, null, userDisplayHints);
|
11027
11350
|
assert.calledWith(canUserRenameSelfAndObservedSpy, userDisplayHints);
|
11351
|
+
assert.calledWith(requiresPostMeetingDataConsentPromptSpy, userDisplayHints);
|
11028
11352
|
assert.calledWith(canUserRenameOthersSpy, userDisplayHints);
|
11029
|
-
assert.calledWith(canShareWhiteBoardSpy, userDisplayHints);
|
11353
|
+
assert.calledWith(canShareWhiteBoardSpy, userDisplayHints, selfUserPolicies);
|
11030
11354
|
|
11031
11355
|
assert.calledWith(ControlsOptionsUtil.hasHints, {
|
11032
11356
|
requiredHints: [DISPLAY_HINTS.MUTE_ALL],
|
@@ -11120,6 +11444,22 @@ describe('plugin-meetings', () => {
|
|
11120
11444
|
requiredPolicies: [SELF_POLICY.SUPPORT_VOIP],
|
11121
11445
|
policies: selfUserPolicies,
|
11122
11446
|
});
|
11447
|
+
assert.calledWith(ControlsOptionsUtil.hasHints, {
|
11448
|
+
requiredHints: [DISPLAY_HINTS.ENABLE_ANNOTATION_MEETING_OPTION],
|
11449
|
+
displayHints: userDisplayHints,
|
11450
|
+
});
|
11451
|
+
assert.calledWith(ControlsOptionsUtil.hasHints, {
|
11452
|
+
requiredHints: [DISPLAY_HINTS.DISABLE_ANNOTATION_MEETING_OPTION],
|
11453
|
+
displayHints: userDisplayHints,
|
11454
|
+
});
|
11455
|
+
assert.calledWith(ControlsOptionsUtil.hasHints, {
|
11456
|
+
requiredHints: [DISPLAY_HINTS.ENABLE_RDC_MEETING_OPTION],
|
11457
|
+
displayHints: userDisplayHints,
|
11458
|
+
});
|
11459
|
+
assert.calledWith(ControlsOptionsUtil.hasHints, {
|
11460
|
+
requiredHints: [DISPLAY_HINTS.DISABLE_RDC_MEETING_OPTION],
|
11461
|
+
displayHints: userDisplayHints,
|
11462
|
+
});
|
11123
11463
|
|
11124
11464
|
assert.calledWith(
|
11125
11465
|
TriggerProxy.trigger,
|
@@ -11326,18 +11666,21 @@ describe('plugin-meetings', () => {
|
|
11326
11666
|
);
|
11327
11667
|
});
|
11328
11668
|
|
11329
|
-
|
11330
11669
|
it('connect ps data channel if ps started in webinar', async () => {
|
11331
11670
|
meeting.joinedWith = {state: 'JOINED'};
|
11332
|
-
meeting.locusInfo = {
|
11671
|
+
meeting.locusInfo = {
|
11672
|
+
url: 'a url',
|
11673
|
+
info: {
|
11674
|
+
datachannelUrl: 'a datachannel url',
|
11675
|
+
practiceSessionDatachannelUrl: 'a ps datachannel url',
|
11676
|
+
},
|
11677
|
+
};
|
11333
11678
|
meeting.webinar.isJoinPracticeSessionDataChannel = sinon.stub().returns(true);
|
11334
11679
|
await meeting.updateLLMConnection();
|
11335
11680
|
|
11336
11681
|
assert.notCalled(webex.internal.llm.disconnectLLM);
|
11337
11682
|
assert.calledWith(webex.internal.llm.registerAndConnect, 'a url', 'a ps datachannel url');
|
11338
|
-
|
11339
11683
|
});
|
11340
|
-
|
11341
11684
|
});
|
11342
11685
|
|
11343
11686
|
describe('#setLocus', () => {
|
@@ -11755,24 +12098,29 @@ describe('plugin-meetings', () => {
|
|
11755
12098
|
|
11756
12099
|
activeSharingId.whiteboard = beneficiaryId;
|
11757
12100
|
|
11758
|
-
eventTrigger.share.push(
|
11759
|
-
|
11760
|
-
|
11761
|
-
|
11762
|
-
|
11763
|
-
|
11764
|
-
|
11765
|
-
|
11766
|
-
|
11767
|
-
|
11768
|
-
|
11769
|
-
|
11770
|
-
|
11771
|
-
|
11772
|
-
|
11773
|
-
|
11774
|
-
|
12101
|
+
eventTrigger.share.push(
|
12102
|
+
meeting.webinar.selfIsAttendee
|
12103
|
+
? {
|
12104
|
+
eventName: EVENT_TRIGGERS.MEETING_STARTED_SHARING_REMOTE,
|
12105
|
+
functionName: 'remoteShare',
|
12106
|
+
eventPayload: {
|
12107
|
+
memberId: null,
|
12108
|
+
url,
|
12109
|
+
shareInstanceId,
|
12110
|
+
annotationInfo: undefined,
|
12111
|
+
resourceType: undefined,
|
12112
|
+
},
|
12113
|
+
}
|
12114
|
+
: {
|
12115
|
+
eventName: EVENT_TRIGGERS.MEETING_STARTED_SHARING_WHITEBOARD,
|
12116
|
+
functionName: 'startWhiteboardShare',
|
12117
|
+
eventPayload: {resourceUrl, memberId: beneficiaryId},
|
12118
|
+
}
|
12119
|
+
);
|
11775
12120
|
|
12121
|
+
shareStatus = meeting.webinar.selfIsAttendee
|
12122
|
+
? SHARE_STATUS.REMOTE_SHARE_ACTIVE
|
12123
|
+
: SHARE_STATUS.WHITEBOARD_SHARE_ACTIVE;
|
11776
12124
|
}
|
11777
12125
|
|
11778
12126
|
if (eventTrigger.member) {
|
@@ -11804,24 +12152,29 @@ describe('plugin-meetings', () => {
|
|
11804
12152
|
newPayload.current.content.disposition = FLOOR_ACTION.ACCEPTED;
|
11805
12153
|
newPayload.current.content.beneficiaryId = otherBeneficiaryId;
|
11806
12154
|
|
11807
|
-
eventTrigger.share.push(
|
11808
|
-
|
11809
|
-
|
11810
|
-
|
11811
|
-
|
11812
|
-
|
11813
|
-
|
11814
|
-
|
11815
|
-
|
11816
|
-
|
11817
|
-
|
11818
|
-
|
11819
|
-
|
11820
|
-
|
11821
|
-
|
11822
|
-
|
11823
|
-
|
12155
|
+
eventTrigger.share.push(
|
12156
|
+
meeting.webinar.selfIsAttendee
|
12157
|
+
? {
|
12158
|
+
eventName: EVENT_TRIGGERS.MEETING_STARTED_SHARING_REMOTE,
|
12159
|
+
functionName: 'remoteShare',
|
12160
|
+
eventPayload: {
|
12161
|
+
memberId: null,
|
12162
|
+
url,
|
12163
|
+
shareInstanceId,
|
12164
|
+
annotationInfo: undefined,
|
12165
|
+
resourceType: undefined,
|
12166
|
+
},
|
12167
|
+
}
|
12168
|
+
: {
|
12169
|
+
eventName: EVENT_TRIGGERS.MEETING_STARTED_SHARING_WHITEBOARD,
|
12170
|
+
functionName: 'startWhiteboardShare',
|
12171
|
+
eventPayload: {resourceUrl, memberId: beneficiaryId},
|
12172
|
+
}
|
12173
|
+
);
|
11824
12174
|
|
12175
|
+
shareStatus = meeting.webinar.selfIsAttendee
|
12176
|
+
? SHARE_STATUS.REMOTE_SHARE_ACTIVE
|
12177
|
+
: SHARE_STATUS.WHITEBOARD_SHARE_ACTIVE;
|
11825
12178
|
} else {
|
11826
12179
|
eventTrigger.share.push({
|
11827
12180
|
eventName: EVENT_TRIGGERS.MEETING_STOPPED_SHARING_WHITEBOARD,
|
@@ -11951,24 +12304,26 @@ describe('plugin-meetings', () => {
|
|
11951
12304
|
describe('Whiteboard Share - Webinar Attendee', () => {
|
11952
12305
|
it('Scenario #1: Whiteboard sharing as a webinar attendee', () => {
|
11953
12306
|
// Set the webinar attendee flag
|
11954
|
-
meeting.webinar = {
|
12307
|
+
meeting.webinar = {selfIsAttendee: true};
|
11955
12308
|
meeting.locusInfo.info.isWebinar = true;
|
12309
|
+
meeting.shareCAEventSentStatus.receiveStart = true;
|
12310
|
+
meeting.shareCAEventSentStatus.receiveStop = true;
|
11956
12311
|
|
11957
12312
|
// Step 1: Start sharing whiteboard A
|
11958
12313
|
const data1 = generateData(
|
11959
|
-
blankPayload,
|
11960
|
-
true,
|
11961
|
-
false,
|
11962
|
-
USER_IDS.REMOTE_A,
|
12314
|
+
blankPayload, // Initial payload
|
12315
|
+
true, // isGranting: Granting share
|
12316
|
+
false, // isContent: Whiteboard (not content)
|
12317
|
+
USER_IDS.REMOTE_A, // Beneficiary ID: Remote user A
|
11963
12318
|
RESOURCE_URLS.WHITEBOARD_A // Resource URL: Whiteboard A
|
11964
12319
|
);
|
11965
12320
|
|
11966
12321
|
// Step 2: Stop sharing whiteboard A
|
11967
12322
|
const data2 = generateData(
|
11968
|
-
data1.payload,
|
11969
|
-
false,
|
11970
|
-
false,
|
11971
|
-
USER_IDS.REMOTE_A
|
12323
|
+
data1.payload, // Updated payload from Step 1
|
12324
|
+
false, // isGranting: Stopping share
|
12325
|
+
false, // isContent: Whiteboard
|
12326
|
+
USER_IDS.REMOTE_A // Beneficiary ID: Remote user A
|
11972
12327
|
);
|
11973
12328
|
|
11974
12329
|
// Validate the payload changes and status updates
|
@@ -11976,10 +12331,11 @@ describe('plugin-meetings', () => {
|
|
11976
12331
|
|
11977
12332
|
// Specific assertions for webinar attendee status
|
11978
12333
|
assert.equal(meeting.shareStatus, SHARE_STATUS.REMOTE_SHARE_ACTIVE);
|
12334
|
+
assert.equal(meeting.shareCAEventSentStatus.receiveStart, false);
|
12335
|
+
assert.equal(meeting.shareCAEventSentStatus.receiveStop, false);
|
11979
12336
|
});
|
11980
12337
|
});
|
11981
12338
|
|
11982
|
-
|
11983
12339
|
describe('Whiteboard A --> Whiteboard B', () => {
|
11984
12340
|
it('Scenario #1: you share both whiteboards', () => {
|
11985
12341
|
const data1 = generateData(
|
@@ -12632,6 +12988,31 @@ describe('plugin-meetings', () => {
|
|
12632
12988
|
});
|
12633
12989
|
});
|
12634
12990
|
});
|
12991
|
+
|
12992
|
+
describe('handleShareVideoStreamMuteStateChange', () => {
|
12993
|
+
it('should emit MEETING_SHARE_VIDEO_MUTE_STATE_CHANGE event with correct fields', () => {
|
12994
|
+
meeting.isMultistream = true;
|
12995
|
+
meeting.statsAnalyzer = {shareVideoEncoderImplementation: 'OpenH264'};
|
12996
|
+
meeting.mediaProperties.shareVideoStream = {
|
12997
|
+
getSettings: sinon.stub().returns({displaySurface: 'monitor', frameRate: 30}),
|
12998
|
+
};
|
12999
|
+
|
13000
|
+
meeting.handleShareVideoStreamMuteStateChange(true);
|
13001
|
+
|
13002
|
+
assert.calledOnceWithExactly(
|
13003
|
+
Metrics.sendBehavioralMetric,
|
13004
|
+
BEHAVIORAL_METRICS.MEETING_SHARE_VIDEO_MUTE_STATE_CHANGE,
|
13005
|
+
{
|
13006
|
+
correlationId: meeting.correlationId,
|
13007
|
+
muted: true,
|
13008
|
+
encoderImplementation: 'OpenH264',
|
13009
|
+
displaySurface: 'monitor',
|
13010
|
+
isMultistream: true,
|
13011
|
+
frameRate: 30,
|
13012
|
+
}
|
13013
|
+
);
|
13014
|
+
});
|
13015
|
+
});
|
12635
13016
|
});
|
12636
13017
|
|
12637
13018
|
describe('#startKeepAlive', () => {
|
@@ -12799,6 +13180,38 @@ describe('plugin-meetings', () => {
|
|
12799
13180
|
});
|
12800
13181
|
});
|
12801
13182
|
|
13183
|
+
describe('#setPostMeetingDataConsent', () => {
|
13184
|
+
it('should have #setPostMeetingDataConsent', () => {
|
13185
|
+
assert.exists(meeting.setPostMeetingDataConsent);
|
13186
|
+
});
|
13187
|
+
|
13188
|
+
beforeEach(() => {
|
13189
|
+
meeting.meetingRequest.setPostMeetingDataConsent = sinon
|
13190
|
+
.stub()
|
13191
|
+
.returns(Promise.resolve());
|
13192
|
+
});
|
13193
|
+
|
13194
|
+
[true, false].forEach((accept) => {
|
13195
|
+
it(`should send consent with ${accept}`, async () => {
|
13196
|
+
const id = uuidv4();
|
13197
|
+
meeting.locusUrl = `https://locus-test.wbx2.com/locus/api/v1/loci/${accept}`;
|
13198
|
+
meeting.deviceUrl = `https://wdm-test.wbx2.com/wdm/api/v1/devices/${accept}`;
|
13199
|
+
meeting.members.selfId = id;
|
13200
|
+
|
13201
|
+
const consentPromise = meeting.setPostMeetingDataConsent(accept);
|
13202
|
+
|
13203
|
+
assert.exists(consentPromise.then);
|
13204
|
+
await consentPromise;
|
13205
|
+
assert.calledOnceWithExactly(meeting.meetingRequest.setPostMeetingDataConsent, {
|
13206
|
+
locusUrl: `https://locus-test.wbx2.com/locus/api/v1/loci/${accept}`,
|
13207
|
+
postMeetingDataConsent: accept,
|
13208
|
+
selfId: id,
|
13209
|
+
deviceUrl: `https://wdm-test.wbx2.com/wdm/api/v1/devices/${accept}`,
|
13210
|
+
});
|
13211
|
+
});
|
13212
|
+
});
|
13213
|
+
});
|
13214
|
+
|
12802
13215
|
describe('#sendReaction', () => {
|
12803
13216
|
it('should have #sendReaction', () => {
|
12804
13217
|
assert.exists(meeting.sendReaction);
|
@@ -13290,7 +13703,7 @@ describe('plugin-meetings', () => {
|
|
13290
13703
|
await meeting.roapMessageReceived(fakeMessage);
|
13291
13704
|
|
13292
13705
|
assert.fail('Expected MultistreamNotSupportedError to be thrown');
|
13293
|
-
} catch(e) {
|
13706
|
+
} catch (e) {
|
13294
13707
|
assert.isTrue(e instanceof MultistreamNotSupportedError);
|
13295
13708
|
}
|
13296
13709
|
|