@webex/internal-plugin-metrics 3.7.0 → 3.8.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.
Files changed (41) hide show
  1. package/dist/business-metrics.js +74 -100
  2. package/dist/business-metrics.js.map +1 -1
  3. package/dist/call-diagnostic/call-diagnostic-metrics-latencies.js +24 -15
  4. package/dist/call-diagnostic/call-diagnostic-metrics-latencies.js.map +1 -1
  5. package/dist/call-diagnostic/call-diagnostic-metrics.js +76 -10
  6. package/dist/call-diagnostic/call-diagnostic-metrics.js.map +1 -1
  7. package/dist/call-diagnostic/config.js +19 -12
  8. package/dist/call-diagnostic/config.js.map +1 -1
  9. package/dist/generic-metrics.js +2 -2
  10. package/dist/generic-metrics.js.map +1 -1
  11. package/dist/index.js.map +1 -1
  12. package/dist/metrics.js +1 -1
  13. package/dist/metrics.types.js.map +1 -1
  14. package/dist/new-metrics.js +28 -5
  15. package/dist/new-metrics.js.map +1 -1
  16. package/dist/types/business-metrics.d.ts +10 -28
  17. package/dist/types/call-diagnostic/call-diagnostic-metrics.d.ts +14 -1
  18. package/dist/types/call-diagnostic/config.d.ts +2 -0
  19. package/dist/types/generic-metrics.d.ts +2 -2
  20. package/dist/types/index.d.ts +2 -2
  21. package/dist/types/metrics.types.d.ts +17 -1
  22. package/dist/types/new-metrics.d.ts +15 -3
  23. package/package.json +12 -12
  24. package/src/business-metrics.ts +66 -76
  25. package/src/call-diagnostic/call-diagnostic-metrics-latencies.ts +36 -14
  26. package/src/call-diagnostic/call-diagnostic-metrics.ts +80 -3
  27. package/src/call-diagnostic/config.ts +8 -0
  28. package/src/generic-metrics.ts +2 -2
  29. package/src/index.ts +2 -0
  30. package/src/metrics.types.ts +21 -1
  31. package/src/new-metrics.ts +32 -4
  32. package/test/unit/spec/business/business-metrics.ts +2 -2
  33. package/test/unit/spec/call-diagnostic/call-diagnostic-metrics-latencies.ts +85 -0
  34. package/test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts +674 -32
  35. package/test/unit/spec/new-metrics.ts +23 -0
  36. package/dist/behavioral/behavioral-metrics.js +0 -199
  37. package/dist/behavioral/behavioral-metrics.js.map +0 -1
  38. package/dist/behavioral/config.js +0 -11
  39. package/dist/behavioral/config.js.map +0 -1
  40. package/dist/types/behavioral/behavioral-metrics.d.ts +0 -63
  41. package/dist/types/behavioral/config.d.ts +0 -1
@@ -40,6 +40,7 @@ import {
40
40
  ClientEventPayloadError,
41
41
  ClientSubServiceType,
42
42
  BrowserLaunchMethodType,
43
+ DelayedClientEvent,
43
44
  } from '../metrics.types';
44
45
  import CallDiagnosticEventsBatcher from './call-diagnostic-metrics-batcher';
45
46
  import PreLoginMetricsBatcher from '../prelogin-metrics-batcher';
@@ -95,6 +96,7 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
95
96
  private logger: any; // to avoid adding @ts-ignore everywhere
96
97
  private hasLoggedBrowserSerial: boolean;
97
98
  private device: any;
99
+ private delayedClientEvents: DelayedClientEvent[] = [];
98
100
 
99
101
  // the default validator before piping an event to the batcher
100
102
  // this function can be overridden by the user
@@ -164,7 +166,15 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
164
166
  if (meetingInfo?.webexScheduled && !meetingInfo?.enableEvent && !meetingInfo?.pmr) {
165
167
  return WEBEX_SUB_SERVICE_TYPES.SCHEDULED_MEETING;
166
168
  }
