@webex/internal-plugin-metrics 3.0.0-beta.42 → 3.0.0-beta.421

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 (78) hide show
  1. package/dist/batcher.js +40 -1
  2. package/dist/batcher.js.map +1 -1
  3. package/dist/call-diagnostic/call-diagnostic-metrics-batcher.js +65 -0
  4. package/dist/call-diagnostic/call-diagnostic-metrics-batcher.js.map +1 -0
  5. package/dist/call-diagnostic/call-diagnostic-metrics-latencies.js +552 -0
  6. package/dist/call-diagnostic/call-diagnostic-metrics-latencies.js.map +1 -0
  7. package/dist/call-diagnostic/call-diagnostic-metrics.js +863 -0
  8. package/dist/call-diagnostic/call-diagnostic-metrics.js.map +1 -0
  9. package/dist/call-diagnostic/call-diagnostic-metrics.util.js +371 -0
  10. package/dist/call-diagnostic/call-diagnostic-metrics.util.js.map +1 -0
  11. package/dist/call-diagnostic/config.js +627 -0
  12. package/dist/call-diagnostic/config.js.map +1 -0
  13. package/dist/client-metrics-batcher.js +2 -1
  14. package/dist/client-metrics-batcher.js.map +1 -1
  15. package/dist/client-metrics-prelogin-batcher.js +33 -0
  16. package/dist/client-metrics-prelogin-batcher.js.map +1 -0
  17. package/dist/config.js +2 -1
  18. package/dist/config.js.map +1 -1
  19. package/dist/index.js +33 -0
  20. package/dist/index.js.map +1 -1
  21. package/dist/metrics.js +30 -50
  22. package/dist/metrics.js.map +1 -1
  23. package/dist/metrics.types.js +7 -0
  24. package/dist/metrics.types.js.map +1 -0
  25. package/dist/new-metrics.js +300 -0
  26. package/dist/new-metrics.js.map +1 -0
  27. package/dist/prelogin-metrics-batcher.js +82 -0
  28. package/dist/prelogin-metrics-batcher.js.map +1 -0
  29. package/dist/types/batcher.d.ts +7 -0
  30. package/dist/types/call-diagnostic/call-diagnostic-metrics-batcher.d.ts +2 -0
  31. package/dist/types/call-diagnostic/call-diagnostic-metrics-latencies.d.ts +237 -0
  32. package/dist/types/call-diagnostic/call-diagnostic-metrics.d.ts +425 -0
  33. package/dist/types/call-diagnostic/call-diagnostic-metrics.util.d.ts +103 -0
  34. package/dist/types/call-diagnostic/config.d.ts +178 -0
  35. package/dist/types/client-metrics-batcher.d.ts +2 -0
  36. package/dist/types/client-metrics-prelogin-batcher.d.ts +2 -0
  37. package/dist/types/config.d.ts +36 -0
  38. package/dist/types/index.d.ts +15 -0
  39. package/dist/types/metrics.d.ts +3 -0
  40. package/dist/types/metrics.types.d.ts +107 -0
  41. package/dist/types/new-metrics.d.ts +131 -0
  42. package/dist/types/prelogin-metrics-batcher.d.ts +2 -0
  43. package/dist/types/utils.d.ts +6 -0
  44. package/dist/utils.js +27 -0
  45. package/dist/utils.js.map +1 -0
  46. package/package.json +15 -8
  47. package/src/batcher.js +38 -0
  48. package/src/call-diagnostic/call-diagnostic-metrics-batcher.ts +72 -0
  49. package/src/call-diagnostic/call-diagnostic-metrics-latencies.ts +511 -0
  50. package/src/call-diagnostic/call-diagnostic-metrics.ts +925 -0
  51. package/src/call-diagnostic/call-diagnostic-metrics.util.ts +399 -0
  52. package/src/call-diagnostic/config.ts +685 -0
  53. package/src/client-metrics-batcher.js +1 -0
  54. package/src/client-metrics-prelogin-batcher.ts +26 -0
  55. package/src/config.js +1 -0
  56. package/src/index.ts +56 -0
  57. package/src/metrics.js +26 -47
  58. package/src/metrics.types.ts +179 -0
  59. package/src/new-metrics.ts +278 -0
  60. package/src/prelogin-metrics-batcher.ts +95 -0
  61. package/src/utils.ts +17 -0
  62. package/test/unit/spec/batcher.js +2 -0
  63. package/test/unit/spec/call-diagnostic/call-diagnostic-metrics-batcher.ts +469 -0
  64. package/test/unit/spec/call-diagnostic/call-diagnostic-metrics-latencies.ts +718 -0
  65. package/test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts +2371 -0
  66. package/test/unit/spec/call-diagnostic/call-diagnostic-metrics.util.ts +637 -0
  67. package/test/unit/spec/client-metrics-batcher.js +2 -0
  68. package/test/unit/spec/client-metrics-prelogin-batcher.ts +54 -0
  69. package/test/unit/spec/metrics.js +78 -129
  70. package/test/unit/spec/new-metrics.ts +231 -0
  71. package/test/unit/spec/prelogin-metrics-batcher.ts +253 -0
  72. package/test/unit/spec/utils.ts +22 -0
  73. package/tsconfig.json +6 -0
  74. package/dist/call-diagnostic-events-batcher.js +0 -60
  75. package/dist/call-diagnostic-events-batcher.js.map +0 -1
  76. package/src/call-diagnostic-events-batcher.js +0 -62
  77. package/src/index.js +0 -17
  78. package/test/unit/spec/call-diagnostic-events-batcher.js +0 -195
