@percy/core 1.28.3 → 1.28.4-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/discovery.js CHANGED
@@ -3,6 +3,7 @@ import Queue from './queue.js';
3
3
  import { normalizeURL, hostnameMatches, createResource, createRootResource, createPercyCSSResource, createLogResource, yieldAll, snapshotLogName, withRetries } from './utils.js';
4
4
  import { sha256hash } from '@percy/client/utils';
5
5
  import Pako from 'pako';
6
+ import TimeIt from './timing.js';
6
7
 
7
8
  // Logs verbose debug logs detailing various snapshot options.
8
9
  function debugSnapshotOptions(snapshot) {
@@ -312,6 +313,7 @@ export function createDiscoveryQueue(percy) {
312
313
  concurrency
313
314
  } = percy.config.discovery;
314
315
  let queue = new Queue('discovery');
316
+ let timeit = new TimeIt();
315
317
  let cache;
316
318
  return queue.set({
317
319
  concurrency
@@ -342,54 +344,56 @@ export function createDiscoveryQueue(percy) {
342
344
  })
343
345
  // discovery resources for snapshots and call the callback for each discovered snapshot
344
346
  .handle('task', async function* (snapshot, callback) {
345
- percy.log.debug(`Discovering resources: ${snapshot.name}`, snapshot.meta);
347
+ await timeit.measure('asset-discovery', snapshot.name, async () => {
348
+ percy.log.debug(`Discovering resources: ${snapshot.name}`, snapshot.meta);
346
349
 
347
- // expectation explained in tests
348
- /* istanbul ignore next: tested, but coverage is stripped */
349
- let assetDiscoveryPageEnableJS = snapshot.cliEnableJavaScript && !snapshot.domSnapshot || (snapshot.enableJavaScript ?? !snapshot.domSnapshot);
350
- percy.log.debug(`Asset discovery Browser Page enable JS: ${assetDiscoveryPageEnableJS}`);
351
- await withRetries(async function* () {
352
- // create a new browser page
353
- let page = yield percy.browser.page({
354
- enableJavaScript: assetDiscoveryPageEnableJS,
355
- networkIdleTimeout: snapshot.discovery.networkIdleTimeout,
356
- requestHeaders: snapshot.discovery.requestHeaders,
357
- authorization: snapshot.discovery.authorization,
358
- userAgent: snapshot.discovery.userAgent,
359
- captureMockedServiceWorker: snapshot.discovery.captureMockedServiceWorker,
360
- meta: snapshot.meta,
361
- // enable network inteception
362
- intercept: {
363
- enableJavaScript: snapshot.enableJavaScript,
364
- disableCache: snapshot.discovery.disableCache,
365
- allowedHostnames: snapshot.discovery.allowedHostnames,
366
- disallowedHostnames: snapshot.discovery.disallowedHostnames,
367
- getResource: u => snapshot.resources.get(u) || cache.get(u),
368
- saveResource: r => {
369
- snapshot.resources.set(r.url, r);
370
- if (!r.root) {
371
- cache.set(r.url, r);
350
+ // expectation explained in tests
351
+ /* istanbul ignore next: tested, but coverage is stripped */
352
+ let assetDiscoveryPageEnableJS = snapshot.cliEnableJavaScript && !snapshot.domSnapshot || (snapshot.enableJavaScript ?? !snapshot.domSnapshot);
353
+ percy.log.debug(`Asset discovery Browser Page enable JS: ${assetDiscoveryPageEnableJS}`);
354
+ await withRetries(async function* () {
355
+ // create a new browser page
356
+ let page = yield percy.browser.page({
357
+ enableJavaScript: assetDiscoveryPageEnableJS,
358
+ networkIdleTimeout: snapshot.discovery.networkIdleTimeout,
359
+ requestHeaders: snapshot.discovery.requestHeaders,
360
+ authorization: snapshot.discovery.authorization,
361
+ userAgent: snapshot.discovery.userAgent,
362
+ captureMockedServiceWorker: snapshot.discovery.captureMockedServiceWorker,
363
+ meta: snapshot.meta,
364
+ // enable network inteception
365
+ intercept: {
366
+ enableJavaScript: snapshot.enableJavaScript,
367
+ disableCache: snapshot.discovery.disableCache,
368
+ allowedHostnames: snapshot.discovery.allowedHostnames,
369
+ disallowedHostnames: snapshot.discovery.disallowedHostnames,
370
+ getResource: u => snapshot.resources.get(u) || cache.get(u),
371
+ saveResource: r => {
372
+ snapshot.resources.set(r.url, r);
373
+ if (!r.root) {
374
+ cache.set(r.url, r);
375
+ }
372
376
  }
373
377
  }
378
+ });
379
+ try {
380
+ yield* captureSnapshotResources(page, snapshot, {
381
+ captureWidths: !snapshot.domSnapshot && percy.deferUploads,
382
+ capture: callback,
383
+ captureForDevices: percy.deviceDetails || []
384
+ });
385
+ } finally {
386
+ // always close the page when done
387
+ await page.close();
374
388
  }
389
+ }, {
390
+ count: snapshot.discovery.retry ? 3 : 1,
391
+ onRetry: () => {
392
+ percy.log.info(`Retrying snapshot: ${snapshotLogName(snapshot.name, snapshot.meta)}`, snapshot.meta);
393
+ },
394
+ signal: snapshot._ctrl.signal,
395
+ throwOn: ['AbortError']
375
396
  });
376
- try {
377
- yield* captureSnapshotResources(page, snapshot, {
378
- captureWidths: !snapshot.domSnapshot && percy.deferUploads,
379
- capture: callback,
380
- captureForDevices: percy.deviceDetails || []
381
- });
382
- } finally {
383
- // always close the page when done
384
- await page.close();
385
- }
386
- }, {
387
- count: snapshot.discovery.retry ? 3 : 1,
388
- onRetry: () => {
389
- percy.log.info(`Retrying snapshot: ${snapshotLogName(snapshot.name, snapshot.meta)}`, snapshot.meta);
390
- },
391
- signal: snapshot._ctrl.signal,
392
- throwOn: ['AbortError']
393
397
  });
394
398
  }).handle('error', ({
395
399
  name,
package/dist/percy.js CHANGED
@@ -2,10 +2,11 @@ import PercyClient from '@percy/client';
2
2
  import PercyConfig from '@percy/config';
3
3
  import logger from '@percy/logger';
4
4
  import Browser from './browser.js';
5
+ import Pako from 'pako';
6
+ import { base64encode, generatePromise, yieldAll, yieldTo, redactSecrets } from './utils.js';
5
7
  import { createPercyServer, createStaticServer } from './api.js';
6
8
  import { gatherSnapshots, createSnapshotsQueue, validateSnapshotOptions } from './snapshot.js';
7
9
  import { discoverSnapshotResources, createDiscoveryQueue } from './discovery.js';
8
- import { generatePromise, yieldAll, yieldTo } from './utils.js';
9
10
  import { WaitForJob } from './wait-for-job.js';
10
11
 
11
12
  // A Percy instance will create a new build when started, handle snapshot creation, asset discovery,
@@ -146,6 +147,7 @@ export class Percy {
146
147
  this.readyState = 0;
147
148
  try {
148
149
  var _this$build;
150
+ this.log.warn('Notice: Percy collects CI logs for service improvement, stored for 14 days. Opt-out anytime with export PERCY_CLIENT_ERROR_LOGS=false');
149
151
  // start the snapshots queue immediately when not delayed or deferred
150
152
  if (!this.delayUploads && !this.deferUploads) yield this.#snapshots.start();
151
153
  // do not start the discovery queue when not needed
@@ -206,54 +208,61 @@ export class Percy {
206
208
  // Stops the local API server and closes the browser and internal queues once snapshots have
207
209
  // completed. Does nothing if not running. When `force` is true, any queued snapshots are cleared.
208
210
  async *stop(force) {
209
- var _this$server3;
210
211
  // not started, but the browser was launched
211
- if (!this.readyState && this.browser.isConnected()) {
212
- await this.browser.close();
213
- }
214
- if (this.syncQueue) this.syncQueue.stop();
215
- // not started or already stopped
216
- if (!this.readyState || this.readyState > 2) return;
217
-
218
- // close queues asap
219
- if (force) {
220
- this.#discovery.close(true);
221
- this.#snapshots.close(true);
222
- }
212
+ try {
213
+ var _this$server3;
214
+ if (!this.readyState && this.browser.isConnected()) {
215
+ await this.browser.close();
216
+ }
217
+ if (this.syncQueue) this.syncQueue.stop();
218
+ // not started or already stopped
219
+ if (!this.readyState || this.readyState > 2) return;
220
+
221
+ // close queues asap
222
+ if (force) {
223
+ this.#discovery.close(true);
224
+ this.#snapshots.close(true);
225
+ }
223
226
 
224
- // already stopping
225
- if (this.readyState === 2) return;
226
- this.readyState = 2;
227
+ // already stopping
228
+ if (this.readyState === 2) return;
229
+ this.readyState = 2;
227
230
 
228
- // log when force stopping
229
- if (force) this.log.info('Stopping percy...');
231
+ // log when force stopping
232
+ if (force) this.log.info('Stopping percy...');
230
233
 
231
- // used to log snapshot count information
232
- let info = (state, size) => `${state} ` + `${size} snapshot${size !== 1 ? 's' : ''}`;
233
- try {
234
- // flush discovery and snapshot queues
235
- yield* this.yield.flush((state, size) => {
236
- this.log.progress(`${info(state, size)}...`, !!size);
237
- });
238
- } catch (error) {
239
- // reset ready state when aborted
240
- /* istanbul ignore else: all errors bubble */
241
- if (error.name === 'AbortError') this.readyState = 1;
242
- throw error;
243
- }
234
+ // used to log snapshot count information
235
+ let info = (state, size) => `${state} ` + `${size} snapshot${size !== 1 ? 's' : ''}`;
236
+ try {
237
+ // flush discovery and snapshot queues
238
+ yield* this.yield.flush((state, size) => {
239
+ this.log.progress(`${info(state, size)}...`, !!size);
240
+ });
241
+ } catch (error) {
242
+ // reset ready state when aborted
243
+ /* istanbul ignore else: all errors bubble */
244
+ if (error.name === 'AbortError') this.readyState = 1;
245
+ throw error;
246
+ }
244
247
 
245
- // if dry-running, log the total number of snapshots
246
- if (this.dryRun && this.#snapshots.size) {
247
- this.log.info(info('Found', this.#snapshots.size));
248
- }
248
+ // if dry-running, log the total number of snapshots
249
+ if (this.dryRun && this.#snapshots.size) {
250
+ this.log.info(info('Found', this.#snapshots.size));
251
+ }
249
252
 
250
- // close server and end queues
251
- await ((_this$server3 = this.server) === null || _this$server3 === void 0 ? void 0 : _this$server3.close());
252
- await this.#discovery.end();
253
- await this.#snapshots.end();
253
+ // close server and end queues
254
+ await ((_this$server3 = this.server) === null || _this$server3 === void 0 ? void 0 : _this$server3.close());
255
+ await this.#discovery.end();
256
+ await this.#snapshots.end();
254
257
 
255
- // mark instance as stopped
256
- this.readyState = 3;
258
+ // mark instance as stopped
259
+ this.readyState = 3;
260
+ } catch (err) {
261
+ this.log.error(err);
262
+ throw err;
263
+ } finally {
264
+ await this.sendBuildLogs();
265
+ }
257
266
  }
258
267
 
259
268
  // Takes one or more snapshots of a page while discovering resources to upload with the resulting
@@ -412,5 +421,33 @@ export class Percy {
412
421
  if (syncMode) options.sync = syncMode;
413
422
  return syncMode;
414
423
  }
424
+ async sendBuildLogs() {
425
+ if (!process.env.PERCY_TOKEN) return;
426
+ try {
427
+ var _this$build3, _this$build4, _this$build5, _this$build6;
428
+ const logsObject = {
429
+ clilogs: logger.query(() => true)
430
+ };
431
+ // Only add CI logs if not disabled voluntarily.
432
+ if (process.env.PERCY_CLIENT_ERROR_LOGS !== 'false') {
433
+ const redactedContent = redactSecrets(logger.query(() => true, true));
434
+ logsObject.cilogs = redactedContent;
435
+ }
436
+ const content = base64encode(Pako.gzip(JSON.stringify(logsObject)));
437
+ const referenceId = (_this$build3 = this.build) !== null && _this$build3 !== void 0 && _this$build3.id ? `build_${(_this$build4 = this.build) === null || _this$build4 === void 0 ? void 0 : _this$build4.id}` : (_this$build5 = this.build) === null || _this$build5 === void 0 ? void 0 : _this$build5.id;
438
+ const eventObject = {
439
+ content: content,
440
+ build_id: (_this$build6 = this.build) === null || _this$build6 === void 0 ? void 0 : _this$build6.id,
441
+ reference_id: referenceId,
442
+ service_name: 'cli',
443
+ base64encoded: true
444
+ };
445
+ // Ignore this will update once I implement logs controller.
446
+ const logsSHA = await this.client.sendBuildLogs(eventObject);
447
+ this.log.info(`Build logs sent successfully. Please share this log ID with Percy team in case of any issues - ${logsSHA}`);
448
+ } catch (err) {
449
+ this.log.warn('Could not send the builds logs');
450
+ }
451
+ }
415
452
  }
416
453
  export default Percy;