@webex/plugin-meetings 2.60.0-next.1 → 2.60.0-next.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/breakouts/breakout.js +1 -1
- package/dist/breakouts/index.js +1 -1
- package/dist/constants.js +1 -1
- package/dist/constants.js.map +1 -1
- package/dist/controls-options-manager/enums.js +2 -1
- package/dist/controls-options-manager/enums.js.map +1 -1
- package/dist/interpretation/index.js +1 -1
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/locus-info/parser.js +5 -5
- package/dist/locus-info/parser.js.map +1 -1
- package/dist/media/index.js +6 -5
- package/dist/media/index.js.map +1 -1
- package/dist/meeting/in-meeting-actions.js +4 -0
- package/dist/meeting/in-meeting-actions.js.map +1 -1
- package/dist/meeting/index.js +276 -150
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting-info/meeting-info-v2.js +3 -0
- package/dist/meeting-info/meeting-info-v2.js.map +1 -1
- package/dist/meeting-info/utilv2.js +14 -29
- package/dist/meeting-info/utilv2.js.map +1 -1
- package/dist/meetings/collection.js +17 -0
- package/dist/meetings/collection.js.map +1 -1
- package/dist/meetings/index.js +30 -9
- package/dist/meetings/index.js.map +1 -1
- package/dist/metrics/constants.js +3 -0
- package/dist/metrics/constants.js.map +1 -1
- package/dist/reconnection-manager/index.js +27 -28
- package/dist/reconnection-manager/index.js.map +1 -1
- package/dist/rtcMetrics/index.js +25 -0
- package/dist/rtcMetrics/index.js.map +1 -1
- package/dist/statsAnalyzer/index.js +21 -1
- package/dist/statsAnalyzer/index.js.map +1 -1
- package/dist/statsAnalyzer/mqaUtil.js +16 -16
- package/dist/statsAnalyzer/mqaUtil.js.map +1 -1
- package/dist/webinar/index.js +1 -1
- package/package.json +21 -22
- package/src/constants.ts +10 -4
- package/src/controls-options-manager/enums.ts +2 -0
- package/src/locus-info/parser.ts +6 -6
- package/src/media/index.ts +5 -5
- package/src/meeting/in-meeting-actions.ts +8 -0
- package/src/meeting/index.ts +249 -120
- package/src/meeting-info/meeting-info-v2.ts +4 -0
- package/src/meeting-info/utilv2.ts +6 -19
- package/src/meetings/collection.ts +13 -0
- package/src/meetings/index.ts +28 -10
- package/src/metrics/constants.ts +3 -0
- package/src/reconnection-manager/index.ts +63 -68
- package/src/rtcMetrics/index.ts +24 -0
- package/src/statsAnalyzer/index.ts +30 -1
- package/src/statsAnalyzer/mqaUtil.ts +17 -14
- package/test/unit/spec/media/index.ts +20 -4
- package/test/unit/spec/meeting/in-meeting-actions.ts +4 -0
- package/test/unit/spec/meeting/index.js +1253 -157
- package/test/unit/spec/meeting/muteState.js +2 -1
- package/test/unit/spec/meeting-info/meetinginfov2.js +28 -0
- package/test/unit/spec/meetings/collection.js +12 -0
- package/test/unit/spec/meetings/index.js +382 -118
- package/test/unit/spec/member/util.js +0 -31
- package/test/unit/spec/reconnection-manager/index.js +42 -12
- package/test/unit/spec/rtcMetrics/index.ts +20 -0
- package/test/unit/spec/stats-analyzer/index.js +12 -2
|
@@ -33,6 +33,7 @@ import {
|
|
|
33
33
|
NETWORK_STATUS,
|
|
34
34
|
ONLINE,
|
|
35
35
|
OFFLINE,
|
|
36
|
+
RECONNECTION,
|
|
36
37
|
} from '@webex/plugin-meetings/src/constants';
|
|
37
38
|
import * as InternalMediaCoreModule from '@webex/internal-media-core';
|
|
38
39
|
import {
|
|
@@ -307,6 +308,7 @@ describe('plugin-meetings', () => {
|
|
|
307
308
|
assert.equal(meeting.resource, uuid2);
|
|
308
309
|
assert.equal(meeting.deviceUrl, uuid3);
|
|
309
310
|
assert.equal(meeting.correlationId, correlationId);
|
|
311
|
+
assert.deepEqual(meeting.callStateForMetrics, {correlationId});
|
|
310
312
|
assert.deepEqual(meeting.meetingInfo, {});
|
|
311
313
|
assert.instanceOf(meeting.members, Members);
|
|
312
314
|
assert.calledOnceWithExactly(
|
|
@@ -374,6 +376,34 @@ describe('plugin-meetings', () => {
|
|
|
374
376
|
}
|
|
375
377
|
);
|
|
376
378
|
assert.equal(newMeeting.correlationId, newMeeting.id);
|
|
379
|
+
assert.deepEqual(newMeeting.callStateForMetrics, {correlationId: newMeeting.id});
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
it('correlationId can be provided in callStateForMetrics', () => {
|
|
383
|
+
const newMeeting = new Meeting(
|
|
384
|
+
{
|
|
385
|
+
userId: uuid1,
|
|
386
|
+
resource: uuid2,
|
|
387
|
+
deviceUrl: uuid3,
|
|
388
|
+
locus: {url: url1},
|
|
389
|
+
destination: testDestination,
|
|
390
|
+
destinationType: _MEETING_ID_,
|
|
391
|
+
callStateForMetrics: {
|
|
392
|
+
correlationId: uuid4,
|
|
393
|
+
joinTrigger: 'fake-join-trigger',
|
|
394
|
+
loginType: 'fake-login-type',
|
|
395
|
+
}
|
|
396
|
+
},
|
|
397
|
+
{
|
|
398
|
+
parent: webex,
|
|
399
|
+
}
|
|
400
|
+
);
|
|
401
|
+
assert.equal(newMeeting.correlationId, uuid4);
|
|
402
|
+
assert.deepEqual(newMeeting.callStateForMetrics, {
|
|
403
|
+
correlationId: uuid4,
|
|
404
|
+
joinTrigger: 'fake-join-trigger',
|
|
405
|
+
loginType: 'fake-login-type',
|
|
406
|
+
});
|
|
377
407
|
});
|
|
378
408
|
|
|
379
409
|
describe('creates ReceiveSlot manager instance', () => {
|
|
@@ -827,11 +857,11 @@ describe('plugin-meetings', () => {
|
|
|
827
857
|
});
|
|
828
858
|
|
|
829
859
|
it('should join the meeting and return promise', async () => {
|
|
830
|
-
const join = meeting.join();
|
|
860
|
+
const join = meeting.join({pstnAudioType: 'dial-in'});
|
|
831
861
|
|
|
832
|
-
assert.
|
|
862
|
+
assert.calledWith(webex.internal.newMetrics.submitClientEvent, {
|
|
833
863
|
name: 'client.call.initiated',
|
|
834
|
-
payload: {trigger: 'user-interaction', isRoapCallEnabled: true},
|
|
864
|
+
payload: {trigger: 'user-interaction', isRoapCallEnabled: true, pstnAudioType: 'dial-in'},
|
|
835
865
|
options: {meetingId: meeting.id},
|
|
836
866
|
});
|
|
837
867
|
|
|
@@ -850,6 +880,17 @@ describe('plugin-meetings', () => {
|
|
|
850
880
|
assert.calledOnce(meeting.startTranscription);
|
|
851
881
|
});
|
|
852
882
|
|
|
883
|
+
it('should take trigger from meeting joinTrigger if available', () => {
|
|
884
|
+
meeting.updateCallStateForMetrics({joinTrigger: 'fake-join-trigger'});
|
|
885
|
+
const join = meeting.join();
|
|
886
|
+
|
|
887
|
+
assert.calledWithMatch(webex.internal.newMetrics.submitClientEvent, {
|
|
888
|
+
name: 'client.call.initiated',
|
|
889
|
+
payload: {trigger: 'fake-join-trigger', isRoapCallEnabled: true},
|
|
890
|
+
options: {meetingId: meeting.id},
|
|
891
|
+
});
|
|
892
|
+
});
|
|
893
|
+
|
|
853
894
|
it('should not create new correlation ID on join immediately after create', async () => {
|
|
854
895
|
await meeting.join();
|
|
855
896
|
sinon.assert.notCalled(setCorrelationIdSpy);
|
|
@@ -1172,8 +1213,8 @@ describe('plugin-meetings', () => {
|
|
|
1172
1213
|
assert.exists(meeting.addMedia);
|
|
1173
1214
|
});
|
|
1174
1215
|
|
|
1175
|
-
it('should reject promise if meeting is not active', async () => {
|
|
1176
|
-
const result = await assert.isRejected(meeting.addMedia());
|
|
1216
|
+
it('should reject promise if meeting is not active and the meeting in lobby is not enabled', async () => {
|
|
1217
|
+
const result = await assert.isRejected(meeting.addMedia({allowMediaInLobby: false}));
|
|
1177
1218
|
|
|
1178
1219
|
assert.instanceOf(result, MeetingNotActiveError);
|
|
1179
1220
|
});
|
|
@@ -1791,17 +1832,26 @@ describe('plugin-meetings', () => {
|
|
|
1791
1832
|
}]);
|
|
1792
1833
|
|
|
1793
1834
|
const sendBehavioralMetricCalls = Metrics.sendBehavioralMetric.getCalls();
|
|
1794
|
-
assert.equal(sendBehavioralMetricCalls.length,
|
|
1795
|
-
assert.deepEqual(sendBehavioralMetricCalls[0].args, [
|
|
1835
|
+
assert.equal(sendBehavioralMetricCalls.length, 3);
|
|
1836
|
+
assert.deepEqual(sendBehavioralMetricCalls[0].args, [
|
|
1837
|
+
BEHAVIORAL_METRICS.ADD_MEDIA_RETRY,
|
|
1838
|
+
{
|
|
1839
|
+
correlation_id: meeting.correlationId,
|
|
1840
|
+
state: meeting.state,
|
|
1841
|
+
meetingState: meeting.meetingState,
|
|
1842
|
+
reason: 'forcingTurnTls',
|
|
1843
|
+
},
|
|
1844
|
+
]);
|
|
1845
|
+
assert.deepEqual(sendBehavioralMetricCalls[1].args, [
|
|
1796
1846
|
BEHAVIORAL_METRICS.TURN_DISCOVERY_LATENCY,
|
|
1797
1847
|
{
|
|
1798
1848
|
correlation_id: meeting.correlationId,
|
|
1799
1849
|
turnServerUsed: true,
|
|
1800
1850
|
retriedWithTurnServer: true,
|
|
1801
1851
|
latency: undefined,
|
|
1802
|
-
}
|
|
1852
|
+
},
|
|
1803
1853
|
]);
|
|
1804
|
-
assert.deepEqual(sendBehavioralMetricCalls[
|
|
1854
|
+
assert.deepEqual(sendBehavioralMetricCalls[2].args, [
|
|
1805
1855
|
BEHAVIORAL_METRICS.ADD_MEDIA_FAILURE,
|
|
1806
1856
|
{
|
|
1807
1857
|
correlation_id: meeting.correlationId,
|
|
@@ -1816,7 +1866,7 @@ describe('plugin-meetings', () => {
|
|
|
1816
1866
|
signalingState: 'unknown',
|
|
1817
1867
|
connectionState: 'unknown',
|
|
1818
1868
|
iceConnectionState: 'unknown',
|
|
1819
|
-
}
|
|
1869
|
+
},
|
|
1820
1870
|
]);
|
|
1821
1871
|
|
|
1822
1872
|
// Check that doTurnDiscovery is called with th4 correct value of isForced
|
|
@@ -1957,17 +2007,26 @@ describe('plugin-meetings', () => {
|
|
|
1957
2007
|
}]);
|
|
1958
2008
|
|
|
1959
2009
|
const sendBehavioralMetricCalls = Metrics.sendBehavioralMetric.getCalls();
|
|
1960
|
-
assert.equal(sendBehavioralMetricCalls.length,
|
|
1961
|
-
assert.deepEqual(sendBehavioralMetricCalls[0].args, [
|
|
2010
|
+
assert.equal(sendBehavioralMetricCalls.length, 3);
|
|
2011
|
+
assert.deepEqual(sendBehavioralMetricCalls[0].args, [
|
|
2012
|
+
BEHAVIORAL_METRICS.ADD_MEDIA_RETRY,
|
|
2013
|
+
{
|
|
2014
|
+
correlation_id: meeting.correlationId,
|
|
2015
|
+
state: meeting.state,
|
|
2016
|
+
meetingState: meeting.meetingState,
|
|
2017
|
+
reason: 'forcingTurnTls',
|
|
2018
|
+
},
|
|
2019
|
+
]);
|
|
2020
|
+
assert.deepEqual(sendBehavioralMetricCalls[1].args, [
|
|
1962
2021
|
BEHAVIORAL_METRICS.TURN_DISCOVERY_LATENCY,
|
|
1963
2022
|
{
|
|
1964
2023
|
correlation_id: meeting.correlationId,
|
|
1965
2024
|
turnServerUsed: true,
|
|
1966
2025
|
retriedWithTurnServer: true,
|
|
1967
2026
|
latency: undefined,
|
|
1968
|
-
}
|
|
2027
|
+
},
|
|
1969
2028
|
]);
|
|
1970
|
-
assert.deepEqual(sendBehavioralMetricCalls[
|
|
2029
|
+
assert.deepEqual(sendBehavioralMetricCalls[2].args, [
|
|
1971
2030
|
BEHAVIORAL_METRICS.ADD_MEDIA_SUCCESS,
|
|
1972
2031
|
{
|
|
1973
2032
|
correlation_id: meeting.correlationId,
|
|
@@ -1975,7 +2034,7 @@ describe('plugin-meetings', () => {
|
|
|
1975
2034
|
connectionType: 'udp',
|
|
1976
2035
|
isMultistream: false,
|
|
1977
2036
|
retriedWithTurnServer: true,
|
|
1978
|
-
}
|
|
2037
|
+
},
|
|
1979
2038
|
]);
|
|
1980
2039
|
meeting.roap.doTurnDiscovery
|
|
1981
2040
|
|
|
@@ -1999,6 +2058,90 @@ describe('plugin-meetings', () => {
|
|
|
1999
2058
|
assert.isNotOk(errorThrown);
|
|
2000
2059
|
});
|
|
2001
2060
|
|
|
2061
|
+
it('should call join if state is LEFT after first media connection attempt', async () => {
|
|
2062
|
+
const FAKE_TURN_URL = 'turns:webex.com:3478';
|
|
2063
|
+
const FAKE_TURN_USER = 'some-turn-username';
|
|
2064
|
+
const FAKE_TURN_PASSWORD = 'some-password';
|
|
2065
|
+
let errorThrown = undefined;
|
|
2066
|
+
|
|
2067
|
+
meeting.meetingState = 'ACTIVE';
|
|
2068
|
+
meeting.state = 'LEFT';
|
|
2069
|
+
meeting.roap.doTurnDiscovery = sinon.stub().onFirstCall().returns({
|
|
2070
|
+
turnServerInfo: undefined,
|
|
2071
|
+
turnDiscoverySkippedReason: 'reachability',
|
|
2072
|
+
}).onSecondCall().returns({
|
|
2073
|
+
turnServerInfo: {
|
|
2074
|
+
url: FAKE_TURN_URL,
|
|
2075
|
+
username: FAKE_TURN_USER,
|
|
2076
|
+
password: FAKE_TURN_PASSWORD,
|
|
2077
|
+
},
|
|
2078
|
+
turnDiscoverySkippedReason: undefined,
|
|
2079
|
+
});
|
|
2080
|
+
meeting.mediaProperties.waitForMediaConnectionConnected = sinon.stub().onFirstCall().rejects().onSecondCall().resolves();
|
|
2081
|
+
meeting.join = sinon.stub().resolves();
|
|
2082
|
+
|
|
2083
|
+
const closeMediaConnectionStub = sinon.stub();
|
|
2084
|
+
Media.createMediaConnection = sinon.stub().returns({
|
|
2085
|
+
close: closeMediaConnectionStub,
|
|
2086
|
+
getConnectionState: sinon.stub().returns(ConnectionState.Connected),
|
|
2087
|
+
initiateOffer: sinon.stub().resolves({}),
|
|
2088
|
+
on: sinon.stub(),
|
|
2089
|
+
});
|
|
2090
|
+
|
|
2091
|
+
await meeting
|
|
2092
|
+
.addMedia({
|
|
2093
|
+
mediaSettings: {},
|
|
2094
|
+
})
|
|
2095
|
+
.catch((err) => {
|
|
2096
|
+
errorThrown = err;
|
|
2097
|
+
});
|
|
2098
|
+
|
|
2099
|
+
assert.isNotOk(errorThrown);
|
|
2100
|
+
assert.calledOnceWithExactly(meeting.join, {rejoin: true});
|
|
2101
|
+
});
|
|
2102
|
+
|
|
2103
|
+
it('should reject if join attempt fails if state is LEFT after first media connection attempt', async () => {
|
|
2104
|
+
const FAKE_TURN_URL = 'turns:webex.com:3478';
|
|
2105
|
+
const FAKE_TURN_USER = 'some-turn-username';
|
|
2106
|
+
const FAKE_TURN_PASSWORD = 'some-password';
|
|
2107
|
+
let errorThrown = undefined;
|
|
2108
|
+
|
|
2109
|
+
meeting.meetingState = 'ACTIVE';
|
|
2110
|
+
meeting.state = 'LEFT';
|
|
2111
|
+
meeting.roap.doTurnDiscovery = sinon.stub().onFirstCall().returns({
|
|
2112
|
+
turnServerInfo: undefined,
|
|
2113
|
+
turnDiscoverySkippedReason: 'reachability',
|
|
2114
|
+
}).onSecondCall().returns({
|
|
2115
|
+
turnServerInfo: {
|
|
2116
|
+
url: FAKE_TURN_URL,
|
|
2117
|
+
username: FAKE_TURN_USER,
|
|
2118
|
+
password: FAKE_TURN_PASSWORD,
|
|
2119
|
+
},
|
|
2120
|
+
turnDiscoverySkippedReason: undefined,
|
|
2121
|
+
});
|
|
2122
|
+
meeting.mediaProperties.waitForMediaConnectionConnected = sinon.stub().onFirstCall().rejects().onSecondCall().resolves();
|
|
2123
|
+
meeting.join = sinon.stub().rejects();
|
|
2124
|
+
|
|
2125
|
+
const closeMediaConnectionStub = sinon.stub();
|
|
2126
|
+
Media.createMediaConnection = sinon.stub().returns({
|
|
2127
|
+
close: closeMediaConnectionStub,
|
|
2128
|
+
getConnectionState: sinon.stub().returns(ConnectionState.Connected),
|
|
2129
|
+
initiateOffer: sinon.stub().resolves({}),
|
|
2130
|
+
on: sinon.stub(),
|
|
2131
|
+
});
|
|
2132
|
+
|
|
2133
|
+
await meeting
|
|
2134
|
+
.addMedia({
|
|
2135
|
+
mediaSettings: {},
|
|
2136
|
+
})
|
|
2137
|
+
.catch((err) => {
|
|
2138
|
+
errorThrown = err;
|
|
2139
|
+
});
|
|
2140
|
+
|
|
2141
|
+
assert.isOk(errorThrown);
|
|
2142
|
+
assert.calledOnceWithExactly(meeting.join, {rejoin: true});
|
|
2143
|
+
});
|
|
2144
|
+
|
|
2002
2145
|
it('should send ADD_MEDIA_SUCCESS metrics', async () => {
|
|
2003
2146
|
meeting.meetingState = 'ACTIVE';
|
|
2004
2147
|
meeting.webex.meetings.reachability = {
|
|
@@ -2383,6 +2526,7 @@ describe('plugin-meetings', () => {
|
|
|
2383
2526
|
category: 'media',
|
|
2384
2527
|
errorCode: clientErrorCode,
|
|
2385
2528
|
serviceErrorCode: undefined,
|
|
2529
|
+
rawErrorMessage: undefined,
|
|
2386
2530
|
...expectedErrorPayload,
|
|
2387
2531
|
},
|
|
2388
2532
|
],
|
|
@@ -2479,8 +2623,12 @@ describe('plugin-meetings', () => {
|
|
|
2479
2623
|
setUnmuteAllowed: sinon.stub(),
|
|
2480
2624
|
setMuted: sinon.stub(),
|
|
2481
2625
|
setServerMuted: sinon.stub(),
|
|
2482
|
-
|
|
2483
|
-
|
|
2626
|
+
outputStream: {
|
|
2627
|
+
getTracks: () => {
|
|
2628
|
+
return [{
|
|
2629
|
+
id: 'fake mic'
|
|
2630
|
+
}];
|
|
2631
|
+
}
|
|
2484
2632
|
}
|
|
2485
2633
|
}
|
|
2486
2634
|
|
|
@@ -2691,10 +2839,10 @@ describe('plugin-meetings', () => {
|
|
|
2691
2839
|
assert.calledOnceWithExactly(roapMediaConnectionConstructorStub, mediaConnectionConfig,
|
|
2692
2840
|
{
|
|
2693
2841
|
localTracks: {
|
|
2694
|
-
audio: localStreams.audio?.
|
|
2695
|
-
video: localStreams.video?.
|
|
2696
|
-
screenShareVideo: localStreams.screenShareVideo?.
|
|
2697
|
-
screenShareAudio: localStreams.screenShareAudio?.
|
|
2842
|
+
audio: localStreams.audio?.outputStream?.getTracks()[0],
|
|
2843
|
+
video: localStreams.video?.outputStream?.getTracks()[0],
|
|
2844
|
+
screenShareVideo: localStreams.screenShareVideo?.outputStream?.getTracks()[0],
|
|
2845
|
+
screenShareAudio: localStreams.screenShareAudio?.outputStream?.getTracks()[0],
|
|
2698
2846
|
},
|
|
2699
2847
|
direction: {audio: direction.audio, video: direction.video, screenShareVideo: direction.screenShare},
|
|
2700
2848
|
remoteQualityLevel,
|
|
@@ -2969,7 +3117,7 @@ describe('plugin-meetings', () => {
|
|
|
2969
3117
|
assert.calledOnceWithExactly(meeting.sendSlotManager.getSlot(MediaType.AudioMain).publishStream, fakeMicrophoneStream);
|
|
2970
3118
|
} else {
|
|
2971
3119
|
assert.calledOnceWithExactly(fakeRoapMediaConnection.update, {
|
|
2972
|
-
localTracks: { audio: fakeMicrophoneStream.
|
|
3120
|
+
localTracks: { audio: fakeMicrophoneStream.outputStream.getTracks()[0], video: null, screenShareVideo: null, screenShareAudio: null },
|
|
2973
3121
|
direction: {
|
|
2974
3122
|
audio: expected.direction,
|
|
2975
3123
|
video: 'sendrecv',
|
|
@@ -2995,8 +3143,12 @@ describe('plugin-meetings', () => {
|
|
|
2995
3143
|
muted: false,
|
|
2996
3144
|
setUnmuteAllowed: sinon.stub(),
|
|
2997
3145
|
setMuted: sinon.stub(),
|
|
2998
|
-
|
|
2999
|
-
|
|
3146
|
+
outputStream: {
|
|
3147
|
+
getTracks: () => {
|
|
3148
|
+
return [{
|
|
3149
|
+
id: 'fake mic 2',
|
|
3150
|
+
}];
|
|
3151
|
+
}
|
|
3000
3152
|
}
|
|
3001
3153
|
}
|
|
3002
3154
|
|
|
@@ -3008,7 +3160,7 @@ describe('plugin-meetings', () => {
|
|
|
3008
3160
|
assert.calledOnceWithExactly(meeting.sendSlotManager.getSlot(MediaType.AudioMain).publishStream, fakeMicrophoneStream2);
|
|
3009
3161
|
} else {
|
|
3010
3162
|
assert.calledOnceWithExactly(fakeRoapMediaConnection.update, {
|
|
3011
|
-
localTracks: { audio: fakeMicrophoneStream2.
|
|
3163
|
+
localTracks: { audio: fakeMicrophoneStream2.outputStream.getTracks()[0], video: null, screenShareVideo: null, screenShareAudio: null },
|
|
3012
3164
|
direction: {
|
|
3013
3165
|
audio: expected.direction,
|
|
3014
3166
|
video: 'sendrecv',
|
|
@@ -3079,7 +3231,7 @@ describe('plugin-meetings', () => {
|
|
|
3079
3231
|
assert.equal(meeting.sendSlotManager.getSlot(MediaType.AudioMain).active, expectedDirection !== 'inactive');
|
|
3080
3232
|
} else {
|
|
3081
3233
|
assert.calledOnceWithExactly(fakeRoapMediaConnection.update, {
|
|
3082
|
-
localTracks: { audio: expectedStream?.
|
|
3234
|
+
localTracks: { audio: expectedStream?.outputStream.getTracks()[0] ?? null, video: null, screenShareVideo: null, screenShareAudio: null },
|
|
3083
3235
|
direction: {
|
|
3084
3236
|
audio: expectedDirection,
|
|
3085
3237
|
video: 'sendrecv',
|
|
@@ -3529,7 +3681,11 @@ describe('plugin-meetings', () => {
|
|
|
3529
3681
|
let sandbox;
|
|
3530
3682
|
|
|
3531
3683
|
const createFakeLocalStream = () => ({
|
|
3532
|
-
|
|
3684
|
+
outputStream: {
|
|
3685
|
+
getTracks: () => {
|
|
3686
|
+
return [{id: 'fake underlying track'}];
|
|
3687
|
+
}
|
|
3688
|
+
}
|
|
3533
3689
|
});
|
|
3534
3690
|
beforeEach(() => {
|
|
3535
3691
|
sandbox = sinon.createSandbox();
|
|
@@ -3612,10 +3768,10 @@ describe('plugin-meetings', () => {
|
|
|
3612
3768
|
meeting.mediaProperties.webrtcMediaConnection.update,
|
|
3613
3769
|
{
|
|
3614
3770
|
localTracks: {
|
|
3615
|
-
audio: meeting.mediaProperties.audioStream.
|
|
3616
|
-
video: meeting.mediaProperties.videoStream.
|
|
3617
|
-
screenShareVideo: meeting.mediaProperties.shareVideoStream.
|
|
3618
|
-
screenShareAudio: meeting.mediaProperties.shareVideoStream.
|
|
3771
|
+
audio: meeting.mediaProperties.audioStream.outputStream.getTracks()[0],
|
|
3772
|
+
video: meeting.mediaProperties.videoStream.outputStream.getTracks()[0],
|
|
3773
|
+
screenShareVideo: meeting.mediaProperties.shareVideoStream.outputStream.getTracks()[0],
|
|
3774
|
+
screenShareAudio: meeting.mediaProperties.shareVideoStream.outputStream.getTracks()[0],
|
|
3619
3775
|
},
|
|
3620
3776
|
direction: {
|
|
3621
3777
|
audio: 'inactive',
|
|
@@ -3644,7 +3800,13 @@ describe('plugin-meetings', () => {
|
|
|
3644
3800
|
meeting.mediaProperties.mediaDirection = mediaDirection;
|
|
3645
3801
|
meeting.mediaProperties.remoteVideoStream = sinon
|
|
3646
3802
|
.stub()
|
|
3647
|
-
.returns({
|
|
3803
|
+
.returns({
|
|
3804
|
+
outputStream: {
|
|
3805
|
+
getTracks: () => {
|
|
3806
|
+
id: 'some mock id'
|
|
3807
|
+
}
|
|
3808
|
+
}
|
|
3809
|
+
});
|
|
3648
3810
|
|
|
3649
3811
|
meeting.meetingRequest.changeVideoLayoutDebounced = sinon
|
|
3650
3812
|
.stub()
|
|
@@ -4469,7 +4631,7 @@ describe('plugin-meetings', () => {
|
|
|
4469
4631
|
};
|
|
4470
4632
|
const FAKE_MEETING_INFO_LOOKUP_URL = 'meetingLookupUrl';
|
|
4471
4633
|
const FAKE_PERMISSION_TOKEN = {someField: 'some value'};
|
|
4472
|
-
const
|
|
4634
|
+
const FAKE_TIMESTAMPS = {timeLeft: 13, expiryTime: 123456, currentTime: 123478};
|
|
4473
4635
|
|
|
4474
4636
|
beforeEach(() => {
|
|
4475
4637
|
meeting.locusId = 'locus-id';
|
|
@@ -4479,7 +4641,7 @@ describe('plugin-meetings', () => {
|
|
|
4479
4641
|
meeting.destination = 'meeting-destination';
|
|
4480
4642
|
meeting.destinationType = 'meeting-destination-type';
|
|
4481
4643
|
meeting.updateMeetingActions = sinon.stub().returns(undefined);
|
|
4482
|
-
|
|
4644
|
+
|
|
4483
4645
|
meeting.meetingInfoExtraParams = {
|
|
4484
4646
|
extraParam1: 'value1'
|
|
4485
4647
|
};
|
|
@@ -4500,6 +4662,50 @@ describe('plugin-meetings', () => {
|
|
|
4500
4662
|
});
|
|
4501
4663
|
|
|
4502
4664
|
it('calls meetingInfoProvider.fetchMeetingInfo() with the right params', async () => {
|
|
4665
|
+
meeting.getPermissionTokenExpiryInfo = sinon.stub().returns(FAKE_TIMESTAMPS);
|
|
4666
|
+
await meeting.refreshPermissionToken('fake reason');
|
|
4667
|
+
|
|
4668
|
+
assert.calledOnceWithExactly(
|
|
4669
|
+
meeting.attrs.meetingInfoProvider.fetchMeetingInfo,
|
|
4670
|
+
'meeting-destination',
|
|
4671
|
+
'meeting-destination-type',
|
|
4672
|
+
null,
|
|
4673
|
+
null,
|
|
4674
|
+
'fake-installed-org-id',
|
|
4675
|
+
'locus-id',
|
|
4676
|
+
{extraParam1: 'value1', permissionToken: FAKE_PERMISSION_TOKEN},
|
|
4677
|
+
{meetingId: meeting.id, sendCAevents: true}
|
|
4678
|
+
);
|
|
4679
|
+
assert.deepEqual(meeting.meetingInfo, {
|
|
4680
|
+
...FAKE_MEETING_INFO,
|
|
4681
|
+
meetingLookupUrl: FAKE_MEETING_INFO_LOOKUP_URL
|
|
4682
|
+
});
|
|
4683
|
+
assert.equal(meeting.meetingInfoFailureReason, MEETING_INFO_FAILURE_REASON.NONE);
|
|
4684
|
+
assert.equal(meeting.requiredCaptcha, null);
|
|
4685
|
+
assert.equal(meeting.passwordStatus, PASSWORD_STATUS.NOT_REQUIRED);
|
|
4686
|
+
|
|
4687
|
+
assert.calledWith(
|
|
4688
|
+
TriggerProxy.trigger,
|
|
4689
|
+
meeting,
|
|
4690
|
+
{file: 'meetings', function: 'fetchMeetingInfo'},
|
|
4691
|
+
'meeting:meetingInfoAvailable'
|
|
4692
|
+
);
|
|
4693
|
+
assert.calledWith(meeting.updateMeetingActions);
|
|
4694
|
+
|
|
4695
|
+
assert.calledWith(
|
|
4696
|
+
Metrics.sendBehavioralMetric, BEHAVIORAL_METRICS.PERMISSION_TOKEN_REFRESH, {
|
|
4697
|
+
correlationId: meeting.correlationId,
|
|
4698
|
+
timeLeft: FAKE_TIMESTAMPS.timeLeft,
|
|
4699
|
+
expiryTime: FAKE_TIMESTAMPS.expiryTime,
|
|
4700
|
+
currentTime: FAKE_TIMESTAMPS.currentTime,
|
|
4701
|
+
reason: 'fake reason',
|
|
4702
|
+
destinationType: 'meeting-destination-type',
|
|
4703
|
+
}
|
|
4704
|
+
);
|
|
4705
|
+
});
|
|
4706
|
+
|
|
4707
|
+
it('calls meetingInfoProvider.fetchMeetingInfo() with the right params when getPermissionTokenExpiryInfo returns undefined', async () => {
|
|
4708
|
+
meeting.getPermissionTokenExpiryInfo = sinon.stub().returns(undefined);
|
|
4503
4709
|
await meeting.refreshPermissionToken('fake reason');
|
|
4504
4710
|
|
|
4505
4711
|
assert.calledOnceWithExactly(
|
|
@@ -4532,7 +4738,9 @@ describe('plugin-meetings', () => {
|
|
|
4532
4738
|
assert.calledWith(
|
|
4533
4739
|
Metrics.sendBehavioralMetric, BEHAVIORAL_METRICS.PERMISSION_TOKEN_REFRESH, {
|
|
4534
4740
|
correlationId: meeting.correlationId,
|
|
4535
|
-
timeLeft:
|
|
4741
|
+
timeLeft: undefined,
|
|
4742
|
+
expiryTime: undefined,
|
|
4743
|
+
currentTime: undefined,
|
|
4536
4744
|
reason: 'fake reason',
|
|
4537
4745
|
destinationType: 'meeting-destination-type',
|
|
4538
4746
|
}
|
|
@@ -4540,6 +4748,7 @@ describe('plugin-meetings', () => {
|
|
|
4540
4748
|
});
|
|
4541
4749
|
|
|
4542
4750
|
it('calls meetingInfoProvider.fetchMeetingInfo() with the right params when we are starting an instant space meeting', async () => {
|
|
4751
|
+
meeting.getPermissionTokenExpiryInfo = sinon.stub().returns(FAKE_TIMESTAMPS);
|
|
4543
4752
|
meeting.destination = 'some-convo-url';
|
|
4544
4753
|
meeting.destinationType = 'CONVERSATION_URL';
|
|
4545
4754
|
meeting.config.experimental = {enableAdhocMeetings: true};
|
|
@@ -4581,7 +4790,9 @@ describe('plugin-meetings', () => {
|
|
|
4581
4790
|
assert.calledWith(
|
|
4582
4791
|
Metrics.sendBehavioralMetric, BEHAVIORAL_METRICS.PERMISSION_TOKEN_REFRESH, {
|
|
4583
4792
|
correlationId: meeting.correlationId,
|
|
4584
|
-
timeLeft:
|
|
4793
|
+
timeLeft: FAKE_TIMESTAMPS.timeLeft,
|
|
4794
|
+
expiryTime: FAKE_TIMESTAMPS.expiryTime,
|
|
4795
|
+
currentTime: FAKE_TIMESTAMPS.currentTime,
|
|
4585
4796
|
reason: 'some reason',
|
|
4586
4797
|
destinationType: 'MEETING_LINK',
|
|
4587
4798
|
}
|
|
@@ -5074,6 +5285,31 @@ describe('plugin-meetings', () => {
|
|
|
5074
5285
|
}
|
|
5075
5286
|
});
|
|
5076
5287
|
});
|
|
5288
|
+
|
|
5289
|
+
describe('#setCorrelationId', () => {
|
|
5290
|
+
it('should set the correlationId and return undefined', () => {
|
|
5291
|
+
assert.equal(meeting.correlationId, correlationId);
|
|
5292
|
+
assert.deepEqual(meeting.callStateForMetrics, {correlationId});
|
|
5293
|
+
meeting.setCorrelationId(uuid1);
|
|
5294
|
+
assert.equal(meeting.correlationId, uuid1);
|
|
5295
|
+
assert.deepEqual(meeting.callStateForMetrics, {correlationId: uuid1});
|
|
5296
|
+
});
|
|
5297
|
+
});
|
|
5298
|
+
|
|
5299
|
+
describe('#updateCallStateForMetrics', () => {
|
|
5300
|
+
it('should update the callState, overriding existing values', () => {
|
|
5301
|
+
assert.deepEqual(meeting.callStateForMetrics, {correlationId});
|
|
5302
|
+
meeting.updateCallStateForMetrics({correlationId: uuid1, joinTrigger: 'jt', loginType: 'lt'});
|
|
5303
|
+
assert.deepEqual(meeting.callStateForMetrics, {correlationId: uuid1, joinTrigger: 'jt', loginType: 'lt'});
|
|
5304
|
+
});
|
|
5305
|
+
|
|
5306
|
+
it('should update the callState, keeping non-supplied values', () => {
|
|
5307
|
+
assert.deepEqual(meeting.callStateForMetrics, {correlationId});
|
|
5308
|
+
meeting.updateCallStateForMetrics({joinTrigger: 'jt', loginType: 'lt'});
|
|
5309
|
+
assert.deepEqual(meeting.callStateForMetrics, {correlationId, joinTrigger: 'jt', loginType: 'lt'});
|
|
5310
|
+
});
|
|
5311
|
+
});
|
|
5312
|
+
|
|
5077
5313
|
describe('Local tracks publishing', () => {
|
|
5078
5314
|
let audioStream;
|
|
5079
5315
|
let videoStream;
|
|
@@ -5436,31 +5672,72 @@ describe('plugin-meetings', () => {
|
|
|
5436
5672
|
it('should have #reconnect', () => {
|
|
5437
5673
|
assert.exists(meeting.reconnect);
|
|
5438
5674
|
});
|
|
5675
|
+
|
|
5439
5676
|
describe('successful reconnect', () => {
|
|
5677
|
+
let eventListeners;
|
|
5678
|
+
|
|
5440
5679
|
beforeEach(() => {
|
|
5680
|
+
eventListeners = {};
|
|
5681
|
+
meeting.mediaProperties.webrtcMediaConnection = {
|
|
5682
|
+
// mock the on() method and store all the listeners
|
|
5683
|
+
on: sinon.stub().callsFake((event, listener) => {
|
|
5684
|
+
eventListeners[event] = listener;
|
|
5685
|
+
}),
|
|
5686
|
+
};
|
|
5687
|
+
meeting.setupMediaConnectionListeners();
|
|
5688
|
+
meeting.deferSDPAnswer = {
|
|
5689
|
+
resolve: sinon.stub(),
|
|
5690
|
+
reject: sinon.stub(),
|
|
5691
|
+
};
|
|
5692
|
+
meeting.sdpResponseTimer = '1234';
|
|
5693
|
+
meeting.mediaProperties.waitForMediaConnectionConnected = sinon.stub().resolves();
|
|
5694
|
+
|
|
5695
|
+
eventListeners[Event.REMOTE_SDP_ANSWER_PROCESSED]();
|
|
5441
5696
|
meeting.config.reconnection.enabled = true;
|
|
5442
5697
|
meeting.currentMediaStatus = {audio: true};
|
|
5443
5698
|
meeting.reconnectionManager = new ReconnectionManager(meeting);
|
|
5444
5699
|
meeting.reconnectionManager.reconnect = sinon.stub().returns(Promise.resolve());
|
|
5445
5700
|
meeting.reconnectionManager.reset = sinon.stub().returns(true);
|
|
5446
5701
|
meeting.reconnectionManager.cleanup = sinon.stub().returns(true);
|
|
5702
|
+
meeting.reconnectionManager.setStatus = sinon.stub();
|
|
5447
5703
|
});
|
|
5448
5704
|
|
|
5449
|
-
it('should throw error if media not established before trying
|
|
5705
|
+
it('should throw error if media not established before trying reconnect', async () => {
|
|
5450
5706
|
meeting.currentMediaStatus = null;
|
|
5451
5707
|
await meeting.reconnect().catch((err) => {
|
|
5452
5708
|
assert.instanceOf(err, ParameterError);
|
|
5453
5709
|
});
|
|
5454
5710
|
});
|
|
5455
5711
|
|
|
5456
|
-
it('should
|
|
5712
|
+
it('should reconnect successfully if reconnectionManager.cleanUp is called before reconnection attempt', async () => {
|
|
5713
|
+
meeting.reconnectionManager.cleanUp();
|
|
5714
|
+
|
|
5715
|
+
try {
|
|
5716
|
+
await meeting.reconnect();
|
|
5717
|
+
} catch (err) {
|
|
5718
|
+
assert.fail('reconnect should not error after clean up');
|
|
5719
|
+
}
|
|
5720
|
+
})
|
|
5721
|
+
|
|
5722
|
+
it('should trigger reconnection success and send CA metric', async () => {
|
|
5457
5723
|
await meeting.reconnect();
|
|
5724
|
+
|
|
5458
5725
|
assert.calledWith(
|
|
5459
5726
|
TriggerProxy.trigger,
|
|
5460
5727
|
sinon.match.instanceOf(Meeting),
|
|
5461
5728
|
{file: 'meeting/index', function: 'reconnect'},
|
|
5462
5729
|
'meeting:reconnectionSuccess'
|
|
5463
5730
|
);
|
|
5731
|
+
assert.calledWithMatch(webex.internal.newMetrics.submitClientEvent, {
|
|
5732
|
+
name: 'client.media.recovered',
|
|
5733
|
+
payload: {
|
|
5734
|
+
recoveredBy: 'new',
|
|
5735
|
+
},
|
|
5736
|
+
options: {
|
|
5737
|
+
meetingId: meeting.id,
|
|
5738
|
+
},
|
|
5739
|
+
});
|
|
5740
|
+
assert.calledOnceWithExactly(meeting.reconnectionManager.setStatus, RECONNECTION.STATE.COMPLETE);
|
|
5464
5741
|
});
|
|
5465
5742
|
|
|
5466
5743
|
it('should reset after reconnection success', async () => {
|
|
@@ -5527,7 +5804,7 @@ describe('plugin-meetings', () => {
|
|
|
5527
5804
|
it('should stop remote tracks, and trigger a media:stopped event when the remote tracks are stopped', async () => {
|
|
5528
5805
|
await meeting.closeRemoteStreams();
|
|
5529
5806
|
|
|
5530
|
-
assert.equal(TriggerProxy.trigger.callCount,
|
|
5807
|
+
assert.equal(TriggerProxy.trigger.callCount, 5);
|
|
5531
5808
|
assert.calledWith(
|
|
5532
5809
|
TriggerProxy.trigger,
|
|
5533
5810
|
sinon.match.instanceOf(Meeting),
|
|
@@ -5613,37 +5890,42 @@ describe('plugin-meetings', () => {
|
|
|
5613
5890
|
});
|
|
5614
5891
|
});
|
|
5615
5892
|
|
|
5616
|
-
describe('
|
|
5617
|
-
it('sends client.ice.
|
|
5618
|
-
|
|
5619
|
-
const getErrorPayloadForClientErrorCodeStub = webex.internal.newMetrics.callDiagnosticMetrics.getErrorPayloadForClientErrorCode = sinon
|
|
5620
|
-
.stub()
|
|
5621
|
-
.returns(FAKE_ERROR);
|
|
5893
|
+
describe('CONNECTION_STATE_CHANGED event when state = "Connecting"', () => {
|
|
5894
|
+
it('sends client.ice.start correctly when hasMediaConnectionConnectedAtLeastOnce = true', () => {
|
|
5895
|
+
meeting.hasMediaConnectionConnectedAtLeastOnce = true;
|
|
5622
5896
|
meeting.setupMediaConnectionListeners();
|
|
5623
5897
|
eventListeners[Event.CONNECTION_STATE_CHANGED]({
|
|
5624
|
-
state: '
|
|
5898
|
+
state: 'Connecting',
|
|
5899
|
+
});
|
|
5900
|
+
|
|
5901
|
+
assert.notCalled(webex.internal.newMetrics.submitClientEvent);
|
|
5902
|
+
})
|
|
5903
|
+
|
|
5904
|
+
it('sends client.ice.start correctly when hasMediaConnectionConnectedAtLeastOnce = false', () => {
|
|
5905
|
+
meeting.hasMediaConnectionConnectedAtLeastOnce = false;
|
|
5906
|
+
meeting.setupMediaConnectionListeners();
|
|
5907
|
+
eventListeners[Event.CONNECTION_STATE_CHANGED]({
|
|
5908
|
+
state: 'Connecting',
|
|
5625
5909
|
});
|
|
5626
|
-
|
|
5910
|
+
|
|
5627
5911
|
assert.calledOnce(webex.internal.newMetrics.submitClientEvent);
|
|
5628
5912
|
assert.calledWithMatch(webex.internal.newMetrics.submitClientEvent, {
|
|
5629
|
-
name: 'client.ice.
|
|
5630
|
-
payload: {
|
|
5631
|
-
canProceed: false,
|
|
5632
|
-
icePhase: 'IN_MEETING',
|
|
5633
|
-
errors: [FAKE_ERROR],
|
|
5634
|
-
},
|
|
5913
|
+
name: 'client.ice.start',
|
|
5635
5914
|
options: {
|
|
5636
5915
|
meetingId: meeting.id,
|
|
5637
5916
|
},
|
|
5638
5917
|
});
|
|
5639
|
-
})
|
|
5918
|
+
})
|
|
5640
5919
|
});
|
|
5641
5920
|
|
|
5642
5921
|
describe('submitClientEvent on connectionSuccess', () => {
|
|
5643
|
-
|
|
5922
|
+
let setNetworkStatusSpy;
|
|
5923
|
+
|
|
5924
|
+
const setupSpies = () => {
|
|
5925
|
+
setNetworkStatusSpy = sinon.spy(meeting, 'setNetworkStatus');
|
|
5926
|
+
|
|
5644
5927
|
meeting.reconnectionManager = new ReconnectionManager(meeting);
|
|
5645
5928
|
meeting.reconnectionManager.iceReconnected = sinon.stub().returns(undefined);
|
|
5646
|
-
meeting.setNetworkStatus = sinon.stub().returns(undefined);
|
|
5647
5929
|
meeting.statsAnalyzer = {startAnalyzer: sinon.stub()};
|
|
5648
5930
|
meeting.mediaProperties.webrtcMediaConnection = {
|
|
5649
5931
|
// mock the on() method and store all the listeners
|
|
@@ -5651,45 +5933,234 @@ describe('plugin-meetings', () => {
|
|
|
5651
5933
|
eventListeners[event] = listener;
|
|
5652
5934
|
}),
|
|
5653
5935
|
};
|
|
5936
|
+
};
|
|
5654
5937
|
|
|
5655
|
-
|
|
5656
|
-
|
|
5657
|
-
|
|
5658
|
-
|
|
5659
|
-
|
|
5660
|
-
|
|
5661
|
-
|
|
5662
|
-
|
|
5663
|
-
|
|
5664
|
-
|
|
5665
|
-
|
|
5938
|
+
const checkExpectedSpies = (expected) => {
|
|
5939
|
+
if (expected.icePhase) {
|
|
5940
|
+
assert.calledOnce(webex.internal.newMetrics.submitClientEvent);
|
|
5941
|
+
assert.calledWithMatch(webex.internal.newMetrics.submitClientEvent, {
|
|
5942
|
+
name: 'client.ice.end',
|
|
5943
|
+
options: {
|
|
5944
|
+
meetingId: meeting.id,
|
|
5945
|
+
},
|
|
5946
|
+
payload: {
|
|
5947
|
+
canProceed: true,
|
|
5948
|
+
icePhase: expected.icePhase,
|
|
5949
|
+
},
|
|
5950
|
+
});
|
|
5951
|
+
} else {
|
|
5952
|
+
assert.notCalled(webex.internal.newMetrics.submitClientEvent);
|
|
5953
|
+
}
|
|
5666
5954
|
assert.calledOnce(Metrics.sendBehavioralMetric);
|
|
5667
5955
|
assert.calledWith(Metrics.sendBehavioralMetric, BEHAVIORAL_METRICS.CONNECTION_SUCCESS, {
|
|
5668
5956
|
correlation_id: meeting.correlationId,
|
|
5669
5957
|
locus_id: meeting.locusId,
|
|
5670
5958
|
latency: undefined,
|
|
5671
5959
|
});
|
|
5672
|
-
assert.
|
|
5673
|
-
|
|
5960
|
+
assert.deepEqual(
|
|
5961
|
+
setNetworkStatusSpy.getCalls().map((call) => call.args[0]),
|
|
5962
|
+
expected.setNetworkStatusCallParams
|
|
5963
|
+
);
|
|
5674
5964
|
assert.calledOnce(meeting.reconnectionManager.iceReconnected);
|
|
5675
5965
|
assert.calledOnce(meeting.statsAnalyzer.startAnalyzer);
|
|
5676
5966
|
assert.calledWith(
|
|
5677
5967
|
meeting.statsAnalyzer.startAnalyzer,
|
|
5678
5968
|
meeting.mediaProperties.webrtcMediaConnection
|
|
5679
5969
|
);
|
|
5680
|
-
}
|
|
5681
|
-
});
|
|
5970
|
+
};
|
|
5682
5971
|
|
|
5683
|
-
|
|
5684
|
-
|
|
5685
|
-
|
|
5686
|
-
|
|
5972
|
+
const resetSpies = () => {
|
|
5973
|
+
setNetworkStatusSpy.resetHistory();
|
|
5974
|
+
webex.internal.newMetrics.submitClientEvent.resetHistory();
|
|
5975
|
+
Metrics.sendBehavioralMetric.resetHistory();
|
|
5976
|
+
meeting.reconnectionManager.iceReconnected.resetHistory();
|
|
5977
|
+
meeting.statsAnalyzer.startAnalyzer.resetHistory();
|
|
5978
|
+
};
|
|
5979
|
+
|
|
5980
|
+
it('sends client.ice.end with the correct icePhase when we get ConnectionState.Connected on CONNECTION_STATE_CHANGED event', () => {
|
|
5981
|
+
setupSpies();
|
|
5687
5982
|
|
|
5688
|
-
beforeEach(() => {
|
|
5689
5983
|
meeting.setupMediaConnectionListeners();
|
|
5690
|
-
});
|
|
5691
5984
|
|
|
5692
|
-
|
|
5985
|
+
assert.equal(meeting.hasMediaConnectionConnectedAtLeastOnce, false);
|
|
5986
|
+
|
|
5987
|
+
// simulate first connection success
|
|
5988
|
+
eventListeners[Event.CONNECTION_STATE_CHANGED]({
|
|
5989
|
+
state: 'Connected',
|
|
5990
|
+
});
|
|
5991
|
+
checkExpectedSpies({
|
|
5992
|
+
icePhase: 'JOIN_MEETING_FINAL',
|
|
5993
|
+
setNetworkStatusCallParams: [NETWORK_STATUS.CONNECTED],
|
|
5994
|
+
});
|
|
5995
|
+
assert.equal(meeting.hasMediaConnectionConnectedAtLeastOnce, true);
|
|
5996
|
+
|
|
5997
|
+
// now simulate short connection loss, client.ice.end is not sent a second time as hasMediaConnectionConnectedAtLeastOnce = true
|
|
5998
|
+
resetSpies();
|
|
5999
|
+
|
|
6000
|
+
eventListeners[Event.CONNECTION_STATE_CHANGED]({
|
|
6001
|
+
state: 'Disconnected',
|
|
6002
|
+
});
|
|
6003
|
+
eventListeners[Event.CONNECTION_STATE_CHANGED]({
|
|
6004
|
+
state: 'Connected',
|
|
6005
|
+
});
|
|
6006
|
+
|
|
6007
|
+
checkExpectedSpies({
|
|
6008
|
+
setNetworkStatusCallParams: [NETWORK_STATUS.DISCONNECTED, NETWORK_STATUS.CONNECTED],
|
|
6009
|
+
});
|
|
6010
|
+
|
|
6011
|
+
resetSpies();
|
|
6012
|
+
|
|
6013
|
+
eventListeners[Event.CONNECTION_STATE_CHANGED]({
|
|
6014
|
+
state: 'Disconnected',
|
|
6015
|
+
});
|
|
6016
|
+
eventListeners[Event.CONNECTION_STATE_CHANGED]({
|
|
6017
|
+
state: 'Connected',
|
|
6018
|
+
});
|
|
6019
|
+
});
|
|
6020
|
+
});
|
|
6021
|
+
|
|
6022
|
+
describe('CONNECTION_STATE_CHANGED event when state = "Disconnected"', () => {
|
|
6023
|
+
beforeEach(() => {
|
|
6024
|
+
meeting.reconnectionManager = new ReconnectionManager(meeting);
|
|
6025
|
+
meeting.reconnectionManager.iceReconnected = sinon.stub().returns(undefined);
|
|
6026
|
+
meeting.setNetworkStatus = sinon.stub().returns(undefined);
|
|
6027
|
+
meeting.statsAnalyzer = {startAnalyzer: sinon.stub()};
|
|
6028
|
+
meeting.mediaProperties.webrtcMediaConnection = {
|
|
6029
|
+
// mock the on() method and store all the listeners
|
|
6030
|
+
on: sinon.stub().callsFake((event, listener) => {
|
|
6031
|
+
eventListeners[event] = listener;
|
|
6032
|
+
}),
|
|
6033
|
+
};
|
|
6034
|
+
meeting.reconnect = sinon.stub().resolves();
|
|
6035
|
+
});
|
|
6036
|
+
|
|
6037
|
+
const mockDisconnectedEvent = () => {
|
|
6038
|
+
meeting.setupMediaConnectionListeners();
|
|
6039
|
+
eventListeners[Event.CONNECTION_STATE_CHANGED]({
|
|
6040
|
+
state: 'Disconnected',
|
|
6041
|
+
});
|
|
6042
|
+
};
|
|
6043
|
+
|
|
6044
|
+
const checkBehavioralMetricSent = (hasMediaConnectionConnectedAtLeastOnce = false) => {
|
|
6045
|
+
assert.calledOnce(Metrics.sendBehavioralMetric);
|
|
6046
|
+
assert.calledWith(
|
|
6047
|
+
Metrics.sendBehavioralMetric,
|
|
6048
|
+
BEHAVIORAL_METRICS.CONNECTION_FAILURE,
|
|
6049
|
+
{
|
|
6050
|
+
correlation_id: meeting.correlationId,
|
|
6051
|
+
locus_id: meeting.locusUrl.split('/').pop(),
|
|
6052
|
+
networkStatus: meeting.networkStatus,
|
|
6053
|
+
hasMediaConnectionConnectedAtLeastOnce,
|
|
6054
|
+
},
|
|
6055
|
+
);
|
|
6056
|
+
};
|
|
6057
|
+
|
|
6058
|
+
it('handles "Disconnected" state correctly when waitForIceReconnect resolves', async () => {
|
|
6059
|
+
meeting.reconnectionManager.waitForIceReconnect = sinon.stub().resolves();
|
|
6060
|
+
|
|
6061
|
+
|
|
6062
|
+
mockDisconnectedEvent();
|
|
6063
|
+
|
|
6064
|
+
await testUtils.flushPromises();
|
|
6065
|
+
|
|
6066
|
+
assert.calledOnce(meeting.setNetworkStatus);
|
|
6067
|
+
assert.calledWith(meeting.setNetworkStatus, NETWORK_STATUS.DISCONNECTED);
|
|
6068
|
+
assert.calledOnce(meeting.reconnectionManager.waitForIceReconnect);
|
|
6069
|
+
assert.notCalled(webex.internal.newMetrics.submitClientEvent);
|
|
6070
|
+
assert.notCalled(Metrics.sendBehavioralMetric);
|
|
6071
|
+
});
|
|
6072
|
+
|
|
6073
|
+
it('handles "Disconnected" state correctly when waitForIceReconnect rejects and hasMediaConnectionConnectedAtLeastOnce = true', async () => {
|
|
6074
|
+
const FAKE_ERROR = {fatal: true};
|
|
6075
|
+
const getErrorPayloadForClientErrorCodeStub = webex.internal.newMetrics.callDiagnosticMetrics.getErrorPayloadForClientErrorCode = sinon
|
|
6076
|
+
.stub()
|
|
6077
|
+
.returns(FAKE_ERROR);
|
|
6078
|
+
meeting.waitForMediaConnectionConnected = sinon.stub().resolves();
|
|
6079
|
+
meeting.reconnectionManager.waitForIceReconnect = sinon.stub().rejects();
|
|
6080
|
+
meeting.hasMediaConnectionConnectedAtLeastOnce = true;
|
|
6081
|
+
|
|
6082
|
+
|
|
6083
|
+
mockDisconnectedEvent();
|
|
6084
|
+
|
|
6085
|
+
await testUtils.flushPromises();
|
|
6086
|
+
|
|
6087
|
+
assert.calledOnce(meeting.setNetworkStatus);
|
|
6088
|
+
assert.calledWith(meeting.setNetworkStatus, NETWORK_STATUS.DISCONNECTED);
|
|
6089
|
+
assert.calledOnce(meeting.reconnectionManager.waitForIceReconnect);
|
|
6090
|
+
assert.notCalled(webex.internal.newMetrics.submitClientEvent);
|
|
6091
|
+
checkBehavioralMetricSent(true);
|
|
6092
|
+
});
|
|
6093
|
+
|
|
6094
|
+
it('handles "Disconnected" state correctly when waitForIceReconnect rejects and hasMediaConnectionConnectedAtLeastOnce = false', async () => {
|
|
6095
|
+
meeting.reconnectionManager.waitForIceReconnect = sinon.stub().rejects();
|
|
6096
|
+
|
|
6097
|
+
|
|
6098
|
+
mockDisconnectedEvent();
|
|
6099
|
+
|
|
6100
|
+
await testUtils.flushPromises();
|
|
6101
|
+
|
|
6102
|
+
assert.calledOnce(meeting.setNetworkStatus);
|
|
6103
|
+
assert.calledWith(meeting.setNetworkStatus, NETWORK_STATUS.DISCONNECTED);
|
|
6104
|
+
assert.calledOnce(meeting.reconnectionManager.waitForIceReconnect);
|
|
6105
|
+
assert.notCalled(webex.internal.newMetrics.submitClientEvent);
|
|
6106
|
+
checkBehavioralMetricSent();
|
|
6107
|
+
});
|
|
6108
|
+
});
|
|
6109
|
+
|
|
6110
|
+
describe('CONNECTION_STATE_CHANGED event when state = "Failed"', () => {
|
|
6111
|
+
|
|
6112
|
+
const mockFailedEvent = () => {
|
|
6113
|
+
meeting.setupMediaConnectionListeners();
|
|
6114
|
+
eventListeners[Event.CONNECTION_STATE_CHANGED]({
|
|
6115
|
+
state: 'Failed',
|
|
6116
|
+
});
|
|
6117
|
+
};
|
|
6118
|
+
|
|
6119
|
+
const checkBehavioralMetricSent = (hasMediaConnectionConnectedAtLeastOnce = false) => {
|
|
6120
|
+
assert.calledOnce(Metrics.sendBehavioralMetric);
|
|
6121
|
+
assert.calledWith(
|
|
6122
|
+
Metrics.sendBehavioralMetric,
|
|
6123
|
+
BEHAVIORAL_METRICS.CONNECTION_FAILURE,
|
|
6124
|
+
{
|
|
6125
|
+
correlation_id: meeting.correlationId,
|
|
6126
|
+
locus_id: meeting.locusUrl.split('/').pop(),
|
|
6127
|
+
networkStatus: meeting.networkStatus,
|
|
6128
|
+
hasMediaConnectionConnectedAtLeastOnce,
|
|
6129
|
+
},
|
|
6130
|
+
);
|
|
6131
|
+
};
|
|
6132
|
+
|
|
6133
|
+
it('handles "Failed" state correctly when hasMediaConnectionConnectedAtLeastOnce = false', async () => {
|
|
6134
|
+
meeting.waitForMediaConnectionConnected = sinon.stub().resolves();
|
|
6135
|
+
|
|
6136
|
+
mockFailedEvent();
|
|
6137
|
+
|
|
6138
|
+
assert.notCalled(webex.internal.newMetrics.submitClientEvent);
|
|
6139
|
+
checkBehavioralMetricSent();
|
|
6140
|
+
});
|
|
6141
|
+
|
|
6142
|
+
it('handles "Failed" state correctly when hasMediaConnectionConnectedAtLeastOnce = true', async () => {
|
|
6143
|
+
meeting.hasMediaConnectionConnectedAtLeastOnce = true;
|
|
6144
|
+
|
|
6145
|
+
mockFailedEvent();
|
|
6146
|
+
|
|
6147
|
+
assert.notCalled(webex.internal.newMetrics.submitClientEvent);
|
|
6148
|
+
checkBehavioralMetricSent(true);
|
|
6149
|
+
});
|
|
6150
|
+
});
|
|
6151
|
+
|
|
6152
|
+
describe('should send correct metrics for ROAP_FAILURE event', () => {
|
|
6153
|
+
const fakeErrorMessage = 'test error';
|
|
6154
|
+
const fakeRootCauseName = 'root cause name';
|
|
6155
|
+
const fakeErrorName = 'test error name';
|
|
6156
|
+
|
|
6157
|
+
beforeEach(() => {
|
|
6158
|
+
meeting.setupMediaConnectionListeners();
|
|
6159
|
+
webex.internal.newMetrics.submitClientEvent.resetHistory();
|
|
6160
|
+
Metrics.sendBehavioralMetric.resetHistory();
|
|
6161
|
+
});
|
|
6162
|
+
|
|
6163
|
+
const checkMetricSent = (event, error) => {
|
|
5693
6164
|
assert.calledOnce(webex.internal.newMetrics.submitClientEvent);
|
|
5694
6165
|
assert.calledWithMatch(webex.internal.newMetrics.submitClientEvent, {
|
|
5695
6166
|
name: event,
|
|
@@ -6732,6 +7203,7 @@ describe('plugin-meetings', () => {
|
|
|
6732
7203
|
done();
|
|
6733
7204
|
});
|
|
6734
7205
|
});
|
|
7206
|
+
|
|
6735
7207
|
describe('#setUpLocusInfoMediaInactiveListener', () => {
|
|
6736
7208
|
it('listens to disconnect due to un activity ', (done) => {
|
|
6737
7209
|
TriggerProxy.trigger.reset();
|
|
@@ -6919,12 +7391,18 @@ describe('plugin-meetings', () => {
|
|
|
6919
7391
|
it('sets correctly when policy data is present in token', () => {
|
|
6920
7392
|
assert.notOk(meeting.selfUserPolicies);
|
|
6921
7393
|
|
|
6922
|
-
const
|
|
7394
|
+
const testUrl = 'https://example.com';
|
|
7395
|
+
|
|
7396
|
+
const policyData = {permission: {
|
|
7397
|
+
userPolicies: {a: true},
|
|
7398
|
+
enforceVBGImagesURL: testUrl
|
|
7399
|
+
}};
|
|
6923
7400
|
meeting.permissionTokenPayload = policyData;
|
|
6924
7401
|
|
|
6925
7402
|
meeting.setSelfUserPolicies();
|
|
6926
7403
|
|
|
6927
7404
|
assert.deepEqual(meeting.selfUserPolicies, policyData.permission.userPolicies);
|
|
7405
|
+
assert.equal(meeting.enforceVBGImagesURL, testUrl);
|
|
6928
7406
|
});
|
|
6929
7407
|
|
|
6930
7408
|
it('handles missing permission data', () => {
|
|
@@ -6977,12 +7455,14 @@ describe('plugin-meetings', () => {
|
|
|
6977
7455
|
});
|
|
6978
7456
|
describe('#closePeerConnections', () => {
|
|
6979
7457
|
it('should close the webrtc media connection, and return a promise', async () => {
|
|
7458
|
+
const setNetworkStatusSpy = sinon.spy(meeting, 'setNetworkStatus');
|
|
6980
7459
|
meeting.mediaProperties.webrtcMediaConnection = {close: sinon.stub()};
|
|
6981
7460
|
const pcs = meeting.closePeerConnections();
|
|
6982
7461
|
|
|
6983
7462
|
assert.exists(pcs.then);
|
|
6984
7463
|
await pcs;
|
|
6985
7464
|
assert.calledOnce(meeting.mediaProperties.webrtcMediaConnection.close);
|
|
7465
|
+
assert.calledOnceWithExactly(setNetworkStatusSpy, undefined);
|
|
6986
7466
|
});
|
|
6987
7467
|
});
|
|
6988
7468
|
describe('#unsetPeerConnections', () => {
|
|
@@ -7190,14 +7670,6 @@ describe('plugin-meetings', () => {
|
|
|
7190
7670
|
});
|
|
7191
7671
|
});
|
|
7192
7672
|
|
|
7193
|
-
describe('#setCorrelationId', () => {
|
|
7194
|
-
it('should set the correlationId and return undefined', () => {
|
|
7195
|
-
assert.ok(meeting.correlationId);
|
|
7196
|
-
meeting.setCorrelationId(uuid1);
|
|
7197
|
-
assert.equal(meeting.correlationId, uuid1);
|
|
7198
|
-
});
|
|
7199
|
-
});
|
|
7200
|
-
|
|
7201
7673
|
describe('#setUpLocusInfoAssignHostListener', () => {
|
|
7202
7674
|
let locusInfoOnSpy;
|
|
7203
7675
|
let inMeetingActionsSetSpy;
|
|
@@ -7246,47 +7718,12 @@ describe('plugin-meetings', () => {
|
|
|
7246
7718
|
});
|
|
7247
7719
|
|
|
7248
7720
|
describe('#setUpLocusInfoMeetingInfoListener', () => {
|
|
7249
|
-
let inMeetingActionsSetSpy;
|
|
7250
|
-
let canUserLockSpy;
|
|
7251
|
-
let canUserUnlockSpy;
|
|
7252
|
-
let canUserStartSpy;
|
|
7253
|
-
let canUserStopSpy;
|
|
7254
|
-
let canUserPauseSpy;
|
|
7255
|
-
let canUserResumeSpy;
|
|
7256
|
-
let canSetMuteOnEntrySpy;
|
|
7257
|
-
let canUnsetMuteOnEntrySpy;
|
|
7258
|
-
let canSetDisallowUnmuteSpy;
|
|
7259
|
-
let canUnsetDisallowUnmuteSpy;
|
|
7260
|
-
let canUserRaiseHandSpy;
|
|
7261
|
-
let bothLeaveAndEndMeetingAvailableSpy;
|
|
7262
|
-
let canUserLowerAllHandsSpy;
|
|
7263
|
-
let canUserLowerSomeoneElsesHandSpy;
|
|
7264
|
-
let waitingForOthersToJoinSpy;
|
|
7265
7721
|
let locusInfoOnSpy;
|
|
7266
7722
|
let handleDataChannelUrlChangeSpy;
|
|
7267
7723
|
let updateMeetingActionsSpy;
|
|
7268
7724
|
|
|
7269
7725
|
beforeEach(() => {
|
|
7270
7726
|
locusInfoOnSpy = sinon.spy(meeting.locusInfo, 'on');
|
|
7271
|
-
canUserLockSpy = sinon.spy(MeetingUtil, 'canUserLock');
|
|
7272
|
-
canUserUnlockSpy = sinon.spy(MeetingUtil, 'canUserUnlock');
|
|
7273
|
-
canUserStartSpy = sinon.spy(RecordingUtil, 'canUserStart');
|
|
7274
|
-
canUserStopSpy = sinon.spy(RecordingUtil, 'canUserStop');
|
|
7275
|
-
canUserPauseSpy = sinon.spy(RecordingUtil, 'canUserPause');
|
|
7276
|
-
canUserResumeSpy = sinon.spy(RecordingUtil, 'canUserResume');
|
|
7277
|
-
canSetMuteOnEntrySpy = sinon.spy(ControlsOptionsUtil, 'canSetMuteOnEntry');
|
|
7278
|
-
canUnsetMuteOnEntrySpy = sinon.spy(ControlsOptionsUtil, 'canUnsetMuteOnEntry');
|
|
7279
|
-
canSetDisallowUnmuteSpy = sinon.spy(ControlsOptionsUtil, 'canSetDisallowUnmute');
|
|
7280
|
-
canUnsetDisallowUnmuteSpy = sinon.spy(ControlsOptionsUtil, 'canUnsetDisallowUnmute');
|
|
7281
|
-
inMeetingActionsSetSpy = sinon.spy(meeting.inMeetingActions, 'set');
|
|
7282
|
-
canUserRaiseHandSpy = sinon.spy(MeetingUtil, 'canUserRaiseHand');
|
|
7283
|
-
canUserLowerAllHandsSpy = sinon.spy(MeetingUtil, 'canUserLowerAllHands');
|
|
7284
|
-
bothLeaveAndEndMeetingAvailableSpy = sinon.spy(
|
|
7285
|
-
MeetingUtil,
|
|
7286
|
-
'bothLeaveAndEndMeetingAvailable'
|
|
7287
|
-
);
|
|
7288
|
-
canUserLowerSomeoneElsesHandSpy = sinon.spy(MeetingUtil, 'canUserLowerSomeoneElsesHand');
|
|
7289
|
-
waitingForOthersToJoinSpy = sinon.spy(MeetingUtil, 'waitingForOthersToJoin');
|
|
7290
7727
|
handleDataChannelUrlChangeSpy = sinon.spy(meeting, 'handleDataChannelUrlChange');
|
|
7291
7728
|
updateMeetingActionsSpy = sinon.spy(meeting, 'updateMeetingActions');
|
|
7292
7729
|
});
|
|
@@ -7324,29 +7761,686 @@ describe('plugin-meetings', () => {
|
|
|
7324
7761
|
assert.equal(locusInfoOnSpy.thirdCall.args[0], 'MEETING_INFO_UPDATED');
|
|
7325
7762
|
const callback = locusInfoOnSpy.thirdCall.args[1];
|
|
7326
7763
|
|
|
7327
|
-
const payload = {
|
|
7328
|
-
info: {
|
|
7329
|
-
userDisplayHints: ['LOCK_CONTROL_UNLOCK'],
|
|
7330
|
-
},
|
|
7331
|
-
};
|
|
7332
|
-
|
|
7333
7764
|
callback();
|
|
7334
7765
|
|
|
7335
|
-
assert.calledWith(
|
|
7336
|
-
assert.calledWith(
|
|
7337
|
-
assert.calledWith(
|
|
7338
|
-
assert.calledWith(
|
|
7339
|
-
assert.calledWith(
|
|
7340
|
-
|
|
7341
|
-
|
|
7342
|
-
|
|
7343
|
-
|
|
7344
|
-
|
|
7345
|
-
|
|
7346
|
-
|
|
7347
|
-
|
|
7348
|
-
|
|
7349
|
-
|
|
7766
|
+
assert.calledWith(updateMeetingActionsSpy);
|
|
7767
|
+
assert.calledWith(setRecordingDisplayHintsSpy, userDisplayHints);
|
|
7768
|
+
assert.calledWith(setUserPolicySpy, userDisplayPolicy);
|
|
7769
|
+
assert.calledWith(setControlsDisplayHintsSpy, userDisplayHints);
|
|
7770
|
+
assert.calledWith(handleDataChannelUrlChangeSpy, datachannelUrl);
|
|
7771
|
+
});
|
|
7772
|
+
});
|
|
7773
|
+
|
|
7774
|
+
describe('#updateMeetingActions', () => {
|
|
7775
|
+
let inMeetingActionsSetSpy;
|
|
7776
|
+
let canUserLockSpy;
|
|
7777
|
+
let canUserUnlockSpy;
|
|
7778
|
+
let canUserStartSpy;
|
|
7779
|
+
let canUserStopSpy;
|
|
7780
|
+
let canUserPauseSpy;
|
|
7781
|
+
let canUserResumeSpy;
|
|
7782
|
+
let canSetMuteOnEntrySpy;
|
|
7783
|
+
let canUnsetMuteOnEntrySpy;
|
|
7784
|
+
let canSetDisallowUnmuteSpy;
|
|
7785
|
+
let canUnsetDisallowUnmuteSpy;
|
|
7786
|
+
let canUserRaiseHandSpy;
|
|
7787
|
+
let bothLeaveAndEndMeetingAvailableSpy;
|
|
7788
|
+
let canUserLowerAllHandsSpy;
|
|
7789
|
+
let canUserLowerSomeoneElsesHandSpy;
|
|
7790
|
+
let waitingForOthersToJoinSpy;
|
|
7791
|
+
let canSendReactionsSpy;
|
|
7792
|
+
let canUserRenameSelfAndObservedSpy;
|
|
7793
|
+
let canUserRenameOthersSpy;
|
|
7794
|
+
let canShareWhiteBoardSpy;
|
|
7795
|
+
// Due to import tree issues, hasHints must be stubed within the scope of the `it`.
|
|
7796
|
+
|
|
7797
|
+
beforeEach(() => {
|
|
7798
|
+
canUserLockSpy = sinon.spy(MeetingUtil, 'canUserLock');
|
|
7799
|
+
canUserUnlockSpy = sinon.spy(MeetingUtil, 'canUserUnlock');
|
|
7800
|
+
canUserStartSpy = sinon.spy(RecordingUtil, 'canUserStart');
|
|
7801
|
+
canUserStopSpy = sinon.spy(RecordingUtil, 'canUserStop');
|
|
7802
|
+
canUserPauseSpy = sinon.spy(RecordingUtil, 'canUserPause');
|
|
7803
|
+
canUserResumeSpy = sinon.spy(RecordingUtil, 'canUserResume');
|
|
7804
|
+
canSetMuteOnEntrySpy = sinon.spy(ControlsOptionsUtil, 'canSetMuteOnEntry');
|
|
7805
|
+
canUnsetMuteOnEntrySpy = sinon.spy(ControlsOptionsUtil, 'canUnsetMuteOnEntry');
|
|
7806
|
+
canSetDisallowUnmuteSpy = sinon.spy(ControlsOptionsUtil, 'canSetDisallowUnmute');
|
|
7807
|
+
canUnsetDisallowUnmuteSpy = sinon.spy(ControlsOptionsUtil, 'canUnsetDisallowUnmute');
|
|
7808
|
+
inMeetingActionsSetSpy = sinon.spy(meeting.inMeetingActions, 'set');
|
|
7809
|
+
canUserRaiseHandSpy = sinon.spy(MeetingUtil, 'canUserRaiseHand');
|
|
7810
|
+
canUserLowerAllHandsSpy = sinon.spy(MeetingUtil, 'canUserLowerAllHands');
|
|
7811
|
+
bothLeaveAndEndMeetingAvailableSpy = sinon.spy(
|
|
7812
|
+
MeetingUtil,
|
|
7813
|
+
'bothLeaveAndEndMeetingAvailable'
|
|
7814
|
+
);
|
|
7815
|
+
canUserLowerSomeoneElsesHandSpy = sinon.spy(MeetingUtil, 'canUserLowerSomeoneElsesHand');
|
|
7816
|
+
waitingForOthersToJoinSpy = sinon.spy(MeetingUtil, 'waitingForOthersToJoin');
|
|
7817
|
+
canSendReactionsSpy = sinon.spy(MeetingUtil, 'canSendReactions');
|
|
7818
|
+
canUserRenameSelfAndObservedSpy = sinon.spy(MeetingUtil, 'canUserRenameSelfAndObserved');
|
|
7819
|
+
canUserRenameOthersSpy = sinon.spy(MeetingUtil, 'canUserRenameOthers');
|
|
7820
|
+
canShareWhiteBoardSpy = sinon.spy(MeetingUtil, 'canShareWhiteBoard');
|
|
7821
|
+
});
|
|
7822
|
+
|
|
7823
|
+
afterEach(() => {
|
|
7824
|
+
inMeetingActionsSetSpy.restore();
|
|
7825
|
+
waitingForOthersToJoinSpy.restore();
|
|
7826
|
+
});
|
|
7827
|
+
|
|
7828
|
+
forEach(
|
|
7829
|
+
[
|
|
7830
|
+
{
|
|
7831
|
+
actionName: 'canShareApplication',
|
|
7832
|
+
expectedEnabled: true,
|
|
7833
|
+
arePolicyRestrictionsSupported: false,
|
|
7834
|
+
},
|
|
7835
|
+
{
|
|
7836
|
+
actionName: 'canShareApplication',
|
|
7837
|
+
expectedEnabled: false,
|
|
7838
|
+
arePolicyRestrictionsSupported: true,
|
|
7839
|
+
},
|
|
7840
|
+
{
|
|
7841
|
+
actionName: 'canShareDesktop',
|
|
7842
|
+
arePolicyRestrictionsSupported: false,
|
|
7843
|
+
expectedEnabled: true,
|
|
7844
|
+
},
|
|
7845
|
+
{
|
|
7846
|
+
actionName: 'canShareDesktop',
|
|
7847
|
+
arePolicyRestrictionsSupported: true,
|
|
7848
|
+
expectedEnabled: false,
|
|
7849
|
+
},
|
|
7850
|
+
{
|
|
7851
|
+
actionName: 'canShareContent',
|
|
7852
|
+
arePolicyRestrictionsSupported: false,
|
|
7853
|
+
expectedEnabled: true,
|
|
7854
|
+
},
|
|
7855
|
+
{
|
|
7856
|
+
actionName: 'canShareContent',
|
|
7857
|
+
arePolicyRestrictionsSupported: true,
|
|
7858
|
+
expectedEnabled: false,
|
|
7859
|
+
},
|
|
7860
|
+
{
|
|
7861
|
+
actionName: 'canUseVoip',
|
|
7862
|
+
expectedEnabled: true,
|
|
7863
|
+
arePolicyRestrictionsSupported: false,
|
|
7864
|
+
},
|
|
7865
|
+
{
|
|
7866
|
+
actionName: 'canUseVoip',
|
|
7867
|
+
expectedEnabled: false,
|
|
7868
|
+
arePolicyRestrictionsSupported: true,
|
|
7869
|
+
},
|
|
7870
|
+
],
|
|
7871
|
+
({actionName, arePolicyRestrictionsSupported, expectedEnabled}) => {
|
|
7872
|
+
it(`${actionName} is ${expectedEnabled} when the call type is ${arePolicyRestrictionsSupported}`, () => {
|
|
7873
|
+
meeting.userDisplayHints = [];
|
|
7874
|
+
meeting.meetingInfo = {some: 'info'};
|
|
7875
|
+
sinon
|
|
7876
|
+
.stub(meeting, 'arePolicyRestrictionsSupported')
|
|
7877
|
+
.returns(arePolicyRestrictionsSupported);
|
|
7878
|
+
|
|
7879
|
+
meeting.updateMeetingActions();
|
|
7880
|
+
|
|
7881
|
+
assert.equal(meeting.inMeetingActions.get()[actionName], expectedEnabled);
|
|
7882
|
+
});
|
|
7883
|
+
}
|
|
7884
|
+
);
|
|
7885
|
+
|
|
7886
|
+
forEach(
|
|
7887
|
+
[
|
|
7888
|
+
{
|
|
7889
|
+
actionName: 'canShareFile',
|
|
7890
|
+
requiredDisplayHints: [DISPLAY_HINTS.SHARE_FILE],
|
|
7891
|
+
requiredPolicies: [SELF_POLICY.SUPPORT_FILE_SHARE],
|
|
7892
|
+
},
|
|
7893
|
+
{
|
|
7894
|
+
actionName: 'canShareApplication',
|
|
7895
|
+
requiredDisplayHints: [DISPLAY_HINTS.SHARE_APPLICATION],
|
|
7896
|
+
requiredPolicies: [SELF_POLICY.SUPPORT_APP_SHARE],
|
|
7897
|
+
},
|
|
7898
|
+
{
|
|
7899
|
+
actionName: 'canShareCamera',
|
|
7900
|
+
requiredDisplayHints: [DISPLAY_HINTS.SHARE_CAMERA],
|
|
7901
|
+
requiredPolicies: [SELF_POLICY.SUPPORT_CAMERA_SHARE],
|
|
7902
|
+
},
|
|
7903
|
+
{
|
|
7904
|
+
actionName: 'canBroadcastMessageToBreakout',
|
|
7905
|
+
requiredDisplayHints: [DISPLAY_HINTS.BROADCAST_MESSAGE_TO_BREAKOUT],
|
|
7906
|
+
requiredPolicies: [SELF_POLICY.SUPPORT_BROADCAST_MESSAGE],
|
|
7907
|
+
},
|
|
7908
|
+
{
|
|
7909
|
+
actionName: 'canShareDesktop',
|
|
7910
|
+
requiredDisplayHints: [DISPLAY_HINTS.SHARE_DESKTOP],
|
|
7911
|
+
requiredPolicies: [SELF_POLICY.SUPPORT_DESKTOP_SHARE],
|
|
7912
|
+
},
|
|
7913
|
+
{
|
|
7914
|
+
actionName: 'canTransferFile',
|
|
7915
|
+
requiredDisplayHints: [],
|
|
7916
|
+
requiredPolicies: [SELF_POLICY.SUPPORT_FILE_TRANSFER],
|
|
7917
|
+
},
|
|
7918
|
+
{
|
|
7919
|
+
actionName: 'canChat',
|
|
7920
|
+
requiredDisplayHints: [],
|
|
7921
|
+
requiredPolicies: [SELF_POLICY.SUPPORT_CHAT],
|
|
7922
|
+
},
|
|
7923
|
+
{
|
|
7924
|
+
actionName: 'canShareDesktop',
|
|
7925
|
+
requiredDisplayHints: [DISPLAY_HINTS.SHARE_DESKTOP],
|
|
7926
|
+
requiredPolicies: [],
|
|
7927
|
+
enableUnifiedMeetings: false,
|
|
7928
|
+
arePolicyRestrictionsSupported: false,
|
|
7929
|
+
},
|
|
7930
|
+
{
|
|
7931
|
+
actionName: 'canShareApplication',
|
|
7932
|
+
requiredDisplayHints: [DISPLAY_HINTS.SHARE_APPLICATION],
|
|
7933
|
+
requiredPolicies: [],
|
|
7934
|
+
enableUnifiedMeetings: false,
|
|
7935
|
+
arePolicyRestrictionsSupported: false,
|
|
7936
|
+
},
|
|
7937
|
+
{
|
|
7938
|
+
actionName: 'canAnnotate',
|
|
7939
|
+
requiredDisplayHints: [],
|
|
7940
|
+
requiredPolicies: [SELF_POLICY.SUPPORT_ANNOTATION],
|
|
7941
|
+
},
|
|
7942
|
+
],
|
|
7943
|
+
({
|
|
7944
|
+
actionName,
|
|
7945
|
+
requiredDisplayHints,
|
|
7946
|
+
requiredPolicies,
|
|
7947
|
+
enableUnifiedMeetings,
|
|
7948
|
+
meetingInfo,
|
|
7949
|
+
arePolicyRestrictionsSupported,
|
|
7950
|
+
}) => {
|
|
7951
|
+
it(`${actionName} is enabled when the conditions are met`, () => {
|
|
7952
|
+
meeting.userDisplayHints = requiredDisplayHints;
|
|
7953
|
+
meeting.selfUserPolicies = undefined;
|
|
7954
|
+
sinon
|
|
7955
|
+
.stub(meeting, 'arePolicyRestrictionsSupported')
|
|
7956
|
+
.returns(arePolicyRestrictionsSupported);
|
|
7957
|
+
|
|
7958
|
+
meeting.config.experimental.enableUnifiedMeetings = isUndefined(enableUnifiedMeetings)
|
|
7959
|
+
? true
|
|
7960
|
+
: enableUnifiedMeetings;
|
|
7961
|
+
|
|
7962
|
+
meeting.meetingInfo = isUndefined(meetingInfo) ? {some: 'info'} : meetingInfo;
|
|
7963
|
+
|
|
7964
|
+
if (requiredPolicies) {
|
|
7965
|
+
meeting.selfUserPolicies = {};
|
|
7966
|
+
}
|
|
7967
|
+
forEach(requiredPolicies, (policy) => {
|
|
7968
|
+
meeting.selfUserPolicies[policy] = true;
|
|
7969
|
+
});
|
|
7970
|
+
|
|
7971
|
+
meeting.updateMeetingActions();
|
|
7972
|
+
|
|
7973
|
+
assert.isTrue(meeting.inMeetingActions.get()[actionName]);
|
|
7974
|
+
});
|
|
7975
|
+
|
|
7976
|
+
if (requiredDisplayHints.length !== 0) {
|
|
7977
|
+
it(`${actionName} is disabled when the required display hints are missing`, () => {
|
|
7978
|
+
meeting.userDisplayHints = [];
|
|
7979
|
+
meeting.selfUserPolicies = undefined;
|
|
7980
|
+
|
|
7981
|
+
meeting.meetingInfo = isUndefined(meetingInfo) ? {some: 'info'} : meetingInfo;
|
|
7982
|
+
|
|
7983
|
+
if (requiredPolicies) {
|
|
7984
|
+
meeting.selfUserPolicies = {};
|
|
7985
|
+
}
|
|
7986
|
+
forEach(requiredPolicies, (policy) => {
|
|
7987
|
+
meeting.selfUserPolicies[policy] = true;
|
|
7988
|
+
});
|
|
7989
|
+
|
|
7990
|
+
meeting.updateMeetingActions();
|
|
7991
|
+
|
|
7992
|
+
assert.isFalse(meeting.inMeetingActions.get()[actionName]);
|
|
7993
|
+
});
|
|
7994
|
+
}
|
|
7995
|
+
|
|
7996
|
+
it(`${actionName} is disabled when the required policies are missing`, () => {
|
|
7997
|
+
meeting.userDisplayHints = requiredDisplayHints;
|
|
7998
|
+
meeting.selfUserPolicies = undefined;
|
|
7999
|
+
|
|
8000
|
+
if (requiredPolicies) {
|
|
8001
|
+
meeting.selfUserPolicies = {};
|
|
8002
|
+
}
|
|
8003
|
+
meeting.meetingInfo = isUndefined(meetingInfo) ? {some: 'info'} : meetingInfo;
|
|
8004
|
+
|
|
8005
|
+
meeting.updateMeetingActions();
|
|
8006
|
+
|
|
8007
|
+
assert.isFalse(meeting.inMeetingActions.get()[actionName]);
|
|
8008
|
+
});
|
|
8009
|
+
}
|
|
8010
|
+
);
|
|
8011
|
+
|
|
8012
|
+
forEach(
|
|
8013
|
+
[
|
|
8014
|
+
{
|
|
8015
|
+
meetingInfo: {
|
|
8016
|
+
video: {
|
|
8017
|
+
supportHDV: true,
|
|
8018
|
+
supportHQV: true,
|
|
8019
|
+
},
|
|
8020
|
+
},
|
|
8021
|
+
selfUserPolicies: {
|
|
8022
|
+
[SELF_POLICY.SUPPORT_HDV]: true,
|
|
8023
|
+
[SELF_POLICY.SUPPORT_HQV]: true,
|
|
8024
|
+
},
|
|
8025
|
+
expectedActions: {
|
|
8026
|
+
supportHQV: true,
|
|
8027
|
+
supportHDV: true,
|
|
8028
|
+
},
|
|
8029
|
+
},
|
|
8030
|
+
{
|
|
8031
|
+
meetingInfo: {
|
|
8032
|
+
video: {
|
|
8033
|
+
supportHDV: false,
|
|
8034
|
+
supportHQV: false,
|
|
8035
|
+
},
|
|
8036
|
+
},
|
|
8037
|
+
selfUserPolicies: {
|
|
8038
|
+
[SELF_POLICY.SUPPORT_HDV]: true,
|
|
8039
|
+
[SELF_POLICY.SUPPORT_HQV]: true,
|
|
8040
|
+
},
|
|
8041
|
+
expectedActions: {
|
|
8042
|
+
supportHQV: false,
|
|
8043
|
+
supportHDV: false,
|
|
8044
|
+
},
|
|
8045
|
+
},
|
|
8046
|
+
{
|
|
8047
|
+
meetingInfo: {
|
|
8048
|
+
video: {
|
|
8049
|
+
supportHDV: true,
|
|
8050
|
+
supportHQV: true,
|
|
8051
|
+
},
|
|
8052
|
+
},
|
|
8053
|
+
selfUserPolicies: {
|
|
8054
|
+
[SELF_POLICY.SUPPORT_HDV]: false,
|
|
8055
|
+
[SELF_POLICY.SUPPORT_HQV]: false,
|
|
8056
|
+
},
|
|
8057
|
+
expectedActions: {
|
|
8058
|
+
supportHQV: false,
|
|
8059
|
+
supportHDV: false,
|
|
8060
|
+
},
|
|
8061
|
+
},
|
|
8062
|
+
{
|
|
8063
|
+
meetingInfo: undefined,
|
|
8064
|
+
selfUserPolicies: {},
|
|
8065
|
+
expectedActions: {
|
|
8066
|
+
supportHQV: true,
|
|
8067
|
+
supportHDV: true,
|
|
8068
|
+
},
|
|
8069
|
+
},
|
|
8070
|
+
{
|
|
8071
|
+
meetingInfo: {some: 'data'},
|
|
8072
|
+
selfUserPolicies: undefined,
|
|
8073
|
+
expectedActions: {
|
|
8074
|
+
supportHQV: true,
|
|
8075
|
+
supportHDV: true,
|
|
8076
|
+
},
|
|
8077
|
+
},
|
|
8078
|
+
],
|
|
8079
|
+
({meetingInfo, selfUserPolicies, expectedActions}) => {
|
|
8080
|
+
it(`expectedActions are ${JSON.stringify(
|
|
8081
|
+
expectedActions
|
|
8082
|
+
)} when policies are ${JSON.stringify(
|
|
8083
|
+
selfUserPolicies
|
|
8084
|
+
)} and meetingInfo is ${JSON.stringify(meetingInfo)}`, () => {
|
|
8085
|
+
meeting.meetingInfo = meetingInfo;
|
|
8086
|
+
meeting.selfUserPolicies = selfUserPolicies;
|
|
8087
|
+
|
|
8088
|
+
meeting.updateMeetingActions();
|
|
8089
|
+
|
|
8090
|
+
assert.deepEqual(
|
|
8091
|
+
{
|
|
8092
|
+
supportHDV: meeting.inMeetingActions.supportHDV,
|
|
8093
|
+
supportHQV: meeting.inMeetingActions.supportHQV,
|
|
8094
|
+
},
|
|
8095
|
+
expectedActions
|
|
8096
|
+
);
|
|
8097
|
+
});
|
|
8098
|
+
}
|
|
8099
|
+
);
|
|
8100
|
+
|
|
8101
|
+
forEach(
|
|
8102
|
+
[
|
|
8103
|
+
// policies supported and enforce is true
|
|
8104
|
+
{
|
|
8105
|
+
meetingInfo: {video: {}},
|
|
8106
|
+
selfUserPolicies: {
|
|
8107
|
+
[SELF_POLICY.ENFORCE_VIRTUAL_BACKGROUND]: true,
|
|
8108
|
+
},
|
|
8109
|
+
expectedActions: {
|
|
8110
|
+
enforceVirtualBackground: true,
|
|
8111
|
+
},
|
|
8112
|
+
},
|
|
8113
|
+
// policies supported and enforce is false
|
|
8114
|
+
{
|
|
8115
|
+
meetingInfo: {video: {}},
|
|
8116
|
+
selfUserPolicies: {
|
|
8117
|
+
[SELF_POLICY.ENFORCE_VIRTUAL_BACKGROUND]: false,
|
|
8118
|
+
},
|
|
8119
|
+
expectedActions: {
|
|
8120
|
+
enforceVirtualBackground: false,
|
|
8121
|
+
},
|
|
8122
|
+
},
|
|
8123
|
+
// policies not supported but enforce is true
|
|
8124
|
+
{
|
|
8125
|
+
meetingInfo: undefined,
|
|
8126
|
+
selfUserPolicies: {
|
|
8127
|
+
[SELF_POLICY.ENFORCE_VIRTUAL_BACKGROUND]: true,
|
|
8128
|
+
},
|
|
8129
|
+
expectedActions: {
|
|
8130
|
+
enforceVirtualBackground: false,
|
|
8131
|
+
},
|
|
8132
|
+
},
|
|
8133
|
+
],
|
|
8134
|
+
({meetingInfo, selfUserPolicies, expectedActions}) => {
|
|
8135
|
+
it(`expectedActions are ${JSON.stringify(
|
|
8136
|
+
expectedActions
|
|
8137
|
+
)} when policies are ${JSON.stringify(
|
|
8138
|
+
selfUserPolicies
|
|
8139
|
+
)} and meetingInfo is ${JSON.stringify(meetingInfo)}`, () => {
|
|
8140
|
+
meeting.meetingInfo = meetingInfo;
|
|
8141
|
+
meeting.selfUserPolicies = selfUserPolicies;
|
|
8142
|
+
|
|
8143
|
+
meeting.updateMeetingActions();
|
|
8144
|
+
|
|
8145
|
+
assert.deepEqual(
|
|
8146
|
+
{
|
|
8147
|
+
enforceVirtualBackground: meeting.inMeetingActions.enforceVirtualBackground,
|
|
8148
|
+
},
|
|
8149
|
+
expectedActions
|
|
8150
|
+
);
|
|
8151
|
+
});
|
|
8152
|
+
}
|
|
8153
|
+
);
|
|
8154
|
+
|
|
8155
|
+
it('canUseVoip is disabled when the required policies are missing', () => {
|
|
8156
|
+
meeting.userDisplayHints = [DISPLAY_HINTS.VOIP_IS_ENABLED];
|
|
8157
|
+
meeting.selfUserPolicies = {};
|
|
8158
|
+
meeting.meetingInfo.supportVoIP = true;
|
|
8159
|
+
|
|
8160
|
+
meeting.updateMeetingActions();
|
|
8161
|
+
|
|
8162
|
+
assert.isFalse(meeting.inMeetingActions.get()['canUseVoip']);
|
|
8163
|
+
});
|
|
8164
|
+
|
|
8165
|
+
it('canUseVoip is enabled based on api info when the conditions are met', () => {
|
|
8166
|
+
meeting.userDisplayHints = undefined;
|
|
8167
|
+
meeting.selfUserPolicies = {[SELF_POLICY.SUPPORT_VOIP]: true};
|
|
8168
|
+
meeting.meetingInfo.supportVoIP = true;
|
|
8169
|
+
|
|
8170
|
+
meeting.updateMeetingActions();
|
|
8171
|
+
|
|
8172
|
+
assert.isTrue(meeting.inMeetingActions.get()['canUseVoip']);
|
|
8173
|
+
});
|
|
8174
|
+
|
|
8175
|
+
it('canUseVoip is enabled based on api info when the conditions are met - no display hints', () => {
|
|
8176
|
+
meeting.userDisplayHints = [];
|
|
8177
|
+
meeting.selfUserPolicies = {[SELF_POLICY.SUPPORT_VOIP]: true};
|
|
8178
|
+
meeting.meetingInfo.supportVoIP = true;
|
|
8179
|
+
|
|
8180
|
+
meeting.updateMeetingActions();
|
|
8181
|
+
|
|
8182
|
+
assert.isTrue(meeting.inMeetingActions.get()['canUseVoip']);
|
|
8183
|
+
});
|
|
8184
|
+
|
|
8185
|
+
it('canUseVoip is enabled when there is no meeting info', () => {
|
|
8186
|
+
meeting.updateMeetingActions();
|
|
8187
|
+
|
|
8188
|
+
assert.isTrue(meeting.inMeetingActions.get()['canUseVoip']);
|
|
8189
|
+
});
|
|
8190
|
+
|
|
8191
|
+
it('canUseVoip is enabled when it is a locus call', () => {
|
|
8192
|
+
meeting.meetingInfo = {some: 'info'};
|
|
8193
|
+
meeting.type = 'CALL';
|
|
8194
|
+
|
|
8195
|
+
meeting.updateMeetingActions();
|
|
8196
|
+
|
|
8197
|
+
assert.isTrue(meeting.inMeetingActions.get()['canUseVoip']);
|
|
8198
|
+
});
|
|
8199
|
+
|
|
8200
|
+
it('canUseVoip is disabled based on api info when supportVoip is false', () => {
|
|
8201
|
+
meeting.userDisplayHints = undefined;
|
|
8202
|
+
meeting.selfUserPolicies = {[SELF_POLICY.SUPPORT_VOIP]: true};
|
|
8203
|
+
meeting.meetingInfo.supportVoIP = false;
|
|
8204
|
+
|
|
8205
|
+
meeting.updateMeetingActions();
|
|
8206
|
+
|
|
8207
|
+
assert.isFalse(meeting.inMeetingActions.get()['canUseVoip']);
|
|
8208
|
+
});
|
|
8209
|
+
|
|
8210
|
+
it('canUseVoip is disabled based on api info when the required policies are missing', () => {
|
|
8211
|
+
meeting.userDisplayHints = undefined;
|
|
8212
|
+
meeting.selfUserPolicies = {};
|
|
8213
|
+
meeting.meetingInfo.supportVoIP = true;
|
|
8214
|
+
|
|
8215
|
+
meeting.updateMeetingActions();
|
|
8216
|
+
|
|
8217
|
+
assert.isFalse(meeting.inMeetingActions.get()['canUseVoip']);
|
|
8218
|
+
});
|
|
8219
|
+
|
|
8220
|
+
it('canUseVoip is enabled when there are no policies', () => {
|
|
8221
|
+
meeting.userDisplayHints = [DISPLAY_HINTS.VOIP_IS_ENABLED];
|
|
8222
|
+
meeting.selfUserPolicies = undefined;
|
|
8223
|
+
meeting.meetingInfo.supportVoIP = false;
|
|
8224
|
+
|
|
8225
|
+
meeting.updateMeetingActions();
|
|
8226
|
+
|
|
8227
|
+
assert.isTrue(meeting.inMeetingActions.get()['canUseVoip']);
|
|
8228
|
+
});
|
|
8229
|
+
|
|
8230
|
+
forEach(
|
|
8231
|
+
[
|
|
8232
|
+
{
|
|
8233
|
+
meetingInfo: {},
|
|
8234
|
+
selfUserPolicies: {
|
|
8235
|
+
[SELF_POLICY.SUPPORT_VIDEO]: true,
|
|
8236
|
+
},
|
|
8237
|
+
expectedActions: {
|
|
8238
|
+
canDoVideo: true,
|
|
8239
|
+
},
|
|
8240
|
+
},
|
|
8241
|
+
{
|
|
8242
|
+
meetingInfo: {},
|
|
8243
|
+
selfUserPolicies: {
|
|
8244
|
+
[SELF_POLICY.SUPPORT_VIDEO]: false,
|
|
8245
|
+
},
|
|
8246
|
+
expectedActions: {
|
|
8247
|
+
canDoVideo: true,
|
|
8248
|
+
},
|
|
8249
|
+
},
|
|
8250
|
+
{
|
|
8251
|
+
meetingInfo: {some: 'data'},
|
|
8252
|
+
selfUserPolicies: {
|
|
8253
|
+
[SELF_POLICY.SUPPORT_VIDEO]: true,
|
|
8254
|
+
},
|
|
8255
|
+
expectedActions: {
|
|
8256
|
+
canDoVideo: false,
|
|
8257
|
+
},
|
|
8258
|
+
},
|
|
8259
|
+
{
|
|
8260
|
+
meetingInfo: {some: 'data'},
|
|
8261
|
+
selfUserPolicies: undefined,
|
|
8262
|
+
expectedActions: {
|
|
8263
|
+
canDoVideo: true,
|
|
8264
|
+
},
|
|
8265
|
+
},
|
|
8266
|
+
{
|
|
8267
|
+
meetingInfo: {
|
|
8268
|
+
video: {},
|
|
8269
|
+
},
|
|
8270
|
+
selfUserPolicies: {
|
|
8271
|
+
[SELF_POLICY.SUPPORT_VIDEO]: true,
|
|
8272
|
+
},
|
|
8273
|
+
expectedActions: {
|
|
8274
|
+
canDoVideo: true,
|
|
8275
|
+
},
|
|
8276
|
+
},
|
|
8277
|
+
{
|
|
8278
|
+
meetingInfo: undefined,
|
|
8279
|
+
selfUserPolicies: {},
|
|
8280
|
+
expectedActions: {
|
|
8281
|
+
canDoVideo: true,
|
|
8282
|
+
},
|
|
8283
|
+
},
|
|
8284
|
+
{
|
|
8285
|
+
meetingInfo: {
|
|
8286
|
+
video: {},
|
|
8287
|
+
},
|
|
8288
|
+
selfUserPolicies: {
|
|
8289
|
+
[SELF_POLICY.SUPPORT_VIDEO]: false,
|
|
8290
|
+
},
|
|
8291
|
+
expectedActions: {
|
|
8292
|
+
canDoVideo: false,
|
|
8293
|
+
},
|
|
8294
|
+
},
|
|
8295
|
+
],
|
|
8296
|
+
({meetingInfo, selfUserPolicies, expectedActions}) => {
|
|
8297
|
+
it(`has expectedActions ${JSON.stringify(
|
|
8298
|
+
expectedActions
|
|
8299
|
+
)} when policies are ${JSON.stringify(
|
|
8300
|
+
selfUserPolicies
|
|
8301
|
+
)} and meetingInfo is ${JSON.stringify(meetingInfo)}`, () => {
|
|
8302
|
+
meeting.meetingInfo = meetingInfo;
|
|
8303
|
+
meeting.selfUserPolicies = selfUserPolicies;
|
|
8304
|
+
meeting.config.experimental.enableUnifiedMeetings = true;
|
|
8305
|
+
|
|
8306
|
+
meeting.updateMeetingActions();
|
|
8307
|
+
|
|
8308
|
+
assert.deepEqual(
|
|
8309
|
+
{
|
|
8310
|
+
canDoVideo: meeting.inMeetingActions.canDoVideo,
|
|
8311
|
+
},
|
|
8312
|
+
expectedActions
|
|
8313
|
+
);
|
|
8314
|
+
});
|
|
8315
|
+
}
|
|
8316
|
+
);
|
|
8317
|
+
|
|
8318
|
+
it('correctly updates the meeting actions', () => {
|
|
8319
|
+
// Due to import tree issues, hasHints must be stubed within the scope of the `it`.
|
|
8320
|
+
const restorableHasHints = ControlsOptionsUtil.hasHints;
|
|
8321
|
+
ControlsOptionsUtil.hasHints = sinon.stub().returns(true);
|
|
8322
|
+
ControlsOptionsUtil.hasPolicies = sinon.stub().returns(true);
|
|
8323
|
+
|
|
8324
|
+
const selfUserPolicies = {a: true};
|
|
8325
|
+
meeting.selfUserPolicies = {a: true};
|
|
8326
|
+
const userDisplayHints = ['LOCK_CONTROL_UNLOCK'];
|
|
8327
|
+
meeting.userDisplayHints = ['LOCK_CONTROL_UNLOCK'];
|
|
8328
|
+
meeting.meetingInfo.supportVoIP = true;
|
|
8329
|
+
|
|
8330
|
+
meeting.updateMeetingActions();
|
|
8331
|
+
|
|
8332
|
+
assert.calledWith(canUserLockSpy, userDisplayHints);
|
|
8333
|
+
assert.calledWith(canUserUnlockSpy, userDisplayHints);
|
|
8334
|
+
assert.calledWith(canUserStartSpy, userDisplayHints);
|
|
8335
|
+
assert.calledWith(canUserStopSpy, userDisplayHints);
|
|
8336
|
+
assert.calledWith(canUserPauseSpy, userDisplayHints);
|
|
8337
|
+
assert.calledWith(canUserResumeSpy, userDisplayHints);
|
|
8338
|
+
assert.calledWith(canSetMuteOnEntrySpy, userDisplayHints);
|
|
8339
|
+
assert.calledWith(canUnsetMuteOnEntrySpy, userDisplayHints);
|
|
8340
|
+
assert.calledWith(canSetDisallowUnmuteSpy, userDisplayHints);
|
|
8341
|
+
assert.calledWith(canUnsetDisallowUnmuteSpy, userDisplayHints);
|
|
8342
|
+
assert.calledWith(canUserRaiseHandSpy, userDisplayHints);
|
|
8343
|
+
assert.calledWith(bothLeaveAndEndMeetingAvailableSpy, userDisplayHints);
|
|
8344
|
+
assert.calledWith(canUserLowerAllHandsSpy, userDisplayHints);
|
|
8345
|
+
assert.calledWith(canUserLowerSomeoneElsesHandSpy, userDisplayHints);
|
|
8346
|
+
assert.calledWith(waitingForOthersToJoinSpy, userDisplayHints);
|
|
8347
|
+
assert.calledWith(canSendReactionsSpy, null, userDisplayHints);
|
|
8348
|
+
assert.calledWith(canUserRenameSelfAndObservedSpy, userDisplayHints);
|
|
8349
|
+
assert.calledWith(canUserRenameOthersSpy, userDisplayHints);
|
|
8350
|
+
assert.calledWith(canShareWhiteBoardSpy, userDisplayHints);
|
|
8351
|
+
|
|
8352
|
+
assert.calledWith(ControlsOptionsUtil.hasHints, {
|
|
8353
|
+
requiredHints: [DISPLAY_HINTS.MUTE_ALL],
|
|
8354
|
+
displayHints: userDisplayHints,
|
|
8355
|
+
});
|
|
8356
|
+
assert.calledWith(ControlsOptionsUtil.hasHints, {
|
|
8357
|
+
requiredHints: [DISPLAY_HINTS.UNMUTE_ALL],
|
|
8358
|
+
displayHints: userDisplayHints,
|
|
8359
|
+
});
|
|
8360
|
+
assert.calledWith(ControlsOptionsUtil.hasHints, {
|
|
8361
|
+
requiredHints: [DISPLAY_HINTS.ENABLE_HARD_MUTE],
|
|
8362
|
+
displayHints: userDisplayHints,
|
|
8363
|
+
});
|
|
8364
|
+
assert.calledWith(ControlsOptionsUtil.hasHints, {
|
|
8365
|
+
requiredHints: [DISPLAY_HINTS.DISABLE_HARD_MUTE],
|
|
8366
|
+
displayHints: userDisplayHints,
|
|
8367
|
+
});
|
|
8368
|
+
assert.calledWith(ControlsOptionsUtil.hasHints, {
|
|
8369
|
+
requiredHints: [DISPLAY_HINTS.ENABLE_MUTE_ON_ENTRY],
|
|
8370
|
+
displayHints: userDisplayHints,
|
|
8371
|
+
});
|
|
8372
|
+
assert.calledWith(ControlsOptionsUtil.hasHints, {
|
|
8373
|
+
requiredHints: [DISPLAY_HINTS.DISABLE_MUTE_ON_ENTRY],
|
|
8374
|
+
displayHints: userDisplayHints,
|
|
8375
|
+
});
|
|
8376
|
+
assert.calledWith(ControlsOptionsUtil.hasHints, {
|
|
8377
|
+
requiredHints: [DISPLAY_HINTS.ENABLE_REACTIONS],
|
|
8378
|
+
displayHints: userDisplayHints,
|
|
8379
|
+
});
|
|
8380
|
+
assert.calledWith(ControlsOptionsUtil.hasHints, {
|
|
8381
|
+
requiredHints: [DISPLAY_HINTS.DISABLE_REACTIONS],
|
|
8382
|
+
displayHints: userDisplayHints,
|
|
8383
|
+
});
|
|
8384
|
+
assert.calledWith(ControlsOptionsUtil.hasHints, {
|
|
8385
|
+
requiredHints: [DISPLAY_HINTS.ENABLE_SHOW_DISPLAY_NAME],
|
|
8386
|
+
displayHints: userDisplayHints,
|
|
8387
|
+
});
|
|
8388
|
+
assert.calledWith(ControlsOptionsUtil.hasHints, {
|
|
8389
|
+
requiredHints: [DISPLAY_HINTS.DISABLE_SHOW_DISPLAY_NAME],
|
|
8390
|
+
displayHints: userDisplayHints,
|
|
8391
|
+
});
|
|
8392
|
+
assert.calledWith(ControlsOptionsUtil.hasHints, {
|
|
8393
|
+
requiredHints: [DISPLAY_HINTS.SHARE_CONTROL],
|
|
8394
|
+
displayHints: userDisplayHints,
|
|
8395
|
+
});
|
|
8396
|
+
assert.calledWith(ControlsOptionsUtil.hasHints, {
|
|
8397
|
+
requiredHints: [DISPLAY_HINTS.ENABLE_VIEW_THE_PARTICIPANT_LIST],
|
|
8398
|
+
displayHints: userDisplayHints,
|
|
8399
|
+
});
|
|
8400
|
+
assert.calledWith(ControlsOptionsUtil.hasHints, {
|
|
8401
|
+
requiredHints: [DISPLAY_HINTS.DISABLE_VIEW_THE_PARTICIPANT_LIST],
|
|
8402
|
+
displayHints: userDisplayHints,
|
|
8403
|
+
});
|
|
8404
|
+
assert.calledWith(ControlsOptionsUtil.hasHints, {
|
|
8405
|
+
requiredHints: [DISPLAY_HINTS.SHARE_FILE],
|
|
8406
|
+
displayHints: userDisplayHints,
|
|
8407
|
+
});
|
|
8408
|
+
assert.calledWith(ControlsOptionsUtil.hasPolicies, {
|
|
8409
|
+
requiredPolicies: [SELF_POLICY.SUPPORT_FILE_SHARE],
|
|
8410
|
+
policies: selfUserPolicies,
|
|
8411
|
+
});
|
|
8412
|
+
assert.calledWith(ControlsOptionsUtil.hasHints, {
|
|
8413
|
+
requiredHints: [DISPLAY_HINTS.SHARE_APPLICATION],
|
|
8414
|
+
displayHints: userDisplayHints,
|
|
8415
|
+
});
|
|
8416
|
+
assert.calledWith(ControlsOptionsUtil.hasPolicies, {
|
|
8417
|
+
requiredPolicies: [SELF_POLICY.SUPPORT_APP_SHARE],
|
|
8418
|
+
policies: selfUserPolicies,
|
|
8419
|
+
});
|
|
8420
|
+
assert.calledWith(ControlsOptionsUtil.hasHints, {
|
|
8421
|
+
requiredHints: [DISPLAY_HINTS.SHARE_CAMERA],
|
|
8422
|
+
displayHints: userDisplayHints,
|
|
8423
|
+
});
|
|
8424
|
+
assert.calledWith(ControlsOptionsUtil.hasPolicies, {
|
|
8425
|
+
requiredPolicies: [SELF_POLICY.SUPPORT_CAMERA_SHARE],
|
|
8426
|
+
policies: selfUserPolicies,
|
|
8427
|
+
});
|
|
8428
|
+
assert.calledWith(ControlsOptionsUtil.hasHints, {
|
|
8429
|
+
requiredHints: [DISPLAY_HINTS.SHARE_DESKTOP],
|
|
8430
|
+
displayHints: userDisplayHints,
|
|
8431
|
+
});
|
|
8432
|
+
assert.calledWith(ControlsOptionsUtil.hasPolicies, {
|
|
8433
|
+
requiredPolicies: [SELF_POLICY.SUPPORT_DESKTOP_SHARE],
|
|
8434
|
+
policies: selfUserPolicies,
|
|
8435
|
+
});
|
|
8436
|
+
assert.calledWith(ControlsOptionsUtil.hasHints, {
|
|
8437
|
+
requiredHints: [DISPLAY_HINTS.SHARE_CONTENT],
|
|
8438
|
+
displayHints: userDisplayHints,
|
|
8439
|
+
});
|
|
8440
|
+
assert.calledWith(ControlsOptionsUtil.hasPolicies, {
|
|
8441
|
+
requiredPolicies: [SELF_POLICY.SUPPORT_VOIP],
|
|
8442
|
+
policies: selfUserPolicies,
|
|
8443
|
+
});
|
|
7350
8444
|
|
|
7351
8445
|
assert.calledWith(updateMeetingActionsSpy);
|
|
7352
8446
|
assert.calledWith(setRecordingDisplayHintsSpy, userDisplayHints);
|
|
@@ -9465,7 +10559,7 @@ describe('plugin-meetings', () => {
|
|
|
9465
10559
|
});
|
|
9466
10560
|
});
|
|
9467
10561
|
|
|
9468
|
-
describe('#
|
|
10562
|
+
describe('#getPermissionTokenExpiryInfo', () => {
|
|
9469
10563
|
let now;
|
|
9470
10564
|
let clock;
|
|
9471
10565
|
|
|
@@ -9481,63 +10575,65 @@ describe('plugin-meetings', () => {
|
|
|
9481
10575
|
})
|
|
9482
10576
|
|
|
9483
10577
|
it('should return undefined if exp is undefined', () => {
|
|
9484
|
-
assert.equal(meeting.
|
|
10578
|
+
assert.equal(meeting.getPermissionTokenExpiryInfo(), undefined)
|
|
9485
10579
|
});
|
|
9486
10580
|
|
|
9487
10581
|
it('should return the expected positive exp', () => {
|
|
9488
10582
|
// set permission token as now + 1 sec
|
|
9489
|
-
|
|
9490
|
-
|
|
10583
|
+
const expiryTime = now + 1000;
|
|
10584
|
+
meeting.permissionTokenPayload = {exp: (expiryTime).toString()};
|
|
10585
|
+
assert.deepEqual(meeting.getPermissionTokenExpiryInfo(), {timeLeft: 1, expiryTime: Number(expiryTime), currentTime: now});
|
|
9491
10586
|
});
|
|
9492
10587
|
|
|
9493
10588
|
it('should return the expected negative exp', () => {
|
|
9494
10589
|
// set permission token as now - 1 sec
|
|
9495
|
-
|
|
9496
|
-
|
|
10590
|
+
const expiryTime = now - 1000;
|
|
10591
|
+
meeting.permissionTokenPayload = {exp: (expiryTime).toString()};
|
|
10592
|
+
assert.deepEqual(meeting.getPermissionTokenExpiryInfo(), {timeLeft: -1, expiryTime: Number(expiryTime), currentTime: now});
|
|
9497
10593
|
});
|
|
9498
10594
|
});
|
|
9499
10595
|
|
|
9500
10596
|
describe('#checkAndRefreshPermissionToken', () => {
|
|
9501
10597
|
it('should not fire refreshPermissionToken if permissionToken is not defined', async() => {
|
|
9502
|
-
meeting.
|
|
10598
|
+
meeting.getPermissionTokenExpiryInfo = sinon.stub().returns(undefined)
|
|
9503
10599
|
meeting.refreshPermissionToken = sinon.stub().returns(Promise.resolve('test return value'));
|
|
9504
10600
|
|
|
9505
10601
|
const returnValue = await meeting.checkAndRefreshPermissionToken(10, 'ttl-join');
|
|
9506
10602
|
|
|
9507
|
-
assert.calledOnce(meeting.
|
|
10603
|
+
assert.calledOnce(meeting.getPermissionTokenExpiryInfo);
|
|
9508
10604
|
assert.notCalled(meeting.refreshPermissionToken);
|
|
9509
10605
|
assert.equal(returnValue, undefined);
|
|
9510
10606
|
});
|
|
9511
10607
|
|
|
9512
10608
|
it('should fire refreshPermissionToken if time left is below 10sec', async() => {
|
|
9513
|
-
meeting.
|
|
10609
|
+
meeting.getPermissionTokenExpiryInfo = sinon.stub().returns({timeLeft: 9, expiryTime: 122132, currentTime: Date.now()})
|
|
9514
10610
|
meeting.refreshPermissionToken = sinon.stub().returns(Promise.resolve('test return value'));
|
|
9515
10611
|
|
|
9516
10612
|
const returnValue = await meeting.checkAndRefreshPermissionToken(10, 'ttl-join');
|
|
9517
10613
|
|
|
9518
|
-
assert.calledOnce(meeting.
|
|
10614
|
+
assert.calledOnce(meeting.getPermissionTokenExpiryInfo);
|
|
9519
10615
|
assert.calledOnceWithExactly(meeting.refreshPermissionToken, 'ttl-join');
|
|
9520
10616
|
assert.equal(returnValue, 'test return value');
|
|
9521
10617
|
});
|
|
9522
10618
|
|
|
9523
10619
|
it('should fire refreshPermissionToken if time left is equal 10sec', async () => {
|
|
9524
|
-
meeting.
|
|
10620
|
+
meeting.getPermissionTokenExpiryInfo = sinon.stub().returns({timeLeft: 10, expiryTime: 122132, currentTime: Date.now()})
|
|
9525
10621
|
meeting.refreshPermissionToken = sinon.stub().returns(Promise.resolve('test return value'));
|
|
9526
10622
|
|
|
9527
10623
|
const returnValue = await meeting.checkAndRefreshPermissionToken(10, 'ttl-join');
|
|
9528
10624
|
|
|
9529
|
-
assert.calledOnce(meeting.
|
|
10625
|
+
assert.calledOnce(meeting.getPermissionTokenExpiryInfo);
|
|
9530
10626
|
assert.calledOnceWithExactly(meeting.refreshPermissionToken, 'ttl-join');
|
|
9531
10627
|
assert.equal(returnValue, 'test return value');
|
|
9532
10628
|
});
|
|
9533
10629
|
|
|
9534
10630
|
it('should not fire refreshPermissionToken if time left is higher than 10sec', async () => {
|
|
9535
|
-
meeting.
|
|
10631
|
+
meeting.getPermissionTokenExpiryInfo = sinon.stub().returns({timeLeft: 11, expiryTime: 122132, currentTime: Date.now()})
|
|
9536
10632
|
meeting.refreshPermissionToken = sinon.stub().returns(Promise.resolve('test return value'));
|
|
9537
10633
|
|
|
9538
10634
|
const returnValue = await meeting.checkAndRefreshPermissionToken(10, 'ttl-join');
|
|
9539
10635
|
|
|
9540
|
-
assert.calledOnce(meeting.
|
|
10636
|
+
assert.calledOnce(meeting.getPermissionTokenExpiryInfo);
|
|
9541
10637
|
assert.notCalled(meeting.refreshPermissionToken);
|
|
9542
10638
|
assert.equal(returnValue, undefined);
|
|
9543
10639
|
});
|