@checkly/playwright-core 1.41.2-beta.3 → 1.41.2-beta.4

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.
@@ -0,0 +1,106 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.setSWatcherCurrentStepData = exports.parseQueryParameters = exports.initializeTimings = exports.getTimings = exports.getRequestHeaders = exports.getClientCertificates = exports.getBody = exports.addRequestListeners = void 0;
7
+ /**
8
+ * Get client custom certificates.
9
+ *
10
+ * @param {Object} options
11
+ * @returns Object
12
+ */
13
+ const getClientCertificates = options => {
14
+ if (!options.certs) return {};
15
+ return {
16
+ cert: options.cert,
17
+ key: options.key,
18
+ passphrase: options.passphrase,
19
+ ca: options.ca
20
+ };
21
+ };
22
+ exports.getClientCertificates = getClientCertificates;
23
+ const setSWatcherCurrentStepData = (sWatcher, result) => {
24
+ if (!sWatcher) return;
25
+ sWatcher.appendCurrentStepData({
26
+ ...result.response,
27
+ checklyStepCategory: 'api-call'
28
+ });
29
+ };
30
+ exports.setSWatcherCurrentStepData = setSWatcherCurrentStepData;
31
+ const getRequestHeaders = headers => {
32
+ return Object.entries(headers).map(([key, value]) => {
33
+ return {
34
+ name: key,
35
+ value: value
36
+ };
37
+ });
38
+ };
39
+ exports.getRequestHeaders = getRequestHeaders;
40
+ const parseQueryParameters = url => {
41
+ const searchParams = new URL(url).searchParams;
42
+ const parameters = [];
43
+ for (const [name, value] of searchParams.entries()) {
44
+ parameters.push({
45
+ name,
46
+ value
47
+ });
48
+ }
49
+ return parameters;
50
+ };
51
+ exports.parseQueryParameters = parseQueryParameters;
52
+ const getTimings = eventTimes => {
53
+ const {
54
+ agent,
55
+ values
56
+ } = eventTimes;
57
+ const end = performance.now() - values.startTimeNow;
58
+ const socket = values.socket || 0;
59
+ const lookup = values.lookup || socket;
60
+ const connect = values.connect || lookup;
61
+ const response = values.response || connect;
62
+ return {
63
+ wait: agent ? socket - lookup : socket,
64
+ dns: agent ? lookup : lookup - socket,
65
+ tcp: agent ? connect - socket : connect - lookup,
66
+ firstByte: response - connect,
67
+ download: end - response,
68
+ total: end
69
+ };
70
+ };
71
+ exports.getTimings = getTimings;
72
+ const getBody = body => {
73
+ const responseBodySizeLimit = 100000;
74
+ //@ts-ignore
75
+ const bodyAsText = Buffer.byteLength(body, 'utf-8') > responseBodySizeLimit ? body.slice(0, responseBodySizeLimit).toString('utf8') + '...' : body.toString('utf8');
76
+ try {
77
+ return JSON.parse(bodyAsText);
78
+ } catch {
79
+ return bodyAsText;
80
+ }
81
+ };
82
+ exports.getBody = getBody;
83
+ const initializeTimings = () => {
84
+ return {
85
+ values: {
86
+ startTimeNow: performance.now()
87
+ },
88
+ agent: false
89
+ };
90
+ };
91
+ exports.initializeTimings = initializeTimings;
92
+ const addRequestListeners = (request, timings) => {
93
+ request.on('socket', socket => {
94
+ timings.values.socket = performance.now() - timings.values.startTimeNow;
95
+ socket.on('connect', () => {
96
+ timings.values.connect = performance.now() - timings.values.startTimeNow;
97
+ });
98
+ socket.on('lookup', () => {
99
+ timings.values.lookup = performance.now() - timings.values.startTimeNow;
100
+ });
101
+ socket.on('secureConnect', () => {
102
+ timings.values.tlsHandshake = performance.now() - timings.values.startTimeNow;
103
+ });
104
+ });
105
+ };
106
+ exports.addRequestListeners = addRequestListeners;
@@ -13,6 +13,7 @@ var _channelOwner = require("./channelOwner");
13
13
  var _network = require("./network");