167
- // if Scheduled, Webinar, not pmr - then Webinar
169
+
170
+ // if ConvergedArchitecture enable and isConvergedWebinarWebcast -- then webcast
171
+ if (meetingInfo?.enableConvergedArchitecture && meetingInfo?.enableEvent) {
172
+ return meetingInfo?.isConvergedWebinarWebcast
173
+ ? WEBEX_SUB_SERVICE_TYPES.WEBCAST
174
+ : WEBEX_SUB_SERVICE_TYPES.WEBINAR;
175
+ }
176
+
177
+ // if Scheduled, enable event, not pmr - then Webinar
168
178
  if (meetingInfo?.webexScheduled && meetingInfo?.enableEvent && !meetingInfo?.pmr) {
169
179
  return WEBEX_SUB_SERVICE_TYPES.WEBINAR;
170
180
  }
@@ -377,7 +387,7 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
377
387
  * @returns
378
388
  */
379
389
  prepareDiagnosticEvent(eventData: Event['event'], options: any) {
380
- const {meetingId} = options;
390
+ const {meetingId, triggeredTime} = options;
381
391
  const origin = this.getOrigin(options, meetingId);
382
392
 
383
393
  const event: Event = {
@@ -385,7 +395,7 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
385
395
  version: 1,
386
396
  origin,
387
397
  originTime: {
388
- triggered: new Date().toISOString(),
398
+ triggered: triggeredTime || new Date().toISOString(),
389
399
  // is overridden in prepareRequest batcher
390
400
  sent: 'not_defined_yet',
391
401
  },
@@ -715,13 +725,25 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
715
725
  isConvergedArchitectureEnabled: this.getIsConvergedArchitectureEnabled({
716
726
  meetingId,
717
727
  }),
728
+ ...(meeting.userNameInput && {userNameInput: meeting.userNameInput}),
729
+ ...(meeting.emailInput && {emailInput: meeting.emailInput}),
718
730
  webexSubServiceType: this.getSubServiceType(meeting),
731
+ // @ts-ignore
732
+ webClientPreload: this.webex.meetings?.config?.metrics?.webClientPreload,
719
733
  };
720
734
 
721
735
  const joinFlowVersion = options.joinFlowVersion ?? meeting.callStateForMetrics?.joinFlowVersion;
722
736
  if (joinFlowVersion) {
723
737
  clientEventObject.joinFlowVersion = joinFlowVersion;
724
738
  }
739
+ const meetingJoinedTime = meeting.isoLocalClientMeetingJoinTime;
740
+ if (meetingJoinedTime) {
741
+ clientEventObject.meetingJoinedTime = meetingJoinedTime;
742
+ }
743
+
744
+ if (options.meetingJoinPhase) {
745
+ clientEventObject.meetingJoinPhase = options.meetingJoinPhase;
746
+ }
725
747
 
726
748
  return clientEventObject;
727
749
  }
@@ -764,12 +786,26 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
764
786
  webClientDomain: window.location.hostname,
765
787
  },
766
788
  loginType: this.getCurLoginType(),
789
+ // @ts-ignore
790
+ webClientPreload: this.webex.meetings?.config?.metrics?.webClientPreload,
767
791
  };
768
792
 
769
793
  if (options.joinFlowVersion) {
770
794
  clientEventObject.joinFlowVersion = options.joinFlowVersion;
771
795
  }
772
796
 
797
+ if (options.meetingJoinPhase) {
798
+ clientEventObject.meetingJoinPhase = options.meetingJoinPhase;
799
+ }
800
+
801
+ if (options.userNameInput) {
802
+ clientEventObject.userNameInput = options.userNameInput;
803
+ }
804
+
805
+ if (options.emailInput) {
806
+ clientEventObject.emailInput = options.emailInput;
807
+ }
808
+
773
809
  return clientEventObject;
774
810
  }
