@vizzly-testing/cli 0.28.1 → 0.29.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.
@@ -161,13 +161,35 @@ function formatBuildForJson(build, includeComparisons = false) {
161
161
  completedAt: build.completed_at
162
162
  };
163
163
  if (includeComparisons && build.comparisons) {
164
- result.comparisonDetails = build.comparisons.map(c => ({
165
- id: c.id,
166
- name: c.name,
167
- status: c.status,
168
- diffPercentage: c.diff_percentage,
169
- approvalStatus: c.approval_status
170
- }));
164
+ result.comparisonDetails = build.comparisons.map(c => {
165
+ let diffUrl = c.diff_image?.url || c.diff_image_url || c.diff_url || null;
166
+ let diffImage = c.diff_image || {};
167
+ let clusterMetadata = c.cluster_metadata || diffImage.cluster_metadata || null;
168
+ let ssimScore = c.ssim_score ?? diffImage.ssim_score ?? null;
169
+ let gmsdScore = c.gmsd_score ?? diffImage.gmsd_score ?? null;
170
+ let fingerprintHash = c.fingerprint_hash || diffImage.fingerprint_hash || null;
171
+ let hasHoneydiff = clusterMetadata || ssimScore != null || gmsdScore != null || fingerprintHash;
172
+ return {
173
+ id: c.id,
174
+ name: c.name,
175
+ status: c.status,
176
+ diffPercentage: c.diff_percentage,
177
+ approvalStatus: c.approval_status,
178
+ urls: {
179
+ baseline: c.baseline_screenshot?.original_url || c.baseline_original_url || c.baseline_screenshot_url || null,
180
+ current: c.current_screenshot?.original_url || c.current_original_url || c.current_screenshot_url || null,
181
+ diff: diffUrl
182
+ },
183
+ honeydiff: hasHoneydiff ? {
184
+ ssimScore,
185
+ gmsdScore,
186
+ clusterClassification: clusterMetadata?.classification || null,
187
+ clusterMetadata,
188
+ fingerprintHash,
189
+ diffRegions: c.diff_regions ?? diffImage.diff_regions ?? null
190
+ } : null
191
+ };
192
+ });
171
193
  }
172
194
  return result;
173
195
  }