14
14
  var _tracing = require("./tracing");
15
15
  var _errors = require("./errors");
16
+ var _fetch = require("playwright-core/lib/checkly/fetch");
16
17
  let _Symbol$asyncDispose, _Symbol$asyncDispose2, _util$inspect$custom;
17
18
  /**
18
19
  * Copyright (c) Microsoft Corporation.
@@ -55,6 +56,9 @@ class APIRequest {
55
56
  storageState,
56
57
  tracesDir
57
58
  })).request);
59
+ // Checkly related code.
60
+ //@ts-ignore
61
+ context.__sWatcher__ = options.sWatcher;
58
62
  this._contexts.add(context);
59
63
  context._request = this;
60
64
  context._tracing._tracesDir = tracesDir;
@@ -198,8 +202,12 @@ class APIRequestContext extends _channelOwner.ChannelOwner {
198
202
  failOnStatusCode: options.failOnStatusCode,
199
203
  ignoreHTTPSErrors: options.ignoreHTTPSErrors,
200
204
  maxRedirects: maxRedirects,
201
- ...fixtures
205
+ tokenizedURL: options.tokenizedURL,
206
+ ...fixtures,
207
+ ...(0, _fetch.getClientCertificates)(options)
202
208
  });
209
+ //@ts-ignore
210
+ (0, _fetch.setSWatcherCurrentStepData)(this.__sWatcher__, result);
203
211
  return new APIResponse(this, result.response);
204
212
  });
205
213
  }
@@ -251,6 +259,9 @@ class APIResponse {
251
259
  headersArray() {
252
260
  return this._headers.headersArray();
253
261
  }
262
+ timings() {
263
+ return this._initializer.timings;
264
+ }
254
265
  async body() {
255
266
  try {
256
267
  const result = await this._request._channel.fetchResponseBody({
@@ -207,7 +207,12 @@ _validatorPrimitives.scheme.APIRequestContextFetchParams = (0, _validatorPrimiti
207
207
  timeout: (0, _validatorPrimitives.tOptional)(_validatorPrimitives.tNumber),
208
208
  failOnStatusCode: (0, _validatorPrimitives.tOptional)(_validatorPrimitives.tBoolean),
209
209
  ignoreHTTPSErrors: (0, _validatorPrimitives.tOptional)(_validatorPrimitives.tBoolean),
210
- maxRedirects: (0, _validatorPrimitives.tOptional)(_validatorPrimitives.tNumber)
210
+ maxRedirects: (0, _validatorPrimitives.tOptional)(_validatorPrimitives.tNumber),
211
+ tokenizedURL: (0, _validatorPrimitives.tOptional)(_validatorPrimitives.tString),
212
+ cert: (0, _validatorPrimitives.tOptional)(_validatorPrimitives.tString),
213
+ key: (0, _validatorPrimitives.tOptional)(_validatorPrimitives.tString),
214
+ ca: (0, _validatorPrimitives.tOptional)(_validatorPrimitives.tString),
215
+ passphrase: (0, _validatorPrimitives.tOptional)(_validatorPrimitives.tString)
211
216
  });
212
217
  _validatorPrimitives.scheme.APIRequestContextFetchResult = (0, _validatorPrimitives.tObject)({
213
218
  response: (0, _validatorPrimitives.tType)('APIResponse')
@@ -240,7 +245,23 @@ _validatorPrimitives.scheme.APIResponse = (0, _validatorPrimitives.tObject)({
240
245
  url: _validatorPrimitives.tString,
241
246
  status: _validatorPrimitives.tNumber,
242
247
  statusText: _validatorPrimitives.tString,
243
- headers: (0, _validatorPrimitives.tArray)((0, _validatorPrimitives.tType)('NameValue'))
248
+ headers: (0, _validatorPrimitives.tArray)((0, _validatorPrimitives.tType)('NameValue')),
249
+ requestHeaders: (0, _validatorPrimitives.tArray)((0, _validatorPrimitives.tType)('NameValue')),
250
+ responseHeaders: (0, _validatorPrimitives.tArray)((0, _validatorPrimitives.tType)('NameValue')),
251
+ body: (0, _validatorPrimitives.tOptional)(_validatorPrimitives.tAny),
252
+ method: _validatorPrimitives.tString,
253
+ tokenizedURL: (0, _validatorPrimitives.tOptional)(_validatorPrimitives.tString),
254
+ timings: (0, _validatorPrimitives.tObject)({
255
+ wait: (0, _validatorPrimitives.tOptional)(_validatorPrimitives.tNumber),
256
+ dns: (0, _validatorPrimitives.tOptional)(_validatorPrimitives.tNumber),
257
+ lookup: (0, _validatorPrimitives.tOptional)(_validatorPrimitives.tNumber),
258
+ connect: (0, _validatorPrimitives.tOptional)(_validatorPrimitives.tNumber),
259
+ tcp: (0, _validatorPrimitives.tOptional)(_validatorPrimitives.tNumber),
260
+ firstByte: (0, _validatorPrimitives.tOptional)(_validatorPrimitives.tNumber),
261
+ download: (0, _validatorPrimitives.tOptional)(_validatorPrimitives.tNumber),
262
+ total: (0, _validatorPrimitives.tOptional)(_validatorPrimitives.tNumber)
263
+ }),
264
+ queryParams: (0, _validatorPrimitives.tOptional)((0, _validatorPrimitives.tArray)((0, _validatorPrimitives.tType)('NameValue')))
244
265
  });
245
266
  _validatorPrimitives.scheme.LifecycleEvent = (0, _validatorPrimitives.tEnum)(['load', 'domcontentloaded', 'networkidle', 'commit']);
246
267
  _validatorPrimitives.scheme.LocalUtilsInitializer = (0, _validatorPrimitives.tObject)({
@@ -25,7 +25,7 @@ var _instrumentation = require("../instrumentation");
25
25
  * limitations under the License.
26
26
  */
