@percy/core 1.28.2-beta.1 → 1.28.2-beta.2

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
@@ -255,6 +255,10 @@ export const configSchema = {
255
255
  type: 'integer',
256
256
  minimum: 1
257
257
  },
258
+ retry: {
259
+ type: 'boolean',
260
+ default: false
261
+ },
258
262
  launchOptions: {
259
263
  type: 'object',
260
264
  additionalProperties: false,
@@ -357,6 +361,9 @@ export const snapshotSchema = {
357
361
  },
358
362
  devicePixelRatio: {
359
363
  $ref: '/config/discovery#/properties/devicePixelRatio'
364
+ },
365
+ retry: {
366
+ $ref: '/config/discovery#/properties/retry'
360
367
  }
361
368
  }
362
369
  }
package/dist/discovery.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import logger from '@percy/logger';
2
2
  import Queue from './queue.js';
3
- import { normalizeURL, hostnameMatches, createResource, createRootResource, createPercyCSSResource, createLogResource, yieldAll, snapshotLogName } from './utils.js';
3
+ import { normalizeURL, hostnameMatches, createResource, createRootResource, createPercyCSSResource, createLogResource, yieldAll, snapshotLogName, withRetries } from './utils.js';
4
4
 
5
5
  // Logs verbose debug logs detailing various snapshot options.