@@ -8,7 +8,12 @@ import {Token, Credentials} from '@webex/webex-core';
8
8
  import FakeTimers from '@sinonjs/fake-timers';
9
9
  import sinon from 'sinon';
10
10
  import Metrics, {config} from '@webex/internal-plugin-metrics';
11
+ import {BrowserDetection} from '@webex/common';
11
12
 
13
+ const {getOSVersion} = BrowserDetection();
14
+
15
+ //@ts-ignore
16
+ global.window = {location: {hostname: 'whatever'}};
12
17
  function promiseTick(count) {
13
18
  let promise = Promise.resolve();
14
19
 
@@ -82,6 +87,7 @@ describe('plugin-metrics', () => {
82
87
  return Promise.resolve({
83
88
  statusCode: 204,
84
89
  body: undefined,
90
+ waitForServiceTimeout: 30,
85
91
  options,
86
92
  });
87
93
  };
@@ -89,7 +95,7 @@ describe('plugin-metrics', () => {
89
95
  webex.credentials = new Credentials(undefined, {parent: webex});
90
96
  sinon.stub(webex.credentials, 'getClientToken').returns(Promise.resolve('token'));
91
97
 
92
- webex.internal = {...webex.internal, device: {userId: 'userId'}};
98
+ webex.internal = {...webex.internal};
93
99
  webex.config = {
94
100
  ...webex.config,
95
101
  appName: 'appName',
@@ -98,11 +104,16 @@ describe('plugin-metrics', () => {
98
104
  };
99
105
  webex.config.metrics.type = ['operational'];
100
106
  webex.config.metrics.appType = 'sdk';
107
+ webex.meetings = {
108
+ config: {
109
+ metrics: {
110
+ clientVersion: '43.0.105'
111
+ }
112
+ }
113
+ }
101
114
 
102
115
  sinon.spy(webex, 'request');
103
- sinon.spy(metrics, 'postPreLoginMetric');
104
116
  sinon.spy(metrics, 'aliasUser');
105
- sinon.spy(metrics, 'submitCallDiagnosticEvents');
106
117
  });
107
118
 
108
119
  describe('#submit()', () => {
@@ -131,9 +142,71 @@ describe('plugin-metrics', () => {
131
142
  });
132
143
  });
133
144
 
145
+ describe('#getClientMetricsPayload()', () => {
146
+ it('returns the expected payload', () => {
147
+ webex.credentials.supertoken = new Token(
148
+ {
149
+ access_token: 'a_b_orgid',
150
+ },
151
+ {parent: webex}
152
+ );
153
+
154
+ const testPayload = {
155
+ tags: {success: true},
156
+ fields: {perceivedDurationInMillis: 314},
157
+ context: {},
158
+ eventPayload: {value: 'splunk business metric payload'},
159
+ };
160
+ const date = clock.now;
161
+
162
+ const result = metrics.getClientMetricsPayload('test', testPayload);
163
+
164
+ assert.deepEqual(result, {
165
+ context: {
166
+ app: {
167
+ version: undefined,
168
+ },
169
+ locale: 'en-US',
170
+ os: {
171
+ name: 'other',
172
+ version: getOSVersion(),
173
+ },
174
+ },
175
+ eventPayload: {
176
+ value: 'splunk business metric payload',
177
+ },
178
+ fields: {
179
+ browser_version: '',
180
+ client_id: 'fake',
181
+ os_version: getOSVersion(),
182
+ perceivedDurationInMillis: 314,
183
+ platform: 'Web',
184
+ sdk_version: undefined,
185
+ spark_user_agent: 'webex-js-sdk appName/appVersion appPlatform',
186
+ },
187
+ metricName: 'test',
188
+ tags: {
189
+ appVersion: '43.0.105',
190
+ browser: '',
191
+ domain: 'whatever',
192
+ os: 'other',
193
+ success: true,
194
+ },
195
+ timestamp: 0,
196
+ type: ['operational'],
197
+ });
198
+ });
199
+
200
+ it('throws when no event name is specified', () => {
201
+ assert.throws(() => {
202
+ metrics.getClientMetricsPayload();
203
+ }, 'Missing behavioral metric name. Please provide one');
204
+ });
205
+ });
206
+
134
207
  describe('#submitClientMetrics()', () => {
135
208
  describe('before login', () => {
136
- it('posts pre-login metric', () => {
209
+ it('clientMetricsPreloginBatcher pre-login metric', () => {
137
210
  const date = clock.now;
138
211
  const promise = metrics.submitClientMetrics(eventName, mockPayload, preLoginId);
139
212
 
@@ -141,8 +214,6 @@ describe('plugin-metrics', () => {
141
214
  .then(() => clock.tick(config.metrics.batcherWait))
142
215
  .then(() => promise)
143
216
  .then(() => {
144
- assert.called(metrics.postPreLoginMetric);
145
- assert.calledOnce(webex.credentials.getClientToken);
146
217
  assert.calledOnce(webex.request);
147
218
  const req = webex.request.args[0][0];
148
219
  const metric = req.body.metrics[0];
@@ -199,17 +270,15 @@ describe('plugin-metrics', () => {
199
270
  assert.property(metric, 'eventPayload');
200
271
 
201
272
  assert.property(metric.tags, 'browser');
202
- assert.property(metric.tags, 'org_id');
203
273
  assert.property(metric.tags, 'os');
204
274
  assert.property(metric.tags, 'domain');
205
- assert.property(metric.tags, 'client_id');
206
- assert.property(metric.tags, 'user_id');
207
275
 
208
276
  assert.property(metric.fields, 'browser_version');
209
277
  assert.property(metric.fields, 'os_version');
210
278
  assert.property(metric.fields, 'sdk_version');
211
279
  assert.property(metric.fields, 'platform');
212
280
  assert.property(metric.fields, 'spark_user_agent');
281
+ assert.property(metric.fields, 'client_id');
213
282
 
214
283
  assert.property(metric.context, 'app');
215
284
  assert.property(metric.context, 'locale');
@@ -232,33 +301,6 @@ describe('plugin-metrics', () => {
232
301
  });
233
302
  });
234
303
 
235
- describe('#postPreLoginMetric()', () => {
236
- it('returns an HttpResponse object', () => {
237
- const promise = metrics.postPreLoginMetric(preLoginProps, preLoginId);
238
-
239
- return promiseTick(50)
240
- .then(() => clock.tick(config.metrics.batcherWait))
241
- .then(() => promise)
242
- .then(() => {
243
- assert.calledOnce(webex.request);
244
- const req = webex.request.args[0][0];
245
- const metric = req.body.metrics[0];
246
- const {headers} = req;
247
-
248
- assert.property(headers, 'x-prelogin-userid');
249
- assert.property(metric, 'metricName');
250
- assert.property(metric, 'tags');
251
- assert.property(metric, 'fields');
252
- assert.property(metric, 'timestamp');
253
-
254
- assert.equal(metric.timestamp, transformedProps.timestamp);
255
- assert.equal(metric.metricName, eventName);
256
- assert.equal(metric.tags.testTag, 'tag value');
257
- assert.equal(metric.fields.testField, 123);
258
- });
259
- });
260
- });
261
-
262
304
  describe('#aliasUser()', () => {
263
305
  it('returns an HttpResponse object', () =>
264
306
  metrics.aliasUser(preLoginId).then(() => {
@@ -269,98 +311,5 @@ describe('plugin-metrics', () => {
269
311
  assert.match(params, {alias: true});
270
312
  }));
271
313
  });
272
-
273
- describe('#submitCallDiagnosticEvents()', () => {
274
- it('submits a call diagnostic event', () => {
275
- const promise = metrics.submitCallDiagnosticEvents(mockCallDiagnosticEvent);
276
-
277
- return promiseTick(50)
278
- .then(() => clock.tick(config.metrics.batcherWait))
279
- .then(() => promise)
280
- .then(() => {
281
- assert.calledOnce(webex.request);
282
- const req = webex.request.args[0][0];
283
- const metric = req.body.metrics[0];
284
-
285
- assert.property(metric.eventPayload, 'origin');
286
- assert.property(metric.eventPayload, 'originTime');
287
- assert.property(metric.eventPayload.origin, 'buildType');
288
- assert.property(metric.eventPayload.origin, 'networkType');
289
- assert.property(metric.eventPayload.originTime, 'sent');
290
- assert.equal(metric.eventPayload.origin.buildType, 'test');
291
- });
292
- });
293
-
294
- it('submits a call diagnostic event with buildType set in the payload', () => {
295
- const promise = metrics.submitCallDiagnosticEvents({
296
- ...mockCallDiagnosticEvent,
297
- origin: {
298
- buildType: 'prod',
299
- },
300
- });
301
-
302
- return promiseTick(50)
303
- .then(() => clock.tick(config.metrics.batcherWait))
304
- .then(() => promise)
305
- .then(() => {
306
- assert.calledOnce(webex.request);
307
- const req = webex.request.args[0][0];
308
- const metric = req.body.metrics[0];
309
-
310
- assert.property(metric.eventPayload, 'origin');
311
- assert.property(metric.eventPayload, 'originTime');
312
- assert.property(metric.eventPayload.origin, 'buildType');
313
- assert.property(metric.eventPayload.origin, 'networkType');
314
- assert.property(metric.eventPayload.originTime, 'sent');
315
- assert.equal(metric.eventPayload.origin.buildType, 'prod');
316
- });
317
- });
318
-
319
- xit('submits a call diagnostic event with a test domain', () => {
320
- global.window.location.hostname = 'test.webex.com';
321
-
322
- const promise = metrics.submitCallDiagnosticEvents(mockCallDiagnosticEvent);
323
-
324
- return promiseTick(50)
325
- .then(() => clock.tick(config.metrics.batcherWait))
326
- .then(() => promise)
327
- .then(() => {
328
- assert.calledOnce(webex.request);
329
- const req = webex.request.args[0][0];
330
- const metric = req.body.metrics[0];
331
-
332
- assert.property(metric.eventPayload, 'origin');
333
- assert.property(metric.eventPayload, 'originTime');
334
- assert.property(metric.eventPayload.origin, 'buildType');
335
- assert.property(metric.eventPayload.origin, 'networkType');
336
- assert.property(metric.eventPayload.originTime, 'sent');
337
- assert.equal(metric.eventPayload.origin.buildType, 'test');
338
- });
339
- });
340
-
341
- // Skip because it's current unable to overwrite NODE_ENV
342
- // However doing `NODE_ENV=test npm run test ...` will get this test case to pass
343
- xit('submits a call diagnostic event with a NODE_ENV=production', () => {
344
- process.env.NODE_ENV = 'production';
345
-
346
- const promise = metrics.submitCallDiagnosticEvents(mockCallDiagnosticEvent);
347
-
348
- return promiseTick(50)
349
- .then(() => clock.tick(config.metrics.batcherWait))
350
- .then(() => promise)
351
- .then(() => {
352
- assert.calledOnce(webex.request);
353
- const req = webex.request.args[0][0];
354
- const metric = req.body.metrics[0];
355
-
356
- assert.property(metric.eventPayload, 'origin');
357
- assert.property(metric.eventPayload, 'originTime');
358
- assert.property(metric.eventPayload.origin, 'buildType');
359
- assert.property(metric.eventPayload.origin, 'networkType');
360
- assert.property(metric.eventPayload.originTime, 'sent');
361
- assert.equal(metric.eventPayload.origin.buildType, 'prod');
362
- });
363
- });
364
- });
365
314
  });
366
315
  });
@@ -0,0 +1,231 @@
1
+ import {assert} from '@webex/test-helper-chai';
2
+ import {NewMetrics, CallDiagnosticLatencies} from '@webex/internal-plugin-metrics';
3
+ import MockWebex from '@webex/test-helper-mock-webex';
4
+ import sinon from 'sinon';
5
+ import {Utils} from '@webex/internal-plugin-metrics';
6
+
7
+ describe('internal-plugin-metrics', () => {
8
+
9
+ const mockWebex = () => new MockWebex({
10
+ children: {
11
+ newMetrics: NewMetrics,
12
+ },
13
+ meetings: {
14
+ meetingCollection: {
15
+ get: sinon.stub(),
16
+ },
17
+ },
18
+ request: sinon.stub().resolves({}),
19
+ logger: {
20
+ log: sinon.stub(),
21
+ error: sinon.stub(),
22
+ }
23
+ });
24
+
25
+ describe('check submitClientEvent when webex is not ready', () => {
26
+ let webex;
27
+ //@ts-ignore
28
+ webex = mockWebex();
29
+
30
+ it('checks the log', () => {
31
+ webex.internal.newMetrics.submitClientEvent({
32
+ name: 'client.alert.displayed',
33
+ options: {
34
+ meetingId: '123',
35
+ },
36
+ });
37
+ assert.calledWith(
38
+ webex.logger.log,
39
+ 'NewMetrics: @submitClientEvent. Attempted to submit before webex.ready. Event name: client.alert.displayed'
40
+ );
41
+ });
42
+ });
43
+
44
+ describe('new-metrics contstructor', () => {
45
+ it('checks callDiagnosticLatencies is defined before ready emit', () => {
46
+
47
+ const webex = mockWebex();
48
+
49
+ assert.instanceOf(webex.internal.newMetrics.callDiagnosticLatencies, CallDiagnosticLatencies);
50
+ });
51
+ });
52
+
53
+ describe('new-metrics', () => {
54
+ let webex;
55
+
56
+ beforeEach(() => {
57
+ //@ts-ignore
58
+ webex = mockWebex();
59
+
60
+ webex.emit('ready');
61
+
62
+ webex.internal.newMetrics.callDiagnosticLatencies.saveTimestamp = sinon.stub();
63
+ webex.internal.newMetrics.callDiagnosticLatencies.clearTimestamps = sinon.stub();
64
+ webex.internal.newMetrics.callDiagnosticMetrics.submitClientEvent = sinon.stub();
65
+ webex.internal.newMetrics.callDiagnosticMetrics.submitMQE = sinon.stub();
66
+ webex.internal.newMetrics.callDiagnosticMetrics.clientMetricsAliasUser = sinon.stub();
67
+ webex.internal.newMetrics.callDiagnosticMetrics.buildClientEventFetchRequestOptions =
68
+ sinon.stub();
69
+ webex.setTimingsAndFetch = sinon.stub();
70
+ });
71
+
72
+ afterEach(() => {
73
+ sinon.restore();
74
+ })
75
+
76
+ it('submits Client Event successfully', () => {
77
+ webex.internal.newMetrics.submitClientEvent({
78
+ name: 'client.alert.displayed',
79
+ options: {
80
+ meetingId: '123',
81
+ },
82
+ });
83
+
84
+ assert.calledWith(webex.internal.newMetrics.callDiagnosticLatencies.saveTimestamp, {
85
+ key: 'client.alert.displayed',
86
+ options: {meetingId: '123'},
87
+ });
88
+ assert.calledWith(webex.internal.newMetrics.callDiagnosticMetrics.submitClientEvent, {
89
+ name: 'client.alert.displayed',
90
+ payload: undefined,
91
+ options: {meetingId: '123'},
92
+ });
93
+ });
94
+
95
+
96
+ it('submits MQE successfully', () => {
97
+ webex.internal.newMetrics.submitMQE({
98
+ name: 'client.mediaquality.event',
99
+ //@ts-ignore
100
+ payload: {intervals: [{}]},
101
+ options: {
102
+ meetingId: '123',
103
+ networkType: 'wifi',
104
+ },
105
+ });
106
+
107
+ assert.calledWith(webex.internal.newMetrics.callDiagnosticLatencies.saveTimestamp, {
108
+ key: 'client.mediaquality.event',
109
+ });
110
+ assert.calledWith(webex.internal.newMetrics.callDiagnosticMetrics.submitMQE, {
111
+ name: 'client.mediaquality.event',
112
+ //@ts-ignore
113
+ payload: {intervals: [{}]},
114
+ options: {
115
+ meetingId: '123',
116
+ networkType: 'wifi',
117
+ },
118
+ });
119
+ });
120
+
121
+ it('submits Internal Event successfully', () => {
122
+ webex.internal.newMetrics.submitInternalEvent({
123
+ name: 'client.mediaquality.event',
124
+ });
125
+
126
+ assert.calledWith(webex.internal.newMetrics.callDiagnosticLatencies.saveTimestamp, {
127
+ key: 'client.mediaquality.event',
128
+ });
129
+ assert.notCalled(webex.internal.newMetrics.callDiagnosticLatencies.clearTimestamps);
130
+ });
131
+
132
+ it('submits Internal Event successfully for clearing the join latencies', () => {
133
+ webex.internal.newMetrics.submitInternalEvent({
134
+ name: 'internal.reset.join.latencies',
135
+ });
136
+
137
+ assert.notCalled(webex.internal.newMetrics.callDiagnosticLatencies.saveTimestamp);
138
+ assert.calledOnce(webex.internal.newMetrics.callDiagnosticLatencies.clearTimestamps);
139
+ });
140
+
141
+ describe('#clientMetricsAliasUser', () => {
142
+ it('aliases the user correctly', async () => {
143
+ webex.request.resolves({response: 'abc'});
144
+ await webex.internal.newMetrics.clientMetricsAliasUser('my-id');
145
+ assert.calledWith(webex.request, {
146
+ method: 'POST',
147
+ api: 'metrics',
148
+ resource: 'clientmetrics',
149
+ headers: { 'x-prelogin-userid': 'my-id' },
150
+ body: {},
151
+ qs: { alias: true },
152
+ });
153
+ assert.calledWith(
154
+ webex.logger.log,
155
+ 'NewMetrics: @clientMetricsAliasUser. Request successful.'
156
+ );
157
+ });
158
+
159
+ it('handles failed request correctly', async () => {
160
+ webex.request.rejects(new Error("test error"));
161
+ sinon.stub(Utils, 'generateCommonErrorMetadata').returns('formattedError')
162
+ try {
163
+ await webex.internal.newMetrics.clientMetricsAliasUser({event: 'test'}, 'my-id');
164
+ } catch (err) {
165
+ assert.calledWith(
166
+ webex.logger.error,
167
+ 'NewMetrics: @clientMetricsAliasUser. Request failed:',
168
+ `err: formattedError`
169
+ );
170
+ }
171
+ });
172
+ });
173
+
174
+ describe('#buildClientEventFetchRequestOptions', () => {
175
+ it('builds client event fetch options successfully', () => {
176
+ webex.internal.newMetrics.buildClientEventFetchRequestOptions({
177
+ name: 'client.alert.displayed',
178
+ options: {
179
+ meetingId: '123',
180
+ },
181
+ });
182
+
183
+ assert.calledWith(
184
+ webex.internal.newMetrics.callDiagnosticMetrics.buildClientEventFetchRequestOptions,
185
+ {
186
+ name: 'client.alert.displayed',
187
+ payload: undefined,
188
+ options: {meetingId: '123'},
189
+ }
190
+ );
191
+ });
192
+ });
193
+
194
+ describe('#setMetricTimingsAndFetch', () => {
195
+ beforeEach(() => {
196
+ global.fetch = sinon.stub();
197
+ });
198
+
199
+ it('calls fetch with the expected options', () => {
200
+ const now = new Date();
201
+ sinon.useFakeTimers(now.getTime());
202
+
203
+ webex.internal.newMetrics.setMetricTimingsAndFetch({
204
+ json: true,
205
+ body: JSON.stringify({metrics: [{eventPayload: {}}]}),
206
+ });
207
+
208
+ const expected = {
209
+ json: true,
210
+ body: JSON.stringify({
211
+ metrics: [
212
+ {
213
+ eventPayload: {
214
+ originTime: {
215
+ triggered: now.toISOString(),
216
+ sent: now.toISOString(),
217
+ },
218
+ },
219
+ },
220
+ ],
221
+ }),
222
+ };
223
+
224
+ sinon.assert.calledOnce(webex.setTimingsAndFetch);
225
+ sinon.assert.calledWith(webex.setTimingsAndFetch, expected);
226
+
227
+ sinon.restore();
228
+ });
229
+ });
230
+ });
231
+ });