27
27
 
28
- const fps = 25;
28
+ const fps = 12;
29
29
  class VideoRecorder {
30
30
  static async launch(page, ffmpegPath, options) {
31
31
  if (!options.outputFile.endsWith('.webm')) throw new Error('File must have .webm extension');
@@ -199,7 +199,14 @@ class APIRequestContextDispatcher extends _dispatcher.Dispatcher {
199
199
  status: fetchResponse.status,
200
200
  statusText: fetchResponse.statusText,
201
201
  headers: fetchResponse.headers,
202
- fetchUid: fetchResponse.fetchUid
202
+ fetchUid: fetchResponse.fetchUid,
203
+ requestHeaders: fetchResponse.requestHeaders,
204
+ responseHeaders: fetchResponse.responseHeaders,
205
+ timings: fetchResponse.timings,
206
+ queryParams: fetchResponse.queryParams,
207
+ body: fetchResponse.body,
208
+ method: fetchResponse.method,
209
+ tokenizedURL: fetchResponse.tokenizedURL
203
210
  }
204
211
  };
205
212
  }
@@ -9,6 +9,7 @@ var https = _interopRequireWildcard(require("https"));
9
9
  var _stream = require("stream");
10
10
  var _url = _interopRequireDefault(require("url"));
11
11
  var _zlib = _interopRequireDefault(require("zlib"));
12
+ var _perf_hooks = require("perf_hooks");
12
13
  var _timeoutSettings = require("../common/timeoutSettings");
13
14
  var _userAgent = require("../utils/userAgent");
14
15
  var _utils = require("../utils");
