@webex/plugin-meetings 2.33.2 → 2.35.0
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/constants.js +4 -4
- package/dist/constants.js.map +1 -1
- package/dist/media/properties.js +139 -0
- package/dist/media/properties.js.map +1 -1
- package/dist/meeting/index.js +106 -102
- package/dist/meeting/index.js.map +1 -1
- package/dist/roap/turnDiscovery.js +16 -4
- package/dist/roap/turnDiscovery.js.map +1 -1
- package/package.json +17 -17
- package/src/constants.ts +8 -8
- package/src/media/properties.js +97 -0
- package/src/meeting/index.js +111 -110
- package/src/roap/turnDiscovery.ts +4 -4
- package/test/unit/spec/media/properties.ts +305 -0
- package/test/unit/spec/meeting/index.js +94 -4
- package/test/unit/spec/roap/turnDiscovery.ts +36 -20
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
import {assert} from '@webex/test-helper-chai';
|
|
2
|
+
import sinon from 'sinon';
|
|
3
|
+
import MediaProperties from '@webex/plugin-meetings/src/media/properties';
|
|
4
|
+
import MediaUtil from '@webex/plugin-meetings/src/media/util';
|
|
5
|
+
import testUtils from '../../../utils/testUtils';
|
|
6
|
+
import {PC_BAIL_TIMEOUT} from '@webex/plugin-meetings/src/constants';
|
|
7
|
+
import {Defer} from '@webex/common';
|
|
8
|
+
|
|
9
|
+
describe('MediaProperties', () => {
|
|
10
|
+
let mediaProperties;
|
|
11
|
+
let mockPc;
|
|
12
|
+
let clock;
|
|
13
|
+
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
clock = sinon.useFakeTimers();
|
|
16
|
+
|
|
17
|
+
mockPc = {
|
|
18
|
+
getStats: sinon.stub().resolves([]),
|
|
19
|
+
addEventListener: sinon.stub(),
|
|
20
|
+
removeEventListener: sinon.stub(),
|
|
21
|
+
iceConnectionState: 'connected',
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
sinon.stub(MediaUtil, 'createPeerConnection').returns(mockPc);
|
|
25
|
+
|
|
26
|
+
mediaProperties = new MediaProperties();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
afterEach(() => {
|
|
30
|
+
clock.restore();
|
|
31
|
+
sinon.restore();
|
|
32
|
+
});
|
|
33
|
+
describe('waitForIceConnectedState', () => {
|
|
34
|
+
it('resolves immediately if ice state is connected', async () => {
|
|
35
|
+
mockPc.iceConnectionState = 'connected';
|
|
36
|
+
|
|
37
|
+
await mediaProperties.waitForIceConnectedState();
|
|
38
|
+
});
|
|
39
|
+
it('resolves immediately if ice state is completed', async () => {
|
|
40
|
+
mockPc.iceConnectionState = 'completed';
|
|
41
|
+
|
|
42
|
+
await mediaProperties.waitForIceConnectedState();
|
|
43
|
+
});
|
|
44
|
+
it('rejects after timeout if ice state does not reach connected/completed', async () => {
|
|
45
|
+
mockPc.iceConnectionState = 'connecting';
|
|
46
|
+
|
|
47
|
+
let promiseResolved = false;
|
|
48
|
+
let promiseRejected = false;
|
|
49
|
+
|
|
50
|
+
mediaProperties
|
|
51
|
+
.waitForIceConnectedState()
|
|
52
|
+
.then(() => {
|
|
53
|
+
promiseResolved = true;
|
|
54
|
+
})
|
|
55
|
+
.catch(() => {
|
|
56
|
+
promiseRejected = true;
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
assert.equal(promiseResolved, false);
|
|
60
|
+
assert.equal(promiseRejected, false);
|
|
61
|
+
|
|
62
|
+
await clock.tickAsync(PC_BAIL_TIMEOUT);
|
|
63
|
+
await testUtils.flushPromises();
|
|
64
|
+
|
|
65
|
+
assert.equal(promiseResolved, false);
|
|
66
|
+
assert.equal(promiseRejected, true);
|
|
67
|
+
|
|
68
|
+
// check that listener was registered and removed
|
|
69
|
+
assert.calledOnce(mockPc.addEventListener);
|
|
70
|
+
assert.equal(mockPc.addEventListener.getCall(0).args[0], 'iceconnectionstatechange');
|
|
71
|
+
const listener = mockPc.addEventListener.getCall(0).args[1];
|
|
72
|
+
|
|
73
|
+
assert.calledOnce(mockPc.removeEventListener);
|
|
74
|
+
assert.calledWith(mockPc.removeEventListener, 'iceconnectionstatechange', listener);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
['connected', 'completed'].forEach((successIceState) =>
|
|
78
|
+
it(`resolves when ice state reaches ${successIceState}`, async () => {
|
|
79
|
+
mockPc.iceConnectionState = 'connecting';
|
|
80
|
+
|
|
81
|
+
const clearTimeoutSpy = sinon.spy(clock, 'clearTimeout');
|
|
82
|
+
|
|
83
|
+
let promiseResolved = false;
|
|
84
|
+
let promiseRejected = false;
|
|
85
|
+
|
|
86
|
+
mediaProperties
|
|
87
|
+
.waitForIceConnectedState()
|
|
88
|
+
.then(() => {
|
|
89
|
+
promiseResolved = true;
|
|
90
|
+
})
|
|
91
|
+
.catch(() => {
|
|
92
|
+
promiseRejected = true;
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
assert.equal(promiseResolved, false);
|
|
96
|
+
assert.equal(promiseRejected, false);
|
|
97
|
+
|
|
98
|
+
// check the right listener was registered
|
|
99
|
+
assert.calledOnce(mockPc.addEventListener);
|
|
100
|
+
assert.equal(mockPc.addEventListener.getCall(0).args[0], 'iceconnectionstatechange');
|
|
101
|
+
const listener = mockPc.addEventListener.getCall(0).args[1];
|
|
102
|
+
|
|
103
|
+
// call the listener and pretend we are now connected
|
|
104
|
+
mockPc.iceConnectionState = successIceState;
|
|
105
|
+
listener();
|
|
106
|
+
await testUtils.flushPromises();
|
|
107
|
+
|
|
108
|
+
assert.equal(promiseResolved, true);
|
|
109
|
+
assert.equal(promiseRejected, false);
|
|
110
|
+
|
|
111
|
+
// check that listener was removed
|
|
112
|
+
assert.calledOnce(mockPc.removeEventListener);
|
|
113
|
+
assert.calledWith(mockPc.removeEventListener, 'iceconnectionstatechange', listener);
|
|
114
|
+
|
|
115
|
+
assert.calledOnce(clearTimeoutSpy);
|
|
116
|
+
})
|
|
117
|
+
);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
describe('getCurrentConnectionType', () => {
|
|
121
|
+
it('calls waitForIceConnectedState', async () => {
|
|
122
|
+
const spy = sinon.stub(mediaProperties, 'waitForIceConnectedState');
|
|
123
|
+
|
|
124
|
+
await mediaProperties.getCurrentConnectionType();
|
|
125
|
+
|
|
126
|
+
assert.calledOnce(spy);
|
|
127
|
+
});
|
|
128
|
+
it('calls getStats() only after waitForIceConnectedState resolves', async () => {
|
|
129
|
+
const waitForIceConnectedStateResult = new Defer();
|
|
130
|
+
|
|
131
|
+
const waitForIceConnectedStateStub = sinon
|
|
132
|
+
.stub(mediaProperties, 'waitForIceConnectedState')
|
|
133
|
+
.returns(waitForIceConnectedStateResult.promise);
|
|
134
|
+
|
|
135
|
+
const result = mediaProperties.getCurrentConnectionType();
|
|
136
|
+
|
|
137
|
+
await testUtils.flushPromises();
|
|
138
|
+
|
|
139
|
+
assert.called(waitForIceConnectedStateStub);
|
|
140
|
+
assert.notCalled(mockPc.getStats);
|
|
141
|
+
|
|
142
|
+
waitForIceConnectedStateResult.resolve();
|
|
143
|
+
await testUtils.flushPromises();
|
|
144
|
+
|
|
145
|
+
assert.called(mockPc.getStats);
|
|
146
|
+
await result;
|
|
147
|
+
});
|
|
148
|
+
it('rejects if waitForIceConnectedState rejects', async () => {
|
|
149
|
+
const waitForIceConnectedStateResult = new Defer();
|
|
150
|
+
|
|
151
|
+
const waitForIceConnectedStateStub = sinon
|
|
152
|
+
.stub(mediaProperties, 'waitForIceConnectedState')
|
|
153
|
+
.returns(waitForIceConnectedStateResult.promise);
|
|
154
|
+
|
|
155
|
+
const result = mediaProperties.getCurrentConnectionType();
|
|
156
|
+
|
|
157
|
+
await testUtils.flushPromises();
|
|
158
|
+
|
|
159
|
+
assert.called(waitForIceConnectedStateStub);
|
|
160
|
+
|
|
161
|
+
waitForIceConnectedStateResult.reject(new Error('fake error'));
|
|
162
|
+
await testUtils.flushPromises();
|
|
163
|
+
|
|
164
|
+
assert.notCalled(mockPc.getStats);
|
|
165
|
+
|
|
166
|
+
await assert.isRejected(result);
|
|
167
|
+
});
|
|
168
|
+
it('returns "unknown" if getStats() fails', async () => {
|
|
169
|
+
mockPc.getStats.rejects(new Error());
|
|
170
|
+
|
|
171
|
+
const connectionType = await mediaProperties.getCurrentConnectionType();
|
|
172
|
+
assert.equal(connectionType, 'unknown');
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it('returns "unknown" if getStats() returns no candidate pairs', async () => {
|
|
176
|
+
mockPc.getStats.resolves([{type: 'something', id: '1234'}]);
|
|
177
|
+
|
|
178
|
+
const connectionType = await mediaProperties.getCurrentConnectionType();
|
|
179
|
+
assert.equal(connectionType, 'unknown');
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it('returns "unknown" if getStats() returns no successful candidate pair', async () => {
|
|
183
|
+
mockPc.getStats.resolves([{type: 'candidate-pair', id: '1234', state: 'inprogress'}]);
|
|
184
|
+
|
|
185
|
+
const connectionType = await mediaProperties.getCurrentConnectionType();
|
|
186
|
+
assert.equal(connectionType, 'unknown');
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it('returns "unknown" if getStats() returns a successful candidate pair but local candidate is missing', async () => {
|
|
190
|
+
mockPc.getStats.resolves([
|
|
191
|
+
{type: 'candidate-pair', id: '1234', state: 'succeeded', localCandidateId: 'wrong id'},
|
|
192
|
+
]);
|
|
193
|
+
|
|
194
|
+
const connectionType = await mediaProperties.getCurrentConnectionType();
|
|
195
|
+
assert.equal(connectionType, 'unknown');
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
it('returns "UDP" if getStats() returns a successful candidate pair with udp local candidate', async () => {
|
|
199
|
+
mockPc.getStats.resolves([
|
|
200
|
+
{
|
|
201
|
+
type: 'candidate-pair',
|
|
202
|
+
id: 'some candidate pair id',
|
|
203
|
+
state: 'succeeded',
|
|
204
|
+
localCandidateId: 'local candidate id',
|
|
205
|
+
},
|
|
206
|
+
{type: 'local-candidate', id: 'some other candidate id', protocol: 'tcp'},
|
|
207
|
+
{type: 'local-candidate', id: 'local candidate id', protocol: 'udp'},
|
|
208
|
+
]);
|
|
209
|
+
|
|
210
|
+
const connectionType = await mediaProperties.getCurrentConnectionType();
|
|
211
|
+
assert.equal(connectionType, 'UDP');
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
it('returns "TCP" if getStats() returns a successful candidate pair with tcp local candidate', async () => {
|
|
215
|
+
mockPc.getStats.resolves([
|
|
216
|
+
{
|
|
217
|
+
type: 'candidate-pair',
|
|
218
|
+
id: 'some candidate pair id',
|
|
219
|
+
state: 'succeeded',
|
|
220
|
+
localCandidateId: 'some candidate id',
|
|
221
|
+
},
|
|
222
|
+
{type: 'local-candidate', id: 'some other candidate id', protocol: 'udp'},
|
|
223
|
+
{type: 'local-candidate', id: 'some candidate id', protocol: 'tcp'},
|
|
224
|
+
]);
|
|
225
|
+
|
|
226
|
+
const connectionType = await mediaProperties.getCurrentConnectionType();
|
|
227
|
+
assert.equal(connectionType, 'TCP');
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
[
|
|
231
|
+
{relayProtocol: 'tls', expectedConnectionType: 'TURN-TLS'},
|
|
232
|
+
{relayProtocol: 'tcp', expectedConnectionType: 'TURN-TCP'},
|
|
233
|
+
{relayProtocol: 'udp', expectedConnectionType: 'TURN-UDP'},
|
|
234
|
+
].forEach(({relayProtocol, expectedConnectionType}) =>
|
|
235
|
+
it(`returns "${expectedConnectionType}" if getStats() returns a successful candidate pair with a local candidate with relayProtocol=${relayProtocol}`, async () => {
|
|
236
|
+
mockPc.getStats.resolves([
|
|
237
|
+
{
|
|
238
|
+
type: 'candidate-pair',
|
|
239
|
+
id: 'some candidate pair id',
|
|
240
|
+
state: 'succeeded',
|
|
241
|
+
localCandidateId: 'selected candidate id',
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
type: 'candidate-pair',
|
|
245
|
+
id: 'some other candidate pair id',
|
|
246
|
+
state: 'failed',
|
|
247
|
+
localCandidateId: 'some other candidate id 1',
|
|
248
|
+
},
|
|
249
|
+
{type: 'local-candidate', id: 'some other candidate id 1', protocol: 'udp'},
|
|
250
|
+
{type: 'local-candidate', id: 'some other candidate id 2', protocol: 'tcp'},
|
|
251
|
+
{
|
|
252
|
+
type: 'local-candidate',
|
|
253
|
+
id: 'selected candidate id',
|
|
254
|
+
protocol: 'udp',
|
|
255
|
+
relayProtocol,
|
|
256
|
+
},
|
|
257
|
+
]);
|
|
258
|
+
|
|
259
|
+
const connectionType = await mediaProperties.getCurrentConnectionType();
|
|
260
|
+
assert.equal(connectionType, expectedConnectionType);
|
|
261
|
+
})
|
|
262
|
+
);
|
|
263
|
+
|
|
264
|
+
it('returns connection type of the first successful candidate pair', async () => {
|
|
265
|
+
// in real life this will never happen and all active candidate pairs will have same transport,
|
|
266
|
+
// but here we're simulating a situation where they have different transports and just checking
|
|
267
|
+
// that the code still works and just returns the first one
|
|
268
|
+
mockPc.getStats.resolves([
|
|
269
|
+
{
|
|
270
|
+
type: 'inbound-rtp',
|
|
271
|
+
id: 'whatever',
|
|
272
|
+
},
|
|
273
|
+
{
|
|
274
|
+
type: 'candidate-pair',
|
|
275
|
+
id: 'some candidate pair id',
|
|
276
|
+
state: 'succeeded',
|
|
277
|
+
localCandidateId: '1st selected candidate id',
|
|
278
|
+
},
|
|
279
|
+
{
|
|
280
|
+
type: 'candidate-pair',
|
|
281
|
+
id: 'some other candidate pair id',
|
|
282
|
+
state: 'succeeded',
|
|
283
|
+
localCandidateId: '2nd selected candidate id',
|
|
284
|
+
},
|
|
285
|
+
{type: 'local-candidate', id: 'some other candidate id 1', protocol: 'udp'},
|
|
286
|
+
{type: 'local-candidate', id: 'some other candidate id 2', protocol: 'tcp'},
|
|
287
|
+
{
|
|
288
|
+
type: 'local-candidate',
|
|
289
|
+
id: '1st selected candidate id',
|
|
290
|
+
protocol: 'udp',
|
|
291
|
+
relayProtocol: 'tls',
|
|
292
|
+
},
|
|
293
|
+
{
|
|
294
|
+
type: 'local-candidate',
|
|
295
|
+
id: '2nd selected candidate id',
|
|
296
|
+
protocol: 'udp',
|
|
297
|
+
relayProtocol: 'tcp',
|
|
298
|
+
},
|
|
299
|
+
]);
|
|
300
|
+
|
|
301
|
+
const connectionType = await mediaProperties.getCurrentConnectionType();
|
|
302
|
+
assert.equal(connectionType, 'TURN-TLS');
|
|
303
|
+
});
|
|
304
|
+
});
|
|
305
|
+
});
|
|
@@ -42,6 +42,7 @@ import BrowserDetection from '@webex/plugin-meetings/src/common/browser-detectio
|
|
|
42
42
|
import Metrics from '@webex/plugin-meetings/src/metrics';
|
|
43
43
|
import {trigger, eventType} from '@webex/plugin-meetings/src/metrics/config';
|
|
44
44
|
import BEHAVIORAL_METRICS from '@webex/plugin-meetings/src/metrics/constants';
|
|
45
|
+
import {IceGatheringFailed} from '@webex/plugin-meetings/src/common/errors/webex-errors';
|
|
45
46
|
|
|
46
47
|
import locus from '../fixture/locus';
|
|
47
48
|
import {
|
|
@@ -910,6 +911,8 @@ describe('plugin-meetings', () => {
|
|
|
910
911
|
|
|
911
912
|
beforeEach(() => {
|
|
912
913
|
meeting.mediaProperties.setMediaDirection = sinon.stub().returns(true);
|
|
914
|
+
meeting.mediaProperties.waitForIceConnectedState = sinon.stub().resolves();
|
|
915
|
+
meeting.mediaProperties.getCurrentConnectionType = sinon.stub().resolves('udp');
|
|
913
916
|
meeting.audio = muteStateStub;
|
|
914
917
|
meeting.video = muteStateStub;
|
|
915
918
|
Media.attachMedia = sinon.stub().returns(Promise.resolve([test1, test2]));
|
|
@@ -920,7 +923,7 @@ describe('plugin-meetings', () => {
|
|
|
920
923
|
meeting.mediaProperties.peerConnection.connectionState = CONSTANTS.CONNECTION_STATE.CONNECTED;
|
|
921
924
|
resolve();
|
|
922
925
|
}));
|
|
923
|
-
meeting.roap.doTurnDiscovery = sinon.stub().resolves();
|
|
926
|
+
meeting.roap.doTurnDiscovery = sinon.stub().resolves({turnServerInfo: {}, turnDiscoverySkippedReason: undefined});
|
|
924
927
|
PeerConnectionManager.setContentSlides = sinon.stub().returns(true);
|
|
925
928
|
});
|
|
926
929
|
|
|
@@ -961,6 +964,39 @@ describe('plugin-meetings', () => {
|
|
|
961
964
|
await meeting.addMedia().catch((err) => {
|
|
962
965
|
assert.exists(err);
|
|
963
966
|
assert.isNull(meeting.statsAnalyzer);
|
|
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
|
+
);
|
|
980
|
+
});
|
|
981
|
+
});
|
|
982
|
+
|
|
983
|
+
it('checks metrics called with skipped reason config', async () => {
|
|
984
|
+
meeting.roap.doTurnDiscovery = sinon.stub().resolves({turnServerInfo: undefined, turnDiscoverySkippedReason: 'config'});
|
|
985
|
+
meeting.meetingState = 'ACTIVE';
|
|
986
|
+
await meeting.addMedia().catch((err) => {
|
|
987
|
+
assert.exists(err);
|
|
988
|
+
assert(Metrics.sendBehavioralMetric.calledOnce);
|
|
989
|
+
assert.calledWith(
|
|
990
|
+
Metrics.sendBehavioralMetric,
|
|
991
|
+
BEHAVIORAL_METRICS.ADD_MEDIA_FAILURE, {
|
|
992
|
+
correlation_id: meeting.correlationId,
|
|
993
|
+
locus_id: meeting.locusUrl.split('/').pop(),
|
|
994
|
+
reason: err.message,
|
|
995
|
+
stack: err.stack,
|
|
996
|
+
turnDiscoverySkippedReason: 'config',
|
|
997
|
+
turnServerUsed: false
|
|
998
|
+
}
|
|
999
|
+
);
|
|
964
1000
|
});
|
|
965
1001
|
});
|
|
966
1002
|
|
|
@@ -970,6 +1006,18 @@ describe('plugin-meetings', () => {
|
|
|
970
1006
|
await meeting.addMedia().catch((err) => {
|
|
971
1007
|
assert.exists(err);
|
|
972
1008
|
assert.isNull(meeting.mediaProperties.peerConnection);
|
|
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
|
+
);
|
|
973
1021
|
});
|
|
974
1022
|
});
|
|
975
1023
|
|
|
@@ -1035,6 +1083,8 @@ describe('plugin-meetings', () => {
|
|
|
1035
1083
|
});
|
|
1036
1084
|
|
|
1037
1085
|
it('should attach the media and return promise', async () => {
|
|
1086
|
+
meeting.roap.doTurnDiscovery = sinon.stub().resolves({turnServerInfo: undefined, turnDiscoverySkippedReason: undefined});
|
|
1087
|
+
|
|
1038
1088
|
meeting.meetingState = 'ACTIVE';
|
|
1039
1089
|
MediaUtil.createPeerConnection.resetHistory();
|
|
1040
1090
|
const media = meeting.addMedia({
|
|
@@ -1065,10 +1115,14 @@ describe('plugin-meetings', () => {
|
|
|
1065
1115
|
meeting.meetingState = 'ACTIVE';
|
|
1066
1116
|
MediaUtil.createPeerConnection.resetHistory();
|
|
1067
1117
|
|
|
1118
|
+
|
|
1068
1119
|
meeting.roap.doTurnDiscovery = sinon.stub().resolves({
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1120
|
+
turnServerInfo: {
|
|
1121
|
+
url: FAKE_TURN_URL,
|
|
1122
|
+
username: FAKE_TURN_USER,
|
|
1123
|
+
password: FAKE_TURN_PASSWORD
|
|
1124
|
+
},
|
|
1125
|
+
turnServerSkippedReason: undefined
|
|
1072
1126
|
});
|
|
1073
1127
|
const media = meeting.addMedia({
|
|
1074
1128
|
mediaSettings: {}
|
|
@@ -1087,6 +1141,7 @@ describe('plugin-meetings', () => {
|
|
|
1087
1141
|
});
|
|
1088
1142
|
|
|
1089
1143
|
it('should attach the media and return promise', async () => {
|
|
1144
|
+
meeting.roap.doTurnDiscovery = sinon.stub().resolves({turnServerInfo: undefined, turnDiscoverySkippedReason: undefined});
|
|
1090
1145
|
meeting.meetingState = 'ACTIVE';
|
|
1091
1146
|
meeting.mediaProperties.peerConnection.connectionState = 'DISCONNECTED';
|
|
1092
1147
|
const media = meeting.addMedia({
|
|
@@ -1099,6 +1154,41 @@ describe('plugin-meetings', () => {
|
|
|
1099
1154
|
});
|
|
1100
1155
|
});
|
|
1101
1156
|
|
|
1157
|
+
it('should reject if waitForIceConnectedState() rejects', async () => {
|
|
1158
|
+
meeting.meetingState = 'ACTIVE';
|
|
1159
|
+
meeting.mediaProperties.waitForIceConnectedState.rejects(new Error('fake error'));
|
|
1160
|
+
|
|
1161
|
+
let errorThrown = false;
|
|
1162
|
+
|
|
1163
|
+
await meeting.addMedia({
|
|
1164
|
+
mediaSettings: {}
|
|
1165
|
+
})
|
|
1166
|
+
.catch((error) => {
|
|
1167
|
+
assert.equal(error.code, IceGatheringFailed.CODE);
|
|
1168
|
+
errorThrown = true;
|
|
1169
|
+
});
|
|
1170
|
+
|
|
1171
|
+
assert.isTrue(errorThrown);
|
|
1172
|
+
});
|
|
1173
|
+
|
|
1174
|
+
it('should send ADD_MEDIA_SUCCESS metrics', async () => {
|
|
1175
|
+
meeting.meetingState = 'ACTIVE';
|
|
1176
|
+
await meeting.addMedia({
|
|
1177
|
+
mediaSettings: {}
|
|
1178
|
+
});
|
|
1179
|
+
|
|
1180
|
+
assert.calledOnce(Metrics.sendBehavioralMetric);
|
|
1181
|
+
assert.calledWith(
|
|
1182
|
+
Metrics.sendBehavioralMetric,
|
|
1183
|
+
BEHAVIORAL_METRICS.ADD_MEDIA_SUCCESS,
|
|
1184
|
+
{
|
|
1185
|
+
correlation_id: meeting.correlationId,
|
|
1186
|
+
locus_id: meeting.locusUrl.split('/').pop(),
|
|
1187
|
+
connectionType: 'udp'
|
|
1188
|
+
}
|
|
1189
|
+
);
|
|
1190
|
+
});
|
|
1191
|
+
|
|
1102
1192
|
describe('handles StatsAnalyzer events', () => {
|
|
1103
1193
|
let prevConfigValue;
|
|
1104
1194
|
let statsAnalyzerStub;
|
|
@@ -117,14 +117,15 @@ describe('TurnDiscovery', () => {
|
|
|
117
117
|
// check that we've sent OK
|
|
118
118
|
await checkRoapMessageSent('OK', 0);
|
|
119
119
|
|
|
120
|
-
const
|
|
120
|
+
const {turnServerInfo, turnDiscoverySkippedReason} = await result;
|
|
121
121
|
|
|
122
|
-
assert.deepEqual(
|
|
122
|
+
assert.deepEqual(turnServerInfo, {
|
|
123
123
|
url: FAKE_TURN_URL,
|
|
124
124
|
username: FAKE_TURN_USERNAME,
|
|
125
125
|
password: FAKE_TURN_PASSWORD
|
|
126
126
|
});
|
|
127
127
|
|
|
128
|
+
assert.isUndefined(turnDiscoverySkippedReason);
|
|
128
129
|
});
|
|
129
130
|
|
|
130
131
|
it('sends TURN_DISCOVERY_REQUEST with empty mediaId when isReconnecting is true', async () => {
|
|
@@ -152,13 +153,14 @@ describe('TurnDiscovery', () => {
|
|
|
152
153
|
// check that we've sent OK
|
|
153
154
|
await checkRoapMessageSent('OK', 0);
|
|
154
155
|
|
|
155
|
-
const
|
|
156
|
+
const {turnServerInfo, turnDiscoverySkippedReason} = await result;
|
|
156
157
|
|
|
157
|
-
assert.deepEqual(
|
|
158
|
+
assert.deepEqual(turnServerInfo, {
|
|
158
159
|
url: FAKE_TURN_URL,
|
|
159
160
|
username: FAKE_TURN_USERNAME,
|
|
160
161
|
password: FAKE_TURN_PASSWORD
|
|
161
162
|
});
|
|
163
|
+
assert.isUndefined(turnDiscoverySkippedReason);
|
|
162
164
|
});
|
|
163
165
|
|
|
164
166
|
it('ignores any extra, unexpected headers in the response', async () => {
|
|
@@ -187,13 +189,13 @@ describe('TurnDiscovery', () => {
|
|
|
187
189
|
// check that we've sent OK and still parsed the headers we care about
|
|
188
190
|
await checkRoapMessageSent('OK', 0);
|
|
189
191
|
|
|
190
|
-
const
|
|
191
|
-
|
|
192
|
-
assert.deepEqual(turnInfo, {
|
|
192
|
+
const {turnServerInfo, turnDiscoverySkippedReason} = await result;
|
|
193
|
+
assert.deepEqual(turnServerInfo, {
|
|
193
194
|
url: FAKE_TURN_URL,
|
|
194
195
|
username: FAKE_TURN_USERNAME,
|
|
195
196
|
password: FAKE_TURN_PASSWORD
|
|
196
197
|
});
|
|
198
|
+
assert.isUndefined(turnDiscoverySkippedReason);
|
|
197
199
|
});
|
|
198
200
|
|
|
199
201
|
it('resolves with undefined if turn discovery feature is disabled in config', async () => {
|
|
@@ -203,7 +205,10 @@ describe('TurnDiscovery', () => {
|
|
|
203
205
|
|
|
204
206
|
const result = await new TurnDiscovery(mockRoapRequest).doTurnDiscovery(testMeeting);
|
|
205
207
|
|
|
206
|
-
|
|
208
|
+
const {turnServerInfo, turnDiscoverySkippedReason} = result;
|
|
209
|
+
|
|
210
|
+
assert.isUndefined(turnServerInfo);
|
|
211
|
+
assert.equal(turnDiscoverySkippedReason, 'config');
|
|
207
212
|
assert.notCalled(mockRoapRequest.sendRoap);
|
|
208
213
|
assert.notCalled(Metrics.sendBehavioralMetric);
|
|
209
214
|
|
|
@@ -218,7 +223,10 @@ describe('TurnDiscovery', () => {
|
|
|
218
223
|
|
|
219
224
|
const result = await td.doTurnDiscovery(testMeeting, false);
|
|
220
225
|
|
|
221
|
-
|
|
226
|
+
const {turnServerInfo, turnDiscoverySkippedReason} = result;
|
|
227
|
+
|
|
228
|
+
assert.isUndefined(turnServerInfo);
|
|
229
|
+
assert.isUndefined(turnDiscoverySkippedReason);
|
|
222
230
|
checkFailureMetricsSent();
|
|
223
231
|
});
|
|
224
232
|
|
|
@@ -227,7 +235,10 @@ describe('TurnDiscovery', () => {
|
|
|
227
235
|
testMeeting.webex.meetings.reachability.isAnyClusterReachable = () => true;
|
|
228
236
|
const result = await new TurnDiscovery(mockRoapRequest).doTurnDiscovery(testMeeting);
|
|
229
237
|
|
|
230
|
-
|
|
238
|
+
const {turnServerInfo, turnDiscoverySkippedReason} = result;
|
|
239
|
+
|
|
240
|
+
assert.isUndefined(turnServerInfo);
|
|
241
|
+
assert.equal(turnDiscoverySkippedReason, 'reachability');
|
|
231
242
|
assert.notCalled(mockRoapRequest.sendRoap);
|
|
232
243
|
assert.notCalled(Metrics.sendBehavioralMetric);
|
|
233
244
|
testMeeting.webex.meetings.reachability.isAnyClusterReachable = prev;
|
|
@@ -242,9 +253,10 @@ describe('TurnDiscovery', () => {
|
|
|
242
253
|
await clock.tickAsync(10 * 1000);
|
|
243
254
|
await testUtils.flushPromises();
|
|
244
255
|
|
|
245
|
-
const
|
|
256
|
+
const {turnServerInfo, turnDiscoverySkippedReason} = await promise;
|
|
246
257
|
|
|
247
|
-
assert.isUndefined(
|
|
258
|
+
assert.isUndefined(turnServerInfo);
|
|
259
|
+
assert.isUndefined(turnDiscoverySkippedReason);
|
|
248
260
|
checkFailureMetricsSent();
|
|
249
261
|
});
|
|
250
262
|
|
|
@@ -260,9 +272,10 @@ describe('TurnDiscovery', () => {
|
|
|
260
272
|
]
|
|
261
273
|
});
|
|
262
274
|
await testUtils.flushPromises();
|
|
263
|
-
const
|
|
275
|
+
const {turnServerInfo, turnDiscoverySkippedReason} = await turnDiscoveryPromise;
|
|
264
276
|
|
|
265
|
-
assert.isUndefined(
|
|
277
|
+
assert.isUndefined(turnServerInfo);
|
|
278
|
+
assert.isUndefined(turnDiscoverySkippedReason);
|
|
266
279
|
checkFailureMetricsSent();
|
|
267
280
|
});
|
|
268
281
|
|
|
@@ -274,9 +287,10 @@ describe('TurnDiscovery', () => {
|
|
|
274
287
|
td.handleTurnDiscoveryResponse({});
|
|
275
288
|
|
|
276
289
|
await testUtils.flushPromises();
|
|
277
|
-
const
|
|
290
|
+
const {turnServerInfo, turnDiscoverySkippedReason} = await turnDiscoveryPromise;
|
|
278
291
|
|
|
279
|
-
assert.isUndefined(
|
|
292
|
+
assert.isUndefined(turnServerInfo);
|
|
293
|
+
assert.isUndefined(turnDiscoverySkippedReason);
|
|
280
294
|
checkFailureMetricsSent();
|
|
281
295
|
});
|
|
282
296
|
|
|
@@ -288,9 +302,10 @@ describe('TurnDiscovery', () => {
|
|
|
288
302
|
td.handleTurnDiscoveryResponse({headers: []});
|
|
289
303
|
|
|
290
304
|
await testUtils.flushPromises();
|
|
291
|
-
const
|
|
305
|
+
const {turnServerInfo, turnDiscoverySkippedReason}= await turnDiscoveryPromise;
|
|
292
306
|
|
|
293
|
-
assert.isUndefined(
|
|
307
|
+
assert.isUndefined(turnServerInfo);
|
|
308
|
+
assert.isUndefined(turnDiscoverySkippedReason);
|
|
294
309
|
checkFailureMetricsSent();
|
|
295
310
|
});
|
|
296
311
|
|
|
@@ -321,9 +336,10 @@ describe('TurnDiscovery', () => {
|
|
|
321
336
|
// check that we've sent OK
|
|
322
337
|
await checkRoapMessageSent('OK', 0);
|
|
323
338
|
|
|
324
|
-
const
|
|
339
|
+
const {turnServerInfo, turnDiscoverySkippedReason} = await turnDiscoveryPromise;
|
|
325
340
|
|
|
326
|
-
assert.isUndefined(
|
|
341
|
+
assert.isUndefined(turnServerInfo);
|
|
342
|
+
assert.isUndefined(turnDiscoverySkippedReason);
|
|
327
343
|
checkFailureMetricsSent();
|
|
328
344
|
});
|
|
329
345
|
});
|