@vizzly-testing/cli 0.35.3-beta.4 → 0.36.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 +8 -7
- package/dist/api/core.js +8 -8
- package/dist/api/endpoints.js +10 -7
- package/dist/client/index.js +0 -6
- package/dist/commands/run.js +55 -3
- package/dist/reporter/reporter-bundle.iife.js +51 -51
- package/dist/sdk/index.js +0 -10
- package/dist/server/handlers/api-handler.js +105 -195
- package/dist/server/handlers/tdd-handler.js +13 -54
- package/dist/server/routers/screenshot.js +6 -3
- package/dist/test-runner/core.js +7 -1
- package/dist/test-runner/operations.js +22 -7
- package/dist/types/client.d.ts +12 -5
- package/dist/uploader/core.js +4 -3
- package/dist/utils/screenshot-options.js +4 -84
- package/dist/utils/security.js +27 -42
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -144,18 +144,19 @@ test('homepage looks correct', async ({ page }) => {
|
|
|
144
144
|
fullPage: true,
|
|
145
145
|
requestTimeout: 5000,
|
|
146
146
|
properties: {
|
|
147
|
-
|
|
148
|
-
|
|
147
|
+
theme: 'dark',
|
|
148
|
+
locale: 'en-US',
|
|
149
149
|
},
|
|
150
150
|
});
|
|
151
151
|
});
|
|
152
152
|
```
|
|
153
153
|
|
|
154
|
-
`properties` is your metadata bag for baseline grouping, filtering,
|
|
155
|
-
debugging.
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
154
|
+
`properties` is your key/value metadata bag for baseline grouping, filtering,
|
|
155
|
+
and debugging. Vizzly passes every property through as user metadata; property
|
|
156
|
+
names are never interpreted as options. The supported top-level options are
|
|
157
|
+
`threshold`, `minClusterSize`, `fullPage`, `requestTimeout`, and `buildId`.
|
|
158
|
+
Vizzly reads width and height from the captured image, so viewport dimensions do
|
|
159
|
+
not need to be included in `properties`.
|
|
159
160
|
|
|
160
161
|
The client SDK is lightweight. It posts screenshots to the local Vizzly server
|
|
161
162
|
or the cloud build wrapper. It works with any test runner.
|
package/dist/api/core.js
CHANGED
|
@@ -30,14 +30,17 @@ export function buildAuthHeader(token) {
|
|
|
30
30
|
* @param {string} version - CLI version
|
|
31
31
|
* @param {string} command - Command being executed (run, upload, tdd, etc.)
|
|
32
32
|
* @param {string|null} sdkUserAgent - Optional SDK user agent to append
|
|
33
|
+
* @param {string} nodeVersion - Node.js version to append
|
|
33
34
|
* @returns {string} Complete User-Agent string
|
|
34
35
|
*/
|
|
35
|
-
export function buildUserAgent(version, command, sdkUserAgent = null) {
|
|
36
|
+
export function buildUserAgent(version, command, sdkUserAgent = null, nodeVersion = process.versions.node) {
|
|
36
37
|
let baseUserAgent = `vizzly-cli/${version} (${command})`;
|
|
38
|
+
let userAgentParts = [baseUserAgent];
|
|
37
39
|
if (sdkUserAgent) {
|
|
38
|
-
|
|
40
|
+
userAgentParts.push(sdkUserAgent);
|
|
39
41
|
}
|
|
40
|
-
|
|
42
|
+
if (nodeVersion) userAgentParts.push(`node/${nodeVersion}`);
|
|
43
|
+
return userAgentParts.join(' ');
|
|
41
44
|
}
|
|
42
45
|
|
|
43
46
|
/**
|
|
@@ -74,7 +77,7 @@ export function buildRequestHeaders({
|
|
|
74
77
|
* Build payload for screenshot upload or SHA resolution
|
|
75
78
|
* @param {string} name - Screenshot name
|
|
76
79
|
* @param {Buffer|null} buffer - Image data, or null when resolving by SHA
|
|
77
|
-
* @param {Object} metadata -
|
|
80
|
+
* @param {Object} metadata - User screenshot properties
|
|
78
81
|
* @param {string|null} sha256 - Pre-computed SHA256 hash (optional)
|
|
79
82
|
* @returns {Object} Screenshot upload payload
|
|
80
83
|
*/
|
|
@@ -98,7 +101,7 @@ export function buildScreenshotPayload(name, buffer, metadata = {}, sha256 = nul
|
|
|
98
101
|
/**
|
|
99
102
|
* Build payload for server-side screenshot resolution without sending image bytes
|
|
100
103
|
* @param {string} name - Screenshot name
|
|
101
|
-
* @param {Object} metadata -
|
|
104
|
+
* @param {Object} metadata - User screenshot properties
|
|
102
105
|
* @param {string} sha256 - Pre-computed SHA256 hash
|
|
103
106
|
* @returns {Object} Screenshot resolution payload
|
|
104
107
|
*/
|
|
@@ -190,9 +193,6 @@ export function buildScreenshotCheckObject(sha256, name, metadata = {}) {
|
|
|
190
193
|
return {
|
|
191
194
|
sha256,
|
|
192
195
|
name,
|
|
193
|
-
browser: meta.browser || 'chrome',
|
|
194
|
-
viewport_width: meta.viewport?.width || meta.viewport_width || 1920,
|
|
195
|
-
viewport_height: meta.viewport?.height || meta.viewport_height || 1080,
|
|
196
196
|
properties
|
|
197
197
|
};
|
|
198
198
|
}
|
package/dist/api/endpoints.js
CHANGED
|
@@ -80,13 +80,15 @@ export async function createBuild(client, metadata) {
|
|
|
80
80
|
* @param {Object} client - API client
|
|
81
81
|
* @param {string} buildId - Build ID
|
|
82
82
|
* @param {string} status - Build status (pending|running|completed|failed)
|
|
83
|
-
* @param {number|null} executionTimeMs - Execution time in milliseconds
|
|
83
|
+
* @param {number|null} [executionTimeMs=null] - Execution time in milliseconds
|
|
84
|
+
* @param {string} [failureReason] - Explanation sent only for a failed build
|
|
84
85
|
* @returns {Promise<Object>} Updated build data
|
|
85
86
|
*/
|
|
86
|
-
export async function updateBuildStatus(client, buildId, status, executionTimeMs = null) {
|
|
87
|
+
export async function updateBuildStatus(client, buildId, status, executionTimeMs = null, failureReason) {
|
|
87
88
|
let body = {
|
|
88
89
|
status
|
|
89
90
|
};
|
|
91
|
+
if (status === 'failed' && failureReason) body.failureReason = failureReason;
|
|
90
92
|
if (executionTimeMs != null) {
|
|
91
93
|
body.executionTimeMs = executionTimeMs;
|
|
92
94
|
}
|
|
@@ -100,16 +102,17 @@ export async function updateBuildStatus(client, buildId, status, executionTimeMs
|
|
|
100
102
|
}
|
|
101
103
|
|
|
102
104
|
/**
|
|
103
|
-
*
|
|
105
|
+
* Mark a build completed or failed, with an optional failure explanation.
|
|
104
106
|
* @param {Object} client - API client
|
|
105
107
|
* @param {string} buildId - Build ID
|
|
106
|
-
* @param {boolean} success - Whether
|
|
107
|
-
* @param {number|null} executionTimeMs - Execution time in milliseconds
|
|
108
|
+
* @param {boolean} [success=true] - Whether tests and uploads succeeded
|
|
109
|
+
* @param {number|null} [executionTimeMs=null] - Execution time in milliseconds
|
|
110
|
+
* @param {string} [failureReason] - Explanation sent only for a failed build
|
|
108
111
|
* @returns {Promise<Object>} Finalized build data
|
|
109
112
|
*/
|
|
110
|
-
export async function finalizeBuild(client, buildId, success = true, executionTimeMs = null) {
|
|
113
|
+
export async function finalizeBuild(client, buildId, success = true, executionTimeMs = null, failureReason) {
|
|
111
114
|
let status = success ? 'completed' : 'failed';
|
|
112
|
-
return updateBuildStatus(client, buildId, status, executionTimeMs);
|
|
115
|
+
return updateBuildStatus(client, buildId, status, executionTimeMs, failureReason);
|
|
113
116
|
}
|
|
114
117
|
|
|
115
118
|
/**
|
package/dist/client/index.js
CHANGED
|
@@ -203,9 +203,6 @@ function createSimpleClient(serverUrl, clientOptions = {}) {
|
|
|
203
203
|
async screenshot(name, imageBuffer, options = {}) {
|
|
204
204
|
let normalizedOptions = normalizeScreenshotOptions(options);
|
|
205
205
|
let requestTimeout = normalizedOptions.requestTimeout || DEFAULT_TIMEOUT_MS;
|
|
206
|
-
for (let warning of normalizedOptions.warnings) {
|
|
207
|
-
console.warn(`[vizzly] ${warning.message}`);
|
|
208
|
-
}
|
|
209
206
|
try {
|
|
210
207
|
// If it's a string, assume it's a file path and send directly
|
|
211
208
|
// Otherwise it's a Buffer, so convert to base64
|
|
@@ -220,9 +217,6 @@ function createSimpleClient(serverUrl, clientOptions = {}) {
|
|
|
220
217
|
properties: normalizedOptions.properties
|
|
221
218
|
};
|
|
222
219
|
Object.assign(screenshotData, getScreenshotOptionsPayload(normalizedOptions));
|
|
223
|
-
if (normalizedOptions.warnings.length > 0) {
|
|
224
|
-
screenshotData.warnings = normalizedOptions.warnings;
|
|
225
|
-
}
|
|
226
220
|
let httpStart = Date.now();
|
|
227
221
|
let {
|
|
228
222
|
status,
|
package/dist/commands/run.js
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Run
|
|
2
|
+
* Run tests and report their visual results in terminal or JSON output.
|
|
3
|
+
*
|
|
4
|
+
* Incomplete uploads preserve the test exit code and skip comparison waiting.
|
|
5
|
+
* After successful uploads, --wait can still fail for visual comparison results.
|
|
6
|
+
* The returned success flag describes command success, not upload completeness.
|
|
7
|
+
*
|
|
3
8
|
* Uses functional operations directly - no class wrappers needed
|
|
4
9
|
*/
|
|
5
10
|
|
|
@@ -16,6 +21,14 @@ import { loadConfig as defaultLoadConfig } from '../utils/config-loader.js';
|
|
|
16
21
|
import { detectBranch as defaultDetectBranch, detectCommit as defaultDetectCommit, detectCommitMessage as defaultDetectCommitMessage, detectPullRequestNumber as defaultDetectPullRequestNumber, generateBuildNameWithGit as defaultGenerateBuildNameWithGit } from '../utils/git.js';
|
|
17
22
|
import * as defaultOutput from '../utils/output.js';
|
|
18
23
|
import { writeSession as defaultWriteSession } from '../utils/session.js';
|
|
24
|
+
function formatUploadCounts({
|
|
25
|
+
total,
|
|
26
|
+
uploaded,
|
|
27
|
+
reused,
|
|
28
|
+
failed
|
|
29
|
+
}) {
|
|
30
|
+
return `${total} captured, ${uploaded} uploaded, ${reused} reused, ${failed} failed`;
|
|
31
|
+
}
|
|
19
32
|
export async function resolveBuildDisplayUrl({
|
|
20
33
|
result,
|
|
21
34
|
config,
|
|
@@ -68,7 +81,9 @@ function buildContextCommand(buildId, {
|
|
|
68
81
|
* @param {string} testCommand - Test command to execute
|
|
69
82
|
* @param {Object} options - Command options
|
|
70
83
|
* @param {Object} globalOptions - Global CLI options
|
|
71
|
-
* @param {Object} deps -
|
|
84
|
+
* @param {Object} deps - Command services and process hooks.
|
|
85
|
+
* @returns {Promise<Object>} Command outcome with success, an optional exitCode,
|
|
86
|
+
* and run results when available.
|
|
72
87
|
*/
|
|
73
88
|
export async function runCommand(testCommand, options = {}, globalOptions = {}, deps = {}) {
|
|
74
89
|
let {
|
|
@@ -321,6 +336,33 @@ export async function runCommand(testCommand, options = {}, globalOptions = {},
|
|
|
321
336
|
if (result.buildId) {
|
|
322
337
|
buildId = result.buildId;
|
|
323
338
|
}
|
|
339
|
+
if (result.uploads?.failed > 0) {
|
|
340
|
+
let message = `${result.uploads.failed} screenshot(s) failed to upload. Visual testing is incomplete.`;
|
|
341
|
+
if (globalOptions.json) {
|
|
342
|
+
output.data({
|
|
343
|
+
buildId: result.buildId,
|
|
344
|
+
status: 'incomplete',
|
|
345
|
+
screenshotsCaptured: result.screenshotsCaptured,
|
|
346
|
+
uploads: result.uploads,
|
|
347
|
+
error: {
|
|
348
|
+
code: 'SCREENSHOT_UPLOAD_FAILED',
|
|
349
|
+
message
|
|
350
|
+
},
|
|
351
|
+
url: result.url,
|
|
352
|
+
exitCode: 0
|
|
353
|
+
});
|
|
354
|
+
} else {
|
|
355
|
+
output.warn(message);
|
|
356
|
+
output.print(`Screenshots: ${formatUploadCounts(result.uploads)}`);
|
|
357
|
+
if (result.url) output.info(`Results: ${result.url}`);
|
|
358
|
+
}
|
|
359
|
+
output.cleanup();
|
|
360
|
+
return {
|
|
361
|
+
success: true,
|
|
362
|
+
exitCode: 0,
|
|
363
|
+
result
|
|
364
|
+
};
|
|
365
|
+
}
|
|
324
366
|
|
|
325
367
|
// JSON output mode - output structured data and exit
|
|
326
368
|
if (globalOptions.json && !runOptions.wait) {
|
|
@@ -336,6 +378,9 @@ export async function runCommand(testCommand, options = {}, globalOptions = {},
|
|
|
336
378
|
status: 'completed',
|
|
337
379
|
url: displayUrl,
|
|
338
380
|
screenshotsCaptured: result.screenshotsCaptured || 0,
|
|
381
|
+
...(result.uploads ? {
|
|
382
|
+
uploads: result.uploads
|
|
383
|
+
} : {}),
|
|
339
384
|
executionTimeMs,
|
|
340
385
|
git: {
|
|
341
386
|
branch,
|
|
@@ -362,7 +407,8 @@ export async function runCommand(testCommand, options = {}, globalOptions = {},
|
|
|
362
407
|
if (result.buildId && !globalOptions.json) {
|
|
363
408
|
output.blank();
|
|
364
409
|
let colors = output.getColors();
|
|
365
|
-
|
|
410
|
+
let screenshotSummary = result.uploads ? formatUploadCounts(result.uploads) : result.screenshotsCaptured;
|
|
411
|
+
output.print(` ${colors.brand.textTertiary('Screenshots')} ${colors.white(screenshotSummary)}`);
|
|
366
412
|
let displayUrl = await resolveBuildDisplayUrl({
|
|
367
413
|
result,
|
|
368
414
|
config,
|
|
@@ -392,6 +438,9 @@ export async function runCommand(testCommand, options = {}, globalOptions = {},
|
|
|
392
438
|
output.data({
|
|
393
439
|
buildId: buildId || null,
|
|
394
440
|
status: 'failed',
|
|
441
|
+
...(error.uploads ? {
|
|
442
|
+
uploads: error.uploads
|
|
443
|
+
} : {}),
|
|
395
444
|
error: {
|
|
396
445
|
code: error.code,
|
|
397
446
|
message: error.message
|
|
@@ -479,6 +528,9 @@ export async function runCommand(testCommand, options = {}, globalOptions = {},
|
|
|
479
528
|
status: buildResult.status,
|
|
480
529
|
url: displayUrl,
|
|
481
530
|
screenshotsCaptured: result.screenshotsCaptured || 0,
|
|
531
|
+
...(result.uploads ? {
|
|
532
|
+
uploads: result.uploads
|
|
533
|
+
} : {}),
|
|
482
534
|
executionTimeMs,
|
|
483
535
|
git: {
|
|
484
536
|
branch,
|