@percy/core 1.28.1-alpha.1 → 1.28.1-alpha.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/config.js CHANGED
@@ -71,6 +71,9 @@ export const configSchema = {
71
71
  sync: {
72
72
  type: 'boolean'
73
73
  },
74
+ testCase: {
75
+ type: 'string'
76
+ },
74
77
  fullPage: {
75
78
  type: 'boolean',
76
79
  onlyAutomate: true
@@ -249,6 +252,10 @@ export const configSchema = {
249
252
  type: 'integer',
250
253
  minimum: 1
251
254
  },
255
+ retry: {
256
+ type: 'boolean',
257
+ default: false
258
+ },
252
259
  launchOptions: {
253
260
  type: 'object',
254
261
  additionalProperties: false,
@@ -312,6 +319,9 @@ export const snapshotSchema = {
312
319
  sync: {
313
320
  $ref: '/config/snapshot#/properties/sync'
314
321
  },
322
+ testCase: {
323
+ $ref: '/config/snapshot#/properties/testCase'
324
+ },
315
325
  reshuffleInvalidTags: {
316
326
  $ref: '/config/snapshot#/properties/reshuffleInvalidTags'
317
327
  },
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 } 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) {
@@ -139,8 +139,8 @@ function processSnapshotResources({
139
139
 
140
140
  // include associated snapshot logs matched by meta information
141
141
  resources.push(createLogResource(logger.query(log => {
142
- var _log$meta$snapshot;
143
- return ((_log$meta$snapshot = log.meta.snapshot) === null || _log$meta$snapshot === void 0 ? void 0 : _log$meta$snapshot.name) === snapshot.meta.snapshot.name;
142
+ var _log$meta$snapshot, _log$meta$snapshot2;
143
+ return ((_log$meta$snapshot = log.meta.snapshot) === null || _log$meta$snapshot === void 0 ? void 0 : _log$meta$snapshot.testCase) === snapshot.meta.snapshot.testCase && ((_log$meta$snapshot2 = log.meta.snapshot) === null || _log$meta$snapshot2 === void 0 ? void 0 : _log$meta$snapshot2.name) === snapshot.meta.snapshot.name;
144
144
  })));
145
145
  return {
146
146
  ...snapshot,
@@ -279,7 +279,8 @@ export const RESOURCE_CACHE_KEY = Symbol('resource-cache');
279
279
  // snapshot which is used to intercept and capture snapshot resource requests.
280
280
  export function createDiscoveryQueue(percy) {
281
281
  let {
282
- concurrency
282
+ concurrency,
283
+ retry
283
284
  } = percy.config.discovery;
284
285
  let queue = new Queue('discovery');
285
286
  let cache;
@@ -296,11 +297,12 @@ export function createDiscoveryQueue(percy) {
296
297
  .handle('end', async () => {
297
298
  await percy.browser.close();
298
299
  })
299
- // snapshots are unique by name; when deferred also by widths
300
+ // snapshots are unique by name and testCase; when deferred also by widths
300
301
  .handle('find', ({
301
302
  name,
303
+ testCase,
302
304
  widths
303
- }, snapshot) => snapshot.name === name && (!percy.deferUploads || !widths || widths.join() === snapshot.widths.join()))
305
+ }, snapshot) => snapshot.testCase === testCase && snapshot.name === name && (!percy.deferUploads || !widths || widths.join() === snapshot.widths.join()))
304
306
  // initialize the resources for DOM snapshots
305
307
  .handle('push', snapshot => {
306
308
  let resources = parseDomResources(snapshot);
@@ -317,46 +319,55 @@ export function createDiscoveryQueue(percy) {
317
319
  /* istanbul ignore next: tested, but coverage is stripped */
318
320
  let assetDiscoveryPageEnableJS = snapshot.cliEnableJavaScript && !snapshot.domSnapshot || (snapshot.enableJavaScript ?? !snapshot.domSnapshot);
319
321
  percy.log.debug(`Asset discovery Browser Page enable JS: ${assetDiscoveryPageEnableJS}`);
320
- // create a new browser page
321
- let page = yield percy.browser.page({
322
- enableJavaScript: assetDiscoveryPageEnableJS,
323
- networkIdleTimeout: snapshot.discovery.networkIdleTimeout,
324
- requestHeaders: snapshot.discovery.requestHeaders,
325
- authorization: snapshot.discovery.authorization,
326
- userAgent: snapshot.discovery.userAgent,
327
- captureMockedServiceWorker: snapshot.discovery.captureMockedServiceWorker,
328
- meta: snapshot.meta,
329
- // enable network inteception
330
- intercept: {
331
- enableJavaScript: snapshot.enableJavaScript,
332
- disableCache: snapshot.discovery.disableCache,
333
- allowedHostnames: snapshot.discovery.allowedHostnames,
334
- disallowedHostnames: snapshot.discovery.disallowedHostnames,
335
- getResource: u => snapshot.resources.get(u) || cache.get(u),
336
- saveResource: r => {
337
- snapshot.resources.set(r.url, r);
338
- if (!r.root) {
339
- cache.set(r.url, r);
322
+ await withRetries(async function* () {
323
+ // create a new browser page
324
+ let page = yield percy.browser.page({
325
+ enableJavaScript: assetDiscoveryPageEnableJS,
326
+ networkIdleTimeout: snapshot.discovery.networkIdleTimeout,
327
+ requestHeaders: snapshot.discovery.requestHeaders,
328
+ authorization: snapshot.discovery.authorization,
329
+ userAgent: snapshot.discovery.userAgent,
330
+ captureMockedServiceWorker: snapshot.discovery.captureMockedServiceWorker,
331
+ meta: snapshot.meta,
332
+ // enable network inteception
333
+ intercept: {
334
+ enableJavaScript: snapshot.enableJavaScript,
335
+ disableCache: snapshot.discovery.disableCache,
336
+ allowedHostnames: snapshot.discovery.allowedHostnames,
337
+ disallowedHostnames: snapshot.discovery.disallowedHostnames,
338
+ getResource: u => snapshot.resources.get(u) || cache.get(u),
339
+ saveResource: r => {
340
+ snapshot.resources.set(r.url, r);
341
+ if (!r.root) {
342
+ cache.set(r.url, r);
343
+ }
340
344
  }
341
345
  }
346
+ });
347
+ try {
348
+ yield* captureSnapshotResources(page, snapshot, {
349
+ captureWidths: !snapshot.domSnapshot && percy.deferUploads,
350
+ capture: callback
351
+ });
352
+ } finally {
353
+ // always close the page when done
354
+ await page.close();
342
355
  }
356
+ }, {
357
+ count: retry ? 3 : 1,
358
+ onRetry: () => {
359
+ percy.log.info(`Retrying snapshot: ${snapshotLogName(snapshot.name, snapshot.meta)}`, snapshot.meta);
360
+ },
361
+ signal: snapshot._ctrl.signal,
362
+ throwOn: ['AbortError']
343
363
  });
344
- try {
345
- yield* captureSnapshotResources(page, snapshot, {
346
- captureWidths: !snapshot.domSnapshot && percy.deferUploads,
347
- capture: callback
348
- });
349
- } finally {
350
- // always close the page when done
351
- await page.close();
352
- }
353
364
  }).handle('error', ({
354
365
  name,
355
366
  meta
356
367
  }, error) => {
357
368
  if (error.name === 'AbortError' && queue.readyState < 3) {
358
369
  // only error about aborted snapshots when not closed
359
- percy.log.error('Received a duplicate snapshot, ' + `the previous snapshot was aborted: ${name}`, meta);
370
+ percy.log.error('Received a duplicate snapshot, ' + `the previous snapshot was aborted: ${snapshotLogName(name, meta)}`, meta);
360
371
  } else {
361
372
  // log all other encountered errors
362
373
  percy.log.error(`Encountered an error taking snapshot: ${name}`, meta);
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
@@ -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 } from './utils.js';
6
+ import { request, hostnameMatches, yieldTo, snapshotLogName } 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
@@ -113,7 +113,8 @@ function getSnapshotOptions(options, {
113
113
  meta: {
114
114
  ...meta,
115
115
  snapshot: {
116
- name: options.name
116
+ name: options.name,
117
+ testCase: options.testCase
117
118
  }
118
119
  }
119
120
  }, config.snapshot, {
@@ -391,10 +392,11 @@ export function createSnapshotsQueue(percy) {
391
392
  });
392
393
  }
393
394
  })
394
- // snapshots are unique by name alone
395
+ // snapshots are unique by name and testCase both
395
396
  .handle('find', ({
396
- name
397
- }, snapshot) => snapshot.name === name)
397
+ name,
398
+ testCase
399
+ }, snapshot) => snapshot.testCase === testCase && snapshot.name === name)
398
400
  // when pushed, maybe flush old snapshots or possibly merge with existing snapshots
399
401
  .handle('push', (snapshot, existing) => {
400
402
  let {
@@ -403,8 +405,8 @@ export function createSnapshotsQueue(percy) {
403
405
  } = snapshot;
404
406
 
405
407
  // log immediately when not deferred or dry-running
406
- if (!percy.deferUploads) percy.log.info(`Snapshot taken: ${name}`, meta);
407
- if (percy.dryRun) percy.log.info(`Snapshot found: ${name}`, meta);
408
+ if (!percy.deferUploads) percy.log.info(`Snapshot taken: ${snapshotLogName(name, meta)}`, meta);
409
+ if (percy.dryRun) percy.log.info(`Snapshot found: ${snapshotLogName(name, meta)}`, meta);
408
410
 
409
411
  // immediately flush when uploads are delayed but not skipped
410
412
  if (percy.delayUploads && !percy.deferUploads) queue.flush();
package/dist/utils.js CHANGED
@@ -328,6 +328,36 @@ 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
+ }
354
+ export function snapshotLogName(name, meta) {
355
+ var _meta$snapshot;
356
+ if (meta !== null && meta !== void 0 && (_meta$snapshot = meta.snapshot) !== null && _meta$snapshot !== void 0 && _meta$snapshot.testCase) {
357
+ return `testCase: ${meta.snapshot.testCase}, ${name}`;
358
+ }
359
+ return name;
360
+ }
331
361
 
332
362
  // DefaultMap, which returns a default value for an uninitialized key
333
363
  // Similar to defaultDict in python
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@percy/core",
3
- "version": "1.28.1-alpha.1",
3
+ "version": "1.28.1-alpha.3",
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.1-alpha.1",
47
- "@percy/config": "1.28.1-alpha.1",
48
- "@percy/dom": "1.28.1-alpha.1",
49
- "@percy/logger": "1.28.1-alpha.1",
50
- "@percy/webdriver-utils": "1.28.1-alpha.1",
46
+ "@percy/client": "1.28.1-alpha.3",
47
+ "@percy/config": "1.28.1-alpha.3",
48
+ "@percy/dom": "1.28.1-alpha.3",
49
+ "@percy/logger": "1.28.1-alpha.3",
50
+ "@percy/webdriver-utils": "1.28.1-alpha.3",
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": "a8ac47269d65a7cf88658c8899c61bef1808c2d0"
61
+ "gitHead": "46d1e874e1092fee45899d70cd97cf793db17de5"
62
62
  }