@vizzly-testing/cli 0.35.3-beta.1 → 0.35.3-beta.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/README.md +8 -3
- package/dist/api/endpoints.js +1 -46
- package/dist/api/index.js +1 -1
- package/dist/cli.js +5 -5
- package/dist/commands/api.js +3 -3
- package/dist/commands/builds.js +12 -15
- package/dist/commands/comparisons.js +7 -9
- package/dist/commands/context.js +10 -20
- package/dist/commands/init.js +62 -13
- package/dist/commands/review.js +122 -35
- package/dist/commands/run.js +1 -3
- package/dist/commands/status.js +35 -79
- package/dist/commands/upload.js +1 -1
- package/dist/context/local-workspace-provider.js +36 -126
- package/dist/reporter/reporter-bundle.iife.js +49 -81
- package/dist/server/handlers/tdd-handler.js +2 -7
- package/dist/server/routers/dashboard.js +5 -1
- package/dist/tdd/runtime-context.js +0 -2
- package/dist/tdd/services/comparison-service.js +36 -76
- package/dist/tdd/tdd-service.js +7 -205
- package/dist/types/index.d.ts +1 -1
- package/dist/uploader/core.js +7 -8
- package/dist/utils/visual-context-normalizers.js +86 -139
- package/package.json +4 -4
- package/skills/vizzly/SKILL.md +42 -52
- package/skills/vizzly/references/cli-context.md +49 -52
- package/skills/vizzly/references/dynamic-content.md +18 -29
- package/skills/vizzly/references/sdks.md +20 -28
- package/skills/vizzly/references/setup-ci.md +18 -42
- package/dist/tdd/core/hotspot-coverage.js +0 -112
- package/dist/tdd/core/region-coverage.js +0 -203
- package/dist/tdd/metadata/hotspot-metadata.js +0 -93
- package/dist/tdd/metadata/region-metadata.js +0 -56
- package/skills/vizzly/agents/openai.yaml +0 -4
package/dist/commands/review.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* Review commands - approve, reject, and comment on comparisons/builds
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
+
import { randomUUID as defaultRandomUUID } from 'node:crypto';
|
|
5
6
|
import { createApiClient as defaultCreateApiClient } from '../api/index.js';
|
|
6
7
|
import { loadConfig as defaultLoadConfig } from '../utils/config-loader.js';
|
|
7
8
|
import { getAccessToken as defaultGetAccessToken } from '../utils/global-config.js';
|
|
@@ -11,6 +12,7 @@ function createReviewDeps(deps = {}) {
|
|
|
11
12
|
loadConfig: deps.loadConfig || defaultLoadConfig,
|
|
12
13
|
createApiClient: deps.createApiClient || defaultCreateApiClient,
|
|
13
14
|
getAccessToken: deps.getAccessToken || defaultGetAccessToken,
|
|
15
|
+
randomUUID: deps.randomUUID || defaultRandomUUID,
|
|
14
16
|
output: deps.output || defaultOutput,
|
|
15
17
|
exit: deps.exit || (code => process.exit(code))
|
|
16
18
|
};
|
|
@@ -144,12 +146,12 @@ async function runReviewMutation({
|
|
|
144
146
|
}
|
|
145
147
|
export function createApprovalBody(options = {}) {
|
|
146
148
|
return options.comment ? {
|
|
147
|
-
|
|
149
|
+
annotation: options.comment.trim()
|
|
148
150
|
} : {};
|
|
149
151
|
}
|
|
150
152
|
export function createRejectionBody(options = {}) {
|
|
151
153
|
return {
|
|
152
|
-
|
|
154
|
+
annotation: options.reason.trim()
|
|
153
155
|
};
|
|
154
156
|
}
|
|
155
157
|
export function createCommentBody(message, options = {}) {
|
|
@@ -158,6 +160,116 @@ export function createCommentBody(message, options = {}) {
|
|
|
158
160
|
type: options.type || 'general'
|
|
159
161
|
};
|
|
160
162
|
}
|
|
163
|
+
function requireReviewLocator(response) {
|
|
164
|
+
let locator = response?.review_locator;
|
|
165
|
+
if (!locator?.comparison_id || !locator?.build_id || !locator?.organization_slug) {
|
|
166
|
+
let error = new Error('Comparison response did not include review location details');
|
|
167
|
+
error.code = 'INVALID_REVIEW_CONTEXT';
|
|
168
|
+
throw error;
|
|
169
|
+
}
|
|
170
|
+
return locator;
|
|
171
|
+
}
|
|
172
|
+
async function runComparisonDecision({
|
|
173
|
+
command,
|
|
174
|
+
comparisonId,
|
|
175
|
+
decision,
|
|
176
|
+
annotation,
|
|
177
|
+
failureMessage,
|
|
178
|
+
globalOptions,
|
|
179
|
+
options,
|
|
180
|
+
spinnerMessage,
|
|
181
|
+
writeHumanSuccess,
|
|
182
|
+
deps,
|
|
183
|
+
configure = true
|
|
184
|
+
}) {
|
|
185
|
+
let {
|
|
186
|
+
loadConfig,
|
|
187
|
+
createApiClient,
|
|
188
|
+
getAccessToken,
|
|
189
|
+
randomUUID,
|
|
190
|
+
output,
|
|
191
|
+
exit
|
|
192
|
+
} = createReviewDeps(deps);
|
|
193
|
+
if (configure) {
|
|
194
|
+
configureOutput(output, globalOptions);
|
|
195
|
+
}
|
|
196
|
+
try {
|
|
197
|
+
let config = await loadReviewConfig({
|
|
198
|
+
loadConfig,
|
|
199
|
+
options,
|
|
200
|
+
globalOptions
|
|
201
|
+
});
|
|
202
|
+
let token = await getReviewToken(config, getAccessToken);
|
|
203
|
+
if (!token) {
|
|
204
|
+
output.error('User login required for review actions');
|
|
205
|
+
output.hint('Run "vizzly login" to approve, reject, or comment');
|
|
206
|
+
output.cleanup();
|
|
207
|
+
exit(1);
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
output.startSpinner(spinnerMessage);
|
|
211
|
+
let client = createReviewClient({
|
|
212
|
+
createApiClient,
|
|
213
|
+
config,
|
|
214
|
+
command,
|
|
215
|
+
token
|
|
216
|
+
});
|
|
217
|
+
let comparisonResponse = await client.request(`/api/sdk/comparisons/${comparisonId}`);
|
|
218
|
+
let locator = requireReviewLocator(comparisonResponse);
|
|
219
|
+
if (locator.comparison_id !== comparisonId) {
|
|
220
|
+
let error = new Error('Comparison response did not match the requested comparison');
|
|
221
|
+
error.code = 'INVALID_REVIEW_CONTEXT';
|
|
222
|
+
throw error;
|
|
223
|
+
}
|
|
224
|
+
let organizationHeaders = {
|
|
225
|
+
'X-Organization': locator.organization_slug
|
|
226
|
+
};
|
|
227
|
+
let commandId = randomUUID();
|
|
228
|
+
let response = await client.request(`/api/visual-review/builds/${locator.build_id}/comparisons/${comparisonId}/decision`, {
|
|
229
|
+
method: 'POST',
|
|
230
|
+
headers: {
|
|
231
|
+
...organizationHeaders,
|
|
232
|
+
'Content-Type': 'application/json'
|
|
233
|
+
},
|
|
234
|
+
body: JSON.stringify({
|
|
235
|
+
commandId,
|
|
236
|
+
decision,
|
|
237
|
+
...(annotation ? {
|
|
238
|
+
annotation
|
|
239
|
+
} : {})
|
|
240
|
+
})
|
|
241
|
+
});
|
|
242
|
+
output.stopSpinner();
|
|
243
|
+
if (globalOptions.json) {
|
|
244
|
+
output.data({
|
|
245
|
+
eventId: response.eventId,
|
|
246
|
+
idempotent: response.idempotent,
|
|
247
|
+
review: response.review
|
|
248
|
+
});
|
|
249
|
+
output.cleanup();
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
writeHumanSuccess(output);
|
|
253
|
+
output.cleanup();
|
|
254
|
+
} catch (error) {
|
|
255
|
+
output.stopSpinner();
|
|
256
|
+
if (globalOptions.json) {
|
|
257
|
+
output.data({
|
|
258
|
+
comparisonId,
|
|
259
|
+
error: {
|
|
260
|
+
message: error.message,
|
|
261
|
+
code: error.code
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
output.cleanup();
|
|
265
|
+
exit(1);
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
output.error(failureMessage, error);
|
|
269
|
+
output.cleanup();
|
|
270
|
+
exit(1);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
161
273
|
|
|
162
274
|
/**
|
|
163
275
|
* Approve a comparison
|
|
@@ -167,27 +279,15 @@ export function createCommentBody(message, options = {}) {
|
|
|
167
279
|
* @param {Object} deps - Dependencies for testing
|
|
168
280
|
*/
|
|
169
281
|
export async function approveCommand(comparisonId, options = {}, globalOptions = {}, deps = {}) {
|
|
170
|
-
return await
|
|
282
|
+
return await runComparisonDecision({
|
|
171
283
|
command: 'approve',
|
|
172
|
-
|
|
284
|
+
comparisonId,
|
|
285
|
+
decision: 'approved',
|
|
286
|
+
annotation: createApprovalBody(options).annotation,
|
|
173
287
|
failureMessage: 'Failed to approve comparison',
|
|
174
288
|
globalOptions,
|
|
175
289
|
options,
|
|
176
|
-
requestBody: createApprovalBody(options),
|
|
177
290
|
spinnerMessage: 'Approving comparison...',
|
|
178
|
-
writeJsonError: (output, error) => output.data({
|
|
179
|
-
approved: false,
|
|
180
|
-
comparisonId,
|
|
181
|
-
error: {
|
|
182
|
-
message: error.message,
|
|
183
|
-
code: error.code
|
|
184
|
-
}
|
|
185
|
-
}),
|
|
186
|
-
writeJsonSuccess: (output, response) => output.data({
|
|
187
|
-
approved: true,
|
|
188
|
-
comparisonId,
|
|
189
|
-
comparison: response.comparison
|
|
190
|
-
}),
|
|
191
291
|
writeHumanSuccess: output => {
|
|
192
292
|
output.complete(`Comparison ${comparisonId} approved`);
|
|
193
293
|
if (options.comment) {
|
|
@@ -218,28 +318,15 @@ export async function rejectCommand(comparisonId, options = {}, globalOptions =
|
|
|
218
318
|
exit(1);
|
|
219
319
|
return;
|
|
220
320
|
}
|
|
221
|
-
return await
|
|
321
|
+
return await runComparisonDecision({
|
|
222
322
|
command: 'reject',
|
|
223
|
-
|
|
323
|
+
comparisonId,
|
|
324
|
+
decision: 'rejected',
|
|
325
|
+
annotation: createRejectionBody(options).annotation,
|
|
224
326
|
failureMessage: 'Failed to reject comparison',
|
|
225
327
|
globalOptions,
|
|
226
328
|
options,
|
|
227
|
-
requestBody: createRejectionBody(options),
|
|
228
329
|
spinnerMessage: 'Rejecting comparison...',
|
|
229
|
-
writeJsonError: (output, error) => output.data({
|
|
230
|
-
rejected: false,
|
|
231
|
-
comparisonId,
|
|
232
|
-
error: {
|
|
233
|
-
message: error.message,
|
|
234
|
-
code: error.code
|
|
235
|
-
}
|
|
236
|
-
}),
|
|
237
|
-
writeJsonSuccess: (output, response) => output.data({
|
|
238
|
-
rejected: true,
|
|
239
|
-
comparisonId,
|
|
240
|
-
reason: options.reason,
|
|
241
|
-
comparison: response.comparison
|
|
242
|
-
}),
|
|
243
330
|
writeHumanSuccess: output => {
|
|
244
331
|
output.complete(`Comparison ${comparisonId} rejected`);
|
|
245
332
|
output.hint(`Reason: "${options.reason}"`);
|
package/dist/commands/run.js
CHANGED
|
@@ -493,9 +493,7 @@ export async function runCommand(testCommand, options = {}, globalOptions = {},
|
|
|
493
493
|
if (Object.keys(comparisons).length > 0) {
|
|
494
494
|
jsonResult.comparisons = comparisons;
|
|
495
495
|
}
|
|
496
|
-
|
|
497
|
-
jsonResult.approvalStatus = buildResult.approvalStatus;
|
|
498
|
-
}
|
|
496
|
+
jsonResult.visual_review = buildResult.visual_review;
|
|
499
497
|
if (buildResult.build?.conclusion !== undefined) {
|
|
500
498
|
jsonResult.conclusion = buildResult.build.conclusion;
|
|
501
499
|
}
|
package/dist/commands/status.js
CHANGED
|
@@ -49,67 +49,38 @@ async function fetchOptionalPreviewInfo(getPreviewInfo, client, buildId) {
|
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
51
|
export function normalizeBuildStatus(buildStatus) {
|
|
52
|
-
return buildStatus.build
|
|
52
|
+
return buildStatus.build;
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
/**
|
|
56
|
-
* Read
|
|
56
|
+
* Read the processing counts from the status response.
|
|
57
57
|
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
* becoming client-authored zeroes.
|
|
61
|
-
*
|
|
62
|
-
* @param {Object} status - Canonical status bundle or legacy build record.
|
|
63
|
-
* @returns {Object|undefined} API-provided processing facts when available.
|
|
58
|
+
* @param {Object} status - Status response.
|
|
59
|
+
* @returns {Object} API-provided processing facts.
|
|
64
60
|
*/
|
|
65
61
|
export function getProcessingStatus(status = {}) {
|
|
66
|
-
|
|
67
|
-
return status.processing;
|
|
68
|
-
}
|
|
69
|
-
let build = normalizeBuildStatus(status);
|
|
70
|
-
let processing = {};
|
|
71
|
-
let fields = [['total', build.screenshot_count], ['completed', build.completed_jobs], ['failed', build.failed_jobs], ['active', build.processing_screenshots]];
|
|
72
|
-
for (let [name, value] of fields) {
|
|
73
|
-
if (value != null) {
|
|
74
|
-
processing[name] = value;
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
return Object.keys(processing).length > 0 ? processing : undefined;
|
|
62
|
+
return status.processing;
|
|
78
63
|
}
|
|
79
64
|
|
|
80
65
|
/**
|
|
81
66
|
* Read comparison totals without deriving them from screenshot counts.
|
|
82
67
|
*
|
|
83
|
-
* @param {Object} status -
|
|
84
|
-
* @returns {Object
|
|
68
|
+
* @param {Object} status - Status response.
|
|
69
|
+
* @returns {Object} API-provided comparison facts.
|
|
85
70
|
*/
|
|
86
71
|
export function getComparisonStatus(status = {}) {
|
|
87
|
-
|
|
88
|
-
return status.comparisons;
|
|
89
|
-
}
|
|
90
|
-
let build = normalizeBuildStatus(status);
|
|
91
|
-
let comparisons = {};
|
|
92
|
-
let fields = [['total', build.total_comparisons], ['new', build.new_comparisons], ['changed', build.changed_comparisons], ['identical', build.identical_comparisons]];
|
|
93
|
-
for (let [name, value] of fields) {
|
|
94
|
-
if (value != null) {
|
|
95
|
-
comparisons[name] = value;
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
return Object.keys(comparisons).length > 0 ? comparisons : undefined;
|
|
72
|
+
return status.comparisons;
|
|
99
73
|
}
|
|
100
74
|
|
|
101
75
|
/**
|
|
102
|
-
*
|
|
103
|
-
*
|
|
104
|
-
* Processing status stays separate: a pending review never means a screenshot
|
|
105
|
-
* is still being processed.
|
|
76
|
+
* Read the build review state without mixing it with processing status.
|
|
106
77
|
*
|
|
107
|
-
* @param {Object} status -
|
|
78
|
+
* @param {Object} status - Status response.
|
|
108
79
|
* @returns {string|null} API-provided review state when available.
|
|
109
80
|
*/
|
|
110
81
|
export function getBuildReviewState(status = {}) {
|
|
111
82
|
let build = normalizeBuildStatus(status);
|
|
112
|
-
return getVisualReviewState(build)
|
|
83
|
+
return getVisualReviewState(build);
|
|
113
84
|
}
|
|
114
85
|
|
|
115
86
|
/**
|
|
@@ -132,13 +103,10 @@ export function createStatusSuggestedCommands(build = {}) {
|
|
|
132
103
|
}
|
|
133
104
|
|
|
134
105
|
/**
|
|
135
|
-
* Preserve the established JSON fields while exposing canonical status facts.
|
|
136
|
-
*
|
|
137
106
|
* Every lifecycle, processing, comparison, and review value comes directly
|
|
138
|
-
* from the API.
|
|
139
|
-
* rather than being presented as false certainty.
|
|
107
|
+
* from the API.
|
|
140
108
|
*
|
|
141
|
-
* @param {Object} status -
|
|
109
|
+
* @param {Object} status - Status response.
|
|
142
110
|
* @param {Object|null} previewInfo - Optional preview response.
|
|
143
111
|
* @returns {Object} Machine-readable status payload.
|
|
144
112
|
*/
|
|
@@ -146,7 +114,6 @@ export function createStatusData(status, previewInfo = null) {
|
|
|
146
114
|
let build = normalizeBuildStatus(status);
|
|
147
115
|
let processing = getProcessingStatus(status);
|
|
148
116
|
let comparisons = getComparisonStatus(status);
|
|
149
|
-
let visualReview = build.visual_review || status.visualReview?.build || status.visual_review;
|
|
150
117
|
return {
|
|
151
118
|
resource: status.resource,
|
|
152
119
|
schemaVersion: status.schema_version,
|
|
@@ -161,18 +128,14 @@ export function createStatusData(status, previewInfo = null) {
|
|
|
161
128
|
branch: build.branch,
|
|
162
129
|
commit: build.commit_sha,
|
|
163
130
|
commitMessage: build.commit_message,
|
|
164
|
-
screenshotsTotal: processing
|
|
131
|
+
screenshotsTotal: processing.total,
|
|
165
132
|
processing,
|
|
166
|
-
comparisonsTotal: comparisons
|
|
133
|
+
comparisonsTotal: comparisons.total,
|
|
167
134
|
comparisons,
|
|
168
|
-
newComparisons: comparisons
|
|
169
|
-
changedComparisons: comparisons
|
|
170
|
-
identicalComparisons: comparisons
|
|
171
|
-
|
|
172
|
-
review: status.review,
|
|
173
|
-
reviewFlow: status.reviewFlow,
|
|
174
|
-
visualReview,
|
|
175
|
-
approvalStatus: build.approval_status,
|
|
135
|
+
newComparisons: comparisons.new,
|
|
136
|
+
changedComparisons: comparisons.changed,
|
|
137
|
+
identicalComparisons: comparisons.identical,
|
|
138
|
+
visual_review: build.visual_review || null,
|
|
176
139
|
executionTime: build.execution_time_ms,
|
|
177
140
|
isBaseline: build.is_baseline,
|
|
178
141
|
userAgent: build.user_agent,
|
|
@@ -203,19 +166,18 @@ export function createBuildInfo(build) {
|
|
|
203
166
|
}
|
|
204
167
|
|
|
205
168
|
/**
|
|
206
|
-
* Format API comparison outcomes
|
|
169
|
+
* Format API comparison outcomes.
|
|
207
170
|
*
|
|
208
|
-
* @param {Object} status -
|
|
171
|
+
* @param {Object} status - Status response.
|
|
209
172
|
* @param {Object} colors - Output color helpers.
|
|
210
173
|
* @returns {string} Human-readable comparison summary.
|
|
211
174
|
*/
|
|
212
175
|
export function createComparisonStats(status, colors) {
|
|
213
|
-
let
|
|
214
|
-
let comparisons = getComparisonStatus(status) || {};
|
|
176
|
+
let comparisons = getComparisonStatus(status);
|
|
215
177
|
let stats = [];
|
|
216
|
-
let newCount = comparisons.new
|
|
217
|
-
let changedCount = comparisons.changed
|
|
218
|
-
let identicalCount = comparisons.identical
|
|
178
|
+
let newCount = comparisons.new;
|
|
179
|
+
let changedCount = comparisons.changed;
|
|
180
|
+
let identicalCount = comparisons.identical;
|
|
219
181
|
if (newCount > 0) {
|
|
220
182
|
stats.push(`${colors.brand.info(newCount)} new`);
|
|
221
183
|
}
|
|
@@ -229,45 +191,39 @@ export function createComparisonStats(status, colors) {
|
|
|
229
191
|
}
|
|
230
192
|
|
|
231
193
|
/**
|
|
232
|
-
* Build the
|
|
233
|
-
*
|
|
234
|
-
* Slug routes match the current app, while the project-ID route remains as a
|
|
235
|
-
* compatibility fallback for older build responses.
|
|
194
|
+
* Build the scoped app link when the API did not return one.
|
|
236
195
|
*
|
|
237
196
|
* @param {string} baseUrl - API or app base URL.
|
|
238
197
|
* @param {Object} build - Build record.
|
|
239
|
-
* @param {Object|null} scope -
|
|
198
|
+
* @param {Object|null} scope - Organization and project details.
|
|
240
199
|
* @returns {string|null} Build URL when the response has enough identity.
|
|
241
200
|
*/
|
|
242
201
|
export function createBuildUrl(baseUrl, build, scope = null) {
|
|
243
202
|
if (!baseUrl) {
|
|
244
203
|
return null;
|
|
245
204
|
}
|
|
246
|
-
let organizationSlug = scope?.organization?.slug
|
|
247
|
-
let projectSlug = scope?.project?.slug
|
|
205
|
+
let organizationSlug = scope?.organization?.slug;
|
|
206
|
+
let projectSlug = scope?.project?.slug;
|
|
248
207
|
let appBaseUrl = getAppBaseUrl(baseUrl);
|
|
249
208
|
if (organizationSlug && projectSlug) {
|
|
250
209
|
return `${appBaseUrl}/${organizationSlug}/${projectSlug}/builds/${build.id}`;
|
|
251
210
|
}
|
|
252
|
-
if (build.project_id) {
|
|
253
|
-
return `${appBaseUrl}/projects/${build.project_id}/builds/${build.id}`;
|
|
254
|
-
}
|
|
255
211
|
return null;
|
|
256
212
|
}
|
|
257
213
|
|
|
258
214
|
/**
|
|
259
|
-
* Preserve status failure behavior while
|
|
215
|
+
* Preserve status failure behavior while using the server's conclusion.
|
|
260
216
|
*
|
|
261
217
|
* Review-required and rejected builds keep their existing successful command
|
|
262
218
|
* exit behavior. Only build or processing failures produce a failing status.
|
|
263
219
|
*
|
|
264
|
-
* @param {Object} status -
|
|
220
|
+
* @param {Object} status - Status response.
|
|
265
221
|
* @returns {boolean} Whether human status should exit non-zero.
|
|
266
222
|
*/
|
|
267
223
|
export function shouldFailStatus(status) {
|
|
268
224
|
let build = normalizeBuildStatus(status);
|
|
269
225
|
let processing = getProcessingStatus(status);
|
|
270
|
-
return build.status === 'failed' || ['build_failed', 'processing_failed'].includes(status.conclusion) ||
|
|
226
|
+
return build.status === 'failed' || ['build_failed', 'processing_failed'].includes(status.conclusion) || processing.failed > 0;
|
|
271
227
|
}
|
|
272
228
|
function writeStatusTiming({
|
|
273
229
|
build,
|
|
@@ -314,7 +270,7 @@ function writeSuggestedCommands({
|
|
|
314
270
|
/**
|
|
315
271
|
* Add optional API facts without inventing placeholders for missing metadata.
|
|
316
272
|
*
|
|
317
|
-
* @param {Object} status -
|
|
273
|
+
* @param {Object} status - Status response.
|
|
318
274
|
* @returns {Object} Available verbose fields.
|
|
319
275
|
*/
|
|
320
276
|
function createVerboseInfo(status) {
|
|
@@ -348,7 +304,7 @@ function createVerboseInfo(status) {
|
|
|
348
304
|
/**
|
|
349
305
|
* Format only the processing facts supplied by the API.
|
|
350
306
|
*
|
|
351
|
-
* @param {Object|undefined} processing -
|
|
307
|
+
* @param {Object|undefined} processing - Processing counts.
|
|
352
308
|
* @returns {string} Human-readable processing summary.
|
|
353
309
|
*/
|
|
354
310
|
function formatProcessingStatus(processing) {
|
|
@@ -387,7 +343,7 @@ function writeHumanStatus({
|
|
|
387
343
|
let colors = output.getColors();
|
|
388
344
|
let comparisonStats = createComparisonStats(status, colors);
|
|
389
345
|
let processing = getProcessingStatus(status);
|
|
390
|
-
let screenshotsTotal = processing
|
|
346
|
+
let screenshotsTotal = processing.total;
|
|
391
347
|
let processingSummary = formatProcessingStatus(processing);
|
|
392
348
|
if (screenshotsTotal != null) {
|
|
393
349
|
output.labelValue('Screenshots', String(screenshotsTotal));
|
package/dist/commands/upload.js
CHANGED
|
@@ -262,7 +262,7 @@ export async function uploadCommand(screenshotsPath, options = {}, globalOptions
|
|
|
262
262
|
failed: buildResult.failedComparisons || 0,
|
|
263
263
|
new: buildResult.newComparisons || 0
|
|
264
264
|
},
|
|
265
|
-
|
|
265
|
+
visual_review: buildResult.visual_review,
|
|
266
266
|
executionTimeMs
|
|
267
267
|
});
|
|
268
268
|
output.cleanup();
|