@percy/core 1.31.15-alpha.0 → 1.31.15-beta.0

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/browser.js CHANGED
@@ -10,20 +10,6 @@ import logger from '@percy/logger';
10
10
  import install from './install.js';
11
11
  import Session from './session.js';
12
12
  import Page from './page.js';
13
-
14
- // Chrome features Percy disables for v143 new-headless asset discovery.
15
- const DISABLED_FEATURES = ['Translate',
16
- // suppress translate prompt overlay
17
- 'OptimizationGuideModelDownloading',
18
- // suppress background model fetches
19
- 'IsolateOrigins',
20
- // [headless-only] keep cross-origin sub-resources on the page session for CDP capture
21
- 'site-per-process',
22
- // companion to IsolateOrigins
23
- 'HttpsFirstBalancedModeAutoEnable',
24
- // allow HTTP customer URLs (CI / local dev / staging)
25
- 'LocalNetworkAccessChecks' // allow loopback/RFC1918 sub-resources (Chrome 143 LNA gating)
26
- ];
27
13
  export class Browser extends EventEmitter {
28
14
  log = logger('core:browser');
29
15
  sessions = new Map();
@@ -31,7 +17,9 @@ export class Browser extends EventEmitter {
31
17
  closed = false;
32
18
  #callbacks = new Map();
33
19
  #lastid = 0;
34
- args = [`--disable-features=${DISABLED_FEATURES.join(',')}`,
20
+ args = [
21
+ // disable the translate popup and optimization downloads
22
+ '--disable-features=Translate,OptimizationGuideModelDownloading',
35
23
  // disable several subsystems which run network requests in the background
36
24
  '--disable-background-networking',
37
25
  // disable task throttling of timer tasks from background pages
@@ -330,8 +318,8 @@ export class Browser extends EventEmitter {
330
318
  let match = chunk.match(/^DevTools listening on (ws:\/\/.*)$/m);
331
319
  if (match) cleanup(() => resolve(match[1]));
332
320
  };
333
- let handleExitClose = () => handleError(new Error('Browser exited before devtools address'));
334
- let handleError = error => cleanup(() => reject(new Error(`Failed to launch browser. ${error.message}\n${stderr}'\n\n`)));
321
+ let handleExitClose = () => handleError();
322
+ let handleError = error => cleanup(() => reject(new Error(`Failed to launch browser. ${(error === null || error === void 0 ? void 0 : error.message) ?? ''}\n${stderr}'\n\n`)));
335
323
  let cleanup = callback => {
336
324
  clearTimeout(timeoutId);
337
325
  this.process.stderr.off('data', handleData);
package/dist/install.js CHANGED
@@ -162,14 +162,13 @@ export function chromium({
162
162
  });
163
163
  }
164
164
 
165
- // Chrome 143.0.7499.169 (base position 1536371) — closest per-platform
166
- // revision from https://commondatastorage.googleapis.com/chromium-browser-snapshots/index.html
165
+ // default chromium revisions corresponds to v126.0.6478.184
167
166
  chromium.revisions = {
168
- linux: '1536366',
169
- win64: '1536376',
170
- win32: '1536377',
171
- darwin: '1536380',
172
- darwinArm: '1536376'
167
+ linux: '1300309',
168
+ win64: '1300297',
169
+ win32: '1300295',
170
+ darwin: '1300293',
171
+ darwinArm: '1300314'
173
172
  };
174
173
 
175
174
  // export the namespace by default
package/dist/network.js CHANGED
@@ -6,12 +6,6 @@ const MAX_RESOURCE_SIZE = 25 * 1024 ** 2 * 0.63; // 25MB, 0.63 factor for accoun
6
6
  const ALLOWED_STATUSES = [200, 201, 301, 302, 304, 307, 308];
7
7
  const ALLOWED_RESOURCES = ['Document', 'Stylesheet', 'Image', 'Media', 'Font', 'Other'];
8
8
  const ABORTED_MESSAGE = 'Request was aborted by browser';
9
- // Chrome 143 omits Network.responseReceived for worker scripts; cap the wait
10
- // so loadingFinished can clean up. Per-request — N timeouts accumulate to N*2s;
11
- // PERCY_NETWORK_IDLE_WAIT_TIMEOUT (default 30s) caps cumulative impact.
12
- const RESPONSE_RECEIVED_TIMEOUT = 2000;
13
- // Cap idle() impact when a host accepts the TCP connection then stalls during a direct fetch.
14
- const DIRECT_FETCH_TIMEOUT = 5000;
15
9
 
16
10
  // Stable, machine-readable codes for abort errors thrown from this module.
17
11
  // Consumers should prefer `error.code` over string matching on `error.message`.
@@ -67,14 +61,14 @@ export class Network {
67
61
  #aborted = new Set();
68
62
  #finishedUrls = new Set();
69
63
  constructor(page, options) {
70
- var _page$session$browser;
71
64
  this.page = page;
72
65
  this.timeout = options.networkIdleTimeout ?? 100;
73
66
  this.authorization = options.authorization;
74
67
  this.requestHeaders = options.requestHeaders ?? {};
75
68
  this.captureMockedServiceWorker = options.captureMockedServiceWorker ?? false;
76
- this.userAgent = options.userAgent ?? (// by default, emulate a non-headless browser
77
- (_page$session$browser = page.session.browser) === null || _page$session$browser === void 0 || (_page$session$browser = _page$session$browser.version) === null || _page$session$browser === void 0 || (_page$session$browser = _page$session$browser.userAgent) === null || _page$session$browser === void 0 ? void 0 : _page$session$browser.replace('Headless', ''));
69
+ this.userAgent = options.userAgent ??
70
+ // by default, emulate a non-headless browser
71
+ page.session.browser.version.userAgent.replace('Headless', '');
78
72
  this.fontDomains = options.fontDomains || [];
79
73
  this.intercept = options.intercept;
80
74
  this.meta = options.meta;
@@ -252,13 +246,6 @@ export class Network {
252
246
  resourceType
253
247
  } = event;
254
248
 
255
- // Response-stage events arrive here when Fetch.continueRequest was called
256
- // with interceptResponse:true (see sendResponseResource).
257
- if (event.responseStatusCode != null || event.responseErrorReason != null) {
258
- await this._handleResponsePaused(session, event);
259
- return;
260
- }
261
-
262
249
  // wait for request to be sent
263
250
  await this.#requestsLifeCycleHandler.get(requestId).requestWillBeSent;
264
251
  let pending = this.#pending.get(requestId);
@@ -273,88 +260,6 @@ export class Network {
273
260
  }));
274
261
  };
275
262
 
276
- // Response-stage interception is kept ONLY to detect oversized/malformed
277
- // Content-Length and abort the request before Chrome streams a body it
278
- // would never terminate (Chrome 143 quirk). For everything else we just
279
- // continue — body capture happens later via Network.loadingFinished →
280
- // Network.getResponseBody (the v126 path). Reading the body at this stage
281
- // hangs worker-initiated fetches, so we don't.
282
- _handleResponsePaused = async (session, event) => {
283
- var _event$request;
284
- let {
285
- networkId: requestId,
286
- requestId: interceptId,
287
- responseHeaders,
288
- responseStatusCode
289
- } = event;
290
- // request may be undefined when a response-stage pause arrives for a request
291
- // whose request-stage tracking we never installed (service-worker-fulfilled,
292
- // or a cleanup race). We still need to unpause Chrome regardless.
293
- let request = this.#requests.get(requestId);
294
- let url = request ? originURL(request) : ((_event$request = event.request) === null || _event$request === void 0 ? void 0 : _event$request.url) && normalizeURL(event.request.url);
295
- let headersObj = headersArrayToObject(responseHeaders);
296
- let {
297
- tooLarge,
298
- malformed,
299
- rawValue
300
- } = inspectContentLength(headersObj);
301
- if (tooLarge || malformed) {
302
- let meta = {
303
- ...this.meta,
304
- url,
305
- responseStatus: responseStatusCode
306
- };
307
- logAssetInstrumentation(this.log, 'asset_not_uploaded', 'resource_too_large', {
308
- url,
309
- size: rawValue,
310
- snapshot: meta.snapshot
311
- });
312
- this.log.debug('- Skipping resource larger than 25MB', meta);
313
-
314
- // Disposition first, then forget the request — so we never leave Chrome's
315
- // Fetch state paused while Percy thinks the request is already done.
316
- try {
317
- await this.send(session, 'Fetch.failRequest', {
318
- requestId: interceptId,
319
- errorReason: 'Aborted'
320
- });
321
- } catch (error) {
322
- if (error.message === ABORTED_MESSAGE || error.message.includes('Invalid InterceptionId')) {
323
- // benign race — request was already aborted upstream; nothing to un-pause
324
- } else {
325
- this.log.debug(`Failed to abort oversized response for ${url}: ${error.message}`);
326
- // Last-resort: un-pause Chrome's Fetch so it doesn't leak the response.
327
- try {
328
- await this.send(session, 'Fetch.continueResponse', {
329
- requestId: interceptId
330
- });
331
- } catch (continueError) {
332
- this.log.debug(`Last-resort continueResponse also failed for ${url}: ${continueError.message}`);
333
- }
334
- }
335
- }
336
- if (request) {
337
- this._forgetRequest(request);
338
- this.#requestsLifeCycleHandler.get(requestId).resolveResponseReceived();
339
- }
340
- return;
341
- }
342
- return this._continueResponse(session, interceptId, url);
343
- };
344
-
345
- // Tell the browser to continue the paused response, swallowing expected
346
- // races (request already aborted, interception ID no longer valid).
347
- _continueResponse = async (session, interceptId, url) => {
348
- try {
349
- await this.send(session, 'Fetch.continueResponse', {
350
- requestId: interceptId
351
- });
352
- } catch (error) {
353
- if (error.message === ABORTED_MESSAGE || error.message.includes('Invalid InterceptionId')) return;
354
- this.log.debug(`Failed to continue response for ${url}: ${error.message}`);
355
- }
356
- };
357
-
358
263
  // Called when a request will be sent. If the request has already been intercepted, handle it;
359
264
  // otherwise set it to be pending until it is paused.
360
265
  _handleRequestWillBeSent = async event => {
@@ -466,36 +371,11 @@ export class Network {
466
371
  let {
467
372
  requestId
468
373
  } = event;
374
+ // wait for upto 2 seconds or check if response has been sent
375
+ await this.#requestsLifeCycleHandler.get(requestId).responseReceived;
469
376
  let request = this.#requests.get(requestId);
470
377
  /* istanbul ignore if: race condition paranoia */
471
378
  if (!request) return;
472
- if (!request.response) {
473
- let timerId;
474
- await Promise.race([this.#requestsLifeCycleHandler.get(requestId).responseReceived, new Promise(resolve => {
475
- timerId = setTimeout(resolve, RESPONSE_RECEIVED_TIMEOUT);
476
- })]);
477
- clearTimeout(timerId);
478
- }
479
- if (!request.response) {
480
- this.log.debug(`Skipping resource: responseReceived not received within ${RESPONSE_RECEIVED_TIMEOUT}ms - ${request.url}`);
481
- // Chrome 143+ PlzDedicatedWorker: dedicated worker scripts fetch in the browser
482
- // process and never surface a CDP response. resourceType varies ('Other' on v143,
483
- // 'Script' on older Chrome) so we gate on hostname rather than type, and mirror
484
- // sendResponseResource's disallowedHostnames-before-allowedHostnames precedence.
485
- let url = originURL(request);
486
- /* istanbul ignore else: the else only fires for PlzDedicatedWorker requests
487
- whose worker-script fetch bypasses Fetch.requestPaused. Cross-origin assets
488
- loaded via the document session still go through sendResponseResource
489
- (which performs its own disallowedHostnames check), so the test harness
490
- can't reliably reach this skip branch via integration tests. */
491
- if (!hostnameMatches(this.intercept.disallowedHostnames, url) && hostnameMatches(this.intercept.allowedHostnames, url)) {
492
- await captureResourceDirectly(this, request, session);
493
- } else {
494
- this.log.debug(`- Skipping direct-fetch fallback for ${url}: hostname not allowed`, this.meta);
495
- }
496
- this._forgetRequest(request);
497
- return;
498
- }
499
379
  await saveResponseResource(this, request, session);
500
380
  this._forgetRequest(request);
501
381
  };
@@ -574,7 +454,7 @@ export class Network {
574
454
  _initializeNetworkIdleWaitTimeout() {
575
455
  // Per-instance timeout so concurrent pages with different env values
576
456
  // (or env values changed mid-run by tests) don't stomp each other.
577
- this.networkIdleWaitTimeout = parseInt(process.env.PERCY_NETWORK_IDLE_WAIT_TIMEOUT, 10) || 30000;
457
+ this.networkIdleWaitTimeout = parseInt(process.env.PERCY_NETWORK_IDLE_WAIT_TIMEOUT) || 30000;
578
458
  if (this.networkIdleWaitTimeout > 60000) {
579
459
  this.log.warn('Setting PERCY_NETWORK_IDLE_WAIT_TIMEOUT over 60000ms is not recommended. ' + 'If your page needs more than 60000ms to idle due to CPU/Network load, ' + 'its recommended to increase CI resources where this cli is running.');
580
460
  }
@@ -613,31 +493,6 @@ function originURL(request) {
613
493
  return normalizeURL((request.redirectChain[0] || request).url);
614
494
  }
615
495
 
616
- // Convert Fetch event responseHeaders ([{name, value}, …]) to a header object.
617
- function headersArrayToObject(arr) {
618
- let out = {};
619
- if (!Array.isArray(arr)) return out;
620
- for (let {
621
- name,
622
- value
623
- } of arr) out[name] = value;
624
- return out;
625
- }
626
-
627
- // Returns { tooLarge, malformed, rawValue } for Content-Length classification.
628
- function inspectContentLength(headers) {
629
- let key = headers && Object.keys(headers).find(k => k.toLowerCase() === 'content-length');
630
- let rawValue = key ? headers[key] : undefined;
631
- let parsed = parseInt(rawValue, 10);
632
- let tooLarge = Number.isFinite(parsed) && parsed > MAX_RESOURCE_SIZE;
633
- let malformed = rawValue !== undefined && rawValue !== null && String(rawValue).length > 0 && !Number.isFinite(parsed);
634
- return {
635
- tooLarge,
636
- malformed,
637
- rawValue
638
- };
639
- }
640
-
641
496
  // Validate domain for auto-allowlisting feature
642
497
  // Only validates domains that returned 200 status
643
498
  async function validateDomainForAllowlist(network, hostname, url, statusCode) {
@@ -719,10 +574,8 @@ async function sendResponseResource(network, request, session) {
719
574
  }))
720
575
  });
721
576
  } else {
722
- // interceptResponse:true triggers a second pause at the response stage. See _handleResponsePaused.
723
577
  await send('Fetch.continueRequest', {
724
- requestId: request.interceptId,
725
- interceptResponse: true
578
+ requestId: request.interceptId
726
579
  });
727
580
  }
