@elastic/synthetics 1.0.0-beta.44 → 1.0.0-beta.46

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.
@@ -38,6 +38,22 @@ function epochTimeInSeconds() {
38
38
  return getTimestamp() / 1e6;
39
39
  }
40
40
 
41
+ /**
42
+ * Find the first positive number in an array for Resource timing data
43
+ */
44
+ const firstPositive = (numbers: number[]) => {
45
+ for (let i = 0; i < numbers.length; ++i) {
46
+ if (numbers[i] > 0) {
47
+ return numbers[i];
48
+ }
49
+ }
50
+ return null;
51
+ };
52
+
53
+ const roundMilliSecs = (value: number): number => {
54
+ return Math.floor(value * 1000) / 1000;
55
+ };
56
+
41
57
  /**
42
58
  * Used as a key in each Network Request to identify the
43
59
  * associated request across distinct lifecycle events
@@ -83,7 +99,6 @@ export class NetworkManager {
83
99
  const { product } = await client.send('Browser.getVersion');
84
100
  const [name, version] = product.split('/');
85
101
  this._browser = { name, version };
86
- await client.send('Network.enable');
87
102
  /**
88
103
  * Listen for all network events from PW context
89
104
  */
@@ -135,7 +150,16 @@ export class NetworkManager {
135
150
  responseReceivedTime: -1,
136
151
  resourceSize: 0,
137
152
  transferSize: 0,
138
- timings: null,
153
+ timings: {
154
+ blocked: -1,
155
+ dns: -1,
156
+ ssl: -1,
157
+ connect: -1,
158
+ send: -1,
159
+ wait: -1,
160
+ receive: -1,
161
+ total: -1,
162
+ },
139
163
  };
140
164
 
