@webex/internal-plugin-metrics 3.8.1 → 3.9.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.
Files changed (35) hide show
  1. package/dist/call-diagnostic/call-diagnostic-metrics-latencies.js +92 -14
  2. package/dist/call-diagnostic/call-diagnostic-metrics-latencies.js.map +1 -1
  3. package/dist/call-diagnostic/call-diagnostic-metrics.js +348 -48
  4. package/dist/call-diagnostic/call-diagnostic-metrics.js.map +1 -1
  5. package/dist/call-diagnostic/call-diagnostic-metrics.util.js +21 -0
  6. package/dist/call-diagnostic/call-diagnostic-metrics.util.js.map +1 -1
  7. package/dist/call-diagnostic/config.js +3 -1
  8. package/dist/call-diagnostic/config.js.map +1 -1
  9. package/dist/index.js.map +1 -1
  10. package/dist/metrics.js +1 -1
  11. package/dist/metrics.types.js.map +1 -1
  12. package/dist/new-metrics.js +43 -1
  13. package/dist/new-metrics.js.map +1 -1
  14. package/dist/types/call-diagnostic/call-diagnostic-metrics-latencies.d.ts +23 -1
  15. package/dist/types/call-diagnostic/call-diagnostic-metrics.d.ts +176 -10
  16. package/dist/types/call-diagnostic/config.d.ts +2 -0
  17. package/dist/types/index.d.ts +2 -2
  18. package/dist/types/metrics.types.d.ts +18 -7
  19. package/dist/types/new-metrics.d.ts +19 -2
  20. package/package.json +11 -12
  21. package/src/call-diagnostic/call-diagnostic-metrics-latencies.ts +104 -14
  22. package/src/call-diagnostic/call-diagnostic-metrics.ts +363 -25
  23. package/src/call-diagnostic/call-diagnostic-metrics.util.ts +20 -0
  24. package/src/call-diagnostic/config.ts +3 -0
  25. package/src/index.ts +2 -0
  26. package/src/metrics.types.ts +25 -6
  27. package/src/new-metrics.ts +52 -1
  28. package/test/unit/spec/call-diagnostic/call-diagnostic-metrics-batcher.ts +20 -1
  29. package/test/unit/spec/call-diagnostic/call-diagnostic-metrics-latencies.ts +255 -0
  30. package/test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts +829 -39
  31. package/test/unit/spec/call-diagnostic/call-diagnostic-metrics.util.ts +6 -0
  32. package/test/unit/spec/new-metrics.ts +67 -2
  33. package/test/unit/spec/prelogin-metrics-batcher.ts +72 -3
  34. package/dist/call-diagnostic-events-batcher.js +0 -60
  35. package/dist/call-diagnostic-events-batcher.js.map +0 -1
