@percy/client 1.10.3 → 1.11.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/README.md CHANGED
@@ -7,6 +7,7 @@ builds. Can also be used to query for a project's builds using a read access tok
7
7
  - [Usage](#usage)
8
8
  - [Create a build](#create-a-build)
9
9
  - [Create, upload, and finalize snapshots](#create-upload-and-finalize-snapshots)
10
+ - [Create, upload, and finalize comparisons](#create-upload-and-finalize-comparisons)
10
11
  - [Finalize a build](#finalize-a-build)
11
12
  - [Query for a build*](#query-for-a-build)
12
13
  - [Query for a project's builds*](#query-for-a-projects-builds)
@@ -59,7 +60,39 @@ await client.sendSnapshot(buildId, snapshotOptions)
59
60
  - `mimetype` — Resource mimetype (**required**)
60
61
  - `content` — Resource content (**required**)
61
62
  - `sha` — Resource content sha
62
- - `root` — Boolean indicating a root resource
63
+ - `root` — Boolean indicating a root resource## Create, upload, and finalize snapshots
64
+
65
+ ## Create, upload, and finalize comparisons
66
+
67
+ This method combines the work of creating a snapshot, creating an associated comparison, uploading
68
+ associated comparison tiles, and finally finalizing the comparison.
69
+
70
+ ``` js
71
+ await client.sendComparison(buildId, comparisonOptions)
72
+ ```
73
+
74
+ #### Options
75
+
76
+ - `name` — Snapshot name (**required**)
77
+ - `clientInfo` — Additional client info
78
+ - `environmentInfo` — Additional environment info
79
+ - `externalDebugUrl` — External debug URL
80
+ - `tag` — Tagged information about this comparison
81
+ - `name` — The tag name for this comparison, e.g. "iPhone 14 Pro" (**required**)
82
+ - `osName` - OS name for the comparison tag; e.g. "iOS"
83
+ - `osVersion` - OS version for the comparison tag; e.g. "16"
84
+ - `width` - The width for this type of comparison
85
+ - `height` - The height for this type of comparison
86
+ - `orientation` - Either "portrait" or "landscape"
87
+ - `tiles` — Array of comparison tiles
88
+ - `sha` — Tile file contents SHA-256 hash
89
+ - `filepath` — Tile filepath in the filesystem (required when missing `content`)
90
+ - `content` — Tile contents as a string or buffer (required when missing `filepath`)
91
+ - `statusBarHeight` — Height of any status bar in this tile
92
+ - `navBarHeight` — Height of any nav bar in this tile
93
+ - `headerHeight` — Height of any header area in this tile
94
+ - `footerHeight` — Height of any footer area in this tile
95
+ - `fullscreen` — Boolean indicating this is a fullscreen tile
63
96
 
64
97
  ## Finalize a build
65
98
 
package/dist/client.js CHANGED
@@ -7,13 +7,13 @@ import { pool, request, sha256hash, base64encode, getPackageJSON } from './utils
7
7
  const {
8
8
  PERCY_CLIENT_API_URL = 'https://percy.io/api/v1'
9
9
  } = process.env;
10
- const pkg = getPackageJSON(import.meta.url); // Validate build ID arguments
10
+ const pkg = getPackageJSON(import.meta.url); // Validate ID arguments
11
11
 
12
- function validateBuildId(id) {
13
- if (!id) throw new Error('Missing build ID');
12
+ function validateId(type, id) {
13
+ if (!id) throw new Error(`Missing ${type} ID`);
14
14
 
15
15
  if (!(typeof id === 'string' || typeof id === 'number')) {
16
- throw new Error('Invalid build ID');
16
+ throw new Error(`Invalid ${type} ID`);
17
17
  }
18
18
  } // Validate project path arguments
19
19
 
@@ -157,7 +157,7 @@ export class PercyClient {
157
157
  async finalizeBuild(buildId, {
158
158
  all = false
159
159
  } = {}) {
160
- validateBuildId(buildId);
160
+ validateId('build', buildId);
161
161
  let qs = all ? 'all-shards=true' : '';
162
162
  this.log.debug(`Finalizing build ${buildId}...`);
163
163
  return this.post(`builds/${buildId}/finalize?${qs}`);
@@ -165,7 +165,7 @@ export class PercyClient {
165
165
 
166
166
 
167
167
  async getBuild(buildId) {
168
- validateBuildId(buildId);
168
+ validateId('build', buildId);
169
169
  this.log.debug(`Get build ${buildId}`);
170
170
  return this.get(`builds/${buildId}`);
171
171
  } // Retrieves project builds optionally filtered. Requires a read access token.
@@ -252,9 +252,9 @@ export class PercyClient {
252
252
  filepath,
253
253
  content
254
254
  } = {}) {
255
- validateBuildId(buildId);
255
+ validateId('build', buildId);
256
256
  this.log.debug(`Uploading resource: ${url}...`);
257
- if (filepath) content = fs.readFileSync(filepath);
257
+ if (filepath) content = await fs.promises.readFile(filepath);
258
258
  return this.post(`builds/${buildId}/resources`, {
259
259
  data: {
260
260
  type: 'resources',
@@ -268,7 +268,7 @@ export class PercyClient {
268
268
 
269
269
 
270
270
  async uploadResources(buildId, resources) {
271
- validateBuildId(buildId);
271
+ validateId('build', buildId);
272
272
  this.log.debug(`Uploading resources for ${buildId}...`);
273
273
  return pool(function* () {
274
274
  for (let resource of resources) {
@@ -288,7 +288,7 @@ export class PercyClient {
288
288
  environmentInfo,
289
289
  resources = []
290
290
  } = {}) {
291
- validateBuildId(buildId);
291
+ validateId('build', buildId);
292
292
  this.addClientInfo(clientInfo);
293
293
  this.addEnvironmentInfo(environmentInfo);
294
294
 
@@ -297,6 +297,12 @@ export class PercyClient {
297
297
  }
298
298
 
299
299
  this.log.debug(`Creating snapshot: ${name}...`);
300
+
301
+ for (let resource of resources) {
302
+ if (resource.sha || resource.content || !resource.filepath) continue;
303
+ resource.content = await fs.promises.readFile(resource.filepath);
304
+ }
305
+
300
306
  return this.post(`builds/${buildId}/snapshots`, {
301
307
  data: {
302
308
  type: 'snapshots',
@@ -311,10 +317,11 @@ export class PercyClient {
311
317
  resources: {
312
318
  data: resources.map(r => ({
313
319
  type: 'resources',
314
- id: r.sha || sha256hash(r.content),
320
+ id: r.sha ?? (r.content && sha256hash(r.content)),
315
321
  attributes: {
316
322
  'resource-url': r.url || null,
317
323
  'is-root': r.root || null,
324
+ 'for-widths': r.widths || null,
318
325
  mimetype: r.mimetype || null
319
326
  }
320
327
  }))
@@ -326,7 +333,7 @@ export class PercyClient {
326
333
 
327
334
 
328
335
  async finalizeSnapshot(snapshotId) {
329
- if (!snapshotId) throw new Error('Missing snapshot ID');
336
+ validateId('snapshot', snapshotId);
330
337
  this.log.debug(`Finalizing snapshot ${snapshotId}...`);
331
338
  return this.post(`snapshots/${snapshotId}/finalize`);
332
339
  } // Convenience method for creating a snapshot for the active build, uploading
@@ -352,5 +359,104 @@ export class PercyClient {
352
359
  return snapshot;
353
360
  }
354
361
 
362
+ async createComparison(snapshotId, {
363
+ tag,
364
+ tiles = [],
365
+ externalDebugUrl
366
+ } = {}) {
367
+ validateId('snapshot', snapshotId);
368
+ this.log.debug(`Creating comparision: ${tag.name}...`);
369
+
370
+ for (let tile of tiles) {
371
+ if (tile.sha || tile.content || !tile.filepath) continue;
372
+ tile.content = await fs.promises.readFile(tile.filepath);
373
+ }
374
+
375
+ return this.post(`snapshots/${snapshotId}/comparisons`, {
376
+ data: {
377
+ type: 'comparisons',
378
+ attributes: {
379
+ 'external-debug-url': externalDebugUrl || null
380
+ },
381
+ relationships: {
382
+ tag: {
383
+ data: {
384
+ type: 'tag',
385
+ attributes: {
386
+ name: tag.name || null,
387
+ width: tag.width || null,
388
+ height: tag.height || null,
389
+ 'os-name': tag.osName || null,
390
+ 'os-version': tag.osVersion || null,
391
+ orientation: tag.orientation || null
392
+ }
393
+ }
394
+ },
395
+ tiles: {
396
+ data: tiles.map(t => ({
397
+ type: 'tiles',
398
+ attributes: {
399
+ sha: t.sha || t.content && sha256hash(t.content),
400
+ 'status-bar-height': t.statusBarHeight || null,
401
+ 'nav-bar-height': t.navBarHeight || null,
402
+ 'header-height': t.headerHeight || null,
403
+ 'footer-height': t.footerHeight || null,
404
+ fullscreen: t.fullscreen || null
405
+ }
406
+ }))
407
+ }
408
+ }
409
+ }
410
+ });
411
+ }
412
+
413
+ async uploadComparisonTile(comparisonId, {
414
+ index = 0,
415
+ total = 1,
416
+ filepath,
417
+ content
418
+ } = {}) {
419
+ validateId('comparison', comparisonId);
420
+ this.log.debug(`Uploading comparison tile: ${index + 1}/${total} (${comparisonId})...`);
421
+ if (filepath) content = await fs.promises.readFile(filepath);
422
+ return this.post(`comparisons/${comparisonId}/tiles`, {
423
+ data: {
424
+ type: 'tiles',
425
+ attributes: {
426
+ 'base64-content': base64encode(content),
427
+ index
428
+ }
429
+ }
430
+ });
431
+ }
432
+
433
+ async uploadComparisonTiles(comparisonId, tiles) {
434
+ validateId('comparison', comparisonId);
435
+ this.log.debug(`Uploading comparison tiles for ${comparisonId}...`);
436
+ return pool(function* () {
437
+ for (let index = 0; index < tiles.length; index++) {
438
+ yield this.uploadComparisonTile(comparisonId, {
439
+ index,
440
+ total: tiles.length,
441
+ ...tiles[index]
442
+ });
443
+ }
444
+ }, this, 2);
445
+ }
446
+
447
+ async finalizeComparison(comparisonId) {
448
+ validateId('comparison', comparisonId);
449
+ this.log.debug(`Finalizing comparison ${comparisonId}...`);
450
+ return this.post(`comparisons/${comparisonId}/finalize`);
451
+ }
452
+
453
+ async sendComparison(buildId, options) {
454
+ let snapshot = await this.createSnapshot(buildId, options);
455
+ let comparison = await this.createComparison(snapshot.data.id, options);
456
+ await this.uploadComparisonTiles(comparison.data.id, options.tiles);
457
+ await this.finalizeComparison(comparison.data.id);
458
+ return comparison;
459
+ }
460
+
355
461
  }
356
462
  export default PercyClient;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@percy/client",
3
- "version": "1.10.3",
3
+ "version": "1.11.0",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -31,8 +31,8 @@
31
31
  "test:coverage": "yarn test --coverage"
32
32
  },
33
33
  "dependencies": {
34
- "@percy/env": "1.10.3",
35
- "@percy/logger": "1.10.3"
34
+ "@percy/env": "1.11.0",
35
+ "@percy/logger": "1.11.0"
36
36
  },
37
- "gitHead": "a9858d20a9b9708da0464c0617b32b2ee1c97433"
37
+ "gitHead": "0a5043cd677266390889063924f342af9b347055"
38
38
  }
package/test/helpers.js CHANGED
@@ -120,6 +120,14 @@ export const api = {
120
120
  }
121
121
  }
122
122
  }
123
+ }],
124
+
125
+ '/snapshots/4567/comparisons': ({ body }) => [201, {
126
+ data: {
127
+ id: '891011',
128
+ attributes: body.attributes,
129
+ relationships: body.relationships
130
+ }
123
131
  }]
124
132
  },
125
133