@webex/plugin-meetings 3.0.0-beta.1 → 3.0.0-beta.2
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/common/errors/webex-errors.js +5 -29
- package/dist/common/errors/webex-errors.js.map +1 -1
- package/dist/constants.js +15 -74
- package/dist/constants.js.map +1 -1
- package/dist/media/index.js +68 -213
- package/dist/media/index.js.map +1 -1
- package/dist/media/internal-media-core-wrapper.js +22 -0
- package/dist/media/internal-media-core-wrapper.js.map +1 -0
- package/dist/media/properties.js +20 -25
- package/dist/media/properties.js.map +1 -1
- package/dist/media/util.js +0 -27
- package/dist/media/util.js.map +1 -1
- package/dist/meeting/index.js +694 -432
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/request.js +1 -0
- package/dist/meeting/request.js.map +1 -1
- package/dist/meeting/util.js +3 -44
- package/dist/meeting/util.js.map +1 -1
- package/dist/meetings/index.js +64 -5
- package/dist/meetings/index.js.map +1 -1
- package/dist/meetings/util.js +24 -1
- package/dist/meetings/util.js.map +1 -1
- package/dist/members/index.js +68 -0
- package/dist/members/index.js.map +1 -1
- package/dist/multistream/mediaRequestManager.js +132 -0
- package/dist/multistream/mediaRequestManager.js.map +1 -0
- package/dist/multistream/multistreamMedia.js +116 -0
- package/dist/multistream/multistreamMedia.js.map +1 -0
- package/dist/multistream/receiveSlot.js +209 -0
- package/dist/multistream/receiveSlot.js.map +1 -0
- package/dist/multistream/receiveSlotManager.js +195 -0
- package/dist/multistream/receiveSlotManager.js.map +1 -0
- package/dist/multistream/remoteMedia.js +284 -0
- package/dist/multistream/remoteMedia.js.map +1 -0
- package/dist/multistream/remoteMediaGroup.js +243 -0
- package/dist/multistream/remoteMediaGroup.js.map +1 -0
- package/dist/multistream/remoteMediaManager.js +1113 -0
- package/dist/multistream/remoteMediaManager.js.map +1 -0
- package/dist/reconnection-manager/index.js +109 -130
- package/dist/reconnection-manager/index.js.map +1 -1
- package/dist/roap/index.js +57 -240
- package/dist/roap/index.js.map +1 -1
- package/dist/roap/request.js +2 -114
- package/dist/roap/request.js.map +1 -1
- package/dist/roap/turnDiscovery.js +11 -5
- package/dist/roap/turnDiscovery.js.map +1 -1
- package/dist/statsAnalyzer/global.js +2 -0
- package/dist/statsAnalyzer/global.js.map +1 -1
- package/dist/statsAnalyzer/index.js +39 -36
- package/dist/statsAnalyzer/index.js.map +1 -1
- package/package.json +20 -19
- package/src/common/errors/webex-errors.js +0 -18
- package/src/constants.ts +139 -180
- package/src/media/index.js +60 -194
- package/src/media/internal-media-core-wrapper.ts +9 -0
- package/src/media/properties.js +19 -25
- package/src/media/util.js +0 -22
- package/src/meeting/index.js +565 -320
- package/src/meeting/request.js +1 -0
- package/src/meeting/util.js +3 -46
- package/src/meetings/index.js +30 -1
- package/src/meetings/util.js +23 -2
- package/src/members/index.js +48 -0
- package/src/multistream/mediaRequestManager.ts +164 -0
- package/src/multistream/multistreamMedia.ts +92 -0
- package/src/multistream/receiveSlot.ts +141 -0
- package/src/multistream/receiveSlotManager.ts +142 -0
- package/src/multistream/remoteMedia.ts +219 -0
- package/src/multistream/remoteMediaGroup.ts +224 -0
- package/src/multistream/remoteMediaManager.ts +911 -0
- package/src/reconnection-manager/index.js +40 -53
- package/src/roap/index.js +47 -207
- package/src/roap/request.js +1 -72
- package/src/roap/turnDiscovery.ts +12 -6
- package/src/statsAnalyzer/global.js +2 -0
- package/src/statsAnalyzer/index.js +32 -46
- package/test/integration/spec/journey.js +1 -1
- package/test/unit/spec/media/index.ts +223 -0
- package/test/unit/spec/media/properties.ts +73 -82
- package/test/unit/spec/meeting/effectsState.js +1 -3
- package/test/unit/spec/meeting/index.js +420 -228
- package/test/unit/spec/meeting/muteState.js +7 -0
- package/test/unit/spec/meeting/utils.js +61 -2
- package/test/unit/spec/meetings/index.js +0 -4
- package/test/unit/spec/members/index.js +164 -2
- package/test/unit/spec/multistream/mediaRequestManager.ts +511 -0
- package/test/unit/spec/multistream/receiveSlot.ts +104 -0
- package/test/unit/spec/multistream/receiveSlotManager.ts +173 -0
- package/test/unit/spec/multistream/remoteMedia.ts +217 -0
- package/test/unit/spec/multistream/remoteMediaGroup.ts +396 -0
- package/test/unit/spec/multistream/remoteMediaManager.ts +1251 -0
- package/test/unit/spec/roap/index.ts +63 -35
- package/test/unit/spec/stats-analyzer/index.js +19 -22
- package/dist/peer-connection-manager/index.js +0 -794
- package/dist/peer-connection-manager/index.js.map +0 -1
- package/dist/roap/collection.js +0 -73
- package/dist/roap/collection.js.map +0 -1
- package/dist/roap/handler.js +0 -337
- package/dist/roap/handler.js.map +0 -1
- package/dist/roap/state.js +0 -164
- package/dist/roap/state.js.map +0 -1
- package/dist/roap/util.js +0 -102
- package/dist/roap/util.js.map +0 -1
- package/src/peer-connection-manager/index.js +0 -723
- package/src/roap/collection.js +0 -63
- package/src/roap/handler.js +0 -252
- package/src/roap/state.js +0 -149
- package/src/roap/util.js +0 -93
- package/test/unit/spec/peerconnection-manager/index.js +0 -188
- package/test/unit/spec/peerconnection-manager/utils.js +0 -48
- package/test/unit/spec/roap/util.js +0 -30
|
@@ -20,7 +20,9 @@ import {
|
|
|
20
20
|
_SIP_URI_,
|
|
21
21
|
_MEETING_ID_,
|
|
22
22
|
LOCUSINFO,
|
|
23
|
+
PC_BAIL_TIMEOUT,
|
|
23
24
|
} from '@webex/plugin-meetings/src/constants';
|
|
25
|
+
import {MediaConnection as MC} from '@webex/internal-media-core';
|
|
24
26
|
import * as StatsAnalyzerModule from '@webex/plugin-meetings/src/statsAnalyzer';
|
|
25
27
|
import EventsScope from '@webex/plugin-meetings/src/common/events/events-scope';
|
|
26
28
|
import Meetings, {CONSTANTS} from '@webex/plugin-meetings';
|
|
@@ -32,7 +34,6 @@ import LocusInfo from '@webex/plugin-meetings/src/locus-info';
|
|
|
32
34
|
import MediaProperties from '@webex/plugin-meetings/src/media/properties';
|
|
33
35
|
import MeetingUtil from '@webex/plugin-meetings/src/meeting/util';
|
|
34
36
|
import Media from '@webex/plugin-meetings/src/media/index';
|
|
35
|
-
import PeerConnectionManager from '@webex/plugin-meetings/src/peer-connection-manager';
|
|
36
37
|
import ReconnectionManager from '@webex/plugin-meetings/src/reconnection-manager';
|
|
37
38
|
import MediaUtil from '@webex/plugin-meetings/src/media/util';
|
|
38
39
|
import LoggerProxy from '@webex/plugin-meetings/src/common/logs/logger-proxy';
|
|
@@ -178,7 +179,6 @@ describe('plugin-meetings', () => {
|
|
|
178
179
|
TriggerProxy.trigger = sinon.stub().returns(true);
|
|
179
180
|
Metrics.postEvent = sinon.stub();
|
|
180
181
|
Metrics.initialSetup(null, webex);
|
|
181
|
-
MediaUtil.createPeerConnection = sinon.stub().returns({});
|
|
182
182
|
MediaUtil.createMediaStream = sinon.stub().returns(true);
|
|
183
183
|
|
|
184
184
|
uuid1 = uuid.v4();
|
|
@@ -222,7 +222,6 @@ describe('plugin-meetings', () => {
|
|
|
222
222
|
assert.equal(meeting.userId, uuid1);
|
|
223
223
|
assert.equal(meeting.resource, uuid2);
|
|
224
224
|
assert.equal(meeting.deviceUrl, uuid3);
|
|
225
|
-
assert.equal(meeting.roapSeq, -1);
|
|
226
225
|
assert.deepEqual(meeting.meetingInfo, {});
|
|
227
226
|
assert.instanceOf(meeting.members, Members);
|
|
228
227
|
assert.instanceOf(meeting.roap, Roap);
|
|
@@ -909,22 +908,25 @@ describe('plugin-meetings', () => {
|
|
|
909
908
|
applyClientStateLocally: sinon.stub().returns(Promise.resolve(true))
|
|
910
909
|
};
|
|
911
910
|
|
|
911
|
+
let fakeMediaConnection;
|
|
912
|
+
|
|
912
913
|
beforeEach(() => {
|
|
914
|
+
fakeMediaConnection = {
|
|
915
|
+
close: sinon.stub(),
|
|
916
|
+
getConnectionState: sinon.stub().returns(MC.ConnectionState.Connected),
|
|
917
|
+
initiateOffer: sinon.stub().resolves({}),
|
|
918
|
+
on: sinon.stub(),
|
|
919
|
+
};
|
|
913
920
|
meeting.mediaProperties.setMediaDirection = sinon.stub().returns(true);
|
|
914
|
-
meeting.mediaProperties.
|
|
921
|
+
meeting.mediaProperties.waitForMediaConnectionConnected = sinon.stub().resolves();
|
|
915
922
|
meeting.mediaProperties.getCurrentConnectionType = sinon.stub().resolves('udp');
|
|
916
923
|
meeting.audio = muteStateStub;
|
|
917
924
|
meeting.video = muteStateStub;
|
|
918
|
-
Media.
|
|
925
|
+
Media.createMediaConnection = sinon.stub().returns(fakeMediaConnection);
|
|
919
926
|
meeting.setMercuryListener = sinon.stub().returns(true);
|
|
920
|
-
meeting.
|
|
927
|
+
meeting.setupMediaConnectionListeners = sinon.stub();
|
|
921
928
|
meeting.setMercuryListener = sinon.stub();
|
|
922
|
-
meeting.roap.sendRoapMediaRequest = sinon.stub().returns(new Promise((resolve) => {
|
|
923
|
-
meeting.mediaProperties.peerConnection.connectionState = CONSTANTS.CONNECTION_STATE.CONNECTED;
|
|
924
|
-
resolve();
|
|
925
|
-
}));
|
|
926
929
|
meeting.roap.doTurnDiscovery = sinon.stub().resolves({turnServerInfo: {}, turnDiscoverySkippedReason: undefined});
|
|
927
|
-
PeerConnectionManager.setContentSlides = sinon.stub().returns(true);
|
|
928
930
|
});
|
|
929
931
|
|
|
930
932
|
it('should have #addMedia', () => {
|
|
@@ -932,16 +934,17 @@ describe('plugin-meetings', () => {
|
|
|
932
934
|
});
|
|
933
935
|
|
|
934
936
|
it('should reject promise if meeting is not active', async () => {
|
|
935
|
-
await meeting.addMedia()
|
|
936
|
-
|
|
937
|
-
|
|
937
|
+
const result = await assert.isRejected(meeting.addMedia());
|
|
938
|
+
|
|
939
|
+
assert.instanceOf(result, MeetingNotActiveError);
|
|
938
940
|
});
|
|
939
941
|
|
|
940
942
|
it('should reject promise if user already in left state', async () => {
|
|
941
943
|
meeting.meetingState = 'ACTIVE';
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
944
|
+
meeting.locusInfo.parsedLocus = {self: {state: 'LEFT'}};
|
|
945
|
+
const result = await assert.isRejected(meeting.addMedia());
|
|
946
|
+
|
|
947
|
+
assert.instanceOf(result, UserNotJoinedError);
|
|
945
948
|
});
|
|
946
949
|
|
|
947
950
|
it('should reject promise if user is in lobby ', async () => {
|
|
@@ -960,24 +963,29 @@ describe('plugin-meetings', () => {
|
|
|
960
963
|
|
|
961
964
|
it('should reset the statsAnalyzer to null if addMedia throws an error', async () => {
|
|
962
965
|
meeting.meetingState = 'ACTIVE';
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
assert(Metrics.sendBehavioralMetric.calledOnce);
|
|
968
|
-
assert.calledWith(
|
|
969
|
-
Metrics.sendBehavioralMetric,
|
|
970
|
-
BEHAVIORAL_METRICS.ADD_MEDIA_FAILURE, {
|
|
971
|
-
correlation_id: meeting.correlationId,
|
|
972
|
-
locus_id: meeting.locusUrl.split('/').pop(),
|
|
973
|
-
reason: err.message,
|
|
974
|
-
stack: err.stack,
|
|
975
|
-
code: err.code,
|
|
976
|
-
turnDiscoverySkippedReason: undefined,
|
|
977
|
-
turnServerUsed: true
|
|
978
|
-
}
|
|
979
|
-
);
|
|
966
|
+
// setup the mock to return an incomplete object - this will cause addMedia to fail
|
|
967
|
+
// because some methods (like on() or initiateOffer()) are missing
|
|
968
|
+
Media.createMediaConnection = sinon.stub().returns({
|
|
969
|
+
close: sinon.stub(),
|
|
980
970
|
});
|
|
971
|
+
// set a statsAnalyzer on the meeting so that we can check that it gets reset to null
|
|
972
|
+
meeting.statsAnalyzer = {stopAnalyzer: sinon.stub().resolves()};
|
|
973
|
+
const error = await assert.isRejected(meeting.addMedia());
|
|
974
|
+
|
|
975
|
+
assert.isNull(meeting.statsAnalyzer);
|
|
976
|
+
assert(Metrics.sendBehavioralMetric.calledOnce);
|
|
977
|
+
assert.calledWith(
|
|
978
|
+
Metrics.sendBehavioralMetric,
|
|
979
|
+
BEHAVIORAL_METRICS.ADD_MEDIA_FAILURE, {
|
|
980
|
+
correlation_id: meeting.correlationId,
|
|
981
|
+
locus_id: meeting.locusUrl.split('/').pop(),
|
|
982
|
+
reason: error.message,
|
|
983
|
+
stack: error.stack,
|
|
984
|
+
code: error.code,
|
|
985
|
+
turnDiscoverySkippedReason: undefined,
|
|
986
|
+
turnServerUsed: true
|
|
987
|
+
}
|
|
988
|
+
);
|
|
981
989
|
});
|
|
982
990
|
|
|
983
991
|
it('checks metrics called with skipped reason config', async () => {
|
|
@@ -1000,37 +1008,44 @@ describe('plugin-meetings', () => {
|
|
|
1000
1008
|
});
|
|
1001
1009
|
});
|
|
1002
1010
|
|
|
1003
|
-
it('should reset the
|
|
1011
|
+
it('should reset the webrtcMediaConnection to null if addMedia throws an error', async () => {
|
|
1004
1012
|
meeting.meetingState = 'ACTIVE';
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
assert(Metrics.sendBehavioralMetric.calledOnce);
|
|
1010
|
-
assert.calledWith(
|
|
1011
|
-
Metrics.sendBehavioralMetric,
|
|
1012
|
-
BEHAVIORAL_METRICS.ADD_MEDIA_FAILURE, {
|
|
1013
|
-
correlation_id: meeting.correlationId,
|
|
1014
|
-
locus_id: meeting.locusUrl.split('/').pop(),
|
|
1015
|
-
reason: err.message,
|
|
1016
|
-
stack: err.stack,
|
|
1017
|
-
turnDiscoverySkippedReason: undefined,
|
|
1018
|
-
turnServerUsed: true
|
|
1019
|
-
}
|
|
1020
|
-
);
|
|
1013
|
+
// setup the mock so that a media connection is created, but its initiateOffer() method fails
|
|
1014
|
+
Media.createMediaConnection = sinon.stub().returns({
|
|
1015
|
+
initiateOffer: sinon.stub().throws(new Error('fake error')),
|
|
1016
|
+
close: sinon.stub(),
|
|
1021
1017
|
});
|
|
1018
|
+
const result = await assert.isRejected(meeting.addMedia());
|
|
1019
|
+
|
|
1020
|
+
assert.instanceOf(result, Error);
|
|
1021
|
+
assert.isNull(meeting.mediaProperties.webrtcMediaConnection);
|
|
1022
|
+
|
|
1023
|
+
assert(Metrics.sendBehavioralMetric.calledOnce);
|
|
1024
|
+
assert.calledWith(
|
|
1025
|
+
Metrics.sendBehavioralMetric,
|
|
1026
|
+
BEHAVIORAL_METRICS.ADD_MEDIA_FAILURE, sinon.match({
|
|
1027
|
+
correlation_id: meeting.correlationId,
|
|
1028
|
+
locus_id: meeting.locusUrl.split('/').pop(),
|
|
1029
|
+
reason: result.message,
|
|
1030
|
+
turnDiscoverySkippedReason: undefined,
|
|
1031
|
+
turnServerUsed: true
|
|
1032
|
+
})
|
|
1033
|
+
);
|
|
1022
1034
|
});
|
|
1023
1035
|
|
|
1024
1036
|
it('should work the second time addMedia is called in case the first time fails', async () => {
|
|
1025
1037
|
meeting.meetingState = 'ACTIVE';
|
|
1026
1038
|
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1039
|
+
// setup the mock to cause addMedia() to fail
|
|
1040
|
+
Media.createMediaConnection = sinon.stub().returns({
|
|
1041
|
+
initiateOffer: sinon.stub().throws(new Error('fake error')),
|
|
1042
|
+
close: sinon.stub(),
|
|
1043
|
+
});
|
|
1044
|
+
|
|
1045
|
+
await assert.isRejected(meeting.addMedia());
|
|
1046
|
+
|
|
1047
|
+
// reset the mock to a good one, that doesn't fail
|
|
1048
|
+
Media.createMediaConnection = sinon.stub().returns(fakeMediaConnection);
|
|
1034
1049
|
|
|
1035
1050
|
try {
|
|
1036
1051
|
await meeting.addMedia({
|
|
@@ -1044,12 +1059,11 @@ describe('plugin-meetings', () => {
|
|
|
1044
1059
|
|
|
1045
1060
|
it('if an error occurs after media request has already been sent, and the user waits until the server kicks them out, a UserNotJoinedError should be thrown when attempting to addMedia again', async () => {
|
|
1046
1061
|
meeting.meetingState = 'ACTIVE';
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
}));
|
|
1062
|
+
// setup the mock to cause addMedia() to fail
|
|
1063
|
+
Media.createMediaConnection = sinon.stub().returns({
|
|
1064
|
+
initiateOffer: sinon.stub().throws(new Error('fake error')),
|
|
1065
|
+
close: sinon.stub(),
|
|
1066
|
+
});
|
|
1053
1067
|
await meeting.addMedia().catch((err) => {
|
|
1054
1068
|
assert.exists(err);
|
|
1055
1069
|
});
|
|
@@ -1062,21 +1076,16 @@ describe('plugin-meetings', () => {
|
|
|
1062
1076
|
|
|
1063
1077
|
it('if an error occurs after media request has already been sent, and the user does NOT wait until the server kicks them out, the user should be able to addMedia successfully', async () => {
|
|
1064
1078
|
meeting.meetingState = 'ACTIVE';
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
}));
|
|
1079
|
+
// setup the mock to cause addMedia() to fail
|
|
1080
|
+
Media.createMediaConnection = sinon.stub().returns({
|
|
1081
|
+
initiateOffer: sinon.stub().throws(new Error('fake error')),
|
|
1082
|
+
close: sinon.stub(),
|
|
1083
|
+
});
|
|
1071
1084
|
await meeting.addMedia().catch((err) => {
|
|
1072
1085
|
assert.exists(err);
|
|
1073
1086
|
});
|
|
1074
1087
|
|
|
1075
|
-
|
|
1076
|
-
meeting.roap.sendRoapMediaRequest = sinon.stub().returns(new Promise((resolve) => {
|
|
1077
|
-
meeting.mediaProperties.peerConnection.connectionState = CONSTANTS.CONNECTION_STATE.CONNECTED;
|
|
1078
|
-
resolve();
|
|
1079
|
-
}));
|
|
1088
|
+
Media.createMediaConnection = sinon.stub().returns(fakeMediaConnection);
|
|
1080
1089
|
await meeting.addMedia().catch((err) => {
|
|
1081
1090
|
assert.fail('No error should appear: ', err);
|
|
1082
1091
|
});
|
|
@@ -1086,7 +1095,6 @@ describe('plugin-meetings', () => {
|
|
|
1086
1095
|
meeting.roap.doTurnDiscovery = sinon.stub().resolves({turnServerInfo: undefined, turnDiscoverySkippedReason: undefined});
|
|
1087
1096
|
|
|
1088
1097
|
meeting.meetingState = 'ACTIVE';
|
|
1089
|
-
MediaUtil.createPeerConnection.resetHistory();
|
|
1090
1098
|
const media = meeting.addMedia({
|
|
1091
1099
|
mediaSettings: {}
|
|
1092
1100
|
});
|
|
@@ -1096,12 +1104,10 @@ describe('plugin-meetings', () => {
|
|
|
1096
1104
|
assert.calledOnce(meeting.roap.doTurnDiscovery);
|
|
1097
1105
|
assert.calledWith(meeting.roap.doTurnDiscovery, meeting, false);
|
|
1098
1106
|
assert.calledOnce(meeting.mediaProperties.setMediaDirection);
|
|
1099
|
-
assert.calledOnce(Media.
|
|
1107
|
+
assert.calledOnce(Media.createMediaConnection);
|
|
1108
|
+
assert.calledWith(Media.createMediaConnection, sinon.match.any, sinon.match({turnServerInfo: undefined}));
|
|
1100
1109
|
assert.calledOnce(meeting.setMercuryListener);
|
|
1101
|
-
assert.calledOnce(
|
|
1102
|
-
assert.calledOnce(meeting.roap.sendRoapMediaRequest);
|
|
1103
|
-
assert.calledOnce(MediaUtil.createPeerConnection);
|
|
1104
|
-
assert.calledWith(MediaUtil.createPeerConnection, undefined);
|
|
1110
|
+
assert.calledOnce(fakeMediaConnection.initiateOffer);
|
|
1105
1111
|
/* statsAnalyzer is initiated inside of addMedia so there isn't
|
|
1106
1112
|
* a good way to mock it without mocking the constructor
|
|
1107
1113
|
*/
|
|
@@ -1113,7 +1119,7 @@ describe('plugin-meetings', () => {
|
|
|
1113
1119
|
const FAKE_TURN_PASSWORD = 'some-password';
|
|
1114
1120
|
|
|
1115
1121
|
meeting.meetingState = 'ACTIVE';
|
|
1116
|
-
|
|
1122
|
+
Media.createMediaConnection.resetHistory();
|
|
1117
1123
|
|
|
1118
1124
|
|
|
1119
1125
|
meeting.roap.doTurnDiscovery = sinon.stub().resolves({
|
|
@@ -1132,31 +1138,39 @@ describe('plugin-meetings', () => {
|
|
|
1132
1138
|
await media;
|
|
1133
1139
|
assert.calledOnce(meeting.roap.doTurnDiscovery);
|
|
1134
1140
|
assert.calledWith(meeting.roap.doTurnDiscovery, meeting, false);
|
|
1135
|
-
assert.calledOnce(
|
|
1136
|
-
assert.calledWith(
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
+
assert.calledOnce(Media.createMediaConnection);
|
|
1142
|
+
assert.calledWith(Media.createMediaConnection, sinon.match.any, sinon.match({
|
|
1143
|
+
turnServerInfo: {
|
|
1144
|
+
url: FAKE_TURN_URL,
|
|
1145
|
+
username: FAKE_TURN_USER,
|
|
1146
|
+
password: FAKE_TURN_PASSWORD
|
|
1147
|
+
}
|
|
1148
|
+
}));
|
|
1149
|
+
assert.calledOnce(fakeMediaConnection.initiateOffer);
|
|
1141
1150
|
});
|
|
1142
1151
|
|
|
1143
|
-
it('should attach the media and return
|
|
1144
|
-
meeting.roap.doTurnDiscovery = sinon.stub().resolves({turnServerInfo: undefined, turnDiscoverySkippedReason: undefined});
|
|
1152
|
+
it('should attach the media and return WebExMeetingsErrors when connection does not reach CONNECTED state', async () => {
|
|
1145
1153
|
meeting.meetingState = 'ACTIVE';
|
|
1146
|
-
|
|
1154
|
+
fakeMediaConnection.getConnectionState = sinon.stub().returns(MC.ConnectionState.Connecting);
|
|
1155
|
+
const clock = sinon.useFakeTimers();
|
|
1147
1156
|
const media = meeting.addMedia({
|
|
1148
1157
|
mediaSettings: {}
|
|
1149
1158
|
});
|
|
1150
1159
|
|
|
1160
|
+
await clock.tickAsync(4000 /* meetingState timer, hardcoded inside addMedia */ + PC_BAIL_TIMEOUT /* connection state timer */);
|
|
1161
|
+
await testUtils.flushPromises();
|
|
1162
|
+
|
|
1151
1163
|
assert.exists(media);
|
|
1152
1164
|
await media.catch((err) => {
|
|
1153
1165
|
assert.instanceOf(err, WebExMeetingsErrors);
|
|
1154
1166
|
});
|
|
1167
|
+
|
|
1168
|
+
clock.restore();
|
|
1155
1169
|
});
|
|
1156
1170
|
|
|
1157
|
-
it('should reject if
|
|
1171
|
+
it('should reject if waitForMediaConnectionConnected() rejects', async () => {
|
|
1158
1172
|
meeting.meetingState = 'ACTIVE';
|
|
1159
|
-
meeting.mediaProperties.
|
|
1173
|
+
meeting.mediaProperties.waitForMediaConnectionConnected.rejects(new Error('fake error'));
|
|
1160
1174
|
|
|
1161
1175
|
let errorThrown = false;
|
|
1162
1176
|
|
|
@@ -1346,7 +1360,6 @@ describe('plugin-meetings', () => {
|
|
|
1346
1360
|
meeting.statsAnalyzer = {stopAnalyzer: sinon.stub().resolves()};
|
|
1347
1361
|
meeting.unsetRemoteStream = sinon.stub().returns(true);
|
|
1348
1362
|
meeting.unsetPeerConnections = sinon.stub().returns(true);
|
|
1349
|
-
meeting.roap.stop = sinon.stub().returns(Promise.resolve());
|
|
1350
1363
|
meeting.logger.error = sinon.stub().returns(true);
|
|
1351
1364
|
|
|
1352
1365
|
// A meeting needs to be joined to leave
|
|
@@ -1371,7 +1384,6 @@ describe('plugin-meetings', () => {
|
|
|
1371
1384
|
assert.calledOnce(meeting.unsetLocalShareTrack);
|
|
1372
1385
|
assert.calledOnce(meeting.unsetRemoteTracks);
|
|
1373
1386
|
assert.calledOnce(meeting.unsetPeerConnections);
|
|
1374
|
-
assert.calledOnce(meeting.roap.stop);
|
|
1375
1387
|
});
|
|
1376
1388
|
describe('after audio/video is defined', () => {
|
|
1377
1389
|
let handleClientRequest;
|
|
@@ -1484,61 +1496,6 @@ describe('plugin-meetings', () => {
|
|
|
1484
1496
|
});
|
|
1485
1497
|
});
|
|
1486
1498
|
|
|
1487
|
-
describe('getter: isLocalShareLive', () => {
|
|
1488
|
-
const LIVE = 'live';
|
|
1489
|
-
const ENDED = 'ended';
|
|
1490
|
-
const SENDRECV = 'sendrecv';
|
|
1491
|
-
const RECVONLY = 'reconly';
|
|
1492
|
-
let sandbox;
|
|
1493
|
-
let _direction;
|
|
1494
|
-
let _trackReadyState = ENDED;
|
|
1495
|
-
|
|
1496
|
-
beforeEach(() => {
|
|
1497
|
-
sandbox = sinon.createSandbox();
|
|
1498
|
-
sandbox.stub(meeting.mediaProperties, 'shareTrack').value(true);
|
|
1499
|
-
sandbox.stub(meeting.mediaProperties, 'peerConnection').value({
|
|
1500
|
-
shareTransceiver: {
|
|
1501
|
-
get direction() {
|
|
1502
|
-
return _direction;
|
|
1503
|
-
},
|
|
1504
|
-
sender: {
|
|
1505
|
-
track: {
|
|
1506
|
-
get readyState() {
|
|
1507
|
-
return _trackReadyState;
|
|
1508
|
-
}
|
|
1509
|
-
}
|
|
1510
|
-
}
|
|
1511
|
-
}
|
|
1512
|
-
});
|
|
1513
|
-
});
|
|
1514
|
-
|
|
1515
|
-
afterEach(() => {
|
|
1516
|
-
sandbox.restore();
|
|
1517
|
-
sandbox = null;
|
|
1518
|
-
});
|
|
1519
|
-
|
|
1520
|
-
it('sets isLocalShareLive to true when sharing screen', () => {
|
|
1521
|
-
_direction = SENDRECV;
|
|
1522
|
-
_trackReadyState = LIVE;
|
|
1523
|
-
|
|
1524
|
-
assert.isTrue(meeting.isLocalShareLive);
|
|
1525
|
-
});
|
|
1526
|
-
|
|
1527
|
-
it('sets isLocalShareLive to false when not sharing screen', () => {
|
|
1528
|
-
_direction = RECVONLY;
|
|
1529
|
-
_trackReadyState = ENDED;
|
|
1530
|
-
|
|
1531
|
-
assert.isFalse(meeting.isLocalShareLive);
|
|
1532
|
-
});
|
|
1533
|
-
|
|
1534
|
-
it('sets isLocalShareLive to false when track is live but share direction is recv only', () => {
|
|
1535
|
-
_direction = RECVONLY;
|
|
1536
|
-
_trackReadyState = LIVE;
|
|
1537
|
-
|
|
1538
|
-
assert.isFalse(meeting.isLocalShareLive);
|
|
1539
|
-
});
|
|
1540
|
-
});
|
|
1541
|
-
|
|
1542
1499
|
describe('stops share immediately', () => {
|
|
1543
1500
|
let sandbox;
|
|
1544
1501
|
|
|
@@ -1556,7 +1513,6 @@ describe('plugin-meetings', () => {
|
|
|
1556
1513
|
const receiveShare = false;
|
|
1557
1514
|
const stream = 'stream';
|
|
1558
1515
|
|
|
1559
|
-
sandbox.stub(meeting.mediaProperties, 'peerConnection').value({shareTransceiver: true});
|
|
1560
1516
|
sandbox.stub(MeetingUtil, 'getTrack').returns({videoTrack: true});
|
|
1561
1517
|
MeetingUtil.validateOptions = sinon.stub().returns(Promise.resolve(true));
|
|
1562
1518
|
sandbox.stub(meeting, 'canUpdateMedia').returns(true);
|
|
@@ -1620,42 +1576,6 @@ describe('plugin-meetings', () => {
|
|
|
1620
1576
|
sandbox = null;
|
|
1621
1577
|
});
|
|
1622
1578
|
|
|
1623
|
-
it('calls handleShareTrackEnded if sharing is out of sync', async () => {
|
|
1624
|
-
const sendShare = true;
|
|
1625
|
-
const receiveShare = false;
|
|
1626
|
-
const stream = 'stream';
|
|
1627
|
-
const SENDRECV = 'sendrecv';
|
|
1628
|
-
const delay = 1e3;
|
|
1629
|
-
|
|
1630
|
-
MeetingUtil.validateOptions = sinon.stub().returns(Promise.resolve(true));
|
|
1631
|
-
MeetingUtil.updateTransceiver = sinon.stub().returns(Promise.resolve(true));
|
|
1632
|
-
sandbox.stub(meeting, 'canUpdateMedia').returns(true);
|
|
1633
|
-
sandbox.stub(MeetingUtil, 'getTrack').returns({videoTrack: null});
|
|
1634
|
-
sandbox.stub(meeting, 'setLocalShareTrack');
|
|
1635
|
-
sandbox.stub(meeting, 'unsetLocalShareTrack');
|
|
1636
|
-
sandbox.stub(meeting, 'checkForStopShare').returns(false);
|
|
1637
|
-
|
|
1638
|
-
sandbox.stub(meeting, 'isLocalShareLive').value(false);
|
|
1639
|
-
sandbox.stub(meeting, 'handleShareTrackEnded');
|
|
1640
|
-
sandbox.stub(meeting.mediaProperties, 'peerConnection').value({
|
|
1641
|
-
shareTransceiver: {
|
|
1642
|
-
direction: SENDRECV
|
|
1643
|
-
}
|
|
1644
|
-
});
|
|
1645
|
-
sandbox.useFakeTimers();
|
|
1646
|
-
|
|
1647
|
-
await meeting.updateShare({
|
|
1648
|
-
sendShare,
|
|
1649
|
-
receiveShare,
|
|
1650
|
-
stream,
|
|
1651
|
-
skipSignalingCheck: true
|
|
1652
|
-
});
|
|
1653
|
-
// simulate the setTimeout in code
|
|
1654
|
-
sandbox.clock.tick(delay);
|
|
1655
|
-
|
|
1656
|
-
assert.calledOnce(meeting.handleShareTrackEnded);
|
|
1657
|
-
});
|
|
1658
|
-
|
|
1659
1579
|
it('handleShareTrackEnded triggers an event', () => {
|
|
1660
1580
|
const stream = 'stream';
|
|
1661
1581
|
const {EVENT_TYPES} = CONSTANTS;
|
|
@@ -1917,6 +1837,11 @@ describe('plugin-meetings', () => {
|
|
|
1917
1837
|
});
|
|
1918
1838
|
|
|
1919
1839
|
describe('#updateAudio', () => {
|
|
1840
|
+
const FAKE_AUDIO_TRACK = {
|
|
1841
|
+
id: 'fake audio track',
|
|
1842
|
+
getSettings: sinon.stub().returns({}),
|
|
1843
|
+
};
|
|
1844
|
+
|
|
1920
1845
|
describe('when canUpdateMedia is true', () => {
|
|
1921
1846
|
beforeEach(() => {
|
|
1922
1847
|
meeting.canUpdateMedia = sinon.stub().returns(true);
|
|
@@ -1924,20 +1849,33 @@ describe('plugin-meetings', () => {
|
|
|
1924
1849
|
describe('when options are valid', () => {
|
|
1925
1850
|
beforeEach(() => {
|
|
1926
1851
|
MeetingUtil.validateOptions = sinon.stub().returns(Promise.resolve());
|
|
1852
|
+
meeting.mediaProperties.mediaDirection = {
|
|
1853
|
+
sendAudio: false,
|
|
1854
|
+
sendVideo: true,
|
|
1855
|
+
sendShare: false,
|
|
1856
|
+
receiveAudio: false,
|
|
1857
|
+
receiveVideo: true,
|
|
1858
|
+
receiveShare: true,
|
|
1859
|
+
};
|
|
1860
|
+
meeting.mediaProperties.webrtcMediaConnection = {updateSendReceiveOptions: sinon.stub()};
|
|
1861
|
+
sinon.stub(MeetingUtil, 'getTrack').returns({audioTrack: FAKE_AUDIO_TRACK});
|
|
1927
1862
|
});
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1863
|
+
it('calls this.mediaProperties.webrtcMediaConnection.updateSendReceiveOptions', () => meeting.updateAudio({
|
|
1864
|
+
sendAudio: true,
|
|
1865
|
+
receiveAudio: true,
|
|
1866
|
+
stream: {id: 'fake stream'}
|
|
1867
|
+
}).then(() => {
|
|
1868
|
+
assert.calledOnce(meeting.mediaProperties.webrtcMediaConnection.updateSendReceiveOptions);
|
|
1869
|
+
assert.calledWith(meeting.mediaProperties.webrtcMediaConnection.updateSendReceiveOptions, {
|
|
1870
|
+
send: {audio: FAKE_AUDIO_TRACK},
|
|
1871
|
+
receive: {
|
|
1872
|
+
audio: true, video: true, screenShareVideo: true, remoteQualityLevel: 'HIGH'
|
|
1873
|
+
}
|
|
1932
1874
|
});
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
}).then(() => {
|
|
1938
|
-
assert.calledOnce(MeetingUtil.updateTransceiver);
|
|
1939
|
-
}));
|
|
1940
|
-
});
|
|
1875
|
+
}));
|
|
1876
|
+
});
|
|
1877
|
+
afterEach(() => {
|
|
1878
|
+
sinon.restore();
|
|
1941
1879
|
});
|
|
1942
1880
|
});
|
|
1943
1881
|
});
|
|
@@ -1988,10 +1926,36 @@ describe('plugin-meetings', () => {
|
|
|
1988
1926
|
let sandbox;
|
|
1989
1927
|
const mockLocalStream = {id: 'mock local stream'};
|
|
1990
1928
|
const mockLocalShare = {id: 'mock local share stream'};
|
|
1929
|
+
const FAKE_TRACKS = {
|
|
1930
|
+
audio: {
|
|
1931
|
+
id: 'fake audio track',
|
|
1932
|
+
getSettings: sinon.stub().returns({}),
|
|
1933
|
+
},
|
|
1934
|
+
video: {
|
|
1935
|
+
id: 'fake video track',
|
|
1936
|
+
getSettings: sinon.stub().returns({}),
|
|
1937
|
+
},
|
|
1938
|
+
screenshareVideo: {
|
|
1939
|
+
id: 'fake share track',
|
|
1940
|
+
getSettings: sinon.stub().returns({}),
|
|
1941
|
+
},
|
|
1942
|
+
|
|
1943
|
+
};
|
|
1991
1944
|
|
|
1992
1945
|
beforeEach(() => {
|
|
1993
1946
|
sandbox = sinon.createSandbox();
|
|
1994
1947
|
meeting.mediaProperties.mediaDirection = {sendShare: true};
|
|
1948
|
+
// setup the stub to return the right tracks
|
|
1949
|
+
sandbox.stub(MeetingUtil, 'getTrack').callsFake((stream) => {
|
|
1950
|
+
if (stream === mockLocalStream) {
|
|
1951
|
+
return {audioTrack: FAKE_TRACKS.audio, videoTrack: FAKE_TRACKS.video};
|
|
1952
|
+
}
|
|
1953
|
+
if (stream === mockLocalShare) {
|
|
1954
|
+
return {audioTrack: null, videoTrack: FAKE_TRACKS.screenshareVideo};
|
|
1955
|
+
}
|
|
1956
|
+
|
|
1957
|
+
return {audioTrack: null, videoTrack: null};
|
|
1958
|
+
});
|
|
1995
1959
|
});
|
|
1996
1960
|
|
|
1997
1961
|
afterEach(() => {
|
|
@@ -2011,7 +1975,9 @@ describe('plugin-meetings', () => {
|
|
|
2011
1975
|
};
|
|
2012
1976
|
|
|
2013
1977
|
sandbox.stub(meeting, 'canUpdateMedia').returns(false);
|
|
2014
|
-
|
|
1978
|
+
meeting.mediaProperties.webrtcMediaConnection = {
|
|
1979
|
+
updateSendReceiveOptions: sinon.stub().resolves({})
|
|
1980
|
+
};
|
|
2015
1981
|
|
|
2016
1982
|
let myPromiseResolved = false;
|
|
2017
1983
|
|
|
@@ -2025,18 +1991,30 @@ describe('plugin-meetings', () => {
|
|
|
2025
1991
|
});
|
|
2026
1992
|
|
|
2027
1993
|
// verify that nothing was done
|
|
2028
|
-
assert.notCalled(
|
|
1994
|
+
assert.notCalled(meeting.mediaProperties.webrtcMediaConnection.updateSendReceiveOptions);
|
|
2029
1995
|
|
|
2030
1996
|
// now trigger processing of the queue
|
|
2031
1997
|
meeting.canUpdateMedia.restore();
|
|
2032
1998
|
sandbox.stub(meeting, 'canUpdateMedia').returns(true);
|
|
2033
|
-
meeting.updateMedia = sinon.stub().returns(Promise.resolve());
|
|
2034
1999
|
|
|
2035
2000
|
meeting.processNextQueuedMediaUpdate();
|
|
2036
2001
|
await testUtils.flushPromises();
|
|
2037
2002
|
|
|
2038
|
-
// and check that
|
|
2039
|
-
assert.
|
|
2003
|
+
// and check that updateSendReceiveOptions is called with the original args
|
|
2004
|
+
assert.calledOnce(meeting.mediaProperties.webrtcMediaConnection.updateSendReceiveOptions);
|
|
2005
|
+
assert.calledWith(meeting.mediaProperties.webrtcMediaConnection.updateSendReceiveOptions, {
|
|
2006
|
+
send: {
|
|
2007
|
+
audio: FAKE_TRACKS.audio,
|
|
2008
|
+
video: FAKE_TRACKS.video,
|
|
2009
|
+
screenShareVideo: FAKE_TRACKS.screenshareVideo,
|
|
2010
|
+
},
|
|
2011
|
+
receive: {
|
|
2012
|
+
audio: true,
|
|
2013
|
+
video: true,
|
|
2014
|
+
screenShareVideo: true,
|
|
2015
|
+
remoteQualityLevel: 'HIGH'
|
|
2016
|
+
}
|
|
2017
|
+
});
|
|
2040
2018
|
assert.isTrue(myPromiseResolved);
|
|
2041
2019
|
});
|
|
2042
2020
|
});
|
|
@@ -2282,7 +2260,7 @@ describe('plugin-meetings', () => {
|
|
|
2282
2260
|
meeting.mediaProperties.mediaDirection = mediaDirection;
|
|
2283
2261
|
meeting.canUpdateMedia = sinon.stub().returns(true);
|
|
2284
2262
|
MeetingUtil.validateOptions = sinon.stub().returns(Promise.resolve());
|
|
2285
|
-
|
|
2263
|
+
meeting.updateVideo = sinon.stub().resolves();
|
|
2286
2264
|
sinon.stub(MeetingUtil, 'getTrack').returns({videoTrack: fakeTrack});
|
|
2287
2265
|
});
|
|
2288
2266
|
|
|
@@ -2842,7 +2820,6 @@ describe('plugin-meetings', () => {
|
|
|
2842
2820
|
meeting.statsAnalyzer = {stopAnalyzer: sinon.stub().resolves()};
|
|
2843
2821
|
meeting.unsetRemoteStream = sinon.stub().returns(true);
|
|
2844
2822
|
meeting.unsetPeerConnections = sinon.stub().returns(true);
|
|
2845
|
-
meeting.roap.stop = sinon.stub().returns(Promise.resolve());
|
|
2846
2823
|
meeting.logger.error = sinon.stub().returns(true);
|
|
2847
2824
|
|
|
2848
2825
|
// A meeting needs to be joined to end
|
|
@@ -2867,7 +2844,6 @@ describe('plugin-meetings', () => {
|
|
|
2867
2844
|
assert.calledOnce(meeting?.unsetLocalShareTrack);
|
|
2868
2845
|
assert.calledOnce(meeting?.unsetRemoteTracks);
|
|
2869
2846
|
assert.calledOnce(meeting?.unsetPeerConnections);
|
|
2870
|
-
assert.calledOnce(meeting?.roap?.stop);
|
|
2871
2847
|
});
|
|
2872
2848
|
});
|
|
2873
2849
|
|
|
@@ -3295,31 +3271,254 @@ describe('plugin-meetings', () => {
|
|
|
3295
3271
|
assert.calledOnce(meeting.stopShare);
|
|
3296
3272
|
});
|
|
3297
3273
|
});
|
|
3298
|
-
describe('#
|
|
3274
|
+
describe('#setupMediaConnectionListeners', () => {
|
|
3275
|
+
let eventListeners;
|
|
3276
|
+
|
|
3299
3277
|
beforeEach(() => {
|
|
3278
|
+
eventListeners = {};
|
|
3300
3279
|
meeting.statsAnalyzer = {startAnalyzer: sinon.stub()};
|
|
3280
|
+
meeting.mediaProperties.webrtcMediaConnection = {
|
|
3281
|
+
// mock the on() method and store all the listeners
|
|
3282
|
+
on: sinon.stub().callsFake((event, listener) => {
|
|
3283
|
+
eventListeners[event] = listener;
|
|
3284
|
+
})
|
|
3285
|
+
};
|
|
3301
3286
|
});
|
|
3302
|
-
it('should trigger a media:ready event when remote stream track ontrack is fired', () => {
|
|
3303
|
-
const pc = {};
|
|
3304
3287
|
|
|
3305
|
-
|
|
3306
|
-
|
|
3288
|
+
it('should register for all the correct RoapMediaConnection events', () => {
|
|
3289
|
+
meeting.setupMediaConnectionListeners();
|
|
3290
|
+
assert.isFunction(eventListeners[MC.Event.ROAP_STARTED]);
|
|
3291
|
+
assert.isFunction(eventListeners[MC.Event.ROAP_DONE]);
|
|
3292
|
+
assert.isFunction(eventListeners[MC.Event.ROAP_FAILURE]);
|
|
3293
|
+
assert.isFunction(eventListeners[MC.Event.ROAP_MESSAGE_TO_SEND]);
|
|
3294
|
+
assert.isFunction(eventListeners[MC.Event.REMOTE_TRACK_ADDED]);
|
|
3295
|
+
assert.isFunction(eventListeners[MC.Event.CONNECTION_STATE_CHANGED]);
|
|
3296
|
+
});
|
|
3297
|
+
|
|
3298
|
+
it('should trigger a media:ready event when REMOTE_TRACK_ADDED is fired', () => {
|
|
3299
|
+
meeting.setupMediaConnectionListeners();
|
|
3300
|
+
eventListeners[MC.Event.REMOTE_TRACK_ADDED]({track: 'track', type: MC.RemoteTrackType.AUDIO});
|
|
3307
3301
|
assert.equal(TriggerProxy.trigger.getCall(1).args[2], 'media:ready');
|
|
3308
3302
|
assert.deepEqual(TriggerProxy.trigger.getCall(1).args[3], {type: 'remoteAudio', stream: true});
|
|
3309
3303
|
|
|
3310
|
-
|
|
3304
|
+
eventListeners[MC.Event.REMOTE_TRACK_ADDED]({track: 'track', type: MC.RemoteTrackType.VIDEO});
|
|
3311
3305
|
assert.equal(TriggerProxy.trigger.getCall(2).args[2], 'media:ready');
|
|
3312
3306
|
assert.deepEqual(TriggerProxy.trigger.getCall(2).args[3], {type: 'remoteVideo', stream: true});
|
|
3313
3307
|
|
|
3314
|
-
|
|
3308
|
+
eventListeners[MC.Event.REMOTE_TRACK_ADDED]({track: 'track', type: MC.RemoteTrackType.SCREENSHARE_VIDEO});
|
|
3315
3309
|
assert.equal(TriggerProxy.trigger.getCall(3).args[2], 'media:ready');
|
|
3316
3310
|
assert.deepEqual(TriggerProxy.trigger.getCall(3).args[3], {type: 'remoteShare', stream: true});
|
|
3311
|
+
});
|
|
3317
3312
|
|
|
3313
|
+
describe('should send correct metrics for ROAP_FAILURE event', () => {
|
|
3314
|
+
const fakeErrorMessage = 'test error';
|
|
3315
|
+
const fakeRootCauseName = 'root cause name';
|
|
3316
|
+
const fakeErrorName = 'test error name';
|
|
3318
3317
|
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
|
|
3318
|
+
beforeEach(() => {
|
|
3319
|
+
meeting.setupMediaConnectionListeners();
|
|
3320
|
+
});
|
|
3321
|
+
|
|
3322
|
+
const checkMetricSent = (event) => {
|
|
3323
|
+
assert.calledOnce(Metrics.postEvent);
|
|
3324
|
+
assert.calledWithMatch(Metrics.postEvent, {event, meetingId: meeting.id, data: {canProceed: false}});
|
|
3325
|
+
};
|
|
3326
|
+
|
|
3327
|
+
const checkBehavioralMetricSent = (metricName, expectedCode, expectedReason, expectedMetadataType) => {
|
|
3328
|
+
assert.calledOnce(Metrics.sendBehavioralMetric);
|
|
3329
|
+
assert.calledWith(
|
|
3330
|
+
Metrics.sendBehavioralMetric,
|
|
3331
|
+
metricName,
|
|
3332
|
+
{
|
|
3333
|
+
code: expectedCode,
|
|
3334
|
+
correlation_id: meeting.correlationId,
|
|
3335
|
+
reason: expectedReason,
|
|
3336
|
+
stack: sinon.match.any
|
|
3337
|
+
},
|
|
3338
|
+
{
|
|
3339
|
+
type: expectedMetadataType
|
|
3340
|
+
}
|
|
3341
|
+
);
|
|
3342
|
+
};
|
|
3343
|
+
|
|
3344
|
+
it('should send metrics for SdpOfferCreationError error', () => {
|
|
3345
|
+
const fakeError = new MC.Errors.SdpOfferCreationError(fakeErrorMessage, {name: fakeErrorName, cause: {name: fakeRootCauseName}});
|
|
3346
|
+
|
|
3347
|
+
eventListeners[MC.Event.ROAP_FAILURE](fakeError);
|
|
3348
|
+
|
|
3349
|
+
checkMetricSent(eventType.LOCAL_SDP_GENERATED);
|
|
3350
|
+
checkBehavioralMetricSent(BEHAVIORAL_METRICS.PEERCONNECTION_FAILURE, MC.Errors.ErrorCode.SdpOfferCreationError, fakeErrorMessage, fakeRootCauseName);
|
|
3351
|
+
});
|
|
3352
|
+
|
|
3353
|
+
it('should send metrics for SdpOfferHandlingError error', () => {
|
|
3354
|
+
const fakeError = new MC.Errors.SdpOfferHandlingError(fakeErrorMessage, {name: fakeErrorName, cause: {name: fakeRootCauseName}});
|
|
3355
|
+
|
|
3356
|
+
eventListeners[MC.Event.ROAP_FAILURE](fakeError);
|
|
3357
|
+
|
|
3358
|
+
checkMetricSent(eventType.REMOTE_SDP_RECEIVED);
|
|
3359
|
+
checkBehavioralMetricSent(BEHAVIORAL_METRICS.PEERCONNECTION_FAILURE, MC.Errors.ErrorCode.SdpOfferHandlingError, fakeErrorMessage, fakeRootCauseName);
|
|
3360
|
+
});
|
|
3361
|
+
|
|
3362
|
+
it('should send metrics for SdpAnswerHandlingError error', () => {
|
|
3363
|
+
const fakeError = new MC.Errors.SdpAnswerHandlingError(fakeErrorMessage, {name: fakeErrorName, cause: {name: fakeRootCauseName}});
|
|
3364
|
+
|
|
3365
|
+
eventListeners[MC.Event.ROAP_FAILURE](fakeError);
|
|
3366
|
+
|
|
3367
|
+
checkMetricSent(eventType.REMOTE_SDP_RECEIVED);
|
|
3368
|
+
checkBehavioralMetricSent(BEHAVIORAL_METRICS.PEERCONNECTION_FAILURE, MC.Errors.ErrorCode.SdpAnswerHandlingError, fakeErrorMessage, fakeRootCauseName);
|
|
3369
|
+
});
|
|
3370
|
+
|
|
3371
|
+
it('should send metrics for SdpError error', () => {
|
|
3372
|
+
// SdpError is usually without a cause
|
|
3373
|
+
const fakeError = new MC.Errors.SdpError(fakeErrorMessage, {name: fakeErrorName});
|
|
3374
|
+
|
|
3375
|
+
eventListeners[MC.Event.ROAP_FAILURE](fakeError);
|
|
3376
|
+
|
|
3377
|
+
checkMetricSent(eventType.LOCAL_SDP_GENERATED);
|
|
3378
|
+
// expectedMetadataType is the error name in this case
|
|
3379
|
+
checkBehavioralMetricSent(BEHAVIORAL_METRICS.INVALID_ICE_CANDIDATE, MC.Errors.ErrorCode.SdpError, fakeErrorMessage, fakeErrorName);
|
|
3380
|
+
});
|
|
3381
|
+
|
|
3382
|
+
it('should send metrics for IceGatheringError error', () => {
|
|
3383
|
+
// IceGatheringError is usually without a cause
|
|
3384
|
+
const fakeError = new MC.Errors.IceGatheringError(fakeErrorMessage, {name: fakeErrorName});
|
|
3385
|
+
|
|
3386
|
+
eventListeners[MC.Event.ROAP_FAILURE](fakeError);
|
|
3387
|
+
|
|
3388
|
+
checkMetricSent(eventType.LOCAL_SDP_GENERATED);
|
|
3389
|
+
// expectedMetadataType is the error name in this case
|
|
3390
|
+
checkBehavioralMetricSent(BEHAVIORAL_METRICS.INVALID_ICE_CANDIDATE, MC.Errors.ErrorCode.IceGatheringError, fakeErrorMessage, fakeErrorName);
|
|
3391
|
+
});
|
|
3392
|
+
});
|
|
3393
|
+
|
|
3394
|
+
describe('handles MC.Event.ROAP_MESSAGE_TO_SEND correctly', () => {
|
|
3395
|
+
let sendRoapOKStub;
|
|
3396
|
+
let sendRoapMediaRequestStub;
|
|
3397
|
+
let sendRoapAnswerStub;
|
|
3398
|
+
let sendRoapErrorStub;
|
|
3399
|
+
|
|
3400
|
+
beforeEach(() => {
|
|
3401
|
+
sendRoapOKStub = sinon.stub(meeting.roap, 'sendRoapOK').resolves({});
|
|
3402
|
+
sendRoapMediaRequestStub = sinon.stub(meeting.roap, 'sendRoapMediaRequest').resolves({});
|
|
3403
|
+
sendRoapAnswerStub = sinon.stub(meeting.roap, 'sendRoapAnswer').resolves({});
|
|
3404
|
+
sendRoapErrorStub = sinon.stub(meeting.roap, 'sendRoapError').resolves({});
|
|
3405
|
+
|
|
3406
|
+
meeting.setupMediaConnectionListeners();
|
|
3407
|
+
});
|
|
3408
|
+
|
|
3409
|
+
it('handles OK message correctly', () => {
|
|
3410
|
+
eventListeners[MC.Event.ROAP_MESSAGE_TO_SEND]({roapMessage: {messageType: 'OK', seq: 1}});
|
|
3411
|
+
|
|
3412
|
+
assert.calledOnce(Metrics.postEvent);
|
|
3413
|
+
assert.calledWithMatch(Metrics.postEvent, {event: eventType.REMOTE_SDP_RECEIVED, meetingId: meeting.id});
|
|
3414
|
+
|
|
3415
|
+
assert.calledOnce(sendRoapOKStub);
|
|
3416
|
+
assert.calledWith(sendRoapOKStub, {seq: 1, mediaId: meeting.mediaId, correlationId: meeting.correlationId});
|
|
3417
|
+
});
|
|
3418
|
+
|
|
3419
|
+
it('handles OFFER message correctly', () => {
|
|
3420
|
+
eventListeners[MC.Event.ROAP_MESSAGE_TO_SEND]({
|
|
3421
|
+
roapMessage: {
|
|
3422
|
+
messageType: 'OFFER',
|
|
3423
|
+
seq: 1,
|
|
3424
|
+
sdp: 'fake sdp',
|
|
3425
|
+
tieBreaker: 12345,
|
|
3426
|
+
}
|
|
3427
|
+
});
|
|
3428
|
+
|
|
3429
|
+
assert.calledOnce(Metrics.postEvent);
|
|
3430
|
+
assert.calledWithMatch(Metrics.postEvent, {event: eventType.LOCAL_SDP_GENERATED, meetingId: meeting.id});
|
|
3431
|
+
|
|
3432
|
+
assert.calledOnce(sendRoapMediaRequestStub);
|
|
3433
|
+
assert.calledWith(sendRoapMediaRequestStub, {
|
|
3434
|
+
seq: 1, sdp: 'fake sdp', tieBreaker: 12345, meeting, reconnect: false
|
|
3435
|
+
});
|
|
3436
|
+
});
|
|
3437
|
+
|
|
3438
|
+
it('handles ANSWER message correctly', () => {
|
|
3439
|
+
eventListeners[MC.Event.ROAP_MESSAGE_TO_SEND]({
|
|
3440
|
+
roapMessage: {
|
|
3441
|
+
messageType: 'ANSWER',
|
|
3442
|
+
seq: 10,
|
|
3443
|
+
sdp: 'fake sdp answer',
|
|
3444
|
+
tieBreaker: 12345,
|
|
3445
|
+
}
|
|
3446
|
+
});
|
|
3447
|
+
|
|
3448
|
+
assert.calledOnce(Metrics.postEvent);
|
|
3449
|
+
assert.calledWithMatch(Metrics.postEvent, {event: eventType.REMOTE_SDP_RECEIVED, meetingId: meeting.id});
|
|
3450
|
+
|
|
3451
|
+
assert.calledOnce(sendRoapAnswerStub);
|
|
3452
|
+
assert.calledWith(sendRoapAnswerStub, {
|
|
3453
|
+
seq: 10, sdp: 'fake sdp answer', mediaId: meeting.mediaId, correlationId: meeting.correlationId
|
|
3454
|
+
});
|
|
3455
|
+
});
|
|
3456
|
+
|
|
3457
|
+
it('sends metrics if fails to send roap ANSWER message', async () => {
|
|
3458
|
+
sendRoapAnswerStub.rejects(new Error('sending answer failed'));
|
|
3459
|
+
|
|
3460
|
+
await eventListeners[MC.Event.ROAP_MESSAGE_TO_SEND]({
|
|
3461
|
+
roapMessage: {
|
|
3462
|
+
messageType: 'ANSWER',
|
|
3463
|
+
seq: 10,
|
|
3464
|
+
sdp: 'fake sdp answer',
|
|
3465
|
+
tieBreaker: 12345,
|
|
3466
|
+
}
|
|
3467
|
+
});
|
|
3468
|
+
await testUtils.flushPromises();
|
|
3469
|
+
|
|
3470
|
+
assert.calledOnce(Metrics.sendBehavioralMetric);
|
|
3471
|
+
assert.calledWithMatch(Metrics.sendBehavioralMetric, BEHAVIORAL_METRICS.ROAP_ANSWER_FAILURE, {
|
|
3472
|
+
correlation_id: meeting.correlationId,
|
|
3473
|
+
locus_id: meeting.locusUrl.split('/').pop(),
|
|
3474
|
+
reason: 'sending answer failed'
|
|
3475
|
+
});
|
|
3476
|
+
});
|
|
3477
|
+
|
|
3478
|
+
[MC.ErrorType.CONFLICT, MC.ErrorType.DOUBLECONFLICT].forEach((errorType) =>
|
|
3479
|
+
it(`handles ERROR message indicating glare condition correctly (errorType=${errorType})`, () => {
|
|
3480
|
+
eventListeners[MC.Event.ROAP_MESSAGE_TO_SEND]({
|
|
3481
|
+
roapMessage: {
|
|
3482
|
+
messageType: 'ERROR',
|
|
3483
|
+
seq: 10,
|
|
3484
|
+
errorType,
|
|
3485
|
+
tieBreaker: 12345,
|
|
3486
|
+
}
|
|
3487
|
+
});
|
|
3488
|
+
|
|
3489
|
+
assert.calledOnce(Metrics.sendBehavioralMetric);
|
|
3490
|
+
assert.calledWithMatch(Metrics.sendBehavioralMetric, BEHAVIORAL_METRICS.ROAP_GLARE_CONDITION, {
|
|
3491
|
+
correlation_id: meeting.correlationId,
|
|
3492
|
+
locus_id: meeting.locusUrl.split('/').pop(),
|
|
3493
|
+
sequence: 10
|
|
3494
|
+
});
|
|
3495
|
+
|
|
3496
|
+
assert.calledOnce(sendRoapErrorStub);
|
|
3497
|
+
assert.calledWith(sendRoapErrorStub, {
|
|
3498
|
+
seq: 10, errorType, mediaId: meeting.mediaId, correlationId: meeting.correlationId
|
|
3499
|
+
});
|
|
3500
|
+
}));
|
|
3501
|
+
|
|
3502
|
+
it('handles ERROR message indicating other errors correctly', () => {
|
|
3503
|
+
eventListeners[MC.Event.ROAP_MESSAGE_TO_SEND]({
|
|
3504
|
+
roapMessage: {
|
|
3505
|
+
messageType: 'ERROR',
|
|
3506
|
+
seq: 10,
|
|
3507
|
+
errorType: MC.ErrorType.FAILED,
|
|
3508
|
+
tieBreaker: 12345,
|
|
3509
|
+
}
|
|
3510
|
+
});
|
|
3511
|
+
|
|
3512
|
+
assert.notCalled(Metrics.sendBehavioralMetric);
|
|
3513
|
+
|
|
3514
|
+
assert.calledOnce(sendRoapErrorStub);
|
|
3515
|
+
assert.calledWith(sendRoapErrorStub, {
|
|
3516
|
+
seq: 10,
|
|
3517
|
+
errorType: MC.ErrorType.FAILED,
|
|
3518
|
+
mediaId: meeting.mediaId,
|
|
3519
|
+
correlationId: meeting.correlationId
|
|
3520
|
+
});
|
|
3521
|
+
});
|
|
3323
3522
|
});
|
|
3324
3523
|
});
|
|
3325
3524
|
describe('#setUpLocusInfoSelfListener', () => {
|
|
@@ -3514,13 +3713,13 @@ describe('plugin-meetings', () => {
|
|
|
3514
3713
|
});
|
|
3515
3714
|
});
|
|
3516
3715
|
describe('#closePeerConnections', () => {
|
|
3517
|
-
it('should close the
|
|
3518
|
-
|
|
3716
|
+
it('should close the webrtc media connection, and return a promise', async () => {
|
|
3717
|
+
meeting.mediaProperties.webrtcMediaConnection = {close: sinon.stub()};
|
|
3519
3718
|
const pcs = meeting.closePeerConnections();
|
|
3520
3719
|
|
|
3521
3720
|
assert.exists(pcs.then);
|
|
3522
3721
|
await pcs;
|
|
3523
|
-
assert.calledOnce(
|
|
3722
|
+
assert.calledOnce(meeting.mediaProperties.webrtcMediaConnection.close);
|
|
3524
3723
|
});
|
|
3525
3724
|
});
|
|
3526
3725
|
describe('#unsetPeerConnections', () => {
|
|
@@ -3687,13 +3886,6 @@ describe('plugin-meetings', () => {
|
|
|
3687
3886
|
});
|
|
3688
3887
|
});
|
|
3689
3888
|
});
|
|
3690
|
-
describe('#setRoapSeq', () => {
|
|
3691
|
-
it('should set the roap seq and return null', () => {
|
|
3692
|
-
assert.equal(-1, meeting.roapSeq);
|
|
3693
|
-
meeting.setRoapSeq(1);
|
|
3694
|
-
assert.equal(meeting.roapSeq, 1);
|
|
3695
|
-
});
|
|
3696
|
-
});
|
|
3697
3889
|
describe('#setCorrelationId', () => {
|
|
3698
3890
|
it('should set the correlationId and return undefined', () => {
|
|
3699
3891
|
assert.ok(meeting.correlationId);
|