@@ -154,6 +154,16 @@ export async function comparisonsCommand(options = {}, globalOptions = {}, deps
154
154
  * Format a comparison for JSON output
155
155
  */
156
156
  function formatComparisonForJson(comparison) {
157
+ // API endpoints return different shapes:
158
+ // - Single comparison: nested baseline_screenshot/current_screenshot + flat diff_url, honeydiff at top level
159
+ // - Build comparisons: flat diff_url/diff_image_url, no storage URLs, limited honeydiff
160
+ // - Search: nested diff_image with honeydiff, no current/baseline URLs
161
+ let diffImage = comparison.diff_image || {};
162
+ let clusterMetadata = comparison.cluster_metadata || diffImage.cluster_metadata || null;
163
+ let ssimScore = comparison.ssim_score ?? diffImage.ssim_score ?? null;
164
+ let gmsdScore = comparison.gmsd_score ?? diffImage.gmsd_score ?? null;
165
+ let fingerprintHash = comparison.fingerprint_hash || diffImage.fingerprint_hash || null;
166
+ let hasHoneydiff = clusterMetadata || ssimScore != null || gmsdScore != null || fingerprintHash;
157
167
  return {
158
168
  id: comparison.id,
159
169
  name: comparison.name,
@@ -166,10 +176,20 @@ function formatComparisonForJson(comparison) {
166
176
  } : null,
167
177
  browser: comparison.browser || null,
168
178
  urls: {
169
- baseline: comparison.baseline_screenshot?.original_url || null,
170
- current: comparison.current_screenshot?.original_url || null,
171
- diff: comparison.diff_image?.url || null
179
+ baseline: comparison.baseline_screenshot?.original_url || comparison.baseline_original_url || comparison.baseline_screenshot_url || null,
180
+ current: comparison.current_screenshot?.original_url || comparison.current_original_url || comparison.current_screenshot_url || null,
181
+ diff: comparison.diff_image?.url || comparison.diff_image_url || comparison.diff_url || null
172
182
  },
183
+ honeydiff: hasHoneydiff ? {
184
+ ssimScore,
185
+ gmsdScore,
186
+ clusterClassification: clusterMetadata?.classification || null,
187
+ clusterMetadata,
188
+ fingerprintHash,
189
+ diffRegions: comparison.diff_regions ?? diffImage.diff_regions ?? null,
190
+ diffLines: comparison.diff_lines ?? diffImage.diff_lines ?? null,
191
+ fingerprintData: comparison.fingerprint_data ?? diffImage.fingerprint_data ?? null
192
+ } : null,
173
193
  buildId: comparison.build_id,
174
194
  buildName: comparison.build_name,
175
195
  buildBranch: comparison.build_branch,
@@ -209,18 +229,40 @@ function displayComparison(output, comparison, verbose) {
209
229
  output.labelValue('Commit', comparison.build_commit_sha.substring(0, 8));
210
230
  }
211
231
 
212
- // URLs in verbose mode
232
+ // Honeydiff analysis in verbose mode
213
233
  if (verbose) {
214
- output.blank();
215
- output.labelValue('URLs', '');
216
- if (comparison.baseline_screenshot?.original_url) {
217
- output.print(` Baseline: ${comparison.baseline_screenshot.original_url}`);
218
- }
219
- if (comparison.current_screenshot?.original_url) {
220
- output.print(` Current: ${comparison.current_screenshot.original_url}`);
234
+ let clusterMetadata = comparison.cluster_metadata || comparison.diff_image?.cluster_metadata;
235
+ let ssim = comparison.ssim_score ?? comparison.diff_image?.ssim_score;
236
+ let gmsd = comparison.gmsd_score ?? comparison.diff_image?.gmsd_score;
237
+ let fingerprint = comparison.fingerprint_hash || comparison.diff_image?.fingerprint_hash;
238
+ if (clusterMetadata || ssim != null || gmsd != null || fingerprint) {
239
+ output.blank();
240
+ if (clusterMetadata?.classification) {
241
+ output.labelValue('Classification', clusterMetadata.classification);
242
+ }
243
+ if (ssim != null) {
244
+ output.labelValue('SSIM', ssim.toFixed(4));
245
+ }
246
+ if (gmsd != null) {
247
+ output.labelValue('GMSD', gmsd.toFixed(4));
248
+ }
249
+ if (fingerprint) {
250
+ output.labelValue('Fingerprint', fingerprint);
251
+ }
221
252
  }
222
- if (comparison.diff_image?.url) {
223
- output.print(` Diff: ${comparison.diff_image.url}`);
253
+ }
254
+
255
+ // URLs in verbose mode
256
+ if (verbose) {
257
+ let baselineUrl = comparison.baseline_screenshot?.original_url || comparison.baseline_original_url || comparison.baseline_screenshot_url;
258
+ let currentUrl = comparison.current_screenshot?.original_url || comparison.current_original_url || comparison.current_screenshot_url;
259
+ let diffUrl = comparison.diff_image?.url || comparison.diff_image_url || comparison.diff_url;
260
+ if (baselineUrl || currentUrl || diffUrl) {
261
+ output.blank();
262
+ output.labelValue('URLs', '');
263
+ if (baselineUrl) output.print(` Baseline: ${baselineUrl}`);
264
+ if (currentUrl) output.print(` Current: ${currentUrl}`);
265
+ if (diffUrl) output.print(` Diff: ${diffUrl}`);
224
266
  }
225
267
  }
226
268
  if (comparison.created_at) {
@@ -258,7 +300,8 @@ function displayBuildComparisons(output, build, comparisons, verbose) {
258
300
  for (let comp of comparisons.slice(0, verbose ? 100 : 20)) {
259
301
  let icon = getStatusIcon(colors, comp.status);
260
302
  let diffInfo = comp.diff_percentage != null ? colors.dim(` (${(comp.diff_percentage * 100).toFixed(1)}%)`) : '';
261
- output.print(` ${icon} ${comp.name}${diffInfo}`);
303
+ let classification = verbose ? getClassificationLabel(colors, comp.cluster_metadata) : '';
304
+ output.print(` ${icon} ${comp.name}${diffInfo}${classification}`);
262
305
  }
263
306
  if (comparisons.length > (verbose ? 100 : 20)) {
264
307
  output.blank();
@@ -299,7 +342,8 @@ function displaySearchResults(output, comparisons, searchPattern, pagination, ve
299
342
  }
300
343
  for (let comp of group.comparisons.slice(0, verbose ? 10 : 3)) {
301
344
  let icon = getStatusIcon(colors, comp.status);
302
- output.print(` ${icon} ${comp.name}`);
345
+ let classification = verbose ? getClassificationLabel(colors, comp.cluster_metadata || comp.diff_image?.cluster_metadata) : '';
346
+ output.print(` ${icon} ${comp.name}${classification}`);
303
347
  }
304
348
  if (group.comparisons.length > (verbose ? 10 : 3)) {
305
349
  output.print(` ${colors.dim(`... and ${group.comparisons.length - (verbose ? 10 : 3)} more`)}`);
@@ -311,6 +355,15 @@ function displaySearchResults(output, comparisons, searchPattern, pagination, ve
311
355
  }
312
356
  }
313
357
 
358
+ /**
359
+ * Get a classification label for verbose display
360
+ */
361
+ function getClassificationLabel(colors, clusterMetadata) {
362
+ let classification = clusterMetadata?.classification;
363
+ if (!classification) return '';
364
+ return colors.dim(` [${classification}]`);
365
+ }
366
+
314
367
  /**
315
368
  * Get icon for comparison status
316
369
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vizzly-testing/cli",
3
- "version": "0.28.1",
3
+ "version": "0.29.0",
4
4
  "description": "Visual review platform for UI developers and designers",
5
5
  "keywords": [
6
6
  "visual-testing",