728
581
  } catch (error) {
@@ -756,68 +609,21 @@ async function sendResponseResource(network, request, session) {
756
609
  }
757
610
  }
758
611
 
759
- // Pick the CDP session for Network.getCookies. Worker/auxiliary sessions
760
- // expose a partial Network domain where Network.getCookies throws
761
- // "Internal error", so prefer the page's session whenever available and
762
- // fall back to the request's own session otherwise.
763
- export function pickCookieSession(network, session) {
764
- var _network$page;
765
- return ((_network$page = network.page) === null || _network$page === void 0 ? void 0 : _network$page.session) ?? session;
766
- }
767
-
768
- // Decide whether to attach a Basic auth header to the Node-side direct fetch.
769
- // The browser's URLLoader origin-scopes Basic auth; this fallback runs in
770
- // Node, so we re-enforce the same-origin rule explicitly to avoid leaking
771
- // credentials cross-origin. Malformed URLs fall through to `false` defensively.
772
- export function shouldAttachAuth(authorization, requestUrl, snapshotUrl) {
773
- if (!(authorization !== null && authorization !== void 0 && authorization.username)) return false;
774
- try {
775
- return new URL(requestUrl).origin === new URL(snapshotUrl).origin;
776
- } catch {
777
- return false;
778
- }
779
- }
780
-
781
- // Race a promise against a timeout. Resolves with the promise's value if it
782
- // settles within `ms`, otherwise rejects with `new Error(message)`. The
783
- // internal timer is always cleared so the event loop can exit cleanly.
784
- export function raceWithTimeout(promise, ms, message) {
785
- let timerId;
786
- return Promise.race([promise, new Promise((_, reject) => {
787
- timerId = setTimeout(() => reject(new Error(message)), ms);
788
- })]).finally(() => clearTimeout(timerId));
789
- }
790
-
791
- // Server Content-Type wins; URL-extension mime is the fallback; binary default last.
792
- export function resolveDirectFetchMime(responseHeaders, urlForLookup) {
793
- var _responseHeaders$cont;
794
- let serverMime = responseHeaders === null || responseHeaders === void 0 || (_responseHeaders$cont = responseHeaders['content-type']) === null || _responseHeaders$cont === void 0 ? void 0 : _responseHeaders$cont.split(';')[0].trim();
795
- return serverMime || mime.lookup(urlForLookup) || 'application/octet-stream';
796
- }
797
-
798
- // Make a new request with Node based on a network request. Cookies are read
799
- // from the page session because worker/auxiliary sessions have a partial
800
- // Network domain where Network.getCookies throws "Internal error".
612
+ // Make a new request with Node based on a network request
801
613
  async function makeDirectRequest(network, request, session) {
802
- var _network$meta;
803
- let cookies = [];
804
- let cookieSession = pickCookieSession(network, session);
805
- try {
806
- ({
807
- cookies
808
- } = await cookieSession.send('Network.getCookies', {
809
- urls: [request.url]
810
- }));
811
- } catch (error) {
812
- network.log.debug(`Network.getCookies unavailable for ${request.url}: ${error.message}`);
813
- }
614
+ var _network$authorizatio;
615
+ const {
616
+ cookies
617
+ } = await session.send('Network.getCookies', {
618
+ urls: [request.url]
619
+ });
814
620
  let headers = {
815
621
  // add default browser
816
622
  accept: '*/*',
817
623
  'sec-fetch-site': 'same-origin',
818
624
  'sec-fetch-mode': 'cors',
819
625
  'sec-fetch-dest': 'font',
820
- 'sec-ch-ua': '"Chromium";v="143", "Google Chrome";v="143", "Not?A_Brand";v="99"',
626
+ 'sec-ch-ua': '"Chromium";v="123", "Google Chrome";v="123", "Not?A_Brand";v="99"',
821
627
  'sec-ch-ua-mobile': '?0',
822
628
  'sec-ch-ua-platform': '"macOS"',
823
629
  'sec-fetch-user': '?1',
@@ -826,7 +632,8 @@ async function makeDirectRequest(network, request, session) {
826
632
  // add applicable cookies
827
633
  cookie: cookies.map(cookie => `${cookie.name}=${cookie.value}`).join('; ')
828
634
  };
829
- if (shouldAttachAuth(network.authorization, request.url, (_network$meta = network.meta) === null || _network$meta === void 0 ? void 0 : _network$meta.snapshotURL)) {
635
+ if ((_network$authorizatio = network.authorization) !== null && _network$authorizatio !== void 0 && _network$authorizatio.username) {
636
+ // include basic authorization username and password
830
637
  let {
831
638
  username,
832
639
  password
@@ -837,56 +644,12 @@ async function makeDirectRequest(network, request, session) {
837
644
  return makeRequest(request.url, {
838
645
  buffer: true,
839
646
  headers
840
- }, (body, res) => ({
841
- body,
842
- status: res.statusCode,
843
- headers: res.headers
844
- }));
845
- }
846
-
847
- // Capture a resource via direct HTTP fetch when the browser-side response
848
- // never surfaces — Chrome 143+ fetches dedicated worker scripts in the browser
849
- // process (PlzDedicatedWorker) so loadingFinished fires without a body on CDP.
850
- async function captureResourceDirectly(network, request, session) {
851
- let log = network.log;
852
- let url = originURL(request);
853
- let meta = {
854
- ...network.meta,
855
- url
856
- };
857
- try {
858
- log.debug('- Requesting resource directly (responseReceived timeout fallback)', meta);
859
- let {
860
- body,
861
- status,
862
- headers: responseHeaders
863
- } = await raceWithTimeout(makeDirectRequest(network, request, session), DIRECT_FETCH_TIMEOUT, `Direct fetch timed out after ${DIRECT_FETCH_TIMEOUT}ms`);
864
- if (body.length > MAX_RESOURCE_SIZE) {
865
- logAssetInstrumentation(log, 'asset_not_uploaded', 'resource_too_large', {
866
- url,
867
- size: body.length,
868
- snapshot: meta.snapshot
869
- });
870
- log.debug('- Skipping resource larger than 25MB', meta);
871
- return;
872
- }
873
- let urlObj = new URL(url);
874
- let mimeType = resolveDirectFetchMime(responseHeaders, urlObj.origin + urlObj.pathname);
875
- let resource = createResource(url, body, mimeType, {
876
- status,
877
- headers: {
878
- 'content-type': [mimeType]
879
- }
880
- });
881
- log.debug(`- Saving direct-fetched resource sha=${resource.sha} mimetype=${mimeType}`, meta);
882
- network.intercept.saveResource(resource);
883
- } catch (error) {
884
- log.debug(`Direct fetch failed for ${url} - ${error.message}`, meta);
885
- }
647
+ });
886
648
  }
887
649
 
888
650
  // Save a resource from a request, skipping it if specific parameters are not met
889
651
  async function saveResponseResource(network, request, session) {
652
+ var _response$headers;
890
653
  let {
891
654
  disableCache,
892
655
  allowedHostnames,
@@ -900,8 +663,19 @@ async function saveResponseResource(network, request, session) {
900
663
  url,
901
664
  responseStatus: response === null || response === void 0 ? void 0 : response.status
902
665
  };
903
- // Oversized/malformed Content-Length is rejected earlier in _handleResponsePaused;
904
- // the body.length check below still guards cached responses where headers may lie.
666
+ // Checking for content length more than 100MB, to prevent websocket error which is governed by
667
+ // maxPayload option of websocket defaulted to 100MB.
668
+ // If content-length is more than our allowed 25MB, no need to process that resouce we can return log.
669
+ let contentLength = (_response$headers = response.headers) === null || _response$headers === void 0 ? void 0 : _response$headers[Object.keys(response.headers).find(key => key.toLowerCase() === 'content-length')];
670
+ contentLength = parseInt(contentLength);
671
+ if (contentLength > MAX_RESOURCE_SIZE) {
672
+ logAssetInstrumentation(log, 'asset_not_uploaded', 'resource_too_large', {
673
+ url,
674
+ size: contentLength,
675
+ snapshot: meta.snapshot
676
+ });
677
+ return log.debug('- Skipping resource larger than 25MB', meta);
678
+ }
905
679
  let resource = network.intercept.getResource(url);
906
680
  if (!resource || !resource.root && !resource.provided && disableCache) {
907
681
  try {
@@ -993,9 +767,7 @@ async function saveResponseResource(network, request, session) {
993
767
  // so request them directly.
994
768
  if ((_mimeType = mimeType) !== null && _mimeType !== void 0 && _mimeType.includes('font') || detectedMime && detectedMime.includes('font')) {
995
769
  log.debug('- Requesting asset directly', meta);
996
- ({
997
- body
998
- } = await makeDirectRequest(network, request, session));
770
+ body = await makeDirectRequest(network, request, session);
999
771
  log.debug('- Got direct response', meta);
1000
772
  }
1001
773
  resource = createResource(url, body, mimeType, {
package/dist/percy.js CHANGED
@@ -73,7 +73,7 @@ export class Percy {
73
73
  // options which will become accessible via the `.config` property
74
74
  ..._options
75
75
  } = {}) {
76
- var _config$percy, _config$percy2;
76
+ var _config$percy, _config$percy2, _config$percy3;
77
77
  _classPrivateMethodInitSpec(this, _Percy_brand);
78
78
  _defineProperty(this, "log", logger('core'));
79
79
  _defineProperty(this, "readyState", null);
@@ -85,6 +85,7 @@ export class Percy {
85
85
  });
86
86
  labels ?? (labels = (_config$percy = config.percy) === null || _config$percy === void 0 ? void 0 : _config$percy.labels);
87
87
  deferUploads ?? (deferUploads = (_config$percy2 = config.percy) === null || _config$percy2 === void 0 ? void 0 : _config$percy2.deferUploads);
88
+ archiveDir ?? (archiveDir = (_config$percy3 = config.percy) === null || _config$percy3 === void 0 ? void 0 : _config$percy3.archiveDir);
88
89
  if (archiveDir) skipUploads = skipUploads != null ? skipUploads : true;
89
90
  this.config = config;
90
91
  this.cliStartTime = null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@percy/core",
3
- "version": "1.31.15-alpha.0",
3
+ "version": "1.31.15-beta.0",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -9,7 +9,7 @@
9
9
  },
10
10
  "publishConfig": {
11
11
  "access": "public",
12
- "tag": "alpha"
12
+ "tag": "beta"
13
13
  },
14
14
  "engines": {
15
15
  "node": ">=14"
@@ -44,12 +44,12 @@
44
44
  "test:types": "tsd"
45
45
  },
46
46
  "dependencies": {
47
- "@percy/client": "1.31.15-alpha.0",
48
- "@percy/config": "1.31.15-alpha.0",
49
- "@percy/dom": "1.31.15-alpha.0",
50
- "@percy/logger": "1.31.15-alpha.0",
51
- "@percy/monitoring": "1.31.15-alpha.0",
52
- "@percy/webdriver-utils": "1.31.15-alpha.0",
47
+ "@percy/client": "1.31.15-beta.0",
48
+ "@percy/config": "1.31.15-beta.0",
49
+ "@percy/dom": "1.31.15-beta.0",
50
+ "@percy/logger": "1.31.15-beta.0",
51
+ "@percy/monitoring": "1.31.15-beta.0",
52
+ "@percy/webdriver-utils": "1.31.15-beta.0",
53
53
  "content-disposition": "^0.5.4",
54
54
  "cross-spawn": "^7.0.3",
55
55
  "extract-zip": "^2.0.1",
@@ -63,7 +63,7 @@
63
63
  "yaml": "^2.4.1"
64
64
  },
65
65
  "optionalDependencies": {
66
- "@percy/cli-doctor": "1.31.15-alpha.0"
66
+ "@percy/cli-doctor": "1.31.15-beta.0"
67
67
  },
68
- "gitHead": "726ab78fcd37479a0904996a88400a0b6626daac"
68
+ "gitHead": "b2012ce5dae37e5009dd3f4190454bb9d9d118e3"
69
69
  }
@@ -28,14 +28,8 @@ export function createTestServer({ default: defaultReply, ...replies }, port = 8
28
28
  server.route(async (req, res, next) => {
29
29
  let pathname = req.url.pathname;
30
30
  if (req.url.search) pathname += req.url.search;
31
- let reply = replies[pathname] || defaultReply;
32
- // Chrome >=128 auto-fetches /favicon.ico on every navigation; reply 204
33
- // by default so it doesn't pollute snapshot resources. Tests can still
34
- // override via `server.reply('/favicon.ico', ...)`.
35
- if (req.url.pathname === '/favicon.ico' && !replies['/favicon.ico']) {
36
- return res.writeHead(204).end();
37
- }
38
31
  server.requests.push(req.body ? [pathname, req.body, req.headers] : [pathname, req.headers]);
32
+ let reply = replies[pathname] || defaultReply;
39
33
  return reply ? await reply(req, res) : next();
40
34
  });
41
35