@percy/core 1.26.2-beta.0 → 1.26.3-alpha.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.
package/dist/api.js CHANGED
@@ -32,8 +32,8 @@ export function createPercyServer(percy, port) {
32
32
 
33
33
  // skip or change api version header in testing mode
34
34
  if (((_percy$testing = percy.testing) === null || _percy$testing === void 0 ? void 0 : _percy$testing.version) !== false) {
35
- var _percy$testing2;
36
- res.setHeader('X-Percy-Core-Version', ((_percy$testing2 = percy.testing) === null || _percy$testing2 === void 0 ? void 0 : _percy$testing2.version) ?? pkg.version);
35
+ var _percy$testing$versio, _percy$testing2;
36
+ res.setHeader('X-Percy-Core-Version', (_percy$testing$versio = (_percy$testing2 = percy.testing) === null || _percy$testing2 === void 0 ? void 0 : _percy$testing2.version) !== null && _percy$testing$versio !== void 0 ? _percy$testing$versio : pkg.version);
37
37
  }
38
38
 
39
39
  // track all api reqeusts in testing mode
@@ -58,8 +58,8 @@ export function createPercyServer(percy, port) {
58
58
 
59
59
  // return json errors
60
60
  return next().catch(e => {
61
- var _percy$testing6;
62
- return res.json(e.status ?? 500, {
61
+ var _e$status, _percy$testing6;
62
+ return res.json((_e$status = e.status) !== null && _e$status !== void 0 ? _e$status : 500, {
63
63
  build: ((_percy$testing6 = percy.testing) === null || _percy$testing6 === void 0 ? void 0 : _percy$testing6.build) || percy.build,
64
64
  error: e.message,
65
65
  success: false
@@ -68,9 +68,9 @@ export function createPercyServer(percy, port) {
68
68
  })
69
69
  // healthcheck returns basic information
70
70
  .route('get', '/percy/healthcheck', (req, res) => {
71
- var _percy$testing7;
71
+ var _percy$testing$build2, _percy$testing7;
72
72
  return res.json(200, {
73
- build: ((_percy$testing7 = percy.testing) === null || _percy$testing7 === void 0 ? void 0 : _percy$testing7.build) ?? percy.build,
73
+ build: (_percy$testing$build2 = (_percy$testing7 = percy.testing) === null || _percy$testing7 === void 0 ? void 0 : _percy$testing7.build) !== null && _percy$testing$build2 !== void 0 ? _percy$testing$build2 : percy.build,
74
74
  loglevel: percy.loglevel(),
75
75
  config: percy.config,
76
76
  success: true
@@ -202,6 +202,7 @@ export function createPercyServer(percy, port) {
202
202
 
203
203
  // Create a static server instance with an automatic sitemap
204
204
  export function createStaticServer(options) {
205
+ var _options$rewrites;
205
206
  let {
206
207
  serve: dir,
207
208
  baseUrl = ''
@@ -214,7 +215,7 @@ export function createStaticServer(options) {
214
215
  // used when generating an automatic sitemap
215
216
  let toURL = Server.createRewriter(
216
217
  // reverse rewrites' src, dest, & order
217
- Object.entries((options === null || options === void 0 ? void 0 : options.rewrites) ?? {}).reduce((acc, rw) => [rw.reverse(), ...acc], []), (filename, rewrite) => new URL(path.posix.join('/', baseUrl,
218
+ Object.entries((_options$rewrites = options === null || options === void 0 ? void 0 : options.rewrites) !== null && _options$rewrites !== void 0 ? _options$rewrites : {}).reduce((acc, rw) => [rw.reverse(), ...acc], []), (filename, rewrite) => new URL(path.posix.join('/', baseUrl,
218
219
  // cleanUrls will trim trailing .html/index.html from paths
219
220
  !options.cleanUrls ? rewrite(filename) : rewrite(filename).replace(/(\/index)?\.html$/, '')), server.address()));
220
221
 
package/dist/browser.js CHANGED
@@ -1,3 +1,8 @@
1
+ function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
2
+ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
3
+ function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
4
+ function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); }
5
+ function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
1
6
  import fs from 'fs';
2
7
  import os from 'os';
3
8
  import path from 'path';
@@ -67,6 +72,7 @@ export class Browser extends EventEmitter {
67
72
  super().percy = percy;
68
73
  }
69
74
  async launch() {
75
+ var _executable;
70
76
  // already launching or launched
71
77
  if (this.readyState != null) return;
72
78
  this.readyState = 0;
@@ -80,7 +86,7 @@ export class Browser extends EventEmitter {
80
86
  args = [],
81
87
  timeout
82
88
  } = launchOptions;
83
- executable ?? (executable = process.env.PERCY_BROWSER_EXECUTABLE);
89
+ (_executable = executable) !== null && _executable !== void 0 ? _executable : executable = process.env.PERCY_BROWSER_EXECUTABLE;
84
90
 
85
91
  // transform cookies object to an array of cookie params
86
92
  this.cookies = Array.isArray(cookies) ? cookies : Object.entries(cookies).map(([name, value]) => ({
@@ -212,10 +218,9 @@ export class Browser extends EventEmitter {
212
218
  let id = ++this.#lastid;
213
219
  if (!params && typeof method === 'object') {
214
220
  // allow providing a raw message as the only argument and return the id
215
- this.ws.send(JSON.stringify({
216
- ...method,
221
+ this.ws.send(JSON.stringify(_objectSpread(_objectSpread({}, method), {}, {
217
222
  id
218
- }));
223
+ })));
219
224
  return id;
220
225
  } else {
221
226
  // send the message payload
@@ -251,7 +256,10 @@ export class Browser extends EventEmitter {
251
256
  if (match) cleanup(() => resolve(match[1]));
252
257
  };
253
258
  let handleExitClose = () => handleError();
254
- let handleError = error => cleanup(() => reject(new Error(`Failed to launch browser. ${(error === null || error === void 0 ? void 0 : error.message) ?? ''}\n${stderr}'\n\n`)));
259
+ let handleError = error => cleanup(() => {
260
+ var _error$message;
261
+ return reject(new Error(`Failed to launch browser. ${(_error$message = error === null || error === void 0 ? void 0 : error.message) !== null && _error$message !== void 0 ? _error$message : ''}\n${stderr}'\n\n`));
262
+ });
255
263
  let cleanup = callback => {
256
264
  clearTimeout(timeoutId);
257
265
  this.process.stderr.off('data', handleData);
package/dist/discovery.js CHANGED
@@ -1,3 +1,13 @@
1
+ const _excluded = ["domSnapshot", "resources"],
2
+ _excluded2 = ["discovery", "additionalSnapshots"],
3
+ _excluded3 = ["additionalSnapshots"];
4
+ function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
5
+ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
6
+ function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
7
+ function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); }
8
+ function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
9
+ function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
10
+ function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
1
11
  import logger from '@percy/logger';
2
12
  import Queue from './queue.js';
3
13
  import { normalizeURL, hostnameMatches, createResource, createRootResource, createPercyCSSResource, createLogResource, yieldAll } from './utils.js';
@@ -95,24 +105,24 @@ function parseDomResources({
95
105
  }
96
106
 
97
107
  // Calls the provided callback with additional resources
98
- function processSnapshotResources({
99
- domSnapshot,
100
- resources,
101
- ...snapshot
102
- }) {
103
- var _resources;
104
- resources = [...(((_resources = resources) === null || _resources === void 0 ? void 0 : _resources.values()) ?? [])];
108
+ function processSnapshotResources(_ref) {
109
+ var _resources$values, _resources, _domSnapshot$html;
110
+ let {
111
+ domSnapshot,
112
+ resources
113
+ } = _ref,
114
+ snapshot = _objectWithoutProperties(_ref, _excluded);
115
+ resources = [...((_resources$values = (_resources = resources) === null || _resources === void 0 ? void 0 : _resources.values()) !== null && _resources$values !== void 0 ? _resources$values : [])];
105
116
 
106
117
  // find any root resource matching the provided dom snapshot
107
- let rootContent = (domSnapshot === null || domSnapshot === void 0 ? void 0 : domSnapshot.html) ?? domSnapshot;
118
+ let rootContent = (_domSnapshot$html = domSnapshot === null || domSnapshot === void 0 ? void 0 : domSnapshot.html) !== null && _domSnapshot$html !== void 0 ? _domSnapshot$html : domSnapshot;
108
119
  let root = resources.find(r => r.content === rootContent);
109
120
 
110
121
  // initialize root resources if needed
111
122
  if (!root) {
112
- let domResources = parseDomResources({
113
- ...snapshot,
123
+ let domResources = parseDomResources(_objectSpread(_objectSpread({}, snapshot), {}, {
114
124
  domSnapshot
115
- });
125
+ }));
116
126
  resources = [...domResources.values(), ...resources];
117
127
  root = resources[0];
118
128
  }
@@ -131,20 +141,19 @@ function processSnapshotResources({
131
141
  var _log$meta$snapshot;
132
142
  return ((_log$meta$snapshot = log.meta.snapshot) === null || _log$meta$snapshot === void 0 ? void 0 : _log$meta$snapshot.name) === snapshot.meta.snapshot.name;
133
143
  })));
134
- return {
135
- ...snapshot,
144
+ return _objectSpread(_objectSpread({}, snapshot), {}, {
136
145
  resources
137
- };
146
+ });
138
147
  }
139
148
 
140
149
  // Triggers the capture of resource requests for a page by iterating over snapshot widths to resize
141
150
  // the page and calling any provided execute options.
142
151
  async function* captureSnapshotResources(page, snapshot, options) {
143
152
  let {
144
- discovery,
145
- additionalSnapshots = [],
146
- ...baseSnapshot
147
- } = snapshot;
153
+ discovery,
154
+ additionalSnapshots = []
155
+ } = snapshot,
156
+ baseSnapshot = _objectWithoutProperties(snapshot, _excluded2);
148
157
  let {
149
158
  capture,
150
159
  captureWidths,
@@ -154,10 +163,9 @@ async function* captureSnapshotResources(page, snapshot, options) {
154
163
 
155
164
  // used to take snapshots and remove any discovered root resource
156
165
  let takeSnapshot = async (options, width) => {
157
- if (captureWidths) options = {
158
- ...options,
166
+ if (captureWidths) options = _objectSpread(_objectSpread({}, options), {}, {
159
167
  width
160
- };
168
+ });
161
169
  let captured = await page.snapshot(options);
162
170
  captured.resources.delete(normalizeURL(captured.url));
163
171
  capture(processSnapshotResources(captured));
@@ -185,10 +193,7 @@ async function* captureSnapshotResources(page, snapshot, options) {
185
193
  // iterate over additional snapshots for proper DOM capturing
186
194
  for (let additionalSnapshot of [baseSnapshot, ...additionalSnapshots]) {
187
195
  let isBaseSnapshot = additionalSnapshot === baseSnapshot;
188
- let snap = {
189
- ...baseSnapshot,
190
- ...additionalSnapshot
191
- };
196
+ let snap = _objectSpread(_objectSpread({}, baseSnapshot), additionalSnapshot);
192
197
  let {
193
198
  widths,
194
199
  execute
@@ -247,9 +252,9 @@ export async function* discoverSnapshotResources(queue, options, callback) {
247
252
  debugSnapshotOptions(snapshot);
248
253
  if (skipDiscovery) {
249
254
  let {
250
- additionalSnapshots,
251
- ...baseSnapshot
252
- } = snapshot;
255
+ additionalSnapshots
256
+ } = snapshot,
257
+ baseSnapshot = _objectWithoutProperties(snapshot, _excluded3);
253
258
  additionalSnapshots = dryRun && additionalSnapshots || [];
254
259
  for (let snap of [baseSnapshot, ...additionalSnapshots]) {
255
260
  callback(dryRun ? snap : processSnapshotResources(snap));
@@ -293,18 +298,18 @@ export function createDiscoveryQueue(percy) {
293
298
  // initialize the resources for DOM snapshots
294
299
  .handle('push', snapshot => {
295
300
  let resources = parseDomResources(snapshot);
296
- return {
297
- ...snapshot,
301
+ return _objectSpread(_objectSpread({}, snapshot), {}, {
298
302
  resources
299
- };
303
+ });
300
304
  })
301
305
  // discovery resources for snapshots and call the callback for each discovered snapshot
302
306
  .handle('task', async function* (snapshot, callback) {
307
+ var _snapshot$enableJavaS;
303
308
  percy.log.debug(`Discovering resources: ${snapshot.name}`, snapshot.meta);
304
309
 
305
310
  // expectation explained in tests
306
311
  /* istanbul ignore next: tested, but coverage is stripped */
307
- let assetDiscoveryPageEnableJS = snapshot.cliEnableJavaScript && !snapshot.domSnapshot || (snapshot.enableJavaScript ?? !snapshot.domSnapshot);
312
+ let assetDiscoveryPageEnableJS = snapshot.cliEnableJavaScript && !snapshot.domSnapshot || ((_snapshot$enableJavaS = snapshot.enableJavaScript) !== null && _snapshot$enableJavaS !== void 0 ? _snapshot$enableJavaS : !snapshot.domSnapshot);
308
313
  percy.log.debug(`Asset discovery Browser Page enable JS: ${assetDiscoveryPageEnableJS}`);
309
314
  // create a new browser page
310
315
  let page = yield percy.browser.page({
package/dist/install.js CHANGED
@@ -84,8 +84,9 @@ export async function download({
84
84
  let total = parseInt(response.headers['content-length'], 10);
85
85
  let start, progress;
86
86
  response.on('data', chunk => {
87
- start ?? (start = Date.now());
88
- progress = (progress ?? 0) + chunk.length;
87
+ var _start, _progress;
88
+ (_start = start) !== null && _start !== void 0 ? _start : start = Date.now();
89
+ progress = ((_progress = progress) !== null && _progress !== void 0 ? _progress : 0) + chunk.length;
89
90
  log.progress(formatProgress(premsg, total, start, progress));
90
91
  });
91
92
  }
package/dist/network.js CHANGED
@@ -1,3 +1,8 @@
1
+ function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
2
+ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
3
+ function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
4
+ function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); }
5
+ function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
1
6
  import mime from 'mime-types';
2
7
  import logger from '@percy/logger';
3
8
  import { request as makeRequest } from '@percy/client/utils';
@@ -5,6 +10,7 @@ import { normalizeURL, hostnameMatches, createResource, waitFor } from './utils.
5
10
  const MAX_RESOURCE_SIZE = 25 * 1024 ** 2; // 25MB
6
11
  const ALLOWED_STATUSES = [200, 201, 301, 302, 304, 307, 308];
7
12
  const ALLOWED_RESOURCES = ['Document', 'Stylesheet', 'Image', 'Media', 'Font', 'Other'];
13
+ const ABORTED_MESSAGE = 'Request was aborted by browser';
8
14
 
9
15
  // The Interceptor class creates common handlers for dealing with intercepting asset requests
10
16
  // for a given page using various devtools protocol events and commands.
@@ -15,12 +21,14 @@ export class Network {
15
21
  #requests = new Map();
16
22
  #intercepts = new Map();
17
23
  #authentications = new Set();
24
+ #aborted = new Set();
18
25
  constructor(page, options) {
26
+ var _options$networkIdleT, _options$requestHeade, _options$userAgent;
19
27
  this.page = page;
20
- this.timeout = options.networkIdleTimeout ?? 100;
28
+ this.timeout = (_options$networkIdleT = options.networkIdleTimeout) !== null && _options$networkIdleT !== void 0 ? _options$networkIdleT : 100;
21
29
  this.authorization = options.authorization;
22
- this.requestHeaders = options.requestHeaders ?? {};
23
- this.userAgent = options.userAgent ??
30
+ this.requestHeaders = (_options$requestHeade = options.requestHeaders) !== null && _options$requestHeade !== void 0 ? _options$requestHeade : {};
31
+ this.userAgent = (_options$userAgent = options.userAgent) !== null && _options$userAgent !== void 0 ? _options$userAgent :
24
32
  // by default, emulate a non-headless browser
25
33
  page.session.browser.version.userAgent.replace('Headless', '');
26
34
  this.intercept = options.intercept;
@@ -77,6 +85,23 @@ export class Network {
77
85
  });
78
86
  }
79
87
 
88
+ // Validates that requestId is still valid as sometimes request gets cancelled and we have already executed
89
+ // _forgetRequest for the same, but we still attempt to make a call for it and it fails
90
+ // with Protocol error (Fetch.failRequest): Invalid InterceptionId.
91
+ async send(session, method, params) {
92
+ /* istanbul ignore else: currently all send have requestId */
93
+ if (params.requestId) {
94
+ /* istanbul ignore if: race condition, very hard to mock this */
95
+ if (this.isAborted(params.requestId)) {
96
+ throw new Error(ABORTED_MESSAGE);
97
+ }
98
+ }
99
+ return await session.send(method, params);
100
+ }
101
+ isAborted(requestId) {
102
+ return this.#aborted.has(requestId);
103
+ }
104
+
80
105
  // Throw a better network timeout error
81
106
  _throwTimeoutError(msg, filter = () => true) {
82
107
  if (this.log.shouldLog('debug')) {
@@ -102,10 +127,11 @@ export class Network {
102
127
  // Called when a request requires authentication. Responds to the auth request with any
103
128
  // provided authorization credentials.
104
129
  _handleAuthRequired = async (session, event) => {
130
+ var _this$authorization;
105
131
  let {
106
132
  username,
107
133
  password
108
- } = this.authorization ?? {};
134
+ } = (_this$authorization = this.authorization) !== null && _this$authorization !== void 0 ? _this$authorization : {};
109
135
  let {
110
136
  requestId
111
137
  } = event;
@@ -116,7 +142,7 @@ export class Network {
116
142
  response = 'ProvideCredentials';
117
143
  this.#authentications.add(requestId);
118
144
  }
119
- await session.send('Fetch.continueWithAuth', {
145
+ await this.send(session, 'Fetch.continueWithAuth', {
120
146
  requestId: event.requestId,
121
147
  authChallengeResponse: {
122
148
  response,
@@ -139,17 +165,15 @@ export class Network {
139
165
 
140
166
  // guard against redirects with the same requestId
141
167
  if ((pending === null || pending === void 0 ? void 0 : pending.request.url) === event.request.url && pending.request.method === event.request.method) {
142
- await this._handleRequest(session, {
143
- ...pending,
168
+ await this._handleRequest(session, _objectSpread(_objectSpread({}, pending), {}, {
144
169
  resourceType,
145
170
  interceptId
146
- });
171
+ }));
147
172
  } else {
148
173
  // track the session that intercepted the request
149
- this.#intercepts.set(requestId, {
150
- ...event,
174
+ this.#intercepts.set(requestId, _objectSpread(_objectSpread({}, event), {}, {
151
175
  session
152
- });
176
+ }));
153
177
  }
154
178
  };
155
179
 
@@ -173,11 +197,10 @@ export class Network {
173
197
  requestId: interceptId,
174
198
  resourceType
175
199
  } = intercept;
176
- await this._handleRequest(session, {
177
- ...event,
200
+ await this._handleRequest(session, _objectSpread(_objectSpread({}, event), {}, {
178
201
  resourceType,
179
202
  interceptId
180
- });
203
+ }));
181
204
  this.#intercepts.delete(requestId);
182
205
  }
183
206
  }
@@ -222,7 +245,7 @@ export class Network {
222
245
  if (!request) return;
223
246
  request.response = response;
224
247
  request.response.buffer = async () => {
225
- let result = await session.send('Network.getResponseBody', {
248
+ let result = await this.send(session, 'Network.getResponseBody', {
226
249
  requestId
227
250
  });
228
251
  return Buffer.from(result.body, result.base64Encoded ? 'base64' : 'utf-8');
@@ -253,13 +276,21 @@ export class Network {
253
276
  /* istanbul ignore if: race condition paranioa */
254
277
  if (!request) return;
255
278
 
256
- // do not log generic messages since the real error was likely logged elsewhere
257
- if (event.errorText !== 'net::ERR_FAILED') {
279
+ // If request was aborted, keep track of it as we need to cancel any in process callbacks for
280
+ // such a request to avoid Invalid InterceptionId errors
281
+ // Note: 404s also show up under ERR_ABORTED and not ERR_FAILED
282
+ if (event.errorText === 'net::ERR_ABORTED') {
283
+ let message = `Request aborted for ${request.url}: ${event.errorText}`;
284
+ this.log.debug(message, _objectSpread(_objectSpread({}, this.meta), {}, {
285
+ url: request.url
286
+ }));
287
+ this.#aborted.add(request.requestId);
288
+ } else if (event.errorText !== 'net::ERR_FAILED') {
289
+ // do not log generic messages since the real error was likely logged elsewhere
258
290
  let message = `Request failed for ${request.url}: ${event.errorText}`;
259
- this.log.debug(message, {
260
- ...this.meta,
291
+ this.log.debug(message, _objectSpread(_objectSpread({}, this.meta), {}, {
261
292
  url: request.url
262
- });
293
+ }));
263
294
  }
264
295
  this._forgetRequest(request);
265
296
  };
@@ -285,22 +316,22 @@ async function sendResponseResource(network, request, session) {
285
316
  } = network.intercept;
286
317
  let log = network.log;
287
318
  let url = originURL(request);
288
- let meta = {
289
- ...network.meta,
319
+ let meta = _objectSpread(_objectSpread({}, network.meta), {}, {
290
320
  url
291
- };
321
+ });
322
+ let send = (method, params) => network.send(session, method, params);
292
323
  try {
293
324
  let resource = network.intercept.getResource(url);
294
325
  network.log.debug(`Handling request: ${url}`, meta);
295
326
  if (!(resource !== null && resource !== void 0 && resource.root) && hostnameMatches(disallowedHostnames, url)) {
296
327
  log.debug('- Skipping disallowed hostname', meta);
297
- await session.send('Fetch.failRequest', {
328
+ await send('Fetch.failRequest', {
298
329
  requestId: request.interceptId,
299
330
  errorReason: 'Aborted'
300
331
  });
301
332
  } else if (resource && (resource.root || resource.provided || !disableCache)) {
302
333
  log.debug(resource.root ? '- Serving root resource' : '- Resource cache hit', meta);
303
- await session.send('Fetch.fulfillRequest', {
334
+ await send('Fetch.fulfillRequest', {
304
335
  requestId: request.interceptId,
305
336
  responseCode: resource.status || 200,
306
337
  body: Buffer.from(resource.content).toString('base64'),
@@ -310,18 +341,35 @@ async function sendResponseResource(network, request, session) {
310
341
  }))
311
342
  });
312
343
  } else {
313
- await session.send('Fetch.continueRequest', {
344
+ await send('Fetch.continueRequest', {
314
345
  requestId: request.interceptId
315
346
  });
316
347
  }
317
348
  } catch (error) {
318
349
  /* istanbul ignore next: too hard to test (create race condition) */
319
350
  if (session.closing && error.message.includes('close')) return;
351
+
352
+ // if failure is due to an already aborted request, ignore it
353
+ // due to race condition we might get aborted event later and see a `Invalid InterceptionId`
354
+ // error before, in which case we should wait for a tick and check again
355
+ // Note: its not a necessity that we would get aborted callback in a tick, its just that if we
356
+ // already have it then we can safely ignore this error
357
+ // Its very hard to test it as this function should be called and request should get cancelled before
358
+ if (error.message === ABORTED_MESSAGE || error.message.includes('Invalid InterceptionId')) {
359
+ // defer this to the end of queue to make sure that any incoming aborted messages were
360
+ // handled and network.#aborted is updated
361
+ await new Promise((res, _) => process.nextTick(res));
362
+ /* istanbul ignore else: too hard to create race where abortion event is delayed */
363
+ if (network.isAborted(request.requestId)) {
364
+ log.debug(`Ignoring further steps for ${url} as request was aborted by the browser.`);
365
+ return;
366
+ }
367
+ }
320
368
  log.debug(`Encountered an error handling request: ${url}`, meta);
321
369
  log.debug(error);
322
370
 
323
371
  /* istanbul ignore next: catch race condition */
324
- await session.send('Fetch.failRequest', {
372
+ await send('Fetch.failRequest', {
325
373
  requestId: request.interceptId,
326
374
  errorReason: 'Failed'
327
375
  }).catch(e => log.debug(e, meta));
@@ -331,9 +379,7 @@ async function sendResponseResource(network, request, session) {
331
379
  // Make a new request with Node based on a network request
332
380
  function makeDirectRequest(network, request) {
333
381
  var _network$authorizatio;
334
- let headers = {
335
- ...request.headers
336
- };
382
+ let headers = _objectSpread({}, request.headers);
337
383
  if ((_network$authorizatio = network.authorization) !== null && _network$authorizatio !== void 0 && _network$authorizatio.username) {
338
384
  // include basic authorization username and password
339
385
  let {
@@ -359,10 +405,9 @@ async function saveResponseResource(network, request) {
359
405
  let log = network.log;
360
406
  let url = originURL(request);
361
407
  let response = request.response;
362
- let meta = {
363
- ...network.meta,
408
+ let meta = _objectSpread(_objectSpread({}, network.meta), {}, {
364
409
  url
365
- };
410
+ });
366
411
  let resource = network.intercept.getResource(url);
367
412
  if (!resource || !resource.root && !resource.provided && disableCache) {
368
413
  try {
package/dist/page.js CHANGED
@@ -1,3 +1,12 @@
1
+ const _excluded = ["waitForTimeout", "waitForSelector", "execute"];
2
+ function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
3
+ function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
4
+ function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
5
+ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
6
+ function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
7
+ function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); }
8
+ function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
9
+ function _objectDestructuringEmpty(obj) { if (obj == null) throw new TypeError("Cannot destructure " + obj); }
1
10
  import fs from 'fs';
2
11
  import logger from '@percy/logger';
3
12
  import Network from './network.js';
@@ -7,9 +16,10 @@ export class Page {
7
16
  static TIMEOUT = undefined;
8
17
  log = logger('core:page');
9
18
  constructor(session, options) {
19
+ var _options$enableJavaSc;
10
20
  this.session = session;
11
21
  this.browser = session.browser;
12
- this.enableJavaScript = options.enableJavaScript ?? true;
22
+ this.enableJavaScript = (_options$enableJavaSc = options.enableJavaScript) !== null && _options$enableJavaSc !== void 0 ? _options$enableJavaSc : true;
13
23
  this.network = new Network(this, options);
14
24
  this.meta = options.meta;
15
25
  this._initializeLoadTimeout();
@@ -53,9 +63,8 @@ export class Page {
53
63
  let defaultDomain = hostname(url);
54
64
  await this.session.send('Network.setCookies', {
55
65
  // spread is used to make a shallow copy of the cookie
56
- cookies: this.session.browser.cookies.map(({
57
- ...cookie
58
- }) => {
66
+ cookies: this.session.browser.cookies.map(_ref => {
67
+ let cookie = Object.assign({}, (_objectDestructuringEmpty(_ref), _ref));
59
68
  if (!cookie.url) cookie.domain || (cookie.domain = defaultDomain);
60
69
  return cookie;
61
70
  })
@@ -125,22 +134,22 @@ export class Page {
125
134
  async evaluate(scripts) {
126
135
  var _scripts;
127
136
  if (!((_scripts = scripts && (scripts = [].concat(scripts))) !== null && _scripts !== void 0 && _scripts.length)) return;
128
- this.log.debug('Evaluate JavaScript', {
129
- ...this.meta,
137
+ this.log.debug('Evaluate JavaScript', _objectSpread(_objectSpread({}, this.meta), {}, {
130
138
  scripts
131
- });
139
+ }));
132
140
  for (let script of scripts) await this.eval(script);
133
141
  }
134
142
 
135
143
  // Takes a snapshot after waiting for any timeout, waiting for any selector, executing any
136
144
  // scripts, and waiting for the network idle. Returns all other provided snapshot options along
137
145
  // with the captured URL and DOM snapshot.
138
- async snapshot({
139
- waitForTimeout,
140
- waitForSelector,
141
- execute,
142
- ...snapshot
143
- }) {
146
+ async snapshot(_ref2) {
147
+ let {
148
+ waitForTimeout,
149
+ waitForSelector,
150
+ execute
151
+ } = _ref2,
152
+ snapshot = _objectWithoutProperties(_ref2, _excluded);
144
153
  let {
145
154
  name,
146
155
  width,
@@ -193,10 +202,7 @@ export class Page {
193
202
  disableShadowDOM,
194
203
  domTransformation
195
204
  });
196
- return {
197
- ...snapshot,
198
- ...capture
199
- };
205
+ return _objectSpread(_objectSpread({}, snapshot), capture);
200
206
  }
201
207
 
202
208
  // Initialize newly attached pages and iframes with page options