6
6
  function debugSnapshotOptions(snapshot) {
@@ -340,40 +340,49 @@ export function createDiscoveryQueue(percy) {
340
340
  /* istanbul ignore next: tested, but coverage is stripped */
341
341
  let assetDiscoveryPageEnableJS = snapshot.cliEnableJavaScript && !snapshot.domSnapshot || (snapshot.enableJavaScript ?? !snapshot.domSnapshot);
342
342
  percy.log.debug(`Asset discovery Browser Page enable JS: ${assetDiscoveryPageEnableJS}`);
343
- // create a new browser page
344
- let page = yield percy.browser.page({
345
- enableJavaScript: assetDiscoveryPageEnableJS,
346
- networkIdleTimeout: snapshot.discovery.networkIdleTimeout,
347
- requestHeaders: snapshot.discovery.requestHeaders,
348
- authorization: snapshot.discovery.authorization,
349
- userAgent: snapshot.discovery.userAgent,
350
- captureMockedServiceWorker: snapshot.discovery.captureMockedServiceWorker,
351
- meta: snapshot.meta,
352
- // enable network inteception
353
- intercept: {
354
- enableJavaScript: snapshot.enableJavaScript,
355
- disableCache: snapshot.discovery.disableCache,
356
- allowedHostnames: snapshot.discovery.allowedHostnames,
357
- disallowedHostnames: snapshot.discovery.disallowedHostnames,
358
- getResource: u => snapshot.resources.get(u) || cache.get(u),
359
- saveResource: r => {
360
- snapshot.resources.set(r.url, r);
361
- if (!r.root) {
362
- cache.set(r.url, r);
343
+ await withRetries(async function* () {
344
+ // create a new browser page
345
+ let page = yield percy.browser.page({
346
+ enableJavaScript: assetDiscoveryPageEnableJS,
347
+ networkIdleTimeout: snapshot.discovery.networkIdleTimeout,
348
+ requestHeaders: snapshot.discovery.requestHeaders,
349
+ authorization: snapshot.discovery.authorization,
350
+ userAgent: snapshot.discovery.userAgent,
351
+ captureMockedServiceWorker: snapshot.discovery.captureMockedServiceWorker,
352
+ meta: snapshot.meta,
353
+ // enable network inteception
354
+ intercept: {
355
+ enableJavaScript: snapshot.enableJavaScript,
356
+ disableCache: snapshot.discovery.disableCache,
357
+ allowedHostnames: snapshot.discovery.allowedHostnames,
358
+ disallowedHostnames: snapshot.discovery.disallowedHostnames,
359
+ getResource: u => snapshot.resources.get(u) || cache.get(u),
360
+ saveResource: r => {
361
+ snapshot.resources.set(r.url, r);
362
+ if (!r.root) {
363
+ cache.set(r.url, r);
364
+ }
363
365
  }
364
366
  }
367
+ });
368
+ try {
369
+ yield* captureSnapshotResources(page, snapshot, {
370
+ captureWidths: !snapshot.domSnapshot && percy.deferUploads,
371
+ capture: callback,
372
+ captureForDevices: percy.deviceDetails || []
373
+ });
374
+ } finally {
375
+ // always close the page when done
376
+ await page.close();
365
377
  }
378
+ }, {
379
+ count: snapshot.discovery.retry ? 3 : 1,
380
+ onRetry: () => {
381
+ percy.log.info(`Retrying snapshot: ${snapshotLogName(snapshot.name, snapshot.meta)}`, snapshot.meta);
382
+ },
383
+ signal: snapshot._ctrl.signal,
384
+ throwOn: ['AbortError']
366
385
  });
367
- try {
368
- yield* captureSnapshotResources(page, snapshot, {
369
- captureWidths: !snapshot.domSnapshot && percy.deferUploads,
370
- capture: callback,
371
- captureForDevices: percy.deviceDetails || []
372
- });
373
- } finally {
374
- // always close the page when done
375
- await page.close();
376
- }
377
386
  }).handle('error', ({
378
387
  name,
379
388
  meta
package/dist/network.js CHANGED
@@ -28,6 +28,7 @@ export class Network {
28
28
  #requests = new Map();
29
29
  #authentications = new Set();
30
30
  #aborted = new Set();
31
+ #finishedUrls = new Set();
31
32
  constructor(page, options) {
32
33
  this.page = page;
33
34
  this.timeout = options.networkIdleTimeout ?? 100;
@@ -78,6 +79,12 @@ export class Network {
78
79
  throw new Error(`Network error: ${this.page.session.closedReason}`);
79
80
  }
80
81
  requests = Array.from(this.#requests.values()).filter(filter);
82
+ // remove requests which are finished at least once
83
+ // this happens when same request is made multiple times by browser in parallel and one of
84
+ // them gets stuck in pending state in browser [ need to debug why ]. So we dont receive
85
+ // loadingFinished event, causing it to show up in Active requests, but we can only store one
86
+ // response per url so as long as we have captured one, we dont care about other such requests
87
+ requests = requests.filter(req => !this.#finishedUrls.has(req.url));
81
88
  return requests.length === 0;
82
89
  }, {
83
90
  timeout: Network.TIMEOUT,
@@ -120,10 +127,12 @@ export class Network {
120
127
  // Called when a request should be removed from various trackers
121
128
  _forgetRequest({
122
129
  requestId,
123
- interceptId
130
+ interceptId,
131
+ url
124
132
  }, keepPending) {
125
133
  this.#requests.delete(requestId);
126
134
  this.#authentications.delete(interceptId);
135
+ this.#finishedUrls.add(url);
127
136
  if (!keepPending) {
128
137
  this.#pending.delete(requestId);
129
138
  }
package/dist/queue.js CHANGED
@@ -122,9 +122,15 @@ export class Queue {
122
122
 
123
123
  // call or set up other handlers
124
124
  let exists = this.cancel(item);
125
+ task.ctrl = new AbortController();
126
+ // duplicate abortion controller on task, so it can can be used in further
127
+ // generators and can be cancelled internally
128
+ // TODO fix this for non object item usecase
129
+ if (typeof item === 'object' && !Array.isArray(item) && item !== null) {
130
+ item._ctrl = task.ctrl;
131
+ }
125
132
  task.item = item = _classPrivateFieldGet(this, _handlers).push ? _classPrivateFieldGet(this, _handlers).push(item, exists) : item;
126
133
  task.handler = () => _classPrivateFieldGet(this, _handlers).task ? _classPrivateFieldGet(this, _handlers).task(item, ...args) : item;
127
- task.ctrl = new AbortController();
128
134
 
129
135
  // queue this task & maybe dequeue the next task
130
136
  _classPrivateFieldGet(this, _queued).add(task);
package/dist/snapshot.js CHANGED
@@ -129,7 +129,8 @@ function getSnapshotOptions(options, {
129
129
  disableCache: config.discovery.disableCache,
130
130
  captureMockedServiceWorker: config.discovery.captureMockedServiceWorker,
131
131
  captureSrcset: config.discovery.captureSrcset,
132
- userAgent: config.discovery.userAgent
132
+ userAgent: config.discovery.userAgent,
133
+ retry: config.discovery.retry
133
134
  }
134
135
  }, options], (path, prev, next) => {
135
136
  var _next, _next2;
package/dist/utils.js CHANGED
@@ -328,6 +328,29 @@ export function serializeFunction(fn) {
328
328
  }
329
329
  return fnbody;
330
330
  }
331
+ export async function withRetries(fn, {
332
+ count,
333
+ onRetry,
334
+ signal,
335
+ throwOn
336
+ }) {
337
+ count || (count = 1); // default a single try
338
+ let run = 0;
339
+ while (true) {
340
+ run += 1;
341
+ try {
342
+ return await generatePromise(fn, signal);
343
+ } catch (e) {
344
+ // if this error should not be retried on, we want to skip errors
345
+ let throwError = throwOn === null || throwOn === void 0 ? void 0 : throwOn.includes(e.name);
346
+ if (!throwError && run < count) {
347
+ await (onRetry === null || onRetry === void 0 ? void 0 : onRetry());
348
+ continue;
349
+ }
350
+ throw e;
351
+ }
352
+ }
353
+ }
331
354
  export function snapshotLogName(name, meta) {
332
355
  var _meta$snapshot;
333
356
  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.28.2-beta.1",
3
+ "version": "1.28.2-beta.2",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -43,11 +43,11 @@
43
43
  "test:types": "tsd"
44
44
  },
45
45
  "dependencies": {
46
- "@percy/client": "1.28.2-beta.1",
47
- "@percy/config": "1.28.2-beta.1",
48
- "@percy/dom": "1.28.2-beta.1",
49
- "@percy/logger": "1.28.2-beta.1",
50
- "@percy/webdriver-utils": "1.28.2-beta.1",
46
+ "@percy/client": "1.28.2-beta.2",
47
+ "@percy/config": "1.28.2-beta.2",
48
+ "@percy/dom": "1.28.2-beta.2",
49
+ "@percy/logger": "1.28.2-beta.2",
50
+ "@percy/webdriver-utils": "1.28.2-beta.2",
51
51
  "content-disposition": "^0.5.4",
52
52
  "cross-spawn": "^7.0.3",
53
53
  "extract-zip": "^2.0.1",
@@ -58,5 +58,5 @@
58
58
  "rimraf": "^3.0.2",
59
59
  "ws": "^8.0.0"
60
60
  },
61
- "gitHead": "c4d0637366dbc28eeda234f93d44424d9c565f49"
61
+ "gitHead": "7c2bdccd896be9da7d07dd30ead802fbb4ca9fab"
62
62
  }