141
165
  if (request.redirectedFrom()) {
@@ -162,6 +186,46 @@ export class NetworkManager {
162
186
  redirectURL: networkEntry.response.redirectURL,
163
187
  };
164
188
 
189
+ // Gather all resource timing information up until the
190
+ // TTFB(Time to first byte) is received
191
+ const timing = request.timing();
192
+ const blocked =
193
+ roundMilliSecs(
194
+ firstPositive([
195
+ timing.domainLookupStart,
196
+ timing.connectStart,
197
+ timing.requestStart,
198
+ ])
199
+ ) || -1;
200
+ const dns =
201
+ timing.domainLookupEnd !== -1
202
+ ? roundMilliSecs(timing.domainLookupEnd - timing.domainLookupStart)
203
+ : -1;
204
+ const connect =
205
+ timing.connectEnd !== -1
206
+ ? roundMilliSecs(timing.connectEnd - timing.connectStart)
207
+ : -1;
208
+ const ssl =
209
+ timing.secureConnectionStart !== -1
210
+ ? roundMilliSecs(timing.connectEnd - timing.secureConnectionStart)
211
+ : -1;
212
+ const wait =
213
+ timing.responseStart !== -1
214
+ ? roundMilliSecs(timing.responseStart - timing.requestStart)
215
+ : -1;
216
+
217
+ networkEntry.timings = {
218
+ blocked,
219
+ dns,
220
+ ssl,
221
+ connect,
222
+ send: 0, // not exposed via RT api
223
+ wait,
224
+ receive: -1, // will be available after full response is received
225
+ total: -1,
226
+ };
227
+ this._calcTotalTime(networkEntry, timing);
228
+
165
229
  const page = request.frame().page();
166
230
  this._addBarrier(
167
231
  page,
@@ -200,83 +264,21 @@ export class NetworkManager {
200
264
  if (!networkEntry) return;
201
265
 
202
266
  networkEntry.loadEndTime = epochTimeInSeconds();
267
+ // responseEnd is fired after the last byte of the response is received.
203
268
  const timing = request.timing();
204
- const { loadEndTime, requestSentTime } = networkEntry;
205
- networkEntry.timings = {
206
- blocked: -1,
207
- dns: -1,
208
- ssl: -1,
209
- connect: -1,
210
- send: -1,
211
- wait: -1,
212
- receive: -1,
213
- total: -1,
214
- };
215
-
216
- const firstPositive = (numbers: number[]) => {
217
- for (let i = 0; i < numbers.length; ++i) {
218
- if (numbers[i] > 0) {
219
- return numbers[i];
220
- }
221
- }
222
- return null;
223
- };
224
- const roundMilliSecs = (value: number): number => {
225
- return Math.floor(value * 1000) / 1000;
226
- };
227
-
228
- if (timing.startTime === 0) {
229
- // Convert to milliseconds before round off
230
- const total = roundMilliSecs((loadEndTime - requestSentTime) * 1000);
231
- networkEntry.timings.total = total;
232
- return;
233
- }
234
-
235
- const blocked =
236
- roundMilliSecs(
237
- firstPositive([
238
- timing.domainLookupStart,
239
- timing.connectStart,
240
- timing.requestStart,
241
- ])
242
- ) || -1;
243
- const dns =
244
- timing.domainLookupEnd !== -1
245
- ? roundMilliSecs(timing.domainLookupEnd - timing.domainLookupStart)
246
- : -1;
247
- const connect =
248
- timing.connectEnd !== -1
249
- ? roundMilliSecs(timing.connectEnd - timing.connectStart)
250
- : -1;
251
- const ssl =
252
- timing.secureConnectionStart !== -1
253
- ? roundMilliSecs(timing.connectEnd - timing.secureConnectionStart)
254
- : -1;
255
- const wait =
256
- timing.responseStart !== -1
257
- ? roundMilliSecs(timing.responseStart - timing.requestStart)
258
- : -1;
259
269
  const receive =
260
270
  timing.responseEnd !== -1
261
271
  ? roundMilliSecs(timing.responseEnd - timing.responseStart)
262
272
  : -1;
263
- const total = [blocked, dns, connect, wait, receive].reduce(
264
- (pre, cur) => (cur > 0 ? cur + pre : pre),
265
- 0
266
- );
267
- networkEntry.timings = {
268
- blocked,
269
- dns,
270
- connect,
271
- ssl,
272
- wait,
273
- send: 0, // not exposed via RT api
274
- receive,
275
- total,
276
- };
273
+ networkEntry.timings.receive = receive;
274
+ this._calcTotalTime(networkEntry, timing);
277
275
 
278
- const page = request.frame().page();
279
276
  // For aborted/failed requests sizes will not be present
277
+ if (timing.startTime <= 0) {
278
+ return;
279
+ }
280
+
281
+ const page = request.frame().page();
280
282
  this._addBarrier(
281
283
  page,
282
284
  request.sizes().then(sizes => {
@@ -295,6 +297,35 @@ export class NetworkManager {
295
297
  );
296
298
  }
297
299
 
300
+ /**
301
+ * Calculates the total time for the network request based on the ResourceTiming
302
+ * data from Playwright. Fallbacks to the event timings if ResourceTiming data
303
+ * is not available.
304
+ */
305
+ private _calcTotalTime(
306
+ entry: NetworkInfo,
307
+ rtiming: ReturnType<Request['timing']>
308
+ ) {
309
+ const timings = entry.timings;
310
+ entry.timings.total = [
311
+ timings.blocked,
312
+ timings.dns,
313
+ timings.connect,
314
+ timings.wait,
315
+ timings.receive,
316
+ ].reduce((pre, cur) => ((cur || -1) > 0 ? cur + pre : pre), 0);
317
+
318
+ // fallback when ResourceTiming data is not available
319
+ if (rtiming.startTime <= 0) {
320
+ const end =
321
+ entry.loadEndTime ||
322
+ entry.responseReceivedTime ||
323
+ entry.requestSentTime;
324
+ const total = roundMilliSecs((end - entry.requestSentTime) * 1000);
325
+ entry.timings.total = total <= 0 ? -1 : total;
326
+ }
327
+ }
328
+
298
329
  async stop() {
299
330
  /**
300
331
  * Waiting for all network events is error prone and might hang the tests
package/src/push/index.ts CHANGED
@@ -42,6 +42,7 @@ import {
42
42
  indent,
43
43
  done,
44
44
  getMonitorManagementURL,
45
+ THROTTLING_WARNING_MSG,
45
46
  } from '../helpers';
46
47
  import type { PushOptions, ProjectSettings } from '../common_types';
47
48
  import { findSyntheticsConfig, readConfig } from '../config';
@@ -280,3 +281,11 @@ export async function pushLegacy(
280
281
 
281
282
  done(`Pushed: ${grey(getMonitorManagementURL(options.url))}`);
282
283
  }
284
+
285
+ // prints warning if any of the monitors has throttling settings enabled during push
286
+ export function warnIfThrottled(monitors: Monitor[]) {
287
+ const throttled = monitors.some(monitor => monitor.config.throttling != null);
288
+ if (throttled) {
289
+ warn(THROTTLING_WARNING_MSG);
290
+ }
291
+ }