@@ -21,6 +22,7 @@ var _instrumentation = require("./instrumentation");
21
22
  var _progress = require("./progress");
22
23
  var _tracing = require("./trace/recorder/tracing");
23
24
  var _network = require("./network");
25
+ var _fetch = require("playwright-core/lib/checkly/fetch");
24
26
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
25
27
  function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
26
28
  function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
@@ -124,7 +126,9 @@ class APIRequestContext extends _instrumentation.SdkObject {
124
126
  maxRedirects: params.maxRedirects === 0 ? -1 : params.maxRedirects === undefined ? 20 : params.maxRedirects,
125
127
  timeout,
126
128
  deadline,
127
- __testHookLookup: params.__testHookLookup
129
+ __testHookLookup: params.__testHookLookup,
130
+ tokenizedURL: params.tokenizedURL,
131
+ ...(0, _fetch.getClientCertificates)(params)
128
132
  };
129
133
  // rejectUnauthorized = undefined is treated as true in node 12.
130
134
  if (params.ignoreHTTPSErrors || defaults.ignoreHTTPSErrors) options.rejectUnauthorized = false;
@@ -139,7 +143,14 @@ class APIRequestContext extends _instrumentation.SdkObject {
139
143
  if (params.failOnStatusCode && (fetchResponse.status < 200 || fetchResponse.status >= 400)) throw new Error(`${fetchResponse.status} ${fetchResponse.statusText}`);
140
144
  return {
141
145
  ...fetchResponse,
142
- fetchUid
146
+ fetchUid,
147
+ requestHeaders: (0, _fetch.getRequestHeaders)(headers),
148
+ responseHeaders: fetchResponse.headers,
149
+ queryParams: (0, _fetch.parseQueryParameters)(fetchResponse.url),
150
+ url: params.tokenizedURL || fetchResponse.url,
151
+ timings: (0, _fetch.getTimings)(fetchResponse.timings),
152
+ body: (0, _fetch.getBody)(fetchResponse.body),
153
+ method: options.method
143
154
  };
144
155
  }