775
811
 
@@ -834,17 +870,36 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
834
870
  * @param arg.event - event key
835
871
  * @param arg.payload - additional payload to be merged with default payload
836
872
  * @param arg.options - payload
873
+ * @param arg.delaySubmitEvent - a boolean value indicating whether to delay the submission of client events.
837
874
  * @throws
838
875
  */
839
876
  public submitClientEvent({
840
877
  name,
841
878
  payload,
842
879
  options,
880
+ delaySubmitEvent,
843
881
  }: {
844
882
  name: ClientEvent['name'];
845
883
  payload?: ClientEventPayload;
846
884
  options?: SubmitClientEventOptions;
885
+ delaySubmitEvent?: boolean;
847
886
  }) {
887
+ if (delaySubmitEvent) {
888
+ // Preserve the time when the event was triggered if delaying the submission to Call Diagnostics
889
+ const delayedOptions = {
890
+ ...options,
891
+ triggeredTime: new Date().toISOString(),
892
+ };
893
+
894
+ this.delayedClientEvents.push({
895
+ name,
896
+ payload,
897
+ options: delayedOptions,
898
+ });
899
+
900
+ return Promise.resolve();
901
+ }
902
+
848
903
  this.logger.log(
849
904
  CALL_DIAGNOSTIC_LOG_IDENTIFIER,
850
905
  'CallDiagnosticMetrics: @submitClientEvent. Submit Client Event CA event.',
@@ -861,6 +916,28 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
861
916
  return this.submitToCallDiagnostics(diagnosticEvent);
862
917
  }
863
918
 
919
+ /**
920
+ * Submit Delayed Client Event CA events. Clears delayedClientEvents array after submission.
921
+ */
922
+ public submitDelayedClientEvents() {
923
+ this.logger.log(
924
+ CALL_DIAGNOSTIC_LOG_IDENTIFIER,
925
+ 'CallDiagnosticMetrics: @submitDelayedClientEvents. Submitting delayed client events.'
926
+ );
927
+
928
+ if (this.delayedClientEvents.length === 0) {
929
+ return Promise.resolve();
930
+ }
931
+
932
+ const promises = this.delayedClientEvents.map((delayedSubmitClientEventParams) => {
933
+ return this.submitClientEvent(delayedSubmitClientEventParams);
934
+ });
935
+
936
+ this.delayedClientEvents = [];
937
+
938
+ return Promise.all(promises);
939
+ }
940
+
864
941
  /**
865
942
  * Prepare the event and send the request to metrics-a service.
866
943
  * @param event
@@ -17,12 +17,14 @@ 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
19
  export const ICE_AND_REACHABILITY_FAILED_CLIENT_CODE = 2011;
20
+ export const MULTISTREAM_NOT_AVAILABLE_CLIENT_CODE = 2012;
20
21
  export const WBX_APP_API_URL = 'wbxappapi'; // MeetingInfo WebexAppApi response object normally contains a body.url that includes the string 'wbxappapi'
21
22
 
22
23
  export const WEBEX_SUB_SERVICE_TYPES: Record<string, ClientSubServiceType> = {
23
24
  PMR: 'PMR',
24
25
  SCHEDULED_MEETING: 'ScheduledMeeting',
25
26
  WEBINAR: 'Webinar',
27
+ WEBCAST: 'Webcast',
26
28
  };
27
29
 
28
30
  // Found in https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia
@@ -127,6 +129,7 @@ export const ERROR_DESCRIPTIONS = {
127
129
  ICE_FAILED_WITHOUT_TURN_TLS: 'ICEFailedWithoutTURN_TLS',
128
130
  ICE_FAILED_WITH_TURN_TLS: 'ICEFailedWithTURN_TLS',
129
131
  ICE_AND_REACHABILITY_FAILED: 'ICEAndReachabilityFailed',
132
+ MULTISTREAM_NOT_AVAILABLE: 'MultistreamNotAvailable',
130
133
  SDP_OFFER_CREATION_ERROR: 'SdpOfferCreationError',
131
134
  SDP_OFFER_CREATION_ERROR_MISSING_CODEC: 'SdpOfferCreationErrorMissingCodec',
132
135
  WDM_RESTRICTED_REGION: 'WdmRestrictedRegion',
@@ -409,6 +412,11 @@ export const CLIENT_ERROR_CODE_TO_ERROR_PAYLOAD: Record<number, Partial<ClientEv
409
412
  category: 'expected',
410
413
  fatal: true,
411
414
  },
415
+ [MULTISTREAM_NOT_AVAILABLE_CLIENT_CODE]: {
416
+ errorDescription: ERROR_DESCRIPTIONS.MULTISTREAM_NOT_AVAILABLE,
417
+ category: 'expected',
418
+ fatal: false,
419
+ },
412
420
  2050: {
413
421
  errorDescription: ERROR_DESCRIPTIONS.SDP_OFFER_CREATION_ERROR,
414
422
  category: 'media',
@@ -37,7 +37,7 @@ export default abstract class GenericMetrics extends StatelessWebexPlugin {
37
37
  }
38
38
 
39
39
  /**
40
- * Submit a buisness metric to our metrics endpoint.
40
+ * Submit a business metric to our metrics endpoint.
41
41
  * @param {string} kind of metric for logging
42
42
  * @param {string} name of the metric
43
43
  * @param {object} event
@@ -105,7 +105,7 @@ export default abstract class GenericMetrics extends StatelessWebexPlugin {
105
105
  }
106
106
 
107
107
  /**
108
- * Returns true once we have the deviceId we need to submit behavioral/operational/buisness events
108
+ * Returns true once we have the deviceId we need to submit behavioral/operational/business events
109
109
  * @returns {boolean}
110
110
  */
111
111
  public isReadyToSubmitEvents(): boolean {
package/src/index.ts CHANGED
@@ -15,6 +15,7 @@ import {
15
15
  SubmitClientEvent,
16
16
  SubmitInternalEvent,
17
17
  SubmitOperationalEvent,
18
+ SubmitBusinessEvent,
18
19
  SubmitMQE,
19
20
  PreComputedLatencies,
20
21
  } from './metrics.types';
@@ -58,5 +59,6 @@ export type {
58
59
  SubmitInternalEvent,
59
60
  SubmitMQE,
60
61
  SubmitOperationalEvent,
62
+ SubmitBusinessEvent,
61
63
  PreComputedLatencies,
62
64
  };
@@ -16,11 +16,15 @@ export type ClientLaunchMethodType = NonNullable<
16
16
  RawEvent['origin']['clientInfo']
17
17
  >['clientLaunchMethod'];
18
18
 
19
+ export type ClientUserNameInput = NonNullable<RawClientEvent['userNameInput']>;
20
+
21
+ export type ClientEmailInput = NonNullable<RawClientEvent['emailInput']>;
22
+
19
23
  export type BrowserLaunchMethodType = NonNullable<
20
24
  RawEvent['origin']['clientInfo']
21
25
  >['browserLaunchMethod'];
22
26
 
23
- export type MetricEventProduct = 'webex' | 'wxcc_desktop';
27
+ export type MetricEventProduct = 'webex' | 'wxcc_desktop' | 'wxcc_crm' | 'wxcc_sdk';
24
28
 
25
29
  export type MetricEventAgent = 'user' | 'browser' | 'system' | 'sdk' | 'redux' | 'service' | 'api';
26
30
 
@@ -111,6 +115,7 @@ export type MetricEventVerb =
111
115
  | 'exit';
112
116
 
113
117
  export type MetricEventJoinFlowVersion = 'Other' | 'NewFTE';
118
+ export type MetricEventMeetingJoinPhase = 'pre-join' | 'join' | 'in-meeting';
114
119
 
115
120
  export type SubmitClientEventOptions = {
116
121
  meetingId?: string;
@@ -126,6 +131,10 @@ export type SubmitClientEventOptions = {
126
131
  webexConferenceIdStr?: string;
127
132
  globalMeetingId?: string;
128
133
  joinFlowVersion?: MetricEventJoinFlowVersion;
134
+ meetingJoinPhase?: MetricEventMeetingJoinPhase;
135
+ triggeredTime?: string;
136
+ emailInput?: ClientEmailInput;
137
+ userNameInput?: ClientUserNameInput;
129
138
  };
130
139
 
131
140
  export type SubmitMQEOptions = {
@@ -278,6 +287,11 @@ export type SubmitOperationalEvent = (args: {
278
287
  payload: EventPayload;
279
288
  }) => void;
280
289
 
290
+ export type SubmitBusinessEvent = (args: {
291
+ name: OperationalEvent['metricName'];
292
+ payload: EventPayload;
293
+ }) => void;
294
+
281
295
  export type SubmitMQE = (args: {
282
296
  name: MediaQualityEvent['name'];
283
297
  payload: SubmitMQEPayload;
@@ -317,3 +331,9 @@ export interface IMetricsAttributes {
317
331
  meetingId?: string;
318
332
  callId?: string;
319
333
  }
334
+
335
+ export interface DelayedClientEvent {
336
+ name: ClientEvent['name'];
337
+ payload?: RecursivePartial<ClientEvent['payload']>;
338
+ options?: SubmitClientEventOptions;
339
+ }
@@ -45,6 +45,11 @@ class Metrics extends WebexPlugin {
45
45
  businessMetrics: BusinessMetrics;
46
46
  isReady = false;
47
47
 
48
+ /**
49
+ * Whether or not to delay the submission of client events.
50
+ */
51
+ delaySubmitClientEvents = false;
52
+
48
53
  /**
49
54
  * Constructor
50
55
  * @param args
@@ -141,7 +146,7 @@ class Metrics extends WebexPlugin {
141
146
  }
142
147
 
143
148
  /**
144
- * @returns true once we have the deviceId we need to submit buisness events
149
+ * @returns true once we have the deviceId we need to submit business events
145
150
  */
146
151
  isReadyToSubmitBusinessEvents() {
147
152
  this.lazyBuildBusinessMetrics();
@@ -200,17 +205,19 @@ class Metrics extends WebexPlugin {
200
205
  }
201
206
 
202
207
  /**
203
- * Buisness event
208
+ * Business event
204
209
  * @param args
205
210
  */
206
211
  submitBusinessEvent({
207
212
  name,
208
213
  payload,
209
214
  table,
215
+ metadata,
210
216
  }: {
211
217
  name: string;
212
218
  payload: EventPayload;
213
219
  table?: Table;
220
+ metadata?: EventPayload;
214
221
  }) {
215
222
  if (!this.isReady) {
216
223
  // @ts-ignore
@@ -223,7 +230,7 @@ class Metrics extends WebexPlugin {
223
230
 
224
231
  this.lazyBuildBusinessMetrics();
225
232
 
226
- return this.businessMetrics.submitBusinessEvent({name, payload, table});
233
+ return this.businessMetrics.submitBusinessEvent({name, payload, table, metadata});
227
234
  }
228
235
 
229
236
  /**
@@ -288,7 +295,12 @@ class Metrics extends WebexPlugin {
288
295
  options: {meetingId: options?.meetingId},
289
296
  });
290
297
 
291
- return this.callDiagnosticMetrics.submitClientEvent({name, payload, options});
298
+ return this.callDiagnosticMetrics.submitClientEvent({
299
+ name,
300
+ payload,
301
+ options,
302
+ delaySubmitEvent: this.delaySubmitClientEvents,
303
+ });
292
304
  }
293
305
 
294
306
  /**
@@ -387,6 +399,22 @@ class Metrics extends WebexPlugin {
387
399
  public isServiceErrorExpected(serviceErrorCode: number): boolean {
388
400
  return this.callDiagnosticMetrics.isServiceErrorExpected(serviceErrorCode);
389
401
  }
402
+
403
+ /**
404
+ * Sets the value of delaySubmitClientEvents. If set to true, client events will be delayed until submitDelayedClientEvents is called. If
405
+ * set to false, delayed client events will be submitted.
406
+ *
407
+ * @param {boolean} shouldDelay - A boolean value indicating whether to delay the submission of client events.
408
+ */
409
+ public setDelaySubmitClientEvents(shouldDelay: boolean) {
410
+ this.delaySubmitClientEvents = shouldDelay;
411
+
412
+ if (!shouldDelay) {
413
+ return this.callDiagnosticMetrics.submitDelayedClientEvents();
414
+ }
415
+
416
+ return Promise.resolve();
417
+ }
390
418
  }
391
419
 
392
420
  export default Metrics;
@@ -154,11 +154,12 @@ describe('internal-plugin-metrics', () => {
154
154
  businessMetrics.clientMetricsBatcher.request = request;
155
155
 
156
156
  assert.equal(requestCalls.length, 0)
157
- businessMetrics.submitBusinessEvent({ name: "foobar", payload: {bar:"gee"}, table: 'business_metrics' })
157
+ businessMetrics.submitBusinessEvent({ name: "foobar", payload: {bar: "gee"}, table: 'business_metrics', metadata: {asdf: 'hjkl'} })
158
158
  assert.equal(requestCalls.length, 1)
159
159
  assert.deepEqual(requestCalls[0], {
160
160
  eventPayload: {
161
161
  key: 'foobar',
162
+ asdf: 'hjkl',
162
163
  appType: 'Web Client',
163
164
  client_timestamp: requestCalls[0].eventPayload.client_timestamp, // This is to bypass time check, which is checked below.
164
165
  value: {
@@ -173,7 +174,6 @@ describe('internal-plugin-metrics', () => {
173
174
  os: getOSNameInternal(),
174
175
  app: {version: 'webex-version'},
175
176
  device: {id: 'deviceId'},
176
- locale: 'language',
177
177
  }
178
178
  },
179
179
  type: ['business'],
@@ -283,6 +283,18 @@ describe('internal-plugin-metrics', () => {
283
283
  cdl.saveTimestamp({key: 'internal.client.meetinginfo.response', value: 20});
284
284
  assert.deepEqual(saveFirstTimestamp.callCount, 1);
285
285
  });
286
+
287
+ it('calls saveFirstTimestamp for remote SDP received', () => {
288
+ const saveFirstTimestamp = sinon.stub(cdl, 'saveFirstTimestampOnly');
289
+ cdl.saveTimestamp({key: 'client.media-engine.remote-sdp-received', value: 10});
290
+ assert.deepEqual(saveFirstTimestamp.callCount, 1);
291
+ });
292
+
293
+ it('clears timestamp for remote SDP received when local SDP generated', () => {
294
+ cdl.saveTimestamp({key: 'client.media-engine.remote-sdp-received', value: 10});
295
+ cdl.saveTimestamp({key: 'client.media-engine.local-sdp-generated', value: 20});
296
+ assert.isUndefined(cdl.latencyTimestamps.get('client.media-engine.remote-sdp-received'));
297
+ });
286
298
  });
287
299
 
288
300
  it('calculates getShowInterstitialTime correctly', () => {
@@ -425,6 +437,15 @@ describe('internal-plugin-metrics', () => {
425
437
  assert.deepEqual(cdl.getClickToInterstitial(), 5);
426
438
  });
427
439
 
440
+ it('calculates getClickToInterstitial without join button timestamp when it is 0', () => {
441
+ cdl.saveLatency('internal.click.to.interstitial', 0);
442
+ cdl.saveTimestamp({
443
+ key: 'internal.client.meeting.interstitial-window.showed',
444
+ value: 20,
445
+ });
446
+ assert.deepEqual(cdl.getClickToInterstitial(), 0);
447
+ });
448
+
428
449
  it('calculates getInterstitialToJoinOK correctly', () => {
429
450
  cdl.saveTimestamp({
430
451
  key: 'internal.client.interstitial-window.click.joinbutton',
@@ -437,6 +458,18 @@ describe('internal-plugin-metrics', () => {
437
458
  assert.deepEqual(cdl.getInterstitialToJoinOK(), 10);
438
459
  });
439
460
 
461
+ it('calculates getInterstitialToJoinOK correctly when one value is not a number', () => {
462
+ cdl.saveTimestamp({
463
+ key: 'internal.client.interstitial-window.click.joinbutton',
464
+ value: 'ten' as unknown as number,
465
+ });
466
+ cdl.saveTimestamp({
467
+ key: 'client.locus.join.response',
468
+ value: 20,
469
+ });
470
+ assert.deepEqual(cdl.getInterstitialToJoinOK(), undefined);
471
+ });
472
+
440
473
  it('calculates getCallInitMediaEngineReady correctly', () => {
441
474
  cdl.saveTimestamp({
442
475
  key: 'internal.client.interstitial-window.click.joinbutton',
@@ -469,6 +502,58 @@ describe('internal-plugin-metrics', () => {
469
502
  assert.deepEqual(cdl.getTotalJMT(), 45);
470
503
  });
471
504
 
505
+ it('calculates getTotalJMT correctly when clickToInterstitial is 0', () => {
506
+ cdl.saveLatency('internal.click.to.interstitial', 0);
507
+ cdl.saveTimestamp({
508
+ key: 'internal.client.interstitial-window.click.joinbutton',
509
+ value: 20,
510
+ });
511
+ cdl.saveTimestamp({
512
+ key: 'client.locus.join.response',
513
+ value: 40,
514
+ });
515
+ assert.deepEqual(cdl.getTotalJMT(), 20);
516
+ });
517
+
518
+ it('calculates getTotalJMT correctly when interstitialToJoinOk is 0', () => {
519
+ cdl.saveTimestamp({
520
+ key: 'internal.client.interstitial-window.click.joinbutton',
521
+ value: 40,
522
+ });
523
+ cdl.saveLatency('internal.click.to.interstitial', 12);
524
+ cdl.saveTimestamp({
525
+ key: 'client.locus.join.response',
526
+ value: 40,
527
+ });
528
+ assert.deepEqual(cdl.getTotalJMT(), 12);
529
+ });
530
+
531
+ it('calculates getTotalJMT correctly when both clickToInterstitial and interstitialToJoinOk are 0', () => {
532
+ cdl.saveTimestamp({
533
+ key: 'internal.client.interstitial-window.click.joinbutton',
534
+ value: 40,
535
+ });
536
+ cdl.saveLatency('internal.click.to.interstitial', 0);
537
+ cdl.saveTimestamp({
538
+ key: 'client.locus.join.response',
539
+ value: 40,
540
+ });
541
+ assert.deepEqual(cdl.getTotalJMT(), 0);
542
+ });
543
+
544
+ it('calculates getTotalJMT correctly when both clickToInterstitial is not a number', () => {
545
+ cdl.saveTimestamp({
546
+ key: 'internal.client.interstitial-window.click.joinbutton',
547
+ value: 40,
548
+ });
549
+ cdl.saveLatency('internal.click.to.interstitial', 'eleven' as unknown as number);
550
+ cdl.saveTimestamp({
551
+ key: 'client.locus.join.response',
552
+ value: 40,
553
+ });
554
+ assert.deepEqual(cdl.getTotalJMT(), undefined);
555
+ });
556
+
472
557
  it('calculates getTotalMediaJMT correctly', () => {
473
558
  cdl.saveTimestamp({
474
559
  key: 'internal.client.meeting.click.joinbutton',