@webex/plugin-meetings 3.4.0 → 3.5.0-next.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/breakouts/breakout.js +1 -1
- package/dist/breakouts/index.js +1 -1
- package/dist/interpretation/index.js +1 -1
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/media/index.js +6 -9
- package/dist/media/index.js.map +1 -1
- package/dist/meeting/index.js +151 -50
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/util.js +1 -0
- package/dist/meeting/util.js.map +1 -1
- package/dist/meetings/index.js +11 -2
- package/dist/meetings/index.js.map +1 -1
- package/dist/reachability/index.js +175 -103
- package/dist/reachability/index.js.map +1 -1
- package/dist/reconnection-manager/index.js +1 -1
- package/dist/reconnection-manager/index.js.map +1 -1
- package/dist/rtcMetrics/index.js +26 -6
- package/dist/rtcMetrics/index.js.map +1 -1
- package/dist/types/meeting/index.d.ts +22 -2
- package/dist/types/meetings/index.d.ts +4 -2
- package/dist/types/reachability/index.d.ts +14 -2
- package/dist/types/rtcMetrics/index.d.ts +11 -1
- package/dist/webinar/index.js +1 -1
- package/package.json +22 -22
- package/src/media/index.ts +5 -9
- package/src/meeting/index.ts +88 -10
- package/src/meeting/util.ts +2 -0
- package/src/meetings/index.ts +11 -4
- package/src/reachability/index.ts +49 -4
- package/src/reconnection-manager/index.ts +1 -1
- package/src/rtcMetrics/index.ts +25 -5
- package/test/integration/spec/converged-space-meetings.js +1 -1
- package/test/unit/spec/breakouts/index.ts +1 -0
- package/test/unit/spec/interceptors/locusRetry.ts +11 -10
- package/test/unit/spec/media/MediaConnectionAwaiter.ts +1 -0
- package/test/unit/spec/media/index.ts +34 -7
- package/test/unit/spec/media/properties.ts +1 -1
- package/test/unit/spec/meeting/connectionStateHandler.ts +1 -0
- package/test/unit/spec/meeting/index.js +116 -12
- package/test/unit/spec/meeting/locusMediaRequest.ts +3 -2
- package/test/unit/spec/meeting/request.js +1 -0
- package/test/unit/spec/meeting/utils.js +4 -0
- package/test/unit/spec/meeting-info/meetinginfov2.js +10 -11
- package/test/unit/spec/meeting-info/request.js +1 -1
- package/test/unit/spec/meetings/index.js +40 -5
- package/test/unit/spec/members/request.js +2 -1
- package/test/unit/spec/multistream/mediaRequestManager.ts +1 -0
- package/test/unit/spec/multistream/receiveSlot.ts +1 -0
- package/test/unit/spec/multistream/receiveSlotManager.ts +1 -0
- package/test/unit/spec/multistream/remoteMedia.ts +1 -0
- package/test/unit/spec/multistream/remoteMediaGroup.ts +1 -0
- package/test/unit/spec/multistream/remoteMediaManager.ts +1 -0
- package/test/unit/spec/multistream/sendSlotManager.ts +1 -0
- package/test/unit/spec/personal-meeting-room/personal-meeting-room.js +0 -1
- package/test/unit/spec/reachability/index.ts +211 -13
- package/test/unit/spec/reachability/request.js +1 -0
- package/test/unit/spec/roap/request.ts +1 -0
- package/test/unit/spec/rtcMetrics/index.ts +31 -0
- package/dist/networkQualityMonitor/index.js +0 -227
- package/dist/networkQualityMonitor/index.js.map +0 -1
- package/dist/types/networkQualityMonitor/index.d.ts +0 -70
- package/src/networkQualityMonitor/index.ts +0 -211
- package/test/unit/spec/networkQualityMonitor/index.js +0 -99
|
@@ -93,6 +93,8 @@ export default class Reachability extends EventsScope {
|
|
|
93
93
|
expectedResultsCount = {videoMesh: {udp: 0}, public: {udp: 0, tcp: 0, xtls: 0}};
|
|
94
94
|
resultsCount = {videoMesh: {udp: 0}, public: {udp: 0, tcp: 0, xtls: 0}};
|
|
95
95
|
|
|
96
|
+
protected lastTrigger?: string;
|
|
97
|
+
|
|
96
98
|
/**
|
|
97
99
|
* Creates an instance of Reachability.
|
|
98
100
|
* @param {object} webex
|
|
@@ -114,18 +116,50 @@ export default class Reachability extends EventsScope {
|
|
|
114
116
|
this.clusterReachability = {};
|
|
115
117
|
}
|
|
116
118
|
|
|
119
|
+
/**
|
|
120
|
+
* Fetches the list of media clusters from the backend
|
|
121
|
+
* @param {boolean} isRetry
|
|
122
|
+
* @private
|
|
123
|
+
* @returns {Promise<{clusters: ClusterList, joinCookie: any}>}
|
|
124
|
+
*/
|
|
125
|
+
async getClusters(isRetry = false): Promise<{clusters: ClusterList; joinCookie: any}> {
|
|
126
|
+
try {
|
|
127
|
+
const {clusters, joinCookie} = await this.reachabilityRequest.getClusters(
|
|
128
|
+
MeetingUtil.getIpVersion(this.webex)
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
return {clusters, joinCookie};
|
|
132
|
+
} catch (error) {
|
|
133
|
+
if (isRetry) {
|
|
134
|
+
throw error;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
LoggerProxy.logger.error(
|
|
138
|
+
`Reachability:index#getClusters --> Failed with error: ${error}, retrying...`
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
return this.getClusters(true);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
117
145
|
/**
|
|
118
146
|
* Gets a list of media clusters from the backend and performs reachability checks on all the clusters
|
|
147
|
+
* @param {string} trigger - explains the reason for starting reachability
|
|
119
148
|
* @returns {Promise<ReachabilityResults>} reachability results
|
|
120
149
|
* @public
|
|
121
150
|
* @memberof Reachability
|
|
122
151
|
*/
|
|
123
|
-
public async gatherReachability(): Promise<ReachabilityResults> {
|
|
152
|
+
public async gatherReachability(trigger: string): Promise<ReachabilityResults> {
|
|
124
153
|
// Fetch clusters and measure latency
|
|
125
154
|
try {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
155
|
+
this.lastTrigger = trigger;
|
|
156
|
+
|
|
157
|
+
// kick off ip version detection. For now we don't await it, as we're doing it
|
|
158
|
+
// to gather the timings and send them with our reachability metrics
|
|
159
|
+
// @ts-ignore
|
|
160
|
+
this.webex.internal.device.ipNetworkDetector.detect();
|
|
161
|
+
|
|
162
|
+
const {clusters, joinCookie} = await this.getClusters();
|
|
129
163
|
|
|
130
164
|
// @ts-ignore
|
|
131
165
|
await this.webex.boundedStorage.put(
|
|
@@ -513,6 +547,17 @@ export default class Reachability extends EventsScope {
|
|
|
513
547
|
tcp: this.getStatistics(results, 'tcp', false),
|
|
514
548
|
xtls: this.getStatistics(results, 'xtls', false),
|
|
515
549
|
},
|
|
550
|
+
ipver: {
|
|
551
|
+
// @ts-ignore
|
|
552
|
+
firstIpV4: this.webex.internal.device.ipNetworkDetector.firstIpV4,
|
|
553
|
+
// @ts-ignore
|
|
554
|
+
firstIpV6: this.webex.internal.device.ipNetworkDetector.firstIpV6,
|
|
555
|
+
// @ts-ignore
|
|
556
|
+
firstMdns: this.webex.internal.device.ipNetworkDetector.firstMdns,
|
|
557
|
+
// @ts-ignore
|
|
558
|
+
totalTime: this.webex.internal.device.ipNetworkDetector.totalTime,
|
|
559
|
+
},
|
|
560
|
+
trigger: this.lastTrigger,
|
|
516
561
|
};
|
|
517
562
|
Metrics.sendBehavioralMetric(
|
|
518
563
|
BEHAVIORAL_METRICS.REACHABILITY_COMPLETED,
|
|
@@ -342,7 +342,7 @@ export default class ReconnectionManager {
|
|
|
342
342
|
}
|
|
343
343
|
|
|
344
344
|
try {
|
|
345
|
-
await this.webex.meetings.startReachability();
|
|
345
|
+
await this.webex.meetings.startReachability('reconnection');
|
|
346
346
|
} catch (err) {
|
|
347
347
|
LoggerProxy.logger.info(
|
|
348
348
|
'ReconnectionManager:index#reconnect --> Reachability failed, continuing with reconnection attempt, err: ',
|
package/src/rtcMetrics/index.ts
CHANGED
|
@@ -34,6 +34,8 @@ export default class RtcMetrics {
|
|
|
34
34
|
|
|
35
35
|
connectionId: string;
|
|
36
36
|
|
|
37
|
+
shouldSendMetricsOnNextStatsReport: boolean;
|
|
38
|
+
|
|
37
39
|
/**
|
|
38
40
|
* Initialize the interval.
|
|
39
41
|
*
|
|
@@ -47,9 +49,7 @@ export default class RtcMetrics {
|
|
|
47
49
|
this.meetingId = meetingId;
|
|
48
50
|
this.webex = webex;
|
|
49
51
|
this.correlationId = correlationId;
|
|
50
|
-
this.
|
|
51
|
-
// Send the first set of metrics at 5 seconds in the case of a user leaving the call shortly after joining.
|
|
52
|
-
setTimeout(this.sendMetricsInQueue.bind(this), 5 * 1000);
|
|
52
|
+
this.resetConnection();
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
/**
|
|
@@ -64,6 +64,18 @@ export default class RtcMetrics {
|
|
|
64
64
|
}
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
+
/**
|
|
68
|
+
* Forces sending metrics when we get the next stats-report
|
|
69
|
+
*
|
|
70
|
+
* This is useful for cases when something important happens that affects the media connection,
|
|
71
|
+
* for example when we move from lobby into the meeting.
|
|
72
|
+
*
|
|
73
|
+
* @returns {void}
|
|
74
|
+
*/
|
|
75
|
+
public sendNextMetrics() {
|
|
76
|
+
this.shouldSendMetricsOnNextStatsReport = true;
|
|
77
|
+
}
|
|
78
|
+
|
|
67
79
|
/**
|
|
68
80
|
* Add metrics items to the metrics queue.
|
|
69
81
|
*
|
|
@@ -79,6 +91,13 @@ export default class RtcMetrics {
|
|
|
79
91
|
|
|
80
92
|
this.metricsQueue.push(data);
|
|
81
93
|
|
|
94
|
+
if (this.shouldSendMetricsOnNextStatsReport && data.name === 'stats-report') {
|
|
95
|
+
// this is the first useful set of data (WCME gives it to us after 5s), send it out immediately
|
|
96
|
+
// in case the user is unhappy and closes the browser early
|
|
97
|
+
this.sendMetricsInQueue();
|
|
98
|
+
this.shouldSendMetricsOnNextStatsReport = false;
|
|
99
|
+
}
|
|
100
|
+
|
|
82
101
|
try {
|
|
83
102
|
// If a connection fails, send the rest of the metrics in queue and get a new connection id.
|
|
84
103
|
const parsedPayload = parseJsonPayload(data.payload);
|
|
@@ -88,7 +107,7 @@ export default class RtcMetrics {
|
|
|
88
107
|
parsedPayload.value === 'failed'
|
|
89
108
|
) {
|
|
90
109
|
this.sendMetricsInQueue();
|
|
91
|
-
this.
|
|
110
|
+
this.resetConnection();
|
|
92
111
|
}
|
|
93
112
|
} catch (e) {
|
|
94
113
|
console.error(e);
|
|
@@ -130,8 +149,9 @@ export default class RtcMetrics {
|
|
|
130
149
|
*
|
|
131
150
|
* @returns {void}
|
|
132
151
|
*/
|
|
133
|
-
private
|
|
152
|
+
private resetConnection() {
|
|
134
153
|
this.connectionId = uuid.v4();
|
|
154
|
+
this.shouldSendMetricsOnNextStatsReport = true;
|
|
135
155
|
}
|
|
136
156
|
|
|
137
157
|
/**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { config } from 'dotenv';
|
|
2
1
|
import 'jsdom-global/register';
|
|
2
|
+
import {config} from 'dotenv';
|
|
3
3
|
import {assert} from '@webex/test-helper-chai';
|
|
4
4
|
import {skipInNode} from '@webex/test-helper-mocha';
|
|
5
5
|
import BrowserDetection from '@webex/plugin-meetings/dist/common/browser-detection';
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
/* eslint-disable camelcase */
|
|
6
|
+
import 'jsdom-global/register';
|
|
6
7
|
import {assert} from '@webex/test-helper-chai';
|
|
7
8
|
import { expect } from "@webex/test-helper-chai";
|
|
8
9
|
import MockWebex from '@webex/test-helper-mock-webex';
|
|
@@ -13,7 +14,7 @@ import sinon from 'sinon';
|
|
|
13
14
|
|
|
14
15
|
describe('plugin-meetings', () => {
|
|
15
16
|
describe('Interceptors', () => {
|
|
16
|
-
describe('LocusRetryStatusInterceptor', () => {
|
|
17
|
+
describe('LocusRetryStatusInterceptor', () => {
|
|
17
18
|
let interceptor, webex;
|
|
18
19
|
beforeEach(() => {
|
|
19
20
|
webex = new MockWebex({
|
|
@@ -24,7 +25,7 @@ describe('plugin-meetings', () => {
|
|
|
24
25
|
interceptor = Reflect.apply(LocusRetryStatusInterceptor.create, {
|
|
25
26
|
sessionId: 'mock-webex_uuid',
|
|
26
27
|
}, []);
|
|
27
|
-
});
|
|
28
|
+
});
|
|
28
29
|
describe('#onResponseError', () => {
|
|
29
30
|
const options = {
|
|
30
31
|
method: 'POST',
|
|
@@ -41,7 +42,7 @@ describe('plugin-meetings', () => {
|
|
|
41
42
|
headers: {
|
|
42
43
|
trackingid: 'test',
|
|
43
44
|
'retry-after': 1000,
|
|
44
|
-
},
|
|
45
|
+
},
|
|
45
46
|
uri: `https://locus-test.webex.com/locus/api/v1/loci/call`,
|
|
46
47
|
},
|
|
47
48
|
body: {
|
|
@@ -54,7 +55,7 @@ describe('plugin-meetings', () => {
|
|
|
54
55
|
headers: {
|
|
55
56
|
trackingid: 'test',
|
|
56
57
|
'retry-after': 1000,
|
|
57
|
-
},
|
|
58
|
+
},
|
|
58
59
|
uri: `https://locus-test.webex.com/locus/api/v1/loci/call`,
|
|
59
60
|
},
|
|
60
61
|
body: {
|
|
@@ -73,7 +74,7 @@ describe('plugin-meetings', () => {
|
|
|
73
74
|
|
|
74
75
|
return interceptor.onResponseError(options, reason2).then(() => {
|
|
75
76
|
expect(handleRetryStub.calledWith(options, 1000)).to.be.true;
|
|
76
|
-
|
|
77
|
+
|
|
77
78
|
});
|
|
78
79
|
});
|
|
79
80
|
});
|
|
@@ -92,7 +93,7 @@ describe('plugin-meetings', () => {
|
|
|
92
93
|
it('returns the correct resolved value when the request is successful', () => {
|
|
93
94
|
const mockResponse = 'mock response'
|
|
94
95
|
interceptor.webex.request = sinon.stub().returns(Promise.resolve(mockResponse));
|
|
95
|
-
|
|
96
|
+
|
|
96
97
|
return interceptor.handleRetryRequestLocusServiceError(options, retryAfterTime)
|
|
97
98
|
.then((response) => {
|
|
98
99
|
expect(response).to.equal(mockResponse);
|
|
@@ -101,9 +102,9 @@ describe('plugin-meetings', () => {
|
|
|
101
102
|
|
|
102
103
|
it('rejects the promise when the request is unsuccessful', () => {
|
|
103
104
|
const rejectionReason = 'Service Unavaialble after retry';
|
|
104
|
-
|
|
105
|
+
|
|
105
106
|
interceptor.webex.request = sinon.stub().returns(Promise.reject(rejectionReason));
|
|
106
|
-
|
|
107
|
+
|
|
107
108
|
return interceptor.handleRetryRequestLocusServiceError(options, retryAfterTime)
|
|
108
109
|
.catch((error) => {
|
|
109
110
|
expect(error).to.equal(rejectionReason);
|
|
@@ -114,10 +115,10 @@ describe('plugin-meetings', () => {
|
|
|
114
115
|
let clock;
|
|
115
116
|
clock = sinon.useFakeTimers();
|
|
116
117
|
const mockResponse = 'mock response'
|
|
117
|
-
|
|
118
|
+
|
|
118
119
|
interceptor.webex.request = sinon.stub().returns(Promise.resolve(mockResponse));
|
|
119
120
|
const promise = interceptor.handleRetryRequestLocusServiceError(options, retryAfterTime);
|
|
120
|
-
|
|
121
|
+
|
|
121
122
|
clock.tick(retryAfterTime);
|
|
122
123
|
|
|
123
124
|
return promise.then(() => {
|
|
@@ -1,16 +1,15 @@
|
|
|
1
|
+
import 'jsdom-global/register';
|
|
1
2
|
import * as InternalMediaCoreModule from '@webex/internal-media-core';
|
|
2
3
|
import Media from '@webex/plugin-meetings/src/media/index';
|
|
3
4
|
import {assert} from '@webex/test-helper-chai';
|
|
4
5
|
import sinon from 'sinon';
|
|
5
6
|
import StaticConfig from '@webex/plugin-meetings/src/common/config';
|
|
6
|
-
import MockWebex from '@webex/test-helper-mock-webex';
|
|
7
7
|
|
|
8
8
|
describe('createMediaConnection', () => {
|
|
9
9
|
let clock;
|
|
10
10
|
beforeEach(() => {
|
|
11
11
|
clock = sinon.useFakeTimers();
|
|
12
12
|
});
|
|
13
|
-
const webex = MockWebex();
|
|
14
13
|
|
|
15
14
|
const fakeRoapMediaConnection = {
|
|
16
15
|
id: 'roap media connection',
|
|
@@ -61,7 +60,7 @@ describe('createMediaConnection', () => {
|
|
|
61
60
|
const ENABLE_EXTMAP = false;
|
|
62
61
|
const ENABLE_RTX = true;
|
|
63
62
|
|
|
64
|
-
Media.createMediaConnection(false, 'some debug id',
|
|
63
|
+
Media.createMediaConnection(false, 'some debug id', 'meetingId', {
|
|
65
64
|
mediaProperties: {
|
|
66
65
|
mediaDirection: {
|
|
67
66
|
sendAudio: false,
|
|
@@ -139,7 +138,13 @@ describe('createMediaConnection', () => {
|
|
|
139
138
|
.stub(InternalMediaCoreModule, 'MultistreamRoapMediaConnection')
|
|
140
139
|
.returns(fakeRoapMediaConnection);
|
|
141
140
|
|
|
142
|
-
|
|
141
|
+
const rtcMetrics = {
|
|
142
|
+
addMetrics: sinon.stub(),
|
|
143
|
+
closeMetrics: sinon.stub(),
|
|
144
|
+
sendMetricsInQueue: sinon.stub(),
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
Media.createMediaConnection(true, 'some debug id', 'meeting id', {
|
|
143
148
|
mediaProperties: {
|
|
144
149
|
mediaDirection: {
|
|
145
150
|
sendAudio: true,
|
|
@@ -150,6 +155,7 @@ describe('createMediaConnection', () => {
|
|
|
150
155
|
receiveShare: true,
|
|
151
156
|
},
|
|
152
157
|
},
|
|
158
|
+
rtcMetrics,
|
|
153
159
|
turnServerInfo: {
|
|
154
160
|
url: 'turns:turn-server-url:443?transport=tcp',
|
|
155
161
|
username: 'turn username',
|
|
@@ -177,6 +183,27 @@ describe('createMediaConnection', () => {
|
|
|
177
183
|
},
|
|
178
184
|
'meeting id'
|
|
179
185
|
);
|
|
186
|
+
|
|
187
|
+
// check if rtcMetrics callbacks are configured correctly
|
|
188
|
+
const addMetricsCallback = multistreamRoapMediaConnectionConstructorStub.getCalls()[0].args[2];
|
|
189
|
+
const closeMetricsCallback = multistreamRoapMediaConnectionConstructorStub.getCalls()[0].args[3];
|
|
190
|
+
const sendMetricsInQueueCallback = multistreamRoapMediaConnectionConstructorStub.getCalls()[0].args[4];
|
|
191
|
+
|
|
192
|
+
assert.isFunction(addMetricsCallback);
|
|
193
|
+
assert.isFunction(closeMetricsCallback);
|
|
194
|
+
assert.isFunction(sendMetricsInQueueCallback);
|
|
195
|
+
|
|
196
|
+
const fakeMetricsData = {id: 'metrics data'};
|
|
197
|
+
|
|
198
|
+
addMetricsCallback(fakeMetricsData);
|
|
199
|
+
assert.calledOnceWithExactly(rtcMetrics.addMetrics, fakeMetricsData);
|
|
200
|
+
|
|
201
|
+
closeMetricsCallback();
|
|
202
|
+
assert.calledOnce(rtcMetrics.closeMetrics);
|
|
203
|
+
|
|
204
|
+
sendMetricsInQueueCallback();
|
|
205
|
+
assert.calledOnce(rtcMetrics.sendMetricsInQueue);
|
|
206
|
+
|
|
180
207
|
});
|
|
181
208
|
|
|
182
209
|
[
|
|
@@ -191,7 +218,7 @@ describe('createMediaConnection', () => {
|
|
|
191
218
|
.stub(InternalMediaCoreModule, 'MultistreamRoapMediaConnection')
|
|
192
219
|
.returns(fakeRoapMediaConnection);
|
|
193
220
|
|
|
194
|
-
Media.createMediaConnection(true, 'debug string',
|
|
221
|
+
Media.createMediaConnection(true, 'debug string', 'meeting id', {
|
|
195
222
|
mediaProperties: {
|
|
196
223
|
mediaDirection: {
|
|
197
224
|
sendAudio: true,
|
|
@@ -220,7 +247,7 @@ describe('createMediaConnection', () => {
|
|
|
220
247
|
.stub(InternalMediaCoreModule, 'MultistreamRoapMediaConnection')
|
|
221
248
|
.returns(fakeRoapMediaConnection);
|
|
222
249
|
|
|
223
|
-
Media.createMediaConnection(true, 'debug string',
|
|
250
|
+
Media.createMediaConnection(true, 'debug string', 'meeting id', {
|
|
224
251
|
mediaProperties: {
|
|
225
252
|
mediaDirection: {
|
|
226
253
|
sendAudio: true,
|
|
@@ -260,7 +287,7 @@ describe('createMediaConnection', () => {
|
|
|
260
287
|
const ENABLE_EXTMAP = false;
|
|
261
288
|
const ENABLE_RTX = true;
|
|
262
289
|
|
|
263
|
-
Media.createMediaConnection(false, 'some debug id',
|
|
290
|
+
Media.createMediaConnection(false, 'some debug id', 'meeting id', {
|
|
264
291
|
mediaProperties: {
|
|
265
292
|
mediaDirection: {
|
|
266
293
|
sendAudio: true,
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
import 'jsdom-global/register';
|
|
1
2
|
import {assert} from '@webex/test-helper-chai';
|
|
2
3
|
import sinon from 'sinon';
|
|
3
4
|
import {ConnectionState} from '@webex/internal-media-core';
|
|
4
5
|
import MediaProperties from '@webex/plugin-meetings/src/media/properties';
|
|
5
|
-
import testUtils from '../../../utils/testUtils';
|
|
6
6
|
import {Defer} from '@webex/common';
|
|
7
7
|
import MediaConnectionAwaiter from '../../../../src/media/MediaConnectionAwaiter';
|
|
8
8
|
|
|
@@ -5,6 +5,8 @@ import 'jsdom-global/register';
|
|
|
5
5
|
import {cloneDeep, forEach, isEqual, isUndefined} from 'lodash';
|
|
6
6
|
import sinon from 'sinon';
|
|
7
7
|
import * as InternalMediaCoreModule from '@webex/internal-media-core';
|
|
8
|
+
import * as RtcMetricsModule from '@webex/plugin-meetings/src/rtcMetrics';
|
|
9
|
+
import * as RemoteMediaManagerModule from '@webex/plugin-meetings/src/multistream/remoteMediaManager';
|
|
8
10
|
import StateMachine from 'javascript-state-machine';
|
|
9
11
|
import uuid from 'uuid';
|
|
10
12
|
import {assert, expect} from '@webex/test-helper-chai';
|
|
@@ -305,7 +307,7 @@ describe('plugin-meetings', () => {
|
|
|
305
307
|
assert.equal(meeting.resource, uuid2);
|
|
306
308
|
assert.equal(meeting.deviceUrl, uuid3);
|
|
307
309
|
assert.equal(meeting.correlationId, correlationId);
|
|
308
|
-
assert.deepEqual(meeting.callStateForMetrics, {correlationId});
|
|
310
|
+
assert.deepEqual(meeting.callStateForMetrics, {correlationId, sessionCorrelationId: ''});
|
|
309
311
|
assert.deepEqual(meeting.meetingInfo, {});
|
|
310
312
|
assert.instanceOf(meeting.members, Members);
|
|
311
313
|
assert.calledOnceWithExactly(
|
|
@@ -329,6 +331,7 @@ describe('plugin-meetings', () => {
|
|
|
329
331
|
assert.isNull(meeting.partner);
|
|
330
332
|
assert.isNull(meeting.type);
|
|
331
333
|
assert.isNull(meeting.owner);
|
|
334
|
+
assert.isUndefined(meeting.isoLocalClientMeetingJoinTime);
|
|
332
335
|
assert.isNull(meeting.hostId);
|
|
333
336
|
assert.isNull(meeting.policy);
|
|
334
337
|
assert.instanceOf(meeting.meetingRequest, MeetingRequest);
|
|
@@ -373,7 +376,7 @@ describe('plugin-meetings', () => {
|
|
|
373
376
|
}
|
|
374
377
|
);
|
|
375
378
|
assert.equal(newMeeting.correlationId, newMeeting.id);
|
|
376
|
-
assert.deepEqual(newMeeting.callStateForMetrics, {correlationId: newMeeting.id});
|
|
379
|
+
assert.deepEqual(newMeeting.callStateForMetrics, {correlationId: newMeeting.id, sessionCorrelationId: ''});
|
|
377
380
|
});
|
|
378
381
|
|
|
379
382
|
it('correlationId can be provided in callStateForMetrics', () => {
|
|
@@ -400,6 +403,36 @@ describe('plugin-meetings', () => {
|
|
|
400
403
|
correlationId: uuid4,
|
|
401
404
|
joinTrigger: 'fake-join-trigger',
|
|
402
405
|
loginType: 'fake-login-type',
|
|
406
|
+
sessionCorrelationId: '',
|
|
407
|
+
});
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
it('sessionCorrelationId can be provided in callStateForMetrics', () => {
|
|
411
|
+
const newMeeting = new Meeting(
|
|
412
|
+
{
|
|
413
|
+
userId: uuid1,
|
|
414
|
+
resource: uuid2,
|
|
415
|
+
deviceUrl: uuid3,
|
|
416
|
+
locus: {url: url1},
|
|
417
|
+
destination: testDestination,
|
|
418
|
+
destinationType: DESTINATION_TYPE.MEETING_ID,
|
|
419
|
+
callStateForMetrics: {
|
|
420
|
+
correlationId: uuid4,
|
|
421
|
+
sessionCorrelationId: uuid1,
|
|
422
|
+
joinTrigger: 'fake-join-trigger',
|
|
423
|
+
loginType: 'fake-login-type',
|
|
424
|
+
},
|
|
425
|
+
},
|
|
426
|
+
{
|
|
427
|
+
parent: webex,
|
|
428
|
+
}
|
|
429
|
+
);
|
|
430
|
+
assert.exists(newMeeting.sessionCorrelationId);
|
|
431
|
+
assert.deepEqual(newMeeting.callStateForMetrics, {
|
|
432
|
+
correlationId: uuid4,
|
|
433
|
+
sessionCorrelationId: uuid1,
|
|
434
|
+
joinTrigger: 'fake-join-trigger',
|
|
435
|
+
loginType: 'fake-login-type',
|
|
403
436
|
});
|
|
404
437
|
});
|
|
405
438
|
|
|
@@ -1585,6 +1618,10 @@ describe('plugin-meetings', () => {
|
|
|
1585
1618
|
sandbox.stub(MeetingUtil, 'joinMeeting').returns(Promise.resolve(joinMeetingResult));
|
|
1586
1619
|
});
|
|
1587
1620
|
|
|
1621
|
+
afterEach(() => {
|
|
1622
|
+
assert.exists(meeting.isoLocalClientMeetingJoinTime);
|
|
1623
|
+
});
|
|
1624
|
+
|
|
1588
1625
|
it('should join the meeting and return promise', async () => {
|
|
1589
1626
|
const join = meeting.join({pstnAudioType: 'dial-in'});
|
|
1590
1627
|
meeting.config.enableAutomaticLLM = true;
|
|
@@ -1603,6 +1640,7 @@ describe('plugin-meetings', () => {
|
|
|
1603
1640
|
const result = await join;
|
|
1604
1641
|
|
|
1605
1642
|
assert.calledOnce(MeetingUtil.joinMeeting);
|
|
1643
|
+
assert.calledOnce(webex.internal.device.meetingStarted);
|
|
1606
1644
|
assert.calledOnce(meeting.setLocus);
|
|
1607
1645
|
assert.equal(result, joinMeetingResult);
|
|
1608
1646
|
assert.calledWith(webex.internal.llm.on, 'online', meeting.handleLLMOnline);
|
|
@@ -2405,9 +2443,7 @@ describe('plugin-meetings', () => {
|
|
|
2405
2443
|
Media.createMediaConnection,
|
|
2406
2444
|
false,
|
|
2407
2445
|
meeting.getMediaConnectionDebugId(),
|
|
2408
|
-
webex,
|
|
2409
2446
|
meeting.id,
|
|
2410
|
-
meeting.correlationId,
|
|
2411
2447
|
sinon.match({turnServerInfo: undefined})
|
|
2412
2448
|
);
|
|
2413
2449
|
assert.calledOnce(meeting.setMercuryListener);
|
|
@@ -2449,6 +2485,44 @@ describe('plugin-meetings', () => {
|
|
|
2449
2485
|
checkWorking({allowMediaInLobby: true});
|
|
2450
2486
|
});
|
|
2451
2487
|
|
|
2488
|
+
it('should create rtcMetrics and pass them to Media.createMediaConnection()', async () => {
|
|
2489
|
+
const fakeRtcMetrics = {id: 'fake rtc metrics object'};
|
|
2490
|
+
const rtcMetricsCtor = sinon.stub(RtcMetricsModule, 'default').returns(fakeRtcMetrics);
|
|
2491
|
+
|
|
2492
|
+
// setup the minimum mocks required for multistream connection
|
|
2493
|
+
fakeMediaConnection.createSendSlot = sinon.stub().returns({
|
|
2494
|
+
publishStream: sinon.stub(),
|
|
2495
|
+
unpublishStream: sinon.stub(),
|
|
2496
|
+
setNamedMediaGroups: sinon.stub(),
|
|
2497
|
+
});
|
|
2498
|
+
sinon.stub(RemoteMediaManagerModule, 'RemoteMediaManager').returns({
|
|
2499
|
+
start: sinon.stub().resolves(),
|
|
2500
|
+
on: sinon.stub(),
|
|
2501
|
+
logAllReceiveSlots: sinon.stub(),
|
|
2502
|
+
});
|
|
2503
|
+
|
|
2504
|
+
meeting.meetingState = 'ACTIVE';
|
|
2505
|
+
meeting.isMultistream = true;
|
|
2506
|
+
|
|
2507
|
+
await meeting.addMedia({
|
|
2508
|
+
mediaSettings: {},
|
|
2509
|
+
});
|
|
2510
|
+
|
|
2511
|
+
assert.calledOnceWithExactly(rtcMetricsCtor, webex, meeting.id, meeting.correlationId);
|
|
2512
|
+
|
|
2513
|
+
// check that rtcMetrics was passed to Media.createMediaConnection
|
|
2514
|
+
assert.calledOnce(Media.createMediaConnection);
|
|
2515
|
+
assert.calledWith(
|
|
2516
|
+
Media.createMediaConnection,
|
|
2517
|
+
true,
|
|
2518
|
+
meeting.getMediaConnectionDebugId(),
|
|
2519
|
+
meeting.id,
|
|
2520
|
+
sinon.match({
|
|
2521
|
+
rtcMetrics: fakeRtcMetrics,
|
|
2522
|
+
})
|
|
2523
|
+
);
|
|
2524
|
+
});
|
|
2525
|
+
|
|
2452
2526
|
it('should pass the turn server info to the peer connection', async () => {
|
|
2453
2527
|
const FAKE_TURN_URL = 'turns:webex.com:3478';
|
|
2454
2528
|
const FAKE_TURN_USER = 'some-turn-username';
|
|
@@ -2478,9 +2552,7 @@ describe('plugin-meetings', () => {
|
|
|
2478
2552
|
Media.createMediaConnection,
|
|
2479
2553
|
false,
|
|
2480
2554
|
meeting.getMediaConnectionDebugId(),
|
|
2481
|
-
webex,
|
|
2482
2555
|
meeting.id,
|
|
2483
|
-
meeting.correlationId,
|
|
2484
2556
|
sinon.match({
|
|
2485
2557
|
turnServerInfo: {
|
|
2486
2558
|
url: FAKE_TURN_URL,
|
|
@@ -3023,6 +3095,8 @@ describe('plugin-meetings', () => {
|
|
|
3023
3095
|
}),
|
|
3024
3096
|
};
|
|
3025
3097
|
meeting.iceCandidatesCount = 3;
|
|
3098
|
+
meeting.iceCandidateErrors.set('701_error', 3);
|
|
3099
|
+
meeting.iceCandidateErrors.set('701_turn_host_lookup_received_error', 1);
|
|
3026
3100
|
|
|
3027
3101
|
await meeting.addMedia({
|
|
3028
3102
|
mediaSettings: {},
|
|
@@ -3044,6 +3118,8 @@ describe('plugin-meetings', () => {
|
|
|
3044
3118
|
someReachabilityMetric1: 'some value1',
|
|
3045
3119
|
someReachabilityMetric2: 'some value2',
|
|
3046
3120
|
iceCandidatesCount: 3,
|
|
3121
|
+
'701_error': 3,
|
|
3122
|
+
'701_turn_host_lookup_received_error': 1,
|
|
3047
3123
|
}
|
|
3048
3124
|
);
|
|
3049
3125
|
|
|
@@ -3397,9 +3473,7 @@ describe('plugin-meetings', () => {
|
|
|
3397
3473
|
Media.createMediaConnection,
|
|
3398
3474
|
false,
|
|
3399
3475
|
meeting.getMediaConnectionDebugId(),
|
|
3400
|
-
webex,
|
|
3401
3476
|
meeting.id,
|
|
3402
|
-
meeting.correlationId,
|
|
3403
3477
|
sinon.match({
|
|
3404
3478
|
turnServerInfo: {
|
|
3405
3479
|
url: FAKE_TURN_URL,
|
|
@@ -4240,6 +4314,20 @@ describe('plugin-meetings', () => {
|
|
|
4240
4314
|
assert.calledTwice(locusMediaRequestStub);
|
|
4241
4315
|
});
|
|
4242
4316
|
|
|
4317
|
+
it('addMedia() works correctly when media is disabled with no streams to publish', async () => {
|
|
4318
|
+
const handleDeviceLoggingSpy = sinon.spy(Meeting, 'handleDeviceLogging');
|
|
4319
|
+
await meeting.addMedia({audioEnabled: false});
|
|
4320
|
+
//calling handleDeviceLogging with audioEnaled as true adn videoEnabled as false
|
|
4321
|
+
assert.calledWith(handleDeviceLoggingSpy,false,true);
|
|
4322
|
+
});
|
|
4323
|
+
|
|
4324
|
+
it('addMedia() works correctly when video is disabled with no streams to publish', async () => {
|
|
4325
|
+
const handleDeviceLoggingSpy = sinon.spy(Meeting, 'handleDeviceLogging');
|
|
4326
|
+
await meeting.addMedia({videoEnabled: false});
|
|
4327
|
+
//calling handleDeviceLogging audioEnabled as true videoEnabled as false
|
|
4328
|
+
assert.calledWith(handleDeviceLoggingSpy,true,false);
|
|
4329
|
+
});
|
|
4330
|
+
|
|
4243
4331
|
it('addMedia() works correctly when video is disabled with no streams to publish', async () => {
|
|
4244
4332
|
await meeting.addMedia({videoEnabled: false});
|
|
4245
4333
|
await simulateRoapOffer();
|
|
@@ -4306,6 +4394,14 @@ describe('plugin-meetings', () => {
|
|
|
4306
4394
|
assert.calledTwice(locusMediaRequestStub);
|
|
4307
4395
|
});
|
|
4308
4396
|
|
|
4397
|
+
|
|
4398
|
+
it('addMedia() works correctly when both shareAudio and shareVideo is disabled with no streams publish', async () => {
|
|
4399
|
+
const handleDeviceLoggingSpy = sinon.spy(Meeting, 'handleDeviceLogging');
|
|
4400
|
+
await meeting.addMedia({shareAudioEnabled: false, shareVideoEnabled: false});
|
|
4401
|
+
//calling handleDeviceLogging with audioEnabled true and videoEnabled as true
|
|
4402
|
+
assert.calledWith(handleDeviceLoggingSpy,true,true);
|
|
4403
|
+
});
|
|
4404
|
+
|
|
4309
4405
|
describe('publishStreams()/unpublishStreams() calls', () => {
|
|
4310
4406
|
[
|
|
4311
4407
|
{mediaEnabled: true, expected: {direction: 'sendrecv', localMuteSentValue: false}},
|
|
@@ -6861,33 +6957,36 @@ describe('plugin-meetings', () => {
|
|
|
6861
6957
|
describe('#setCorrelationId', () => {
|
|
6862
6958
|
it('should set the correlationId and return undefined', () => {
|
|
6863
6959
|
assert.equal(meeting.correlationId, correlationId);
|
|
6864
|
-
assert.deepEqual(meeting.callStateForMetrics, {correlationId});
|
|
6960
|
+
assert.deepEqual(meeting.callStateForMetrics, {correlationId, sessionCorrelationId: ''});
|
|
6865
6961
|
meeting.setCorrelationId(uuid1);
|
|
6866
6962
|
assert.equal(meeting.correlationId, uuid1);
|
|
6867
|
-
assert.deepEqual(meeting.callStateForMetrics, {correlationId: uuid1});
|
|
6963
|
+
assert.deepEqual(meeting.callStateForMetrics, {correlationId: uuid1, sessionCorrelationId: ''});
|
|
6868
6964
|
});
|
|
6869
6965
|
});
|
|
6870
6966
|
|
|
6871
6967
|
describe('#updateCallStateForMetrics', () => {
|
|
6872
6968
|
it('should update the callState, overriding existing values', () => {
|
|
6873
|
-
assert.deepEqual(meeting.callStateForMetrics, {correlationId});
|
|
6969
|
+
assert.deepEqual(meeting.callStateForMetrics, {correlationId, sessionCorrelationId: ''});
|
|
6874
6970
|
meeting.updateCallStateForMetrics({
|
|
6875
6971
|
correlationId: uuid1,
|
|
6972
|
+
sessionCorrelationId: uuid3,
|
|
6876
6973
|
joinTrigger: 'jt',
|
|
6877
6974
|
loginType: 'lt',
|
|
6878
6975
|
});
|
|
6879
6976
|
assert.deepEqual(meeting.callStateForMetrics, {
|
|
6880
6977
|
correlationId: uuid1,
|
|
6978
|
+
sessionCorrelationId: uuid3,
|
|
6881
6979
|
joinTrigger: 'jt',
|
|
6882
6980
|
loginType: 'lt',
|
|
6883
6981
|
});
|
|
6884
6982
|
});
|
|
6885
6983
|
|
|
6886
6984
|
it('should update the callState, keeping non-supplied values', () => {
|
|
6887
|
-
assert.deepEqual(meeting.callStateForMetrics, {correlationId});
|
|
6985
|
+
assert.deepEqual(meeting.callStateForMetrics, {correlationId, sessionCorrelationId: ''});
|
|
6888
6986
|
meeting.updateCallStateForMetrics({joinTrigger: 'jt', loginType: 'lt'});
|
|
6889
6987
|
assert.deepEqual(meeting.callStateForMetrics, {
|
|
6890
6988
|
correlationId,
|
|
6989
|
+
sessionCorrelationId: '',
|
|
6891
6990
|
joinTrigger: 'jt',
|
|
6892
6991
|
loginType: 'lt',
|
|
6893
6992
|
});
|
|
@@ -8450,6 +8549,9 @@ describe('plugin-meetings', () => {
|
|
|
8450
8549
|
it('listens to the self admitted guest event', (done) => {
|
|
8451
8550
|
meeting.stopKeepAlive = sinon.stub();
|
|
8452
8551
|
meeting.updateLLMConnection = sinon.stub();
|
|
8552
|
+
meeting.rtcMetrics = {
|
|
8553
|
+
sendNextMetrics: sinon.stub(),
|
|
8554
|
+
};
|
|
8453
8555
|
meeting.locusInfo.emit({function: 'test', file: 'test'}, 'SELF_ADMITTED_GUEST', test1);
|
|
8454
8556
|
assert.calledOnceWithExactly(meeting.stopKeepAlive);
|
|
8455
8557
|
assert.calledThrice(TriggerProxy.trigger);
|
|
@@ -8461,6 +8563,8 @@ describe('plugin-meetings', () => {
|
|
|
8461
8563
|
{payload: test1}
|
|
8462
8564
|
);
|
|
8463
8565
|
assert.calledOnce(meeting.updateLLMConnection);
|
|
8566
|
+
assert.calledOnceWithExactly(meeting.rtcMetrics.sendNextMetrics);
|
|
8567
|
+
|
|
8464
8568
|
done();
|
|
8465
8569
|
});
|
|
8466
8570
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import 'jsdom-global/register';
|
|
1
2
|
import sinon from 'sinon';
|
|
2
3
|
import {assert} from '@webex/test-helper-chai';
|
|
3
|
-
import { cloneDeep
|
|
4
|
+
import { cloneDeep } from 'lodash';
|
|
4
5
|
|
|
5
6
|
import MockWebex from '@webex/test-helper-mock-webex';
|
|
6
7
|
import Meetings from '@webex/plugin-meetings';
|
|
@@ -495,4 +496,4 @@ describe('LocusMediaRequest.send()', () => {
|
|
|
495
496
|
});
|
|
496
497
|
|
|
497
498
|
});
|
|
498
|
-
})
|
|
499
|
+
})
|