@percy/core 1.29.3-beta.1 → 1.29.3

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/discovery.js CHANGED
@@ -389,7 +389,10 @@ export function createDiscoveryQueue(percy) {
389
389
  authorization: snapshot.discovery.authorization,
390
390
  userAgent: snapshot.discovery.userAgent,
391
391
  captureMockedServiceWorker: snapshot.discovery.captureMockedServiceWorker,
392
- meta: snapshot.meta,
392
+ meta: {
393
+ ...snapshot.meta,
394
+ snapshotURL: snapshot.url
395
+ },
393
396
  // enable network inteception
394
397
  intercept: {
395
398
  enableJavaScript: snapshot.enableJavaScript,
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 } from './utils.js';
4
+ import { DefaultMap, createResource, hostnameMatches, normalizeURL, waitFor, decodeAndEncodeURLWithLogging } from './utils.js';
5
5
  const MAX_RESOURCE_SIZE = 25 * 1024 ** 2; // 25MB
6
6
  const ALLOWED_STATUSES = [200, 201, 301, 302, 304, 307, 308];
7
7
  const ALLOWED_RESOURCES = ['Document', 'Stylesheet', 'Image', 'Media', 'Font', 'Other'];
@@ -205,6 +205,7 @@ export class Network {
205
205
  // Called when a request will be sent. If the request has already been intercepted, handle it;
206
206
  // otherwise set it to be pending until it is paused.
207
207
  _handleRequestWillBeSent = async event => {
208
+ var _this$meta;
208
209
  let {
209
210
  requestId,
210
211
  request,
@@ -213,6 +214,17 @@ export class Network {
213
214
 
214
215
  // do not handle data urls
215
216
  if (request.url.startsWith('data:')) return;
217
+
218
+ // Browsers handle URL encoding leniently.
219
+ // This code checks for issues such as `%` and leading spaces and warns the user accordingly.
220
+ decodeAndEncodeURLWithLogging(request.url, this.log, {
221
+ meta: {
222
+ ...this.meta,
223
+ url: request.url
224
+ },
225
+ shouldLogWarning: request.url !== ((_this$meta = this.meta) === null || _this$meta === void 0 ? void 0 : _this$meta.snapshotURL),
226
+ warningMessage: `An invalid URL was detected for url: ${request.url} - the snapshot may fail on Percy. Please verify that your asset URL is valid.`
227
+ });
216
228
  if (this.intercept) {
217
229
  this.#pending.set(requestId, event);
218
230
  if (this.captureMockedServiceWorker) {
package/dist/snapshot.js CHANGED
@@ -3,7 +3,7 @@ import PercyConfig from '@percy/config';
3
3
  import micromatch from 'micromatch';
4
4
  import { configSchema } from './config.js';
5
5
  import Queue from './queue.js';
6
- import { request, hostnameMatches, yieldTo, snapshotLogName } from './utils.js';
6
+ import { request, hostnameMatches, yieldTo, snapshotLogName, decodeAndEncodeURLWithLogging } from './utils.js';
7
7
  import { JobData } from './wait-for-job.js';
8
8
 
9
9
  // Throw a better error message for missing or invalid urls
@@ -17,6 +17,23 @@ function validURL(url, base) {
17
17
  throw new Error(`Invalid snapshot URL: ${e.input}`);
18
18
  }
19
19
  }
20
+ function validateAndFixSnapshotUrl(snapshot) {
21
+ let log = logger('core:snapshot');
22
+ // encoding snapshot url, if contians invalid URI characters/syntax
23
+ let modifiedURL = decodeAndEncodeURLWithLogging(snapshot.url, log, {
24
+ meta: {
25
+ snapshot: {
26
+ name: snapshot.name || snapshot.url
27
+ }
28
+ },
29
+ shouldLogWarning: true,
30
+ warningMessage: `Invalid URL detected for url: ${snapshot.url} - the snapshot may fail on Percy. Please confirm that your website URL is valid.`
31
+ });
32
+ if (modifiedURL !== snapshot.url) {
33
+ log.debug(`Snapshot URL modified to: ${modifiedURL}`);
34
+ snapshot.url = modifiedURL;
35
+ }
36
+ }
20
37
 
21
38
  // used to deserialize regular expression strings
22
39
  const RE_REGEXP = /^\/(.+)\/(\w+)?$/;
@@ -86,6 +103,7 @@ function mapSnapshotOptions(snapshots, context) {
86
103
  if (typeof snapshot === 'string') snapshot = {
87
104
  url: snapshot
88
105
  };
106
+ if (process.env.PERCY_MODIFY_SNAPSHOT_URL !== 'false') validateAndFixSnapshotUrl(snapshot);
89
107
 
90
108
  // normalize the snapshot url and use it for the default name
91
109
  let url = validURL(snapshot.url, context === null || context === void 0 ? void 0 : context.baseUrl);
package/dist/utils.js CHANGED
@@ -379,6 +379,79 @@ export function redactSecrets(data) {
379
379
  export function base64encode(content) {
380
380
  return Buffer.from(content).toString('base64');
381
381
  }
382
+ const RESERVED_CHARACTERS = {
383
+ '%3A': ':',
384
+ '%23': '#',
385
+ '%24': '$',
386
+ '%26': '&',
387
+ '%2B': '+',
388
+ '%2C': ',',
389
+ '%2F': '/',
390
+ '%3B': ';',
391
+ '%3D': '=',
392
+ '%3F': '?',
393
+ '%40': '@'
394
+ };
395
+ function _replaceReservedCharactersWithPlaceholder(url) {
396
+ let result = url;
397
+ let matchedPattern = {};
398
+ let placeHolderCount = 0;
399
+ for (let key of Object.keys(RESERVED_CHARACTERS)) {
400
+ let regex = new RegExp(key, 'g');
401
+ if (regex.test(result)) {
402
+ let placeholder = `__PERCY_PLACEHOLDER_${placeHolderCount}__`;
403
+ result = result.replace(regex, placeholder);
404
+ matchedPattern[placeholder] = key;
405
+ placeHolderCount++;
406
+ }
407
+ }
408
+ return {
409
+ url: result,
410
+ matchedPattern
411
+ };
412
+ }
413
+ function _replacePlaceholdersWithReservedCharacters(matchedPattern, url) {
414
+ let result = url;
415
+ for (let [key, value] of Object.entries(matchedPattern)) {
416
+ let regex = new RegExp(key, 'g');
417
+ result = result.replace(regex, value);
418
+ }
419
+ return result;
420
+ }
421
+
422
+ // This function replaces invalid character that are not the
423
+ // part of valid URI syntax with there correct encoded value.
424
+ // Also, if a character is a part of valid URI syntax, those characters
425
+ // are not encoded
426
+ // Eg: [abc] -> gets encoded to %5Babc%5D
427
+ // ab c -> ab%20c
428
+ export function decodeAndEncodeURLWithLogging(url, logger, options = {}) {
429
+ // In case the url is partially encoded, then directly using encodeURI()
430
+ // will encode those characters again. Therefore decodeURI once helps is decoding
431
+ // partially encoded URL and then after encoding it again, full URL get encoded
432
+ // correctly.
433
+ const {
434
+ meta,
435
+ shouldLogWarning,
436
+ warningMessage
437
+ } = options;
438
+ try {
439
+ let {
440
+ url: placeholderURL,
441
+ matchedPattern
442
+ } = _replaceReservedCharactersWithPlaceholder(url);
443
+ let decodedURL = decodeURI(placeholderURL);
444
+ let encodedURL = encodeURI(decodedURL);
445
+ encodedURL = _replacePlaceholdersWithReservedCharacters(matchedPattern, encodedURL);
446
+ return encodedURL;
447
+ } catch (error) {
448
+ logger.debug(error, meta);
449
+ if (error.name === 'URIError' && shouldLogWarning) {
450
+ logger.warn(warningMessage);
451
+ }
452
+ return url;
453
+ }
454
+ }
382
455
  export function snapshotLogName(name, meta) {
383
456
  var _meta$snapshot;
384
457
  if (meta !== null && meta !== void 0 && (_meta$snapshot = meta.snapshot) !== null && _meta$snapshot !== void 0 && _meta$snapshot.testCase) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@percy/core",
3
- "version": "1.29.3-beta.1",
3
+ "version": "1.29.3",
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": "beta"
12
+ "tag": "latest"
13
13
  },
14
14
  "engines": {
15
15
  "node": ">=14"
@@ -43,11 +43,11 @@
43
43
  "test:types": "tsd"
44
44
  },
45
45
  "dependencies": {
46
- "@percy/client": "1.29.3-beta.1",
47
- "@percy/config": "1.29.3-beta.1",
48
- "@percy/dom": "1.29.3-beta.1",
49
- "@percy/logger": "1.29.3-beta.1",
50
- "@percy/webdriver-utils": "1.29.3-beta.1",
46
+ "@percy/client": "1.29.3",
47
+ "@percy/config": "1.29.3",
48
+ "@percy/dom": "1.29.3",
49
+ "@percy/logger": "1.29.3",
50
+ "@percy/webdriver-utils": "1.29.3",
51
51
  "content-disposition": "^0.5.4",
52
52
  "cross-spawn": "^7.0.3",
53
53
  "extract-zip": "^2.0.1",
@@ -60,5 +60,5 @@
60
60
  "ws": "^8.17.1",
61
61
  "yaml": "^2.4.1"
62
62
  },
63
- "gitHead": "2a7eb67f9432b459594c464d5260b48b11cbea26"
63
+ "gitHead": "f374b2c04840cc2992a43bff837d9740bf30f7b8"
64
64
  }