145
156
  _parseSetCookieHeader(responseUrl, setCookie) {
@@ -195,7 +206,9 @@ class APIRequestContext extends _instrumentation.SdkObject {
195
206
  ...options,
196
207
  agent
197
208
  };
209
+ const timings = (0, _fetch.initializeTimings)();
198
210
  const request = requestConstructor(url, requestOptions, async response => {
211
+ timings.values.response = _perf_hooks.performance.now() - timings.values.startTimeNow;
199
212
  const notifyRequestFinished = body => {
200
213
  const requestFinishedEvent = {
201
214
  requestEvent,
@@ -292,6 +305,7 @@ class APIRequestContext extends _instrumentation.SdkObject {
292
305
  response.on('aborted', () => reject(new Error('aborted')));
293
306
  const chunks = [];
294
307
  const notifyBodyFinished = () => {
308
+ timings.values.endAt = _perf_hooks.performance.now();
295
309
  const body = Buffer.concat(chunks);
296
310
  notifyRequestFinished(body);
297
311
  fulfill({
@@ -299,7 +313,15 @@ class APIRequestContext extends _instrumentation.SdkObject {
299
313
  status: response.statusCode || 0,
300
314
  statusText: response.statusMessage || '',
301
315
  headers: toHeadersArray(response.rawHeaders),
302
- body
316
+ body,
317
+ timings: {
318
+ //@ts-ignore
319
+ values: {
320
+ ...timings.values,
321
+ ..._happyEyeballs.httpHappyEyeballsTimings.values
322
+ },
323
+ agent: _happyEyeballs.httpHappyEyeballsTimings.agent || timings.agent
324
+ }
303
325
  });
304
326
  };
305
327
  let body = response;
@@ -328,6 +350,7 @@ class APIRequestContext extends _instrumentation.SdkObject {
328
350
  body.on('data', chunk => chunks.push(chunk));
329
351
  body.on('end', notifyBodyFinished);
330
352
  });
353
+ (0, _fetch.addRequestListeners)(request, timings);
331
354
  request.on('error', reject);
332
355
  const disposeListener = () => {
333
356
  reject(new Error('Request context disposed.'));
@@ -4,7 +4,7 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.createSocket = createSocket;
7
- exports.httpsHappyEyeballsAgent = exports.httpHappyEyeballsAgent = void 0;
7
+ exports.httpsHappyEyeballsAgent = exports.httpHappyEyeballsTimings = exports.httpHappyEyeballsAgent = void 0;
8
8
  var dns = _interopRequireWildcard(require("dns"));
9
9
  var http = _interopRequireWildcard(require("http"));
10
10
  var https = _interopRequireWildcard(require("https"));
@@ -33,6 +33,15 @@ function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj &&
33
33
  // https://www.rfc-editor.org/rfc/rfc8305
34
34
  // Same as in Chromium (https://source.chromium.org/chromium/chromium/src/+/5666ff4f5077a7e2f72902f3a95f5d553ea0d88d:net/socket/transport_connect_job.cc;l=102)
35
35
  const connectionAttemptDelayMs = 300;
36
+ const timings = {
37
+ values: {
38
+ startTimeNow: 0,
39
+ socket: 0,
40
+ connect: 0,
41
+ lookup: 0
42
+ },
43
+ agent: false
44
+ };
36
45
  class HttpHappyEyeballsAgent extends http.Agent {
37
46
  createConnection(options, oncreate) {
38
47
  // There is no ambiguity in case of IP address.
@@ -42,6 +51,8 @@ class HttpHappyEyeballsAgent extends http.Agent {
42
51
  }
43
52
  class HttpsHappyEyeballsAgent extends https.Agent {
44
53
  createConnection(options, oncreate) {
54
+ timings.values.startTimeNow = performance.now();
55
+ timings.agent = true;
45
56
  // There is no ambiguity in case of IP address.
46
57
  if (net.isIP(clientRequestArgsToHostName(options))) return tls.connect(options);
47
58
  createConnectionAsync(options, oncreate, /* useTLS */true).catch(err => oncreate === null || oncreate === void 0 ? void 0 : oncreate(err));
@@ -49,6 +60,7 @@ class HttpsHappyEyeballsAgent extends https.Agent {
49
60
  }
50
61
  const httpsHappyEyeballsAgent = exports.httpsHappyEyeballsAgent = new HttpsHappyEyeballsAgent();
51
62
  const httpHappyEyeballsAgent = exports.httpHappyEyeballsAgent = new HttpHappyEyeballsAgent();
63
+ const httpHappyEyeballsTimings = exports.httpHappyEyeballsTimings = timings;
52
64
  async function createSocket(host, port) {
53
65
  return new Promise((resolve, reject) => {
54
66
  if (net.isIP(host)) {
@@ -97,10 +109,13 @@ async function createConnectionAsync(options, oncreate, useTLS) {
97
109
  port: options.port,
98
110
  host: address
99
111
  });
112
+ // Socket created
113
+ timings.values.socket = performance.now() - timings.values.startTimeNow;
100
114
 
101
115
  // Each socket may fire only one of 'connect', 'timeout' or 'error' events.
102
116
  // None of these events are fired after socket.destroy() is called.
103
117
  socket.on('connect', () => {
118
+ timings.values.connect = performance.now() - timings.values.startTimeNow;
104
119
  connected.resolve();
105
120
  oncreate === null || oncreate === void 0 ? void 0 : oncreate(null, socket);
106
121
  // TODO: Cache the result?
@@ -126,6 +141,7 @@ async function lookupAddresses(hostname) {
126
141
  family: 0,
127
142
  verbatim: true
128
143
  });
144
+ timings.values.lookup = performance.now() - timings.values.startTimeNow;
129
145
  let firstFamily = addresses.filter(({
130
146
  family
131
147
  }) => family === 6);