@@ -239,6 +239,20 @@ export const prepareDiagnosticMetricItem = (webex: any, item: any) => {
239
239
 
240
240
  // Set upgradeChannel to 'gold' if buildType is 'prod', otherwise to the buildType value
241
241
  const upgradeChannel = buildType === 'prod' ? 'gold' : buildType;
242
+ if (webex.devicemanager) {
243
+ const pairedDevice = webex.devicemanager.getPairedDevice();
244
+ if (pairedDevice) {
245
+ const devicePayload = {
246
+ deviceId: pairedDevice.deviceInfo?.id,
247
+ devicePairingType: webex.devicemanager.getPairedMethod(),
248
+ deviceURL: pairedDevice.url,
249
+ isPersonalDevice: pairedDevice.mode === 'personal',
250
+ productName: pairedDevice.devices[0]?.productName,
251
+ };
252
+ item.eventPayload.event.pairingState = 'paired';
253
+ item.eventPayload.event.pairedDevice = devicePayload;
254
+ }
255
+ }
242
256
 
243
257
  const origin: Partial<Event['origin']> = {
244
258
  buildType,
@@ -257,6 +271,7 @@ export const prepareDiagnosticMetricItem = (webex: any, item: any) => {
257
271
  switch (eventName) {
258
272
  case 'client.webexapp.launched':
259
273
  joinTimes.downloadTime = cdl.getDownloadTimeJMT();
274
+ joinTimes.pageJmt = cdl.getPageJMT();
260
275
  break;
261
276
  case 'client.login.end':
262
277
  joinTimes.otherAppApiReqResp = cdl.getOtherAppApiReqResp();
@@ -267,6 +282,7 @@ export const prepareDiagnosticMetricItem = (webex: any, item: any) => {
267
282
  joinTimes.clickToInterstitial = cdl.getClickToInterstitial();
268
283
  joinTimes.refreshCaptchaServiceReqResp = cdl.getRefreshCaptchaReqResp();
269
284
  joinTimes.downloadIntelligenceModelsReqResp = cdl.getDownloadIntelligenceModelsReqResp();
285
+ joinTimes.clickToInterstitialWithUserDelay = cdl.getClickToInterstitialWithUserDelay();
270
286
  break;
271
287
 
272
288
  case 'client.call.initiated':
@@ -287,6 +303,8 @@ export const prepareDiagnosticMetricItem = (webex: any, item: any) => {
287
303
  joinTimes.totalJmt = cdl.getTotalJMT();
288
304
  joinTimes.clientJmt = cdl.getClientJMT();
289
305
  joinTimes.downloadTime = cdl.getDownloadTimeJMT();
306
+ joinTimes.clickToInterstitialWithUserDelay = cdl.getClickToInterstitialWithUserDelay();
307
+ joinTimes.totalJMTWithUserDelay = cdl.getTotalJMTWithUserDelay();
290
308
  break;
291
309
 
292
310
  case 'client.ice.end':
@@ -307,6 +325,8 @@ export const prepareDiagnosticMetricItem = (webex: any, item: any) => {
307
325
  joinTimes.interstitialToMediaOKJMT = cdl.getInterstitialToMediaOKJMT();
308
326
  joinTimes.callInitMediaEngineReady = cdl.getCallInitMediaEngineReady();
309
327
  joinTimes.stayLobbyTime = cdl.getStayLobbyTime();
328
+ joinTimes.totalMediaJMTWithUserDelay = cdl.getTotalMediaJMTWithUserDelay();
329
+ joinTimes.totalJMTWithUserDelay = cdl.getTotalJMTWithUserDelay();
310
330
  break;
311
331
 
312
332
  case 'client.media.tx.start':
@@ -5,6 +5,7 @@
5
5
  import {ClientEventError, ClientSubServiceType} from '../metrics.types';
6
6
 
7
7
  export const CALL_DIAGNOSTIC_LOG_IDENTIFIER = 'call-diagnostic-events -> ';
8
+ export const CALL_FEATURE_LOG_IDENTIFIER = 'call-diagnostic-events-feature -> ';
8
9
 
9
10
  export const AUTHENTICATION_FAILED_CODE = 1010;
10
11
  export const NETWORK_ERROR = 1026;
@@ -738,3 +739,5 @@ export const CLIENT_ERROR_CODE_TO_ERROR_PAYLOAD: Record<number, Partial<ClientEv
738
739
  };
739
740
 
740
741
  export const CALL_DIAGNOSTIC_EVENT_FAILED_TO_SEND = 'js_sdk_call_diagnostic_event_failed_to_send';
742
+
743
+ export const CALL_FEATURE_EVENT_FAILED_TO_SEND = 'js_sdk_call_feature_event_failed_to_send';
package/src/index.ts CHANGED
@@ -18,6 +18,7 @@ import {
18
18
  SubmitBusinessEvent,
19
19
  SubmitMQE,
20
20
  PreComputedLatencies,
21
+ SubmitFeatureEvent,
21
22
  } from './metrics.types';
22
23
  import * as CALL_DIAGNOSTIC_CONFIG from './call-diagnostic/config';
23
24
  import * as CallDiagnosticUtils from './call-diagnostic/call-diagnostic-metrics.util';
@@ -61,4 +62,5 @@ export type {
61
62
  SubmitOperationalEvent,
62
63
  SubmitBusinessEvent,
63
64
  PreComputedLatencies,
65
+ SubmitFeatureEvent,
64
66
  };
@@ -2,9 +2,12 @@ import {
2
2
  ClientEvent as RawClientEvent,
3
3
  Event as RawEvent,
4
4
  MediaQualityEvent as RawMediaQualityEvent,
5
+ FeatureEvent as RawFeatureEvent,
5
6
  } from '@webex/event-dictionary-ts';
6
7
 
7
- export type Event = Omit<RawEvent, 'event'> & {event: RawClientEvent | RawMediaQualityEvent};
8
+ export type Event = Omit<RawEvent, 'event'> & {
9
+ event: RawClientEvent | RawMediaQualityEvent | RawFeatureEvent;
10
+ };
8
11
 
9
12
  export type ClientEventError = NonNullable<RawClientEvent['errors']>[0];
10
13
 
@@ -157,7 +160,9 @@ export type InternalEvent = {
157
160
  | 'internal.client.meeting.interstitial-window.showed'
158
161
  | 'internal.client.interstitial-window.click.joinbutton'
159
162
  | 'internal.client.add-media.turn-discovery.start'
160
- | 'internal.client.add-media.turn-discovery.end';
163
+ | 'internal.client.add-media.turn-discovery.end'
164
+ | 'internal.client.share.initiated'
165
+ | 'internal.client.share.stopped';
161
166
 
162
167
  payload?: never;
163
168
  options?: never;
@@ -212,10 +217,9 @@ export type BehavioralEvent = TaggedEvent;
212
217
  export type OperationalEvent = TaggedEvent;
213
218
 
214
219
  export interface FeatureEvent {
215
- // TODO: not implemented
216
- name: never;
217
- payload?: never;
218
- options?: never;
220
+ name: RawFeatureEvent['name'];
221
+ payload?: RawFeatureEvent;
222
+ options?: SubmitClientEventOptions;
219
223
  }
220
224
 
221
225
  export interface MediaQualityEvent {
@@ -251,6 +255,8 @@ export type ClientEventPayload = RecursivePartial<ClientEvent['payload']>;
251
255
  export type ClientEventLeaveReason = ClientEvent['payload']['leaveReason'];
252
256
  export type ClientEventPayloadError = ClientEvent['payload']['errors'];
253
257
 
258
+ export type ClientFeatureEventPayload = RecursivePartial<FeatureEvent['payload']>;
259
+
254
260
  export type MediaQualityEventAudioSetupDelayPayload = NonNullable<
255
261
  MediaQualityEvent['payload']
256
262
  >['audioSetupDelay'];
@@ -311,6 +317,7 @@ export type PreComputedLatencies =
311
317
  | 'internal.download.time'
312
318
  | 'internal.get.cluster.time'
313
319
  | 'internal.click.to.interstitial'
320
+ | 'internal.click.to.interstitial.with.user.delay'
314
321
  | 'internal.refresh.captcha.time'
315
322
  | 'internal.exchange.ci.token.time'
316
323
  | 'internal.get.u2c.time'
@@ -339,3 +346,15 @@ export interface DelayedClientEvent {
339
346
  payload?: RecursivePartial<ClientEvent['payload']>;
340
347
  options?: SubmitClientEventOptions;
341
348
  }
349
+
350
+ export type SubmitFeatureEvent = (args: {
351
+ name: FeatureEvent['name'];
352
+ payload?: RecursivePartial<FeatureEvent['payload']>;
353
+ options?: SubmitClientEventOptions;
354
+ }) => Promise<any>;
355
+
356
+ export interface DelayedClientFeatureEvent {
357
+ name: FeatureEvent['name'];
358
+ payload?: RecursivePartial<FeatureEvent['payload']>;
359
+ options?: SubmitClientEventOptions;
360
+ }
@@ -23,6 +23,7 @@ import {
23
23
  SubmitClientEventOptions,
24
24
  Table,
25
25
  DelayedClientEvent,
26
+ DelayedClientFeatureEvent,
26
27
  } from './metrics.types';
27
28
  import CallDiagnosticLatencies from './call-diagnostic/call-diagnostic-metrics-latencies';
28
29
  import {setMetricTimings} from './call-diagnostic/call-diagnostic-metrics.util';
@@ -51,11 +52,18 @@ class Metrics extends WebexPlugin {
51
52
  */
52
53
  delaySubmitClientEvents = false;
53
54
 
55
+ /**
56
+ * Whether or not to delay the submission of feature events.
57
+ */
58
+ delaySubmitClientFeatureEvents = false;
59
+
54
60
  /**
55
61
  * Overrides for delayed client events. E.g. if you want to override the correlationId for all delayed client events, you can set this to { correlationId: 'newCorrelationId' }
56
62
  */
57
63
  delayedClientEventsOverrides: Partial<DelayedClientEvent['options']> = {};
58
64
 
65
+ delayedClientFeatureEventsOverrides: Partial<DelayedClientFeatureEvent['options']> = {};
66
+
59
67
  /**
60
68
  * Constructor
61
69
  * @param args
@@ -275,7 +283,25 @@ class Metrics extends WebexPlugin {
275
283
  payload?: RecursivePartial<FeatureEvent['payload']>;
276
284
  options: any;
277
285
  }) {
278
- throw new Error('Not implemented.');
286
+ if (!this.callDiagnosticLatencies || !this.callDiagnosticMetrics) {
287
+ // @ts-ignore
288
+ this.webex.logger.log(
289
+ `NewMetrics: @submitFeatureEvent. Attempted to submit before webex.ready. Event name: ${name}`
290
+ );
291
+
292
+ return Promise.resolve();
293
+ }
294
+ this.callDiagnosticLatencies.saveTimestamp({
295
+ key: name,
296
+ options: {meetingId: options?.meetingId},
297
+ });
298
+
299
+ return this.callDiagnosticMetrics.submitFeatureEvent({
300
+ name,
301
+ payload,
302
+ options,
303
+ delaySubmitEvent: this.delaySubmitClientFeatureEvents,
304
+ });
279
305
  }
280
306
 
281
307
  /**
@@ -432,6 +458,31 @@ class Metrics extends WebexPlugin {
432
458
 
433
459
  return Promise.resolve();
434
460
  }
461
+
462
+ /**
463
+ * Sets the value of setDelaySubmitClientFeatureEvents.
464
+ * If set to true, feature events will be delayed until submitDelayedClientFeatureEvents is called.
465
+ * If set to false, delayed feature events will be submitted.
466
+ *
467
+ * @param {object} options - {shouldDelay: A boolean value indicating whether to delay the submission of feature events,
468
+ * overrides: An object containing overrides for the feature events}
469
+ */
470
+ public setDelaySubmitClientFeatureEvents({
471
+ shouldDelay,
472
+ overrides,
473
+ }: {
474
+ shouldDelay: boolean;
475
+ overrides?: Partial<DelayedClientFeatureEvent['options']>;
476
+ }) {
477
+ this.delaySubmitClientFeatureEvents = shouldDelay;
478
+ this.delayedClientFeatureEventsOverrides = overrides || {};
479
+
480
+ if (this.isReady && !shouldDelay) {
481
+ return this.callDiagnosticMetrics.submitDelayedClientFeatureEvents(overrides);
482
+ }
483
+
484
+ return Promise.resolve();
485
+ }
435
486
  }
436
487
 
437
488
  export default Metrics;
@@ -108,6 +108,8 @@ describe('plugin-metrics', () => {
108
108
  .returns(10);
109
109
  webex.internal.newMetrics.callDiagnosticLatencies.getDownloadIntelligenceModelsReqResp =
110
110
  sinon.stub().returns(42);
111
+ webex.internal.newMetrics.callDiagnosticLatencies.getClickToInterstitialWithUserDelay =
112
+ sinon.stub().returns(12);
111
113
 
112
114
  const promise = webex.internal.newMetrics.callDiagnosticMetrics.submitToCallDiagnostics(
113
115
  //@ts-ignore
@@ -127,6 +129,7 @@ describe('plugin-metrics', () => {
127
129
  meetingInfoReqResp: 10,
128
130
  refreshCaptchaServiceReqResp: 10,
129
131
  downloadIntelligenceModelsReqResp: 42,
132
+ clickToInterstitialWithUserDelay: 12,
130
133
  },
131
134
  });
132
135
  assert.lengthOf(
@@ -183,9 +186,15 @@ describe('plugin-metrics', () => {
183
186
  webex.internal.newMetrics.callDiagnosticLatencies.getCallInitJoinReq = sinon
184
187
  .stub()
185
188
  .returns(10);
186
- webex.internal.newMetrics.callDiagnosticLatencies.getDownloadTimeJMT = sinon
189
+ webex.internal.newMetrics.callDiagnosticLatencies.getDownloadTimeJMT = sinon
187
190
  .stub()
188
191
  .returns(100);
192
+ webex.internal.newMetrics.callDiagnosticLatencies.getClickToInterstitialWithUserDelay = sinon
193
+ .stub()
194
+ .returns(43);
195
+ webex.internal.newMetrics.callDiagnosticLatencies.getTotalJMTWithUserDelay = sinon
196
+ .stub()
197
+ .returns(64);
189
198
  const promise = webex.internal.newMetrics.callDiagnosticMetrics.submitToCallDiagnostics(
190
199
  //@ts-ignore
191
200
  {event: {name: 'client.locus.join.response'}}
@@ -209,6 +218,8 @@ describe('plugin-metrics', () => {
209
218
  totalJmt: 20,
210
219
  clientJmt: 5,
211
220
  downloadTime: 100,
221
+ clickToInterstitialWithUserDelay: 43,
222
+ totalJMTWithUserDelay: 64,
212
223
  },
213
224
  });
214
225
  assert.lengthOf(
@@ -338,6 +349,12 @@ describe('plugin-metrics', () => {
338
349
  webex.internal.newMetrics.callDiagnosticLatencies.getStayLobbyTime = sinon
339
350
  .stub()
340
351
  .returns(1);
352
+ webex.internal.newMetrics.callDiagnosticLatencies.getTotalMediaJMTWithUserDelay = sinon
353
+ .stub()
354
+ .returns(43);
355
+ webex.internal.newMetrics.callDiagnosticLatencies.getTotalJMTWithUserDelay = sinon
356
+ .stub()
357
+ .returns(64);
341
358
  const promise = webex.internal.newMetrics.callDiagnosticMetrics.submitToCallDiagnostics(
342
359
  //@ts-ignore
343
360
  {event: {name: 'client.media-engine.ready'}}
@@ -356,6 +373,8 @@ describe('plugin-metrics', () => {
356
373
  interstitialToMediaOKJMT: 22,
357
374
  callInitMediaEngineReady: 10,
358
375
  stayLobbyTime: 1,
376
+ totalMediaJMTWithUserDelay: 43,
377
+ totalJMTWithUserDelay: 64,
359
378
  },
360
379
  });
361
380
  assert.lengthOf(
@@ -130,6 +130,82 @@ describe('internal-plugin-metrics', () => {
130
130
  assert.deepEqual(res2, undefined);
131
131
  });
132
132
 
133
+ describe('getDiffBetweenTimestamps with clamping', () => {
134
+ it('should return diff without clamping when no clampValues provided', () => {
135
+ cdl.saveTimestamp({key: 'client.alert.displayed', value: 10});
136
+ cdl.saveTimestamp({key: 'client.alert.removed', value: 50});
137
+ const res = cdl.getDiffBetweenTimestamps('client.alert.displayed', 'client.alert.removed');
138
+ assert.deepEqual(res, 40);
139
+ });
140
+
141
+ it('should return diff without clamping when value is within range', () => {
142
+ cdl.saveTimestamp({key: 'client.alert.displayed', value: 10});
143
+ cdl.saveTimestamp({key: 'client.alert.removed', value: 50});
144
+ const res = cdl.getDiffBetweenTimestamps('client.alert.displayed', 'client.alert.removed', {
145
+ minimum: 0,
146
+ maximum: 100
147
+ });
148
+ assert.deepEqual(res, 40);
149
+ });
150
+
151
+ it('should clamp to minimum when diff is below minimum', () => {
152
+ cdl.saveTimestamp({key: 'client.alert.displayed', value: 50});
153
+ cdl.saveTimestamp({key: 'client.alert.removed', value: 45});
154
+ const res = cdl.getDiffBetweenTimestamps('client.alert.displayed', 'client.alert.removed', {
155
+ minimum: 10,
156
+ maximum: 100
157
+ });
158
+ assert.deepEqual(res, 10);
159
+ });
160
+
161
+ it('should clamp to maximum when diff is above maximum', () => {
162
+ cdl.saveTimestamp({key: 'client.alert.displayed', value: 10});
163
+ cdl.saveTimestamp({key: 'client.alert.removed', value: 210});
164
+ const res = cdl.getDiffBetweenTimestamps('client.alert.displayed', 'client.alert.removed', {
165
+ minimum: 0,
166
+ maximum: 100
167
+ });
168
+ assert.deepEqual(res, 100);
169
+ });
170
+
171
+ it('should use default minimum of 0 when only maximum is specified', () => {
172
+ cdl.saveTimestamp({key: 'client.alert.displayed', value: 50});
173
+ cdl.saveTimestamp({key: 'client.alert.removed', value: 45});
174
+ const res = cdl.getDiffBetweenTimestamps('client.alert.displayed', 'client.alert.removed', {
175
+ maximum: 100
176
+ });
177
+ assert.deepEqual(res, 0);
178
+ });
179
+
180
+ it('should not clamp maximum when maximum is undefined', () => {
181
+ cdl.saveTimestamp({key: 'client.alert.displayed', value: 10});
182
+ cdl.saveTimestamp({key: 'client.alert.removed', value: 2000});
183
+ const res = cdl.getDiffBetweenTimestamps('client.alert.displayed', 'client.alert.removed', {
184
+ minimum: 5
185
+ });
186
+ assert.deepEqual(res, 1990);
187
+ });
188
+
189
+ it('should handle negative differences correctly with clamping', () => {
190
+ cdl.saveTimestamp({key: 'client.alert.displayed', value: 100});
191
+ cdl.saveTimestamp({key: 'client.alert.removed', value: 50});
192
+ const res = cdl.getDiffBetweenTimestamps('client.alert.displayed', 'client.alert.removed', {
193
+ minimum: 10,
194
+ maximum: 1000
195
+ });
196
+ assert.deepEqual(res, 10);
197
+ });
198
+
199
+ it('should return undefined when timestamps are missing even with clamping', () => {
200
+ cdl.saveTimestamp({key: 'client.alert.displayed', value: 10});
201
+ const res = cdl.getDiffBetweenTimestamps('client.alert.displayed', 'client.alert.removed', {
202
+ minimum: 0,
203
+ maximum: 100
204
+ });
205
+ assert.deepEqual(res, undefined);
206
+ });
207
+ });
208
+
133
209
  it('calculates getMeetingInfoReqResp correctly', () => {
134
210
  cdl.saveTimestamp({key: 'internal.client.meetinginfo.request', value: 10});
135
211
  cdl.saveTimestamp({key: 'internal.client.meetinginfo.response', value: 20});
@@ -446,6 +522,36 @@ describe('internal-plugin-metrics', () => {
446
522
  assert.deepEqual(cdl.getClickToInterstitial(), 0);
447
523
  });
448
524
 
525
+ it('calculates getClickToInterstitialWithUserDelay correctly', () => {
526
+ cdl.saveTimestamp({
527
+ key: 'internal.client.meeting.click.joinbutton',
528
+ value: 10,
529
+ });
530
+ cdl.saveTimestamp({
531
+ key: 'internal.client.meeting.interstitial-window.showed',
532
+ value: 20,
533
+ });
534
+ assert.deepEqual(cdl.getClickToInterstitialWithUserDelay(), 10);
535
+ });
536
+
537
+ it('calculates getClickToInterstitialWithUserDelay without join button timestamp', () => {
538
+ cdl.saveLatency('internal.click.to.interstitial.with.user.delay', 5);
539
+ cdl.saveTimestamp({
540
+ key: 'internal.client.meeting.interstitial-window.showed',
541
+ value: 20,
542
+ });
543
+ assert.deepEqual(cdl.getClickToInterstitialWithUserDelay(), 5);
544
+ });
545
+
546
+ it('calculates getClickToInterstitialWithUserDelay without join button timestamp when it is 0', () => {
547
+ cdl.saveLatency('internal.click.to.interstitial.with.user.delay', 0);
548
+ cdl.saveTimestamp({
549
+ key: 'internal.client.meeting.interstitial-window.showed',
550
+ value: 20,
551
+ });
552
+ assert.deepEqual(cdl.getClickToInterstitialWithUserDelay(), 0);
553
+ });
554
+
449
555
  it('calculates getInterstitialToJoinOK correctly', () => {
450
556
  cdl.saveTimestamp({
451
557
  key: 'internal.client.interstitial-window.click.joinbutton',
@@ -554,6 +660,78 @@ describe('internal-plugin-metrics', () => {
554
660
  assert.deepEqual(cdl.getTotalJMT(), undefined);
555
661
  });
556
662
 
663
+ it('calculates getTotalJMTWithUserDelay correctly', () => {
664
+ cdl.saveTimestamp({
665
+ key: 'internal.client.interstitial-window.click.joinbutton',
666
+ value: 5,
667
+ });
668
+ cdl.saveTimestamp({
669
+ key: 'internal.client.meeting.click.joinbutton',
670
+ value: 10,
671
+ });
672
+ cdl.saveTimestamp({
673
+ key: 'internal.client.meeting.interstitial-window.showed',
674
+ value: 20,
675
+ });
676
+ cdl.saveTimestamp({
677
+ key: 'client.locus.join.response',
678
+ value: 40,
679
+ });
680
+ assert.deepEqual(cdl.getTotalJMTWithUserDelay(), 45);
681
+ });
682
+
683
+ it('calculates getTotalJMTWithUserDelay correctly when clickToInterstitialWithUserDelay is 0', () => {
684
+ cdl.saveLatency('internal.click.to.interstitial.with.user.delay', 0);
685
+ cdl.saveTimestamp({
686
+ key: 'internal.client.interstitial-window.click.joinbutton',
687
+ value: 20,
688
+ });
689
+ cdl.saveTimestamp({
690
+ key: 'client.locus.join.response',
691
+ value: 40,
692
+ });
693
+ assert.deepEqual(cdl.getTotalJMTWithUserDelay(), 20);
694
+ });
695
+
696
+ it('calculates getTotalJMTWithUserDelay correctly when interstitialToJoinOk is 0', () => {
697
+ cdl.saveTimestamp({
698
+ key: 'internal.client.interstitial-window.click.joinbutton',
699
+ value: 40,
700
+ });
701
+ cdl.saveLatency('internal.click.to.interstitial.with.user.delay', 12);
702
+ cdl.saveTimestamp({
703
+ key: 'client.locus.join.response',
704
+ value: 40,
705
+ });
706
+ assert.deepEqual(cdl.getTotalJMTWithUserDelay(), 12);
707
+ });
708
+
709
+ it('calculates getTotalJMTWithUserDelay correctly when both clickToInterstitialWithUserDelay and interstitialToJoinOk are 0', () => {
710
+ cdl.saveTimestamp({
711
+ key: 'internal.client.interstitial-window.click.joinbutton',
712
+ value: 40,
713
+ });
714
+ cdl.saveLatency('internal.click.to.interstitial.with.user.delay', 0);
715
+ cdl.saveTimestamp({
716
+ key: 'client.locus.join.response',
717
+ value: 40,
718
+ });
719
+ assert.deepEqual(cdl.getTotalJMTWithUserDelay(), 0);
720
+ });
721
+
722
+ it('calculates getTotalJMTWithUserDelay correctly when both clickToInterstitialWithUserDelay is not a number', () => {
723
+ cdl.saveTimestamp({
724
+ key: 'internal.client.interstitial-window.click.joinbutton',
725
+ value: 40,
726
+ });
727
+ cdl.saveLatency('internal.click.to.interstitial.with.user.delay', 'eleven' as unknown as number);
728
+ cdl.saveTimestamp({
729
+ key: 'client.locus.join.response',
730
+ value: 40,
731
+ });
732
+ assert.deepEqual(cdl.getTotalJMTWithUserDelay(), undefined);
733
+ });
734
+
557
735
  it('calculates getTotalMediaJMT correctly', () => {
558
736
  cdl.saveTimestamp({
559
737
  key: 'internal.client.meeting.click.joinbutton',
@@ -627,6 +805,71 @@ describe('internal-plugin-metrics', () => {
627
805
  assert.deepEqual(cdl.getTotalMediaJMT(), 31);
628
806
  });
629
807
 
808
+ it('calculates getTotalMediaJMTWithUserDelay correctly', () => {
809
+ cdl.saveLatency('internal.click.to.interstitial.with.user.delay', 7);
810
+ cdl.saveTimestamp({
811
+ key: 'internal.client.interstitial-window.click.joinbutton',
812
+ value: 10,
813
+ });
814
+ cdl.saveTimestamp({
815
+ key: 'client.locus.join.request',
816
+ value: 12,
817
+ });
818
+ cdl.saveTimestamp({
819
+ key: 'client.locus.join.response',
820
+ value: 20,
821
+ });
822
+ cdl.saveTimestamp({
823
+ key: 'internal.host.meeting.participant.admitted',
824
+ value: 24,
825
+ });
826
+ cdl.saveTimestamp({
827
+ key: 'client.ice.start',
828
+ value: 30,
829
+ });
830
+ cdl.saveTimestamp({
831
+ key: 'client.ice.end',
832
+ value: 40,
833
+ });
834
+ assert.deepEqual(cdl.getTotalMediaJMTWithUserDelay(), 35);
835
+ });
836
+
837
+ it('calculates getTotalMediaJMTWithUserDelay correctly for guest join', () => {
838
+ cdl.saveTimestamp({
839
+ key: 'internal.client.meeting.click.joinbutton',
840
+ value: 5,
841
+ });
842
+ cdl.saveTimestamp({
843
+ key: 'internal.client.meeting.interstitial-window.showed',
844
+ value: 8,
845
+ });
846
+ cdl.saveTimestamp({
847
+ key: 'internal.client.interstitial-window.click.joinbutton',
848
+ value: 10,
849
+ });
850
+ cdl.saveTimestamp({
851
+ key: 'client.locus.join.request',
852
+ value: 12,
853
+ });
854
+ cdl.saveTimestamp({
855
+ key: 'client.locus.join.response',
856
+ value: 20,
857
+ });
858
+ cdl.saveTimestamp({
859
+ key: 'internal.host.meeting.participant.admitted',
860
+ value: 24,
861
+ });
862
+ cdl.saveTimestamp({
863
+ key: 'client.ice.start',
864
+ value: 30,
865
+ });
866
+ cdl.saveTimestamp({
867
+ key: 'client.ice.end',
868
+ value: 40,
869
+ });
870
+ assert.deepEqual(cdl.getTotalMediaJMTWithUserDelay(), 31);
871
+ });
872
+
630
873
  it('calculates getJoinConfJMT correctly', () => {
631
874
  cdl.saveTimestamp({
632
875
  key: 'client.locus.join.request',
@@ -751,6 +994,18 @@ describe('internal-plugin-metrics', () => {
751
994
  assert.deepEqual(cdl.getInterstitialToMediaOKJMT(), 10);
752
995
  });
753
996
 
997
+ it('calculates getShareDuration correctly', () => {
998
+ cdl.saveTimestamp({
999
+ key: 'internal.client.share.initiated',
1000
+ value: 5,
1001
+ });
1002
+ cdl.saveTimestamp({
1003
+ key: 'internal.client.share.stopped',
1004
+ value: 7,
1005
+ });
1006
+ assert.deepEqual(cdl.getShareDuration(), 2);
1007
+ });
1008
+
754
1009
  describe('calculates getU2CTime correctly', () => {
755
1010
  it('returns undefined when no precomputed value available', () => {
756
1011
  assert.deepEqual(cdl.getU2CTime(), undefined);