@webex/internal-plugin-metrics 3.3.1 → 3.4.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/behavioral/behavioral-metrics.js +199 -0
- package/dist/behavioral/behavioral-metrics.js.map +1 -0
- package/dist/behavioral/config.js +11 -0
- package/dist/behavioral/config.js.map +1 -0
- package/dist/call-diagnostic/call-diagnostic-metrics.js +3 -0
- package/dist/call-diagnostic/call-diagnostic-metrics.js.map +1 -1
- package/dist/call-diagnostic/call-diagnostic-metrics.util.js +14 -11
- package/dist/call-diagnostic/call-diagnostic-metrics.util.js.map +1 -1
- package/dist/call-diagnostic/config.js +19 -13
- package/dist/call-diagnostic/config.js.map +1 -1
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -1
- package/dist/metrics.js +1 -1
- package/dist/metrics.types.js.map +1 -1
- package/dist/new-metrics.js +31 -6
- package/dist/new-metrics.js.map +1 -1
- package/dist/types/behavioral/behavioral-metrics.d.ts +63 -0
- package/dist/types/behavioral/config.d.ts +1 -0
- package/dist/types/call-diagnostic/call-diagnostic-metrics.util.d.ts +3 -2
- package/dist/types/call-diagnostic/config.d.ts +2 -0
- package/dist/types/index.d.ts +2 -1
- package/dist/types/metrics.types.d.ts +28 -7
- package/dist/types/new-metrics.d.ts +14 -6
- package/package.json +11 -11
- package/src/behavioral/behavioral-metrics.ts +179 -0
- package/src/behavioral/config.ts +3 -0
- package/src/call-diagnostic/call-diagnostic-metrics.ts +3 -0
- package/src/call-diagnostic/call-diagnostic-metrics.util.ts +16 -17
- package/src/call-diagnostic/config.ts +9 -2
- package/src/index.ts +2 -0
- package/src/metrics.types.ts +59 -8
- package/src/new-metrics.ts +34 -8
- package/test/unit/spec/behavioral/behavioral-metrics.ts +164 -0
- package/test/unit/spec/call-diagnostic/call-diagnostic-metrics-batcher.ts +48 -52
- package/test/unit/spec/call-diagnostic/call-diagnostic-metrics-latencies.ts +1 -1
- package/test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts +9 -0
- package/test/unit/spec/call-diagnostic/call-diagnostic-metrics.util.ts +41 -22
- package/test/unit/spec/prelogin-metrics-batcher.ts +1 -1
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
MISSING_ROAP_ANSWER_CLIENT_CODE,
|
|
22
22
|
WBX_APP_API_URL,
|
|
23
23
|
ERROR_DESCRIPTIONS,
|
|
24
|
+
ICE_AND_REACHABILITY_FAILED_CLIENT_CODE,
|
|
24
25
|
} from './config';
|
|
25
26
|
|
|
26
27
|
const {getOSName, getOSVersion, getBrowserName, getBrowserVersion} = BrowserDetection();
|
|
@@ -225,7 +226,7 @@ export const getBuildType = (
|
|
|
225
226
|
*/
|
|
226
227
|
export const prepareDiagnosticMetricItem = (webex: any, item: any) => {
|
|
227
228
|
const origin: Partial<Event['origin']> = {
|
|
228
|
-
buildType:
|
|
229
|
+
buildType: getBuildType(
|
|
229
230
|
item.eventPayload?.event?.eventData?.webClientDomain,
|
|
230
231
|
item.eventPayload?.event?.eventData?.markAsTestEvent
|
|
231
232
|
),
|
|
@@ -284,6 +285,8 @@ export const prepareDiagnosticMetricItem = (webex: any, item: any) => {
|
|
|
284
285
|
|
|
285
286
|
case 'client.media.rx.start':
|
|
286
287
|
joinTimes.localSDPGenRemoteSDPRecv = cdl.getLocalSDPGenRemoteSDPRecv();
|
|
288
|
+
audioSetupDelay.joinRespRxStart = cdl.getAudioJoinRespRxStart();
|
|
289
|
+
videoSetupDelay.joinRespRxStart = cdl.getVideoJoinRespRxStart();
|
|
287
290
|
break;
|
|
288
291
|
|
|
289
292
|
case 'client.media-engine.ready':
|
|
@@ -293,10 +296,8 @@ export const prepareDiagnosticMetricItem = (webex: any, item: any) => {
|
|
|
293
296
|
joinTimes.stayLobbyTime = cdl.getStayLobbyTime();
|
|
294
297
|
break;
|
|
295
298
|
|
|
296
|
-
case 'client.
|
|
297
|
-
audioSetupDelay.joinRespRxStart = cdl.getAudioJoinRespRxStart();
|
|
299
|
+
case 'client.media.tx.start':
|
|
298
300
|
audioSetupDelay.joinRespTxStart = cdl.getAudioJoinRespTxStart();
|
|
299
|
-
videoSetupDelay.joinRespRxStart = cdl.getVideoJoinRespRxStart();
|
|
300
301
|
videoSetupDelay.joinRespTxStart = cdl.getVideoJoinRespTxStart();
|
|
301
302
|
}
|
|
302
303
|
|
|
@@ -314,7 +315,6 @@ export const prepareDiagnosticMetricItem = (webex: any, item: any) => {
|
|
|
314
315
|
|
|
315
316
|
item.eventPayload.origin = Object.assign(origin, item.eventPayload.origin);
|
|
316
317
|
|
|
317
|
-
// @ts-ignore
|
|
318
318
|
webex.logger.log(
|
|
319
319
|
`CallDiagnosticLatencies,prepareDiagnosticMetricItem: ${JSON.stringify({
|
|
320
320
|
latencies: Object.fromEntries(cdl.latencyTimestamps),
|
|
@@ -370,12 +370,14 @@ export const extractVersionMetadata = (version: string) => {
|
|
|
370
370
|
*/
|
|
371
371
|
export const generateClientErrorCodeForIceFailure = ({
|
|
372
372
|
signalingState,
|
|
373
|
-
|
|
373
|
+
iceConnected,
|
|
374
374
|
turnServerUsed,
|
|
375
|
+
unreachable,
|
|
375
376
|
}: {
|
|
376
377
|
signalingState: RTCPeerConnection['signalingState'];
|
|
377
|
-
|
|
378
|
+
iceConnected: boolean;
|
|
378
379
|
turnServerUsed: boolean;
|
|
380
|
+
unreachable: boolean;
|
|
379
381
|
}) => {
|
|
380
382
|
let errorCode = ICE_FAILURE_CLIENT_CODE; // default;
|
|
381
383
|
|
|
@@ -383,20 +385,17 @@ export const generateClientErrorCodeForIceFailure = ({
|
|
|
383
385
|
errorCode = MISSING_ROAP_ANSWER_CLIENT_CODE;
|
|
384
386
|
}
|
|
385
387
|
|
|
386
|
-
if (
|
|
387
|
-
signalingState === 'stable' &&
|
|
388
|
-
(iceConnectionState === 'connected' || iceConnectionState === 'disconnected')
|
|
389
|
-
) {
|
|
388
|
+
if (signalingState === 'stable' && iceConnected) {
|
|
390
389
|
errorCode = DTLS_HANDSHAKE_FAILED_CLIENT_CODE;
|
|
391
390
|
}
|
|
392
391
|
|
|
393
|
-
if (
|
|
394
|
-
signalingState !== 'have-local-offer' &&
|
|
395
|
-
iceConnectionState !== 'connected' &&
|
|
396
|
-
iceConnectionState !== 'disconnected'
|
|
397
|
-
) {
|
|
392
|
+
if (signalingState !== 'have-local-offer' && !iceConnected) {
|
|
398
393
|
if (turnServerUsed) {
|
|
399
|
-
|
|
394
|
+
if (unreachable) {
|
|
395
|
+
errorCode = ICE_AND_REACHABILITY_FAILED_CLIENT_CODE;
|
|
396
|
+
} else {
|
|
397
|
+
errorCode = ICE_FAILED_WITH_TURN_TLS_CLIENT_CODE;
|
|
398
|
+
}
|
|
400
399
|
} else {
|
|
401
400
|
errorCode = ICE_FAILED_WITHOUT_TURN_TLS_CLIENT_CODE;
|
|
402
401
|
}
|
|
@@ -16,6 +16,7 @@ export const MISSING_ROAP_ANSWER_CLIENT_CODE = 2007;
|
|
|
16
16
|
export const DTLS_HANDSHAKE_FAILED_CLIENT_CODE = 2008;
|
|
17
17
|
export const ICE_FAILED_WITH_TURN_TLS_CLIENT_CODE = 2010;
|
|
18
18
|
export const ICE_FAILED_WITHOUT_TURN_TLS_CLIENT_CODE = 2009;
|
|
19
|
+
export const ICE_AND_REACHABILITY_FAILED_CLIENT_CODE = 2011;
|
|
19
20
|
export const WBX_APP_API_URL = 'wbxappapi'; // MeetingInfo WebexAppApi response object normally contains a body.url that includes the string 'wbxappapi'
|
|
20
21
|
|
|
21
22
|
export const WEBEX_SUB_SERVICE_TYPES: Record<string, ClientSubServiceType> = {
|
|
@@ -125,6 +126,7 @@ export const ERROR_DESCRIPTIONS = {
|
|
|
125
126
|
DTLS_HANDSHAKE_FAILED: 'DTLSHandshakeFailed',
|
|
126
127
|
ICE_FAILED_WITHOUT_TURN_TLS: 'ICEFailedWithoutTURN_TLS',
|
|
127
128
|
ICE_FAILED_WITH_TURN_TLS: 'ICEFailedWithTURN_TLS',
|
|
129
|
+
ICE_AND_REACHABILITY_FAILED: 'ICEAndReachabilityFailed',
|
|
128
130
|
SDP_OFFER_CREATION_ERROR: 'SdpOfferCreationError',
|
|
129
131
|
SDP_OFFER_CREATION_ERROR_MISSING_CODEC: 'SdpOfferCreationErrorMissingCodec',
|
|
130
132
|
};
|
|
@@ -377,7 +379,7 @@ export const CLIENT_ERROR_CODE_TO_ERROR_PAYLOAD: Record<number, Partial<ClientEv
|
|
|
377
379
|
},
|
|
378
380
|
[MISSING_ROAP_ANSWER_CLIENT_CODE]: {
|
|
379
381
|
errorDescription: ERROR_DESCRIPTIONS.MISSING_ROAP_ANSWER,
|
|
380
|
-
category: '
|
|
382
|
+
category: 'media',
|
|
381
383
|
fatal: true,
|
|
382
384
|
},
|
|
383
385
|
[DTLS_HANDSHAKE_FAILED_CLIENT_CODE]: {
|
|
@@ -392,7 +394,12 @@ export const CLIENT_ERROR_CODE_TO_ERROR_PAYLOAD: Record<number, Partial<ClientEv
|
|
|
392
394
|
},
|
|
393
395
|
[ICE_FAILED_WITH_TURN_TLS_CLIENT_CODE]: {
|
|
394
396
|
errorDescription: ERROR_DESCRIPTIONS.ICE_FAILED_WITH_TURN_TLS,
|
|
395
|
-
category: '
|
|
397
|
+
category: 'media',
|
|
398
|
+
fatal: true,
|
|
399
|
+
},
|
|
400
|
+
[ICE_AND_REACHABILITY_FAILED_CLIENT_CODE]: {
|
|
401
|
+
errorDescription: ERROR_DESCRIPTIONS.ICE_AND_REACHABILITY_FAILED,
|
|
402
|
+
category: 'expected',
|
|
396
403
|
fatal: true,
|
|
397
404
|
},
|
|
398
405
|
2050: {
|
package/src/index.ts
CHANGED
|
@@ -22,6 +22,7 @@ import * as CALL_DIAGNOSTIC_CONFIG from './call-diagnostic/config';
|
|
|
22
22
|
import * as CallDiagnosticUtils from './call-diagnostic/call-diagnostic-metrics.util';
|
|
23
23
|
import CallDiagnosticMetrics from './call-diagnostic/call-diagnostic-metrics';
|
|
24
24
|
import CallDiagnosticLatencies from './call-diagnostic/call-diagnostic-metrics-latencies';
|
|
25
|
+
import BehavioralMetrics from './behavioral/behavioral-metrics';
|
|
25
26
|
|
|
26
27
|
registerInternalPlugin('metrics', Metrics, {
|
|
27
28
|
config,
|
|
@@ -41,6 +42,7 @@ export {
|
|
|
41
42
|
CallDiagnosticUtils,
|
|
42
43
|
CallDiagnosticLatencies,
|
|
43
44
|
CallDiagnosticMetrics,
|
|
45
|
+
BehavioralMetrics,
|
|
44
46
|
};
|
|
45
47
|
export type {
|
|
46
48
|
ClientEvent,
|
package/src/metrics.types.ts
CHANGED
|
@@ -20,6 +20,42 @@ export type BrowserLaunchMethodType = NonNullable<
|
|
|
20
20
|
RawEvent['origin']['clientInfo']
|
|
21
21
|
>['browserLaunchMethod'];
|
|
22
22
|
|
|
23
|
+
export type MetricEventProduct = 'webex' | 'wxcc_desktop';
|
|
24
|
+
|
|
25
|
+
export type MetricEventAgent = 'user' | 'browser' | 'system' | 'sdk' | 'redux' | 'service';
|
|
26
|
+
|
|
27
|
+
export type MetricEventVerb =
|
|
28
|
+
| 'create'
|
|
29
|
+
| 'get'
|
|
30
|
+
| 'fetch'
|
|
31
|
+
| 'update'
|
|
32
|
+
| 'list'
|
|
33
|
+
| 'delete'
|
|
34
|
+
| 'select'
|
|
35
|
+
| 'view'
|
|
36
|
+
| 'set'
|
|
37
|
+
| 'toggle'
|
|
38
|
+
| 'load'
|
|
39
|
+
| 'reload'
|
|
40
|
+
| 'click'
|
|
41
|
+
| 'hover'
|
|
42
|
+
| 'register'
|
|
43
|
+
| 'unregister'
|
|
44
|
+
| 'enable'
|
|
45
|
+
| 'disable'
|
|
46
|
+
| 'use'
|
|
47
|
+
| 'complete'
|
|
48
|
+
| 'submit'
|
|
49
|
+
| 'apply'
|
|
50
|
+
| 'cancel'
|
|
51
|
+
| 'abort'
|
|
52
|
+
| 'sync'
|
|
53
|
+
| 'login'
|
|
54
|
+
| 'logout'
|
|
55
|
+
| 'answer'
|
|
56
|
+
| 'activate'
|
|
57
|
+
| 'deactivate';
|
|
58
|
+
|
|
23
59
|
export type SubmitClientEventOptions = {
|
|
24
60
|
meetingId?: string;
|
|
25
61
|
mediaConnections?: any[];
|
|
@@ -66,13 +102,26 @@ export interface ClientEvent {
|
|
|
66
102
|
options?: SubmitClientEventOptions;
|
|
67
103
|
}
|
|
68
104
|
|
|
105
|
+
export interface BehavioralEventContext {
|
|
106
|
+
app: {version: string};
|
|
107
|
+
device: {id: string};
|
|
108
|
+
locale: string;
|
|
109
|
+
os: {
|
|
110
|
+
name: string;
|
|
111
|
+
version: string;
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
69
115
|
export interface BehavioralEvent {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
116
|
+
context: BehavioralEventContext;
|
|
117
|
+
metricName: string;
|
|
118
|
+
tags: Record<string, string | number | boolean>;
|
|
119
|
+
timestamp: number;
|
|
120
|
+
type: string[];
|
|
74
121
|
}
|
|
75
122
|
|
|
123
|
+
export type BehavioralEventPayload = BehavioralEvent['tags'];
|
|
124
|
+
|
|
76
125
|
export interface OperationalEvent {
|
|
77
126
|
// TODO: not implemented
|
|
78
127
|
name: never;
|
|
@@ -104,7 +153,7 @@ export type RecursivePartial<T> = {
|
|
|
104
153
|
export type MetricEventNames =
|
|
105
154
|
| InternalEvent['name']
|
|
106
155
|
| ClientEvent['name']
|
|
107
|
-
| BehavioralEvent['
|
|
156
|
+
| BehavioralEvent['metricName']
|
|
108
157
|
| OperationalEvent['name']
|
|
109
158
|
| FeatureEvent['name']
|
|
110
159
|
| MediaQualityEvent['name'];
|
|
@@ -137,9 +186,11 @@ export type SubmitInternalEvent = (args: {
|
|
|
137
186
|
}) => void;
|
|
138
187
|
|
|
139
188
|
export type SubmitBehavioralEvent = (args: {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
189
|
+
product: MetricEventProduct;
|
|
190
|
+
agent: MetricEventAgent;
|
|
191
|
+
target: string;
|
|
192
|
+
verb: MetricEventVerb;
|
|
193
|
+
payload?: BehavioralEventPayload;
|
|
143
194
|
}) => void;
|
|
144
195
|
|
|
145
196
|
export type SubmitClientEvent = (args: {
|
package/src/new-metrics.ts
CHANGED
|
@@ -6,11 +6,15 @@
|
|
|
6
6
|
import {WebexPlugin} from '@webex/webex-core';
|
|
7
7
|
|
|
8
8
|
import CallDiagnosticMetrics from './call-diagnostic/call-diagnostic-metrics';
|
|
9
|
+
import BehavioralMetrics from './behavioral/behavioral-metrics';
|
|
9
10
|
import {
|
|
10
11
|
RecursivePartial,
|
|
12
|
+
MetricEventProduct,
|
|
13
|
+
MetricEventAgent,
|
|
14
|
+
MetricEventVerb,
|
|
11
15
|
ClientEvent,
|
|
12
16
|
FeatureEvent,
|
|
13
|
-
|
|
17
|
+
BehavioralEventPayload,
|
|
14
18
|
OperationalEvent,
|
|
15
19
|
MediaQualityEvent,
|
|
16
20
|
InternalEvent,
|
|
@@ -32,6 +36,7 @@ class Metrics extends WebexPlugin {
|
|
|
32
36
|
callDiagnosticLatencies: CallDiagnosticLatencies;
|
|
33
37
|
// Helper classes to handle the different types of metrics
|
|
34
38
|
callDiagnosticMetrics: CallDiagnosticMetrics;
|
|
39
|
+
behavioralMetrics: BehavioralMetrics;
|
|
35
40
|
|
|
36
41
|
/**
|
|
37
42
|
* Constructor
|
|
@@ -56,6 +61,8 @@ class Metrics extends WebexPlugin {
|
|
|
56
61
|
this.webex.once('ready', () => {
|
|
57
62
|
// @ts-ignore
|
|
58
63
|
this.callDiagnosticMetrics = new CallDiagnosticMetrics({}, {parent: this.webex});
|
|
64
|
+
// @ts-ignore
|
|
65
|
+
this.behavioralMetrics = new BehavioralMetrics({}, {parent: this.webex});
|
|
59
66
|
});
|
|
60
67
|
}
|
|
61
68
|
|
|
@@ -79,21 +86,40 @@ class Metrics extends WebexPlugin {
|
|
|
79
86
|
}
|
|
80
87
|
}
|
|
81
88
|
|
|
89
|
+
/**
|
|
90
|
+
* @returns true once we have the deviceId we need to submit behavioral events to Amplitude
|
|
91
|
+
*/
|
|
92
|
+
isReadyToSubmitBehavioralEvents() {
|
|
93
|
+
return this.behavioralMetrics.isReadyToSubmitBehavioralEvents();
|
|
94
|
+
}
|
|
95
|
+
|
|
82
96
|
/**
|
|
83
97
|
* Behavioral event
|
|
84
98
|
* @param args
|
|
85
99
|
*/
|
|
86
100
|
submitBehavioralEvent({
|
|
87
|
-
|
|
101
|
+
product,
|
|
102
|
+
agent,
|
|
103
|
+
target,
|
|
104
|
+
verb,
|
|
88
105
|
payload,
|
|
89
|
-
options,
|
|
90
106
|
}: {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
107
|
+
product: MetricEventProduct;
|
|
108
|
+
agent: MetricEventAgent;
|
|
109
|
+
target: string;
|
|
110
|
+
verb: MetricEventVerb;
|
|
111
|
+
payload?: BehavioralEventPayload;
|
|
94
112
|
}) {
|
|
95
|
-
this.
|
|
96
|
-
|
|
113
|
+
if (!this.behavioralMetrics) {
|
|
114
|
+
// @ts-ignore
|
|
115
|
+
this.webex.logger.log(
|
|
116
|
+
`NewMetrics: @submitBehavioralEvent. Attempted to submit before webex.ready: ${product}.${agent}.${target}.${verb}`
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
return Promise.resolve();
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return this.behavioralMetrics.submitBehavioralEvent({product, agent, target, verb, payload});
|
|
97
123
|
}
|
|
98
124
|
|
|
99
125
|
/**
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import sinon from 'sinon';
|
|
2
|
+
import {assert} from '@webex/test-helper-chai';
|
|
3
|
+
import {BrowserDetection} from '@webex/common';
|
|
4
|
+
import {BehavioralMetrics, config, getOSNameInternal} from '@webex/internal-plugin-metrics';
|
|
5
|
+
import uuid from 'uuid';
|
|
6
|
+
import {merge} from 'lodash';
|
|
7
|
+
|
|
8
|
+
//@ts-ignore
|
|
9
|
+
global.window = {location: {hostname: 'whatever'}, navigator: {language: 'language'}};
|
|
10
|
+
process.env.NODE_ENV = 'test';
|
|
11
|
+
|
|
12
|
+
const {getOSVersion, getBrowserName, getBrowserVersion} = BrowserDetection();
|
|
13
|
+
|
|
14
|
+
describe('internal-plugin-metrics', () => {
|
|
15
|
+
describe('BehavioralMetrics', () => {
|
|
16
|
+
let webex;
|
|
17
|
+
let now;
|
|
18
|
+
let behavioralMetrics: BehavioralMetrics;
|
|
19
|
+
|
|
20
|
+
const tags = {key: 'val'};
|
|
21
|
+
|
|
22
|
+
beforeEach(() => {
|
|
23
|
+
now = new Date();
|
|
24
|
+
|
|
25
|
+
webex = {
|
|
26
|
+
canAuthorize: true,
|
|
27
|
+
version: 'webex-version',
|
|
28
|
+
internal: {
|
|
29
|
+
services: {
|
|
30
|
+
get: () => 'locus-url',
|
|
31
|
+
},
|
|
32
|
+
metrics: {
|
|
33
|
+
submitClientMetrics: sinon.stub(),
|
|
34
|
+
config: {...config.metrics},
|
|
35
|
+
},
|
|
36
|
+
newMetrics: {},
|
|
37
|
+
device: {
|
|
38
|
+
userId: 'userId',
|
|
39
|
+
url: 'https://wdm-intb.ciscospark.com/wdm/api/v1/devices/deviceId',
|
|
40
|
+
orgId: 'orgId',
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
meetings: {
|
|
44
|
+
config: {
|
|
45
|
+
metrics: {
|
|
46
|
+
clientType: 'TEAMS_CLIENT',
|
|
47
|
+
subClientType: 'WEB_APP',
|
|
48
|
+
clientName: 'Cantina',
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
geoHintInfo: {
|
|
52
|
+
clientAddress: '1.3.4.5',
|
|
53
|
+
countryCode: 'UK',
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
credentials: {
|
|
57
|
+
isUnverifiedGuest: false,
|
|
58
|
+
},
|
|
59
|
+
prepareFetchOptions: sinon.stub().callsFake((opts: any) => ({...opts, foo: 'bar'})),
|
|
60
|
+
request: sinon.stub().resolves({body: {}}),
|
|
61
|
+
logger: {
|
|
62
|
+
log: sinon.stub(),
|
|
63
|
+
error: sinon.stub(),
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
sinon.createSandbox();
|
|
68
|
+
sinon.useFakeTimers(now.getTime());
|
|
69
|
+
behavioralMetrics = new BehavioralMetrics({}, {parent: webex});
|
|
70
|
+
sinon.stub(uuid, 'v4').returns('my-fake-id');
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
afterEach(() => {
|
|
74
|
+
sinon.restore();
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
describe('#getContext', () => {
|
|
78
|
+
it('should build context correctly', () => {
|
|
79
|
+
const res = behavioralMetrics.getContext();
|
|
80
|
+
|
|
81
|
+
assert.deepEqual(res, {
|
|
82
|
+
app: {
|
|
83
|
+
version: 'webex-version',
|
|
84
|
+
},
|
|
85
|
+
device: {
|
|
86
|
+
id: 'deviceId',
|
|
87
|
+
},
|
|
88
|
+
locale: 'language',
|
|
89
|
+
os: {
|
|
90
|
+
name: getOSNameInternal(),
|
|
91
|
+
version: getOSVersion(),
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
describe('#getDefaultTags', () => {
|
|
98
|
+
it('should build tags correctly', () => {
|
|
99
|
+
const res = behavioralMetrics.getDefaultTags();
|
|
100
|
+
|
|
101
|
+
assert.deepEqual(res, {
|
|
102
|
+
browser: getBrowserName(),
|
|
103
|
+
browserHeight: window.innerHeight,
|
|
104
|
+
browserVersion: getBrowserVersion(),
|
|
105
|
+
browserWidth: window.innerWidth,
|
|
106
|
+
domain: window.location.hostname,
|
|
107
|
+
inIframe: false,
|
|
108
|
+
locale: window.navigator.language,
|
|
109
|
+
os: getOSNameInternal(),
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
describe('#isReadyToSubmitBehavioralEvents', () => {
|
|
115
|
+
it('should return true when we have a deviceId, false when deviceId is empty or undefined', async () => {
|
|
116
|
+
assert.equal(true, behavioralMetrics.isReadyToSubmitBehavioralEvents());
|
|
117
|
+
|
|
118
|
+
webex.internal.device.url = "";
|
|
119
|
+
assert.equal(false, behavioralMetrics.isReadyToSubmitBehavioralEvents());
|
|
120
|
+
|
|
121
|
+
delete webex.internal.device.url;
|
|
122
|
+
assert.equal(false, behavioralMetrics.isReadyToSubmitBehavioralEvents());
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
describe('#createEventObject', () => {
|
|
127
|
+
it('should build event object correctly', async () => {
|
|
128
|
+
const res = behavioralMetrics.createEventObject({
|
|
129
|
+
product: 'webex',
|
|
130
|
+
agent: 'user',
|
|
131
|
+
target: 'target',
|
|
132
|
+
verb: 'create',
|
|
133
|
+
payload: tags,
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
assert.deepEqual(res, {
|
|
137
|
+
context: {
|
|
138
|
+
app: {
|
|
139
|
+
version: 'webex-version',
|
|
140
|
+
},
|
|
141
|
+
device: {
|
|
142
|
+
id: 'deviceId',
|
|
143
|
+
},
|
|
144
|
+
locale: 'language',
|
|
145
|
+
os: {
|
|
146
|
+
name: getOSNameInternal(),
|
|
147
|
+
version: getOSVersion(),
|
|
148
|
+
},
|
|
149
|
+
},
|
|
150
|
+
metricName: 'webex.user.target.create',
|
|
151
|
+
tags: merge(tags, {
|
|
152
|
+
browser: getBrowserName(),
|
|
153
|
+
browserVersion: getBrowserVersion(),
|
|
154
|
+
domain: window.location.hostname,
|
|
155
|
+
locale: window.navigator.language,
|
|
156
|
+
os: getOSNameInternal(),
|
|
157
|
+
}),
|
|
158
|
+
timestamp: res.timestamp,
|
|
159
|
+
type: ['behavioral'],
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
});
|
|
@@ -248,9 +248,15 @@ describe('plugin-metrics', () => {
|
|
|
248
248
|
});
|
|
249
249
|
|
|
250
250
|
it('appends the correct join times to the request for client.media.rx.start', async () => {
|
|
251
|
-
webex.internal.newMetrics.callDiagnosticLatencies.
|
|
251
|
+
webex.internal.newMetrics.callDiagnosticLatencies.getLocalSDPGenRemoteSDPRecv = sinon
|
|
252
|
+
.stub()
|
|
253
|
+
.returns(5);
|
|
254
|
+
webex.internal.newMetrics.callDiagnosticLatencies.getAudioJoinRespRxStart = sinon
|
|
252
255
|
.stub()
|
|
253
256
|
.returns(10);
|
|
257
|
+
webex.internal.newMetrics.callDiagnosticLatencies.getVideoJoinRespRxStart = sinon
|
|
258
|
+
.stub()
|
|
259
|
+
.returns(20);
|
|
254
260
|
const promise = webex.internal.newMetrics.callDiagnosticMetrics.submitToCallDiagnostics(
|
|
255
261
|
//@ts-ignore
|
|
256
262
|
{event: {name: 'client.media.rx.start'}}
|
|
@@ -265,7 +271,13 @@ describe('plugin-metrics', () => {
|
|
|
265
271
|
assert.deepEqual(webex.request.getCalls()[0].args[0].body.metrics[0].eventPayload.event, {
|
|
266
272
|
name: 'client.media.rx.start',
|
|
267
273
|
joinTimes: {
|
|
268
|
-
localSDPGenRemoteSDPRecv:
|
|
274
|
+
localSDPGenRemoteSDPRecv: 5,
|
|
275
|
+
},
|
|
276
|
+
audioSetupDelay: {
|
|
277
|
+
joinRespRxStart: 10,
|
|
278
|
+
},
|
|
279
|
+
videoSetupDelay: {
|
|
280
|
+
joinRespRxStart: 20,
|
|
269
281
|
},
|
|
270
282
|
});
|
|
271
283
|
assert.lengthOf(
|
|
@@ -274,19 +286,16 @@ describe('plugin-metrics', () => {
|
|
|
274
286
|
);
|
|
275
287
|
});
|
|
276
288
|
|
|
277
|
-
it('appends the correct join times to the request for client.media
|
|
278
|
-
webex.internal.newMetrics.callDiagnosticLatencies.
|
|
279
|
-
.stub()
|
|
280
|
-
.returns(10);
|
|
281
|
-
webex.internal.newMetrics.callDiagnosticLatencies.getInterstitialToMediaOKJMT = sinon
|
|
289
|
+
it('appends the correct join times to the request for client.media.tx.start', async () => {
|
|
290
|
+
webex.internal.newMetrics.callDiagnosticLatencies.getAudioJoinRespTxStart = sinon
|
|
282
291
|
.stub()
|
|
283
292
|
.returns(10);
|
|
284
|
-
webex.internal.newMetrics.callDiagnosticLatencies.
|
|
293
|
+
webex.internal.newMetrics.callDiagnosticLatencies.getVideoJoinRespTxStart = sinon
|
|
285
294
|
.stub()
|
|
286
|
-
.returns(
|
|
295
|
+
.returns(20);
|
|
287
296
|
const promise = webex.internal.newMetrics.callDiagnosticMetrics.submitToCallDiagnostics(
|
|
288
297
|
//@ts-ignore
|
|
289
|
-
{event: {name: 'client.media
|
|
298
|
+
{event: {name: 'client.media.tx.start'}}
|
|
290
299
|
);
|
|
291
300
|
await flushPromises();
|
|
292
301
|
clock.tick(config.metrics.batcherWait);
|
|
@@ -296,12 +305,12 @@ describe('plugin-metrics', () => {
|
|
|
296
305
|
//@ts-ignore
|
|
297
306
|
assert.calledOnce(webex.request);
|
|
298
307
|
assert.deepEqual(webex.request.getCalls()[0].args[0].body.metrics[0].eventPayload.event, {
|
|
299
|
-
name: 'client.media
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
308
|
+
name: 'client.media.tx.start',
|
|
309
|
+
audioSetupDelay: {
|
|
310
|
+
joinRespTxStart: 10,
|
|
311
|
+
},
|
|
312
|
+
videoSetupDelay: {
|
|
313
|
+
joinRespTxStart: 20,
|
|
305
314
|
},
|
|
306
315
|
});
|
|
307
316
|
assert.lengthOf(
|
|
@@ -310,13 +319,28 @@ describe('plugin-metrics', () => {
|
|
|
310
319
|
);
|
|
311
320
|
});
|
|
312
321
|
|
|
313
|
-
it('appends the correct
|
|
322
|
+
it('appends the correct join times to the request for client.media-engine.ready', async () => {
|
|
314
323
|
webex.internal.newMetrics.callDiagnosticLatencies.getDiffBetweenTimestamps = sinon
|
|
315
324
|
.stub()
|
|
316
325
|
.returns(10);
|
|
326
|
+
webex.internal.newMetrics.callDiagnosticLatencies.getInterstitialToMediaOKJMT = sinon
|
|
327
|
+
.stub()
|
|
328
|
+
.returns(22);
|
|
329
|
+
webex.internal.newMetrics.callDiagnosticLatencies.getClickToInterstitial = sinon
|
|
330
|
+
.stub()
|
|
331
|
+
.returns(35);
|
|
332
|
+
webex.internal.newMetrics.callDiagnosticLatencies.getInterstitialToJoinOK = sinon
|
|
333
|
+
.stub()
|
|
334
|
+
.returns(5);
|
|
335
|
+
webex.internal.newMetrics.callDiagnosticLatencies.getInterstitialToJoinOK = sinon
|
|
336
|
+
.stub()
|
|
337
|
+
.returns(7);
|
|
338
|
+
webex.internal.newMetrics.callDiagnosticLatencies.getStayLobbyTime = sinon
|
|
339
|
+
.stub()
|
|
340
|
+
.returns(1);
|
|
317
341
|
const promise = webex.internal.newMetrics.callDiagnosticMetrics.submitToCallDiagnostics(
|
|
318
342
|
//@ts-ignore
|
|
319
|
-
{event: {name: 'client.
|
|
343
|
+
{event: {name: 'client.media-engine.ready'}}
|
|
320
344
|
);
|
|
321
345
|
await flushPromises();
|
|
322
346
|
clock.tick(config.metrics.batcherWait);
|
|
@@ -326,14 +350,12 @@ describe('plugin-metrics', () => {
|
|
|
326
350
|
//@ts-ignore
|
|
327
351
|
assert.calledOnce(webex.request);
|
|
328
352
|
assert.deepEqual(webex.request.getCalls()[0].args[0].body.metrics[0].eventPayload.event, {
|
|
329
|
-
name: 'client.
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
joinRespRxStart: 10,
|
|
336
|
-
joinRespTxStart: 10,
|
|
353
|
+
name: 'client.media-engine.ready',
|
|
354
|
+
joinTimes: {
|
|
355
|
+
totalMediaJMT: 61,
|
|
356
|
+
interstitialToMediaOKJMT: 22,
|
|
357
|
+
callInitMediaEngineReady: 10,
|
|
358
|
+
stayLobbyTime: 1,
|
|
337
359
|
},
|
|
338
360
|
});
|
|
339
361
|
assert.lengthOf(
|
|
@@ -341,32 +363,6 @@ describe('plugin-metrics', () => {
|
|
|
341
363
|
0
|
|
342
364
|
);
|
|
343
365
|
});
|
|
344
|
-
|
|
345
|
-
it('doesnt include audioSetup and videoSetup delays for other events', async () => {
|
|
346
|
-
const promise = webex.internal.newMetrics.callDiagnosticMetrics.submitToCallDiagnostics(
|
|
347
|
-
//@ts-ignore
|
|
348
|
-
{event: {name: 'client.alert.displayed'}}
|
|
349
|
-
);
|
|
350
|
-
await flushPromises();
|
|
351
|
-
clock.tick(config.metrics.batcherWait);
|
|
352
|
-
|
|
353
|
-
await promise;
|
|
354
|
-
|
|
355
|
-
//@ts-ignore
|
|
356
|
-
assert.calledOnce(webex.request);
|
|
357
|
-
assert.deepEqual(
|
|
358
|
-
webex.request.getCalls()[0].args[0].body.metrics[0].eventPayload.event.audioSetupDelay,
|
|
359
|
-
undefined
|
|
360
|
-
);
|
|
361
|
-
assert.deepEqual(
|
|
362
|
-
webex.request.getCalls()[0].args[0].body.metrics[0].eventPayload.event.videoSetupDelay,
|
|
363
|
-
undefined
|
|
364
|
-
);
|
|
365
|
-
assert.lengthOf(
|
|
366
|
-
webex.internal.newMetrics.callDiagnosticMetrics.callDiagnosticEventsBatcher.queue,
|
|
367
|
-
0
|
|
368
|
-
);
|
|
369
|
-
});
|
|
370
366
|
});
|
|
371
367
|
|
|
372
368
|
describe('when the request fails', () => {
|
|
@@ -668,7 +668,7 @@ describe('internal-plugin-metrics', () => {
|
|
|
668
668
|
assert.deepEqual(cdl.getInterstitialToMediaOKJMT(), 10);
|
|
669
669
|
});
|
|
670
670
|
|
|
671
|
-
|
|
671
|
+
describe('calculates getU2CTime correctly', () => {
|
|
672
672
|
it('returns undefined when no precomputed value available', () => {
|
|
673
673
|
assert.deepEqual(cdl.getU2CTime(), undefined);
|
|
674
674
|
});
|
|
@@ -1454,6 +1454,9 @@ describe('internal-plugin-metrics', () => {
|
|
|
1454
1454
|
eventData: {webClientDomain: 'whatever'},
|
|
1455
1455
|
intervals: [{}],
|
|
1456
1456
|
callingServiceType: 'LOCUS',
|
|
1457
|
+
meetingJoinInfo: {
|
|
1458
|
+
clientSignallingProtocol: 'WebRTC',
|
|
1459
|
+
},
|
|
1457
1460
|
sourceMetadata: {
|
|
1458
1461
|
applicationSoftwareType: 'webex-js-sdk',
|
|
1459
1462
|
applicationSoftwareVersion: 'webex-version',
|
|
@@ -1490,6 +1493,9 @@ describe('internal-plugin-metrics', () => {
|
|
|
1490
1493
|
eventData: {webClientDomain: 'whatever'},
|
|
1491
1494
|
intervals: [{}],
|
|
1492
1495
|
callingServiceType: 'LOCUS',
|
|
1496
|
+
meetingJoinInfo: {
|
|
1497
|
+
clientSignallingProtocol: 'WebRTC',
|
|
1498
|
+
},
|
|
1493
1499
|
sourceMetadata: {
|
|
1494
1500
|
applicationSoftwareType: 'webex-js-sdk',
|
|
1495
1501
|
applicationSoftwareVersion: 'webex-version',
|
|
@@ -1524,6 +1530,9 @@ describe('internal-plugin-metrics', () => {
|
|
|
1524
1530
|
eventData: {webClientDomain: 'whatever'},
|
|
1525
1531
|
intervals: [{}],
|
|
1526
1532
|
callingServiceType: 'LOCUS',
|
|
1533
|
+
meetingJoinInfo: {
|
|
1534
|
+
clientSignallingProtocol: 'WebRTC',
|
|
1535
|
+
},
|
|
1527
1536
|
sourceMetadata: {
|
|
1528
1537
|
applicationSoftwareType: 'webex-js-sdk',
|
|
1529
1538
|
applicationSoftwareVersion: 'webex-version',
|