@percy/core 1.31.6-beta.6 → 1.31.6-beta.8

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/config.js CHANGED
@@ -508,6 +508,24 @@ export const configSchema = {
508
508
  default: true
509
509
  }
510
510
  }
511
+ },
512
+ fontDomains: {
513
+ type: 'array',
514
+ default: [],
515
+ items: {
516
+ type: 'string',
517
+ allOf: [{
518
+ not: {
519
+ pattern: '[^/]/'
520
+ },
521
+ error: 'must not include a pathname'
522
+ }, {
523
+ not: {
524
+ pattern: '^([a-zA-Z]+:)?//'
525
+ },
526
+ error: 'must not include a protocol'
527
+ }]
528
+ }
511
529
  }
512
530
  }
513
531
  }
@@ -635,6 +653,9 @@ export const snapshotSchema = {
635
653
  },
636
654
  scrollToBottom: {
637
655
  $ref: '/config/discovery#/properties/scrollToBottom'
656
+ },
657
+ fontDomains: {
658
+ $ref: '/config/discovery#/properties/fontDomains'
638
659
  }
639
660
  }
640
661
  }
package/dist/discovery.js CHANGED
@@ -492,6 +492,7 @@ export function createDiscoveryQueue(percy) {
492
492
  requestHeaders: snapshot.discovery.requestHeaders,
493
493
  authorization: snapshot.discovery.authorization,
494
494
  userAgent: snapshot.discovery.userAgent,
495
+ fontDomains: snapshot.discovery.fontDomains,
495
496
  captureMockedServiceWorker: snapshot.discovery.captureMockedServiceWorker,
496
497
  meta: {
497
498
  ...snapshot.meta,
package/dist/network.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { request as makeRequest } from '@percy/client/utils';
2
2
  import logger from '@percy/logger';
3
3
  import mime from 'mime-types';
4
- import { DefaultMap, createResource, hostnameMatches, normalizeURL, waitFor, decodeAndEncodeURLWithLogging, handleGoogleFontMimeType } from './utils.js';
4
+ import { DefaultMap, createResource, hostnameMatches, normalizeURL, waitFor, decodeAndEncodeURLWithLogging, handleIncorrectFontMimeType } from './utils.js';
5
5
  const MAX_RESOURCE_SIZE = 25 * 1024 ** 2 * 0.63; // 25MB, 0.63 factor for accounting for base64 encoding
6
6
  const ALLOWED_STATUSES = [200, 201, 301, 302, 304, 307, 308];
7
7
  const ALLOWED_RESOURCES = ['Document', 'Stylesheet', 'Image', 'Media', 'Font', 'Other'];
@@ -38,6 +38,7 @@ export class Network {
38
38
  this.userAgent = options.userAgent ??
39
39
  // by default, emulate a non-headless browser
40
40
  page.session.browser.version.userAgent.replace('Headless', '');
41
+ this.fontDomains = options.fontDomains || [];
41
42
  this.intercept = options.intercept;
42
43
  this.meta = options.meta;
43
44
  this._initializeNetworkIdleWaitTimeout();
@@ -548,7 +549,7 @@ async function saveResponseResource(network, request, session) {
548
549
  response.mimeType === 'text/plain' && detectedMime || response.mimeType;
549
550
 
550
551
  // Handle Google Fonts MIME type detection and override
551
- mimeType = handleGoogleFontMimeType(urlObj, mimeType, body, log, meta);
552
+ mimeType = handleIncorrectFontMimeType(urlObj, mimeType, body, network.fontDomains, meta);
552
553
 
553
554
  // if we detect a font mime, we dont want to override it as different browsers may behave
554
555
  // differently for incorrect mimetype in font response, but we want to treat it as a
package/dist/snapshot.js CHANGED
@@ -152,7 +152,8 @@ function getSnapshotOptions(options, {
152
152
  captureSrcset: config.discovery.captureSrcset,
153
153
  userAgent: config.discovery.userAgent,
154
154
  retry: config.discovery.retry,
155
- scrollToBottom: config.discovery.scrollToBottom
155
+ scrollToBottom: config.discovery.scrollToBottom,
156
+ fontDomains: config.discovery.fontDomains
156
157
  }
157
158
  }, options], (path, prev, next) => {
158
159
  var _next, _next2, _next3;
package/dist/utils.js CHANGED
@@ -26,61 +26,56 @@ export function normalizeURL(url) {
26
26
  return `${protocol}//${host}${pathname}${search}`;
27
27
  }
28
28
 
29
- // Detects font MIME type from file content by checking magic bytes
30
- // Returns the detected MIME type or null if not a recognized font format
29
+ /**
30
+ * Detects font MIME type from file content by checking magic bytes.
31
+ * Handles string-based signatures (WOFF, OTTO) and binary signatures (TTF).
32
+ */
31
33
  export function detectFontMimeType(buffer) {
32
34
  try {
33
- // Ensure buffer has at least 4 bytes
34
35
  if (!buffer || buffer.length < 4) {
35
36
  return null;
36
37
  }
37
38
 
38
- // Read the first 4 bytes as the file header
39
- const header = buffer.slice(0, 4).toString('binary');
40
-
41
- // Check for WOFF signature: 'wOFF'
42
- if (header === 'wOFF') {
43
- return 'font/woff';
44
- }
45
-
46
- // Check for WOFF2 signature: 'wOF2'
47
- if (header === 'wOF2') {
48
- return 'font/woff2';
49
- }
50
-
51
- // Check for TrueType/OpenType signature: 0x00 0x01 0x00 0x00
52
- if (header.charCodeAt(0) === 0x00 && header.charCodeAt(1) === 0x01 && header.charCodeAt(2) === 0x00 && header.charCodeAt(3) === 0x00) {
53
- return 'font/ttf';
54
- }
55
-
56
- // Check for OpenType signature: 'OTTO'
57
- if (header === 'OTTO') {
58
- return 'font/otf';
59
- }
60
-
61
- // Not a recognized font format
62
- return null;
39
+ // Convert the first 4 bytes into two formats for matching:
40
+ // 1. A lowercase string for text-based signatures (e.g., 'otto')
41
+ // 2. A hex string for binary/null-byte signatures (e.g., '00010000')
42
+ const headerString = buffer.slice(0, 4).toString('binary').toLowerCase();
43
+ const headerHex = buffer.slice(0, 4).toString('hex');
44
+ const mimeMap = {
45
+ woff: 'font/woff',
46
+ wof2: 'font/woff2',
47
+ otto: 'font/otf',
48
+ '00010000': 'font/ttf'
49
+ };
50
+
51
+ // Return the match if it exists in our map for either format
52
+ return mimeMap[headerString] || mimeMap[headerHex] || null;
63
53
  } catch (error) {
64
- // Return null on any error during detection
54
+ // Fail silently and return null if buffer reading fails
65
55
  return null;
66
56
  }
67
57
  }
58
+ const KNOWN_PROBLEMATIC_FONT_DOMAINS = ['fonts.gstatic.com'];
59
+ function isKnownFontDomain(hostname, userConfiguredFontDomains = []) {
60
+ return KNOWN_PROBLEMATIC_FONT_DOMAINS.includes(hostname) || userConfiguredFontDomains.includes(hostname);
61
+ }
68
62
 
69
- // Handles Google Fonts MIME type detection and override
63
+ // Handles Inncorrect Fonts MIME type detection and override
70
64
  // Google Fonts sometimes returns font files with text/html mime type
71
65
  // This function detects the actual font format from the file content
72
- export function handleGoogleFontMimeType(urlObj, mimeType, body, log, meta) {
66
+ export function handleIncorrectFontMimeType(urlObj, mimeType, body, userConfiguredFontDomains, meta) {
73
67
  // Check if this is a Google Fonts request with incorrect mime type
74
- let isGoogleFont = urlObj.hostname === 'fonts.gstatic.com';
75
- if (isGoogleFont && mimeType === 'text/html') {
68
+ const log = logger('core:utils');
69
+ let isFontDomain = isKnownFontDomain(urlObj.hostname, userConfiguredFontDomains);
70
+ if (isFontDomain && mimeType === 'text/html') {
76
71
  const detectedFontMime = detectFontMimeType(body);
77
72
  if (detectedFontMime) {
78
73
  mimeType = detectedFontMime;
79
- log.debug(`- Detected Google Font as ${detectedFontMime} from content, overriding mime type`, meta);
74
+ log.warn(`- Detected Google Font as ${detectedFontMime} from content, overriding mime type`, meta);
80
75
  } else {
81
76
  // Fallback to generic font mime type if we can't detect the specific format
82
77
  mimeType = 'application/font-woff2';
83
- log.debug('- Google Font detected but format unclear, treating as font', meta);
78
+ log.warn('- Google Font detected but format unclear, treating as font', meta);
84
79
  }
85
80
  }
86
81
  return mimeType;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@percy/core",
3
- "version": "1.31.6-beta.6",
3
+ "version": "1.31.6-beta.8",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -43,12 +43,12 @@
43
43
  "test:types": "tsd"
44
44
  },
45
45
  "dependencies": {
46
- "@percy/client": "1.31.6-beta.6",
47
- "@percy/config": "1.31.6-beta.6",
48
- "@percy/dom": "1.31.6-beta.6",
49
- "@percy/logger": "1.31.6-beta.6",
50
- "@percy/monitoring": "1.31.6-beta.6",
51
- "@percy/webdriver-utils": "1.31.6-beta.6",
46
+ "@percy/client": "1.31.6-beta.8",
47
+ "@percy/config": "1.31.6-beta.8",
48
+ "@percy/dom": "1.31.6-beta.8",
49
+ "@percy/logger": "1.31.6-beta.8",
50
+ "@percy/monitoring": "1.31.6-beta.8",
51
+ "@percy/webdriver-utils": "1.31.6-beta.8",
52
52
  "content-disposition": "^0.5.4",
53
53
  "cross-spawn": "^7.0.3",
54
54
  "extract-zip": "^2.0.1",
@@ -61,5 +61,5 @@
61
61
  "ws": "^8.17.1",
62
62
  "yaml": "^2.4.1"
63
63
  },
64
- "gitHead": "d324722ebebe9f69066bfc5d726fa69dfcbb2cf8"
64
+ "gitHead": "568a5597976a30d4feec5c8d58de0108bb595be3"
65
65
  }