@webex/plugin-meetings 3.4.0 → 3.5.0-next.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/breakouts/breakout.js +1 -1
- package/dist/breakouts/index.js +1 -1
- package/dist/constants.js +1 -0
- package/dist/constants.js.map +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/in-meeting-actions.js +3 -1
- package/dist/meeting/in-meeting-actions.js.map +1 -1
- package/dist/meeting/index.js +154 -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/types/constants.d.ts +2 -1
- package/dist/types/meeting/in-meeting-actions.d.ts +2 -0
- 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/webinar/index.js +1 -1
- package/package.json +22 -22
- package/src/constants.ts +1 -0
- package/src/media/index.ts +6 -10
- package/src/meeting/in-meeting-actions.ts +3 -0
- package/src/meeting/index.ts +92 -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/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/in-meeting-actions.ts +2 -0
- package/test/unit/spec/meeting/index.js +119 -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/dist/networkQualityMonitor/index.js +0 -227
- package/dist/networkQualityMonitor/index.js.map +0 -1
- package/dist/rtcMetrics/constants.js +0 -11
- package/dist/rtcMetrics/constants.js.map +0 -1
- package/dist/rtcMetrics/index.js +0 -177
- package/dist/rtcMetrics/index.js.map +0 -1
- package/dist/types/networkQualityMonitor/index.d.ts +0 -70
- package/dist/types/rtcMetrics/constants.d.ts +0 -4
- package/dist/types/rtcMetrics/index.d.ts +0 -61
- package/src/networkQualityMonitor/index.ts +0 -211
- package/src/rtcMetrics/constants.ts +0 -3
- package/src/rtcMetrics/index.ts +0 -166
- package/test/unit/spec/networkQualityMonitor/index.js +0 -99
- package/test/unit/spec/rtcMetrics/index.ts +0 -123
|
@@ -1,211 +0,0 @@
|
|
|
1
|
-
import EventsScope from '../common/events/events-scope';
|
|
2
|
-
import {EVENT_TRIGGERS} from '../constants';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Meeting - network quality event
|
|
6
|
-
* Emitted on each interval of retrieving stats Analyzer data
|
|
7
|
-
* @event network:quality
|
|
8
|
-
* @type {Object}
|
|
9
|
-
* @property {string} mediaType {video|audio}
|
|
10
|
-
* @property {number} networkQualityScore - value determined in determineUplinkNetworkQuality
|
|
11
|
-
* @memberof NetworkQualityMonitor
|
|
12
|
-
*/
|
|
13
|
-
/**
|
|
14
|
-
* NetworkQualityMonitor class that will emit events based on detected quality
|
|
15
|
-
*
|
|
16
|
-
* @class NetworkQualityMonitor
|
|
17
|
-
* @extends {EventsScope}
|
|
18
|
-
*/
|
|
19
|
-
export default class NetworkQualityMonitor extends EventsScope {
|
|
20
|
-
config: any;
|
|
21
|
-
frequencyTypes: any;
|
|
22
|
-
indicatorTypes: any;
|
|
23
|
-
mediaType: any;
|
|
24
|
-
networkQualityScore: any;
|
|
25
|
-
networkQualityStatus: any;
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Creates a new instance of NetworkQualityMonitor
|
|
29
|
-
* @constructor
|
|
30
|
-
* @public
|
|
31
|
-
* @param {Object} config
|
|
32
|
-
* @property {Object} indicatorTypes - network properties used to evaluate network quality used as constants
|
|
33
|
-
* @property {Object} frequencyTypes - frequency properties used as constants {uplink|send} {downlink|receive}
|
|
34
|
-
* @property {number} networkQualityScore - 0|1 1 is acceptable 0 is bad/unknown
|
|
35
|
-
* @property {Object} networkQualityStatus - hash object based on indicatorTypes and frequencyTypes
|
|
36
|
-
* @property {string} mediaType - audio|video
|
|
37
|
-
*/
|
|
38
|
-
constructor(config: any) {
|
|
39
|
-
super();
|
|
40
|
-
this.config = config;
|
|
41
|
-
this.indicatorTypes = Object.freeze({
|
|
42
|
-
PACKETLOSS: 'packetLoss',
|
|
43
|
-
LATENCY: 'latency',
|
|
44
|
-
JITTER: 'jitter',
|
|
45
|
-
});
|
|
46
|
-
this.frequencyTypes = Object.freeze({
|
|
47
|
-
UPLINK: 'uplink',
|
|
48
|
-
DOWNLINK: 'downlink',
|
|
49
|
-
});
|
|
50
|
-
this.networkQualityScore = 1;
|
|
51
|
-
this.networkQualityStatus = {
|
|
52
|
-
[this.frequencyTypes.UPLINK]: {},
|
|
53
|
-
};
|
|
54
|
-
this.mediaType = null;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* emits NETWORK_QUALITY event on meeting with payload of media type and uplinkNetworkQuality score
|
|
59
|
-
*
|
|
60
|
-
* @memberof NetworkQualityMonitor
|
|
61
|
-
* @returns {void}
|
|
62
|
-
*/
|
|
63
|
-
emitNetworkQuality() {
|
|
64
|
-
this.emit(
|
|
65
|
-
{
|
|
66
|
-
file: 'networkQualityMonitor',
|
|
67
|
-
function: 'emitNetworkQuality',
|
|
68
|
-
},
|
|
69
|
-
EVENT_TRIGGERS.NETWORK_QUALITY,
|
|
70
|
-
{
|
|
71
|
-
mediaType: this.mediaType,
|
|
72
|
-
networkQualityScore: this.networkQualityScore,
|
|
73
|
-
}
|
|
74
|
-
);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* invokes emitNetworkQuality method resets values back to default
|
|
79
|
-
* @returns {void}
|
|
80
|
-
* @memberof NetworkQualityMonitor
|
|
81
|
-
*/
|
|
82
|
-
updateNetworkQualityStatus() {
|
|
83
|
-
this.emitNetworkQuality();
|
|
84
|
-
|
|
85
|
-
// reset values
|
|
86
|
-
this.networkQualityScore = 1;
|
|
87
|
-
this.mediaType = null;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* filter data to determine uplink network quality, invoked on same interval as stats analyzer remote-inbout-rtp
|
|
92
|
-
* @param {Object} configObj
|
|
93
|
-
* @param {string} configObj.mediaType {audio|video}
|
|
94
|
-
* @param {RTCStats} configObj.remoteRtpResults RTC stats remote obj
|
|
95
|
-
* @param {Object} configObj.statsAnalyzerCurrentStats statsResults
|
|
96
|
-
* @returns {void}
|
|
97
|
-
* @public
|
|
98
|
-
* @memberof NetworkQualityMonitor
|
|
99
|
-
*/
|
|
100
|
-
public determineUplinkNetworkQuality({
|
|
101
|
-
mediaType,
|
|
102
|
-
remoteRtpResults,
|
|
103
|
-
statsAnalyzerCurrentStats,
|
|
104
|
-
}: {
|
|
105
|
-
mediaType: string;
|
|
106
|
-
remoteRtpResults: any;
|
|
107
|
-
statsAnalyzerCurrentStats: object;
|
|
108
|
-
}) {
|
|
109
|
-
const roundTripTimeInMilliseconds = remoteRtpResults.roundTripTime * 1000;
|
|
110
|
-
const jitterInMilliseconds = remoteRtpResults.jitter * 1000;
|
|
111
|
-
const {currentPacketLossRatio} = statsAnalyzerCurrentStats[mediaType].send;
|
|
112
|
-
|
|
113
|
-
this.mediaType = mediaType;
|
|
114
|
-
|
|
115
|
-
const {JITTER, PACKETLOSS, LATENCY} = this.indicatorTypes;
|
|
116
|
-
const {UPLINK} = this.frequencyTypes;
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* determines if packetLoss ratio is over threshold set in config
|
|
120
|
-
* sets networkQualityScore to 0 if over threshold
|
|
121
|
-
* @returns {boolean}
|
|
122
|
-
*/
|
|
123
|
-
const determinePacketLoss = () => {
|
|
124
|
-
if (currentPacketLossRatio > this.config.videoPacketLossRatioThreshold) {
|
|
125
|
-
this.networkQualityScore = 0;
|
|
126
|
-
|
|
127
|
-
return false;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
return true;
|
|
131
|
-
};
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
* determines if round trip time value is over threshold set in config
|
|
135
|
-
* sets networkQualityScore to 0 if over threshold
|
|
136
|
-
* @returns {boolean}
|
|
137
|
-
*/
|
|
138
|
-
const determineLatency = () => {
|
|
139
|
-
if (roundTripTimeInMilliseconds > this.config.rttThreshold) {
|
|
140
|
-
this.networkQualityScore = 0;
|
|
141
|
-
|
|
142
|
-
return false;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
return true;
|
|
146
|
-
};
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* determines if jitter value is over threshold in config
|
|
150
|
-
* sets networkQualityScore to 0 if over threshold
|
|
151
|
-
* @returns {boolean}
|
|
152
|
-
*/
|
|
153
|
-
const deterMineJitter = () => {
|
|
154
|
-
if (jitterInMilliseconds > this.config.jitterThreshold) {
|
|
155
|
-
this.networkQualityScore = 0;
|
|
156
|
-
|
|
157
|
-
return false;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
return true;
|
|
161
|
-
};
|
|
162
|
-
|
|
163
|
-
/**
|
|
164
|
-
* returns null if val is specifically undefined
|
|
165
|
-
* @param {(number|undefined)} value
|
|
166
|
-
* @returns {(number|null)}
|
|
167
|
-
*/
|
|
168
|
-
const determineIfUndefined = (value: number | undefined) =>
|
|
169
|
-
typeof value === 'undefined' ? null : value;
|
|
170
|
-
|
|
171
|
-
if (!this.networkQualityStatus[UPLINK][mediaType]) {
|
|
172
|
-
this.networkQualityStatus[UPLINK][mediaType] = {};
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
/**
|
|
176
|
-
* Values for some browsers specifically Safari will be undefined we explicitly set to null
|
|
177
|
-
* https://bugs.webkit.org/show_bug.cgi?id=206645
|
|
178
|
-
* https://bugs.webkit.org/show_bug.cgi?id=212668
|
|
179
|
-
*/
|
|
180
|
-
// PACKET LOSS
|
|
181
|
-
this.networkQualityStatus[UPLINK][mediaType][PACKETLOSS] = {
|
|
182
|
-
acceptable: determinePacketLoss(),
|
|
183
|
-
value: determineIfUndefined(currentPacketLossRatio),
|
|
184
|
-
};
|
|
185
|
-
|
|
186
|
-
// LATENCY measured in Round trip time
|
|
187
|
-
this.networkQualityStatus[UPLINK][mediaType][LATENCY] = {
|
|
188
|
-
acceptable: determineLatency(),
|
|
189
|
-
value: determineIfUndefined(remoteRtpResults.roundTripTime),
|
|
190
|
-
};
|
|
191
|
-
|
|
192
|
-
// JITTER
|
|
193
|
-
this.networkQualityStatus[UPLINK][mediaType][JITTER] = {
|
|
194
|
-
acceptable: deterMineJitter(),
|
|
195
|
-
value: determineIfUndefined(remoteRtpResults.jitter),
|
|
196
|
-
};
|
|
197
|
-
|
|
198
|
-
this.updateNetworkQualityStatus();
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
/**
|
|
202
|
-
* Get the current status of network quaility object - networkQualityStatus
|
|
203
|
-
* @returns {Object}
|
|
204
|
-
* @public
|
|
205
|
-
*/
|
|
206
|
-
get networkQualityStats() {
|
|
207
|
-
const {UPLINK} = this.frequencyTypes;
|
|
208
|
-
|
|
209
|
-
return this.networkQualityStatus[UPLINK];
|
|
210
|
-
}
|
|
211
|
-
}
|
package/src/rtcMetrics/index.ts
DELETED
|
@@ -1,166 +0,0 @@
|
|
|
1
|
-
/* eslint-disable class-methods-use-this */
|
|
2
|
-
import {CallDiagnosticUtils} from '@webex/internal-plugin-metrics';
|
|
3
|
-
import uuid from 'uuid';
|
|
4
|
-
import RTC_METRICS from './constants';
|
|
5
|
-
|
|
6
|
-
const parseJsonPayload = (payload: any[]): any | null => {
|
|
7
|
-
try {
|
|
8
|
-
if (payload && payload[0]) {
|
|
9
|
-
return JSON.parse(payload[0]);
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
return null;
|
|
13
|
-
} catch (_) {
|
|
14
|
-
return null;
|
|
15
|
-
}
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Rtc Metrics
|
|
20
|
-
*/
|
|
21
|
-
export default class RtcMetrics {
|
|
22
|
-
/**
|
|
23
|
-
* Array of MetricData items to be sent to the metrics service.
|
|
24
|
-
*/
|
|
25
|
-
metricsQueue = [];
|
|
26
|
-
|
|
27
|
-
intervalId: number;
|
|
28
|
-
|
|
29
|
-
webex: any;
|
|
30
|
-
|
|
31
|
-
meetingId: string;
|
|
32
|
-
|
|
33
|
-
correlationId: string;
|
|
34
|
-
|
|
35
|
-
connectionId: string;
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Initialize the interval.
|
|
39
|
-
*
|
|
40
|
-
* @param {object} webex - The main `webex` object.
|
|
41
|
-
* @param {string} meetingId - The meeting id.
|
|
42
|
-
* @param {string} correlationId - The correlation id.
|
|
43
|
-
*/
|
|
44
|
-
constructor(webex, meetingId, correlationId) {
|
|
45
|
-
// `window` is used to prevent typescript from returning a NodeJS.Timer.
|
|
46
|
-
this.intervalId = window.setInterval(this.sendMetricsInQueue.bind(this), 30 * 1000);
|
|
47
|
-
this.meetingId = meetingId;
|
|
48
|
-
this.webex = webex;
|
|
49
|
-
this.correlationId = correlationId;
|
|
50
|
-
this.setNewConnectionId();
|
|
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);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Check to see if the metrics queue has any items.
|
|
57
|
-
*
|
|
58
|
-
* @returns {void}
|
|
59
|
-
*/
|
|
60
|
-
public sendMetricsInQueue() {
|
|
61
|
-
if (this.metricsQueue.length) {
|
|
62
|
-
this.sendMetrics();
|
|
63
|
-
this.metricsQueue = [];
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Add metrics items to the metrics queue.
|
|
69
|
-
*
|
|
70
|
-
* @param {object} data - An object with a payload array of metrics items.
|
|
71
|
-
*
|
|
72
|
-
* @returns {void}
|
|
73
|
-
*/
|
|
74
|
-
addMetrics(data) {
|
|
75
|
-
if (data.payload.length) {
|
|
76
|
-
if (data.name === 'stats-report') {
|
|
77
|
-
data.payload = data.payload.map(this.anonymizeIp);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
this.metricsQueue.push(data);
|
|
81
|
-
|
|
82
|
-
try {
|
|
83
|
-
// If a connection fails, send the rest of the metrics in queue and get a new connection id.
|
|
84
|
-
const parsedPayload = parseJsonPayload(data.payload);
|
|
85
|
-
if (
|
|
86
|
-
data.name === 'onconnectionstatechange' &&
|
|
87
|
-
parsedPayload &&
|
|
88
|
-
parsedPayload.value === 'failed'
|
|
89
|
-
) {
|
|
90
|
-
this.sendMetricsInQueue();
|
|
91
|
-
this.setNewConnectionId();
|
|
92
|
-
}
|
|
93
|
-
} catch (e) {
|
|
94
|
-
console.error(e);
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* Clear the metrics interval.
|
|
101
|
-
*
|
|
102
|
-
* @returns {void}
|
|
103
|
-
*/
|
|
104
|
-
closeMetrics() {
|
|
105
|
-
this.sendMetricsInQueue();
|
|
106
|
-
clearInterval(this.intervalId);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* Anonymize IP addresses.
|
|
111
|
-
*
|
|
112
|
-
* @param {array} stats - An RTCStatsReport organized into an array of strings.
|
|
113
|
-
* @returns {string}
|
|
114
|
-
*/
|
|
115
|
-
anonymizeIp(stats: string): string {
|
|
116
|
-
const data = JSON.parse(stats);
|
|
117
|
-
// on local and remote candidates, anonymize the last 4 bits.
|
|
118
|
-
if (data.type === 'local-candidate' || data.type === 'remote-candidate') {
|
|
119
|
-
data.ip = CallDiagnosticUtils.anonymizeIPAddress(data.ip) || undefined;
|
|
120
|
-
data.address = CallDiagnosticUtils.anonymizeIPAddress(data.address) || undefined;
|
|
121
|
-
data.relatedAddress =
|
|
122
|
-
CallDiagnosticUtils.anonymizeIPAddress(data.relatedAddress) || undefined;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
return JSON.stringify(data);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* Set a new connection id.
|
|
130
|
-
*
|
|
131
|
-
* @returns {void}
|
|
132
|
-
*/
|
|
133
|
-
private setNewConnectionId() {
|
|
134
|
-
this.connectionId = uuid.v4();
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* Send metrics to the metrics service.
|
|
139
|
-
*
|
|
140
|
-
* @returns {void}
|
|
141
|
-
*/
|
|
142
|
-
private sendMetrics() {
|
|
143
|
-
this.webex.request({
|
|
144
|
-
method: 'POST',
|
|
145
|
-
service: 'unifiedTelemetry',
|
|
146
|
-
resource: 'metric/v2',
|
|
147
|
-
headers: {
|
|
148
|
-
type: 'webrtcMedia',
|
|
149
|
-
appId: RTC_METRICS.APP_ID,
|
|
150
|
-
},
|
|
151
|
-
body: {
|
|
152
|
-
metrics: [
|
|
153
|
-
{
|
|
154
|
-
type: 'webrtc',
|
|
155
|
-
version: '1.1.0',
|
|
156
|
-
userId: this.webex.internal.device.userId,
|
|
157
|
-
meetingId: this.meetingId,
|
|
158
|
-
correlationId: this.correlationId,
|
|
159
|
-
connectionId: this.connectionId,
|
|
160
|
-
data: this.metricsQueue,
|
|
161
|
-
},
|
|
162
|
-
],
|
|
163
|
-
},
|
|
164
|
-
});
|
|
165
|
-
}
|
|
166
|
-
}
|
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
import 'jsdom-global/register';
|
|
2
|
-
import chai from 'chai';
|
|
3
|
-
import chaiAsPromised from 'chai-as-promised';
|
|
4
|
-
import sinon from 'sinon';
|
|
5
|
-
|
|
6
|
-
import NetworkQualityMonitor from '../../../../src/networkQualityMonitor';
|
|
7
|
-
import {EVENT_TRIGGERS} from '../../../../src/constants';
|
|
8
|
-
|
|
9
|
-
const {assert} = chai;
|
|
10
|
-
|
|
11
|
-
chai.use(chaiAsPromised);
|
|
12
|
-
sinon.assert.expose(chai.assert, {prefix: ''});
|
|
13
|
-
|
|
14
|
-
// eslint-disable-next-line mocha/no-exclusive-tests
|
|
15
|
-
describe('plugin-meetings', () => {
|
|
16
|
-
describe('NetworkQualityMonitor', () => {
|
|
17
|
-
let networkQualityMonitor;
|
|
18
|
-
let sandBoxEmitSpy;
|
|
19
|
-
|
|
20
|
-
const initialConfig = {
|
|
21
|
-
videoPacketLossRatioThreshold: 9,
|
|
22
|
-
rttThreshold: 500,
|
|
23
|
-
jitterThreshold: 500,
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
const configObject = {
|
|
27
|
-
mediaType: 'video-send',
|
|
28
|
-
remoteRtpResults: {
|
|
29
|
-
id: 'RTCRemoteInboundRtpVideoStream_2411086660',
|
|
30
|
-
timestamp: 1624472676193.79,
|
|
31
|
-
type: 'remote-inbound-rtp',
|
|
32
|
-
ssrc: 2411086660,
|
|
33
|
-
kind: 'video',
|
|
34
|
-
transportId: 'RTCTransport_1_1',
|
|
35
|
-
codecId: 'RTCCodec_1_Outbound_102',
|
|
36
|
-
jitter: 0.004,
|
|
37
|
-
packetsLost: 8,
|
|
38
|
-
localId: 'RTCOutboundRTPVideoStream_2411086660',
|
|
39
|
-
roundTripTime: 0.648,
|
|
40
|
-
fractionLost: 0,
|
|
41
|
-
totalRoundTripTime: 3.554,
|
|
42
|
-
roundTripTimeMeasurements: 14,
|
|
43
|
-
},
|
|
44
|
-
statsAnalyzerCurrentStats: {
|
|
45
|
-
'audio-send': {
|
|
46
|
-
send: {
|
|
47
|
-
currentPacketLossRatio: 8,
|
|
48
|
-
},
|
|
49
|
-
},
|
|
50
|
-
'video-send': {
|
|
51
|
-
send: {
|
|
52
|
-
currentPacketLossRatio: 10,
|
|
53
|
-
},
|
|
54
|
-
},
|
|
55
|
-
},
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
const sandbox = sinon.createSandbox();
|
|
59
|
-
|
|
60
|
-
beforeEach(() => {
|
|
61
|
-
networkQualityMonitor = new NetworkQualityMonitor(initialConfig);
|
|
62
|
-
sandbox.spy(networkQualityMonitor, 'updateNetworkQualityStatus');
|
|
63
|
-
sandBoxEmitSpy = sandbox.spy(networkQualityMonitor, 'emit');
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
afterEach(() => {
|
|
67
|
-
sandbox.restore();
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
it('should trigger updateNetworkQualityStatus when determineUplinkNetworkQuality has finished', async () => {
|
|
71
|
-
await networkQualityMonitor.determineUplinkNetworkQuality(configObject);
|
|
72
|
-
|
|
73
|
-
assert.calledOnce(networkQualityMonitor.updateNetworkQualityStatus);
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
it('should emit a network quality judgement event with the proper payload', async () => {
|
|
77
|
-
await networkQualityMonitor.determineUplinkNetworkQuality(configObject);
|
|
78
|
-
assert(
|
|
79
|
-
sandBoxEmitSpy.calledWith(
|
|
80
|
-
sinon.match({
|
|
81
|
-
file: 'networkQualityMonitor',
|
|
82
|
-
function: 'emitNetworkQuality',
|
|
83
|
-
}),
|
|
84
|
-
sinon.match(EVENT_TRIGGERS.NETWORK_QUALITY),
|
|
85
|
-
sinon.match({
|
|
86
|
-
mediaType: 'video-send',
|
|
87
|
-
networkQualityScore: 0,
|
|
88
|
-
})
|
|
89
|
-
)
|
|
90
|
-
);
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
it('should reset to default values after determineUplinkNetworkQuality call stack is complete', async () => {
|
|
94
|
-
await networkQualityMonitor.determineUplinkNetworkQuality(configObject);
|
|
95
|
-
assert.isNull(networkQualityMonitor.mediaType);
|
|
96
|
-
assert.deepEqual(networkQualityMonitor.networkQualityScore, 1);
|
|
97
|
-
});
|
|
98
|
-
});
|
|
99
|
-
});
|
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
import 'jsdom-global/register';
|
|
2
|
-
import RtcMetrics from '@webex/plugin-meetings/src/rtcMetrics';
|
|
3
|
-
import MockWebex from '@webex/test-helper-mock-webex';
|
|
4
|
-
import {assert} from '@webex/test-helper-chai';
|
|
5
|
-
import sinon from 'sinon';
|
|
6
|
-
import RTC_METRICS from '../../../../src/rtcMetrics/constants';
|
|
7
|
-
|
|
8
|
-
const FAKE_METRICS_ITEM = {payload: ['{"type":"string","value":"fake-metrics","id":""}']};
|
|
9
|
-
const FAILURE_METRICS_ITEM = {
|
|
10
|
-
name: "onconnectionstatechange",
|
|
11
|
-
payload: ['{"type":"string","value":"failed","id":""}'],
|
|
12
|
-
timestamp: 1707929986667
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
const STATS_WITH_IP = '{"id":"RTCIceCandidate_/kQs0ZNU","type":"remote-candidate","transportId":"RTCTransport_0_1","isRemote":true,"ip":"11.22.111.255","address":"11.22.111.255","port":5004,"protocol":"udp","candidateType":"host","priority":2130706431}';
|
|
16
|
-
const STATS_WITH_IP_RESULT = '{"id":"RTCIceCandidate_/kQs0ZNU","type":"remote-candidate","transportId":"RTCTransport_0_1","isRemote":true,"ip":"11.22.111.240","address":"11.22.111.240","port":5004,"protocol":"udp","candidateType":"host","priority":2130706431}';
|
|
17
|
-
|
|
18
|
-
describe('RtcMetrics', () => {
|
|
19
|
-
let metrics: RtcMetrics;
|
|
20
|
-
let webex: MockWebex;
|
|
21
|
-
let clock;
|
|
22
|
-
let anonymizeIpSpy;
|
|
23
|
-
|
|
24
|
-
const sandbox = sinon.createSandbox();
|
|
25
|
-
|
|
26
|
-
beforeEach(() => {
|
|
27
|
-
clock = sinon.useFakeTimers();
|
|
28
|
-
webex = new MockWebex();
|
|
29
|
-
metrics = new RtcMetrics(webex, 'mock-meeting-id', 'mock-correlation-id');
|
|
30
|
-
anonymizeIpSpy = sandbox.spy(metrics, 'anonymizeIp');
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
afterEach(() => {
|
|
34
|
-
sandbox.restore();
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it('sendMetrics should send a webex request', () => {
|
|
38
|
-
assert.notCalled(webex.request);
|
|
39
|
-
|
|
40
|
-
metrics.addMetrics(FAKE_METRICS_ITEM);
|
|
41
|
-
(metrics as any).sendMetrics();
|
|
42
|
-
|
|
43
|
-
assert.callCount(webex.request, 1);
|
|
44
|
-
assert.calledWithMatch(webex.request, sinon.match.has('headers', {
|
|
45
|
-
type: 'webrtcMedia',
|
|
46
|
-
appId: RTC_METRICS.APP_ID,
|
|
47
|
-
}));
|
|
48
|
-
assert.calledWithMatch(webex.request, sinon.match.hasNested('body.metrics[0].data[0].payload', FAKE_METRICS_ITEM.payload));
|
|
49
|
-
assert.calledWithMatch(webex.request, sinon.match.hasNested('body.metrics[0].meetingId', 'mock-meeting-id'));
|
|
50
|
-
assert.calledWithMatch(webex.request, sinon.match.hasNested('body.metrics[0].correlationId', 'mock-correlation-id'));
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
it('should have a defined sendMetricsInQueue function which is public', () => {
|
|
54
|
-
assert.isDefined(metrics.sendMetricsInQueue);
|
|
55
|
-
assert.isFunction(metrics.sendMetricsInQueue);
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
it('should send metrics requests over time', () => {
|
|
59
|
-
assert.notCalled(webex.request);
|
|
60
|
-
|
|
61
|
-
metrics.addMetrics(FAKE_METRICS_ITEM);
|
|
62
|
-
assert.deepEqual(metrics.metricsQueue, [FAKE_METRICS_ITEM]);
|
|
63
|
-
clock.tick(60 * 1000);
|
|
64
|
-
|
|
65
|
-
assert.callCount(webex.request, 1);
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
it('should not send requests with no items in the queue', () => {
|
|
69
|
-
clock.tick(60 * 1000);
|
|
70
|
-
assert.notCalled(webex.request);
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
it('sendMetricsInQueue should send metrics if any exist in the queue', () => {
|
|
74
|
-
assert.notCalled(webex.request);
|
|
75
|
-
|
|
76
|
-
metrics.addMetrics(FAKE_METRICS_ITEM);
|
|
77
|
-
(metrics as any).sendMetricsInQueue();
|
|
78
|
-
|
|
79
|
-
assert.callCount(webex.request, 1);
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
it('should clear out metrics on close', () => {
|
|
83
|
-
assert.notCalled(webex.request);
|
|
84
|
-
|
|
85
|
-
metrics.addMetrics(FAKE_METRICS_ITEM);
|
|
86
|
-
metrics.closeMetrics();
|
|
87
|
-
|
|
88
|
-
assert.callCount(webex.request, 1);
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
it('should clear out metrics on failure', () => {
|
|
92
|
-
assert.notCalled(webex.request);
|
|
93
|
-
|
|
94
|
-
metrics.addMetrics(FAILURE_METRICS_ITEM);
|
|
95
|
-
|
|
96
|
-
assert.callCount(webex.request, 1);
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
it('should have the same connectionId on success', () => {
|
|
100
|
-
const originalId = metrics.connectionId;
|
|
101
|
-
|
|
102
|
-
metrics.addMetrics(FAKE_METRICS_ITEM);
|
|
103
|
-
|
|
104
|
-
assert.strictEqual(originalId, metrics.connectionId);
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
it('should have a new connectionId on failure', () => {
|
|
108
|
-
const originalId = metrics.connectionId;
|
|
109
|
-
|
|
110
|
-
metrics.addMetrics(FAILURE_METRICS_ITEM);
|
|
111
|
-
|
|
112
|
-
assert.notEqual(originalId, metrics.connectionId);
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
it('should anonymize IP addresses', () => {
|
|
116
|
-
assert.strictEqual(metrics.anonymizeIp(STATS_WITH_IP), STATS_WITH_IP_RESULT);
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
it('should call anonymizeIp', () => {
|
|
120
|
-
metrics.addMetrics({ name: 'stats-report', payload: [STATS_WITH_IP] });
|
|
121
|
-
assert.calledOnce(anonymizeIpSpy);
|
|
122
|
-
})
|
|
123
|
-
});
|