@webex/plugin-meetings 3.8.0-next.7 → 3.8.0-next.71
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/breakouts/breakout.js +1 -1
- package/dist/breakouts/index.js +1 -1
- package/dist/common/errors/webex-errors.js +12 -2
- package/dist/common/errors/webex-errors.js.map +1 -1
- package/dist/config.js +4 -1
- package/dist/config.js.map +1 -1
- package/dist/constants.js +17 -121
- 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 +32 -12
- package/dist/locus-info/index.js.map +1 -1
- package/dist/locus-info/selfUtils.js +432 -418
- package/dist/locus-info/selfUtils.js.map +1 -1
- package/dist/media/index.js +14 -16
- package/dist/media/index.js.map +1 -1
- package/dist/media/properties.js +94 -6
- package/dist/media/properties.js.map +1 -1
- package/dist/meeting/brbState.js +6 -0
- package/dist/meeting/brbState.js.map +1 -1
- package/dist/meeting/in-meeting-actions.js +17 -1
- package/dist/meeting/in-meeting-actions.js.map +1 -1
- package/dist/meeting/index.js +541 -302
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/locusMediaRequest.js +0 -17
- package/dist/meeting/locusMediaRequest.js.map +1 -1
- package/dist/meeting/muteState.js +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 +13 -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 +114 -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 +330 -353
- package/dist/member/util.js.map +1 -1
- package/dist/members/index.js +23 -0
- package/dist/members/index.js.map +1 -1
- package/dist/members/request.js +21 -0
- package/dist/members/request.js.map +1 -1
- package/dist/members/util.js +15 -0
- package/dist/members/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/common/errors/webex-errors.d.ts +7 -1
- package/dist/types/config.d.ts +2 -0
- package/dist/types/constants.d.ts +12 -85
- package/dist/types/controls-options-manager/enums.d.ts +3 -1
- package/dist/types/controls-options-manager/types.d.ts +7 -1
- package/dist/types/locus-info/index.d.ts +3 -3
- package/dist/types/locus-info/selfUtils.d.ts +216 -1
- package/dist/types/media/properties.d.ts +15 -0
- package/dist/types/meeting/in-meeting-actions.d.ts +16 -0
- package/dist/types/meeting/index.d.ts +32 -1
- package/dist/types/meeting/muteState.d.ts +0 -1
- package/dist/types/meeting/request.d.ts +12 -1
- package/dist/types/meeting/request.type.d.ts +6 -0
- package/dist/types/meeting/util.d.ts +3 -1
- package/dist/types/meeting-info/meeting-info-v2.d.ts +80 -0
- package/dist/types/meetings/index.d.ts +48 -0
- package/dist/types/member/index.d.ts +1 -0
- package/dist/types/member/util.d.ts +159 -1
- package/dist/types/members/index.d.ts +8 -0
- package/dist/types/members/request.d.ts +19 -0
- package/dist/types/members/util.d.ts +13 -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 +24 -23
- package/src/common/errors/webex-errors.ts +8 -1
- package/src/config.ts +2 -0
- package/src/constants.ts +19 -90
- package/src/controls-options-manager/enums.ts +2 -0
- package/src/controls-options-manager/types.ts +11 -1
- package/src/controls-options-manager/util.ts +62 -0
- package/src/locus-info/controlsUtils.ts +44 -14
- package/src/locus-info/index.ts +38 -12
- package/src/locus-info/selfUtils.ts +496 -442
- package/src/media/index.ts +20 -21
- package/src/media/properties.ts +96 -0
- package/src/meeting/brbState.ts +7 -0
- package/src/meeting/in-meeting-actions.ts +32 -0
- package/src/meeting/index.ts +346 -93
- package/src/meeting/locusMediaRequest.ts +0 -18
- 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 +11 -2
- package/src/meeting-info/meeting-info-v2.ts +247 -6
- package/src/meetings/index.ts +128 -1
- package/src/meetings/util.ts +18 -0
- package/src/member/index.ts +13 -2
- package/src/member/util.ts +351 -348
- package/src/members/index.ts +25 -0
- package/src/members/request.ts +26 -0
- package/src/members/util.ts +16 -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 +141 -73
- package/test/unit/spec/locus-info/selfUtils.js +98 -24
- package/test/unit/spec/media/index.ts +98 -16
- package/test/unit/spec/media/properties.ts +130 -0
- package/test/unit/spec/meeting/brbState.ts +19 -0
- package/test/unit/spec/meeting/in-meeting-actions.ts +19 -4
- package/test/unit/spec/meeting/index.js +524 -35
- package/test/unit/spec/meeting/locusMediaRequest.ts +0 -30
- 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 +119 -18
- package/test/unit/spec/meeting-info/meetinginfov2.js +443 -114
- package/test/unit/spec/meetings/index.js +133 -2
- package/test/unit/spec/member/index.js +7 -0
- package/test/unit/spec/member/util.js +24 -0
- package/test/unit/spec/members/index.js +103 -26
- package/test/unit/spec/members/request.js +45 -22
- package/test/unit/spec/members/utils.js +33 -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';
|
@@ -115,9 +116,9 @@ import {ERROR_DESCRIPTIONS} from '@webex/internal-plugin-metrics/src/call-diagno
|
|
115
116
|
import MeetingCollection from '@webex/plugin-meetings/src/meetings/collection';
|
116
117
|
|
117
118
|
import {EVENT_TRIGGERS as VOICEAEVENTS} from '@webex/internal-plugin-voicea';
|
118
|
-
import {
|
119
|
+
import {createBrbState} from '@webex/plugin-meetings/src/meeting/brbState';
|
119
120
|
import JoinForbiddenError from '../../../../src/common/errors/join-forbidden-error';
|
120
|
-
import {
|
121
|
+
import {EventEmitter} from 'stream';
|
121
122
|
|
122
123
|
describe('plugin-meetings', () => {
|
123
124
|
const logger = {
|
@@ -210,6 +211,7 @@ describe('plugin-meetings', () => {
|
|
210
211
|
let membersSpy;
|
211
212
|
let meetingRequestSpy;
|
212
213
|
let correlationId;
|
214
|
+
let isoLocalClientMeetingJoinTime;
|
213
215
|
let uploadEvent;
|
214
216
|
|
215
217
|
beforeEach(() => {
|
@@ -241,6 +243,7 @@ describe('plugin-meetings', () => {
|
|
241
243
|
},
|
242
244
|
});
|
243
245
|
|
246
|
+
webex.internal.newMetrics.callDiagnosticMetrics.clearErrorCache = sinon.stub();
|
244
247
|
webex.internal.support.submitLogs = sinon.stub().returns(Promise.resolve());
|
245
248
|
webex.internal.services = {get: sinon.stub().returns('locus-url')};
|
246
249
|
webex.credentials.getOrgId = sinon.stub().returns('fake-org-id');
|
@@ -251,6 +254,7 @@ describe('plugin-meetings', () => {
|
|
251
254
|
getReachabilityResults: sinon.stub().resolves(undefined),
|
252
255
|
getReachabilityMetrics: sinon.stub().resolves({}),
|
253
256
|
stopReachability: sinon.stub(),
|
257
|
+
isSubnetReachable: sinon.stub().returns(true),
|
254
258
|
};
|
255
259
|
webex.internal.llm.on = sinon.stub();
|
256
260
|
webex.internal.newMetrics.callDiagnosticLatencies = new CallDiagnosticLatencies(
|
@@ -281,7 +285,7 @@ describe('plugin-meetings', () => {
|
|
281
285
|
testDestination = `testDestination-${uuid.v4()}`;
|
282
286
|
correlationId = uuid.v4();
|
283
287
|
uploadEvent = new EventEmitter();
|
284
|
-
uploadEvent.addListener('progress', () => {})
|
288
|
+
uploadEvent.addListener('progress', () => {});
|
285
289
|
|
286
290
|
meeting = new Meeting(
|
287
291
|
{
|
@@ -1692,10 +1696,6 @@ describe('plugin-meetings', () => {
|
|
1692
1696
|
sandbox.stub(MeetingUtil, 'joinMeeting').returns(Promise.resolve(joinMeetingResult));
|
1693
1697
|
});
|
1694
1698
|
|
1695
|
-
afterEach(() => {
|
1696
|
-
assert.exists(meeting.isoLocalClientMeetingJoinTime);
|
1697
|
-
});
|
1698
|
-
|
1699
1699
|
it('should join the meeting and return promise', async () => {
|
1700
1700
|
const join = meeting.join({pstnAudioType: 'dial-in'});
|
1701
1701
|
meeting.config.enableAutomaticLLM = true;
|
@@ -2047,7 +2047,12 @@ describe('plugin-meetings', () => {
|
|
2047
2047
|
meeting.mediaProperties.waitForMediaConnectionConnected = sinon.stub().resolves();
|
2048
2048
|
meeting.mediaProperties.getCurrentConnectionInfo = sinon
|
2049
2049
|
.stub()
|
2050
|
-
.resolves({
|
2050
|
+
.resolves({
|
2051
|
+
connectionType: 'udp',
|
2052
|
+
selectedCandidatePairChanges: 2,
|
2053
|
+
numTransports: 1,
|
2054
|
+
ipVersion: 'IPv6',
|
2055
|
+
});
|
2051
2056
|
meeting.audio = muteStateStub;
|
2052
2057
|
meeting.video = muteStateStub;
|
2053
2058
|
sinon.stub(Media, 'createMediaConnection').returns(fakeMediaConnection);
|
@@ -2110,6 +2115,7 @@ describe('plugin-meetings', () => {
|
|
2110
2115
|
someReachabilityMetric2: 'some value2',
|
2111
2116
|
}),
|
2112
2117
|
stopReachability: sinon.stub(),
|
2118
|
+
isSubnetReachable: sinon.stub().returns(false),
|
2113
2119
|
};
|
2114
2120
|
|
2115
2121
|
const forceRtcMetricsSend = sinon.stub().resolves();
|
@@ -2165,6 +2171,7 @@ describe('plugin-meetings', () => {
|
|
2165
2171
|
someReachabilityMetric1: 'some value1',
|
2166
2172
|
someReachabilityMetric2: 'some value2',
|
2167
2173
|
selectedCandidatePairChanges: 2,
|
2174
|
+
isSubnetReachable: null,
|
2168
2175
|
numTransports: 1,
|
2169
2176
|
iceCandidatesCount: 0,
|
2170
2177
|
}
|
@@ -2211,6 +2218,7 @@ describe('plugin-meetings', () => {
|
|
2211
2218
|
signalingState: 'unknown',
|
2212
2219
|
connectionState: 'unknown',
|
2213
2220
|
iceConnectionState: 'unknown',
|
2221
|
+
isSubnetReachable: null,
|
2214
2222
|
})
|
2215
2223
|
);
|
2216
2224
|
|
@@ -2225,6 +2233,7 @@ describe('plugin-meetings', () => {
|
|
2225
2233
|
someReachabilityMetric1: 'some value1',
|
2226
2234
|
someReachabilityMetric2: 'some value2',
|
2227
2235
|
}),
|
2236
|
+
isSubnetReachable: sinon.stub().returns(true),
|
2228
2237
|
};
|
2229
2238
|
|
2230
2239
|
meeting.waitForRemoteSDPAnswer = sinon.stub().rejects();
|
@@ -2275,6 +2284,7 @@ describe('plugin-meetings', () => {
|
|
2275
2284
|
selectedCandidatePairChanges: 2,
|
2276
2285
|
numTransports: 1,
|
2277
2286
|
iceCandidatesCount: 0,
|
2287
|
+
isSubnetReachable: null,
|
2278
2288
|
}
|
2279
2289
|
);
|
2280
2290
|
});
|
@@ -2332,6 +2342,7 @@ describe('plugin-meetings', () => {
|
|
2332
2342
|
signalingState: 'have-local-offer',
|
2333
2343
|
connectionState: 'connecting',
|
2334
2344
|
iceConnectionState: 'checking',
|
2345
|
+
isSubnetReachable: null,
|
2335
2346
|
})
|
2336
2347
|
);
|
2337
2348
|
|
@@ -2389,6 +2400,7 @@ describe('plugin-meetings', () => {
|
|
2389
2400
|
signalingState: 'have-local-offer',
|
2390
2401
|
connectionState: 'connecting',
|
2391
2402
|
iceConnectionState: 'checking',
|
2403
|
+
isSubnetReachable: null,
|
2392
2404
|
})
|
2393
2405
|
);
|
2394
2406
|
|
@@ -2667,7 +2679,7 @@ describe('plugin-meetings', () => {
|
|
2667
2679
|
|
2668
2680
|
meeting.roap.doTurnDiscovery = sinon.stub().resolves({
|
2669
2681
|
turnServerInfo: {
|
2670
|
-
|
2682
|
+
urls: [FAKE_TURN_URL],
|
2671
2683
|
username: FAKE_TURN_USER,
|
2672
2684
|
password: FAKE_TURN_PASSWORD,
|
2673
2685
|
},
|
@@ -2689,7 +2701,7 @@ describe('plugin-meetings', () => {
|
|
2689
2701
|
meeting.id,
|
2690
2702
|
sinon.match({
|
2691
2703
|
turnServerInfo: {
|
2692
|
-
|
2704
|
+
urls: [FAKE_TURN_URL],
|
2693
2705
|
username: FAKE_TURN_USER,
|
2694
2706
|
password: FAKE_TURN_PASSWORD,
|
2695
2707
|
},
|
@@ -2724,8 +2736,9 @@ describe('plugin-meetings', () => {
|
|
2724
2736
|
sinon.stub().returns(FAKE_ERROR));
|
2725
2737
|
webex.meetings.reachability = {
|
2726
2738
|
isWebexMediaBackendUnreachable: sinon.stub().resolves(false),
|
2727
|
-
getReachabilityMetrics: sinon.stub().resolves(),
|
2739
|
+
getReachabilityMetrics: sinon.stub().resolves({}),
|
2728
2740
|
stopReachability: sinon.stub(),
|
2741
|
+
isSubnetReachable: sinon.stub().returns(true),
|
2729
2742
|
};
|
2730
2743
|
const MOCK_CLIENT_ERROR_CODE = 2004;
|
2731
2744
|
const generateClientErrorCodeForIceFailureStub = sinon
|
@@ -2747,14 +2760,15 @@ describe('plugin-meetings', () => {
|
|
2747
2760
|
.onSecondCall()
|
2748
2761
|
.returns({
|
2749
2762
|
turnServerInfo: {
|
2750
|
-
|
2763
|
+
urls: [FAKE_TURN_URL],
|
2751
2764
|
username: FAKE_TURN_USER,
|
2752
2765
|
password: FAKE_TURN_PASSWORD,
|
2753
2766
|
},
|
2754
2767
|
turnDiscoverySkippedReason: undefined,
|
2755
2768
|
});
|
2756
2769
|
meeting.meetingState = 'ACTIVE';
|
2757
|
-
|
2770
|
+
const error = {iceConnected: false};
|
2771
|
+
meeting.mediaProperties.waitForMediaConnectionConnected.rejects(error);
|
2758
2772
|
|
2759
2773
|
const forceRtcMetricsSend = sinon.stub().resolves();
|
2760
2774
|
const closeMediaConnectionStub = sinon.stub();
|
@@ -2772,6 +2786,7 @@ describe('plugin-meetings', () => {
|
|
2772
2786
|
})
|
2773
2787
|
.catch((err) => {
|
2774
2788
|
errorThrown = err;
|
2789
|
+
assert.instanceOf(err.cause, Error);
|
2775
2790
|
assert.instanceOf(err, AddMediaFailed);
|
2776
2791
|
});
|
2777
2792
|
|
@@ -2828,6 +2843,7 @@ describe('plugin-meetings', () => {
|
|
2828
2843
|
},
|
2829
2844
|
options: {
|
2830
2845
|
meetingId: meeting.id,
|
2846
|
+
rawError: error,
|
2831
2847
|
},
|
2832
2848
|
});
|
2833
2849
|
assert.calledWith(webex.internal.newMetrics.submitClientEvent.thirdCall, {
|
@@ -2839,6 +2855,7 @@ describe('plugin-meetings', () => {
|
|
2839
2855
|
},
|
2840
2856
|
options: {
|
2841
2857
|
meetingId: meeting.id,
|
2858
|
+
rawError: error,
|
2842
2859
|
},
|
2843
2860
|
});
|
2844
2861
|
|
@@ -2905,6 +2922,7 @@ describe('plugin-meetings', () => {
|
|
2905
2922
|
selectedCandidatePairChanges: 2,
|
2906
2923
|
numTransports: 1,
|
2907
2924
|
iceCandidatesCount: 0,
|
2925
|
+
isSubnetReachable: null,
|
2908
2926
|
},
|
2909
2927
|
]);
|
2910
2928
|
|
@@ -2935,6 +2953,7 @@ describe('plugin-meetings', () => {
|
|
2935
2953
|
.resolves(false),
|
2936
2954
|
getReachabilityMetrics: sinon.stub().resolves({}),
|
2937
2955
|
stopReachability: sinon.stub(),
|
2956
|
+
isSubnetReachable: sinon.stub().returns(true),
|
2938
2957
|
};
|
2939
2958
|
const getErrorPayloadForClientErrorCodeStub =
|
2940
2959
|
(webex.internal.newMetrics.callDiagnosticMetrics.getErrorPayloadForClientErrorCode =
|
@@ -2959,16 +2978,19 @@ describe('plugin-meetings', () => {
|
|
2959
2978
|
.onSecondCall()
|
2960
2979
|
.returns({
|
2961
2980
|
turnServerInfo: {
|
2962
|
-
|
2981
|
+
urls: [FAKE_TURN_URL],
|
2963
2982
|
username: FAKE_TURN_USER,
|
2964
2983
|
password: FAKE_TURN_PASSWORD,
|
2965
2984
|
},
|
2966
2985
|
turnDiscoverySkippedReason: undefined,
|
2967
2986
|
});
|
2987
|
+
|
2988
|
+
const mediaConnectionError = new Error('fake error');
|
2989
|
+
|
2968
2990
|
meeting.mediaProperties.waitForMediaConnectionConnected = sinon
|
2969
2991
|
.stub()
|
2970
2992
|
.onFirstCall()
|
2971
|
-
.rejects()
|
2993
|
+
.rejects(mediaConnectionError)
|
2972
2994
|
.onSecondCall()
|
2973
2995
|
.resolves();
|
2974
2996
|
|
@@ -3037,10 +3059,14 @@ describe('plugin-meetings', () => {
|
|
3037
3059
|
},
|
3038
3060
|
options: {
|
3039
3061
|
meetingId: meeting.id,
|
3062
|
+
rawError: mediaConnectionError,
|
3040
3063
|
},
|
3041
3064
|
});
|
3042
3065
|
assert.calledWith(webex.internal.newMetrics.submitClientEvent.thirdCall, {
|
3043
3066
|
name: 'client.media-engine.ready',
|
3067
|
+
payload: {
|
3068
|
+
ipVersion: 'IPv6',
|
3069
|
+
},
|
3044
3070
|
options: {
|
3045
3071
|
meetingId: meeting.id,
|
3046
3072
|
},
|
@@ -3097,11 +3123,13 @@ describe('plugin-meetings', () => {
|
|
3097
3123
|
locus_id: meeting.locusUrl.split('/').pop(),
|
3098
3124
|
connectionType: 'udp',
|
3099
3125
|
selectedCandidatePairChanges: 2,
|
3126
|
+
ipVersion: 'IPv6',
|
3100
3127
|
numTransports: 1,
|
3101
3128
|
isMultistream: false,
|
3102
3129
|
retriedWithTurnServer: true,
|
3103
3130
|
isJoinWithMediaRetry: false,
|
3104
3131
|
iceCandidatesCount: 0,
|
3132
|
+
isSubnetReachable: null,
|
3105
3133
|
},
|
3106
3134
|
]);
|
3107
3135
|
meeting.roap.doTurnDiscovery;
|
@@ -3136,7 +3164,7 @@ describe('plugin-meetings', () => {
|
|
3136
3164
|
.onSecondCall()
|
3137
3165
|
.returns({
|
3138
3166
|
turnServerInfo: {
|
3139
|
-
|
3167
|
+
urls: [FAKE_TURN_URL],
|
3140
3168
|
username: FAKE_TURN_USER,
|
3141
3169
|
password: FAKE_TURN_PASSWORD,
|
3142
3170
|
},
|
@@ -3188,7 +3216,7 @@ describe('plugin-meetings', () => {
|
|
3188
3216
|
.onSecondCall()
|
3189
3217
|
.returns({
|
3190
3218
|
turnServerInfo: {
|
3191
|
-
|
3219
|
+
urls: [FAKE_TURN_URL],
|
3192
3220
|
username: FAKE_TURN_USER,
|
3193
3221
|
password: FAKE_TURN_PASSWORD,
|
3194
3222
|
},
|
@@ -3230,6 +3258,7 @@ describe('plugin-meetings', () => {
|
|
3230
3258
|
someReachabilityMetric2: 'some value2',
|
3231
3259
|
}),
|
3232
3260
|
stopReachability: sinon.stub(),
|
3261
|
+
isSubnetReachable: sinon.stub().returns(true),
|
3233
3262
|
};
|
3234
3263
|
meeting.iceCandidatesCount = 3;
|
3235
3264
|
meeting.iceCandidateErrors.set('701_error', 3);
|
@@ -3248,6 +3277,7 @@ describe('plugin-meetings', () => {
|
|
3248
3277
|
locus_id: meeting.locusUrl.split('/').pop(),
|
3249
3278
|
connectionType: 'udp',
|
3250
3279
|
selectedCandidatePairChanges: 2,
|
3280
|
+
ipVersion: 'IPv6',
|
3251
3281
|
numTransports: 1,
|
3252
3282
|
isMultistream: false,
|
3253
3283
|
retriedWithTurnServer: false,
|
@@ -3257,6 +3287,7 @@ describe('plugin-meetings', () => {
|
|
3257
3287
|
iceCandidatesCount: 3,
|
3258
3288
|
'701_error': 3,
|
3259
3289
|
'701_turn_host_lookup_received_error': 1,
|
3290
|
+
isSubnetReachable: null,
|
3260
3291
|
}
|
3261
3292
|
);
|
3262
3293
|
|
@@ -3319,6 +3350,7 @@ describe('plugin-meetings', () => {
|
|
3319
3350
|
iceConnectionState: 'unknown',
|
3320
3351
|
selectedCandidatePairChanges: 2,
|
3321
3352
|
numTransports: 1,
|
3353
|
+
isSubnetReachable: null,
|
3322
3354
|
iceCandidatesCount: 0,
|
3323
3355
|
}
|
3324
3356
|
);
|
@@ -3380,6 +3412,117 @@ describe('plugin-meetings', () => {
|
|
3380
3412
|
numTransports: 1,
|
3381
3413
|
'701_error': 2,
|
3382
3414
|
'701_turn_host_lookup_received_error': 1,
|
3415
|
+
isSubnetReachable: null,
|
3416
|
+
iceCandidatesCount: 0,
|
3417
|
+
}
|
3418
|
+
);
|
3419
|
+
|
3420
|
+
assert.isOk(errorThrown);
|
3421
|
+
});
|
3422
|
+
|
3423
|
+
it('should send valid isSubnetReachability if media connection success', async () => {
|
3424
|
+
meeting.roap.doTurnDiscovery = sinon.stub().returns({
|
3425
|
+
turnServerInfo: undefined,
|
3426
|
+
turnDiscoverySkippedReason: undefined,
|
3427
|
+
});
|
3428
|
+
meeting.meetingState = 'ACTIVE';
|
3429
|
+
meeting.mediaProperties.waitForMediaConnectionConnected.resolves();
|
3430
|
+
meeting.webex.meetings.reachability = {
|
3431
|
+
getReachabilityMetrics: sinon.stub().resolves({
|
3432
|
+
reachability_public_udp_success: 5,
|
3433
|
+
}),
|
3434
|
+
stopReachability: sinon.stub(),
|
3435
|
+
isSubnetReachable: sinon.stub().returns(false),
|
3436
|
+
};
|
3437
|
+
|
3438
|
+
const forceRtcMetricsSend = sinon.stub().resolves();
|
3439
|
+
const closeMediaConnectionStub = sinon.stub();
|
3440
|
+
Media.createMediaConnection = sinon.stub().returns({
|
3441
|
+
close: closeMediaConnectionStub,
|
3442
|
+
forceRtcMetricsSend,
|
3443
|
+
getConnectionState: sinon.stub().returns(ConnectionState.Connected),
|
3444
|
+
initiateOffer: sinon.stub().resolves({}),
|
3445
|
+
on: sinon.stub(),
|
3446
|
+
});
|
3447
|
+
|
3448
|
+
await meeting.addMedia({
|
3449
|
+
mediaSettings: {},
|
3450
|
+
});
|
3451
|
+
|
3452
|
+
assert.calledWith(Metrics.sendBehavioralMetric, BEHAVIORAL_METRICS.ADD_MEDIA_SUCCESS, {
|
3453
|
+
correlation_id: meeting.correlationId,
|
3454
|
+
locus_id: meeting.locusUrl.split('/').pop(),
|
3455
|
+
connectionType: 'udp',
|
3456
|
+
ipVersion: 'IPv6',
|
3457
|
+
selectedCandidatePairChanges: 2,
|
3458
|
+
numTransports: 1,
|
3459
|
+
isMultistream: false,
|
3460
|
+
retriedWithTurnServer: false,
|
3461
|
+
isJoinWithMediaRetry: false,
|
3462
|
+
iceCandidatesCount: 0,
|
3463
|
+
reachability_public_udp_success: 5,
|
3464
|
+
isSubnetReachable: false,
|
3465
|
+
});
|
3466
|
+
});
|
3467
|
+
|
3468
|
+
it('should send valid isSubnetReachability if media connection fails', async () => {
|
3469
|
+
let errorThrown = undefined;
|
3470
|
+
|
3471
|
+
meeting.roap.doTurnDiscovery = sinon.stub().returns({
|
3472
|
+
turnServerInfo: undefined,
|
3473
|
+
turnDiscoverySkippedReason: undefined,
|
3474
|
+
});
|
3475
|
+
meeting.meetingState = 'ACTIVE';
|
3476
|
+
meeting.mediaProperties.waitForMediaConnectionConnected.rejects({iceConnected: false});
|
3477
|
+
meeting.webex.meetings.reachability = {
|
3478
|
+
getReachabilityMetrics: sinon.stub().resolves({
|
3479
|
+
reachability_public_udp_success: 5,
|
3480
|
+
}),
|
3481
|
+
stopReachability: sinon.stub(),
|
3482
|
+
isSubnetReachable: sinon.stub().returns(true),
|
3483
|
+
};
|
3484
|
+
|
3485
|
+
const forceRtcMetricsSend = sinon.stub().resolves();
|
3486
|
+
const closeMediaConnectionStub = sinon.stub();
|
3487
|
+
Media.createMediaConnection = sinon.stub().returns({
|
3488
|
+
close: closeMediaConnectionStub,
|
3489
|
+
forceRtcMetricsSend,
|
3490
|
+
getConnectionState: sinon.stub().returns(ConnectionState.Connected),
|
3491
|
+
initiateOffer: sinon.stub().resolves({}),
|
3492
|
+
on: sinon.stub(),
|
3493
|
+
});
|
3494
|
+
|
3495
|
+
await meeting
|
3496
|
+
.addMedia({
|
3497
|
+
mediaSettings: {},
|
3498
|
+
})
|
3499
|
+
.catch((err) => {
|
3500
|
+
errorThrown = err;
|
3501
|
+
assert.instanceOf(err, AddMediaFailed);
|
3502
|
+
});
|
3503
|
+
|
3504
|
+
// Check that the only metric sent is ADD_MEDIA_FAILURE
|
3505
|
+
assert.calledOnceWithExactly(
|
3506
|
+
Metrics.sendBehavioralMetric,
|
3507
|
+
BEHAVIORAL_METRICS.ADD_MEDIA_FAILURE,
|
3508
|
+
{
|
3509
|
+
correlation_id: meeting.correlationId,
|
3510
|
+
locus_id: meeting.locusUrl.split('/').pop(),
|
3511
|
+
reason: errorThrown.message,
|
3512
|
+
stack: errorThrown.stack,
|
3513
|
+
code: errorThrown.code,
|
3514
|
+
turnDiscoverySkippedReason: undefined,
|
3515
|
+
turnServerUsed: true,
|
3516
|
+
retriedWithTurnServer: false,
|
3517
|
+
isMultistream: false,
|
3518
|
+
isJoinWithMediaRetry: false,
|
3519
|
+
signalingState: 'unknown',
|
3520
|
+
connectionState: 'unknown',
|
3521
|
+
iceConnectionState: 'unknown',
|
3522
|
+
selectedCandidatePairChanges: 2,
|
3523
|
+
numTransports: 1,
|
3524
|
+
reachability_public_udp_success: 5,
|
3525
|
+
isSubnetReachable: true,
|
3383
3526
|
iceCandidatesCount: 0,
|
3384
3527
|
}
|
3385
3528
|
);
|
@@ -3399,6 +3542,8 @@ describe('plugin-meetings', () => {
|
|
3399
3542
|
meeting.config.stats.enableStatsAnalyzer = true;
|
3400
3543
|
|
3401
3544
|
statsAnalyzerStub = new EventsScope();
|
3545
|
+
statsAnalyzerStub.getNetworkType = sinon.stub().returns('wifi');
|
3546
|
+
|
3402
3547
|
// mock the StatsAnalyzer constructor
|
3403
3548
|
sinon.stub(InternalMediaCoreModule, 'StatsAnalyzer').returns(statsAnalyzerStub);
|
3404
3549
|
|
@@ -3439,6 +3584,40 @@ describe('plugin-meetings', () => {
|
|
3439
3584
|
});
|
3440
3585
|
});
|
3441
3586
|
|
3587
|
+
it('LOCAL_MEDIA_STARTED triggers "meeting:media:local:start" event and does not send metric because we already have', async () => {
|
3588
|
+
meeting.shareCAEventSentStatus = {
|
3589
|
+
transmitStart: true,
|
3590
|
+
transmitStop: false,
|
3591
|
+
receiveStart: false,
|
3592
|
+
receiveStop: false,
|
3593
|
+
};
|
3594
|
+
statsAnalyzerStub.emit(
|
3595
|
+
{file: 'test', function: 'test'},
|
3596
|
+
StatsAnalyzerEventNames.LOCAL_MEDIA_STARTED,
|
3597
|
+
{mediaType: 'share'}
|
3598
|
+
);
|
3599
|
+
|
3600
|
+
assert.calledWith(
|
3601
|
+
TriggerProxy.trigger,
|
3602
|
+
sinon.match.instanceOf(Meeting),
|
3603
|
+
{
|
3604
|
+
file: 'meeting/index',
|
3605
|
+
function: 'addMedia',
|
3606
|
+
},
|
3607
|
+
EVENT_TRIGGERS.MEETING_MEDIA_LOCAL_STARTED,
|
3608
|
+
{
|
3609
|
+
mediaType: 'share',
|
3610
|
+
}
|
3611
|
+
);
|
3612
|
+
assert.neverCalledWith(webex.internal.newMetrics.submitClientEvent, {
|
3613
|
+
name: 'client.media.tx.start',
|
3614
|
+
payload: {mediaType: 'share', shareInstanceId: meeting.remoteShareInstanceId},
|
3615
|
+
options: {
|
3616
|
+
meetingId: meeting.id,
|
3617
|
+
},
|
3618
|
+
});
|
3619
|
+
});
|
3620
|
+
|
3442
3621
|
it('LOCAL_MEDIA_STOPPED triggers the right metrics', async () => {
|
3443
3622
|
statsAnalyzerStub.emit(
|
3444
3623
|
{file: 'test', function: 'test'},
|
@@ -3455,6 +3634,28 @@ describe('plugin-meetings', () => {
|
|
3455
3634
|
});
|
3456
3635
|
});
|
3457
3636
|
|
3637
|
+
it('LOCAL_MEDIA_STOPPED does not send metric because we already have', async () => {
|
3638
|
+
meeting.shareCAEventSentStatus = {
|
3639
|
+
transmitStart: false,
|
3640
|
+
transmitStop: true,
|
3641
|
+
receiveStart: false,
|
3642
|
+
receiveStop: false,
|
3643
|
+
};
|
3644
|
+
statsAnalyzerStub.emit(
|
3645
|
+
{file: 'test', function: 'test'},
|
3646
|
+
StatsAnalyzerEventNames.LOCAL_MEDIA_STOPPED,
|
3647
|
+
{mediaType: 'share'}
|
3648
|
+
);
|
3649
|
+
|
3650
|
+
assert.neverCalledWith(webex.internal.newMetrics.submitClientEvent, {
|
3651
|
+
name: 'client.media.tx.stop',
|
3652
|
+
payload: {mediaType: 'share', shareInstanceId: meeting.remoteShareInstanceId},
|
3653
|
+
options: {
|
3654
|
+
meetingId: meeting.id,
|
3655
|
+
},
|
3656
|
+
});
|
3657
|
+
});
|
3658
|
+
|
3458
3659
|
it('REMOTE_MEDIA_STARTED triggers "meeting:media:remote:start" event and sends metrics', async () => {
|
3459
3660
|
statsAnalyzerStub.emit(
|
3460
3661
|
{file: 'test', function: 'test'},
|
@@ -3535,6 +3736,47 @@ describe('plugin-meetings', () => {
|
|
3535
3736
|
});
|
3536
3737
|
});
|
3537
3738
|
|
3739
|
+
it('REMOTE_MEDIA_STARTED triggers "meeting:media:remote:start" event and does not send metric because we already have', async () => {
|
3740
|
+
meeting.shareCAEventSentStatus = {
|
3741
|
+
transmitStart: false,
|
3742
|
+
transmitStop: false,
|
3743
|
+
receiveStart: true,
|
3744
|
+
receiveStop: false,
|
3745
|
+
};
|
3746
|
+
statsAnalyzerStub.emit(
|
3747
|
+
{file: 'test', function: 'test'},
|
3748
|
+
StatsAnalyzerEventNames.REMOTE_MEDIA_STARTED,
|
3749
|
+
{mediaType: 'share'}
|
3750
|
+
);
|
3751
|
+
|
3752
|
+
assert.calledWith(
|
3753
|
+
TriggerProxy.trigger,
|
3754
|
+
sinon.match.instanceOf(Meeting),
|
3755
|
+
{
|
3756
|
+
file: 'meeting/index',
|
3757
|
+
function: 'addMedia',
|
3758
|
+
},
|
3759
|
+
EVENT_TRIGGERS.MEETING_MEDIA_REMOTE_STARTED,
|
3760
|
+
{
|
3761
|
+
mediaType: 'share',
|
3762
|
+
}
|
3763
|
+
);
|
3764
|
+
assert.neverCalledWith(webex.internal.newMetrics.submitClientEvent, {
|
3765
|
+
name: 'client.media.render.start',
|
3766
|
+
payload: {mediaType: 'share', shareInstanceId: meeting.remoteShareInstanceId},
|
3767
|
+
options: {
|
3768
|
+
meetingId: meeting.id,
|
3769
|
+
},
|
3770
|
+
});
|
3771
|
+
assert.neverCalledWith(webex.internal.newMetrics.submitClientEvent, {
|
3772
|
+
name: 'client.media.rx.start',
|
3773
|
+
payload: {mediaType: 'share', shareInstanceId: meeting.remoteShareInstanceId},
|
3774
|
+
options: {
|
3775
|
+
meetingId: meeting.id,
|
3776
|
+
},
|
3777
|
+
});
|
3778
|
+
});
|
3779
|
+
|
3538
3780
|
it('REMOTE_MEDIA_STOPPED triggers the right metrics for share', async () => {
|
3539
3781
|
statsAnalyzerStub.emit(
|
3540
3782
|
{file: 'test', function: 'test'},
|
@@ -3559,6 +3801,34 @@ describe('plugin-meetings', () => {
|
|
3559
3801
|
});
|
3560
3802
|
});
|
3561
3803
|
|
3804
|
+
it('REMOTE_MEDIA_STOPPED does not send metric because we already have', async () => {
|
3805
|
+
meeting.shareCAEventSentStatus = {
|
3806
|
+
transmitStart: false,
|
3807
|
+
transmitStop: false,
|
3808
|
+
receiveStart: true,
|
3809
|
+
receiveStop: true,
|
3810
|
+
};
|
3811
|
+
statsAnalyzerStub.emit(
|
3812
|
+
{file: 'test', function: 'test'},
|
3813
|
+
StatsAnalyzerEventNames.REMOTE_MEDIA_STOPPED,
|
3814
|
+
{mediaType: 'share'}
|
3815
|
+
);
|
3816
|
+
assert.neverCalledWith(webex.internal.newMetrics.submitClientEvent, {
|
3817
|
+
name: 'client.media.render.stop',
|
3818
|
+
payload: {mediaType: 'share', shareInstanceId: meeting.remoteShareInstanceId},
|
3819
|
+
options: {
|
3820
|
+
meetingId: meeting.id,
|
3821
|
+
},
|
3822
|
+
});
|
3823
|
+
assert.neverCalledWith(webex.internal.newMetrics.submitClientEvent, {
|
3824
|
+
name: 'client.media.rx.stop',
|
3825
|
+
payload: {mediaType: 'share', shareInstanceId: meeting.remoteShareInstanceId},
|
3826
|
+
options: {
|
3827
|
+
meetingId: meeting.id,
|
3828
|
+
},
|
3829
|
+
});
|
3830
|
+
});
|
3831
|
+
|
3562
3832
|
it('counts the number of members that are in the meeting for MEDIA_QUALITY event', async () => {
|
3563
3833
|
let fakeMembersCollection = {
|
3564
3834
|
members: {
|
@@ -3568,7 +3838,7 @@ describe('plugin-meetings', () => {
|
|
3568
3838
|
},
|
3569
3839
|
};
|
3570
3840
|
sinon.stub(meeting, 'getMembers').returns({membersCollection: fakeMembersCollection});
|
3571
|
-
const fakeData = {intervalMetadata: {}
|
3841
|
+
const fakeData = {intervalMetadata: {}};
|
3572
3842
|
|
3573
3843
|
statsAnalyzerStub.emit(
|
3574
3844
|
{file: 'test', function: 'test'},
|
@@ -3609,7 +3879,7 @@ describe('plugin-meetings', () => {
|
|
3609
3879
|
});
|
3610
3880
|
|
3611
3881
|
it('calls submitMQE correctly', async () => {
|
3612
|
-
const fakeData = {intervalMetadata: {bla: 'bla'}
|
3882
|
+
const fakeData = {intervalMetadata: {bla: 'bla'}};
|
3613
3883
|
|
3614
3884
|
statsAnalyzerStub.emit(
|
3615
3885
|
{file: 'test', function: 'test'},
|
@@ -3640,7 +3910,7 @@ describe('plugin-meetings', () => {
|
|
3640
3910
|
|
3641
3911
|
meeting.roap.doTurnDiscovery = sinon.stub().resolves({
|
3642
3912
|
turnServerInfo: {
|
3643
|
-
|
3913
|
+
urls: [FAKE_TURN_URL],
|
3644
3914
|
username: FAKE_TURN_USER,
|
3645
3915
|
password: FAKE_TURN_PASSWORD,
|
3646
3916
|
},
|
@@ -3666,7 +3936,7 @@ describe('plugin-meetings', () => {
|
|
3666
3936
|
meeting.id,
|
3667
3937
|
sinon.match({
|
3668
3938
|
turnServerInfo: {
|
3669
|
-
|
3939
|
+
urls: [FAKE_TURN_URL],
|
3670
3940
|
username: FAKE_TURN_USER,
|
3671
3941
|
password: FAKE_TURN_PASSWORD,
|
3672
3942
|
},
|
@@ -3817,6 +4087,9 @@ describe('plugin-meetings', () => {
|
|
3817
4087
|
},
|
3818
4088
|
options: {
|
3819
4089
|
meetingId: meeting.id,
|
4090
|
+
rawError: {
|
4091
|
+
iceConnected: false,
|
4092
|
+
},
|
3820
4093
|
},
|
3821
4094
|
},
|
3822
4095
|
]);
|
@@ -3887,9 +4160,29 @@ describe('plugin-meetings', () => {
|
|
3887
4160
|
} catch (err) {
|
3888
4161
|
assert.instanceOf(err, Error);
|
3889
4162
|
assert.equal(err.message, 'setBrb failed');
|
3890
|
-
assert.isRejected(
|
4163
|
+
assert.isRejected(Promise.reject());
|
3891
4164
|
}
|
3892
4165
|
});
|
4166
|
+
|
4167
|
+
it('updates remote mute state when brb is enabled', async () => {
|
4168
|
+
meeting.audio = {handleServerRemoteMuteUpdate: sinon.stub()};
|
4169
|
+
|
4170
|
+
await meeting.beRightBack(true);
|
4171
|
+
|
4172
|
+
sinon.assert.calledOnceWithExactly(
|
4173
|
+
meeting.audio.handleServerRemoteMuteUpdate,
|
4174
|
+
meeting,
|
4175
|
+
true
|
4176
|
+
);
|
4177
|
+
});
|
4178
|
+
|
4179
|
+
it('does not update remote mute state when brb is disabled', async () => {
|
4180
|
+
meeting.audio = {handleServerRemoteMuteUpdate: sinon.stub()};
|
4181
|
+
|
4182
|
+
await meeting.beRightBack(false);
|
4183
|
+
|
4184
|
+
assert.notCalled(meeting.audio.handleServerRemoteMuteUpdate);
|
4185
|
+
});
|
3893
4186
|
});
|
3894
4187
|
});
|
3895
4188
|
|
@@ -3943,7 +4236,10 @@ describe('plugin-meetings', () => {
|
|
3943
4236
|
.resolves({id: 'fake clientMediaPreferences'});
|
3944
4237
|
meeting.roap.doTurnDiscovery = sinon.stub().resolves({
|
3945
4238
|
turnServerInfo: {
|
3946
|
-
|
4239
|
+
urls: [
|
4240
|
+
'turns:turn-server-url1:443?transport=tcp',
|
4241
|
+
'turns:turn-server-url2:443?transport=tcp',
|
4242
|
+
],
|
3947
4243
|
username: 'turn user',
|
3948
4244
|
password: 'turn password',
|
3949
4245
|
},
|
@@ -3961,12 +4257,10 @@ describe('plugin-meetings', () => {
|
|
3961
4257
|
expectedMediaConnectionConfig = {
|
3962
4258
|
iceServers: [
|
3963
4259
|
{
|
3964
|
-
urls:
|
3965
|
-
|
3966
|
-
|
3967
|
-
|
3968
|
-
{
|
3969
|
-
urls: 'turns:turn-server-url:443?transport=tcp',
|
4260
|
+
urls: [
|
4261
|
+
'turns:turn-server-url1:443?transport=tcp',
|
4262
|
+
'turns:turn-server-url2:443?transport=tcp',
|
4263
|
+
],
|
3970
4264
|
username: 'turn user',
|
3971
4265
|
credential: 'turn password',
|
3972
4266
|
},
|
@@ -4048,9 +4342,11 @@ describe('plugin-meetings', () => {
|
|
4048
4342
|
.stub(InternalMediaCoreModule, 'MultistreamRoapMediaConnection')
|
4049
4343
|
.returns(fakeMultistreamRoapMediaConnection);
|
4050
4344
|
|
4051
|
-
locusMediaRequestStub = sinon
|
4052
|
-
|
4053
|
-
|
4345
|
+
locusMediaRequestStub = sinon.stub(WebexPlugin.prototype, 'request').resolves({
|
4346
|
+
body: {locus: {fullState: {}}},
|
4347
|
+
upload: sinon.match.instanceOf(EventEmitter),
|
4348
|
+
download: sinon.match.instanceOf(EventEmitter),
|
4349
|
+
});
|
4054
4350
|
|
4055
4351
|
// setup some things and mocks so that the call to join() works
|
4056
4352
|
// (we need to call join() because it creates the LocusMediaRequest instance
|
@@ -5234,7 +5530,10 @@ describe('plugin-meetings', () => {
|
|
5234
5530
|
// and check that when we fallback to transcoded we still do another TURN discovery
|
5235
5531
|
await runCheck(
|
5236
5532
|
{
|
5237
|
-
|
5533
|
+
urls: [
|
5534
|
+
'turns:turn-server-url1:443?transport=tcp',
|
5535
|
+
'turns:turn-server-url2:443?transport=tcp',
|
5536
|
+
],
|
5238
5537
|
username: 'turn user',
|
5239
5538
|
password: 'turn password',
|
5240
5539
|
},
|
@@ -5248,7 +5547,10 @@ describe('plugin-meetings', () => {
|
|
5248
5547
|
// but doing it just for completeness
|
5249
5548
|
await runCheck(
|
5250
5549
|
{
|
5251
|
-
|
5550
|
+
urls: [
|
5551
|
+
'turns:turn-server-url1:443?transport=tcp',
|
5552
|
+
'turns:turn-server-url2:443?transport=tcp',
|
5553
|
+
],
|
5252
5554
|
username: 'turn user',
|
5253
5555
|
password: 'turn password',
|
5254
5556
|
},
|
@@ -7530,6 +7832,27 @@ describe('plugin-meetings', () => {
|
|
7530
7832
|
});
|
7531
7833
|
});
|
7532
7834
|
|
7835
|
+
describe('#setIsoLocalClientMeetingJoinTime', () => {
|
7836
|
+
it('should fallback to system clock ISO string when given an undefined value', () => {
|
7837
|
+
const currentSystemTime = new Date().toISOString();
|
7838
|
+
meeting.isoLocalClientMeetingJoinTime = undefined;
|
7839
|
+
assert.equal(meeting.isoLocalClientMeetingJoinTime, currentSystemTime);
|
7840
|
+
});
|
7841
|
+
|
7842
|
+
it('should fallback to system clock ISO string when given an invalid value', () => {
|
7843
|
+
const currentSystemTime = new Date().toISOString();
|
7844
|
+
meeting.isoLocalClientMeetingJoinTime = 'invalid-date';
|
7845
|
+
assert.equal(meeting.isoLocalClientMeetingJoinTime, currentSystemTime);
|
7846
|
+
});
|
7847
|
+
|
7848
|
+
it('should set the isoLocalClientMeetingJoinTime correctly for a valid date string', () => {
|
7849
|
+
const validDateString = 'Tue, 01 Apr 2025 13:00:36 GMT';
|
7850
|
+
const expectedISOString = new Date(validDateString).toISOString();
|
7851
|
+
meeting.isoLocalClientMeetingJoinTime = validDateString;
|
7852
|
+
assert.equal(meeting.isoLocalClientMeetingJoinTime, expectedISOString);
|
7853
|
+
});
|
7854
|
+
});
|
7855
|
+
|
7533
7856
|
describe('#updateCallStateForMetrics', () => {
|
7534
7857
|
it('should update the callState, overriding existing values', () => {
|
7535
7858
|
assert.deepEqual(meeting.callStateForMetrics, {correlationId, sessionCorrelationId: ''});
|
@@ -7611,6 +7934,12 @@ describe('plugin-meetings', () => {
|
|
7611
7934
|
meeting.audio = {handleLocalStreamChange: sinon.stub()};
|
7612
7935
|
meeting.video = {handleLocalStreamChange: sinon.stub()};
|
7613
7936
|
meeting.statsAnalyzer = {updateMediaStatus: sinon.stub()};
|
7937
|
+
meeting.shareCAEventSentStatus = {
|
7938
|
+
transmitStart: false,
|
7939
|
+
transmitStop: false,
|
7940
|
+
receiveStart: false,
|
7941
|
+
receiveStop: false,
|
7942
|
+
};
|
7614
7943
|
fakeMultistreamRoapMediaConnection = {
|
7615
7944
|
createSendSlot: () => {
|
7616
7945
|
return {
|
@@ -7678,6 +8007,9 @@ describe('plugin-meetings', () => {
|
|
7678
8007
|
});
|
7679
8008
|
assert.equal(meeting.mediaProperties.mediaDirection.sendShare, true);
|
7680
8009
|
|
8010
|
+
assert.equal(meeting.shareCAEventSentStatus.transmitStart, false);
|
8011
|
+
assert.equal(meeting.shareCAEventSentStatus.transmitStop, false);
|
8012
|
+
|
7681
8013
|
assert.calledWith(meeting.statsAnalyzer.updateMediaStatus, {
|
7682
8014
|
expected: {sendShare: true},
|
7683
8015
|
});
|
@@ -7698,18 +8030,23 @@ describe('plugin-meetings', () => {
|
|
7698
8030
|
assert.equal(meeting.mediaProperties.shareAudioStream, stream);
|
7699
8031
|
assert.equal(meeting.mediaProperties.mediaDirection.sendShare, true);
|
7700
8032
|
|
8033
|
+
assert.equal(meeting.shareCAEventSentStatus.transmitStart, false);
|
8034
|
+
assert.equal(meeting.shareCAEventSentStatus.transmitStop, false);
|
8035
|
+
|
7701
8036
|
assert.calledWith(meeting.statsAnalyzer.updateMediaStatus, {
|
7702
8037
|
expected: {sendShare: true},
|
7703
8038
|
});
|
7704
8039
|
};
|
7705
8040
|
|
7706
8041
|
it('requests screen share floor and publishes the screen share video stream', async () => {
|
8042
|
+
meeting.shareCAEventSentStatus.transmitStart = true;
|
7707
8043
|
await meeting.publishStreams({screenShare: {video: videoShareStream}});
|
7708
8044
|
|
7709
8045
|
checkScreenShareVideoPublished(videoShareStream);
|
7710
8046
|
});
|
7711
8047
|
|
7712
8048
|
it('requests screen share floor and publishes the screen share audio stream', async () => {
|
8049
|
+
meeting.shareCAEventSentStatus.transmitStart = true;
|
7713
8050
|
await meeting.publishStreams({screenShare: {audio: audioShareStream}});
|
7714
8051
|
|
7715
8052
|
checkScreenShareAudioPublished(audioShareStream);
|
@@ -8596,13 +8933,19 @@ describe('plugin-meetings', () => {
|
|
8596
8933
|
const fakeErrorMessage = 'test error';
|
8597
8934
|
const fakeRootCauseName = 'root cause name';
|
8598
8935
|
const fakeErrorName = 'test error name';
|
8936
|
+
let clock;
|
8599
8937
|
|
8600
8938
|
beforeEach(() => {
|
8939
|
+
clock = sinon.useFakeTimers();
|
8601
8940
|
meeting.setupMediaConnectionListeners();
|
8602
8941
|
webex.internal.newMetrics.submitClientEvent.resetHistory();
|
8603
8942
|
Metrics.sendBehavioralMetric.resetHistory();
|
8604
8943
|
});
|
8605
8944
|
|
8945
|
+
afterEach(() => {
|
8946
|
+
clock.restore();
|
8947
|
+
});
|
8948
|
+
|
8606
8949
|
const checkMetricSent = (event, error) => {
|
8607
8950
|
assert.calledOnce(webex.internal.newMetrics.submitClientEvent);
|
8608
8951
|
assert.calledWithMatch(webex.internal.newMetrics.submitClientEvent, {
|
@@ -8671,6 +9014,13 @@ describe('plugin-meetings', () => {
|
|
8671
9014
|
});
|
8672
9015
|
|
8673
9016
|
it('should send metrics for SdpAnswerHandlingError error', () => {
|
9017
|
+
meeting.sdpResponseTimer = '1234';
|
9018
|
+
meeting.deferSDPAnswer = {
|
9019
|
+
reject: sinon.stub(),
|
9020
|
+
};
|
9021
|
+
|
9022
|
+
const clearTimeoutSpy = sinon.spy(clock, 'clearTimeout');
|
9023
|
+
|
8674
9024
|
const fakeError = new Errors.SdpAnswerHandlingError(fakeErrorMessage, {
|
8675
9025
|
name: fakeErrorName,
|
8676
9026
|
cause: {name: fakeRootCauseName},
|
@@ -8685,6 +9035,8 @@ describe('plugin-meetings', () => {
|
|
8685
9035
|
fakeErrorMessage,
|
8686
9036
|
fakeRootCauseName
|
8687
9037
|
);
|
9038
|
+
assert.calledOnce(meeting.deferSDPAnswer.reject);
|
9039
|
+
assert.calledOnce(clearTimeoutSpy);
|
8688
9040
|
});
|
8689
9041
|
|
8690
9042
|
it('should send metrics for SdpError error', () => {
|
@@ -9590,6 +9942,42 @@ describe('plugin-meetings', () => {
|
|
9590
9942
|
);
|
9591
9943
|
});
|
9592
9944
|
|
9945
|
+
it('listens to CONTROLS_ANNOTATION_CHANGED', async () => {
|
9946
|
+
const state = {example: 'value'};
|
9947
|
+
|
9948
|
+
await meeting.locusInfo.emitScoped(
|
9949
|
+
{function: 'test', file: 'test'},
|
9950
|
+
LOCUSINFO.EVENTS.CONTROLS_ANNOTATION_CHANGED,
|
9951
|
+
{state}
|
9952
|
+
);
|
9953
|
+
|
9954
|
+
assert.calledWith(
|
9955
|
+
TriggerProxy.trigger,
|
9956
|
+
meeting,
|
9957
|
+
{file: 'meeting/index', function: 'setupLocusControlsListener'},
|
9958
|
+
EVENT_TRIGGERS.MEETING_CONTROLS_ANNOTATION_UPDATED,
|
9959
|
+
{state}
|
9960
|
+
);
|
9961
|
+
});
|
9962
|
+
|
9963
|
+
it('listens to CONTROLS_REMOTE_DESKTOP_CONTROL_CHANGED', async () => {
|
9964
|
+
const state = {example: 'value'};
|
9965
|
+
|
9966
|
+
await meeting.locusInfo.emitScoped(
|
9967
|
+
{function: 'test', file: 'test'},
|
9968
|
+
LOCUSINFO.EVENTS.CONTROLS_REMOTE_DESKTOP_CONTROL_CHANGED,
|
9969
|
+
{state}
|
9970
|
+
);
|
9971
|
+
|
9972
|
+
assert.calledWith(
|
9973
|
+
TriggerProxy.trigger,
|
9974
|
+
meeting,
|
9975
|
+
{file: 'meeting/index', function: 'setupLocusControlsListener'},
|
9976
|
+
EVENT_TRIGGERS.MEETING_CONTROLS_REMOTE_DESKTOP_CONTROL_UPDATED,
|
9977
|
+
{state}
|
9978
|
+
);
|
9979
|
+
});
|
9980
|
+
|
9593
9981
|
it('listens to the locus interpretation update event', () => {
|
9594
9982
|
const interpretation = {
|
9595
9983
|
siLanguages: [{languageCode: 20, languageName: 'en'}],
|
@@ -10485,9 +10873,11 @@ describe('plugin-meetings', () => {
|
|
10485
10873
|
let canUserLowerSomeoneElsesHandSpy;
|
10486
10874
|
let waitingForOthersToJoinSpy;
|
10487
10875
|
let canSendReactionsSpy;
|
10876
|
+
let requiresPostMeetingDataConsentPromptSpy;
|
10488
10877
|
let canUserRenameSelfAndObservedSpy;
|
10489
10878
|
let canUserRenameOthersSpy;
|
10490
10879
|
let canShareWhiteBoardSpy;
|
10880
|
+
let canMoveToLobbySpy;
|
10491
10881
|
// Due to import tree issues, hasHints must be stubed within the scope of the `it`.
|
10492
10882
|
|
10493
10883
|
beforeEach(() => {
|
@@ -10512,8 +10902,13 @@ describe('plugin-meetings', () => {
|
|
10512
10902
|
waitingForOthersToJoinSpy = sinon.spy(MeetingUtil, 'waitingForOthersToJoin');
|
10513
10903
|
canSendReactionsSpy = sinon.spy(MeetingUtil, 'canSendReactions');
|
10514
10904
|
canUserRenameSelfAndObservedSpy = sinon.spy(MeetingUtil, 'canUserRenameSelfAndObserved');
|
10905
|
+
requiresPostMeetingDataConsentPromptSpy = sinon.spy(
|
10906
|
+
MeetingUtil,
|
10907
|
+
'requiresPostMeetingDataConsentPrompt'
|
10908
|
+
);
|
10515
10909
|
canUserRenameOthersSpy = sinon.spy(MeetingUtil, 'canUserRenameOthers');
|
10516
10910
|
canShareWhiteBoardSpy = sinon.spy(MeetingUtil, 'canShareWhiteBoard');
|
10911
|
+
canMoveToLobbySpy = sinon.spy(MeetingUtil, 'canMoveToLobby');
|
10517
10912
|
});
|
10518
10913
|
|
10519
10914
|
afterEach(() => {
|
@@ -10611,6 +11006,16 @@ describe('plugin-meetings', () => {
|
|
10611
11006
|
requiredDisplayHints: [],
|
10612
11007
|
requiredPolicies: [SELF_POLICY.SUPPORT_FILE_TRANSFER],
|
10613
11008
|
},
|
11009
|
+
{
|
11010
|
+
actionName: 'canRealtimeCloseCaption',
|
11011
|
+
requiredDisplayHints: [],
|
11012
|
+
requiredPolicies: [SELF_POLICY.SUPPORT_REALTIME_CLOSE_CAPTION],
|
11013
|
+
},
|
11014
|
+
{
|
11015
|
+
actionName: 'canRealtimeCloseCaptionManual',
|
11016
|
+
requiredDisplayHints: [],
|
11017
|
+
requiredPolicies: [SELF_POLICY.SUPPORT_REALTIME_CLOSE_CAPTION_MANUAL],
|
11018
|
+
},
|
10614
11019
|
{
|
10615
11020
|
actionName: 'canChat',
|
10616
11021
|
requiredDisplayHints: [],
|
@@ -10640,6 +11045,11 @@ describe('plugin-meetings', () => {
|
|
10640
11045
|
requiredDisplayHints: [],
|
10641
11046
|
requiredPolicies: [SELF_POLICY.SUPPORT_POLLING_AND_QA],
|
10642
11047
|
},
|
11048
|
+
{
|
11049
|
+
actionName: 'canShareWhiteBoard',
|
11050
|
+
requiredDisplayHints: [DISPLAY_HINTS.SHARE_WHITEBOARD],
|
11051
|
+
requiredPolicies: [SELF_POLICY.SUPPORT_WHITEBOARD],
|
11052
|
+
},
|
10643
11053
|
],
|
10644
11054
|
({
|
10645
11055
|
actionName,
|
@@ -11047,8 +11457,10 @@ describe('plugin-meetings', () => {
|
|
11047
11457
|
assert.calledWith(waitingForOthersToJoinSpy, userDisplayHints);
|
11048
11458
|
assert.calledWith(canSendReactionsSpy, null, userDisplayHints);
|
11049
11459
|
assert.calledWith(canUserRenameSelfAndObservedSpy, userDisplayHints);
|
11460
|
+
assert.calledWith(requiresPostMeetingDataConsentPromptSpy, userDisplayHints);
|
11050
11461
|
assert.calledWith(canUserRenameOthersSpy, userDisplayHints);
|
11051
|
-
assert.calledWith(canShareWhiteBoardSpy, userDisplayHints);
|
11462
|
+
assert.calledWith(canShareWhiteBoardSpy, userDisplayHints, selfUserPolicies);
|
11463
|
+
assert.calledWith(canMoveToLobbySpy, userDisplayHints);
|
11052
11464
|
|
11053
11465
|
assert.calledWith(ControlsOptionsUtil.hasHints, {
|
11054
11466
|
requiredHints: [DISPLAY_HINTS.MUTE_ALL],
|
@@ -11142,6 +11554,22 @@ describe('plugin-meetings', () => {
|
|
11142
11554
|
requiredPolicies: [SELF_POLICY.SUPPORT_VOIP],
|
11143
11555
|
policies: selfUserPolicies,
|
11144
11556
|
});
|
11557
|
+
assert.calledWith(ControlsOptionsUtil.hasHints, {
|
11558
|
+
requiredHints: [DISPLAY_HINTS.ENABLE_ANNOTATION_MEETING_OPTION],
|
11559
|
+
displayHints: userDisplayHints,
|
11560
|
+
});
|
11561
|
+
assert.calledWith(ControlsOptionsUtil.hasHints, {
|
11562
|
+
requiredHints: [DISPLAY_HINTS.DISABLE_ANNOTATION_MEETING_OPTION],
|
11563
|
+
displayHints: userDisplayHints,
|
11564
|
+
});
|
11565
|
+
assert.calledWith(ControlsOptionsUtil.hasHints, {
|
11566
|
+
requiredHints: [DISPLAY_HINTS.ENABLE_RDC_MEETING_OPTION],
|
11567
|
+
displayHints: userDisplayHints,
|
11568
|
+
});
|
11569
|
+
assert.calledWith(ControlsOptionsUtil.hasHints, {
|
11570
|
+
requiredHints: [DISPLAY_HINTS.DISABLE_RDC_MEETING_OPTION],
|
11571
|
+
displayHints: userDisplayHints,
|
11572
|
+
});
|
11145
11573
|
|
11146
11574
|
assert.calledWith(
|
11147
11575
|
TriggerProxy.trigger,
|
@@ -11988,6 +12416,8 @@ describe('plugin-meetings', () => {
|
|
11988
12416
|
// Set the webinar attendee flag
|
11989
12417
|
meeting.webinar = {selfIsAttendee: true};
|
11990
12418
|
meeting.locusInfo.info.isWebinar = true;
|
12419
|
+
meeting.shareCAEventSentStatus.receiveStart = true;
|
12420
|
+
meeting.shareCAEventSentStatus.receiveStop = true;
|
11991
12421
|
|
11992
12422
|
// Step 1: Start sharing whiteboard A
|
11993
12423
|
const data1 = generateData(
|
@@ -12011,6 +12441,8 @@ describe('plugin-meetings', () => {
|
|
12011
12441
|
|
12012
12442
|
// Specific assertions for webinar attendee status
|
12013
12443
|
assert.equal(meeting.shareStatus, SHARE_STATUS.REMOTE_SHARE_ACTIVE);
|
12444
|
+
assert.equal(meeting.shareCAEventSentStatus.receiveStart, false);
|
12445
|
+
assert.equal(meeting.shareCAEventSentStatus.receiveStop, false);
|
12014
12446
|
});
|
12015
12447
|
});
|
12016
12448
|
|
@@ -12666,6 +13098,31 @@ describe('plugin-meetings', () => {
|
|
12666
13098
|
});
|
12667
13099
|
});
|
12668
13100
|
});
|
13101
|
+
|
13102
|
+
describe('handleShareVideoStreamMuteStateChange', () => {
|
13103
|
+
it('should emit MEETING_SHARE_VIDEO_MUTE_STATE_CHANGE event with correct fields', () => {
|
13104
|
+
meeting.isMultistream = true;
|
13105
|
+
meeting.statsAnalyzer = {shareVideoEncoderImplementation: 'OpenH264'};
|
13106
|
+
meeting.mediaProperties.shareVideoStream = {
|
13107
|
+
getSettings: sinon.stub().returns({displaySurface: 'monitor', frameRate: 30}),
|
13108
|
+
};
|
13109
|
+
|
13110
|
+
meeting.handleShareVideoStreamMuteStateChange(true);
|
13111
|
+
|
13112
|
+
assert.calledOnceWithExactly(
|
13113
|
+
Metrics.sendBehavioralMetric,
|
13114
|
+
BEHAVIORAL_METRICS.MEETING_SHARE_VIDEO_MUTE_STATE_CHANGE,
|
13115
|
+
{
|
13116
|
+
correlationId: meeting.correlationId,
|
13117
|
+
muted: true,
|
13118
|
+
encoderImplementation: 'OpenH264',
|
13119
|
+
displaySurface: 'monitor',
|
13120
|
+
isMultistream: true,
|
13121
|
+
frameRate: 30,
|
13122
|
+
}
|
13123
|
+
);
|
13124
|
+
});
|
13125
|
+
});
|
12669
13126
|
});
|
12670
13127
|
|
12671
13128
|
describe('#startKeepAlive', () => {
|
@@ -12833,6 +13290,38 @@ describe('plugin-meetings', () => {
|
|
12833
13290
|
});
|
12834
13291
|
});
|
12835
13292
|
|
13293
|
+
describe('#setPostMeetingDataConsent', () => {
|
13294
|
+
it('should have #setPostMeetingDataConsent', () => {
|
13295
|
+
assert.exists(meeting.setPostMeetingDataConsent);
|
13296
|
+
});
|
13297
|
+
|
13298
|
+
beforeEach(() => {
|
13299
|
+
meeting.meetingRequest.setPostMeetingDataConsent = sinon
|
13300
|
+
.stub()
|
13301
|
+
.returns(Promise.resolve());
|
13302
|
+
});
|
13303
|
+
|
13304
|
+
[true, false].forEach((accept) => {
|
13305
|
+
it(`should send consent with ${accept}`, async () => {
|
13306
|
+
const id = uuidv4();
|
13307
|
+
meeting.locusUrl = `https://locus-test.wbx2.com/locus/api/v1/loci/${accept}`;
|
13308
|
+
meeting.deviceUrl = `https://wdm-test.wbx2.com/wdm/api/v1/devices/${accept}`;
|
13309
|
+
meeting.members.selfId = id;
|
13310
|
+
|
13311
|
+
const consentPromise = meeting.setPostMeetingDataConsent(accept);
|
13312
|
+
|
13313
|
+
assert.exists(consentPromise.then);
|
13314
|
+
await consentPromise;
|
13315
|
+
assert.calledOnceWithExactly(meeting.meetingRequest.setPostMeetingDataConsent, {
|
13316
|
+
locusUrl: `https://locus-test.wbx2.com/locus/api/v1/loci/${accept}`,
|
13317
|
+
postMeetingDataConsent: accept,
|
13318
|
+
selfId: id,
|
13319
|
+
deviceUrl: `https://wdm-test.wbx2.com/wdm/api/v1/devices/${accept}`,
|
13320
|
+
});
|
13321
|
+
});
|
13322
|
+
});
|
13323
|
+
});
|
13324
|
+
|
12836
13325
|
describe('#sendReaction', () => {
|
12837
13326
|
it('should have #sendReaction', () => {
|
12838
13327
|
assert.exists(meeting.sendReaction);
|